This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 211287
Collapse All | Expand All

(-)a/api.search/apichanges.xml (+192 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
6
7
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8
Other names may be trademarks of their respective owners.
9
10
11
The contents of this file are subject to the terms of either the GNU
12
General Public License Version 2 only ("GPL") or the Common
13
Development and Distribution License("CDDL") (collectively, the
14
"License"). You may not use this file except in compliance with the
15
License. You can obtain a copy of the License at
16
http://www.netbeans.org/cddl-gplv2.html
17
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
18
specific language governing permissions and limitations under the
19
License.  When distributing the software, include this License Header
20
Notice in each file and include the License file at
21
nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
22
particular file as subject to the "Classpath" exception as provided
23
by Oracle in the GPL Version 2 section of the License file that
24
accompanied this code. If applicable, add the following below the
25
License Header, with the fields enclosed by brackets [] replaced by
26
your own identifying information:
27
"Portions Copyrighted [year] [name of copyright owner]"
28
29
Contributor(s):
30
31
The Original Software is NetBeans. The Initial Developer of the Original
32
Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
33
Microsystems, Inc. All Rights Reserved.
34
35
If you wish your version of this file to be governed by only the CDDL
36
or only the GPL Version 2, indicate your decision by adding
37
"[Contributor] elects to include this software in this distribution
38
under the [CDDL or GPL Version 2] license." If you do not indicate a
39
single choice of license, a recipient has the option to distribute
40
your version of this file under either the CDDL, the GPL Version 2 or
41
to extend the choice of license to its licensees as provided above.
42
However, if you add GPL Version 2 code and therefore, elected the GPL
43
Version 2 license, then the option applies only if the new code is
44
made subject to such option by the copyright holder.
45
-->
46
<?xml-stylesheet type="text/xml" href="../nbbuild/javadoctools/apichanges.xsl"?>
47
<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.0//EN" "../nbbuild/javadoctools/apichanges.dtd">
48
49
<!--
50
51
INFO FOR PEOPLE ADDING CHANGES:
52
53
Check the DTD (apichanges.dtd) for details on the syntax. You do not
54
need to regenerate the HTML, as this is part of Javadoc generation; just
55
change the XML. Rough syntax of a change (several parts optional):
56
57
<change>
58
    <api name="compiler"/>
59
    <summary>Some brief description here, can use <b>XHTML</b></summary>
60
    <version major="1" minor="99"/>
61
    <date day="13" month="6" year="2001"/>
62
    <author login="jrhacker"/>
63
    <compatibility addition="yes"/>
64
    <description>
65
        The main description of the change here.
66
        Again can use full <b>XHTML</b> as needed.
67
    </description>
68
    <class package="org.openide.compiler" name="DoWhatIWantCompiler"/>
69
    <issue number="14309"/>
70
</change>
71
72
Also permitted elements: <package>, <branch>. <version> is API spec
73
version, recommended for all new changes. <compatibility> should say
74
if things were added/modified/deprecated/etc. and give all information
75
related to upgrading old code. List affected top-level classes and
76
link to issue numbers if applicable. See the DTD for more details.
77
78
Changes need not be in any particular order, they are sorted in various
79
ways by the stylesheet anyway.
80
81
Dates are assumed to mean "on the trunk". If you *also* make the same
82
change on a stabilization branch, use the <branch> tag to indicate this
83
and explain why the change was made on a branch in the <description>.
84
85
Please only change this file on the trunk! Rather: you can change it
86
on branches if you want, but these changes will be ignored; only the
87
trunk version of this file is important.
88
89
Deprecations do not count as incompatible, assuming that code using the
90
deprecated calls continues to see their documented behavior. But do
91
specify deprecation="yes" in <compatibility>.
92
93
This file is not a replacement for Javadoc: it is intended to list changes,
94
not describe the complete current behavior, for which ordinary documentation
95
is the proper place.
96
97
-->
98
99
<apichanges>
100
101
    <!-- First, a list of API names you may use: -->
102
    <apidefs>
103
        <!-- Probably should not be used much: -->
104
        <apidef name="api.search">Search in Projects API</apidef>
105
    </apidefs>
106
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
109
    <changes>
110
        <change id="SearchPatternController">
111
            <api name="api.search"/>
112
            <summary>Added class
113
                <code>SearchPatternController</code>
114
            </summary>
115
            <version major="1" minor="1"/>
116
            <date day="20" month="4" year="2012"/>
117
            <author login="jhavlin"/>
118
            <compatibility addition="yes"/>
119
            <description>
120
                <p>
121
                    Added method
122
                    <code>ComponentUtils.adjustComboForSearchPattern</code>
123
                    and class
124
                    <code>SearchPatternController</code>, that complements
125
                    other controllers for search-related UI components/
126
                </p>
127
            </description>
128
            <class package="org.netbeans.api.search.ui" name="SearchPatternController"/>
129
            <class package="org.netbeans.api.search.ui" name="ComponentUtils"/>
130
        </change>
131
        <change id="DefaultSearchResultsDisplayer">
132
            <api name="api.search"/>
133
            <summary>Added class
134
                <code>DefaultSearchResultsDisplayer</code>
135
            </summary>
136
            <version major="1" minor="1"/>
137
            <date day="20" month="4" year="2012"/>
138
            <author login="jhavlin"/>
139
            <compatibility addition="yes"/>
140
            <description>
141
                <p>
142
                    Method
143
                    <code>SearchResultsDisplayer.createDefault</code> now
144
                    returns object of type
145
                    <code>DefaultSearchResultsDisplayer</code>
146
                    that can be customized, and that let users reuse some features
147
                    used in the standard search provider.
148
                </p>
149
            </description>
150
            <class package="org.netbeans.spi.search.provider" name="DefaultSearchResultsDisplayer"/>
151
        </change>
152
    </changes>
153
154
    <!-- Now the surrounding HTML text and document structure: -->
155
156
    <htmlcontents>
157
        <!--
158
                            NO NO NO NO NO!
159
         ==============>    DO NOT EDIT ME!  <======================
160
          AUTOMATICALLY GENERATED FROM APICHANGES.XML, DO NOT EDIT
161
                SEE openidex/api/apichanges.xml
162
        -->
163
        <head>
164
            <title>Change History for the Search API</title>
165
            <link rel="stylesheet" href="prose.css" type="text/css"/>
166
        </head>
167
        <body>
168
169
            <p class="overviewlink">
170
                <a href="overview-summary.html">Overview</a>
171
            </p>
172
173
            <h1>Introduction</h1>
174
175
            <p>
176
                This document lists changes made to the
177
                <code>Search API</code>.
178
            </p>
179
180
            <!-- The actual lists of changes, as summaries and details: -->
181
182
            <hr/>
183
184
            <standard-changelists module-code-name="org.openidex.util/3"/>
185
186
            <hr/>
187
188
            <p>@FOOTER@</p>
189
190
        </body>
191
    </htmlcontents>
192
</apichanges>
(-)a/api.search/manifest.mf (-1 / +1 lines)
Lines 2-6 Link Here
2
OpenIDE-Module: org.netbeans.api.search
2
OpenIDE-Module: org.netbeans.api.search
3
OpenIDE-Module-Install: org/netbeans/api/search/impl/Installer.class
3
OpenIDE-Module-Install: org/netbeans/api/search/impl/Installer.class
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/api/search/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/api/search/Bundle.properties
5
OpenIDE-Module-Specification-Version: 1.0
5
OpenIDE-Module-Specification-Version: 1.1
6
6
(-)a/api.search/src/org/netbeans/api/search/SearchHistory.java (+13 lines)
Lines 50-55 Link Here
50
import java.util.Collections;
50
import java.util.Collections;
51
import java.util.List;
51
import java.util.List;
52
import java.util.prefs.Preferences;
52
import java.util.prefs.Preferences;
53
import org.netbeans.modules.search.FindDialogMemory;
53
import org.openide.util.NbPreferences;
54
import org.openide.util.NbPreferences;
54
55
55
/**
56
/**
Lines 207-210 Link Here
207
        }
208
        }
208
    }
209
    }
209
    
210
    
211
    /**
212
     * Store last used file name pattern.
213
     */
214
    public void storeFileNamePattern(String pattern) {
215
        FindDialogMemory mem = FindDialogMemory.getDefault();
216
        if (pattern == null) {
217
            mem.setFileNamePatternSpecified(false);
218
        } else {
219
            mem.setFileNamePatternSpecified(true);
220
            mem.storeFileNamePattern(pattern);
221
        }
222
    }
210
}
223
}
(-)a/api.search/src/org/netbeans/api/search/ui/ComponentUtils.java (+16 lines)
Lines 136-139 Link Here
136
         return new ScopeOptionsController(jPanel, fileNameController,
136
         return new ScopeOptionsController(jPanel, fileNameController,
137
                searchAndReplace);
137
                searchAndReplace);
138
    }
138
    }
139
140
    /**
141
     * Adjust a {@link JComboBox} to act as component for selecting search text
142
     * pattern, and return a controller object for interacting with it.
143
     *
144
     * You can bind this combo box with some abstract buttons (usually check
145
     * boxes) to set pattern options.
146
     *
147
     * @param jComboBox Freshly created component that will be adjusted.
148
     * @return Controller for modified {@code jComboBox}.
149
     * @since api.search/1.1
150
     */
151
    public static @NonNull SearchPatternController adjustComboForSearchPattern(
152
            @NonNull JComboBox comboBox) {
153
        return new SearchPatternController(comboBox);
154
    }
