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