mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
347 lines
14 KiB
Java
347 lines
14 KiB
Java
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
|
package com.intellij.internal;
|
|
|
|
import com.intellij.codeInsight.hint.ImplementationViewComponent;
|
|
import com.intellij.codeInsight.hint.PsiImplementationViewElement;
|
|
import com.intellij.ide.ui.search.SearchUtil;
|
|
import com.intellij.ide.util.PropertiesComponent;
|
|
import com.intellij.ide.util.PropertyName;
|
|
import com.intellij.openapi.actionSystem.*;
|
|
import com.intellij.openapi.module.Module;
|
|
import com.intellij.openapi.module.ModuleManager;
|
|
import com.intellij.openapi.module.ModuleUtil;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.ui.DialogWrapper;
|
|
import com.intellij.openapi.ui.popup.JBPopup;
|
|
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
|
import com.intellij.openapi.ui.popup.JBPopupListener;
|
|
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
|
|
import com.intellij.openapi.util.io.FileUtil;
|
|
import com.intellij.openapi.util.text.StringUtil;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.psi.PsiFile;
|
|
import com.intellij.psi.PsiManager;
|
|
import com.intellij.ui.*;
|
|
import com.intellij.ui.awt.RelativePoint;
|
|
import com.intellij.ui.components.JBList;
|
|
import com.intellij.ui.components.JBScrollPane;
|
|
import com.intellij.ui.treeStructure.Tree;
|
|
import com.intellij.util.NotNullFunction;
|
|
import com.intellij.util.PlatformIcons;
|
|
import com.intellij.util.ui.UIUtil;
|
|
import com.intellij.util.ui.tree.TreeUtil;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import javax.swing.*;
|
|
import javax.swing.event.TreeSelectionListener;
|
|
import javax.swing.tree.DefaultMutableTreeNode;
|
|
import javax.swing.tree.DefaultTreeModel;
|
|
import javax.swing.tree.TreePath;
|
|
import java.awt.*;
|
|
import java.awt.event.ActionEvent;
|
|
import java.io.File;
|
|
import java.util.List;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* @author Konstantin Bulenkov
|
|
*/
|
|
public final class ImageDuplicateResultsDialog extends DialogWrapper {
|
|
private final Project myProject;
|
|
private final List<? extends VirtualFile> myImages;
|
|
private final Map<String, Set<VirtualFile>> myDuplicates;
|
|
private final Tree myTree;
|
|
private final TreeSpeedSearch mySpeedSearch;
|
|
private final ResourceModules myResourceModules = new ResourceModules();
|
|
|
|
|
|
public ImageDuplicateResultsDialog(Project project, List<? extends VirtualFile> images, Map<String, Set<VirtualFile>> duplicates) {
|
|
super(project);
|
|
myProject = project;
|
|
myImages = images;
|
|
PropertiesComponent.getInstance(myProject).loadFields(myResourceModules);
|
|
myDuplicates = duplicates;
|
|
setModal(false);
|
|
myTree = new Tree(new MyRootNode());
|
|
myTree.setRootVisible(true);
|
|
MyCellRenderer renderer = new MyCellRenderer();
|
|
myTree.setCellRenderer(renderer);
|
|
mySpeedSearch = TreeSpeedSearch.installOn(myTree, false, x -> renderer.getTreeCellRendererComponent(myTree, x.getLastPathComponent(), false, false, false, 0, false).toString());
|
|
init();
|
|
TreeUtil.expandAll(myTree);
|
|
setTitle("Image Duplicates");
|
|
TreeUtil.promiseSelectFirst(myTree);
|
|
}
|
|
|
|
@Override
|
|
protected Action @NotNull [] createActions() {
|
|
final Action[] actions = new Action[4];
|
|
actions[0] = new AbstractAction() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
}
|
|
};
|
|
actions[0].putValue(Action.NAME, "Fix all");
|
|
actions[0].putValue(DEFAULT_ACTION, Boolean.TRUE);
|
|
actions[0].putValue(FOCUSED_ACTION, Boolean.TRUE);
|
|
actions[1] = new AbstractAction() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
}
|
|
};
|
|
actions[1].putValue(Action.NAME, "Fix selected");
|
|
actions[2] = getCancelAction();
|
|
actions[3] = getHelpAction();
|
|
//return actions;
|
|
return new Action[0];
|
|
}
|
|
|
|
@Override
|
|
protected JComponent createCenterPanel() {
|
|
JPanel panel = new JPanel(new BorderLayout());
|
|
JBList<String> list = new JBList<>(new ResourceModules().getModuleNames());
|
|
final NotNullFunction<Object, JComponent> modulesRenderer =
|
|
dom -> new JLabel(dom instanceof Module ? ((Module)dom).getName() : dom.toString(), PlatformIcons.SOURCE_FOLDERS_ICON, SwingConstants.LEFT);
|
|
list.installCellRenderer(modulesRenderer);
|
|
final JPanel modulesPanel = ToolbarDecorator.createDecorator(list)
|
|
.setAddAction(button -> {
|
|
final Module[] all = ModuleManager.getInstance(myProject).getModules();
|
|
Arrays.sort(all, Comparator.comparing(Module::getName));
|
|
JBList<Module> modules = new JBList<>(all);
|
|
modules.installCellRenderer(modulesRenderer);
|
|
JBPopupFactory.getInstance().createListPopupBuilder(modules)
|
|
.setTitle("Add Resource Module")
|
|
.setNamerForFiltering(o -> o.getName())
|
|
.setItemChosenCallback(() -> {
|
|
Module value = modules.getSelectedValue();
|
|
if (value != null && !myResourceModules.contains(value)) {
|
|
myResourceModules.add(value);
|
|
((DefaultListModel<String>)list.getModel()).addElement(value.getName());
|
|
}
|
|
((DefaultTreeModel)myTree.getModel()).reload();
|
|
TreeUtil.expandAll(myTree);
|
|
}).createPopup().show(button.getPreferredPopupPoint());
|
|
})
|
|
.setRemoveAction(button -> {
|
|
final Object[] values = list.getSelectedValues();
|
|
for (Object value : values) {
|
|
myResourceModules.remove((String)value);
|
|
((DefaultListModel<String>)list.getModel()).removeElement(value);
|
|
}
|
|
((DefaultTreeModel)myTree.getModel()).reload();
|
|
TreeUtil.expandAll(myTree);
|
|
})
|
|
.disableDownAction()
|
|
.disableUpAction()
|
|
.createPanel();
|
|
modulesPanel.setPreferredSize(new Dimension(-1, 60));
|
|
final JPanel top = new JPanel(new BorderLayout());
|
|
top.add(new JLabel("Image modules:"), BorderLayout.NORTH);
|
|
top.add(modulesPanel, BorderLayout.CENTER);
|
|
|
|
panel.add(top, BorderLayout.NORTH);
|
|
panel.add(new JBScrollPane(myTree), BorderLayout.CENTER);
|
|
new AnAction() {
|
|
|
|
@Override
|
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
|
VirtualFile file = getFileFromSelection();
|
|
if (file != null) {
|
|
final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
|
|
if (psiFile != null) {
|
|
final ImplementationViewComponent viewComponent = new ImplementationViewComponent(Collections.singletonList(new PsiImplementationViewElement(psiFile)), 0);
|
|
final TreeSelectionListener listener = e1 -> {
|
|
final VirtualFile selection = getFileFromSelection();
|
|
if (selection != null) {
|
|
final PsiFile newElement = PsiManager.getInstance(myProject).findFile(selection);
|
|
if (newElement != null) {
|
|
viewComponent.update(Collections.singletonList(new PsiImplementationViewElement(newElement)), 0);
|
|
}
|
|
}
|
|
};
|
|
myTree.addTreeSelectionListener(listener);
|
|
|
|
final JBPopup popup =
|
|
JBPopupFactory.getInstance().createComponentPopupBuilder(viewComponent, viewComponent.getPreferredFocusableComponent())
|
|
.setProject(myProject)
|
|
.setDimensionServiceKey(myProject, ImageDuplicateResultsDialog.class.getName(), false)
|
|
.setResizable(true)
|
|
.setMovable(true)
|
|
.setRequestFocus(false)
|
|
.addListener(new JBPopupListener() {
|
|
@Override
|
|
public void onClosed(@NotNull LightweightWindowEvent event) {
|
|
viewComponent.cleanup();
|
|
}
|
|
})
|
|
.setCancelCallback(() -> {
|
|
myTree.removeTreeSelectionListener(listener);
|
|
return true;
|
|
})
|
|
.setTitle("Image Preview")
|
|
.createPopup();
|
|
|
|
|
|
final Window window = ImageDuplicateResultsDialog.this.getWindow();
|
|
popup.show(new RelativePoint(window, new Point(window.getWidth(), 0)));
|
|
viewComponent.setHint(popup, "Image Preview");
|
|
}
|
|
}
|
|
}
|
|
}.registerCustomShortcutSet(CustomShortcutSet.fromString("ENTER"), panel);
|
|
|
|
int total = myDuplicates.values().stream().mapToInt(Set::size).sum() - myDuplicates.size();
|
|
final JLabel label = new JLabel(
|
|
"<html>Press <b>Enter</b> to preview image<br>Total images found: " + myImages.size() + ". Total duplicates found: " + total+"</html>");
|
|
panel.add(label, BorderLayout.SOUTH);
|
|
return UiDataProvider.wrapComponent(panel, sink -> uiDataSnapshot(sink));
|
|
}
|
|
|
|
private void uiDataSnapshot(@NotNull DataSink sink) {
|
|
TreePath path = myTree.getSelectionPath();
|
|
Object component = path == null ? null : path.getLastPathComponent();
|
|
if (component instanceof MyFileNode o) component = o.getParent();
|
|
VirtualFile file = component instanceof MyDuplicatesNode o ? o.getUserObject().iterator().next() : null;
|
|
sink.set(CommonDataKeys.VIRTUAL_FILE, file);
|
|
sink.set(CommonDataKeys.VIRTUAL_FILE_ARRAY, file == null ? null : new VirtualFile[]{file});
|
|
}
|
|
|
|
@Override
|
|
protected String getDimensionServiceKey() {
|
|
return "image.duplicates.dialog";
|
|
}
|
|
|
|
@Override
|
|
public JComponent getPreferredFocusedComponent() {
|
|
return myTree;
|
|
}
|
|
|
|
@Nullable
|
|
private VirtualFile getFileFromSelection() {
|
|
final TreePath path = myTree.getSelectionPath();
|
|
if (path != null) {
|
|
Object component = path.getLastPathComponent();
|
|
VirtualFile file = null;
|
|
if (component instanceof MyFileNode) {
|
|
component = ((MyFileNode)component).getParent();
|
|
}
|
|
if (component instanceof MyDuplicatesNode) {
|
|
file = ((MyDuplicatesNode)component).getUserObject().iterator().next();
|
|
}
|
|
return file;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
private final class MyRootNode extends DefaultMutableTreeNode {
|
|
private MyRootNode() {
|
|
children =
|
|
myDuplicates.values().stream().map(files -> new MyDuplicatesNode(this, files)).collect(Collectors.toCollection(Vector::new));
|
|
}
|
|
}
|
|
|
|
|
|
private static final class MyDuplicatesNode extends DefaultMutableTreeNode {
|
|
|
|
MyDuplicatesNode(DefaultMutableTreeNode node, Set<? extends VirtualFile> files) {
|
|
super(files);
|
|
setParent(node);
|
|
children = files.stream().map(file -> new MyFileNode(this, file)).collect(Collectors.toCollection(Vector::new));
|
|
}
|
|
|
|
@Override
|
|
public Set<VirtualFile> getUserObject() {
|
|
return (Set<VirtualFile>)super.getUserObject();
|
|
}
|
|
}
|
|
|
|
private static final class MyFileNode extends DefaultMutableTreeNode {
|
|
MyFileNode(DefaultMutableTreeNode node, VirtualFile file) {
|
|
super(file);
|
|
setParent(node);
|
|
}
|
|
|
|
@Override
|
|
public VirtualFile getUserObject() {
|
|
return (VirtualFile)super.getUserObject();
|
|
}
|
|
}
|
|
|
|
private final class MyCellRenderer extends ColoredTreeCellRenderer {
|
|
@Override
|
|
public void customizeCellRenderer(@NotNull JTree tree,
|
|
Object value,
|
|
boolean selected,
|
|
boolean expanded,
|
|
boolean leaf,
|
|
int row,
|
|
boolean hasFocus) {
|
|
if (value instanceof MyFileNode) {
|
|
final VirtualFile file = ((MyFileNode)value).getUserObject();
|
|
final Module module = ModuleUtil.findModuleForFile(file, myProject);
|
|
if (module != null) {
|
|
setIcon(PlatformIcons.CONTENT_ROOT_ICON_CLOSED);
|
|
SearchUtil.appendFragments(mySpeedSearch.getEnteredPrefix(), "[" + module.getName() + "] ", SimpleTextAttributes.STYLE_BOLD, UIUtil.getTreeForeground(), UIUtil.getTreeBackground(), this);
|
|
}
|
|
SearchUtil.appendFragments(mySpeedSearch.getEnteredPrefix(), getRelativePathToProject(myProject, file), SimpleTextAttributes.STYLE_PLAIN, UIUtil.getTreeForeground(), UIUtil.getTreeBackground(), this);
|
|
}
|
|
else if (value instanceof MyDuplicatesNode) {
|
|
final Set<VirtualFile> files = ((MyDuplicatesNode)value).getUserObject();
|
|
for (VirtualFile file : files) {
|
|
final Module module = ModuleUtil.findModuleForFile(file, myProject);
|
|
|
|
if (module != null && myResourceModules.contains(module)) {
|
|
append("Icons can be replaced to ");
|
|
append(getRelativePathToProject(myProject, file),
|
|
new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, ColorUtil.fromHex("008000")));
|
|
return;
|
|
}
|
|
}
|
|
append("Icon conflict");
|
|
} else if (value instanceof MyRootNode) {
|
|
append("All conflicts");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static String getRelativePathToProject(Project project, VirtualFile file) {
|
|
final String path = project.getBasePath();
|
|
assert path != null;
|
|
final String result = FileUtil.getRelativePath(path, file.getPath().replace('/', File.separatorChar), File.separatorChar);
|
|
assert result != null;
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static final class ResourceModules {
|
|
@PropertyName(value = "resource.modules", defaultValue = "icons")
|
|
public String modules;
|
|
|
|
public List<String> getModuleNames() {
|
|
return Arrays.asList(StringUtil.splitByLines(modules == null ? "icons" : modules));
|
|
}
|
|
|
|
public boolean contains(Module module) {
|
|
return getModuleNames().contains(module.getName());
|
|
}
|
|
|
|
public void add(Module module) {
|
|
if (StringUtil.isEmpty(modules)) {
|
|
modules = module.getName();
|
|
} else {
|
|
modules += "\n" + module.getName();
|
|
}
|
|
}
|
|
|
|
public void remove(String value) {
|
|
final List<String> names = new ArrayList<>(getModuleNames());
|
|
names.remove(value);
|
|
modules = StringUtil.join(names, "\n");
|
|
}
|
|
}
|
|
}
|