139
}
155
}
(-)a/api.search/src/org/netbeans/api/search/ui/SearchPatternController.java (+360 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.api.search.ui;
43
44
import java.awt.Color;
45
import java.awt.Component;
46
import java.awt.event.ItemEvent;
47
import java.awt.event.ItemListener;
48
import java.util.EnumMap;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.regex.Pattern;
52
import java.util.regex.PatternSyntaxException;
53
import javax.swing.AbstractButton;
54
import javax.swing.DefaultComboBoxModel;
55
import javax.swing.JComboBox;
56
import javax.swing.event.DocumentEvent;
57
import javax.swing.event.DocumentListener;
58
import javax.swing.text.JTextComponent;
59
import org.netbeans.api.annotations.common.NonNull;
60
import org.netbeans.api.annotations.common.NullAllowed;
61
import org.netbeans.api.search.SearchHistory;
62
import org.netbeans.api.search.SearchPattern;
63
import org.netbeans.modules.search.ui.PatternChangeListener;
64
import org.netbeans.modules.search.ui.TextFieldFocusListener;
65
import org.netbeans.modules.search.ui.UiUtils;
66
import org.openide.util.Parameters;
67
68
/**
69
 * Controller for a combo box for selection and editing of search patterns.
70
 *
71
 * @author jhavlin
72
 * @since api.search/1.1
73
 */
74
public final class SearchPatternController
75
        extends ComponentController<JComboBox> {
76
77
    private JTextComponent textToFindEditor;
78
79
    /**
80
     * Options of search patterns.
81
     */
82
    public enum Option {
83
        MATCH_CASE, WHOLE_WORDS, REGULAR_EXPRESSION
84
    }
85
    private final Map<Option, AbstractButton> bindings =
86
            new EnumMap<Option, AbstractButton>(Option.class);
87
    private final Map<Option, Boolean> options =
88
            new EnumMap<Option, Boolean>(Option.class);
89
    private final ItemListener listener;
90
    private boolean valid;
91
    private Color defaultTextColor = null;
92
93
    SearchPatternController(final JComboBox component) {
94
        super(component);
95
        component.setEditable(true);
96
        Component cboxEditorComp = component.getEditor().getEditorComponent();
97
        textToFindEditor = (JTextComponent) cboxEditorComp;
98
        textToFindEditor.getDocument().addDocumentListener(
99
                new DocumentListener() {
100
                    @Override
101
                    public void insertUpdate(DocumentEvent e) {
102
                        patternChanged();
103
                    }
104
105
                    @Override
106
                    public void removeUpdate(DocumentEvent e) {
107
                        patternChanged();
108
                    }
109
110
                    @Override
111
                    public void changedUpdate(DocumentEvent e) {
112
                        patternChanged();
113
                    }
114
                });
115
        initHistory();
116
        valid = checkValid();
117
        updateTextPatternColor();
118
        textToFindEditor.addFocusListener(new TextFieldFocusListener());
119
        textToFindEditor.getDocument().addDocumentListener(
120
                new TextToFindChangeListener());
121
122
        component.addItemListener(new ItemListener() {
123
            @Override
124
            public void itemStateChanged(ItemEvent e) {
125
                Object si = component.getSelectedItem();
126
                if (si instanceof ModelItem) {
127
                    SearchPattern sp = ((ModelItem) si).sp;
128
                    for (Map.Entry<Option, AbstractButton> be :
129
                            bindings.entrySet()) {
130
                        switch (be.getKey()) {
131
                            case MATCH_CASE:
132
                                be.getValue().setSelected(sp.isMatchCase());
133
                                break;
134
                            case WHOLE_WORDS:
135
                                be.getValue().setSelected(sp.isWholeWords());
136
                                break;
137
                            case REGULAR_EXPRESSION:
138
                                be.getValue().setSelected(sp.isRegExp());
139
                                break;
140
                        }
141
                    }
142
                    options.put(Option.MATCH_CASE, sp.isMatchCase());
143
                    options.put(Option.WHOLE_WORDS, sp.isWholeWords());
144
                    options.put(Option.REGULAR_EXPRESSION, sp.isRegExp());
145
                }
146
            }
147
        });
148
149
        listener = new ItemListener() {
150
            @Override
151
            public void itemStateChanged(ItemEvent e) {
152
                for (Map.Entry<Option, AbstractButton> entry :
153
                        bindings.entrySet()) {
154
                    if (entry.getValue() == e.getSource()) {
155
                        setOption(entry.getKey(),
156
                                e.getStateChange() == ItemEvent.SELECTED);
157
                        break;
158
                    }
159
                }
160
            }
161
        };
162
    }
163
164
    private void initHistory() {
165
        final DefaultComboBoxModel model = new DefaultComboBoxModel();
166
        final List<SearchPattern> data =
167
                SearchHistory.getDefault().getSearchPatterns();
168
169
        for (SearchPattern sp : data) {
170
            model.addElement(new ModelItem(sp));
171
        }
172
173
        component.setModel(model);
174
175
        if (data.size() > 0) {
176
            setSearchPattern(data.get(0));
177
        }
178
    }
179
180
    /**
181
     * Get text currently shown in the combo box editor.
182
     */
183
    private @NonNull String getText() {
184
        String s = textToFindEditor.getText();
185
        return s == null ? "" : s;                                      //NOI18N
186
    }
187
188
    /**
189
     * Set text to show in combo box editor
190
     *
191
     * @param text Text to show in the component editor.
192
     */
193
    private void setText(@NullAllowed String text) {
194
        component.setSelectedItem(text == null ? "" : text);            //NOI18N
195
    }
196
197
    /**
198
     * Request focus in window. Select text in the combo box editor if it is not
199
     * empty.
200
     */
201
    public boolean requestFocusInWindow() {
202
        if (textToFindEditor.isFocusOwner()) {
203
            return true;
204
        } else {
205
            return textToFindEditor.requestFocusInWindow();
206
        }
207
    }
208
209
    /**
210
     * Get current value of an option of the search pattern.
211
     */
212
    private boolean getOption(@NonNull Option option) {
213
        Parameters.notNull("option", option);                           //NOI18N
214
        Boolean b = options.get(option);
215
        if (b == null) {
216
            return false;
217
        } else {
218
            return b.booleanValue();
219
        }
220
    }
221
222
    /**
223
     * Set value of a search pattern option. Bound abstract button will be
224
     * selected or deselected accordingly.
225
     */
226
    private void setOption(@NonNull Option option, boolean value) {
227
        Parameters.notNull("option", option);                           //NOI18N
228
        options.put(option, value);
229
        AbstractButton button = bindings.get(option);
230
        if (button != null) {
231
            button.setSelected(value);
232
        }
233
        if (option == Option.REGULAR_EXPRESSION) {
234
            updateValidity();
235
        }
236
        fireChange();
237
    }
238
239
    /**
240
     * Get search pattern currently represented by this controller.
241
     */
242
    public @NonNull SearchPattern getSearchPattern() {
243
        return SearchPattern.create(getText(),
244
                getOption(Option.WHOLE_WORDS),
245
                getOption(Option.MATCH_CASE),
246
                getOption(Option.REGULAR_EXPRESSION));
247
    }
248
249
    /**
250
     * Set text and options to represent a search pattern.
251
     */
252
    public void setSearchPattern(@NonNull SearchPattern searchPattern) {
253
        Parameters.notNull("searchPattern", searchPattern);             //NOI18N
254
        setText(searchPattern.getSearchExpression());
255
        setOption(Option.WHOLE_WORDS, searchPattern.isWholeWords());
256
        setOption(Option.MATCH_CASE, searchPattern.isMatchCase());
257
        setOption(Option.REGULAR_EXPRESSION, searchPattern.isRegExp());
258
    }
259
260
    /**
261
     * Bind an abstract button (usually checkbox) to a SearchPattern option.
262
     *
263
     * @param option Option whose value the button should represent.
264
     * @param button Button to control and display the option.
265
     */
266
    public void bind(@NonNull final Option option,
267
            @NonNull final AbstractButton button) {
268
        Parameters.notNull("option", option);                           //NOI18N
269
        Parameters.notNull("button", button);                           //NOI18N
270
271
        if (bindings.containsKey(option)) {
272
            throw new IllegalStateException(
273
                    "Already binded with option " + option);           // NOI18N
274
        }
275
276
        bindings.put(option, button);
277
        button.setSelected(getOption(option));
278
        button.addItemListener(new ItemListener() {
279
            @Override
280
            public void itemStateChanged(ItemEvent e) {
281
                setOption(option, button.isSelected());
282
            }
283
        });
284
    }
285
286
    /**
287
     * Unbind a button from a SearchPattern option.
288
     */
289
    public void unbind(@NonNull Option option, @NonNull AbstractButton button) {
290
        Parameters.notNull("option", option);                           //NOI18N
291
        Parameters.notNull("button", button);                           //NOI18N
292
        bindings.remove(option);
293
        button.removeItemListener(listener);
294
    }
295
296
    private class TextToFindChangeListener extends PatternChangeListener {
297
298
        @Override
299
        public void handleComboBoxChange(String text) {
300
            patternChanged();
301
        }
302
    }
303
304
    private void patternChanged() {
305
        updateValidity();
306
        fireChange();
307
    }
308
309
    private void updateValidity() {
310
        boolean wasValid = valid;
311
        valid = checkValid();
312
        if (valid != wasValid) {
313
            updateTextPatternColor();
314
        }
315
    }
316
317
    private boolean checkValid() {
318
        String expr = getText();
319
        if (!getOption(Option.REGULAR_EXPRESSION) || expr == null) {
320
            return true;
321
        } else {
322
            try {
323
                Pattern p = Pattern.compile(getText());
324
                return true;
325
            } catch (PatternSyntaxException p) {
326
                return false;
327
            }
328
        }
329
    }
330
331
    /**
332
     * Sets proper color of text pattern.
333
     */
334
    private void updateTextPatternColor() {
335
        Color dfltColor = getDefaultTextColor(); // need to be here to init
336
        textToFindEditor.setForeground(
337
                valid ? dfltColor : UiUtils.getErrorTextColor());
338
    }
339
340
    private Color getDefaultTextColor() {
341
        if (defaultTextColor == null) {
342
            defaultTextColor = component.getForeground();
343
        }
344
        return defaultTextColor;
345
    }
346
347
    private static class ModelItem {
348
349
        final SearchPattern sp;
350
351
        public ModelItem(SearchPattern sp) {
352
            this.sp = sp;
353
        }
354
355
        @Override
356
        public String toString() {
357
            return sp.getSearchExpression();
358
        }
359
    }
360
}
(-)a/api.search/src/org/netbeans/modules/search/BasicSearchForm.java (-99 / +35 lines)
Lines 52-58 Link Here
52
import java.awt.event.ActionListener;
52
import java.awt.event.ActionListener;
53
import java.awt.event.ItemEvent;
53
import java.awt.event.ItemEvent;
54
import java.awt.event.ItemListener;
54
import java.awt.event.ItemListener;
55
import java.util.ArrayList;
56
import java.util.List;
55
import java.util.List;
57
import java.util.logging.Logger;
56
import java.util.logging.Logger;
58
import javax.swing.*;
57
import javax.swing.*;
Lines 66-71 Link Here
66
import org.netbeans.api.search.ui.FileNameController;
65
import org.netbeans.api.search.ui.FileNameController;
67
import org.netbeans.api.search.ui.ScopeController;
66
import org.netbeans.api.search.ui.ScopeController;
68
import org.netbeans.api.search.ui.ScopeOptionsController;
67
import org.netbeans.api.search.ui.ScopeOptionsController;
68
import org.netbeans.api.search.ui.SearchPatternController;
69
import org.netbeans.api.search.ui.SearchPatternController.Option;
69
import org.netbeans.modules.search.ui.CheckBoxWithButtonPanel;
70
import org.netbeans.modules.search.ui.CheckBoxWithButtonPanel;
70
import org.netbeans.modules.search.ui.FormLayoutHelper;
71
import org.netbeans.modules.search.ui.FormLayoutHelper;
71
import org.netbeans.modules.search.ui.PatternChangeListener;
72
import org.netbeans.modules.search.ui.PatternChangeListener;
Lines 125-131 Link Here
125
        } else {
126
        } else {
126
            initValuesFromHistory(searchAndReplace);
127
            initValuesFromHistory(searchAndReplace);
127
        }
128
        }
128
        updateTextPatternColor();
129
        if (searchAndReplace) {
129
        if (searchAndReplace) {
130
            updateReplacePatternColor();
130
            updateReplacePatternColor();
131
        }
131
        }
Lines 144-156 Link Here
144
                if (recentPane != null) {
144
                if (recentPane != null) {
145
                    String initSearchText = recentPane.getSelectedText();
145
                    String initSearchText = recentPane.getSelectedText();
146
                    if (initSearchText != null) {
146
                    if (initSearchText != null) {
147
                        cboxTextToFind.setSelectedIndex(-1);
147
                        cboxTextToFind.setSearchPattern(SearchPattern.create(
148
                        textToFindEditor.setText(initSearchText);
148
                                initSearchText, false, false, false));
149
                        searchCriteria.setTextPattern(initSearchText);
149
                        searchCriteria.setTextPattern(initSearchText);
150
                        return;
150
                    }
151
                    }
151
                }
152
                }
152
            }
153
            }
153
        }
154
        }
155
        searchCriteria.setTextPattern(
156
                cboxTextToFind.getSearchPattern().getSearchExpression());
154
    }
157
    }
155
    
158
    
156
    /** This method is called from within the constructor to
159
    /** This method is called from within the constructor to
Lines 165-173 Link Here
165
    private void initComponents(final boolean searchAndReplace) {
168
    private void initComponents(final boolean searchAndReplace) {
166
169
167
        lblTextToFind = new JLabel();
170
        lblTextToFind = new JLabel();
168
        cboxTextToFind = new JComboBox();        
171
        cboxTextToFind = ComponentUtils.adjustComboForSearchPattern(new JComboBox());
169
        cboxTextToFind.setEditable(true);
172
        lblTextToFind.setLabelFor(cboxTextToFind.getComponent());
170
        lblTextToFind.setLabelFor(cboxTextToFind);
171
        btnTestTextToFind = new JButton();
173
        btnTestTextToFind = new JButton();
172
174
173
        if (searchAndReplace) {
175
        if (searchAndReplace) {
Lines 202-209 Link Here
202
204
203
        /* find the editor components of combo-boxes: */
205
        /* find the editor components of combo-boxes: */
204
        Component cboxEditorComp;
206
        Component cboxEditorComp;
205
        cboxEditorComp = cboxTextToFind.getEditor().getEditorComponent();
206
        textToFindEditor = (JTextComponent) cboxEditorComp;
207
        if (cboxReplacement != null) {
207
        if (cboxReplacement != null) {
208
            cboxEditorComp = cboxReplacement.getEditor().getEditorComponent();
208
            cboxEditorComp = cboxReplacement.getEditor().getEditorComponent();
209
            replacementPatternEditor = (JTextComponent) cboxEditorComp;
209
            replacementPatternEditor = (JTextComponent) cboxEditorComp;
Lines 217-223 Link Here
217
    protected void initFormPanel(boolean searchAndReplace) {
217
    protected void initFormPanel(boolean searchAndReplace) {
218
218
219
        formPanel = new SearchFormPanel();
219
        formPanel = new SearchFormPanel();
220
        formPanel.addRow(lblTextToFind, cboxTextToFind);
220
        formPanel.addRow(lblTextToFind, cboxTextToFind.getComponent());
221
        initContainingTextOptionsRow(searchAndReplace);
221
        initContainingTextOptionsRow(searchAndReplace);
222
        if (searchAndReplace) {
222
        if (searchAndReplace) {
223
            formPanel.addRow(lblReplacement, cboxReplacement);
223
            formPanel.addRow(lblReplacement, cboxReplacement);
Lines 294-308 Link Here
294
     */
294
     */
295
    private void initValuesFromCriteria(BasicSearchCriteria initialCriteria,
295
    private void initValuesFromCriteria(BasicSearchCriteria initialCriteria,
296
            boolean searchAndReplace) {
296
            boolean searchAndReplace) {
297
        cboxTextToFind.setSelectedItem(initialCriteria.getTextPatternExpr());
297
        cboxTextToFind.setSearchPattern(initialCriteria.getSearchPattern());
298
        if (cboxReplacement != null) {
298
        if (cboxReplacement != null) {
299
            cboxReplacement.setSelectedItem(initialCriteria.getReplaceExpr());
299
            cboxReplacement.setSelectedItem(initialCriteria.getReplaceExpr());
300
        }
300
        }
301
301
302
        selectChk(chkPreserveCase, initialCriteria.isPreserveCase());
302
        selectChk(chkPreserveCase, initialCriteria.isPreserveCase());
303
        chkWholeWords.setSelected(initialCriteria.isWholeWords());
304
        chkCaseSensitive.setSelected(initialCriteria.isCaseSensitive());
305
        chkRegexp.setSelected(initialCriteria.isRegexp());
306
        scopeSettingsPanel.setFileNameRegexp(initialCriteria.isFileNameRegexp());
303
        scopeSettingsPanel.setFileNameRegexp(initialCriteria.isFileNameRegexp());
307
        scopeSettingsPanel.setUseIgnoreList(initialCriteria.isUseIgnoreList());
304
        scopeSettingsPanel.setUseIgnoreList(initialCriteria.isUseIgnoreList());
308
        cboxFileNamePattern.setRegularExpression(initialCriteria.isFileNameRegexp());
305
        cboxFileNamePattern.setRegularExpression(initialCriteria.isFileNameRegexp());
Lines 328-348 Link Here
328
        
325
        
329
326
330
        final TextFieldFocusListener focusListener = new TextFieldFocusListener();
327
        final TextFieldFocusListener focusListener = new TextFieldFocusListener();
331
        textToFindEditor.addFocusListener(focusListener);
332
        if (replacementPatternEditor != null) {
328
        if (replacementPatternEditor != null) {
333
            replacementPatternEditor.addFocusListener(focusListener);
329
            replacementPatternEditor.addFocusListener(focusListener);
334
        }
330
        }
335
331
336
        textToFindEditor.getDocument().addDocumentListener(
337
                new TextToFindChangeListener());
338
        if (replacementPatternEditor != null) {
332
        if (replacementPatternEditor != null) {
339
            replacementPatternEditor.getDocument().addDocumentListener(
333
            replacementPatternEditor.getDocument().addDocumentListener(
340
                    new ReplacementPatternListener());
334
                    new ReplacementPatternListener());
341
        }
335
        }
342
        
336
        
343
        chkRegexp.addItemListener(this);
337
        chkRegexp.addItemListener(this);
344
        chkCaseSensitive.addItemListener(this);
338
        cboxTextToFind.bind(Option.REGULAR_EXPRESSION, chkRegexp);
345
        chkWholeWords.addItemListener(this);
339
        cboxTextToFind.bind(Option.MATCH_CASE, chkCaseSensitive);
340
        cboxTextToFind.bind(Option.WHOLE_WORDS, chkWholeWords);
346
341
347
        boolean regexp = chkRegexp.isSelected();
342
        boolean regexp = chkRegexp.isSelected();
348
        boolean caseSensitive = chkCaseSensitive.isSelected();
343
        boolean caseSensitive = chkCaseSensitive.isSelected();
Lines 376-381 Link Here
376
                        cboxFileNamePattern.isRegularExpression());
371
                        cboxFileNamePattern.isRegularExpression());
377
            }
372
            }
378
        });
373
        });
