switcher - actions for simplw tool windows
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / projectView / impl / ProjectViewImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.ide.projectView.impl;
18
19 import com.intellij.ProjectTopics;
20 import com.intellij.history.LocalHistory;
21 import com.intellij.history.LocalHistoryAction;
22 import com.intellij.ide.*;
23 import com.intellij.ide.FileEditorProvider;
24 import com.intellij.ide.impl.ProjectViewSelectInTarget;
25 import com.intellij.ide.projectView.HelpID;
26 import com.intellij.ide.projectView.ProjectView;
27 import com.intellij.ide.projectView.ProjectViewNode;
28 import com.intellij.ide.projectView.impl.nodes.*;
29 import com.intellij.ide.scopeView.ScopeViewPane;
30 import com.intellij.ide.ui.SplitterProportionsDataImpl;
31 import com.intellij.ide.util.DeleteHandler;
32 import com.intellij.ide.util.DirectoryChooserUtil;
33 import com.intellij.ide.util.EditorHelper;
34 import com.intellij.ide.util.treeView.AbstractTreeBuilder;
35 import com.intellij.ide.util.treeView.AbstractTreeNode;
36 import com.intellij.ide.util.treeView.NodeDescriptor;
37 import com.intellij.openapi.Disposable;
38 import com.intellij.openapi.actionSystem.*;
39 import com.intellij.openapi.application.ApplicationManager;
40 import com.intellij.openapi.application.ModalityState;
41 import com.intellij.openapi.application.ex.ApplicationInfoEx;
42 import com.intellij.openapi.command.CommandProcessor;
43 import com.intellij.openapi.components.PersistentStateComponent;
44 import com.intellij.openapi.components.State;
45 import com.intellij.openapi.components.Storage;
46 import com.intellij.openapi.diagnostic.Logger;
47 import com.intellij.openapi.editor.Editor;
48 import com.intellij.openapi.extensions.Extensions;
49 import com.intellij.openapi.fileEditor.*;
50 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
51 import com.intellij.openapi.module.Module;
52 import com.intellij.openapi.project.DumbAware;
53 import com.intellij.openapi.project.Project;
54 import com.intellij.openapi.roots.*;
55 import com.intellij.openapi.roots.ui.configuration.actions.ModuleDeleteProvider;
56 import com.intellij.openapi.ui.ComboBox;
57 import com.intellij.openapi.ui.Messages;
58 import com.intellij.openapi.ui.SimpleToolWindowPanel;
59 import com.intellij.openapi.ui.SplitterProportionsData;
60 import com.intellij.openapi.ui.popup.PopupChooserBuilder;
61 import com.intellij.openapi.util.*;
62 import com.intellij.openapi.vfs.JarFileSystem;
63 import com.intellij.openapi.vfs.LocalFileSystem;
64 import com.intellij.openapi.vfs.VfsUtil;
65 import com.intellij.openapi.vfs.VirtualFile;
66 import com.intellij.openapi.wm.IdeFocusManager;
67 import com.intellij.openapi.wm.ToolWindow;
68 import com.intellij.openapi.wm.ToolWindowId;
69 import com.intellij.openapi.wm.ToolWindowManager;
70 import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter;
71 import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
72 import com.intellij.psi.*;
73 import com.intellij.psi.impl.file.PsiDirectoryFactory;
74 import com.intellij.psi.util.PsiUtilBase;
75 import com.intellij.ui.AutoScrollFromSourceHandler;
76 import com.intellij.ui.AutoScrollToSourceHandler;
77 import com.intellij.ui.GuiUtils;
78 import com.intellij.ui.content.Content;
79 import com.intellij.ui.content.ContentManager;
80 import com.intellij.ui.switcher.QuickAccessProvider;
81 import com.intellij.ui.switcher.QuickActionProvider;
82 import com.intellij.util.Alarm;
83 import com.intellij.util.ArrayUtil;
84 import com.intellij.util.IJSwingUtilities;
85 import com.intellij.util.Icons;
86 import com.intellij.util.messages.MessageBusConnection;
87 import com.intellij.util.ui.UIUtil;
88 import com.intellij.util.ui.tree.TreeUtil;
89 import gnu.trove.THashMap;
90 import gnu.trove.THashSet;
91 import org.jdom.Attribute;
92 import org.jdom.Element;
93 import org.jetbrains.annotations.NonNls;
94 import org.jetbrains.annotations.NotNull;
95 import org.jetbrains.annotations.Nullable;
96
97 import javax.swing.*;
98 import javax.swing.border.EmptyBorder;
99 import javax.swing.event.PopupMenuEvent;
100 import javax.swing.event.PopupMenuListener;
101 import javax.swing.tree.DefaultMutableTreeNode;
102 import javax.swing.tree.DefaultTreeModel;
103 import javax.swing.tree.TreePath;
104 import java.awt.*;
105 import java.awt.event.FocusEvent;
106 import java.awt.event.FocusListener;
107 import java.util.*;
108 import java.util.List;
109
110 @State(
111   name="ProjectView",
112   storages= {
113     @Storage(
114       id="other",
115       file = "$WORKSPACE_FILE$"
116     )}
117 )
118 public final class ProjectViewImpl extends ProjectView implements PersistentStateComponent<Element>, Disposable, QuickActionProvider {
119   private static final Logger LOG = Logger.getInstance("#com.intellij.ide.projectView.impl.ProjectViewImpl");
120   private final CopyPasteDelegator myCopyPasteDelegator;
121   private boolean isInitialized;
122   private boolean myExtensionsLoaded = false;
123   private final Project myProject;
124
125   // + options
126   private final Map<String, Boolean> myFlattenPackages = new THashMap<String, Boolean>();
127   private static final boolean ourFlattenPackagesDefaults = false;
128   private final Map<String, Boolean> myShowMembers = new THashMap<String, Boolean>();
129   private static final boolean ourShowMembersDefaults = false;
130   private final Map<String, Boolean> mySortByType = new THashMap<String, Boolean>();
131   private static final boolean ourSortByTypeDefaults = false;
132   private final Map<String, Boolean> myShowModules = new THashMap<String, Boolean>();
133   private static final boolean ourShowModulesDefaults = true;
134   private final Map<String, Boolean> myShowLibraryContents = new THashMap<String, Boolean>();
135   private static final boolean ourShowLibraryContentsDefaults = true;
136   private final Map<String, Boolean> myHideEmptyPackages = new THashMap<String, Boolean>();
137   private static final boolean ourHideEmptyPackagesDefaults = true;
138   private final Map<String, Boolean> myAbbreviatePackageNames = new THashMap<String, Boolean>();
139   private static final boolean ourAbbreviatePackagesDefaults = false;
140   private final Map<String, Boolean> myAutoscrollToSource = new THashMap<String, Boolean>();
141   private static final boolean ourAutoscrollToSourceDefaults = false;
142   private final Map<String, Boolean> myAutoscrollFromSource = new THashMap<String, Boolean>();
143   private static final boolean ourAutoscrollFromSourceDefaults = false;
144   private static final boolean ourShowStructureDefaults = false;
145
146   private String myCurrentViewId;
147   private String myCurrentViewSubId;
148   // - options
149
150   private final AutoScrollToSourceHandler myAutoScrollToSourceHandler;
151   private final MyAutoScrollFromSourceHandler myAutoScrollFromSourceHandler;
152
153   private final IdeView myIdeView = new MyIdeView();
154   private final MyDeletePSIElementProvider myDeletePSIElementProvider = new MyDeletePSIElementProvider();
155   private final ModuleDeleteProvider myDeleteModuleProvider = new ModuleDeleteProvider();
156
157   private SimpleToolWindowPanel myPanel;
158   private final Map<String, AbstractProjectViewPane> myId2Pane = new LinkedHashMap<String, AbstractProjectViewPane>();
159   private final Collection<AbstractProjectViewPane> myUninitializedPanes = new THashSet<AbstractProjectViewPane>();
160
161   static final DataKey<ProjectViewImpl> DATA_KEY = DataKey.create("com.intellij.ide.projectView.impl.ProjectViewImpl");
162   @Deprecated static final String PROJECT_VIEW_DATA_CONSTANT = DATA_KEY.getName();
163
164   private DefaultActionGroup myActionGroup;
165   private final Runnable myTreeChangeListener;
166   private String mySavedPaneId = ProjectViewPane.ID;
167   private String mySavedPaneSubId;
168   private static final Icon COMPACT_EMPTY_MIDDLE_PACKAGES_ICON = IconLoader.getIcon("/objectBrowser/compactEmptyPackages.png");
169   private static final Icon HIDE_EMPTY_MIDDLE_PACKAGES_ICON = IconLoader.getIcon("/objectBrowser/hideEmptyPackages.png");
170   @NonNls private static final String ELEMENT_NAVIGATOR = "navigator";
171   @NonNls private static final String ELEMENT_PANES = "panes";
172   @NonNls private static final String ELEMENT_PANE = "pane";
173   @NonNls private static final String ATTRIBUTE_CURRENT_VIEW = "currentView";
174   @NonNls private static final String ATTRIBUTE_CURRENT_SUBVIEW = "currentSubView";
175   @NonNls private static final String ELEMENT_FLATTEN_PACKAGES = "flattenPackages";
176   @NonNls private static final String ELEMENT_SHOW_MEMBERS = "showMembers";
177   @NonNls private static final String ELEMENT_SHOW_MODULES = "showModules";
178   @NonNls private static final String ELEMENT_SHOW_LIBRARY_CONTENTS = "showLibraryContents";
179   @NonNls private static final String ELEMENT_HIDE_EMPTY_PACKAGES = "hideEmptyPackages";
180   @NonNls private static final String ELEMENT_ABBREVIATE_PACKAGE_NAMES = "abbreviatePackageNames";
181   @NonNls private static final String ELEMENT_AUTOSCROLL_TO_SOURCE = "autoscrollToSource";
182   @NonNls private static final String ELEMENT_AUTOSCROLL_FROM_SOURCE = "autoscrollFromSource";
183   @NonNls private static final String ELEMENT_SORT_BY_TYPE = "sortByType";
184   private static final String ATTRIBUTE_ID = "id";
185   private ComboBox myCombo;
186   private JPanel myViewContentPanel;
187   private JPanel myActionGroupPanel;
188   private JLabel myLabel;
189   private static final Comparator<AbstractProjectViewPane> PANE_WEIGHT_COMPARATOR = new Comparator<AbstractProjectViewPane>() {
190     public int compare(final AbstractProjectViewPane o1, final AbstractProjectViewPane o2) {
191       return o1.getWeight() - o2.getWeight();
192     }
193   };
194   private final FileEditorManager myFileEditorManager;
195   private final MyPanel myDataProvider;
196   private final SplitterProportionsData splitterProportions = new SplitterProportionsDataImpl();
197   private static final Icon BULLET_ICON = IconLoader.getIcon("/general/bullet.png");
198   private final MessageBusConnection myConnection;
199   private JPanel myTopPanel;
200   private ActionToolbar myToolBar;
201   private final Map<String, Element> myUninitializedPaneState = new HashMap<String, Element>();
202   private final Map<String, SelectInTarget> mySelectInTargets = new HashMap<String, SelectInTarget>();
203
204   public ProjectViewImpl(Project project, final FileEditorManager fileEditorManager, final ToolWindowManagerEx toolWindowManager) {
205     myProject = project;
206
207     constructUi();
208
209
210     Disposer.register(myProject, this);
211     myFileEditorManager = fileEditorManager;
212     myTreeChangeListener = new Runnable() {
213       public void run() {
214         updateToolWindowTitle();
215       }
216     };
217
218     myConnection = project.getMessageBus().connect();
219     myConnection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
220       public void beforeRootsChange(ModuleRootEvent event) {
221       }
222
223       public void rootsChanged(ModuleRootEvent event) {
224         refresh();
225       }
226     });
227
228     myAutoScrollFromSourceHandler = new MyAutoScrollFromSourceHandler();
229
230     myDataProvider = new MyPanel();
231     myDataProvider.add(myPanel, BorderLayout.CENTER);
232     myCopyPasteDelegator = new CopyPasteDelegator(myProject, myPanel) {
233       @NotNull
234       protected PsiElement[] getSelectedElements() {
235         final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
236         return viewPane == null ? PsiElement.EMPTY_ARRAY : viewPane.getSelectedPSIElements();
237       }
238     };
239     myAutoScrollToSourceHandler = new AutoScrollToSourceHandler() {
240     protected boolean isAutoScrollMode() {
241       return isAutoscrollToSource(myCurrentViewId);
242     }
243
244     protected void setAutoScrollMode(boolean state) {
245       setAutoscrollToSource(state, myCurrentViewId);
246     }
247   };
248     toolWindowManager.addToolWindowManagerListener(new ToolWindowManagerAdapter(){
249       private boolean toolWindowVisible;
250
251       public void stateChanged() {
252         ToolWindow window = toolWindowManager.getToolWindow(ToolWindowId.PROJECT_VIEW);
253         if (window == null) return;
254         if (window.isVisible() && !toolWindowVisible) {
255           String id = getCurrentViewId();
256           if (isAutoscrollToSource(id)) {
257             myAutoScrollToSourceHandler.onMouseClicked(getCurrentProjectViewPane().getTree());
258           }
259           if (isAutoscrollFromSource(id)) {
260             myAutoScrollFromSourceHandler.setAutoScrollMode(true);
261           }
262         }
263         toolWindowVisible = window.isVisible();
264       }
265     });
266   }
267
268   private void constructUi() {
269     myActionGroupPanel = new JPanel(new BorderLayout());
270
271     myLabel = new JLabel("View as:");
272     if (!SystemInfo.isMac) { // See IDEADEV-41315
273       myLabel.setDisplayedMnemonic('a');
274     }
275     myCombo = new ComboBox();
276     myCombo.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0));
277     myLabel.setLabelFor(myCombo);
278
279     final JPanel combo = new JPanel(new BorderLayout());
280     combo.setBorder(new EmptyBorder(4, 4, 4, 4));
281     combo.add(myLabel, BorderLayout.WEST);
282     combo.add(myCombo, BorderLayout.CENTER);
283
284
285     myTopPanel = new JPanel(new GridBagLayout());
286     myTopPanel.add(combo, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
287     myTopPanel.add(myActionGroupPanel, new GridBagConstraints(1, 0, 1, 1, 0.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
288
289     myViewContentPanel = new JPanel();
290     myPanel = new SimpleToolWindowPanel(true).setProvideQuickActions(false);
291     myPanel.setToolbar(myTopPanel);
292     myPanel.setContent(myViewContentPanel);
293
294     myPanel.setBorder(new ToolWindow.Border(true, false, false, false));
295   }
296
297   public String getName() {
298     return "Project";
299   }
300
301   public List<AnAction> getActions(boolean originalProvider) {
302     ArrayList<AnAction> result = new ArrayList<AnAction>();
303
304     DefaultActionGroup views = new DefaultActionGroup("Change View", true);
305     boolean lastWasHeader = false;
306     boolean lastHeaderHadKids = false;
307     for (int i = 0; i < myCombo.getModel().getSize(); i++) {
308       Object each = myCombo.getModel().getElementAt(i);
309       if (each instanceof Pair) {
310         Pair<String, String> eachPair = (Pair<String, String>)each;
311
312         if (eachPair.getSecond() == null) {
313           if (lastHeaderHadKids) {
314             views.add(new Separator());
315           } else {
316             if (i + 1 < myCombo.getModel().getSize()) {
317               Object next = myCombo.getModel().getElementAt(i + 1);
318               if (next instanceof Pair) {
319                 if (((Pair)next).getSecond() != null) {
320                   views.add(new Separator());
321                 }
322               }
323             }
324           }
325         } else {
326           lastHeaderHadKids = true;
327         }
328
329         lastWasHeader = eachPair.getSecond() == null;
330
331         views.add(new ChangeViewAction(eachPair.getFirst(), eachPair.getSecond()));
332       }
333     }
334     result.add(views);
335     result.add(new Separator());
336
337
338     ArrayList<AnAction> secondary = new ArrayList<AnAction>();
339     if (myActionGroup != null) {
340       AnAction[] kids = myActionGroup.getChildren(null);
341       for (AnAction each : kids) {
342         if (myActionGroup.isPrimary(each)) {
343           result.add(each);
344         } else {
345           secondary.add(each);
346         }
347       }
348     }
349     result.add(new Separator());
350     result.addAll(secondary);
351
352     return result;
353   }
354
355   private class ChangeViewAction extends AnAction {
356     private String myId;
357     private String mySubId;
358
359     private ChangeViewAction(String id, String subId) {
360       myId = id;
361       mySubId = subId;
362     }
363
364     @Override
365     public void update(AnActionEvent e) {
366       AbstractProjectViewPane pane = getProjectViewPaneById(myId);
367       e.getPresentation().setText(pane.getTitle() + (mySubId != null ? (" - " + pane.getPresentableSubIdName(mySubId)) : ""));
368     }
369
370     @Override
371     public void actionPerformed(AnActionEvent e) {
372       changeView(myId, mySubId);
373     }
374   }
375
376   public boolean isCycleRoot() {
377     return false;
378   }
379
380   public synchronized void addProjectPane(final AbstractProjectViewPane pane) {
381     myUninitializedPanes.add(pane);
382     SelectInTarget selectInTarget = pane.createSelectInTarget();
383     if (selectInTarget != null) {
384       mySelectInTargets.put(pane.getId(), selectInTarget);
385     }                                   
386     if (isInitialized) {
387       doAddUninitializedPanes();
388     }
389   }
390
391   public synchronized void removeProjectPane(AbstractProjectViewPane pane) {
392     myUninitializedPanes.remove(pane);
393     //assume we are completely initialized here
394     String idToRemove = pane.getId();
395
396     if (!myId2Pane.containsKey(idToRemove)) return;
397     pane.removeTreeChangeListener();
398     for (int i = myCombo.getItemCount() - 1; i >= 0; i--) {
399       Pair<String, String> ids = (Pair<String, String>)myCombo.getItemAt(i);
400       String id = ids.first;
401       if (id.equals(idToRemove)) {
402         if (i == myCombo.getSelectedIndex()) {
403           myCombo.setSelectedIndex(0);
404         }
405         myCombo.removeItemAt(i);
406       }
407     }
408     myId2Pane.remove(idToRemove);
409     mySelectInTargets.remove(idToRemove);
410     viewSelectionChanged();
411   }
412
413   private synchronized void doAddUninitializedPanes() {
414     for (AbstractProjectViewPane pane : myUninitializedPanes) {
415       doAddPane(pane);
416     }
417     if (myCombo.getSelectedItem() == null) { //old selection isn't available anymore
418       final DefaultComboBoxModel comboBoxModel = (DefaultComboBoxModel)myCombo.getModel();
419       final int size = comboBoxModel.getSize();
420       if (size > 0) {
421         final Pair<String, String> ids = (Pair<String, String>)comboBoxModel.getElementAt(size - 1);
422         changeView(ids.first, ids.second);
423       }
424     }
425     myUninitializedPanes.clear();
426   }
427
428   private void doAddPane(final AbstractProjectViewPane newPane) {
429     int index;
430     for (index = 0; index < myCombo.getItemCount(); index++) {
431       Pair<String, String> ids = (Pair<String, String>)myCombo.getItemAt(index);
432       String id = ids.first;
433       AbstractProjectViewPane pane = myId2Pane.get(id);
434
435       int comp = PANE_WEIGHT_COMPARATOR.compare(pane, newPane);
436       if (comp == 0) {
437         System.out.println("here");
438       }
439       LOG.assertTrue(comp != 0);
440       if (comp > 0) {
441         break;
442       }
443     }
444     final String id = newPane.getId();
445     myId2Pane.put(id, newPane);
446     String[] subIds = newPane.getSubIds();
447     subIds = ArrayUtil.mergeArrays(new String[]{null}, subIds, String.class);
448     for (String subId : subIds) {
449       myCombo.insertItemAt(Pair.create(id, subId), index++);
450     }
451     myCombo.setMaximumRowCount(myCombo.getItemCount());
452
453     if (id.equals(mySavedPaneId)) {
454       changeView(mySavedPaneId, mySavedPaneSubId);
455       mySavedPaneId = null;
456       mySavedPaneSubId = null;
457     }
458
459     Disposer.register(this, newPane);
460   }
461
462   private void showPane(AbstractProjectViewPane newPane) {
463     AbstractProjectViewPane currentPane = getCurrentProjectViewPane();
464     PsiElement selectedPsiElement = null;
465     Module selectedModule = null;
466     if (currentPane != null) {
467       if (currentPane != newPane) {
468         currentPane.saveExpandedPaths();
469       }
470       final PsiElement[] elements = currentPane.getSelectedPSIElements();
471       if (elements.length > 0) {
472         selectedPsiElement = elements[0];
473       } else {
474         Object selected = currentPane.getSelectedElement();
475         if (selected instanceof Module) {
476           selectedModule = (Module)selected;
477         }
478       }
479     }
480     removeLabelFocusListener();
481     myViewContentPanel.removeAll();
482     JComponent component = newPane.createComponent();
483     UIUtil.removeScrollBorder(component);
484     myViewContentPanel.setLayout(new BorderLayout());
485     myViewContentPanel.add(component, BorderLayout.CENTER);
486     myCurrentViewId = newPane.getId();
487     String newSubId = myCurrentViewSubId = newPane.getSubId();
488     myViewContentPanel.revalidate();
489     myViewContentPanel.repaint();
490     createToolbarActions();
491     myToolBar.updateActionsImmediately();
492     myTopPanel.revalidate();
493
494     newPane.setTreeChangeListener(myTreeChangeListener);
495     myAutoScrollToSourceHandler.install(newPane.myTree);
496
497     IdeFocusManager.getInstance(myProject).requestFocus(newPane.getComponentToFocus(), false);
498     updateToolWindowTitle();
499
500     newPane.restoreExpandedPaths();
501     if (selectedPsiElement != null) {
502       final VirtualFile virtualFile = PsiUtilBase.getVirtualFile(selectedPsiElement);
503       if (virtualFile != null && ((ProjectViewSelectInTarget)newPane.createSelectInTarget()).isSubIdSelectable(newSubId, new SelectInContext() {
504         @NotNull
505         public Project getProject() {
506           return myProject;
507         }
508
509         @NotNull
510         public VirtualFile getVirtualFile() {
511           return virtualFile;
512         }
513
514         public Object getSelectorInFile() {
515           return null;
516         }
517
518         public FileEditorProvider getFileEditorProvider() {
519           return null;
520         }
521       })) {
522         newPane.select(selectedPsiElement, virtualFile, true);
523       }
524     }
525     myAutoScrollToSourceHandler.onMouseClicked(newPane.myTree);
526     installLabelFocusListener();
527   }
528
529   // public for tests
530   public synchronized void setupImpl(final ToolWindow toolWindow) {
531     setupImpl(toolWindow, true);
532   }
533
534   // public for tests
535   public synchronized void setupImpl(final ToolWindow toolWindow, final boolean loadPaneExtensions) {
536     myCombo.setRenderer(new DefaultListCellRenderer() {
537       public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
538         if (value == null) return this;
539         Pair<String, String> ids = (Pair<String, String>)value;
540         String id = ids.first;
541         String subId = ids.second;
542         AbstractProjectViewPane pane = getProjectViewPaneById(id);
543         super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
544         if (pane != null) {
545           if (subId == null) {
546             setText(pane.getTitle());
547             setIcon(pane.getIcon());
548           }
549           else {
550             String presentable = pane.getPresentableSubIdName(subId);
551             if (index == -1) {
552               setText(presentable);
553               setIcon(pane.getIcon());
554             }
555             else {
556               // indent sub id
557               setText(presentable);
558               setIcon(BULLET_ICON);
559             }
560           }
561         }
562         return this;
563       }
564     });
565     myCombo.setMinimumAndPreferredWidth(10);
566
567     myActionGroup = new DefaultActionGroup();
568
569     myAutoScrollFromSourceHandler.install();
570
571     myToolBar = ActionManager.getInstance().createActionToolbar(ActionPlaces.PROJECT_VIEW_TOOLBAR, myActionGroup, true);
572     myToolBar.setSecondaryActionsTooltip("View Options");
573     JComponent toolbarComponent = myToolBar.getComponent();
574     myActionGroupPanel.setLayout(new BorderLayout());
575     myActionGroupPanel.add(toolbarComponent, BorderLayout.CENTER);
576
577     if (toolWindow != null) {
578       final ContentManager contentManager = toolWindow.getContentManager();
579       final Content content = contentManager.getFactory().createContent(getComponent(), ToolWindowId.PROJECT_VIEW, false);
580       contentManager.addContent(content);
581
582       content.setPreferredFocusedComponent(new Computable<JComponent>() {
583         public JComponent compute() {
584           final AbstractProjectViewPane current = getCurrentProjectViewPane();
585           return current != null ? current.getComponentToFocus() : null;
586         }
587       });
588       toolWindow.setIcon(IconLoader.getIcon(ApplicationInfoEx.getInstanceEx().getToolWindowIconUrl()));
589     }
590
591     myCombo.addPopupMenuListener(new PopupMenuListener() {
592       public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
593
594       }
595
596       public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
597         if (!viewSelectionChanged()) {
598           ToolWindowManager.getInstance(myProject).activateEditorComponent();
599         }
600       }
601
602       public void popupMenuCanceled(PopupMenuEvent e) {
603         ToolWindowManager.getInstance(myProject).activateEditorComponent();
604       }
605     });
606     installLabelFocusListener();
607
608     GuiUtils.replaceJSplitPaneWithIDEASplitter(myPanel);
609     SwingUtilities.invokeLater(new Runnable() {
610       public void run() {
611         splitterProportions.restoreSplitterProportions(myPanel);
612       }
613     });
614
615     if (loadPaneExtensions) {
616       ensurePanesLoaded();
617     }
618
619     isInitialized = true;
620     doAddUninitializedPanes();
621   }
622
623   private void ensurePanesLoaded() {
624     if (myExtensionsLoaded) return;
625     myExtensionsLoaded = true;
626     for(AbstractProjectViewPane pane: Extensions.getExtensions(AbstractProjectViewPane.EP_NAME, myProject)) {
627       if (myUninitializedPaneState.containsKey(pane.getId())) {
628         try {
629           pane.readExternal(myUninitializedPaneState.get(pane.getId()));
630         }
631         catch (InvalidDataException e) {
632           // ignore
633         }
634         myUninitializedPaneState.remove(pane.getId());
635       }
636       if (pane.isInitiallyVisible() && !myId2Pane.containsKey(pane.getId())) {
637         addProjectPane(pane);
638       }
639       Disposer.register(this, pane);
640     }
641   }
642
643   private final FocusListener myLabelFocusListener = new FocusListener() {
644     public void focusGained(FocusEvent e) {
645       if (!myCombo.isPopupVisible() && myCombo.isShowing()) {
646         myCombo.requestFocusInWindow();
647         myCombo.showPopup();
648       }
649     }
650
651     public void focusLost(FocusEvent e) {
652
653     }
654   };
655
656   private void installLabelFocusListener() {
657     myLabel.addFocusListener(myLabelFocusListener);
658   }
659
660   private void removeLabelFocusListener() {
661     myLabel.removeFocusListener(myLabelFocusListener);
662   }
663
664   private boolean viewSelectionChanged() {
665     Pair<String, String> ids = (Pair<String, String>)myCombo.getSelectedItem();
666     if (ids == null) return false;
667     final String id = ids.first;
668     String subId = ids.second;
669     if (ids.equals(Pair.create(myCurrentViewId, myCurrentViewSubId))) return false;
670     final AbstractProjectViewPane newPane = getProjectViewPaneById(id);
671     if (newPane == null) return false;
672     newPane.setSubId(subId);
673     String[] subIds = newPane.getSubIds();
674
675     if (subId == null && subIds.length != 0) {
676       final String firstNonTrivialSubId = subIds[0];
677       SwingUtilities.invokeLater(new Runnable() {
678         public void run() {
679           changeView(id, firstNonTrivialSubId);
680           newPane.setSubId(firstNonTrivialSubId);
681         }
682       });
683     }
684     else {
685       showPane(newPane);
686     }
687     return true;
688   }
689
690   private void createToolbarActions() {
691     myActionGroup.removeAll();
692     if (ProjectViewDirectoryHelper.getInstance(myProject).supportsFlattenPackages()) {
693       myActionGroup.addAction(new PaneOptionAction(myFlattenPackages, IdeBundle.message("action.flatten.packages"),
694                                              IdeBundle.message("action.flatten.packages"), Icons.FLATTEN_PACKAGES_ICON,
695                                              ourFlattenPackagesDefaults) {
696         public void setSelected(AnActionEvent event, boolean flag) {
697           final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
698           final SelectionInfo selectionInfo = SelectionInfo.create(viewPane);
699
700           super.setSelected(event, flag);
701
702           selectionInfo.apply(viewPane);
703         }
704       }).setAsSecondary(true);
705     }
706
707     class FlattenPackagesDependableAction extends PaneOptionAction {
708       FlattenPackagesDependableAction(Map<String, Boolean> optionsMap,
709                                              final String text,
710                                              final String description,
711                                              final Icon icon,
712                                              boolean optionDefaultValue) {
713         super(optionsMap, text, description, icon, optionDefaultValue);
714       }
715
716       public void update(AnActionEvent e) {
717         super.update(e);
718         final Presentation presentation = e.getPresentation();
719         presentation.setVisible(isFlattenPackages(myCurrentViewId));
720       }
721     }
722     if (ProjectViewDirectoryHelper.getInstance(myProject).supportsHideEmptyMiddlePackages()) {
723       myActionGroup.addAction(new HideEmptyMiddlePackagesAction()).setAsSecondary(true);
724     }
725     if (ProjectViewDirectoryHelper.getInstance(myProject).supportsFlattenPackages()) {
726       myActionGroup.addAction(new FlattenPackagesDependableAction(myAbbreviatePackageNames,
727                                                             IdeBundle.message("action.abbreviate.qualified.package.names"),
728                                                             IdeBundle.message("action.abbreviate.qualified.package.names"),
729                                                             IconLoader.getIcon("/objectBrowser/abbreviatePackageNames.png"),
730                                                             ourAbbreviatePackagesDefaults) {
731         public boolean isSelected(AnActionEvent event) {
732           return super.isSelected(event) && isAbbreviatePackageNames(myCurrentViewId);
733         }
734
735
736         public void update(AnActionEvent e) {
737           super.update(e);
738           if (ScopeViewPane.ID.equals(myCurrentViewId)) {
739             e.getPresentation().setEnabled(false);
740           }
741         }
742       }).setAsSecondary(true);
743     }
744     myActionGroup.addAction(new PaneOptionAction(myShowMembers, IdeBundle.message("action.show.members"),
745                                            IdeBundle.message("action.show.hide.members"),
746                                            IconLoader.getIcon("/objectBrowser/showMembers.png"), ourShowMembersDefaults)).setAsSecondary(true);
747     myActionGroup.addAction(myAutoScrollToSourceHandler.createToggleAction()).setAsSecondary(true);
748     myActionGroup.addAction(myAutoScrollFromSourceHandler.createToggleAction()).setAsSecondary(true);
749     myActionGroup.addAction(new SortByTypeAction()).setAsSecondary(true);
750
751     myActionGroup.addAction(new ScrollFromSourceAction());
752     AnAction collapseAllAction = CommonActionsManager.getInstance().createCollapseAllAction(new TreeExpander() {
753       public void expandAll() {
754
755       }
756
757       public boolean canExpand() {
758         return false;
759       }
760
761       public void collapseAll() {
762         AbstractProjectViewPane pane = getCurrentProjectViewPane();
763         JTree tree = pane.myTree;
764         if (tree != null) {
765           TreeUtil.collapseAll(tree, -1);
766         }
767       }
768
769       public boolean canCollapse() {
770         return true;
771       }
772     }, getComponent());
773     myActionGroup.add(collapseAllAction);
774
775     getCurrentProjectViewPane().addToolbarActions(myActionGroup);
776   }
777
778   public AbstractProjectViewPane getProjectViewPaneById(String id) {
779     if (!ApplicationManager.getApplication().isUnitTestMode()) {   // most tests don't need all panes to be loaded
780       ensurePanesLoaded();
781     }
782
783     final AbstractProjectViewPane pane = myId2Pane.get(id);
784     if (pane != null) {
785       return pane;
786     }
787     for (AbstractProjectViewPane viewPane : myUninitializedPanes) {
788       if (viewPane.getId().equals(id)) {
789         return viewPane;
790       }
791     }
792     return null;
793   }
794
795   public AbstractProjectViewPane getCurrentProjectViewPane() {
796     return getProjectViewPaneById(myCurrentViewId);
797   }
798
799   public void refresh() {
800     AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane();
801     if (currentProjectViewPane != null) {
802       // may be null for e.g. default project
803       currentProjectViewPane.updateFromRoot(false);
804     }
805   }
806
807   public void select(final Object element, VirtualFile file, boolean requestFocus) {
808     final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
809     if (viewPane != null) {
810       viewPane.select(element, file, requestFocus);
811     }
812   }
813
814   public ActionCallback selectCB(Object element, VirtualFile file, boolean requestFocus) {
815     final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
816     if (viewPane != null && viewPane instanceof AbstractProjectViewPSIPane) {
817       return ((AbstractProjectViewPSIPane) viewPane).selectCB(element, file, requestFocus);
818     } else {
819       select(element, file, requestFocus);
820       return new ActionCallback.Done();
821     }
822   }
823
824   public void dispose() {
825     myConnection.disconnect();
826   }
827
828   public JComponent getComponent() {
829     return myDataProvider;
830   }
831
832   public String getCurrentViewId() {
833     return myCurrentViewId;
834   }
835
836   private void updateToolWindowTitle() {
837     ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
838     ToolWindow toolWindow = toolWindowManager == null ? null : toolWindowManager.getToolWindow(ToolWindowId.PROJECT_VIEW);
839     if (toolWindow == null) return;
840     String title = null;
841     final AbstractProjectViewPane pane = getCurrentProjectViewPane();
842     if (pane != null) {
843       final DefaultMutableTreeNode selectedNode = pane.getSelectedNode();
844       if (selectedNode != null) {
845         final Object o = selectedNode.getUserObject();
846         if (o instanceof ProjectViewNode) {
847           title = ((ProjectViewNode)o).getTitle();
848         }
849       }
850     }
851     if (title == null) {
852       final PsiElement element = (PsiElement)myDataProvider.getData(LangDataKeys.PSI_ELEMENT.getName());
853       if (element != null) {
854         PsiFile file = element.getContainingFile();
855         if (file != null) {
856           title = file.getVirtualFile().getPresentableUrl();
857         }
858         else if (element instanceof PsiDirectory) {
859           title = PsiDirectoryFactory.getInstance(myProject).getQualifiedName((PsiDirectory) element, true);
860         }
861         else {
862           title = element.toString();
863         }
864       }
865       else {
866         title = "";
867         if (myProject != null) {
868           title = myProject.getPresentableUrl();
869         }
870       }
871     }
872
873     toolWindow.setTitle(title);
874   }
875
876   public PsiElement getParentOfCurrentSelection() {
877     final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
878     if (viewPane == null) {
879       return null;
880     }
881     TreePath path = viewPane.getSelectedPath();
882     if (path == null) {
883       return null;
884     }
885     path = path.getParentPath();
886     if (path == null) {
887       return null;
888     }
889     DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
890     Object userObject = node.getUserObject();
891     if (userObject instanceof ProjectViewNode) {
892       ProjectViewNode descriptor = (ProjectViewNode)userObject;
893       Object element = descriptor.getValue();
894       if (element instanceof PsiElement) {
895         PsiElement psiElement = (PsiElement)element;
896         if (!psiElement.isValid()) return null;
897         return psiElement;
898       }
899       else {
900         return null;
901       }
902     }
903     return null;
904   }
905
906
907   private class PaneOptionAction extends ToggleAction implements DumbAware {
908     private final Map<String, Boolean> myOptionsMap;
909     private final boolean myOptionDefaultValue;
910
911     PaneOptionAction(Map<String, Boolean> optionsMap,
912                      final String text,
913                      final String description,
914                      final Icon icon,
915                      boolean optionDefaultValue) {
916       super(text, description, icon);
917       myOptionsMap = optionsMap;
918       myOptionDefaultValue = optionDefaultValue;
919     }
920
921     public boolean isSelected(AnActionEvent event) {
922       return getPaneOptionValue(myOptionsMap, myCurrentViewId, myOptionDefaultValue);
923     }
924
925     public void setSelected(AnActionEvent event, boolean flag) {
926       setPaneOption(myOptionsMap, flag, myCurrentViewId, true);
927     }
928   }
929
930   public void changeView() {
931     final List<AbstractProjectViewPane> views = new ArrayList<AbstractProjectViewPane>(myId2Pane.values());
932     views.remove(getCurrentProjectViewPane());
933     Collections.sort(views, PANE_WEIGHT_COMPARATOR);
934
935     final JList list = new JList(ArrayUtil.toObjectArray(views));
936     list.setCellRenderer(new DefaultListCellRenderer() {
937       public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
938         super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
939         AbstractProjectViewPane pane = (AbstractProjectViewPane)value;
940         setText(pane.getTitle());
941         return this;
942       }
943     });
944
945     if (!views.isEmpty()) {
946       list.setSelectedValue(views.get(0), true);
947     }
948     Runnable runnable = new Runnable() {
949       public void run() {
950         if (list.getSelectedIndex() < 0) return;
951         AbstractProjectViewPane pane = (AbstractProjectViewPane)list.getSelectedValue();
952         changeView(pane.getId());
953       }
954     };
955
956     new PopupChooserBuilder(list).
957       setTitle(IdeBundle.message("title.popup.views")).
958       setItemChoosenCallback(runnable).
959       createPopup().showInCenterOf(getComponent());
960   }
961
962   public void changeView(@NotNull String viewId) {
963     changeView(viewId, null);
964   }
965
966   public void changeView(@NotNull String viewId, @Nullable String subId) {
967     AbstractProjectViewPane pane = getProjectViewPaneById(viewId);
968     LOG.assertTrue(pane != null, "Project view pane not found: " + viewId + "; subId:" + subId);
969     if (!viewId.equals(getCurrentViewId())
970         || subId != null && !subId.equals(pane.getSubId()) ||
971         // element not in model anymore
972         ((DefaultComboBoxModel)myCombo.getModel()).getIndexOf(Pair.create(viewId, pane.getSubId())) == -1) {
973       myCombo.setSelectedItem(Pair.create(viewId, subId));
974       viewSelectionChanged();
975     }
976   }
977
978   private final class MyDeletePSIElementProvider implements DeleteProvider {
979     public boolean canDeleteElement(DataContext dataContext) {
980       final PsiElement[] elements = getElementsToDelete();
981       return DeleteHandler.shouldEnableDeleteAction(elements);
982     }
983
984     public void deleteElement(DataContext dataContext) {
985       List<PsiElement> allElements = Arrays.asList(getElementsToDelete());
986       List<PsiElement> validElements = new ArrayList<PsiElement>();
987       for (PsiElement psiElement : allElements) {
988         if (psiElement != null && psiElement.isValid()) validElements.add(psiElement);
989       }
990       final PsiElement[] elements = validElements.toArray(new PsiElement[validElements.size()]);
991
992       LocalHistoryAction a = LocalHistory.getInstance().startAction(IdeBundle.message("progress.deleting"));
993       try {
994         DeleteHandler.deletePsiElement(elements, myProject);
995       }
996       finally {
997         a.finish();
998       }
999     }
1000
1001     private PsiElement[] getElementsToDelete() {
1002       final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
1003       PsiElement[] elements = viewPane.getSelectedPSIElements();
1004       for (int idx = 0; idx < elements.length; idx++) {
1005         final PsiElement element = elements[idx];
1006         if (element instanceof PsiDirectory) {
1007           PsiDirectory directory = (PsiDirectory)element;
1008           final ProjectViewDirectoryHelper directoryHelper = ProjectViewDirectoryHelper.getInstance(myProject);
1009           if (isHideEmptyMiddlePackages(viewPane.getId()) && directory.getChildren().length == 0 && !directoryHelper.skipDirectory(directory)) {
1010             while (true) {
1011               PsiDirectory parent = directory.getParentDirectory();
1012               if (parent == null) break;
1013               if (directoryHelper.skipDirectory(parent) || PsiDirectoryFactory.getInstance(myProject).getQualifiedName(parent, false).length() == 0) break;
1014               PsiElement[] children = parent.getChildren();
1015               if (children.length == 0 || children.length == 1 && children[0] == directory) {
1016                 directory = parent;
1017               }
1018               else {
1019                 break;
1020               }
1021             }
1022             elements[idx] = directory;
1023           }
1024           final VirtualFile virtualFile = directory.getVirtualFile();
1025           final String path = virtualFile.getPath();
1026           if (path.endsWith(JarFileSystem.JAR_SEPARATOR)) { // if is jar-file root
1027             final VirtualFile vFile =
1028               LocalFileSystem.getInstance().findFileByPath(path.substring(0, path.length() - JarFileSystem.JAR_SEPARATOR.length()));
1029             if (vFile != null) {
1030               final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vFile);
1031               if (psiFile != null) {
1032                 elements[idx] = psiFile;
1033               }
1034             }
1035           }
1036         }
1037       }
1038       return elements;
1039     }
1040
1041   }
1042
1043   private final class MyPanel extends JPanel implements DataProvider {
1044     MyPanel() {
1045       super(new BorderLayout());
1046     }
1047
1048     private Object getSelectedNodeElement() {
1049       final AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane();
1050       if (currentProjectViewPane == null) { // can happen if not initialized yet
1051         return null;
1052       }
1053       DefaultMutableTreeNode node = currentProjectViewPane.getSelectedNode();
1054       if (node == null) {
1055         return null;
1056       }
1057       Object userObject = node.getUserObject();
1058       if (userObject instanceof AbstractTreeNode) {
1059         return ((AbstractTreeNode)userObject).getValue();
1060       }
1061       if (!(userObject instanceof NodeDescriptor)) {
1062         return null;
1063       }
1064       return ((NodeDescriptor)userObject).getElement();
1065     }
1066
1067     public Object getData(String dataId) {
1068       final AbstractProjectViewPane currentProjectViewPane = getCurrentProjectViewPane();
1069       if (currentProjectViewPane != null) {
1070         final Object paneSpecificData = currentProjectViewPane.getData(dataId);
1071         if (paneSpecificData != null) return paneSpecificData;
1072       }
1073       
1074       if (LangDataKeys.PSI_ELEMENT.is(dataId)) {
1075         if (currentProjectViewPane == null) return null;
1076         final PsiElement[] elements = currentProjectViewPane.getSelectedPSIElements();
1077         return elements.length == 1 ? elements[0] : null;
1078       }
1079       if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
1080         if (currentProjectViewPane == null) {
1081           return null;
1082         }
1083         PsiElement[] elements = currentProjectViewPane.getSelectedPSIElements();
1084         return elements.length == 0 ? null : elements;
1085       }
1086       if (PlatformDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) {
1087         PsiElement[] psiElements = (PsiElement[])getData(LangDataKeys.PSI_ELEMENT_ARRAY.getName());
1088         if (psiElements == null) return null;
1089         Set<VirtualFile> files = new LinkedHashSet<VirtualFile>();
1090         for (PsiElement element : psiElements) {
1091           if (element instanceof PsiFileSystemItem) {
1092             files.add(((PsiFileSystemItem)element).getVirtualFile());
1093           }
1094         }
1095         return files.size() > 0 ? VfsUtil.toVirtualFileArray(files) : null;
1096       }
1097       if (LangDataKeys.TARGET_PSI_ELEMENT.is(dataId)) {
1098         return null;
1099       }
1100       if (PlatformDataKeys.CUT_PROVIDER.is(dataId)) {
1101         return myCopyPasteDelegator.getCutProvider();
1102       }
1103       if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) {
1104         return myCopyPasteDelegator.getCopyProvider();
1105       }
1106       if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) {
1107         return myCopyPasteDelegator.getPasteProvider();
1108       }
1109       if (LangDataKeys.IDE_VIEW.is(dataId)) {
1110         return myIdeView;
1111       }
1112       if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) {
1113         final Module[] modules = getSelectedModules();
1114         if (modules != null) {
1115           return myDeleteModuleProvider;
1116         }
1117         final LibraryOrderEntry orderEntry = getSelectedLibrary();
1118         if (orderEntry != null) {
1119           return new DeleteProvider() {
1120             public void deleteElement(DataContext dataContext) {
1121               detachLibrary(orderEntry, myProject);
1122             }
1123
1124             public boolean canDeleteElement(DataContext dataContext) {
1125               return true;
1126             }
1127           };
1128         }
1129         return myDeletePSIElementProvider;
1130       }
1131       if (PlatformDataKeys.HELP_ID.is(dataId)) {
1132         return HelpID.PROJECT_VIEWS;
1133       }
1134       if (ProjectViewImpl.DATA_KEY.is(dataId)) {
1135         return ProjectViewImpl.this;
1136       }
1137       if (PlatformDataKeys.PROJECT_CONTEXT.is(dataId)) {
1138         Object selected = getSelectedNodeElement();
1139         return selected instanceof Project ? selected : null;
1140       }
1141       if (LangDataKeys.MODULE_CONTEXT.is(dataId)) {
1142         Object selected = getSelectedNodeElement();
1143         if (selected instanceof Module) {
1144           return !((Module)selected).isDisposed() ? selected : null;
1145         }
1146         else if (selected instanceof PsiDirectory) {
1147           return moduleByContentRoot(((PsiDirectory)selected).getVirtualFile());
1148         }
1149         else if (selected instanceof VirtualFile) {
1150           return moduleByContentRoot((VirtualFile)selected);
1151         }
1152         else {
1153           return null;
1154         }
1155       }
1156
1157       if (LangDataKeys.MODULE_CONTEXT_ARRAY.is(dataId)) {
1158         return getSelectedModules();
1159       }
1160       if (ModuleGroup.ARRAY_DATA_KEY.is(dataId)) {
1161         final List<ModuleGroup> selectedElements = getSelectedElements(ModuleGroup.class);
1162         return selectedElements.isEmpty() ? null : selectedElements.toArray(new ModuleGroup[selectedElements.size()]);
1163       }
1164       if (LibraryGroupElement.ARRAY_DATA_KEY.is(dataId)) {
1165         final List<LibraryGroupElement> selectedElements = getSelectedElements(LibraryGroupElement.class);
1166         return selectedElements.isEmpty() ? null : selectedElements.toArray(new LibraryGroupElement[selectedElements.size()]);
1167       }
1168       if (NamedLibraryElement.ARRAY_DATA_KEY.is(dataId)) {
1169         final List<NamedLibraryElement> selectedElements = getSelectedElements(NamedLibraryElement.class);
1170         return selectedElements.isEmpty() ? null : selectedElements.toArray(new NamedLibraryElement[selectedElements.size()]);
1171       }
1172
1173       if (QuickActionProvider.KEY.is(dataId)) {
1174         return ProjectViewImpl.this;
1175       }
1176
1177       return null;
1178     }
1179
1180     private LibraryOrderEntry getSelectedLibrary() {
1181       final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
1182       DefaultMutableTreeNode node = viewPane != null ? viewPane.getSelectedNode() : null;
1183       if (node == null) return null;
1184       DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
1185       if (parent == null) return null;
1186       Object userObject = parent.getUserObject();
1187       if (userObject instanceof LibraryGroupNode) {
1188         userObject = node.getUserObject();
1189         if (userObject instanceof NamedLibraryElementNode) {
1190           NamedLibraryElement element = ((NamedLibraryElementNode)userObject).getValue();
1191           OrderEntry orderEntry = element.getOrderEntry();
1192           return orderEntry instanceof LibraryOrderEntry ? (LibraryOrderEntry)orderEntry : null;
1193         }
1194         PsiDirectory directory = ((PsiDirectoryNode)userObject).getValue();
1195         VirtualFile virtualFile = directory.getVirtualFile();
1196         Module module = (Module)((AbstractTreeNode)((DefaultMutableTreeNode)parent.getParent()).getUserObject()).getValue();
1197
1198         if (module == null) return null;
1199         ModuleFileIndex index = ModuleRootManager.getInstance(module).getFileIndex();
1200         OrderEntry entry = index.getOrderEntryForFile(virtualFile);
1201         if (entry instanceof LibraryOrderEntry) {
1202           return (LibraryOrderEntry)entry;
1203         }
1204       }
1205
1206       return null;
1207     }
1208
1209     private void detachLibrary(final LibraryOrderEntry orderEntry, final Project project) {
1210       final Module module = orderEntry.getOwnerModule();
1211       String message = IdeBundle.message("detach.library.from.module", orderEntry.getPresentableName(), module.getName());
1212       String title = IdeBundle.message("detach.library");
1213       int ret = Messages.showOkCancelDialog(project, message, title, Messages.getQuestionIcon());
1214       if (ret != 0) return;
1215       CommandProcessor.getInstance().executeCommand(module.getProject(), new Runnable() {
1216         public void run() {
1217           final Runnable action = new Runnable() {
1218             public void run() {
1219               ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
1220               OrderEntry[] orderEntries = rootManager.getOrderEntries();
1221               ModifiableRootModel model = rootManager.getModifiableModel();
1222               OrderEntry[] modifiableEntries = model.getOrderEntries();
1223               for (int i = 0; i < orderEntries.length; i++) {
1224                 OrderEntry entry = orderEntries[i];
1225                 if (entry instanceof LibraryOrderEntry && ((LibraryOrderEntry)entry).getLibrary() == orderEntry.getLibrary()) {
1226                   model.removeOrderEntry(modifiableEntries[i]);
1227                 }
1228               }
1229               model.commit();
1230             }
1231           };
1232           ApplicationManager.getApplication().runWriteAction(action);
1233         }
1234       }, title, null);
1235     }
1236
1237     @Nullable
1238     private Module[] getSelectedModules() {
1239       final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
1240       if (viewPane == null) return null;
1241       final Object[] elements = viewPane.getSelectedElements();
1242       ArrayList<Module> result = new ArrayList<Module>();
1243       for (Object element : elements) {
1244         if (element instanceof Module) {
1245           final Module module = (Module)element;
1246           if (!module.isDisposed()) {
1247             result.add(module);
1248           }
1249         }
1250         else if (element instanceof ModuleGroup) {
1251           Collection<Module> modules = ((ModuleGroup)element).modulesInGroup(myProject, true);
1252           result.addAll(modules);
1253         }
1254         else if (element instanceof PsiDirectory) {
1255           Module module = moduleByContentRoot(((PsiDirectory)element).getVirtualFile());
1256           if (module != null) result.add(module);
1257         }
1258         else if (element instanceof VirtualFile) {
1259           Module module = moduleByContentRoot((VirtualFile)element);
1260           if (module != null) result.add(module);
1261         }
1262       }
1263
1264       if (result.isEmpty()) {
1265         return null;
1266       }
1267       else {
1268         return result.toArray(new Module[result.size()]);
1269       }
1270     }
1271   }
1272
1273   private Module moduleByContentRoot(VirtualFile file) {
1274     if (ProjectRootsUtil.isModuleContentRoot(file, myProject)) {
1275       Module module = ProjectRootManager.getInstance(myProject).getFileIndex().getModuleForFile(file);
1276       if (module != null && !module.isDisposed()) {
1277         return module;
1278       }
1279     }
1280
1281     return null;
1282   }
1283
1284   private <T> List<T> getSelectedElements(Class<T> klass) {
1285     ArrayList<T> result = new ArrayList<T>();
1286     final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
1287     if (viewPane == null) return result;
1288     final Object[] elements = viewPane.getSelectedElements();
1289     for (Object element : elements) {
1290       //element still valid
1291       if (element != null && klass.isAssignableFrom(element.getClass())) {
1292         result.add((T)element);
1293       }
1294     }
1295     return result;
1296   }
1297
1298   private final class MyIdeView implements IdeView {
1299     public void selectElement(PsiElement element) {
1300       selectPsiElement(element, false);
1301       boolean requestFocus = true;
1302       if (element != null) {
1303         final boolean isDirectory = element instanceof PsiDirectory;
1304         if (!isDirectory) {
1305           Editor editor = EditorHelper.openInEditor(element);
1306           if (editor != null) {
1307             ToolWindowManager.getInstance(myProject).activateEditorComponent();
1308             requestFocus = false;
1309           }
1310         }
1311       }
1312
1313       if (requestFocus) {
1314         selectPsiElement(element, true);
1315       }
1316     }
1317
1318     public PsiDirectory[] getDirectories() {
1319       final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
1320       if (viewPane != null) {
1321         return viewPane.getSelectedDirectories();
1322       }
1323
1324       return PsiDirectory.EMPTY_ARRAY;
1325     }
1326
1327     public PsiDirectory getOrChooseDirectory() {
1328       return DirectoryChooserUtil.getOrChooseDirectory(this);
1329     }
1330   }
1331
1332   public void selectPsiElement(PsiElement element, boolean requestFocus) {
1333     if (element == null) return;
1334     VirtualFile virtualFile = PsiUtilBase.getVirtualFile(element);
1335     select(element, virtualFile, requestFocus);
1336   }
1337
1338
1339   private static void readOption(Element node, Map<String, Boolean> options) {
1340     if (node == null) return;
1341     List attributes = node.getAttributes();
1342     for (final Object attribute1 : attributes) {
1343       Attribute attribute = (Attribute)attribute1;
1344       options.put(attribute.getName(), Boolean.TRUE.toString().equals(attribute.getValue()) ? Boolean.TRUE : Boolean.FALSE);
1345     }
1346   }
1347
1348   private static void writeOption(Element parentNode, Map<String, Boolean> optionsForPanes, String optionName) {
1349     Element e = new Element(optionName);
1350     for (Map.Entry<String, Boolean> entry : optionsForPanes.entrySet()) {
1351       final String key = entry.getKey();
1352       if (key != null) { //SCR48267
1353         e.setAttribute(key, Boolean.toString(entry.getValue().booleanValue()));
1354       }
1355     }
1356
1357     parentNode.addContent(e);
1358   }
1359
1360   public void loadState(Element parentNode) {
1361     Element navigatorElement = parentNode.getChild(ELEMENT_NAVIGATOR);
1362     if (navigatorElement != null) {
1363       mySavedPaneId = navigatorElement.getAttributeValue(ATTRIBUTE_CURRENT_VIEW);
1364       mySavedPaneSubId = navigatorElement.getAttributeValue(ATTRIBUTE_CURRENT_SUBVIEW);
1365       if (mySavedPaneId == null) {
1366         mySavedPaneId = ProjectViewPane.ID;
1367         mySavedPaneSubId = null;
1368       }
1369       readOption(navigatorElement.getChild(ELEMENT_FLATTEN_PACKAGES), myFlattenPackages);
1370       readOption(navigatorElement.getChild(ELEMENT_SHOW_MEMBERS), myShowMembers);
1371       readOption(navigatorElement.getChild(ELEMENT_SHOW_MODULES), myShowModules);
1372       readOption(navigatorElement.getChild(ELEMENT_SHOW_LIBRARY_CONTENTS), myShowLibraryContents);
1373       readOption(navigatorElement.getChild(ELEMENT_HIDE_EMPTY_PACKAGES), myHideEmptyPackages);
1374       readOption(navigatorElement.getChild(ELEMENT_ABBREVIATE_PACKAGE_NAMES), myAbbreviatePackageNames);
1375       readOption(navigatorElement.getChild(ELEMENT_AUTOSCROLL_TO_SOURCE), myAutoscrollToSource);
1376       readOption(navigatorElement.getChild(ELEMENT_AUTOSCROLL_FROM_SOURCE), myAutoscrollFromSource);
1377       readOption(navigatorElement.getChild(ELEMENT_SORT_BY_TYPE), mySortByType);
1378
1379       try {
1380         splitterProportions.readExternal(navigatorElement);
1381       }
1382       catch (InvalidDataException e) {
1383         // ignore
1384       }
1385     }
1386     Element panesElement = parentNode.getChild(ELEMENT_PANES);
1387     if (panesElement != null) {
1388       readPaneState(panesElement);
1389     }
1390   }
1391
1392   private void readPaneState(Element panesElement) {
1393     @SuppressWarnings({"unchecked"})
1394     final List<Element> paneElements = panesElement.getChildren(ELEMENT_PANE);
1395
1396     for (Element paneElement : paneElements) {
1397       String paneId = paneElement.getAttributeValue(ATTRIBUTE_ID);
1398       final AbstractProjectViewPane pane = myId2Pane.get(paneId);
1399       if (pane != null) {
1400         try {
1401           pane.readExternal(paneElement);
1402         }
1403         catch (InvalidDataException e) {
1404           // ignore
1405         }
1406       }
1407       else {
1408         myUninitializedPaneState.put(paneId, paneElement);
1409       }
1410     }
1411   }
1412
1413   public Element getState() {
1414     Element parentNode = new Element("projectView");
1415     Element navigatorElement = new Element(ELEMENT_NAVIGATOR);
1416     AbstractProjectViewPane currentPane = getCurrentProjectViewPane();
1417     if (currentPane != null) {
1418       navigatorElement.setAttribute(ATTRIBUTE_CURRENT_VIEW, currentPane.getId());
1419       String subId = currentPane.getSubId();
1420       if (subId != null) {
1421         navigatorElement.setAttribute(ATTRIBUTE_CURRENT_SUBVIEW, subId);
1422       }
1423     }
1424     writeOption(navigatorElement, myFlattenPackages, ELEMENT_FLATTEN_PACKAGES);
1425     writeOption(navigatorElement, myShowMembers, ELEMENT_SHOW_MEMBERS);
1426     writeOption(navigatorElement, myShowModules, ELEMENT_SHOW_MODULES);
1427     writeOption(navigatorElement, myShowLibraryContents, ELEMENT_SHOW_LIBRARY_CONTENTS);
1428     writeOption(navigatorElement, myHideEmptyPackages, ELEMENT_HIDE_EMPTY_PACKAGES);
1429     writeOption(navigatorElement, myAbbreviatePackageNames, ELEMENT_ABBREVIATE_PACKAGE_NAMES);
1430     writeOption(navigatorElement, myAutoscrollToSource, ELEMENT_AUTOSCROLL_TO_SOURCE);
1431     writeOption(navigatorElement, myAutoscrollFromSource, ELEMENT_AUTOSCROLL_FROM_SOURCE);
1432     writeOption(navigatorElement, mySortByType, ELEMENT_SORT_BY_TYPE);
1433
1434     splitterProportions.saveSplitterProportions(myPanel);
1435     try {
1436       splitterProportions.writeExternal(navigatorElement);
1437     }
1438     catch (WriteExternalException e) {
1439       // ignore
1440     }
1441     parentNode.addContent(navigatorElement);
1442
1443     // for compatibility with idea 5.1
1444     @Deprecated @NonNls final String ATTRIBUTE_SPLITTER_PROPORTION = "splitterProportion";
1445     navigatorElement.setAttribute(ATTRIBUTE_SPLITTER_PROPORTION, "0.5");
1446
1447     Element panesElement = new Element(ELEMENT_PANES);
1448     writePaneState(panesElement);
1449     parentNode.addContent(panesElement);
1450     return parentNode;
1451   }
1452
1453   private void writePaneState(Element panesElement) {
1454     for (AbstractProjectViewPane pane : myId2Pane.values()) {
1455       Element paneElement = new Element(ELEMENT_PANE);
1456       paneElement.setAttribute(ATTRIBUTE_ID, pane.getId());
1457       try {
1458         pane.writeExternal(paneElement);
1459       }
1460       catch (WriteExternalException e) {
1461         continue;
1462       }
1463       panesElement.addContent(paneElement);
1464     }
1465     for (Element element : myUninitializedPaneState.values()) {
1466       panesElement.addContent((Element) element.clone());
1467     }
1468   }
1469
1470   public boolean isAutoscrollToSource(String paneId) {
1471     return getPaneOptionValue(myAutoscrollToSource, paneId, ourAutoscrollToSourceDefaults);
1472   }
1473
1474   private void setAutoscrollToSource(boolean autoscrollMode, String paneId) {
1475     myAutoscrollToSource.put(paneId, autoscrollMode ? Boolean.TRUE : Boolean.FALSE);
1476   }
1477
1478   public boolean isAutoscrollFromSource(String paneId) {
1479     return getPaneOptionValue(myAutoscrollFromSource, paneId, ourAutoscrollFromSourceDefaults);
1480   }
1481
1482   private void setAutoscrollFromSource(boolean autoscrollMode, String paneId) {
1483     setPaneOption(myAutoscrollFromSource, autoscrollMode, paneId, false);
1484   }
1485
1486   public boolean isFlattenPackages(String paneId) {
1487     return getPaneOptionValue(myFlattenPackages, paneId, ourFlattenPackagesDefaults);
1488   }
1489
1490   public void setFlattenPackages(boolean flattenPackages, String paneId) {
1491     setPaneOption(myFlattenPackages, flattenPackages, paneId, true);
1492   }
1493
1494   public boolean isShowMembers(String paneId) {
1495     return getPaneOptionValue(myShowMembers, paneId, ourShowMembersDefaults);
1496   }
1497
1498   public boolean isHideEmptyMiddlePackages(String paneId) {
1499     return getPaneOptionValue(myHideEmptyPackages, paneId, ourHideEmptyPackagesDefaults);
1500   }
1501
1502   public boolean isAbbreviatePackageNames(String paneId) {
1503     return getPaneOptionValue(myAbbreviatePackageNames, paneId, ourAbbreviatePackagesDefaults);
1504   }
1505
1506   public boolean isShowLibraryContents(String paneId) {
1507     return getPaneOptionValue(myShowLibraryContents, paneId, ourShowLibraryContentsDefaults);
1508   }
1509
1510   public void setShowLibraryContents(boolean showLibraryContents, String paneId) {
1511     setPaneOption(myShowLibraryContents, showLibraryContents, paneId, true);
1512   }
1513
1514   public ActionCallback setShowLibraryContentsCB(boolean showLibraryContents, String paneId) {
1515     return setPaneOption(myShowLibraryContents, showLibraryContents, paneId, true);
1516   }
1517
1518   public boolean isShowModules(String paneId) {
1519     return getPaneOptionValue(myShowModules, paneId, ourShowModulesDefaults);
1520   }
1521
1522   public void setShowModules(boolean showModules, String paneId) {
1523     setPaneOption(myShowModules, showModules, paneId, true);
1524   }
1525
1526   public void setHideEmptyPackages(boolean hideEmptyPackages, String paneId) {
1527     setPaneOption(myHideEmptyPackages, hideEmptyPackages, paneId, true);
1528   }
1529
1530   public void setAbbreviatePackageNames(boolean abbreviatePackageNames, String paneId) {
1531     setPaneOption(myAbbreviatePackageNames, abbreviatePackageNames, paneId, true);
1532   }
1533
1534   private ActionCallback setPaneOption(Map<String, Boolean> optionsMap, boolean value, String paneId, final boolean updatePane) {
1535     optionsMap.put(paneId, value ? Boolean.TRUE : Boolean.FALSE);
1536     if (updatePane) {
1537       final AbstractProjectViewPane pane = getProjectViewPaneById(paneId);
1538       if (pane != null) {
1539         return pane.updateFromRoot(false);
1540       }
1541     }
1542     return new ActionCallback.Done();
1543   }
1544
1545   private static boolean getPaneOptionValue(Map<String, Boolean> optionsMap, String paneId, boolean defaultValue) {
1546     final Boolean value = optionsMap.get(paneId);
1547     return value == null ? defaultValue : value.booleanValue();
1548   }
1549
1550   private class HideEmptyMiddlePackagesAction extends PaneOptionAction {
1551     private HideEmptyMiddlePackagesAction() {
1552       super(myHideEmptyPackages, "", "", null, ourHideEmptyPackagesDefaults);
1553     }
1554
1555     public void setSelected(AnActionEvent event, boolean flag) {
1556       final AbstractProjectViewPane viewPane = getCurrentProjectViewPane();
1557       final SelectionInfo selectionInfo = SelectionInfo.create(viewPane);
1558
1559       super.setSelected(event, flag);
1560
1561       selectionInfo.apply(viewPane);
1562     }
1563
1564     public void update(AnActionEvent e) {
1565       super.update(e);
1566       final Presentation presentation = e.getPresentation();
1567       if (isFlattenPackages(myCurrentViewId)) {
1568         presentation.setText(IdeBundle.message("action.hide.empty.middle.packages"));
1569         presentation.setDescription(IdeBundle.message("action.show.hide.empty.middle.packages"));
1570       }
1571       else {
1572         presentation.setText(IdeBundle.message("action.compact.empty.middle.packages"));
1573         presentation.setDescription(IdeBundle.message("action.show.compact.empty.middle.packages"));
1574       }
1575     }
1576   }
1577
1578   private static class SelectionInfo {
1579     private final Object[] myElements;
1580
1581     private SelectionInfo(Object[] elements) {
1582       myElements = elements;
1583     }
1584
1585     public void apply(final AbstractProjectViewPane viewPane) {
1586       if (viewPane == null) {
1587         return;
1588       }
1589       AbstractTreeBuilder treeBuilder = viewPane.getTreeBuilder();
1590       JTree tree = viewPane.myTree;
1591       DefaultTreeModel treeModel = (DefaultTreeModel)tree.getModel();
1592       List<TreePath> paths = new ArrayList<TreePath>(myElements.length);
1593       for (final Object element : myElements) {
1594         DefaultMutableTreeNode node = treeBuilder.getNodeForElement(element);
1595         if (node == null) {
1596           treeBuilder.buildNodeForElement(element);
1597           node = treeBuilder.getNodeForElement(element);
1598         }
1599         if (node != null) {
1600           paths.add(new TreePath(treeModel.getPathToRoot(node)));
1601         }
1602       }
1603       if (!paths.isEmpty()) {
1604         tree.setSelectionPaths(paths.toArray(new TreePath[paths.size()]));
1605       }
1606     }
1607
1608     public static SelectionInfo create(final AbstractProjectViewPane viewPane) {
1609       List<Object> selectedElements = Collections.emptyList();
1610       if (viewPane != null) {
1611         final TreePath[] selectionPaths = viewPane.getSelectionPaths();
1612         if (selectionPaths != null) {
1613           selectedElements = new ArrayList<Object>();
1614           for (TreePath path : selectionPaths) {
1615             final DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
1616             final Object userObject = node.getUserObject();
1617             if (userObject instanceof NodeDescriptor) {
1618               selectedElements.add(((NodeDescriptor)userObject).getElement());
1619             }
1620           }
1621         }
1622       }
1623       return new SelectionInfo(selectedElements.toArray());
1624     }
1625   }
1626
1627   private class MyAutoScrollFromSourceHandler extends AutoScrollFromSourceHandler {
1628     private final Alarm myAlarm = new Alarm(myProject);
1629
1630     private MyAutoScrollFromSourceHandler() {
1631       super(ProjectViewImpl.this.myProject, ProjectViewImpl.this);
1632     }
1633
1634     public void install() {
1635       FileEditorManagerAdapter myEditorManagerListener = new FileEditorManagerAdapter() {
1636         public void selectionChanged(final FileEditorManagerEvent event) {
1637           final FileEditor newEditor = event.getNewEditor();
1638           myAlarm.cancelAllRequests();
1639           myAlarm.addRequest(new Runnable() {
1640             public void run() {
1641               if (myProject.isDisposed() || !myViewContentPanel.isShowing()) return;
1642               if (isAutoscrollFromSource(getCurrentViewId())) {
1643                 if (newEditor instanceof TextEditor) {
1644                   Editor editor = ((TextEditor)newEditor).getEditor();
1645                   selectElementAtCaretNotLosingFocus(editor);
1646                 } else if (newEditor != null) {
1647                   final VirtualFile file = FileEditorManagerEx.getInstanceEx(myProject).getFile(newEditor);
1648                   if (file != null) {
1649                     final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
1650                     if (psiFile != null) {
1651                       final SelectInTarget target = mySelectInTargets.get(getCurrentViewId());
1652                       if (target != null) {
1653                         final MySelectInContext selectInContext = new MySelectInContext(psiFile, null) {
1654                           @Override
1655                           public Object getSelectorInFile() {
1656                             return psiFile;
1657                           }
1658                         };
1659
1660                         if (target.canSelect(selectInContext)) {
1661                           target.selectIn(selectInContext, false);
1662                         }
1663                       }
1664                     }
1665                   }
1666                 }
1667               }
1668             }
1669           }, 300, ModalityState.NON_MODAL);
1670         }
1671       };
1672       myFileEditorManager.addFileEditorManagerListener(myEditorManagerListener, this);
1673     }
1674
1675     public void scrollFromSource() {
1676       final FileEditorManager fileEditorManager = FileEditorManager.getInstance(myProject);
1677       final FileEditor[] editors = fileEditorManager.getSelectedEditors();
1678       for (FileEditor fileEditor : editors) {
1679         if (fileEditor instanceof TextEditor) {
1680           Editor editor = ((TextEditor)fileEditor).getEditor();
1681           selectElementAtCaret(editor);
1682           return;
1683         }
1684       }
1685       final VirtualFile[] selectedFiles = fileEditorManager.getSelectedFiles();
1686       if (selectedFiles.length > 0) {
1687         final PsiFile file = PsiManager.getInstance(myProject).findFile(selectedFiles[0]);
1688         if (file != null) {
1689           scrollFromFile(file, null);
1690         }
1691       }
1692     }
1693
1694     private void selectElementAtCaretNotLosingFocus(final Editor editor) {
1695       if (IJSwingUtilities.hasFocus(getCurrentProjectViewPane().getComponentToFocus())) return;
1696       selectElementAtCaret(editor);
1697     }
1698
1699     private void selectElementAtCaret(Editor editor) {
1700       final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
1701       if (file == null) return;
1702
1703       scrollFromFile(file, editor);
1704     }
1705
1706     private void scrollFromFile(PsiFile file, @Nullable Editor editor) {
1707       final MySelectInContext selectInContext = new MySelectInContext(file, editor);
1708
1709       final SelectInTarget target = mySelectInTargets.get(getCurrentViewId());
1710       if (target != null && target.canSelect(selectInContext)) {
1711         target.selectIn(selectInContext, false);
1712       }
1713     }
1714
1715     public void dispose() {
1716     }
1717
1718     protected boolean isAutoScrollMode() {
1719       return isAutoscrollFromSource(myCurrentViewId);
1720     }
1721
1722     protected void setAutoScrollMode(boolean state) {
1723       setAutoscrollFromSource(state, myCurrentViewId);
1724       if (state) {
1725         final Editor editor = myFileEditorManager.getSelectedTextEditor();
1726         if (editor != null) {
1727           selectElementAtCaretNotLosingFocus(editor);
1728         }
1729       }
1730     }
1731
1732     private class MySelectInContext implements SelectInContext {
1733       private final PsiFile myPsiFile;
1734       @Nullable private final Editor myEditor;
1735
1736       private MySelectInContext(final PsiFile psiFile, @Nullable Editor editor) {
1737         myPsiFile = psiFile;
1738         myEditor = editor;
1739       }
1740
1741       @NotNull
1742       public Project getProject() {
1743         return myProject;
1744       }
1745
1746       private PsiFile getPsiFile() {
1747         return myPsiFile;
1748       }
1749
1750       public FileEditorProvider getFileEditorProvider() {
1751         if (myPsiFile == null) return null;
1752         return new FileEditorProvider() {
1753           public FileEditor openFileEditor() {
1754             return myFileEditorManager.openFile(myPsiFile.getContainingFile().getVirtualFile(), false)[0];
1755           }
1756         };
1757       }
1758
1759       private PsiElement getPsiElement() {
1760         PsiElement e = null;
1761         if (myEditor != null) {
1762           final int offset = myEditor.getCaretModel().getOffset();
1763           PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1764           e = getPsiFile().findElementAt(offset);
1765         }
1766         if (e == null) {
1767           e = getPsiFile();
1768         }
1769         return e;
1770       }
1771
1772       @NotNull
1773       public VirtualFile getVirtualFile() {
1774         return getPsiFile().getVirtualFile();
1775       }
1776
1777       public Object getSelectorInFile() {
1778         return getPsiElement();
1779       }
1780     }
1781   }
1782
1783   public boolean isSortByType(String paneId) {
1784     return getPaneOptionValue(mySortByType, paneId, ourSortByTypeDefaults);
1785   }
1786
1787   public void setSortByType(String paneId, final boolean sortByType) {
1788     setPaneOption(mySortByType, sortByType, paneId, false);
1789     final AbstractProjectViewPane pane = getProjectViewPaneById(paneId);
1790     pane.installComparator();
1791   }
1792
1793   private class SortByTypeAction extends ToggleAction {
1794     private SortByTypeAction() {
1795       super(IdeBundle.message("action.sort.by.type"), IdeBundle.message("action.sort.by.type"),
1796             IconLoader.getIcon("/objectBrowser/sortByType.png"));
1797     }
1798
1799     public boolean isSelected(AnActionEvent event) {
1800       return isSortByType(getCurrentViewId());
1801     }
1802
1803     public void setSelected(AnActionEvent event, boolean flag) {
1804       setSortByType(getCurrentViewId(), flag);
1805     }
1806
1807     public void update(final AnActionEvent e) {
1808       super.update(e);
1809       final Presentation presentation = e.getPresentation();
1810       presentation.setVisible(getCurrentProjectViewPane() != null);
1811     }
1812   }
1813
1814   private class ScrollFromSourceAction extends AnAction {
1815     private ScrollFromSourceAction() {
1816       super("Scroll from Source", "Select the file open in the active editor", IconLoader.getIcon("/general/autoscrollFromSource.png"));
1817     }
1818
1819     @Override
1820     public void actionPerformed(AnActionEvent e) {
1821       myAutoScrollFromSourceHandler.scrollFromSource();
1822     }
1823   }
1824
1825   public Collection<String> getPaneIds() {
1826     return myId2Pane.keySet();
1827   }
1828
1829   @Override
1830   public Collection<SelectInTarget> getSelectInTargets() {
1831     ensurePanesLoaded();
1832     return mySelectInTargets.values();
1833   }
1834 }