a4f3838459db21cbf6000a0dcde5d1e4a5b3e904
[idea/community.git] / platform / lang-impl / src / com / intellij / find / impl / FindDialog.java
1
2 /*
3  * Copyright 2000-2009 JetBrains s.r.o.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package com.intellij.find.impl;
19
20 import com.intellij.CommonBundle;
21 import com.intellij.find.FindBundle;
22 import com.intellij.find.FindModel;
23 import com.intellij.find.FindSettings;
24 import com.intellij.ide.util.scopeChooser.ScopeChooserCombo;
25 import com.intellij.openapi.editor.event.DocumentAdapter;
26 import com.intellij.openapi.editor.event.DocumentEvent;
27 import com.intellij.openapi.fileChooser.FileChooser;
28 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
29 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
30 import com.intellij.openapi.fileTypes.FileType;
31 import com.intellij.openapi.fileTypes.FileTypeManager;
32 import com.intellij.openapi.fileTypes.FileTypes;
33 import com.intellij.openapi.help.HelpManager;
34 import com.intellij.openapi.module.Module;
35 import com.intellij.openapi.module.ModuleManager;
36 import com.intellij.openapi.module.ModuleUtil;
37 import com.intellij.openapi.project.Project;
38 import com.intellij.openapi.ui.*;
39 import com.intellij.openapi.util.IconLoader;
40 import com.intellij.openapi.util.text.StringUtil;
41 import com.intellij.openapi.vfs.LocalFileSystem;
42 import com.intellij.openapi.vfs.VirtualFile;
43 import com.intellij.psi.PsiDirectory;
44 import com.intellij.psi.PsiDocumentManager;
45 import com.intellij.psi.PsiFile;
46 import com.intellij.psi.PsiFileFactory;
47 import com.intellij.psi.search.SearchScope;
48 import com.intellij.ui.EditorComboBoxRenderer;
49 import com.intellij.ui.EditorTextField;
50 import com.intellij.ui.IdeBorderFactory;
51 import com.intellij.ui.StateRestoringCheckBox;
52 import com.intellij.util.ArrayUtil;
53 import org.jetbrains.annotations.NonNls;
54
55 import javax.swing.*;
56 import java.awt.*;
57 import java.awt.event.*;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.HashMap;
61 import java.util.Map;
62 import java.util.regex.Pattern;
63 import java.util.regex.PatternSyntaxException;
64
65 final class FindDialog extends DialogWrapper {
66   private ComboBox myInputComboBox;
67   private ComboBox myReplaceComboBox;
68   private StateRestoringCheckBox myCbCaseSensitive;
69   private StateRestoringCheckBox myCbPreserveCase;
70   private StateRestoringCheckBox myCbWholeWordsOnly;
71   private StateRestoringCheckBox myCbInCommentsOnly;
72   private StateRestoringCheckBox myCbInStringLiteralsOnly;
73   private StateRestoringCheckBox myCbRegularExpressions;
74   private JRadioButton myRbGlobal;
75   private JRadioButton myRbSelectedText;
76   private JRadioButton myRbForward;
77   private JRadioButton myRbBackward;
78   private JRadioButton myRbFromCursor;
79   private JRadioButton myRbEntireScope;
80   private JRadioButton myRbProject;
81   private JRadioButton myRbDirectory;
82   private JRadioButton myRbModule;
83   private ComboBox myModuleComboBox;
84   private ComboBox myDirectoryComboBox;
85   private StateRestoringCheckBox myCbWithSubdirectories;
86   private JCheckBox myCbToOpenInNewTab;
87   private final FindModel myModel;
88   private final Runnable myOkHandler;
89   private FixedSizeButton mySelectDirectoryButton;
90   private StateRestoringCheckBox useFileFilter;
91   private ComboBox myFileFilter;
92   private JCheckBox myCbToSkipResultsWhenOneUsage;
93   private final Project myProject;
94   private final Map<EditorTextField, DocumentAdapter> myComboBoxListeners = new HashMap<EditorTextField, DocumentAdapter>();
95
96   private Action myFindAllAction;
97   private JRadioButton myRbCustomScope;
98   private ScopeChooserCombo myScopeCombo;
99
100   public FindDialog(Project project, FindModel model, Runnable myOkHandler){
101     super(project, true);
102     myProject = project;
103     myModel = model;
104     this.myOkHandler = myOkHandler;
105
106     if (myModel.isReplaceState()){
107       if (myModel.isMultipleFiles()){
108         setTitle(FindBundle.message("find.replace.in.project.dialog.title"));
109       }
110       else{
111         setTitle(FindBundle.message("find.replace.text.dialog.title"));
112       }
113     }
114     else{
115       setButtonsMargin(null);
116       if (myModel.isMultipleFiles()){
117         setTitle(FindBundle.message("find.in.path.dialog.title"));
118       }
119       else{
120         setTitle(FindBundle.message("find.text.dialog.title"));
121       }
122     }
123     setOKButtonText(FindBundle.message("find.button"));
124     setOKButtonIcon(IconLoader.getIcon("/actions/find.png"));
125     init();
126     initByModel();
127   }
128
129   @Override
130   protected void dispose() {
131     for(Map.Entry<EditorTextField, DocumentAdapter> e: myComboBoxListeners.entrySet()) {
132       e.getKey().removeDocumentListener(e.getValue());
133     }
134     myComboBoxListeners.clear();
135     super.dispose();
136   }
137
138   public JComponent getPreferredFocusedComponent() {
139     return myInputComboBox;
140   }
141
142   protected String getDimensionServiceKey() {
143     return myModel.isReplaceState() ? "replaceTextDialog" : "findTextDialog";
144   }
145
146   @Override
147   protected Action[] createActions() {
148     if (!myModel.isMultipleFiles() && !myModel.isReplaceState() && myModel.isFindAllEnabled()) {
149       return new Action[] { getFindAllAction(), getOKAction(), getCancelAction(), getHelpAction() };
150     }
151     return new Action[] { getOKAction(), getCancelAction(), getHelpAction() };
152   }
153
154   private Action getFindAllAction() {
155     return myFindAllAction = new AbstractAction(FindBundle.message("find.all.button")) {
156       public void actionPerformed(ActionEvent e) {
157         doOKAction(true);
158       }
159     };
160   }
161
162   public JComponent createNorthPanel() {
163     JPanel panel = new JPanel(new GridBagLayout());
164     GridBagConstraints gbConstraints = new GridBagConstraints();
165
166     gbConstraints.insets = new Insets(4, 4, 4, 4);
167     gbConstraints.fill = GridBagConstraints.VERTICAL;
168     gbConstraints.weightx = 0;
169     gbConstraints.weighty = 1;
170     gbConstraints.anchor = GridBagConstraints.EAST;
171     JLabel prompt = new JLabel(FindBundle.message("find.text.to.find.label"));
172     panel.add(prompt, gbConstraints);
173
174     myInputComboBox = new ComboBox(300);
175     revealWhitespaces(myInputComboBox);
176     initCombobox(myInputComboBox);
177
178     if (myModel.isReplaceState()){
179       myReplaceComboBox = new ComboBox(300);
180       revealWhitespaces(myReplaceComboBox);
181       
182       initCombobox(myReplaceComboBox);
183       final Component editorComponent = myReplaceComboBox.getEditor().getEditorComponent();
184       editorComponent.addFocusListener(
185         new FocusAdapter() {
186           public void focusGained(FocusEvent e) {
187             myReplaceComboBox.getEditor().selectAll();
188             editorComponent.removeFocusListener(this);
189           }
190         }
191       );
192     }
193
194
195     gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
196     gbConstraints.fill = GridBagConstraints.BOTH;
197     gbConstraints.weightx = 1;
198     panel.add(myInputComboBox, gbConstraints);
199     prompt.setLabelFor(myInputComboBox.getEditor().getEditorComponent());
200
201     if (myModel.isReplaceState()){
202       gbConstraints.gridwidth = GridBagConstraints.RELATIVE;
203       gbConstraints.fill = GridBagConstraints.VERTICAL;
204       gbConstraints.weightx = 0;
205       final JLabel replacePrompt = new JLabel(FindBundle.message("find.replace.with.label"));
206       panel.add(replacePrompt, gbConstraints);
207
208       gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
209       gbConstraints.fill = GridBagConstraints.BOTH;
210       gbConstraints.weightx = 1;
211       panel.add(myReplaceComboBox, gbConstraints);
212       replacePrompt.setLabelFor(myReplaceComboBox.getEditor().getEditorComponent());
213     }
214
215     return panel;
216   }
217
218   private void revealWhitespaces(ComboBox comboBox) {
219     ComboBoxEditor comboBoxEditor = new RevealingSpaceComboboxEditor(myProject, comboBox);
220     comboBox.setEditor(comboBoxEditor);
221     comboBox.setRenderer(new EditorComboBoxRenderer(comboBoxEditor));
222   }
223
224   private void initCombobox(final ComboBox comboBox) {
225     comboBox.setEditable(true);
226     comboBox.setMaximumRowCount(8);
227
228     comboBox.addActionListener(new ActionListener() {
229       public void actionPerformed(ActionEvent e) {
230         validateFindButton();
231       }
232     });
233
234     Component editorComponent = comboBox.getEditor().getEditorComponent();
235
236     if (editorComponent instanceof EditorTextField) {
237       final EditorTextField etf = (EditorTextField) editorComponent;
238
239       DocumentAdapter listener = new DocumentAdapter() {
240         public void documentChanged(final DocumentEvent e) {
241           handleComboBoxValueChanged(comboBox);
242         }
243       };
244       etf.addDocumentListener(listener);
245       myComboBoxListeners.put(etf, listener);
246     }
247     else {
248       editorComponent.addKeyListener(
249         new KeyAdapter() {
250           public void keyReleased(KeyEvent e) {
251             handleComboBoxValueChanged(comboBox);
252           }
253         }
254       );
255     }
256   }
257
258   private void handleComboBoxValueChanged(final ComboBox comboBox) {
259     Object item = comboBox.getEditor().getItem();
260     if (item != null && !item.equals(comboBox.getSelectedItem())){
261       int caretPosition = getCaretPosition(comboBox);
262       comboBox.setSelectedItem(item);
263       setCaretPosition(comboBox, caretPosition);
264     }
265     validateFindButton();
266   }
267
268   private static int getCaretPosition(JComboBox comboBox) {
269     Component editorComponent = comboBox.getEditor().getEditorComponent();
270     if (editorComponent instanceof JTextField){
271       JTextField textField = (JTextField)editorComponent;
272       return textField.getCaretPosition();
273     }
274     return 0;
275   }
276
277   private static void setCaretPosition(JComboBox comboBox, int position) {
278     Component editorComponent = comboBox.getEditor().getEditorComponent();
279     if (editorComponent instanceof JTextField){
280       JTextField textField = (JTextField)editorComponent;
281       textField.setCaretPosition(position);
282     }
283   }
284
285   private void validateFindButton() {
286     final String toFind = getStringToFind();
287
288     if (toFind == null || toFind.length() == 0){
289       setOKStatus(false);
290       return;
291     }
292
293     if (myRbDirectory != null && myRbDirectory.isSelected() &&
294       (getDirectory() == null || getDirectory().length() == 0)){
295       setOKStatus(false);
296       return;
297     }
298     setOKStatus(true);
299   }
300
301   private void setOKStatus(boolean value) {
302     setOKActionEnabled(value);
303     if (myFindAllAction != null) {
304       myFindAllAction.setEnabled(value);
305     }
306   }
307
308   public JComponent createCenterPanel() {
309     JPanel optionsPanel = new JPanel();
310     optionsPanel.setLayout(new GridBagLayout());
311
312     GridBagConstraints gbConstraints = new GridBagConstraints();
313     gbConstraints.weightx = 1;
314     gbConstraints.weighty = 1;
315     gbConstraints.fill = GridBagConstraints.BOTH;
316
317     gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
318     JPanel topOptionsPanel = new JPanel();
319
320     topOptionsPanel.setLayout(new GridLayout(1, 2, 8, 0));
321     optionsPanel.add(topOptionsPanel, gbConstraints);
322
323     topOptionsPanel.add(createFindOptionsPanel());
324     if (!myModel.isMultipleFiles()){
325       JPanel leftOptionsPanel = new JPanel();
326       leftOptionsPanel.setLayout(new GridLayout(3, 1, 0, 4));
327
328       leftOptionsPanel.add(createDirectionPanel());
329       leftOptionsPanel.add(createOriginPanel());
330       leftOptionsPanel.add(createScopePanel());
331       topOptionsPanel.add(leftOptionsPanel);
332     }
333     else{
334       optionsPanel.add(createGlobalScopePanel(), gbConstraints);
335       gbConstraints.weightx = 1;
336       gbConstraints.weighty = 1;
337       gbConstraints.fill = GridBagConstraints.HORIZONTAL;
338
339       gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
340       optionsPanel.add(createFilterPanel(),gbConstraints);
341
342       if (!myModel.isReplaceState()) {
343         myCbToSkipResultsWhenOneUsage = createCheckbox(FindSettings.getInstance().isSkipResultsWithOneUsage(), FindBundle.message("find.options.skip.results.tab.with.one.usage.checkbox"));
344         optionsPanel.add(myCbToSkipResultsWhenOneUsage, gbConstraints);
345       }
346     }
347
348     if (myModel.isOpenInNewTabVisible()){
349       JPanel openInNewTabWindowPanel = new JPanel(new BorderLayout());
350       myCbToOpenInNewTab = new JCheckBox(FindBundle.message("find.open.in.new.tab.checkbox"));
351       myCbToOpenInNewTab.setFocusable(false);
352       myCbToOpenInNewTab.setSelected(myModel.isOpenInNewTab());
353       myCbToOpenInNewTab.setEnabled(myModel.isOpenInNewTabEnabled());
354       openInNewTabWindowPanel.add(myCbToOpenInNewTab, BorderLayout.EAST);
355       optionsPanel.add(openInNewTabWindowPanel, gbConstraints);
356     }
357
358     return optionsPanel;
359   }
360
361   private JComponent createFilterPanel() {
362     JPanel filterPanel = new JPanel();
363     filterPanel.setLayout(new BorderLayout());
364     filterPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.filter.file.name.group")));
365
366     myFileFilter = new ComboBox(100);
367     initCombobox(myFileFilter);
368     filterPanel.add(useFileFilter = createCheckbox(FindBundle.message("find.filter.file.mask.checkbox")),BorderLayout.WEST);
369     filterPanel.add(myFileFilter,BorderLayout.CENTER);
370     myFileFilter.setEditable(true);
371     String[] fileMasks = FindSettings.getInstance().getRecentFileMasks();
372     for(int i=fileMasks.length-1; i >= 0; i--) {
373       myFileFilter.addItem(fileMasks [i]);
374     }
375     myFileFilter.setEnabled(false);
376
377     useFileFilter.addActionListener(
378       new ActionListener() {
379         public void actionPerformed(ActionEvent e) {
380           if (!useFileFilter.isSelected()) {
381             myFileFilter.setEnabled(false);
382           } else {
383             myFileFilter.setEnabled(true);
384             myFileFilter.getEditor().selectAll();
385             myFileFilter.getEditor().getEditorComponent().requestFocusInWindow();
386           }
387         }
388       }
389     );
390
391     return filterPanel;
392   }
393
394   public void doOKAction() {
395     doOKAction(false);
396   }
397
398   private void doOKAction(boolean findAll) {
399     FindModel validateModel = (FindModel)myModel.clone();
400     applyTo(validateModel);
401     validateModel.setFindAll(findAll);
402     if (validateModel.getDirectoryName() != null) {
403       PsiDirectory directory = FindInProjectUtil.getPsiDirectory(validateModel, myProject);
404       if (directory == null) {
405         Messages.showMessageDialog(
406           myProject,
407           FindBundle.message("find.directory.not.found.error", validateModel.getDirectoryName()),
408           CommonBundle.getErrorTitle(),
409           Messages.getErrorIcon()
410         );
411         return;
412       }
413     }
414
415     if (validateModel.isRegularExpressions()) {
416       String toFind = validateModel.getStringToFind();
417       try {
418         Pattern pattern = Pattern.compile(toFind, validateModel.isCaseSensitive() ? Pattern.MULTILINE : Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
419         if (pattern.matcher("").matches() && !toFind.endsWith("$") && !toFind.startsWith("^")) {
420           throw new PatternSyntaxException(FindBundle.message("find.empty.match.regular.expression.error"),toFind, -1);
421         }
422       }
423       catch(PatternSyntaxException e){
424         Messages.showMessageDialog(
425             myProject,
426             FindBundle.message("find.invalid.regular.expression.error", toFind, e.getDescription()),
427             CommonBundle.getErrorTitle(),
428             Messages.getErrorIcon()
429         );
430         return;
431       }
432     }
433
434     validateModel.setFileFilter( null );
435     FindSettings.getInstance().setFileMask(null);
436
437     if (useFileFilter!=null && useFileFilter.isSelected() &&
438         myFileFilter.getSelectedItem()!=null
439        ) {
440       final String mask = (String)myFileFilter.getSelectedItem();
441
442       if (mask.length() > 0) {
443         try {
444           FindInProjectUtil.createFileMaskRegExp(mask);   // verify that the regexp compiles
445           validateModel.setFileFilter(mask);
446           FindSettings.getInstance().setFileMask(mask);
447         }
448         catch (PatternSyntaxException ex) {
449           Messages.showMessageDialog(myProject, FindBundle.message("find.filter.invalid.file.mask.error", myFileFilter.getSelectedItem()),
450                                      CommonBundle.getErrorTitle(), Messages.getErrorIcon());
451           return;
452         }
453       }
454       else {
455         Messages.showMessageDialog(myProject, FindBundle.message("find.filter.empty.file.mask.error"), CommonBundle.getErrorTitle(),
456                                    Messages.getErrorIcon());
457         return;
458       }
459     }
460
461     if (myCbToSkipResultsWhenOneUsage != null){
462       FindSettings.getInstance().setSkipResultsWithOneUsage(
463         isSkipResultsWhenOneUsage()
464       );
465     }
466
467     myModel.copyFrom(validateModel);
468     super.doOKAction();
469     myOkHandler.run();
470   }
471
472   public void doHelpAction() {
473     String id;
474     if (myModel.isReplaceState()) {
475       if (myModel.isMultipleFiles()) {
476         id = HelpID.REPLACE_IN_PATH;
477       } else {
478         id = HelpID.REPLACE_OPTIONS;
479       }
480     } else {
481       if (myModel.isMultipleFiles()) {
482         id = HelpID.FIND_IN_PATH;
483       } else {
484         id = HelpID.FIND_OPTIONS;
485       }
486     }
487     HelpManager.getInstance().invokeHelp(id);
488   }
489
490   private boolean isSkipResultsWhenOneUsage() {
491     return myCbToSkipResultsWhenOneUsage!=null &&
492     myCbToSkipResultsWhenOneUsage.isSelected();
493   }
494
495   private JPanel createFindOptionsPanel() {
496     JPanel findOptionsPanel = new JPanel();
497     findOptionsPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.options.group")));
498     findOptionsPanel.setLayout(new BoxLayout(findOptionsPanel, BoxLayout.Y_AXIS));
499
500     myCbCaseSensitive = createCheckbox(FindBundle.message("find.options.case.sensitive"));
501     findOptionsPanel.add(myCbCaseSensitive);
502     if (myModel.isReplaceState()) {
503       myCbPreserveCase = createCheckbox(FindBundle.message("find.options.replace.preserve.case"));
504       findOptionsPanel.add(myCbPreserveCase);
505     }
506     myCbWholeWordsOnly = createCheckbox(FindBundle.message("find.options.whole.words.only"));
507
508     findOptionsPanel.add(myCbWholeWordsOnly);
509
510     myCbRegularExpressions = createCheckbox(FindBundle.message("find.options.regular.expressions"));
511     findOptionsPanel.add(myCbRegularExpressions);
512
513     myCbInCommentsOnly = createCheckbox(FindBundle.message("find.options.comments.only"));
514
515     findOptionsPanel.add(myCbInCommentsOnly);
516
517     myCbInStringLiteralsOnly = createCheckbox(FindBundle.message("find.options.string.literals.only"));
518
519     findOptionsPanel.add(myCbInStringLiteralsOnly);
520
521
522     ActionListener actionListener = new ActionListener() {
523       public void actionPerformed(ActionEvent e) {
524         updateControls();
525       }
526     };
527     myCbRegularExpressions.addActionListener(actionListener);
528     myCbRegularExpressions.addItemListener(new ItemListener() {
529       public void itemStateChanged(final ItemEvent e) {
530         setupRegExpSetting();
531       }
532     });
533
534     if (myModel.isReplaceState()) {
535       myCbCaseSensitive.addActionListener(actionListener);
536       myCbPreserveCase.addActionListener(actionListener);
537     }
538
539 //    if(isReplaceState) {
540 //      myCbPromptOnReplace = new JCheckBox("Prompt on replace", true);
541 //      myCbPromptOnReplace.setMnemonic('P');
542 //      findOptionsPanel.add(myCbPromptOnReplace);
543 //    }
544     return findOptionsPanel;
545   }
546
547   private void setupRegExpSetting() {
548     updateFileTypeForEditorComponent(myInputComboBox);
549     if (myReplaceComboBox != null) updateFileTypeForEditorComponent(myReplaceComboBox);
550   }
551
552   private void updateFileTypeForEditorComponent(final ComboBox inputComboBox) {
553     final Component editorComponent = inputComboBox.getEditor().getEditorComponent();
554
555     if (editorComponent instanceof EditorTextField) {
556       boolean selected = myCbRegularExpressions.isSelectedWhenSelectable();
557       final @NonNls String s = selected ? "*.regexp" : "*.txt";
558       FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(s);
559
560       if (selected && fileType == FileTypes.UNKNOWN) {
561         fileType = FileTypeManager.getInstance().getFileTypeByFileName("*.txt"); // RegExp plugin is not installed
562       }
563
564       final PsiFile file = PsiFileFactory.getInstance(myProject).createFileFromText(s, fileType, ((EditorTextField)editorComponent).getText(), -1, true);
565
566       ((EditorTextField)editorComponent).setNewDocumentAndFileType(fileType, PsiDocumentManager.getInstance(myProject).getDocument(file));
567     }
568   }
569
570   private void updateControls() {
571     if (myCbRegularExpressions.isSelected()) {
572       myCbWholeWordsOnly.makeUnselectable(false);
573     } else {
574       myCbWholeWordsOnly.makeSelectable();
575     }
576     if (myModel.isReplaceState()) {
577       if (myCbRegularExpressions.isSelected() || myCbCaseSensitive.isSelected()) {
578         myCbPreserveCase.makeUnselectable(false);
579       } else {
580         myCbPreserveCase.makeSelectable();
581       }
582
583       if (myCbPreserveCase.isSelected()) {
584         myCbRegularExpressions.makeUnselectable(false);
585         myCbCaseSensitive.makeUnselectable(false);
586       } else {
587         myCbRegularExpressions.makeSelectable();
588         myCbCaseSensitive.makeSelectable();
589       }
590     }
591
592     if (!myModel.isMultipleFiles()) {
593       myRbFromCursor.setEnabled(myRbGlobal.isSelected());
594       myRbEntireScope.setEnabled(myRbGlobal.isSelected());
595     }
596   }
597
598   private JPanel createDirectionPanel() {
599     JPanel directionPanel = new JPanel();
600     directionPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.direction.group")));
601     directionPanel.setLayout(new BoxLayout(directionPanel, BoxLayout.Y_AXIS));
602
603     myRbForward = new JRadioButton(FindBundle.message("find.direction.forward.radio"), true);
604     directionPanel.add(myRbForward);
605     myRbBackward = new JRadioButton(FindBundle.message("find.direction.backward.radio"));
606     directionPanel.add(myRbBackward);
607     ButtonGroup bgDirection = new ButtonGroup();
608     bgDirection.add(myRbForward);
609     bgDirection.add(myRbBackward);
610
611     return directionPanel;
612   }
613
614   private JComponent createGlobalScopePanel() {
615     JPanel scopePanel = new JPanel();
616     scopePanel.setLayout(new GridBagLayout());
617     scopePanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.scope.group")));
618     GridBagConstraints gbConstraints = new GridBagConstraints();
619     gbConstraints.fill = GridBagConstraints.HORIZONTAL;
620     gbConstraints.gridx = 0;
621     gbConstraints.gridy = 0;
622     gbConstraints.gridwidth = 2;
623     gbConstraints.weightx = 1;
624     myRbProject = new JRadioButton(FindBundle.message("find.scope.whole.project.radio"), true);
625     scopePanel.add(myRbProject, gbConstraints);
626
627     gbConstraints.gridx = 0;
628     gbConstraints.gridy++;
629     gbConstraints.weightx = 0;
630     gbConstraints.gridwidth = 1;
631     myRbModule = new JRadioButton(FindBundle.message("find.scope.module.radio"), false);
632     scopePanel.add(myRbModule, gbConstraints);
633
634     gbConstraints.gridx = 1;
635     gbConstraints.weightx = 1;
636     Module[] modules = ModuleManager.getInstance(myProject).getModules();
637     String[] names = new String[modules.length];
638     for (int i = 0; i < modules.length; i++) {
639       names[i] = modules[i].getName();
640     }
641
642     Arrays.sort(names,String.CASE_INSENSITIVE_ORDER);
643     myModuleComboBox = new ComboBox(names, -1);
644     scopePanel.add(myModuleComboBox, gbConstraints);
645
646     if (modules.length == 1) {
647       myModuleComboBox.setVisible(false);
648       myRbModule.setVisible(false);
649     }
650
651     gbConstraints.gridx = 0;
652     gbConstraints.gridy++;
653     gbConstraints.weightx = 0;
654     gbConstraints.gridwidth = 1;
655     myRbDirectory = new JRadioButton(FindBundle.message("find.scope.directory.radio"), false);
656     scopePanel.add(myRbDirectory, gbConstraints);
657
658     gbConstraints.gridx = 1;
659     gbConstraints.weightx = 1;
660
661     myDirectoryComboBox = new ComboBox(-1);
662     Component editorComponent = myDirectoryComboBox.getEditor().getEditorComponent();
663     if (editorComponent instanceof JTextField) {
664       JTextField field = (JTextField)editorComponent;
665       field.setColumns(40);
666     }
667     initCombobox(myDirectoryComboBox);
668     scopePanel.add(myDirectoryComboBox, gbConstraints);
669
670     gbConstraints.weightx = 0;
671     gbConstraints.gridx = 2;
672     gbConstraints.insets = new Insets(0, 1, 0, 0);
673     mySelectDirectoryButton = new FixedSizeButton(myDirectoryComboBox);
674     TextFieldWithBrowseButton.MyDoClickAction.addTo(mySelectDirectoryButton, myDirectoryComboBox);
675     mySelectDirectoryButton.setMargin(new Insets(0, 0, 0, 0));
676     scopePanel.add(mySelectDirectoryButton, gbConstraints);
677
678     gbConstraints.gridx = 0;
679     gbConstraints.gridy++;
680     gbConstraints.weightx = 1;
681     gbConstraints.gridwidth = 2;
682     gbConstraints.insets = new Insets(0, 16, 0, 0);
683     myCbWithSubdirectories = createCheckbox(true, FindBundle.message("find.scope.directory.recursive.checkbox"));
684     myCbWithSubdirectories.setSelected(true);
685     scopePanel.add(myCbWithSubdirectories, gbConstraints);
686
687
688     gbConstraints.gridx = 0;
689     gbConstraints.gridy++;
690     gbConstraints.weightx = 1;
691     gbConstraints.gridwidth = 1;
692     gbConstraints.insets = new Insets(0, 1, 0, 0);
693     myRbCustomScope = new JRadioButton(FindBundle.message("find.scope.custom.radio"), false);
694     scopePanel.add(myRbCustomScope, gbConstraints);
695
696     gbConstraints.gridx++;
697     gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
698     myScopeCombo = new ScopeChooserCombo(myProject, true, true, FindSettings.getInstance().getDefaultScopeName());
699     scopePanel.add(myScopeCombo, gbConstraints);
700
701
702     ButtonGroup bgScope = new ButtonGroup();
703     bgScope.add(myRbDirectory);
704     bgScope.add(myRbProject);
705     bgScope.add(myRbModule);
706     bgScope.add(myRbCustomScope);
707
708     myRbProject.addActionListener(new ActionListener() {
709       public void actionPerformed(ActionEvent e) {
710         validateScopeControls();
711         validateFindButton();
712       }
713     });
714     myRbCustomScope.addActionListener(new ActionListener() {
715       public void actionPerformed(ActionEvent e) {
716         validateScopeControls();
717         validateFindButton();
718       }
719     });
720
721     myRbDirectory.addActionListener(new ActionListener() {
722       public void actionPerformed(ActionEvent e) {
723         validateScopeControls();
724         validateFindButton();
725         myDirectoryComboBox.getEditor().getEditorComponent().requestFocusInWindow();
726       }
727     });
728
729     myRbModule.addActionListener(new ActionListener() {
730       public void actionPerformed(ActionEvent e) {
731         validateScopeControls();
732         validateFindButton();
733         myModuleComboBox.requestFocusInWindow();
734       }
735     });
736
737     mySelectDirectoryButton.addActionListener(new ActionListener() {
738       public void actionPerformed(ActionEvent e) {
739         FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
740         VirtualFile[] files = FileChooser.chooseFiles(myProject, descriptor);
741         if (files.length != 0) {
742           myDirectoryComboBox.setSelectedItem(files[0].getPresentableUrl());
743           validateFindButton();
744         }
745       }
746     });
747
748     return scopePanel;
749   }
750
751   private static StateRestoringCheckBox createCheckbox(String message) {
752     final StateRestoringCheckBox cb = new StateRestoringCheckBox(message);
753     cb.setFocusable(false);
754     return cb;
755   }
756
757   private static StateRestoringCheckBox createCheckbox(boolean selected, String message) {
758     final StateRestoringCheckBox cb = new StateRestoringCheckBox(message, selected);
759     cb.setFocusable(false);
760     return cb;
761   }
762
763   private void validateScopeControls() {
764     if (myRbDirectory.isSelected()) {
765       myCbWithSubdirectories.makeSelectable();
766     }
767     else {
768       myCbWithSubdirectories.makeUnselectable(myCbWithSubdirectories.isSelected());
769     }
770     myDirectoryComboBox.setEnabled(myRbDirectory.isSelected());
771     mySelectDirectoryButton.setEnabled(myRbDirectory.isSelected());
772
773     myModuleComboBox.setEnabled(myRbModule.isSelected());
774     myScopeCombo.setEnabled(myRbCustomScope.isSelected());
775   }
776
777   private JPanel createScopePanel() {
778     JPanel scopePanel = new JPanel();
779     scopePanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.scope.group")));
780     scopePanel.setLayout(new BoxLayout(scopePanel, BoxLayout.Y_AXIS));
781
782     myRbGlobal = new JRadioButton(FindBundle.message("find.scope.global.radio"), true);
783     scopePanel.add(myRbGlobal);
784     myRbSelectedText = new JRadioButton(FindBundle.message("find.scope.selected.text.radio"));
785     scopePanel.add(myRbSelectedText);
786     ButtonGroup bgScope = new ButtonGroup();
787     bgScope.add(myRbGlobal);
788     bgScope.add(myRbSelectedText);
789
790     ActionListener actionListener = new ActionListener() {
791       public void actionPerformed(ActionEvent e) {
792         updateControls();
793       }
794     };
795     myRbGlobal.addActionListener(actionListener);
796     myRbSelectedText.addActionListener(actionListener);
797
798     return scopePanel;
799   }
800
801   private JPanel createOriginPanel() {
802     JPanel originPanel = new JPanel();
803     originPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.origin.group")));
804     originPanel.setLayout(new BoxLayout(originPanel, BoxLayout.Y_AXIS));
805
806     myRbFromCursor = new JRadioButton(FindBundle.message("find.origin.from.cursor.radio"), true);
807     originPanel.add(myRbFromCursor);
808     myRbEntireScope = new JRadioButton(FindBundle.message("find.origin.entire.scope.radio"));
809     originPanel.add(myRbEntireScope);
810     ButtonGroup bgOrigin = new ButtonGroup();
811     bgOrigin.add(myRbFromCursor);
812     bgOrigin.add(myRbEntireScope);
813
814     return originPanel;
815   }
816
817   private String getStringToFind() {
818     return (String)myInputComboBox.getEditor().getItem();
819   }
820   private String getStringToReplace() {
821     return (String)myReplaceComboBox.getEditor().getItem();
822   }
823
824   private String getDirectory() {
825     return (String)myDirectoryComboBox.getSelectedItem();
826   }
827
828   private static void setStringsToComboBox(String[] strings, ComboBox combo, String selected) {
829     if (combo.getItemCount() > 0){
830       combo.removeAllItems();
831     }
832     if (selected != null && selected.indexOf('\n') < 0) {
833       strings = ArrayUtil.remove(strings, selected);
834       strings = ArrayUtil.append(strings, selected);
835     }
836     for(int i = strings.length - 1; i >= 0; i--){
837       combo.addItem(strings[i]);
838     }
839   }
840
841   private void setDirectories(ArrayList strings, String directoryName) {
842     if (myDirectoryComboBox.getItemCount() > 0){
843       myReplaceComboBox.removeAllItems();
844     }
845     if (directoryName != null && directoryName.length() > 0){
846       if (strings.contains(directoryName)){
847         strings.remove(directoryName);
848       }
849       myDirectoryComboBox.addItem(directoryName);
850     }
851     for(int i = strings.size() - 1; i >= 0; i--){
852       myDirectoryComboBox.addItem(strings.get(i));
853     }
854     if (myDirectoryComboBox.getItemCount() == 0){
855       myDirectoryComboBox.addItem("");
856     }
857   }
858
859   private void applyTo(FindModel model) {
860     FindSettings findSettings = FindSettings.getInstance();
861     model.setCaseSensitive(myCbCaseSensitive.isSelected());
862     findSettings.setCaseSensitive(myCbCaseSensitive.isSelected());
863
864     if (model.isReplaceState()) {
865       model.setPreserveCase(myCbPreserveCase.isSelected());
866       findSettings.setPreserveCase(myCbPreserveCase.isSelected());
867     }
868
869     model.setWholeWordsOnly(myCbWholeWordsOnly.isSelected());
870     findSettings.setWholeWordsOnly(myCbWholeWordsOnly.isSelected());
871     model.setInStringLiteralsOnly(myCbInStringLiteralsOnly.isSelected());
872     findSettings.setInStringLiteralsOnly(myCbInStringLiteralsOnly.isSelected());
873
874     model.setInCommentsOnly(myCbInCommentsOnly.isSelected());
875     findSettings.setInCommentsOnly(myCbInCommentsOnly.isSelected());
876
877     model.setRegularExpressions(myCbRegularExpressions.isSelected());
878     findSettings.setRegularExpressions(myCbRegularExpressions.isSelected());
879     model.setStringToFind(getStringToFind());
880
881     if (model.isReplaceState()){
882       model.setPromptOnReplace(true);
883       model.setReplaceAll(false);
884       String stringToReplace = getStringToReplace();
885       if (stringToReplace == null){
886         stringToReplace = "";
887       }
888       model.setStringToReplace(StringUtil.convertLineSeparators(stringToReplace));
889     }
890
891     if (!model.isMultipleFiles()){
892       model.setForward(myRbForward.isSelected());
893       findSettings.setForward(myRbForward.isSelected());
894       model.setFromCursor(myRbFromCursor.isSelected());
895       findSettings.setFromCursor(myRbFromCursor.isSelected());
896       model.setGlobal(myRbGlobal.isSelected());
897       findSettings.setGlobal(myRbGlobal.isSelected());
898     }
899     else{
900       if (myCbToOpenInNewTab != null){
901         model.setOpenInNewTab(myCbToOpenInNewTab.isSelected());
902       }
903
904       model.setProjectScope(myRbProject.isSelected());
905       model.setDirectoryName(null);
906       model.setModuleName(null);
907       model.setCustomScopeName(null);
908       model.setCustomScope(null);
909
910       if (myRbDirectory.isSelected()) {
911         String directory = getDirectory();
912         model.setDirectoryName(directory == null ? "" : directory);
913         model.setWithSubdirectories(myCbWithSubdirectories.isSelected());
914         findSettings.setWithSubdirectories(myCbWithSubdirectories.isSelected());
915       }
916       else if (myRbModule.isSelected()) {
917         model.setModuleName((String)myModuleComboBox.getSelectedItem());
918       }
919       else if (myRbCustomScope.isSelected()) {
920         SearchScope selectedScope = myScopeCombo.getSelectedScope();
921         String customScopeName = selectedScope == null ? null : selectedScope.getDisplayName();
922         model.setCustomScopeName(customScopeName);
923         model.setCustomScope(selectedScope == null ? null : selectedScope);
924         findSettings.setCustomScope(customScopeName);
925       }
926     }
927   }
928
929
930   private void initByModel() {
931     myCbCaseSensitive.setSelected(myModel.isCaseSensitive());
932     myCbWholeWordsOnly.setSelected(myModel.isWholeWordsOnly());
933     myCbInStringLiteralsOnly.setSelected(myModel.isInStringLiteralsOnly());
934     myCbInCommentsOnly.setSelected(myModel.isInCommentsOnly());
935     myCbRegularExpressions.setSelected(myModel.isRegularExpressions());
936
937     if (myModel.isMultipleFiles()) {
938       final String dirName = myModel.getDirectoryName();
939       setDirectories(FindSettings.getInstance().getRecentDirectories(), dirName);
940
941       if (!StringUtil.isEmptyOrSpaces(dirName)) {
942         VirtualFile dir = LocalFileSystem.getInstance().findFileByPath(dirName);
943         if (dir != null) {
944           Module module = ModuleUtil.findModuleForFile(dir, myProject);
945           if (module != null) {
946             myModuleComboBox.setSelectedItem(module.getName());
947           }
948         }
949       }
950
951       if (myModel.isProjectScope()) {
952         myRbProject.setSelected(true);
953
954         myCbWithSubdirectories.setEnabled(false);
955         myDirectoryComboBox.setEnabled(false);
956         mySelectDirectoryButton.setEnabled(false);
957         myModuleComboBox.setEnabled(false);
958         myScopeCombo.setEnabled(false);
959       }
960       else if (dirName != null) {
961         myRbDirectory.setSelected(true);
962         myCbWithSubdirectories.setEnabled(true);
963         myDirectoryComboBox.setEnabled(true);
964         mySelectDirectoryButton.setEnabled(true);
965         myModuleComboBox.setEnabled(false);
966         myScopeCombo.setEnabled(false);
967       }
968       else if (myModel.getModuleName() != null) {
969         myRbModule.setSelected(true);
970
971         myCbWithSubdirectories.setEnabled(false);
972         myDirectoryComboBox.setEnabled(false);
973         mySelectDirectoryButton.setEnabled(false);
974         myModuleComboBox.setEnabled(true);
975         myModuleComboBox.setSelectedItem(myModel.getModuleName());
976         myScopeCombo.setEnabled(false);
977
978         // force showing even if we have only one module
979         myRbModule.setVisible(true);
980         myModuleComboBox.setVisible(true);
981       }
982       else if (myModel.getCustomScopeName() != null) {
983         myRbCustomScope.setSelected(true);
984
985         myScopeCombo.setEnabled(true);
986         myScopeCombo.init(myProject, true, true, myModel.getCustomScopeName());
987
988         myCbWithSubdirectories.setEnabled(false);
989         myDirectoryComboBox.setEnabled(false);
990         mySelectDirectoryButton.setEnabled(false);
991         myModuleComboBox.setEnabled(false);
992       }
993       else {
994         assert false;
995       }
996
997       myCbWithSubdirectories.setSelected(myModel.isWithSubdirectories());
998
999       if (myModel.getFileFilter()!=null && myModel.getFileFilter().length() > 0) {
1000         myFileFilter.setSelectedItem(myModel.getFileFilter());
1001         myFileFilter.setEnabled(true);
1002         useFileFilter.setSelected(true);
1003       }
1004     }
1005     else {
1006       if (myModel.isForward()){
1007         myRbForward.setSelected(true);
1008       }
1009       else{
1010         myRbBackward.setSelected(true);
1011       }
1012
1013       if (myModel.isFromCursor()){
1014         myRbFromCursor.setSelected(true);
1015       }
1016       else{
1017         myRbEntireScope.setSelected(true);
1018       }
1019
1020       if (myModel.isGlobal()){
1021         myRbGlobal.setSelected(true);
1022       }
1023       else{
1024         myRbSelectedText.setSelected(true);
1025       }
1026     }
1027
1028     setStringsToComboBox(FindSettings.getInstance().getRecentFindStrings(), myInputComboBox, myModel.getStringToFind());
1029     if (myModel.isReplaceState()){
1030       myCbPreserveCase.setSelected(myModel.isPreserveCase());
1031       setStringsToComboBox(FindSettings.getInstance().getRecentReplaceStrings(), myReplaceComboBox, myModel.getStringToReplace());
1032     }
1033     updateControls();
1034     validateFindButton();
1035   }
1036 }
1037