374
375
        cboxTextToFind.addChangeListener(new ChangeListener() {
376
            @Override
377
            public void stateChanged(ChangeEvent e) {
378
                SearchPattern sp = cboxTextToFind.getSearchPattern();
379
                searchCriteria.setTextPattern(sp.getSearchExpression());
380
                searchCriteria.setRegexp(sp.isRegExp());
381
                searchCriteria.setWholeWords(sp.isWholeWords());
382
                searchCriteria.setCaseSensitive(sp.isMatchCase());
383
            }
384
        });
379
        initButtonInteraction();
385
        initButtonInteraction();
380
    }
386
    }
381
387
Lines 392-399 Link Here
392
398
393
    private void openTextPatternSandbox() {
399
    private void openTextPatternSandbox() {
394
400
395
        String expr = cboxTextToFind.getSelectedItem() == null ? "" // NOI18N
401
        SearchPattern sp = cboxTextToFind.getSearchPattern();
396
                : cboxTextToFind.getSelectedItem().toString();
402
        String expr = sp.getSearchExpression() == null ? "" // NOI18N
403
                : sp.getSearchExpression();
397
        boolean matchCase = chkCaseSensitive.isSelected();
404
        boolean matchCase = chkCaseSensitive.isSelected();
398
405
399
        PatternSandbox.openDialog(new PatternSandbox.TextPatternSandbox(
406
        PatternSandbox.openDialog(new PatternSandbox.TextPatternSandbox(
Lines 401-408 Link Here
401
408
402
            @Override
409
            @Override
403
            protected void onApply(String newExpr, boolean newMatchCase) {
410
            protected void onApply(String newExpr, boolean newMatchCase) {
404
                cboxTextToFind.setSelectedItem(newExpr);
411
                cboxTextToFind.setSearchPattern(SearchPattern.create(
405
                chkCaseSensitive.setSelected(newMatchCase);
412
                        newExpr, false, newMatchCase, true));
406
            }
413
            }
407
        }, btnTestTextToFind);
414
        }, btnTestTextToFind);
408
    }
415
    }
Lines 412-426 Link Here
412
     * expressions. The combo-boxes' text-fields remain empty.
419
     * expressions. The combo-boxes' text-fields remain empty.
413
     */
420
     */
