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