classpath editor: simplified enabling/disabling for edit/delete buttons
[idea/community.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / classpath / ClasspathPanelImpl.java
1 /*
2  * Copyright 2000-2014 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 package com.intellij.openapi.roots.ui.configuration.classpath;
17
18 import com.intellij.CommonBundle;
19 import com.intellij.analysis.AnalysisScope;
20 import com.intellij.find.FindBundle;
21 import com.intellij.openapi.actionSystem.*;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.module.Module;
24 import com.intellij.openapi.module.impl.scopes.LibraryScope;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.project.ProjectBundle;
27 import com.intellij.openapi.projectRoots.Sdk;
28 import com.intellij.openapi.roots.*;
29 import com.intellij.openapi.roots.impl.libraries.LibraryTableImplUtil;
30 import com.intellij.openapi.roots.libraries.Library;
31 import com.intellij.openapi.roots.libraries.LibraryTable;
32 import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
33 import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
34 import com.intellij.openapi.roots.ui.CellAppearanceEx;
35 import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
36 import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
37 import com.intellij.openapi.roots.ui.configuration.ModuleConfigurationState;
38 import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
39 import com.intellij.openapi.roots.ui.configuration.dependencyAnalysis.AnalyzeDependenciesDialog;
40 import com.intellij.openapi.roots.ui.configuration.libraries.LibraryEditingUtil;
41 import com.intellij.openapi.roots.ui.configuration.libraryEditor.EditExistingLibraryDialog;
42 import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
43 import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
44 import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
45 import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
46 import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
47 import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
48 import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
49 import com.intellij.openapi.ui.ComboBox;
50 import com.intellij.openapi.ui.ComboBoxTableRenderer;
51 import com.intellij.openapi.ui.Messages;
52 import com.intellij.openapi.ui.popup.JBPopup;
53 import com.intellij.openapi.ui.popup.JBPopupFactory;
54 import com.intellij.openapi.ui.popup.PopupStep;
55 import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
56 import com.intellij.openapi.wm.IdeFocusManager;
57 import com.intellij.openapi.wm.ToolWindowId;
58 import com.intellij.packageDependencies.DependenciesBuilder;
59 import com.intellij.packageDependencies.actions.AnalyzeDependenciesOnSpecifiedTargetHandler;
60 import com.intellij.psi.PsiFile;
61 import com.intellij.psi.search.GlobalSearchScope;
62 import com.intellij.ui.*;
63 import com.intellij.ui.awt.RelativePoint;
64 import com.intellij.ui.table.JBTable;
65 import com.intellij.util.EventDispatcher;
66 import com.intellij.util.IconUtil;
67 import com.intellij.util.containers.ContainerUtil;
68 import com.intellij.util.ui.TextTransferable;
69 import gnu.trove.TIntArrayList;
70 import org.jetbrains.annotations.NotNull;
71 import org.jetbrains.annotations.Nullable;
72
73 import javax.swing.*;
74 import javax.swing.border.Border;
75 import javax.swing.table.TableCellRenderer;
76 import javax.swing.table.TableColumn;
77 import javax.swing.table.TableModel;
78 import javax.swing.table.TableRowSorter;
79 import java.awt.*;
80 import java.awt.datatransfer.Transferable;
81 import java.awt.event.ActionEvent;
82 import java.awt.event.ActionListener;
83 import java.awt.event.KeyEvent;
84 import java.awt.event.MouseEvent;
85 import java.util.*;
86 import java.util.List;
87
88 public class ClasspathPanelImpl extends JPanel implements ClasspathPanel {
89   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.classpath.ClasspathPanelImpl");
90   private final JBTable myEntryTable;
91   private final ClasspathTableModel myModel;
92   private final EventDispatcher<OrderPanelListener> myListeners = EventDispatcher.create(OrderPanelListener.class);
93   private List<AddItemPopupAction<?>> myPopupActions = null;
94   private AnActionButton myEditButton;
95   private final ModuleConfigurationState myState;
96   private AnActionButton myRemoveButton;
97
98   public ClasspathPanelImpl(ModuleConfigurationState state) {
99     super(new BorderLayout());
100
101     myState = state;
102     myModel = new ClasspathTableModel(state, getStructureConfigurableContext());
103     myEntryTable = new JBTable(myModel) {
104       @Override
105       protected TableRowSorter<TableModel> createRowSorter(TableModel model) {
106         return new DefaultColumnInfoBasedRowSorter(model) {
107           @Override
108           public void toggleSortOrder(int column) {
109             if (isSortable(column)) {
110               SortKey oldKey = ContainerUtil.getFirstItem(getSortKeys());
111               SortOrder oldOrder;
112               if (oldKey == null || oldKey.getColumn() != column) {
113                 oldOrder = SortOrder.UNSORTED;
114               }
115               else {
116                 oldOrder = oldKey.getSortOrder();
117               }
118               setSortKeys(Collections.singletonList(new SortKey(column, getNextSortOrder(oldOrder))));
119             }
120           }
121         };
122       }
123     };
124     myEntryTable.setShowGrid(false);
125     myEntryTable.setDragEnabled(false);
126     myEntryTable.setIntercellSpacing(new Dimension(0, 0));
127
128     myEntryTable.setDefaultRenderer(ClasspathTableItem.class, new TableItemRenderer(getStructureConfigurableContext()));
129     myEntryTable.setDefaultRenderer(Boolean.class, new ExportFlagRenderer(myEntryTable.getDefaultRenderer(Boolean.class)));
130
131     JComboBox scopeEditor = new ComboBox(new EnumComboBoxModel<DependencyScope>(DependencyScope.class));
132     myEntryTable.setDefaultEditor(DependencyScope.class, new DefaultCellEditor(scopeEditor));
133     myEntryTable.setDefaultRenderer(DependencyScope.class, new ComboBoxTableRenderer<DependencyScope>(DependencyScope.values()) {
134         @Override
135         protected String getTextFor(@NotNull final DependencyScope value) {
136           return value.getDisplayName();
137         }
138       });
139
140     myEntryTable.setTransferHandler(new TransferHandler() {
141       @Nullable
142       @Override
143       protected Transferable createTransferable(JComponent c) {
144         OrderEntry entry = getSelectedEntry();
145         if (entry == null) return null;
146         String text = entry.getPresentableName();
147         return new TextTransferable(text);
148       }
149
150       @Override
151       public int getSourceActions(JComponent c) {
152         return COPY;
153       }
154     });
155
156     myEntryTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
157
158     new SpeedSearchBase<JBTable>(myEntryTable) {
159       @Override
160       public int getSelectedIndex() {
161         return myEntryTable.getSelectedRow();
162       }
163
164       @Override
165       protected int convertIndexToModel(int viewIndex) {
166         return myEntryTable.convertRowIndexToModel(viewIndex);
167       }
168
169       @Override
170       public Object[] getAllElements() {
171         final int count = myModel.getRowCount();
172         Object[] elements = new Object[count];
173         for (int idx = 0; idx < count; idx++) {
174           elements[idx] = myModel.getItem(idx);
175         }
176         return elements;
177       }
178
179       @Override
180       public String getElementText(Object element) {
181         return getCellAppearance((ClasspathTableItem<?>)element, getStructureConfigurableContext(), false).getText();
182       }
183
184       @Override
185       public void selectElement(Object element, String selectedText) {
186         final int count = myModel.getRowCount();
187         for (int row = 0; row < count; row++) {
188           if (element.equals(myModel.getItem(row))) {
189             final int viewRow = myEntryTable.convertRowIndexToView(row);
190             myEntryTable.getSelectionModel().setSelectionInterval(viewRow, viewRow);
191             TableUtil.scrollSelectionToVisible(myEntryTable);
192             break;
193           }
194         }
195       }
196     };
197     setFixedColumnWidth(ClasspathTableModel.EXPORT_COLUMN);
198     setFixedColumnWidth(ClasspathTableModel.SCOPE_COLUMN);  // leave space for combobox border
199
200     myEntryTable.registerKeyboardAction(
201       new ActionListener() {
202         @Override
203         public void actionPerformed(ActionEvent e) {
204           final int[] selectedRows = myEntryTable.getSelectedRows();
205           boolean currentlyMarked = true;
206           for (final int selectedRow : selectedRows) {
207             final ClasspathTableItem<?> item = getItemAt(selectedRow);
208             if (selectedRow < 0 || !item.isExportable()) {
209               return;
210             }
211             currentlyMarked &= item.isExported();
212           }
213           for (final int selectedRow : selectedRows) {
214             getItemAt(selectedRow).setExported(!currentlyMarked);
215           }
216           myModel.fireTableDataChanged();
217           TableUtil.selectRows(myEntryTable, selectedRows);
218         }
219       },
220       KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
221       WHEN_FOCUSED
222     );
223
224     myEditButton = new AnActionButton(ProjectBundle.message("module.classpath.button.edit"), null, IconUtil.getEditIcon()) {
225       @Override
226       public void actionPerformed(@NotNull AnActionEvent e) {
227         doEdit();
228       }
229
230       @Override
231       public boolean isEnabled() {
232         ClasspathTableItem<?> selectedItem = getSelectedItem();
233         return selectedItem != null && selectedItem.isEditable();
234       }
235
236       @Override
237       public boolean isDumbAware() {
238         return true;
239       }
240     };
241     add(createTableWithButtons(), BorderLayout.CENTER);
242
243     if (myEntryTable.getRowCount() > 0) {
244       myEntryTable.getSelectionModel().setSelectionInterval(0,0);
245     }
246
247     new DoubleClickListener() {
248       @Override
249       protected boolean onDoubleClick(MouseEvent e) {
250         navigate(true);
251         return true;
252       }
253     }.installOn(myEntryTable);
254
255     DefaultActionGroup actionGroup = new DefaultActionGroup();
256     final AnAction navigateAction = new AnAction(ProjectBundle.message("classpath.panel.navigate.action.text")) {
257       @Override
258       public void actionPerformed(@NotNull AnActionEvent e) {
259         navigate(false);
260       }
261
262       @Override
263       public void update(@NotNull AnActionEvent e) {
264         final Presentation presentation = e.getPresentation();
265         presentation.setEnabled(false);
266         final OrderEntry entry = getSelectedEntry();
267         if (entry != null && entry.isValid()){
268           if (!(entry instanceof ModuleSourceOrderEntry)){
269             presentation.setEnabled(true);
270           }
271         }
272       }
273     };
274     navigateAction.registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_SOURCE).getShortcutSet(),
275                                              myEntryTable);
276     actionGroup.add(myEditButton);
277     actionGroup.add(myRemoveButton);
278     actionGroup.add(navigateAction);
279     actionGroup.add(new InlineModuleDependencyAction(this));
280     actionGroup.add(new MyFindUsagesAction());
281     actionGroup.add(new AnalyzeDependencyAction());
282     addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.PROJECT_LEVEL);
283     addChangeLibraryLevelAction(actionGroup, LibraryTablesRegistrar.APPLICATION_LEVEL);
284     addChangeLibraryLevelAction(actionGroup, LibraryTableImplUtil.MODULE_LEVEL);
285     PopupHandler.installPopupHandler(myEntryTable, actionGroup, ActionPlaces.UNKNOWN, ActionManager.getInstance());
286   }
287
288   @NotNull
289   private static SortOrder getNextSortOrder(@NotNull SortOrder order) {
290     switch (order) {
291       case ASCENDING:
292         return SortOrder.DESCENDING;
293       case DESCENDING:
294         return SortOrder.UNSORTED;
295       case UNSORTED:
296       default:
297         return SortOrder.ASCENDING;
298     }
299   }
300
301   private ClasspathTableItem<?> getItemAt(int selectedRow) {
302     return myModel.getItem(myEntryTable.convertRowIndexToModel(selectedRow));
303   }
304
305   private void addChangeLibraryLevelAction(DefaultActionGroup actionGroup, String tableLevel) {
306     final LibraryTablePresentation presentation = LibraryEditingUtil.getLibraryTablePresentation(getProject(), tableLevel);
307     actionGroup.add(new ChangeLibraryLevelInClasspathAction(this, presentation.getDisplayName(true), tableLevel));
308   }
309
310   @Override
311   @Nullable
312   public OrderEntry getSelectedEntry() {
313     ClasspathTableItem<?> item = getSelectedItem();
314     return item != null ? item.getEntry() : null;
315   }
316
317   @Nullable
318   private ClasspathTableItem<?> getSelectedItem() {
319     if (myEntryTable.getSelectedRowCount() != 1) return null;
320     return getItemAt(myEntryTable.getSelectedRow());
321   }
322
323   private void setFixedColumnWidth(final int columnIndex) {
324     final TableColumn column = myEntryTable.getTableHeader().getColumnModel().getColumn(columnIndex);
325     column.setResizable(false);
326     column.setMaxWidth(column.getPreferredWidth());
327   }
328
329   @Override
330   public void navigate(boolean openLibraryEditor) {
331     final OrderEntry entry = getSelectedEntry();
332     final ProjectStructureConfigurable rootConfigurable = ProjectStructureConfigurable.getInstance(myState.getProject());
333     if (entry instanceof ModuleOrderEntry){
334       Module module = ((ModuleOrderEntry)entry).getModule();
335       if (module != null) {
336         rootConfigurable.select(module.getName(), null, true);
337       }
338     }
339     else if (entry instanceof LibraryOrderEntry){
340       if (!openLibraryEditor) {
341         rootConfigurable.select((LibraryOrderEntry)entry, true);
342       }
343       else {
344         doEdit();
345       }
346     }
347     else if (entry instanceof JdkOrderEntry) {
348       Sdk jdk = ((JdkOrderEntry)entry).getJdk();
349       if (jdk != null) {
350         rootConfigurable.select(jdk, true);
351       }
352     }
353   }
354
355
356   private JComponent createTableWithButtons() {
357     final boolean isAnalyzeShown = false;
358
359     final ClasspathPanelAction removeAction = new ClasspathPanelAction(this) {
360       @Override
361       public void run() {
362         removeSelectedItems(TableUtil.removeSelectedItems(myEntryTable));
363       }
364     };
365
366     final AnActionButton analyzeButton = new AnActionButton(ProjectBundle.message("classpath.panel.analyze"), null, IconUtil.getAnalyzeIcon()) {
367       @Override
368       public void actionPerformed(@NotNull AnActionEvent e) {
369         AnalyzeDependenciesDialog.show(getRootModel().getModule());
370       }
371     };
372
373     //addButton.setShortcut(CustomShortcutSet.fromString("alt A", "INSERT"));
374     //removeButton.setShortcut(CustomShortcutSet.fromString("alt DELETE"));
375     //upButton.setShortcut(CustomShortcutSet.fromString("alt UP"));
376     //downButton.setShortcut(CustomShortcutSet.fromString("alt DOWN"));
377
378     final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myEntryTable);
379     AnActionButtonUpdater moveUpDownUpdater = new AnActionButtonUpdater() {
380       @Override
381       public boolean isEnabled(AnActionEvent e) {
382         for (RowSorter.SortKey key : myEntryTable.getRowSorter().getSortKeys()) {
383           if (key.getSortOrder() != SortOrder.UNSORTED) {
384             return false;
385           }
386         }
387         return true;
388       }
389     };
390     decorator.setAddAction(new AnActionButtonRunnable() {
391       @Override
392       public void run(AnActionButton button) {
393         initPopupActions();
394         final JBPopup popup = JBPopupFactory.getInstance().createListPopup(
395           new BaseListPopupStep<AddItemPopupAction<?>>(null, myPopupActions) {
396             @Override
397             public Icon getIconFor(AddItemPopupAction<?> aValue) {
398               return aValue.getIcon();
399             }
400
401             @Override
402             public boolean hasSubstep(AddItemPopupAction<?> selectedValue) {
403               return selectedValue.hasSubStep();
404             }
405
406             @Override
407             public boolean isMnemonicsNavigationEnabled() {
408               return true;
409             }
410
411             @Override
412             public PopupStep onChosen(final AddItemPopupAction<?> selectedValue, final boolean finalChoice) {
413               if (selectedValue.hasSubStep()) {
414                 return selectedValue.createSubStep();
415               }
416               return doFinalStep(new Runnable() {
417                 @Override
418                 public void run() {
419                   selectedValue.execute();
420                 }
421               });
422             }
423
424             @Override
425             @NotNull
426             public String getTextFor(AddItemPopupAction<?> value) {
427               return "&" + value.getIndex() + "  " + value.getTitle();
428             }
429           });
430         popup.show(button.getPreferredPopupPoint());
431       }
432     })
433       .setRemoveAction(new AnActionButtonRunnable() {
434         @Override
435         public void run(AnActionButton button) {
436           removeAction.actionPerformed(null);
437         }
438       })
439       .setRemoveActionUpdater(new AnActionButtonUpdater() {
440         @Override
441         public boolean isEnabled(AnActionEvent e) {
442           final int[] selectedRows = myEntryTable.getSelectedRows();
443           for (final int selectedRow : selectedRows) {
444             if (!getItemAt(selectedRow).isRemovable()) {
445               return false;
446             }
447           }
448           return selectedRows.length > 0;
449         }
450       })
451       .setMoveUpAction(new AnActionButtonRunnable() {
452         @Override
453         public void run(AnActionButton button) {
454           moveSelectedRows(-1);
455         }
456       })
457       .setMoveUpActionUpdater(moveUpDownUpdater)
458       .setMoveUpActionName("Move Up (disabled if items are shown in sorted order)")
459       .setMoveDownAction(new AnActionButtonRunnable() {
460         @Override
461         public void run(AnActionButton button) {
462           moveSelectedRows(+1);
463         }
464       })
465       .setMoveDownActionUpdater(moveUpDownUpdater)
466       .setMoveDownActionName("Move Down (disabled if items are shown in sorted order)")
467       .addExtraAction(myEditButton);
468     if (isAnalyzeShown) {
469       decorator.addExtraAction(analyzeButton);
470     }
471
472     final JPanel panel = decorator.createPanel();
473     myRemoveButton = ToolbarDecorator.findRemoveButton(panel);
474     return panel;
475   }
476
477   private void doEdit() {
478     final OrderEntry entry = getSelectedEntry();
479     if (!(entry instanceof LibraryOrderEntry)) return;
480
481     final Library library = ((LibraryOrderEntry)entry).getLibrary();
482     if (library == null) {
483       return;
484     }
485     final LibraryTable table = library.getTable();
486     final String tableLevel = table != null ? table.getTableLevel() : LibraryTableImplUtil.MODULE_LEVEL;
487     final LibraryTablePresentation presentation = LibraryEditingUtil.getLibraryTablePresentation(getProject(), tableLevel);
488     final LibraryTableModifiableModelProvider provider = getModifiableModelProvider(tableLevel);
489     EditExistingLibraryDialog dialog = EditExistingLibraryDialog.createDialog(this, provider, library, myState.getProject(),
490                                                                               presentation, getStructureConfigurableContext());
491     dialog.setContextModule(getRootModel().getModule());
492     dialog.show();
493     myEntryTable.repaint();
494     ModuleStructureConfigurable.getInstance(myState.getProject()).getTree().repaint();
495   }
496
497   private void removeSelectedItems(final List removedRows) {
498     if (removedRows.isEmpty()) {
499       return;
500     }
501     for (final Object removedRow : removedRows) {
502       final ClasspathTableItem<?> item = (ClasspathTableItem<?>)((Object[])removedRow)[ClasspathTableModel.ITEM_COLUMN];
503       final OrderEntry orderEntry = item.getEntry();
504       if (orderEntry == null) {
505         continue;
506       }
507
508       getRootModel().removeOrderEntry(orderEntry);
509     }
510     final int[] selectedRows = myEntryTable.getSelectedRows();
511     myModel.fireTableDataChanged();
512     TableUtil.selectRows(myEntryTable, selectedRows);
513     final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myState.getProject()).getContext();
514     context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, getRootModel().getModule()));
515   }
516
517   @Override
518   @NotNull
519   public LibraryTableModifiableModelProvider getModifiableModelProvider(@NotNull String tableLevel) {
520     if (LibraryTableImplUtil.MODULE_LEVEL.equals(tableLevel)) {
521       final LibraryTable moduleLibraryTable = getRootModel().getModuleLibraryTable();
522       return new LibraryTableModifiableModelProvider() {
523         @Override
524         public LibraryTable.ModifiableModel getModifiableModel() {
525           return moduleLibraryTable.getModifiableModel();
526         }
527       };
528     }
529     else {
530       return getStructureConfigurableContext().createModifiableModelProvider(tableLevel);
531     }
532   }
533
534   @Override
535   public void runClasspathPanelAction(Runnable action) {
536     try {
537       disableModelUpdate();
538       action.run();
539     }
540     finally {
541       enableModelUpdate();
542       myEntryTable.requestFocus();
543     }
544   }
545
546   @Override
547   public void addItems(List<ClasspathTableItem<?>> toAdd) {
548     for (ClasspathTableItem<?> item : toAdd) {
549       myModel.addRow(item);
550     }
551     TIntArrayList toSelect = new TIntArrayList();
552     for (int i = myModel.getRowCount() - toAdd.size(); i < myModel.getRowCount(); i++) {
553       toSelect.add(myEntryTable.convertRowIndexToView(i));
554     }
555     TableUtil.selectRows(myEntryTable, toSelect.toNativeArray());
556     TableUtil.scrollSelectionToVisible(myEntryTable);
557
558     final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myState.getProject()).getContext();
559     context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, getRootModel().getModule()));
560   }
561
562   @Override
563   public ModifiableRootModel getRootModel() {
564     return myState.getRootModel();
565   }
566
567   @Override
568   public Project getProject() {
569     return myState.getProject();
570   }
571
572   @Override
573   public ModuleConfigurationState getModuleConfigurationState() {
574     return myState;
575   }
576
577   @Override
578   public JComponent getComponent() {
579     return this;
580   }
581
582   public void rootsChanged() {
583     forceInitFromModel();
584   }
585
586   private void initPopupActions() {
587     if (myPopupActions == null) {
588       int actionIndex = 1;
589       final List<AddItemPopupAction<?>> actions = new ArrayList<AddItemPopupAction<?>>();
590       final StructureConfigurableContext context = getStructureConfigurableContext();
591       actions.add(new AddNewModuleLibraryAction(this, actionIndex++, context));
592       actions.add(new AddLibraryDependencyAction(this, actionIndex++, ProjectBundle.message("classpath.add.library.action"), context));
593       actions.add(new AddModuleDependencyAction(this, actionIndex, context)
594       );
595
596       myPopupActions = actions;
597     }
598   }
599
600   private StructureConfigurableContext getStructureConfigurableContext() {
601     return ProjectStructureConfigurable.getInstance(myState.getProject()).getContext();
602   }
603
604
605   private void enableModelUpdate() {
606     myInsideChange--;
607   }
608
609   private void disableModelUpdate() {
610     myInsideChange++;
611   }
612
613   public void addListener(OrderPanelListener listener) {
614     myListeners.addListener(listener);
615   }
616
617   public void removeListener(OrderPanelListener listener) {
618     myListeners.removeListener(listener);
619   }
620
621   private void moveSelectedRows(int increment) {
622     LOG.assertTrue(increment == -1 || increment == 1);
623     if (myEntryTable.isEditing()) {
624       myEntryTable.getCellEditor().stopCellEditing();
625     }
626     final ListSelectionModel selectionModel = myEntryTable.getSelectionModel();
627     for (int row = increment < 0 ? 0 : myModel.getRowCount() - 1; increment < 0 ? row < myModel.getRowCount() : row >= 0; row +=
628       increment < 0 ? +1 : -1) {
629       if (selectionModel.isSelectedIndex(row)) {
630         final int newRow = moveRow(row, increment);
631         selectionModel.removeSelectionInterval(row, row);
632         selectionModel.addSelectionInterval(newRow, newRow);
633       }
634     }
635     Rectangle cellRect = myEntryTable.getCellRect(selectionModel.getMinSelectionIndex(), 0, true);
636     myEntryTable.scrollRectToVisible(cellRect);
637     myEntryTable.repaint();
638   }
639
640   public void selectOrderEntry(@NotNull OrderEntry entry) {
641     for (int row = 0; row < myModel.getRowCount(); row++) {
642       final OrderEntry orderEntry = getItemAt(row).getEntry();
643       if (orderEntry != null && entry.getPresentableName().equals(orderEntry.getPresentableName())) {
644         myEntryTable.getSelectionModel().setSelectionInterval(row, row);
645         TableUtil.scrollSelectionToVisible(myEntryTable);
646       }
647     }
648     IdeFocusManager.getInstance(myState.getProject()).requestFocus(myEntryTable, true);
649   }
650
651   private int moveRow(final int row, final int increment) {
652     int newIndex = Math.abs(row + increment) % myModel.getRowCount();
653     myModel.exchangeRows(row, newIndex);
654     return newIndex;
655   }
656
657   public void stopEditing() {
658     TableUtil.stopEditing(myEntryTable);
659   }
660
661   private int myInsideChange = 0;
662   public void initFromModel() {
663     if (myInsideChange == 0) {
664       forceInitFromModel();
665     }
666   }
667
668   public void forceInitFromModel() {
669     Set<ClasspathTableItem<?>> oldSelection = new HashSet<ClasspathTableItem<?>>();
670     for (int i : myEntryTable.getSelectedRows()) {
671       ContainerUtil.addIfNotNull(getItemAt(i), oldSelection);
672     }
673     myModel.clear();
674     myModel.init();
675     myModel.fireTableDataChanged();
676     TIntArrayList newSelection = new TIntArrayList();
677     for (int i = 0; i < myModel.getRowCount(); i++) {
678       if (oldSelection.contains(getItemAt(i))) {
679         newSelection.add(i);
680       }
681     }
682     TableUtil.selectRows(myEntryTable, newSelection.toNativeArray());
683   }
684
685   static CellAppearanceEx getCellAppearance(final ClasspathTableItem<?> item,
686                                             final StructureConfigurableContext context,
687                                             final boolean selected) {
688     final OrderEntryAppearanceService service = OrderEntryAppearanceService.getInstance();
689     if (item instanceof InvalidJdkItem) {
690       return service.forJdk(null, false, selected, true);
691     }
692     else {
693       final OrderEntry entry = item.getEntry();
694       assert entry != null : item;
695       return service.forOrderEntry(context.getProject(), entry, selected);
696     }
697   }
698
699   private static class TableItemRenderer extends ColoredTableCellRenderer {
700     private final Border NO_FOCUS_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1);
701     private StructureConfigurableContext myContext;
702
703     public TableItemRenderer(StructureConfigurableContext context) {
704       myContext = context;
705     }
706
707     @Override
708     protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
709       setPaintFocusBorder(false);
710       setFocusBorderAroundIcon(true);
711       setBorder(NO_FOCUS_BORDER);
712       if (value instanceof ClasspathTableItem<?>) {
713         final ClasspathTableItem<?> tableItem = (ClasspathTableItem<?>)value;
714         getCellAppearance(tableItem, myContext, selected).customize(this);
715         setToolTipText(tableItem.getTooltipText());
716       }
717     }
718   }
719
720   private static class ExportFlagRenderer implements TableCellRenderer {
721     private final TableCellRenderer myDelegate;
722     private final JPanel myBlankPanel;
723
724     public ExportFlagRenderer(TableCellRenderer delegate) {
725       myDelegate = delegate;
726       myBlankPanel = new JPanel();
727     }
728
729     @Override
730     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
731       if (!table.isCellEditable(row, column)) {
732         myBlankPanel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
733         return myBlankPanel;
734       }
735       return myDelegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
736     }
737   }
738
739   private class MyFindUsagesAction extends FindUsagesInProjectStructureActionBase {
740     private MyFindUsagesAction() {
741       super(myEntryTable, myState.getProject());
742     }
743
744     @Override
745     protected boolean isEnabled() {
746       return getSelectedElement() != null;
747     }
748
749     @Override
750     protected ProjectStructureElement getSelectedElement() {
751       final OrderEntry entry = getSelectedEntry();
752       if (entry instanceof LibraryOrderEntry) {
753         final Library library = ((LibraryOrderEntry)entry).getLibrary();
754         if (library != null) {
755           return new LibraryProjectStructureElement(getContext(), library);
756         }
757       }
758       else if (entry instanceof ModuleOrderEntry) {
759         final Module module = ((ModuleOrderEntry)entry).getModule();
760         if (module != null) {
761           return new ModuleProjectStructureElement(getContext(), module);
762         }
763       }
764       else if (entry instanceof JdkOrderEntry) {
765         final Sdk jdk = ((JdkOrderEntry)entry).getJdk();
766         if (jdk != null) {
767           return new SdkProjectStructureElement(getContext(), jdk);
768         }
769       }
770       return null;
771     }
772
773     @Override
774     protected RelativePoint getPointToShowResults() {
775       Rectangle rect = myEntryTable.getCellRect(myEntryTable.getSelectedRow(), 1, false);
776       Point location = rect.getLocation();
777       location.y += rect.height;
778       return new RelativePoint(myEntryTable, location);
779     }
780   }
781   
782   private class AnalyzeDependencyAction extends AnAction {
783     private AnalyzeDependencyAction() {
784       super("Analyze This Dependency");
785     }
786
787     @Override
788     public void actionPerformed(@NotNull AnActionEvent e) {
789       final OrderEntry selectedEntry = getSelectedEntry();
790       GlobalSearchScope targetScope;
791       if (selectedEntry instanceof ModuleOrderEntry) {
792         final Module module = ((ModuleOrderEntry)selectedEntry).getModule();
793         LOG.assertTrue(module != null);
794         targetScope = GlobalSearchScope.moduleScope(module);
795       }
796       else {
797         Library library = ((LibraryOrderEntry)selectedEntry).getLibrary();
798         LOG.assertTrue(library != null);
799         targetScope = new LibraryScope(getProject(), library);
800       }
801       new AnalyzeDependenciesOnSpecifiedTargetHandler(getProject(), new AnalysisScope(myState.getRootModel().getModule()),
802                                                       targetScope) {
803         @Override
804         protected boolean shouldShowDependenciesPanel(List<DependenciesBuilder> builders) {
805           for (DependenciesBuilder builder : builders) {
806             for (Set<PsiFile> files : builder.getDependencies().values()) {
807               if (!files.isEmpty()) {
808                 Messages.showInfoMessage(myProject,
809                                          "Dependencies were successfully collected in \"" +
810                                          ToolWindowId.DEPENDENCIES + "\" toolwindow",
811                                          FindBundle.message("find.pointcut.applications.not.found.title"));
812                 return true;
813               }
814             }
815           }
816           if (Messages.showOkCancelDialog(myProject,
817                                           "No code dependencies were found. Would you like to remove the dependency?",
818                                           CommonBundle.getWarningTitle(), Messages.getWarningIcon()) == Messages.OK) {
819             removeSelectedItems(TableUtil.removeSelectedItems(myEntryTable));
820           }
821           return false;
822         }
823       }.analyze();
824     }
825
826     @Override
827     public void update(@NotNull AnActionEvent e) {
828       final OrderEntry entry = getSelectedEntry();
829       e.getPresentation().setVisible(entry instanceof ModuleOrderEntry && ((ModuleOrderEntry)entry).getModule() != null
830                                    || entry instanceof LibraryOrderEntry && ((LibraryOrderEntry)entry).getLibrary() != null);
831     }
832   }
833 }