414
    private void initHistory() {
421
    private void initHistory() {
415
        final List<SearchPattern> patterns
416
                = SearchHistory.getDefault().getSearchPatterns();
417
        if (!patterns.isEmpty()) {
418
            List<String> itemsList = new ArrayList<String>(patterns.size());
419
            for (SearchPattern pattern : patterns) {
420
                itemsList.add(pattern.getSearchExpression());
421
            }
422
            cboxTextToFind.setModel(new ListComboBoxModel(itemsList));
423
        }
424
422
425
        FindDialogMemory memory = FindDialogMemory.getDefault();
423
        FindDialogMemory memory = FindDialogMemory.getDefault();
426
        List<String> entries;
424
        List<String> entries;
Lines 438-447 Link Here
438
    private void initValuesFromHistory(final boolean searchAndReplace) {
436
    private void initValuesFromHistory(final boolean searchAndReplace) {
439
        final FindDialogMemory memory = FindDialogMemory.getDefault();
437
        final FindDialogMemory memory = FindDialogMemory.getDefault();
440
438
441
        if (memory.isTextPatternSpecified()
442
                && (cboxTextToFind.getItemCount() != 0)) {
443
            cboxTextToFind.setSelectedIndex(0);
444
        }
445
        if (memory.isFileNamePatternSpecified()
439
        if (memory.isFileNamePatternSpecified()
446
                && cboxFileNamePattern.getComponent().getItemCount() != 0) {
440
                && cboxFileNamePattern.getComponent().getItemCount() != 0) {
447
            cboxFileNamePattern.getComponent().setSelectedIndex(0);
441
            cboxFileNamePattern.getComponent().setSelectedIndex(0);
Lines 467-506 Link Here
467
    
461
    
468
    @Override
462
    @Override
469
    public boolean requestFocusInWindow() {
463
    public boolean requestFocusInWindow() {
470
        assert textToFindEditor != null;
464
	return cboxTextToFind.requestFocusInWindow();
471
        
472
	if (textToFindEditor.isFocusOwner()) {
473
            return true;
474
	}
475
476
        int textLength = textToFindEditor.getText().length();
477
        if (textLength > 0) {
478
            textToFindEditor.setCaretPosition(0);
479
            textToFindEditor.moveCaretPosition(textLength);
480
        }
481
482
        return textToFindEditor.requestFocusInWindow();
483
    }
484
485
    /**
486
     * Sets proper color of text pattern.
487
     */
488
    private void updateTextPatternColor() {
489
        boolean wasInvalid = invalidTextPattern;
490
        invalidTextPattern = searchCriteria.isTextPatternInvalid();
491
        if (invalidTextPattern != wasInvalid) {
492
            Color dfltColor = getDefaultTextColor(); // need to be here to init
493
            textToFindEditor.setForeground(
494
                    invalidTextPattern ? getErrorTextColor()
495
                    : dfltColor);
496
        }
497
    }
498
499
    private Color getDefaultTextColor() {
500
        if (defaultTextColor == null) {
501
            defaultTextColor = textToFindEditor.getForeground();
502
        }
503
        return defaultTextColor;
504
    }
465
    }
505
466
506
    /**
467
    /**
Lines 512-518 Link Here
512
        if (invalidReplacePattern != wasInvalid) {
473
        if (invalidReplacePattern != wasInvalid) {
513
            if (defaultTextColor == null) {
474
            if (defaultTextColor == null) {
514
                assert !wasInvalid;
475
                assert !wasInvalid;
515
                defaultTextColor = textToFindEditor.getForeground();
476
                defaultTextColor = cboxReplacement.getForeground();
516
            }
477
            }
517
            replacementPatternEditor.setForeground(
478
            replacementPatternEditor.setForeground(
518
                    invalidReplacePattern ? getErrorTextColor()
479
                    invalidReplacePattern ? getErrorTextColor()
Lines 573-588 Link Here
573
        final ItemSelectable toggle = e.getItemSelectable();
534
        final ItemSelectable toggle = e.getItemSelectable();
574
        final boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
535
        final boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
575
        if (toggle == chkRegexp) {
536
        if (toggle == chkRegexp) {
576
            searchCriteria.setRegexp(selected);
577
            updateTextPatternColor();
578
            if (cboxReplacement != null){
537
            if (cboxReplacement != null){
579
                updateReplacePatternColor();
538
                updateReplacePatternColor();
580
            }
539
            }
581
            setTextToFindToolTip();
540
            setTextToFindToolTip();
582
        } else if (toggle == chkCaseSensitive) {
583
            searchCriteria.setCaseSensitive(selected);
584
        } else if (toggle == chkWholeWords) {
585
            searchCriteria.setWholeWords(selected);
586
        } else if (toggle == chkPreserveCase) {
541
        } else if (toggle == chkPreserveCase) {
587
            searchCriteria.setPreserveCase(selected);
542
            searchCriteria.setPreserveCase(selected);
588
        } else {
543
        } else {
Lines 598-604 Link Here
598
            t = UiUtils.getText(
553
            t = UiUtils.getText(
599
                    "BasicSearchForm.cboxTextToFind.tooltip");          //NOI18N
554
                    "BasicSearchForm.cboxTextToFind.tooltip");          //NOI18N
600
        }
555
        }
601
        cboxTextToFind.setToolTipText(t);
556
        cboxTextToFind.getComponent().setToolTipText(t);
602
    }
557
    }
603
558
604
    /**
559
    /**
Lines 653-662 Link Here
653
     * * search string is empty, meaning that the dialog is empty.
608
     * * search string is empty, meaning that the dialog is empty.
654
     */
609
     */
655
    private SearchPattern getCurrentSearchPattern() {
610
    private SearchPattern getCurrentSearchPattern() {
656
        return SearchPattern.create(textToFindEditor.getText(),
611
        return cboxTextToFind.getSearchPattern();
657
                                    chkWholeWords.isSelected(),
658
                                    chkCaseSensitive.isSelected(),
659
                                    chkRegexp.isSelected());
660
    }
612
    }
661
613
662
    /**
614
    /**
Lines 714-727 Link Here
714
    private static final Logger watcherLogger = Logger.getLogger(
666
    private static final Logger watcherLogger = Logger.getLogger(
715
            "org.netbeans.modules.search.BasicSearchForm.FileNamePatternWatcher");//NOI18N
667
            "org.netbeans.modules.search.BasicSearchForm.FileNamePatternWatcher");//NOI18N
716
668
717
    private JComboBox cboxTextToFind;
669
    private SearchPatternController cboxTextToFind;
718
    private JComboBox cboxReplacement;
670
    private JComboBox cboxReplacement;
719
    private FileNameController cboxFileNamePattern;
671
    private FileNameController cboxFileNamePattern;
720
    private JCheckBox chkWholeWords;
672
    private JCheckBox chkWholeWords;
721
    private JCheckBox chkCaseSensitive;
673
    private JCheckBox chkCaseSensitive;
722
    private JCheckBox chkRegexp;
674
    private JCheckBox chkRegexp;
723
    private JCheckBox chkPreserveCase;
675
    private JCheckBox chkPreserveCase;
724
    private JTextComponent textToFindEditor;
725
    private JTextComponent replacementPatternEditor;
676
    private JTextComponent replacementPatternEditor;
726
    protected SearchFormPanel formPanel;
677
    protected SearchFormPanel formPanel;
727
    private JButton btnTestTextToFind;
678
    private JButton btnTestTextToFind;
Lines 850-870 Link Here
850
        }
801
        }
851
    }
802
    }
852
803
853
    private class TextToFindChangeListener extends PatternChangeListener {
854
855
        public TextToFindChangeListener() {
856
        }
857
858
        @Override
859
        public void handleComboBoxChange(String text) {
860
            searchCriteria.setTextPattern(text);
861
            updateTextPatternColor();
862
            if (cboxReplacement != null) {
863
                updateReplacePatternColor();
864
            }
865
        }
866
    }
867
868
    private class ReplacementPatternListener extends PatternChangeListener {
804
    private class ReplacementPatternListener extends PatternChangeListener {
869
805
870
        public ReplacementPatternListener() {
806
        public ReplacementPatternListener() {
(-)a/api.search/src/org/netbeans/modules/search/FindDialogMemory.java (-3 / +3 lines)
Lines 257-263 Link Here
257
     * 
257
     * 
258
     * @param  pattern  pattern to be stored
258
     * @param  pattern  pattern to be stored
259
     */
259
     */
260
    void storeFileNamePattern(String pattern) {
260
    public void storeFileNamePattern(String pattern) {
261
        int index = fileNamePatterns.indexOf(pattern);
261
        int index = fileNamePatterns.indexOf(pattern);
262
        if (index != -1) {
262
        if (index != -1) {
263
            if (index == fileNamePatterns.size() - 1) {
263
            if (index == fileNamePatterns.size() - 1) {
Lines 370-376 Link Here
370
        prefs.put(PROP_SCOPE_TYPE_ID, scopeTypeId);
370
        prefs.put(PROP_SCOPE_TYPE_ID, scopeTypeId);
371
    }
371
    }
372
372
373
    boolean isTextPatternSpecified() {
373
    public boolean isTextPatternSpecified() {
374
        return textPatternSpecified;
374
        return textPatternSpecified;
375
    }
375
    }
376
376
Lines 382-388 Link Here
382
        return fileNamePatternSpecified;
382
        return fileNamePatternSpecified;
383
    }
383
    }
384
384
385
    void setFileNamePatternSpecified(boolean specified) {
385
    public void setFileNamePatternSpecified(boolean specified) {
386
        fileNamePatternSpecified = specified;
386
        fileNamePatternSpecified = specified;
387
        prefs.putBoolean(PROP_FILENAME_PATTERN_SPECIFIED, specified);
387
        prefs.putBoolean(PROP_FILENAME_PATTERN_SPECIFIED, specified);
388
    }
388
    }
(-)a/api.search/src/org/netbeans/modules/search/ui/AbstractSearchResultsPanel.java (-22 / +416 lines)
Lines 44-62 Link Here
44
import java.awt.Dimension;
44
import java.awt.Dimension;
45
import java.awt.event.ActionEvent;
45
import java.awt.event.ActionEvent;
46
import java.awt.event.ActionListener;
46
import java.awt.event.ActionListener;
47
import java.awt.event.HierarchyEvent;
47
import java.beans.PropertyChangeEvent;
48
import java.awt.event.HierarchyListener;
48
import java.beans.PropertyChangeListener;
49
import java.beans.PropertyVetoException;
50
import java.util.LinkedList;
51
import java.util.List;
52
import java.util.MissingResourceException;
49
import javax.swing.AbstractButton;
53
import javax.swing.AbstractButton;
54
import javax.swing.ActionMap;
50
import javax.swing.JButton;
55
import javax.swing.JButton;
51
import javax.swing.JPanel;
56
import javax.swing.JPanel;
57
import javax.swing.JToggleButton;
52
import javax.swing.JToolBar;
58
import javax.swing.JToolBar;
53
import org.netbeans.api.search.SearchControl;
59
import org.netbeans.api.search.SearchControl;
54
import org.netbeans.modules.search.ResultView;
60
import org.netbeans.modules.search.ResultView;
55
import org.netbeans.spi.search.provider.SearchComposition;
61
import org.netbeans.spi.search.provider.SearchComposition;
56
import org.netbeans.spi.search.provider.SearchProvider;
62
import org.netbeans.spi.search.provider.SearchProvider;
57
import org.netbeans.spi.search.provider.SearchProvider.Presenter;
63
import org.netbeans.spi.search.provider.SearchProvider.Presenter;
64
import org.netbeans.swing.outline.Outline;
58
import org.openide.explorer.ExplorerManager;
65
import org.openide.explorer.ExplorerManager;
59
import org.openide.explorer.ExplorerUtils;
66
import org.openide.explorer.ExplorerUtils;
67
import org.openide.explorer.view.OutlineView;
68
import org.openide.explorer.view.Visualizer;
69
import org.openide.nodes.AbstractNode;
70
import org.openide.nodes.Children;
71
import org.openide.nodes.Node;
72
import org.openide.nodes.NodeAdapter;
73
import org.openide.nodes.NodeMemberEvent;
74
import org.openide.nodes.NodeReorderEvent;
75
import org.openide.util.Exceptions;
60
import org.openide.util.ImageUtilities;
76
import org.openide.util.ImageUtilities;
61
import org.openide.util.Lookup;
77
import org.openide.util.Lookup;
62
import org.openide.util.Mutex;
78
import org.openide.util.Mutex;
Lines 73-82 Link Here
73
            "org/netbeans/modules/search/res/refresh.png";              //NOI18N
89
            "org/netbeans/modules/search/res/refresh.png";              //NOI18N
74
    private static final String STOP_ICON =
90
    private static final String STOP_ICON =
75
            "org/netbeans/modules/search/res/stop.png";                 //NOI18N
91
            "org/netbeans/modules/search/res/stop.png";                 //NOI18N
92
    private static final String NEXT_ICON =
93
            "org/netbeans/modules/search/res/next.png";                 //NOI18N
94
    private static final String PREV_ICON =
95
            "org/netbeans/modules/search/res/prev.png";                 //NOI18N
96
    private static final String EXPAND_ICON =
97
            "org/netbeans/modules/search/res/expandTree.png";           //NOI18N
98
    private static final String COLLAPSE_ICON =
99
            "org/netbeans/modules/search/res/colapseTree.png";          //NOI18N
76
100
77
    private ExplorerManager explorerManager;
101
    private ExplorerManager explorerManager;
78
    private SearchComposition searchComposition;
102
    private SearchComposition searchComposition;
79
    protected JButton btnStopRefresh = new JButton();
103
    protected JButton btnStopRefresh = new JButton();
104
    protected JButton btnPrev = new JButton();
105
    protected JButton btnNext = new JButton();
106
    protected JToggleButton btnExpand = new JToggleButton();
80
    private final Presenter searchProviderPresenter;
107
    private final Presenter searchProviderPresenter;
81
    private Lookup lookup;
108
    private Lookup lookup;
82
    private volatile boolean btnStopRefreshInRefreshMode = false;
109
    private volatile boolean btnStopRefreshInRefreshMode = false;
Lines 92-106 Link Here
92
        explorerManager = new ExplorerManager();
119
        explorerManager = new ExplorerManager();
93
        lookup = ExplorerUtils.createLookup(explorerManager,
120
        lookup = ExplorerUtils.createLookup(explorerManager,
94
                ResultView.getInstance().getActionMap());
121
                ResultView.getInstance().getActionMap());
95
        this.addHierarchyListener(new HierarchyListener() {
122
        initActions();
96
            @Override
123
        initToolbar();
97
            public synchronized void hierarchyChanged(HierarchyEvent e) {
124
        initSelectionListeners();
98
                if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
99
                    removeHierarchyListener(this);
100
                    initToolbar();
101
                }
102
            }
103
        });
104
    }
125
    }
105
126
106
    /**
127
    /**
Lines 156-161 Link Here
156
        toolBar.setRollover(true);
177
        toolBar.setRollover(true);
157
        toolBar.setFloatable(false);
178
        toolBar.setFloatable(false);
158
179
180
        initStopRefreshButton();
181
        toolBar.add(btnStopRefresh);
182
        initPrevButton();
183
        toolBar.add(btnPrev);
184
        initNextButton();
185
        toolBar.add(btnNext);
186
        initExpandButton();
187
        toolBar.add(btnExpand);
188
    }
189
190
    private void initStopRefreshButton() throws MissingResourceException {
159
        sizeButton(btnStopRefresh);
191
        sizeButton(btnStopRefresh);
160
        btnStopRefresh.addActionListener(new ActionListener() {
192
        btnStopRefresh.addActionListener(new ActionListener() {
161
            @Override
193
            @Override
Lines 167-187 Link Here
167
                }
199
                }
168
            }
200
            }
169
        });
201
        });
170
171
        btnStopRefresh.setToolTipText(
202
        btnStopRefresh.setToolTipText(
172
                UiUtils.getText("TEXT_BUTTON_STOP"));                   //NOI18N
203
                UiUtils.getText("TEXT_BUTTON_STOP"));                   //NOI18N
173
        btnStopRefresh.setIcon(ImageUtilities.loadImageIcon(STOP_ICON, true));
204
        btnStopRefresh.setIcon(ImageUtilities.loadImageIcon(STOP_ICON, true));
174
175
        toolBar.add(btnStopRefresh);
176
        for (AbstractButton b : createButtons()) {
177
            sizeButton(b);
178
            toolBar.add(b);
179
        }
180
        btnStopRefresh.getAccessibleContext().setAccessibleDescription(
205
        btnStopRefresh.getAccessibleContext().setAccessibleDescription(
181
                NbBundle.getMessage(ResultView.class,
206
                NbBundle.getMessage(ResultView.class,
182
                "ACS_TEXT_BUTTON_STOP"));                               //NOI18N
207
                "ACS_TEXT_BUTTON_STOP"));                               //NOI18N
183
    }
208
    }
184
209
210
    private void initExpandButton() {
211
        sizeButton(btnExpand);
212
        btnExpand.setIcon(ImageUtilities.loadImageIcon(EXPAND_ICON, true));
213
        btnExpand.setSelectedIcon(ImageUtilities.loadImageIcon(
214
                COLLAPSE_ICON, true));
215
        btnExpand.setToolTipText(UiUtils.getText(
216
                "TEXT_BUTTON_EXPAND"));                                 //NOI18N
217
        btnExpand.setEnabled(false);
218
        btnExpand.setSelected(false);
219
    }
220
221
    private void initNextButton() {
222
        sizeButton(btnNext);
223
        btnNext.setIcon(ImageUtilities.loadImageIcon(NEXT_ICON, true));
224
        btnNext.setToolTipText(UiUtils.getText(
225
                "TEXT_BUTTON_NEXT_MATCH"));                             //NOI18N
226
        btnNext.setEnabled(false);
227
        btnNext.addActionListener(new ActionListener() {
228
            @Override
229
            public void actionPerformed(ActionEvent e) {
230
                shift(1);
231
            }
232
        });
233
    }
234
235
    private void initPrevButton() {
236
        sizeButton(btnPrev);
237
        btnPrev.setIcon(ImageUtilities.loadImageIcon(PREV_ICON, true));
238
        btnPrev.setToolTipText(UiUtils.getText(
239
                "TEXT_BUTTON_PREV_MATCH"));                             //NOI18N
240
        btnPrev.setEnabled(false);
241
        btnPrev.addActionListener(new ActionListener() {
242
            @Override
243
            public void actionPerformed(ActionEvent e) {
244
                shift(-1);
245
            }
246
        });
247
    }
248
185
    protected void sizeButton(AbstractButton button) {
249
    protected void sizeButton(AbstractButton button) {
186
        Dimension dim = new Dimension(24, 24);
250
        Dimension dim = new Dimension(24, 24);
187
        button.setMinimumSize(dim);
251
        button.setMinimumSize(dim);
Lines 233-244 Link Here
233
        }
297
        }
234
    }
298
    }
235
299
300
    protected void addButton(AbstractButton button) {
301
        toolBar.add(button);
302
    }
303
304
    static class RootNode extends AbstractNode {
305
306
        Node resultsNode;
307
        Node infoNode;
308
309
        public RootNode(Node resultsNode, Node infoNode) {
310
            this(resultsNode, infoNode,
311
                    new RootNodeChildren(resultsNode, infoNode));
312
        }
313
314
        private RootNode(Node resultsNode, Node infoNode,
315
                final RootNodeChildren rootNodeChildren) {
316
            super(rootNodeChildren);
317
            this.infoNode = infoNode;
318
            this.resultsNode = resultsNode;
319
            if (infoNode != null) {
320
                setInfoNodeListener(rootNodeChildren);
321
            }
322
        }
323
324
        private void setInfoNodeListener(
325
                final RootNodeChildren rootNodeChildren) {
326
327
            assert infoNode != null;
328
329
            infoNode.getChildren().getNodes(true);
330
            infoNode.addNodeListener(new NodeAdapter() {
331
                private boolean added = false;
332
333
                @Override
334
                public synchronized void childrenAdded(NodeMemberEvent ev) {
335
                    if (!added) {
336
                        rootNodeChildren.showInfoNode();
337
                        infoNode.removeNodeListener(this);
338
                        added = true;
339
                    }
340
                }
341
342
                @Override
343
                public void propertyChange(PropertyChangeEvent ev) {
344
                    super.propertyChange(ev);
345
                }
346
347
                @Override
348
                public void childrenReordered(NodeReorderEvent ev) {
349
                    super.childrenReordered(ev);
350
                }
351
            });
352
        }
353
    }
354
355
    private static class RootNodeChildren extends Children.Keys<Node> {
356
357
        private Node[] standard;
358
        private Node[] withInfo;
359
        private boolean infoNodeShown = false;
360
361
        public RootNodeChildren(Node resultsNode, Node infoNode) {
362
            standard = new Node[] {resultsNode};
363
            withInfo = new Node[] {resultsNode, infoNode};
364
            setKeys(standard);
365
        }
366
367
        private synchronized void showInfoNode() {
368
            if (!infoNodeShown) {
369
                setKeys(withInfo);
370
                infoNodeShown = true;
371
            }
372
        }
373
374
        @Override
375
        protected Node[] createNodes(Node key) {
376
            return new Node[]{key};
377
        }
378
    }
379
380
    protected void toggleExpand(Node root, boolean expand) {
381
        if (expand) {
382
            getOutlineView().expandNode(root);
383
        }
384
        for (Node n : root.getChildren().getNodes()) {
385
            toggleExpand(n, expand);
386
        }
387
        if (!expand) {
388
            getOutlineView().collapseNode(root);
389
        }
390
    }
391
392
    protected abstract OutlineView getOutlineView();
393
394
    private void initActions() {
395
        ActionMap map = getActionMap();
396
        map.put("jumpNext", new PrevNextAction(1));                    // NOI18N
397
        map.put("jumpPrev", new PrevNextAction(-1));                   // NOI18N
398
    }
399
400
    private void initSelectionListeners() {
401
        getExplorerManager().addPropertyChangeListener(
402
                new PropertyChangeListener() {
403
                    @Override
404
                    public void propertyChange(PropertyChangeEvent evt) {
405
                        if (evt.getPropertyName().equals(
406
                                "selectedNodes")) {                     //NOI18N
407
                            updateShiftButtons();
408
                        }
409
                    }
410
                });
411
    }
412
413
    protected void updateShiftButtons() {
414
        if (btnPrev.isVisible() && btnNext.isVisible()) {
415
            btnPrev.setEnabled(
416
                    findShiftNode(-1, getOutlineView(), false) != null);
417
            btnNext.setEnabled(
418
                    findShiftNode(1, getOutlineView(), false) != null);
419
        }
420
    }
421
422
    private void shift(int direction) {
423
424
        Node next = findShiftNode(direction, getOutlineView(), true);
425
        if (next == null) {
426
            return;
427
        } else {
428
            try {
429
                getExplorerManager().setSelectedNodes(new Node[]{next});
430
                onDetailShift(next);
431
            } catch (PropertyVetoException pve) {
432
                Exceptions.printStackTrace(pve);
433
            }
434
        }
435
    }
436
236
    /**
437
    /**
237
     * Create custom set of buttons that will be resized and placed between
438
     * Should be called after a matching node was added to update state of
238
     * "Refresh" and "Stop" buttons.
439
     * buttons.
239
     */
440
     */
240
    protected AbstractButton[] createButtons() {
441
    protected void afterMatchingNodeAdded() {
241
        return new AbstractButton[] {};
442
        Mutex.EVENT.writeAccess(new Runnable() {
443
            @Override
444
            public void run() {
445
                if (btnNext.isVisible() && !btnNext.isEnabled()) {
446
                    updateShiftButtons();
447
                }
448
            }
449
        });
450
    }
451
452
    /**
453
     * Called when a node is selected by clicking Next or Previous button.
454
     */
455
    protected void onDetailShift(Node n) {
456
    }
457
458
    private Node findShiftNode(int direction, OutlineView outlineView,
459
            boolean canExpand) {
460
        Node[] selected = getExplorerManager().getSelectedNodes();
461
        Node n = null;
462
        if ((selected == null || selected.length == 0)
463
                /* TODO && getExplorerManager().getRootContext() == resultsOutlineSupport.getRootNode() */) {
464
            n = getExplorerManager().getRootContext();
465
        } else if (selected.length == 1) {
466
            n = selected[0];
467
        }
468
        return n == null ? null : findDetailNode(n, direction, outlineView,
469
                canExpand);
470
    }
471
472
    Node findDetailNode(Node fromNode, int direction,
473
            OutlineView outlineView, boolean canExpand) {
474
        return findUp(fromNode, direction,
475
                isDetailNode(fromNode) || direction < 0 ? direction : 0,
476
                outlineView, canExpand);
477
    }
478
479
    /**
480
     * Start finding for next or previous occurance, from a node or its previous
481
     * or next sibling of node {@code node}
482
     *
483
     * @param node reference node
484
     * @param offset 0 to start from node {@code node}, 1 to start from its next
485
     * sibling, -1 to start from its previous sibling.
486
     * @param dir Direction: 1 for next, -1 for previous.
487
     */
488
    Node findUp(Node node, int dir, int offset, OutlineView outlineView,
489
            boolean canExpand) {
490
        if (node == null) {
491
            return null;
492
        }
493
        Node parent = node.getParentNode();
494
        Node[] siblings;
495
        if (parent == null) {
496
            siblings = new Node[]{node};
497
        } else {
498
            siblings = getChildren(parent, outlineView, canExpand);
499
        }
500
        int nodeIndex = findChildIndex(node, siblings);
501
        if (nodeIndex + offset < 0 || nodeIndex + offset >= siblings.length) {
502
            return findUp(parent, dir, dir, outlineView, canExpand);
503
        }
504
        for (int i = nodeIndex + offset;
505
                i >= 0 && i < siblings.length; i += dir) {
506
            Node found = findDown(siblings[i], siblings, i, dir, outlineView,
507
                    canExpand);
508
            return found;
509
        }
510
        return findUp(parent, dir, offset, outlineView, canExpand);
511
    }
512
513
    /**
514
     * Find Depth-first search to find a detail node in the subtree.
515
     */
516
    private Node findDown(Node node, Node[] siblings, int nodeIndex,
517
            int dir, OutlineView outlineView, boolean canExpand) {
518
519
        Node[] children = getChildren(node, outlineView, canExpand);
520
        for (int i = dir > 0 ? 0 : children.length - 1;
521
                i >= 0 && i < children.length; i += dir) {
522
            Node found = findDown(children[i], children, i, dir, outlineView,
523
                    canExpand);
524
            if (found != null) {
525
                return found;
526
            }
527
        }
528
        for (int i = nodeIndex; i >= 0 && i < siblings.length; i += dir) {
529
            if (isDetailNode(siblings[i])) {
530
                return siblings[i];
531
            }
532
        }
533
        return null;
534
    }
535
536
    protected abstract boolean isDetailNode(Node n);
537
538
    private static int findChildIndex(Node selectedNode, Node[] siblings) {
539
        int pos = -1;
540
        for (int i = 0; i < siblings.length; i++) {
541
            if (siblings[i] == selectedNode) {
542
                pos = i;
543
                break;
544
            }
545
        }
546
        return pos;
547
    }
548
549
    private static Node[] getChildren(Node n, OutlineView outlineView,
550
            boolean canExpand) {
551
        if (outlineView != null) {
552
            if (!outlineView.isExpanded(n)) {
553
                if (canExpand) {
554
                    outlineView.expandNode(n);
555
                } else {
556
                    return n.getChildren().getNodes(true);
557
                }
558
            }
559
            return getChildrenInDisplayedOrder(n, outlineView);
560
        } else {
561
            return n.getChildren().getNodes(true);
562
        }
563
    }
564
565
    private static Node[] getChildrenInDisplayedOrder(Node parent,
566
            OutlineView outlineView) {
567
568
        Outline outline = outlineView.getOutline();
569
        Node[] unsortedChildren = parent.getChildren().getNodes(true);
570
        int rows = outlineView.getOutline().getRowCount();
571
        int start = findRowIndexInOutline(parent, outline, rows);
572
        if (start == -1) {
573
            return unsortedChildren;
574
        }
575
        List<Node> children = new LinkedList<Node>();
576
        for (int j = start + 1; j < rows; j++) {
577
            int childModelIndex = outline.convertRowIndexToModel(j);
578
            if (childModelIndex == -1) {
579
                continue;
580
            }
581
            Object childObject = outline.getModel().getValueAt(
582
                    childModelIndex, 0);
583
            Node childNode = Visualizer.findNode(childObject);
584
            if (childNode.getParentNode() == parent) {
585
                children.add(childNode);
586
            } else if (children.size() == unsortedChildren.length) {
587
                break;
588
            }
589
        }
590
        return children.toArray(new Node[children.size()]);
591
    }
592
593
    private static int findRowIndexInOutline(Node node, Outline outline,
594
            int rows) {
595
596
        int startRow = Math.max(outline.getSelectedRow(), 0);
597
        int offset = 0;
598
        while (startRow + offset < rows || startRow - offset >= 0) {
599
            int up = startRow + offset + 1;
600
            int down = startRow - offset;
601
602
            if (up < rows && testNodeInRow(outline, node, up)) {
603
                return up;
604
            } else if (down >= 0 && testNodeInRow(outline, node, down)) {
605
                return down;
606
            } else {
607
                offset++;
608
            }
609
        }
610
        return -1;
611
    }
612
613
    private static boolean testNodeInRow(Outline outline, Node node, int i) {
614
        int modelIndex = outline.convertRowIndexToModel(i);
615
        if (modelIndex != -1) {
616
            Object o = outline.getModel().getValueAt(modelIndex, 0);
617
            Node n = Visualizer.findNode(o);
618
            if (n == node) {
619
                return true;
620
            }
621
        }
622
        return false;
623
    }
624
625
    private final class PrevNextAction extends javax.swing.AbstractAction {
626
627
        private int direction;
628
629
        public PrevNextAction(int direction) {
630
            this.direction = direction;
631
        }
632
633
        public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
634
            shift(direction);
635
        }
242
    }
636
    }
243
637
244
    @Override
638
    @Override
(-)a/api.search/src/org/netbeans/modules/search/ui/BasicAbstractResultsPanel.java (-327 / +52 lines)
Lines 45-59 Link Here
45
import java.awt.EventQueue;
45
import java.awt.EventQueue;
46
import java.awt.event.ActionEvent;
46
import java.awt.event.ActionEvent;
47
import java.awt.event.ActionListener;
47
import java.awt.event.ActionListener;
48
import java.beans.PropertyChangeEvent;
49
import java.beans.PropertyChangeListener;
50
import java.beans.PropertyVetoException;
48
import java.beans.PropertyVetoException;
51
import java.util.LinkedList;
52
import java.util.List;
49
import java.util.List;
53
import java.util.ResourceBundle;
50
import java.util.ResourceBundle;
54
import javax.accessibility.AccessibleContext;
51
import javax.accessibility.AccessibleContext;
55
import javax.swing.AbstractButton;
56
import javax.swing.ActionMap;
57
import javax.swing.JButton;
52
import javax.swing.JButton;
58
import javax.swing.JToggleButton;
53
import javax.swing.JToggleButton;
59
import javax.swing.UIManager;
54
import javax.swing.UIManager;
Lines 66-80 Link Here
66
import org.netbeans.modules.search.ResultModel;
61
import org.netbeans.modules.search.ResultModel;
67
import org.netbeans.modules.search.ResultView;
62
import org.netbeans.modules.search.ResultView;
68
import org.netbeans.modules.search.TextDetail;
63
import org.netbeans.modules.search.TextDetail;
69
import org.netbeans.swing.outline.Outline;
70
import org.openide.explorer.view.OutlineView;
64
import org.openide.explorer.view.OutlineView;
71
import org.openide.explorer.view.Visualizer;
72
import org.openide.filesystems.FileObject;
65
import org.openide.filesystems.FileObject;
73
import org.openide.nodes.Node;
66
import org.openide.nodes.Node;
74
import org.openide.nodes.NodeAdapter;
67
import org.openide.nodes.NodeAdapter;
75
import org.openide.nodes.NodeListener;
68
import org.openide.nodes.NodeListener;
76
import org.openide.nodes.NodeMemberEvent;
69
import org.openide.nodes.NodeMemberEvent;
77
import org.openide.util.Exceptions;
78
import org.openide.util.ImageUtilities;
70
import org.openide.util.ImageUtilities;
79
import org.openide.util.NbBundle;
71
import org.openide.util.NbBundle;
80
72
Lines 85-98 Link Here
85
public abstract class BasicAbstractResultsPanel
77
public abstract class BasicAbstractResultsPanel
86
        extends AbstractSearchResultsPanel {
78
        extends AbstractSearchResultsPanel {
87
79
88
    private static final String NEXT_ICON =
89
            "org/netbeans/modules/search/res/next.png";                 //NOI18N
90
    private static final String PREV_ICON =
91
            "org/netbeans/modules/search/res/prev.png";                 //NOI18N
92
    private static final String EXPAND_ICON =
93
            "org/netbeans/modules/search/res/expandTree.png";           //NOI18N
94
    private static final String COLLAPSE_ICON =
95
            "org/netbeans/modules/search/res/colapseTree.png";          //NOI18N
96
    private static final String SHOW_DETAILS_ICON =
80
    private static final String SHOW_DETAILS_ICON =
97
            "org/netbeans/modules/search/res/search.gif";               //NOI18N
81
            "org/netbeans/modules/search/res/search.gif";               //NOI18N
98
    private static final String FOLDER_VIEW_ICON =
82
    private static final String FOLDER_VIEW_ICON =
Lines 102-112 Link Here
102
    private static final String MODE_FLAT = "flat";                     //NOI18N
86
    private static final String MODE_FLAT = "flat";                     //NOI18N
103
    private static final String MODE_TREE = "tree";                     //NOI18N
87
    private static final String MODE_TREE = "tree";                     //NOI18N
104
    protected ResultModel resultModel;
88
    protected ResultModel resultModel;
105
    protected JButton nextButton;
89
    protected JToggleButton btnTreeView;
106
    protected JButton prevButton;
90
    protected JToggleButton btnFlatView;
107
    protected JToggleButton expandButton;
108
    protected JToggleButton treeViewButton;
109
    protected JToggleButton flatViewButton;
110
    protected JButton showDetailsButton;
91
    protected JButton showDetailsButton;
111
    protected boolean details;
92
    protected boolean details;
112
    protected BasicComposition composition;
93
    protected BasicComposition composition;
Lines 129-136 Link Here
129
        this.resultsOutlineSupport = resultsOutlineSupport;
110
        this.resultsOutlineSupport = resultsOutlineSupport;
130
        getExplorerManager().setRootContext(
111
        getExplorerManager().setRootContext(
131
                resultsOutlineSupport.getRootNode());
112
                resultsOutlineSupport.getRootNode());
132
        initSelectionListeners();
113
        initButtons();
133
        initActions();
134
        initResultNodeAdditionListener();
114
        initResultNodeAdditionListener();
135
        if (MODE_TREE.equals(
115
        if (MODE_TREE.equals(
136
                FindDialogMemory.getDefault().getResultsViewMode())) {
116
                FindDialogMemory.getDefault().getResultsViewMode())) {
Lines 141-168 Link Here
141
        initAccessibility();
121
        initAccessibility();
142
    }
122
    }
143
123
144
    private void initSelectionListeners() {
145
        getExplorerManager().addPropertyChangeListener(
146
                new PropertyChangeListener() {
147
                    @Override
148
                    public void propertyChange(PropertyChangeEvent evt) {
149
                        if (evt.getPropertyName().equals(
150
                                "selectedNodes")) {                     //NOI18N
151
                            updateShiftButtons();
152
                        }
153
                    }
154
                });
155
    }
156
124
157
    private void initActions() {
158
        ActionMap map = getActionMap();
159
160
        map.put("jumpNext", new PrevNextAction(1));                    // NOI18N
161
        map.put("jumpPrev", new PrevNextAction(-1));                   // NOI18N
162
    }
163
    public void update() {
125
    public void update() {
164
        if (details && expandButton != null && !expandButton.isEnabled()) {
126
        if (details && btnExpand.isVisible() && !btnExpand.isEnabled()) {
165
            expandButton.setEnabled(resultModel.size() > 0);
127
            btnExpand.setEnabled(resultModel.size() > 0);
166
        }
128
        }
167
        EventQueue.invokeLater(new Runnable() {
129
        EventQueue.invokeLater(new Runnable() {
168
            @Override
130
            @Override
Lines 173-254 Link Here
173
        resultsOutlineSupport.update();
135
        resultsOutlineSupport.update();
174
    }
136
    }
175
137
176
    private void updateShiftButtons() {
138
    protected void initButtons() {
177
        if (details && prevButton != null && nextButton != null) {
178
            prevButton.setEnabled(
179
                    findShiftNode(-1, getOutlineView(), false) != null);
180
            nextButton.setEnabled(
181
                    findShiftNode(1, getOutlineView(), false) != null);
182
        }
183
    }
184
185
    @Override
186
    protected AbstractButton[] createButtons() {
187
        final FindDialogMemory memory = FindDialogMemory.getDefault();
139
        final FindDialogMemory memory = FindDialogMemory.getDefault();
188
        treeViewButton = new JToggleButton();
140
        btnTreeView = new JToggleButton();
189
        treeViewButton.setEnabled(true);
141
        btnTreeView.setEnabled(true);
190
        treeViewButton.setIcon(ImageUtilities.loadImageIcon(FOLDER_VIEW_ICON,
142
        btnTreeView.setIcon(ImageUtilities.loadImageIcon(FOLDER_VIEW_ICON,
191
                true));
143
                true));
192
        treeViewButton.setToolTipText(UiUtils.getText(
144
        btnTreeView.setToolTipText(UiUtils.getText(
193
                "TEXT_BUTTON_TREE_VIEW"));                              //NOI18N
145
                "TEXT_BUTTON_TREE_VIEW"));                              //NOI18N
194
        treeViewButton.setSelected(
146
        btnTreeView.setSelected(
195
                MODE_TREE.equals(memory.getResultsViewMode()));
147
                MODE_TREE.equals(memory.getResultsViewMode()));
196
        treeViewButton.addActionListener(new ActionListener() {
148
        btnTreeView.addActionListener(new ActionListener() {
197
            @Override
149
            @Override
198
            public void actionPerformed(ActionEvent e) {
150
            public void actionPerformed(ActionEvent e) {
199
                toggleView(!treeViewButton.isSelected());
151
                toggleView(!btnTreeView.isSelected());
200
            }
152
            }
201
        });
153
        });
202
        flatViewButton = new JToggleButton();
154
        btnFlatView = new JToggleButton();
203
        flatViewButton.setEnabled(true);
155
        btnFlatView.setEnabled(true);
204
        flatViewButton.setIcon(ImageUtilities.loadImageIcon(FLAT_VIEW_ICON,
156
        btnFlatView.setIcon(ImageUtilities.loadImageIcon(FLAT_VIEW_ICON,
205
                true));
157
                true));
206
        flatViewButton.setToolTipText(UiUtils.getText(
158
        btnFlatView.setToolTipText(UiUtils.getText(
207
                "TEXT_BUTTON_FLAT_VIEW"));                              //NOI18N
159
                "TEXT_BUTTON_FLAT_VIEW"));                              //NOI18N
208
        flatViewButton.setSelected(!treeViewButton.isSelected());
160
        btnFlatView.setSelected(!btnTreeView.isSelected());
209
        flatViewButton.addActionListener(new ActionListener() {
161
        btnFlatView.addActionListener(new ActionListener() {
210
            @Override
162
            @Override
211
            public void actionPerformed(ActionEvent e) {
163
            public void actionPerformed(ActionEvent e) {
212
                toggleView(flatViewButton.isSelected());
164
                toggleView(btnFlatView.isSelected());
213
            }
165
            }
214
        });
166
        });
167
        addButton(btnTreeView);
168
        addButton(btnFlatView);
215
        if (!details) {
169
        if (!details) {
216
            return new AbstractButton[]{treeViewButton, flatViewButton};
170
            btnPrev.setVisible(false);
171
            btnNext.setVisible(false);
172
            btnExpand.setVisible(false);
173
            return;
217
        }
174
        }
218
        prevButton = new JButton();
175
        btnExpand.addActionListener(new ActionListener() {
219
        prevButton.setEnabled(false);
220
        prevButton.setIcon(ImageUtilities.loadImageIcon(PREV_ICON, true));
221
        prevButton.setToolTipText(UiUtils.getText(
222
                "TEXT_BUTTON_PREV_MATCH"));                             //NOI18N
223
        prevButton.addActionListener(new ActionListener() {
224
            @Override
176
            @Override
225
            public void actionPerformed(ActionEvent e) {
177
            public void actionPerformed(ActionEvent e) {
226
                shift(-1);
178
                toggleExpandNodeChildren(btnExpand.isSelected());
227
            }
228
        });
229
        nextButton = new JButton();
230
        nextButton.setEnabled(false);
231
        nextButton.setIcon(ImageUtilities.loadImageIcon(NEXT_ICON, true));
232
        nextButton.setToolTipText(UiUtils.getText(
233
                "TEXT_BUTTON_NEXT_MATCH"));                             //NOI18N
234
        nextButton.addActionListener(new ActionListener() {
235
            @Override
236
            public void actionPerformed(ActionEvent e) {
237
                shift(1);
238
            }
239
        });
240
        expandButton = new JToggleButton();
241
        expandButton.setEnabled(false);
242
        expandButton.setIcon(ImageUtilities.loadImageIcon(EXPAND_ICON, true));
243
        expandButton.setSelectedIcon(ImageUtilities.loadImageIcon(
244
                COLLAPSE_ICON, true));
245
        expandButton.setToolTipText(UiUtils.getText(
246
                "TEXT_BUTTON_EXPAND"));                                 //NOI18N
247
        expandButton.setSelected(false);
248
        expandButton.addActionListener(new ActionListener() {
249
            @Override
250
            public void actionPerformed(ActionEvent e) {
251
                toggleExpandNodeChildren(expandButton.isSelected());
252
            }
179
            }
253
        });
180
        });
254
        showDetailsButton = new JButton();
181
        showDetailsButton = new JButton();
Lines 263-276 Link Here
263
                fillOutput();
190
                fillOutput();
264
            }
191
            }
265
        });
192
        });
266
        if (showDetailsButton != null) {
193
        showDetailsButton.getAccessibleContext().setAccessibleDescription(
267
            showDetailsButton.getAccessibleContext().setAccessibleDescription(
194
                NbBundle.getMessage(ResultView.class,
268
                    NbBundle.getMessage(ResultView.class,
195
                "ACS_TEXT_BUTTON_FILL"));                               //NOI18N
269
                    "ACS_TEXT_BUTTON_FILL"));                           //NOI18N
196
        addButton(showDetailsButton);
270
        }
271
        return new AbstractButton[]{prevButton, nextButton,
272
                    treeViewButton, flatViewButton, expandButton,
273
                    showDetailsButton};
274
    }
197
    }
275
198
276
    private void toggleView(boolean flat) {
199
    private void toggleView(boolean flat) {
Lines 282-289 Link Here
282
            resultsOutlineSupport.setFolderTreeMode();
205
            resultsOutlineSupport.setFolderTreeMode();
283
            memory.setResultsViewMode(MODE_TREE);
206
            memory.setResultsViewMode(MODE_TREE);
284
        }
207
        }
285
        treeViewButton.setSelected(!flat);
208
        btnTreeView.setSelected(!flat);
286
        flatViewButton.setSelected(flat);
209
        btnFlatView.setSelected(flat);
287
        try {
210
        try {
288
            getExplorerManager().setSelectedNodes(new Node[]{
211
            getExplorerManager().setSelectedNodes(new Node[]{
289
                        resultsOutlineSupport.getResultsNode()});
212
                        resultsOutlineSupport.getResultsNode()});
Lines 312-445 Link Here
312
                bundle.getString("ACSD_ResultTree"));                   //NOI18N
235
                bundle.getString("ACSD_ResultTree"));                   //NOI18N
313
    }
236
    }
314
237
315
    private void shift(int direction) {
316
317
        Node next = findShiftNode(direction, getOutlineView(), true);
318
        if (next == null) {
319
            return;
320
        }
321
        try {
322
            getExplorerManager().setSelectedNodes(new Node[]{next});
323
            TextDetail textDetail = next.getLookup().lookup(
324
                    TextDetail.class);
325
            if (textDetail != null) {
326
                textDetail.showDetail(TextDetail.DH_GOTO);
327
            }
328
        } catch (PropertyVetoException ex) {
329
            Exceptions.printStackTrace(ex);
330
        }
331
    }
332
333
    private Node findShiftNode(int direction, OutlineView outlineView,
334
            boolean canExpand) {
335
        Node[] selected = getExplorerManager().getSelectedNodes();
336
        Node n = null;
337
        if ((selected == null || selected.length == 0)
338
                && getExplorerManager().getRootContext()
339
                == resultsOutlineSupport.getRootNode()) {
340
            n = resultsOutlineSupport.getResultsNode();
341
        } else if (selected.length == 1) {
342
            n = selected[0];
343
        }
344
        return n == null ? null : findTextDetailNode(n, direction, outlineView,
345
                canExpand);
346
    }
347
348
    static Node findTextDetailNode(Node fromNode, int direction,
349
            OutlineView outlineView, boolean canExpand) {
350
        return findUp(fromNode, direction,
351
                isTextDetailNode(fromNode) || direction < 0 ? direction : 0,
352
                outlineView, canExpand);
353
    }
354
355
    /**
356
     * Start finding for next or previous occurance, from a node or its previous
357
     * or next sibling of node {@code node}
358
     *
359
     * @param node reference node
360
     * @param offset 0 to start from node {@code node}, 1 to start from its next
361
     * sibling, -1 to start from its previous sibling.
362
     * @param dir Direction: 1 for next, -1 for previous.
363
     */
364
    static Node findUp(Node node, int dir, int offset, OutlineView outlineView,
365
            boolean canExpand) {
366
        if (node == null) {
367
            return null;
368
        }
369
        Node parent = node.getParentNode();
370
        Node[] siblings;
371
        if (parent == null) {
372
            siblings = new Node[]{node};
373
        } else {
374
            siblings = getChildren(parent, outlineView, canExpand);
375
        }
376
        int nodeIndex = findChildIndex(node, siblings);
377
        if (nodeIndex + offset < 0 || nodeIndex + offset >= siblings.length) {
378
            return findUp(parent, dir, dir, outlineView, canExpand);
379
        }
380
        for (int i = nodeIndex + offset;
381
                i >= 0 && i < siblings.length; i += dir) {
382
            Node found = findDown(siblings[i], siblings, i, dir, outlineView,
383
                    canExpand);
384
            return found;
385
        }
386
        return findUp(parent, dir, offset, outlineView, canExpand);
387
    }
388
389
    /**
390
     * Find Depth-first search to find TextDetail node in the subtree.
391
     */
392
    private static Node findDown(Node node, Node[] siblings, int nodeIndex,
393
            int dir, OutlineView outlineView, boolean canExpand) {
394
395
        Node[] children = getChildren(node, outlineView, canExpand);
396
        for (int i = dir > 0 ? 0 : children.length - 1;
397
                i >= 0 && i < children.length; i += dir) {
398
            Node found = findDown(children[i], children, i, dir, outlineView,
399
                    canExpand);
400
            if (found != null) {
401
                return found;
402
            }
403
        }
404
        for (int i = nodeIndex; i >= 0 && i < siblings.length; i += dir) {
405
            if (isTextDetailNode(siblings[i])) {
406
                return siblings[i];
407
            }
408
        }
409
        return null;
410
    }
411
412
    private static boolean isTextDetailNode(Node n) {
413
        return n.getLookup().lookup(TextDetail.class) != null;
414
    }
415
416
    private static int findChildIndex(Node selectedNode, Node[] siblings) {
417
        int pos = -1;
418
        for (int i = 0; i < siblings.length; i++) {
419
            if (siblings[i] == selectedNode) {
420
                pos = i;
421
                break;
422
            }
423
        }
424
        return pos;
425
    }
426
427
    private static Node[] getChildren(Node n, OutlineView outlineView,
428
            boolean canExpand) {
429
        if (outlineView != null) {
430
            if (!outlineView.isExpanded(n)) {
431
                if (canExpand) {
432
                    outlineView.expandNode(n);
433
                } else {
434
                    return n.getChildren().getNodes(true);
435
                }
436
            }
437
            return getChildrenInDisplayedOrder(n, outlineView);
438
        } else {
439
            return n.getChildren().getNodes(true);
440
        }
441
    }
442
443
    private void toggleExpandNodeChildren(boolean expand) {
238
    private void toggleExpandNodeChildren(boolean expand) {
444
        Node resultsNode = resultsOutlineSupport.getResultsNode();
239
        Node resultsNode = resultsOutlineSupport.getResultsNode();
445
        for (Node n : resultsNode.getChildren().getNodes()) {
240
        for (Node n : resultsNode.getChildren().getNodes()) {
Lines 447-464 Link Here
447
        }
242
        }
448
    }
243
    }
449
244
450
    public void toggleExpand(Node root, boolean expand) {
451
        if (expand) {
452
            getOutlineView().expandNode(root);
453
        }
454
        for (Node n : root.getChildren().getNodes()) {
455
            toggleExpand(n, expand);
456
        }
457
        if (!expand) {
458
            getOutlineView().collapseNode(root);
459
        }
460
    }
461
462
    @Override
245
    @Override
463
    public void searchFinished() {
246
    public void searchFinished() {
464
        super.searchFinished();
247
        super.searchFinished();
Lines 480-485 Link Here
480
    public void addMatchingObject(MatchingObject mo) {
263
    public void addMatchingObject(MatchingObject mo) {
481
        resultsOutlineSupport.addMatchingObject(mo);
264
        resultsOutlineSupport.addMatchingObject(mo);
482
        updateRootNodeText();
265
        updateRootNodeText();
266
        afterMatchingNodeAdded();
483
    }
267
    }
484
268
485
    public final OutlineView getOutlineView() {
269
    public final OutlineView getOutlineView() {
Lines 552-624 Link Here
552
        }
336
        }
553
    }
337
    }
554
338
555
    private static Node[] getChildrenInDisplayedOrder(Node parent,
556
            OutlineView outlineView) {
557
558
        Outline outline = outlineView.getOutline();
559
        Node[] unsortedChildren = parent.getChildren().getNodes(true);
560
        int rows = outlineView.getOutline().getRowCount();
561
        int start = findRowIndexInOutline(parent, outline, rows);
562
        if (start == -1) {
563
            return unsortedChildren;
564
        }
565
        List<Node> children = new LinkedList<Node>();
566
        for (int j = start + 1; j < rows; j++) {
567
            int childModelIndex = outline.convertRowIndexToModel(j);
568
            if (childModelIndex == -1) {
569
                continue;
570
            }
571
            Object childObject = outline.getModel().getValueAt(
572
                    childModelIndex, 0);
573
            Node childNode = Visualizer.findNode(childObject);
574
            if (childNode.getParentNode() == parent) {
575
                children.add(childNode);
576
            } else if (children.size() == unsortedChildren.length) {
577
                break;
578
            }
579
        }
580
        return children.toArray(new Node[children.size()]);
581
    }
582
583
    private static int findRowIndexInOutline(Node node, Outline outline,
584
            int rows) {
585
586
        int startRow = Math.max(outline.getSelectedRow(), 0);
587
        int offset = 0;
588
        while (startRow + offset < rows || startRow - offset >= 0) {
589
            int up = startRow + offset + 1;
590
            int down = startRow - offset;
591
592
            if (up < rows && testNodeInRow(outline, node, up)) {
593
                return up;
594
            } else if (down >= 0 && testNodeInRow(outline, node, down)) {
595
                return down;
596
            } else {
597
                offset++;
598
            }
599
        }
600
        return -1;
601
    }
602
603
    private static boolean testNodeInRow(Outline outline, Node node, int i) {
604
        int modelIndex = outline.convertRowIndexToModel(i);
605
        if (modelIndex != -1) {
606
            Object o = outline.getModel().getValueAt(modelIndex, 0);
607
            Node n = Visualizer.findNode(o);
608
            if (n == node) {
609
                return true;
610
            }
611
        }
612
        return false;
613
    }
614
615
    private void initResultNodeAdditionListener() {
339
    private void initResultNodeAdditionListener() {
616
        resultsNodeAdditionListener = new NodeAdapter() {
340
        resultsNodeAdditionListener = new NodeAdapter() {
617
            @Override
341
            @Override
618
            public void childrenAdded(NodeMemberEvent ev) {
342
            public void childrenAdded(NodeMemberEvent ev) {
619
                if (expandButton != null) {
343
                if (btnExpand != null) {
620
                    for (final Node n : ev.getDelta()) {
344
                    for (final Node n : ev.getDelta()) {
621
                        if (expandButton.isSelected()) {
345
                        if (btnExpand.isSelected()) {
622
                            EventQueue.invokeLater(new Runnable() {
346
                            EventQueue.invokeLater(new Runnable() {
623
                                @Override
347
                                @Override
624
                                public void run() {
348
                                public void run() {
Lines 633-639 Link Here
633
357
634
            @Override
358
            @Override
635
            public void childrenRemoved(NodeMemberEvent ev) {
359
            public void childrenRemoved(NodeMemberEvent ev) {
636
                if (expandButton != null) {
360
                if (btnExpand != null) {
637
                    for (Node removedChild : ev.getDelta()) {
361
                    for (Node removedChild : ev.getDelta()) {
638
                        removeChildAdditionListener(removedChild);
362
                        removeChildAdditionListener(removedChild);
639
                    }
363
                    }
Lines 660-680 Link Here
660
        removedNode.removeNodeListener(resultsNodeAdditionListener);
384
        removedNode.removeNodeListener(resultsNodeAdditionListener);
661
    }
385
    }
662
386
663
    private final class PrevNextAction extends javax.swing.AbstractAction {
664
665
        private int direction;
666
667
        public PrevNextAction(int direction) {
668
            this.direction = direction;
669
        }
670
671
        public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
672
            shift(direction);
673
        }
674
    }
675
676
    @Override
387
    @Override
677
    public boolean requestFocusInWindow() {
388
    public boolean requestFocusInWindow() {
678
        return getOutlineView().requestFocusInWindow();
389
        return getOutlineView().requestFocusInWindow();
679
    }
390
    }
391
392
    @Override
393
    protected boolean isDetailNode(Node n) {
394
        return n.getLookup().lookup(TextDetail.class) != null;
395
    }
396
397
    @Override
398
    protected void onDetailShift(Node next) {
399
        TextDetail textDetail = next.getLookup().lookup(
400
                TextDetail.class);
401
        if (textDetail != null) {
402
            textDetail.showDetail(TextDetail.DH_GOTO);
403
        }
404
    }
680
}
405
}
(-)a/api.search/src/org/netbeans/modules/search/ui/BasicReplaceResultsPanel.java (-7 / +7 lines)
Lines 174-181 Link Here
174
    public void displayIssues(IssuesPanel issuesPanel) {
174
    public void displayIssues(IssuesPanel issuesPanel) {
175
        if (issuesPanel != null) {
175
        if (issuesPanel != null) {
176
            showRefreshButton();
176
            showRefreshButton();
177
            removeButtons(nextButton, prevButton, treeViewButton,
177
            removeButtons(btnNext, btnPrev, btnFlatView, btnTreeView,
178
                    flatViewButton, expandButton, showDetailsButton);
178
                    btnExpand, showDetailsButton);
179
            Container p = getContentPanel();
179
            Container p = getContentPanel();
180
            p.removeAll();
180
            p.removeAll();
181
            p.add(issuesPanel);
181
            p.add(issuesPanel);
Lines 214-224 Link Here
214
                getExplorerManager().setRootContext(an);
214
                getExplorerManager().setRootContext(an);
215
                getOutlineView().validate();
215
                getOutlineView().validate();
216
                getOutlineView().repaint();
216
                getOutlineView().repaint();
217
                nextButton.setEnabled(false);
217
                btnNext.setEnabled(false);
218
                prevButton.setEnabled(false);
218
                btnPrev.setEnabled(false);
219
                treeViewButton.setEnabled(false);
219
                btnTreeView.setEnabled(false);
220
                flatViewButton.setEnabled(false);
220
                btnFlatView.setEnabled(false);
221
                expandButton.setEnabled(false);
221
                btnExpand.setEnabled(false);
222
            }
222
            }
223
        });
223
        });
224
    }
224
    }
(-)a/api.search/src/org/netbeans/modules/search/ui/DefaultSearchResultsPanel.java (-3 / +75 lines)
Lines 41-49 Link Here
41
 */
41
 */
42
package org.netbeans.modules.search.ui;
42
package org.netbeans.modules.search.ui;
43
43
44
import java.awt.event.ActionEvent;
45
import java.awt.event.ActionListener;
44
import java.util.ArrayList;
46
import java.util.ArrayList;
45
import java.util.List;
47
import java.util.List;
48
import javax.swing.AbstractButton;
49
import javax.swing.JButton;
50
import javax.swing.JToggleButton;
46
import org.netbeans.modules.search.Constants;
51
import org.netbeans.modules.search.Constants;
52
import org.netbeans.modules.search.ui.AbstractSearchResultsPanel.RootNode;
47
import org.netbeans.spi.search.provider.SearchComposition;
53
import org.netbeans.spi.search.provider.SearchComposition;
48
import org.netbeans.spi.search.provider.SearchProvider.Presenter;
54
import org.netbeans.spi.search.provider.SearchProvider.Presenter;
49
import org.netbeans.spi.search.provider.SearchResultsDisplayer;
55
import org.netbeans.spi.search.provider.SearchResultsDisplayer;
Lines 63-68 Link Here
63
    private List<T> matchingObjects = new ArrayList<T>();
69
    private List<T> matchingObjects = new ArrayList<T>();
64
    private final NodeDisplayer<T> nodeDisplayer;
70
    private final NodeDisplayer<T> nodeDisplayer;
65
    private ResultsNode resultsNode;
71
    private ResultsNode resultsNode;
72
    private OutlineView outlineView;
66
73
67
    public DefaultSearchResultsPanel(
74
    public DefaultSearchResultsPanel(
68
            SearchResultsDisplayer.NodeDisplayer<T> nodeDisplayer,
75
            SearchResultsDisplayer.NodeDisplayer<T> nodeDisplayer,
Lines 71-86 Link Here
71
78
72
        super(searchComposition, searchProviderPresenter);
79
        super(searchComposition, searchProviderPresenter);
73
        this.resultsNode = new ResultsNode();
80
        this.resultsNode = new ResultsNode();
74
        getExplorerManager().setRootContext(resultsNode);
75
        this.nodeDisplayer = nodeDisplayer;
81
        this.nodeDisplayer = nodeDisplayer;
76
        resultsNode.update();
82
        resultsNode.update();
77
        getContentPanel().add(new OutlineView(UiUtils.getText(
83
        outlineView = new OutlineView(UiUtils.getText(
78
                "BasicSearchResultsPanel.outline.nodes")));             //NOI18N
84
                "BasicSearchResultsPanel.outline.nodes"));              //NOI18N
85
        outlineView.getOutline().setRootVisible(false);
86
        initExpandButton();
87
        getContentPanel().add(outlineView);
88
    }
89
90
    private void initExpandButton() {
91
        btnExpand.addActionListener(new ActionListener() {
92
            @Override
93
            public void actionPerformed(ActionEvent e) {
94
                getOutlineView().expandNode(resultsNode);
95
                for (Node n : resultsNode.getChildren().getNodes(true)) {
96
                    toggleExpand(n, btnExpand.isSelected());
97
                }
98
            }
99
        });
100
        btnExpand.setEnabled(true);
79
    }
101
    }
80
102
81
    public void addMatchingObject(T object) {
103
    public void addMatchingObject(T object) {
82
        matchingObjects.add(object);
104
        matchingObjects.add(object);
83
        resultsNode.update();
105
        resultsNode.update();
106
        afterMatchingNodeAdded();
84
    }
107
    }
85
108
86
    /**
109
    /**
Lines 130-133 Link Here
130
                "TEXT_MSG_FOUND_X_NODES", //NOI18N
153
                "TEXT_MSG_FOUND_X_NODES", //NOI18N
131
                matchingObjects.size()));
154
                matchingObjects.size()));
132
    }
155
    }
156
157
    /**
158
     * Get {@link OutlineView} used for displaying result nodes.
159
     */
160
    public OutlineView getOutlineView() {
161
        return outlineView;
162
    }
163
164
    /**
165
     * Get button for moving to the previous result item. It is hidden by
166
     * default. You can set it visible and add a {@link ActionListener}.
167
     */
168
    public JButton getButtonPrevious() {
169
        return btnPrev;
170
    }
171
172
    /**
173
     * Get button for moving to the next result item. It is hidden by default.
174
     * You can set it visible and add a {@link ActionListener}.
175
     */
176
    public JButton getButtonNext() {
177
        return btnNext;
178
    }
179
180
    /**
181
     * Get button for expanding/collapsing of the result tree. It is hidden by
182
     * default. You can set it visible and add a {@link ActionListener}.
183
     */
184
    public JToggleButton getButtonExpand() {
185
        return btnExpand;
186
    }
187
188
    /**
189
     * Add a custom button to the toolbar.
190
     */
191
    public void addButton(AbstractButton button) {
192
        super.addButton(button);
193
    }
194
195
    public void setInfoNode(Node infoNode) {
196
        Node root = new RootNode(resultsNode, infoNode);
197
        getExplorerManager().setRootContext(root);
198
        getOutlineView().expandNode(resultsNode);
199
    }
200
201
    @Override
202
    protected boolean isDetailNode(Node n) {
203
        return true;
204
    }
133
}
205
}
(-)a/api.search/src/org/netbeans/modules/search/ui/ResultsOutlineSupport.java (-70 / +2 lines)
Lines 71-76 Link Here
71
import org.netbeans.modules.search.MatchingObject;
71
import org.netbeans.modules.search.MatchingObject;
72
import org.netbeans.modules.search.ResultModel;
72
import org.netbeans.modules.search.ResultModel;
73
import org.netbeans.modules.search.Selectable;
73
import org.netbeans.modules.search.Selectable;
74
import org.netbeans.modules.search.ui.AbstractSearchResultsPanel.RootNode;
74
import org.netbeans.swing.etable.ETableColumnModel;
75
import org.netbeans.swing.etable.ETableColumnModel;
75
import org.netbeans.swing.outline.Outline;
76
import org.netbeans.swing.outline.Outline;
76
import org.openide.explorer.view.OutlineView;
77
import org.openide.explorer.view.OutlineView;
Lines 83-91 Link Here
83
import org.openide.nodes.Children;
84
import org.openide.nodes.Children;
84
import org.openide.nodes.FilterNode;
85
import org.openide.nodes.FilterNode;
85
import org.openide.nodes.Node;
86
import org.openide.nodes.Node;
86
import org.openide.nodes.NodeAdapter;
87
import org.openide.nodes.NodeMemberEvent;
88
import org.openide.nodes.NodeReorderEvent;
89
import org.openide.util.Exceptions;
87
import org.openide.util.Exceptions;
90
import org.openide.util.ImageUtilities;
88
import org.openide.util.ImageUtilities;
91
import org.openide.util.datatransfer.PasteType;
89
import org.openide.util.datatransfer.PasteType;
Lines 124-130 Link Here
124
        this.rootFiles = rootFiles;
122
        this.rootFiles = rootFiles;
125
        this.resultsNode = new ResultsNode();
123
        this.resultsNode = new ResultsNode();
126
        this.infoNode = infoNode;
124
        this.infoNode = infoNode;
127
        this.invisibleRoot = new RootNode();
125
        this.invisibleRoot = new RootNode(resultsNode, infoNode);
128
        this.matchingObjectNodes = new LinkedList<MatchingObjectNode>();
126
        this.matchingObjectNodes = new LinkedList<MatchingObjectNode>();
129
        createOutlineView();
127
        createOutlineView();
130
    }
128
    }
Lines 297-368 Link Here
297
        }
295
        }
298
    }
296
    }
299
297
300
    private class RootNode extends AbstractNode {
301
302
        public RootNode() {
303
            this(new RootNodeChildren());
304
        }
305
306
        private RootNode(final RootNodeChildren rootNodeChildren) {
307
            super(rootNodeChildren);
308
            if (infoNode != null) {
309
                setInfoNodeListener(rootNodeChildren);
310
            }
311
        }
312
    }
313
314
    private void setInfoNodeListener(final RootNodeChildren rootNodeChildren) {
315
316
        assert infoNode != null;
317
318
        infoNode.getChildren().getNodes(true);
319
        infoNode.addNodeListener(new NodeAdapter() {
320
            private boolean added = false;
321
322
            @Override
323
            public synchronized void childrenAdded(NodeMemberEvent ev) {
324
                if (!added) {
325
                    rootNodeChildren.showInfoNode();
326
                    infoNode.removeNodeListener(this);
327
                    added = true;
328
                }
329
            }
330
331
            @Override
332
            public void propertyChange(PropertyChangeEvent ev) {
333
                super.propertyChange(ev);
334
            }
335
336
            @Override
337
            public void childrenReordered(NodeReorderEvent ev) {
338
                super.childrenReordered(ev);
339
            }
340
        });
341
    }
342
343
    private class RootNodeChildren extends Children.Keys<Node> {
344
345
        private Node[] standard = new Node[]{resultsNode};
346
        private Node[] withInfo = new Node[]{resultsNode, infoNode};
347
        private boolean infoNodeShown = false;
348
349
        public RootNodeChildren() {
350
            setKeys(standard);
351
        }
352
353
        private synchronized void showInfoNode() {
354
            if (!infoNodeShown) {
355
                setKeys(withInfo);
356
                infoNodeShown = true;
357
            }
358
        }
359
360
        @Override
361
        protected Node[] createNodes(Node key) {
362
            return new Node[]{key};
363
        }
364
    }
365
366
    /**
298
    /**
367
     * Class for representation of the root node.
299
     * Class for representation of the root node.
368
     */
300
     */
(-)a/api.search/src/org/netbeans/spi/search/provider/DefaultSearchResultsDisplayer.java (+230 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.search.provider;
43
44
import javax.swing.AbstractButton;
45
import javax.swing.JComponent;
46
import org.netbeans.api.annotations.common.NonNull;
47
import org.netbeans.modules.search.ui.DefaultSearchResultsPanel;
48
import org.netbeans.spi.search.provider.SearchProvider.Presenter;
49
import org.openide.explorer.view.OutlineView;
50
import org.openide.nodes.Node;
51
import org.openide.util.Parameters;
52
53
/**
54
 * Default search results displayer.
55
 *
56
 * @author jhavlin
57
 * @since api.search/1.1
58
 */
59
public final class DefaultSearchResultsDisplayer<U>
60
        extends SearchResultsDisplayer<U> {
61
62
    private static final ResultNodeShiftSupport DEFAULT_NODE_SHIFT_SUPPORT =
63
            new TrivialResultNodeShiftSupport();
64
    private final SearchResultsDisplayer.NodeDisplayer<U> helper;
65
    private final SearchComposition<U> searchComposition;
66
    private final Presenter presenter;
67
    private final String title;
68
    private ResultNodeShiftSupport shiftSupport = DEFAULT_NODE_SHIFT_SUPPORT;
69
    private DefaultSearchResultsPanel<U> panel = null;
70
71
    DefaultSearchResultsDisplayer(
72
            SearchResultsDisplayer.NodeDisplayer<U> helper,
73
            SearchComposition<U> searchComposition,
74
            Presenter presenter, String title) {
75
        this.helper = helper;
76
        this.searchComposition = searchComposition;
77
        this.presenter = presenter;
78
        this.title = title;
79
    }
80
81
    /**
82
     * {@inheritDoc}
83
     */
84
    @Override
85
    public synchronized JComponent getVisualComponent() {
86
        if (panel == null) {
87
            panel = new DefaultSearchResultsPanel<U>(helper,
88
                    searchComposition, presenter) {
89
                @Override
90
                protected void onDetailShift(Node n) {
91
                    shiftSupport.relevantNodeSelected(n);
92
                }
93
94
                @Override
95
                protected boolean isDetailNode(Node n) {
96
                    return shiftSupport.isRelevantNode(n);
97
                }
98
            };
99
        }
100
        return panel;
101
    }
102
103
    private DefaultSearchResultsPanel<U> getPanel() {
104
        if (panel == null) {
105
            getVisualComponent();
106
        }
107
        return panel;
108
    }
109
110
    /**
111
     * {@inheritDoc}
112
     */
113
    @Override
114
    public void addMatchingObject(U object) {
115
        Parameters.notNull("object", object);
116
        panel.addMatchingObject(object);
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     */
122
    @Override
123
    public String getTitle() {
124
        return title;
125
    }
126
127
    /**
128
     * {@inheritDoc}
129
     */
130
    @Override
131
    public void searchStarted() {
132
        super.searchStarted();
133
        panel.searchStarted();
134
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139
    @Override
140
    public void searchFinished() {
141
        super.searchFinished();
142
        panel.searchFinished();
143
    }
144
145
    /**
146
     * Get outline view. You can alter it to display additional node properties,
147
     * or set custom cell renderer.
148
     *
149
     * @return OutlineView used in the results displayer.
150
     */
151
    public @NonNull OutlineView getOutlineView() {
152
        return panel.getOutlineView();
153
    }
154
155
    /**
156
     * {@inheritDoc}
157
     */
158
    @Override
159
    public void setInfoNode(Node infoNode) {
160
        getPanel().setInfoNode(infoNode);
161
    }
162
163
    /**
164
     * Set a custom {@link ResultNodeShiftSupport} for this displayer.
165
     */
166
    public void setResultNodeShiftSupport(
167
            ResultNodeShiftSupport resultNodeShiftSupport) {
168
        this.shiftSupport = resultNodeShiftSupport;
169
    }
170
171
    /**
172
     * Add a button to the results displayer toolbar. It will be shown right
173
     * above the stop button.
174
     *
175
     * @param button Button to add.
176
     */
177
    public void addButton(@NonNull AbstractButton button) {
178
        Parameters.notNull("button", button);
179
        getPanel().addButton(button);
180
    }
181
182
    /**
183
     * Class definining which nodes should be selected when Previous or Next
184
     * button is pressed and what action should be performed.
185
     */
186
    public static abstract class ResultNodeShiftSupport {
187
188
        /**
189
         * Method that checks whether a node should be selected when Next or
190
         * Previous button is pressed.
191
         *
192
         * @param node Node to check.
193
         * @return True if {@code node} is a node that Next and Previous buttons
194
         * should consider, false it if is structural or informational node
195
         * only.
196
         */
197
        public abstract boolean isRelevantNode(Node node);
198
199
        /**
200
         * This method is called when a relevant node is selected by pressing
201
         * Next or Previous button.
202
         *
203
         * Clients should implement this method to perform an appropriate
204
         * action, e.g. to show the relevant part of found file in editor.
205
         *
206
         * @param node Node that has been just selected.
207
         */
208
        public abstract void relevantNodeSelected(Node node);
209
    }
210
211
    /**
212
     * Trivial implementation of {@link ResultNodeShiftSupport} that consider
213
     * only leaf nodes as relevant and that do nothing when a relevant node is
214
     * selected.
215
     */
216
    private static class TrivialResultNodeShiftSupport
217
            extends ResultNodeShiftSupport {
218
219
        @Override
220
        public boolean isRelevantNode(Node node) {
221
            Node parent = node.getParentNode();
222
            return node.isLeaf() && node != null
223
                    && parent.getParentNode() != null;
224
        }
225
226
        @Override
227
        public void relevantNodeSelected(Node node) {
228
        }
229
    }
230
}
(-)a/api.search/src/org/netbeans/spi/search/provider/SearchResultsDisplayer.java (-40 / +7 lines)
Lines 44-50 Link Here
44
import javax.swing.JComponent;
44
import javax.swing.JComponent;
45
import org.netbeans.api.annotations.common.NonNull;
45
import org.netbeans.api.annotations.common.NonNull;
46
import org.netbeans.api.annotations.common.NullAllowed;
46
import org.netbeans.api.annotations.common.NullAllowed;
47
import org.netbeans.modules.search.ui.DefaultSearchResultsPanel;
48
import org.openide.nodes.Node;
47
import org.openide.nodes.Node;
49
48
50
/**
49
/**
Lines 96-141 Link Here
96
     * @param title Title that will be shown in the tab of search results
95
     * @param title Title that will be shown in the tab of search results
97
     * window.
96
     * window.
98
     */
97
     */
99
    public static <U> SearchResultsDisplayer<U> createDefault(
98
    public static <U> DefaultSearchResultsDisplayer<U> createDefault(
100
            @NonNull final NodeDisplayer<U> helper,
99
            @NonNull NodeDisplayer<U> helper,
101
            @NonNull final SearchComposition<U> searchComposition,
100
            @NonNull SearchComposition<U> searchComposition,
102
            @NullAllowed final SearchProvider.Presenter presenter,
101
            @NullAllowed SearchProvider.Presenter presenter,
103
            @NonNull final String title) {
102
            @NonNull String title) {
104
103
105
        return new SearchResultsDisplayer<U>() {
104
        return new DefaultSearchResultsDisplayer(helper, searchComposition,
106
            private DefaultSearchResultsPanel panel = null;
105
                presenter, title);
107
108
            @Override
109
            public synchronized JComponent getVisualComponent() {
110
                if (panel == null) {
111
                    panel = new DefaultSearchResultsPanel(helper,
112
                            searchComposition, presenter);
113
                }
114
                return panel;
115
            }
116
117
            @Override
118
            public void addMatchingObject(U object) {
119
                panel.addMatchingObject(object);
120
            }
121
122
            @Override
123
            public String getTitle() {
124
                return title;
125
            }
126
127
            @Override
128
            public void searchStarted() {
129
                super.searchStarted();
130
                panel.searchStarted();
131
            }
132
133
            @Override
134
            public void searchFinished() {
135
                super.searchFinished();
136
                panel.searchFinished();
137
            }
138
        };
139
    }
106
    }
140
107
141
    /**
108
    /**
(-)a/api.search/test/unit/src/org/netbeans/modules/search/MatchingObjectTest.java (-4 lines)
Lines 57-70 Link Here
57
import org.netbeans.modules.search.MatchingObject.Def;
57
import org.netbeans.modules.search.MatchingObject.Def;
58
import org.netbeans.modules.search.matcher.AbstractMatcher;
58
import org.netbeans.modules.search.matcher.AbstractMatcher;
59
import org.netbeans.modules.search.matcher.DefaultMatcher;
59
import org.netbeans.modules.search.matcher.DefaultMatcher;
60
import org.netbeans.modules.search.ui.BasicReplaceResultsPanel;
61
import org.netbeans.spi.queries.FileEncodingQueryImplementation;
60
import org.netbeans.spi.queries.FileEncodingQueryImplementation;
62
import org.netbeans.spi.search.SearchScopeDefinition;
61
import org.netbeans.spi.search.SearchScopeDefinition;
63
import org.netbeans.spi.search.provider.SearchComposition;
62
import org.netbeans.spi.search.provider.SearchComposition;
64
import org.openide.filesystems.FileObject;
63
import org.openide.filesystems.FileObject;
65
import org.openide.filesystems.FileSystem;
64
import org.openide.filesystems.FileSystem;
66
import org.openide.filesystems.FileUtil;
65
import org.openide.filesystems.FileUtil;
67
import org.openide.nodes.Node;
68
66
69
/**
67
/**
70
 * @author jhavlin
68
 * @author jhavlin
Lines 170-176 Link Here
170
                new DefaultMatcher(bsc.getSearchPattern()),
168
                new DefaultMatcher(bsc.getSearchPattern()),
171
                bsc, null);
169
                bsc, null);
172
        EventQueue.invokeAndWait(new Runnable() {
170
        EventQueue.invokeAndWait(new Runnable() {
173
174
            @Override
171
            @Override
175
            public void run() {
172
            public void run() {
176
                sc.getSearchResultsDisplayer().getVisualComponent(); // initialize model
173
                sc.getSearchResultsDisplayer().getVisualComponent(); // initialize model
Lines 216-222 Link Here
216
        @Override
213
        @Override
217
        public SearchInfo getSearchInfo() {
214
        public SearchInfo getSearchInfo() {
218
            return new SearchInfo() {
215
            return new SearchInfo() {
219
220
                @Override
216
                @Override
221
                public boolean canSearch() {
217
                public boolean canSearch() {
222
                    return true;
218
                    return true;
(-)a/api.search/test/unit/src/org/netbeans/modules/search/ui/BasicSearchResultsPanelTest.java (-2 / +17 lines)
Lines 47-52 Link Here
47
import org.junit.Before;
47
import org.junit.Before;
48
import org.junit.Test;
48
import org.junit.Test;
49
import org.netbeans.modules.search.TextDetail;
49
import org.netbeans.modules.search.TextDetail;
50
import org.openide.explorer.view.OutlineView;
50
import org.openide.nodes.AbstractNode;
51
import org.openide.nodes.AbstractNode;
51
import org.openide.nodes.Children;
52
import org.openide.nodes.Children;
52
import org.openide.nodes.Node;
53
import org.openide.nodes.Node;
Lines 66-71 Link Here
66
    private Node c3;
67
    private Node c3;
67
    private RootNode rootNode;
68
    private RootNode rootNode;
68
69
70
    private AbstractSearchResultsPanel resultsPanel;
71
69
    @Before
72
    @Before
70
    public void setUp() {
73
    public void setUp() {
71
        rootNode = new RootNode();
74
        rootNode = new RootNode();
Lines 75-80 Link Here
75
        a1 = a.getChildren().getNodeAt(0);
78
        a1 = a.getChildren().getNodeAt(0);
76
        b2 = b.getChildren().getNodeAt(1);
79
        b2 = b.getChildren().getNodeAt(1);
77
        c3 = c.getChildren().getNodeAt(2);
80
        c3 = c.getChildren().getNodeAt(2);
81
        resultsPanel = new AbstractSearchResultsPanel(null, null) {
82
83
            @Override
84
            protected OutlineView getOutlineView() {
85
                return null;
86
            }
87
88
            @Override
89
            protected boolean isDetailNode(Node n) {
90
                return n.getLookup().lookup(TextDetail.class) != null;
91
            }
92
        };
78
    }
93
    }
79
94
80
    @Test
95
    @Test
Lines 107-118 Link Here
107
    }
122
    }
108
123
109
    private Node next(Node fromNode) {
124
    private Node next(Node fromNode) {
110
        return BasicAbstractResultsPanel.findTextDetailNode(fromNode, 1, null,
125
        return resultsPanel.findDetailNode(fromNode, 1, null,
111
                false);
126
                false);
112
    }
127
    }
113
128
114
    private Node prev(Node fromNode) {
129
    private Node prev(Node fromNode) {
115
        return BasicAbstractResultsPanel.findTextDetailNode(fromNode, -1, null,
130
        return resultsPanel.findDetailNode(fromNode, -1, null,
116
                false);
131
                false);
117
    }
132
    }
118
133

Return to bug 211287