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