| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.idea.maven.indices; |
| |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.editor.colors.EditorColorsManager; |
| import com.intellij.openapi.editor.colors.EditorFontType; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.ui.*; |
| import com.intellij.ui.components.JBScrollPane; |
| import com.intellij.ui.treeStructure.Tree; |
| import com.intellij.util.Alarm; |
| import com.intellij.util.ui.AbstractLayoutManager; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.idea.maven.model.MavenArtifactInfo; |
| import org.jetbrains.idea.maven.model.MavenId; |
| import org.jetbrains.idea.maven.utils.MavenLog; |
| |
| import javax.swing.*; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.event.TreeModelListener; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.TreeCellRenderer; |
| import javax.swing.tree.TreeModel; |
| import javax.swing.tree.TreePath; |
| import java.awt.*; |
| import java.awt.event.KeyAdapter; |
| import java.awt.event.KeyEvent; |
| import java.awt.event.MouseEvent; |
| import java.util.*; |
| import java.util.List; |
| |
| public class MavenArtifactSearchPanel extends JPanel { |
| private final Project myProject; |
| private final MavenArtifactSearchDialog myDialog; |
| private final boolean myClassMode; |
| private final Listener myListener; |
| |
| private JTextField mySearchField; |
| private Tree myResultList; |
| |
| private final Alarm myAlarm; |
| |
| private final Map<Pair<String, String>, String> myManagedDependenciesMap; |
| |
| public MavenArtifactSearchPanel(Project project, |
| String initialText, |
| boolean classMode, |
| Listener listener, |
| MavenArtifactSearchDialog dialog, Map<Pair<String, String>, String> managedDependenciesMap) { |
| myProject = project; |
| myDialog = dialog; |
| myClassMode = classMode; |
| myListener = listener; |
| myManagedDependenciesMap = managedDependenciesMap; |
| |
| initComponents(initialText); |
| myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, dialog.getDisposable()); |
| } |
| |
| public JTextField getSearchField() { |
| return mySearchField; |
| } |
| |
| private void initComponents(String initialText) { |
| myResultList = new Tree(); |
| myResultList.getExpandableItemsHandler().setEnabled(false); |
| myResultList.getEmptyText().setText("Loading..."); |
| myResultList.setRootVisible(false); |
| myResultList.setShowsRootHandles(true); |
| myResultList.setModel(null); |
| MyArtifactCellRenderer renderer = myClassMode ? new MyClassCellRenderer(myResultList) |
| : new MyArtifactCellRenderer(myResultList); |
| myResultList.setCellRenderer(renderer); |
| myResultList.setRowHeight(renderer.getPreferredSize().height); |
| |
| mySearchField = new JTextField(initialText); |
| mySearchField.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| int d; |
| if (e.getKeyCode() == KeyEvent.VK_DOWN) { |
| d = 1; |
| } |
| else if (e.getKeyCode() == KeyEvent.VK_UP) { |
| d = -1; |
| } |
| else { |
| return; |
| } |
| |
| int row = myResultList.getSelectionModel().getLeadSelectionRow(); |
| row += d; |
| |
| if (row >=0 && row < myResultList.getRowCount()) { |
| myResultList.setSelectionRow(row); |
| } |
| } |
| }); |
| |
| setLayout(new BorderLayout()); |
| add(mySearchField, BorderLayout.NORTH); |
| JScrollPane pane = ScrollPaneFactory.createScrollPane(myResultList); |
| pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); |
| pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); // Don't remove this line. |
| // Without VERTICAL_SCROLLBAR_ALWAYS policy our custom layout |
| // works incorrectly, see http://youtrack.jetbrains.com/issue/IDEA-72986 |
| |
| add(pane, BorderLayout.CENTER); |
| |
| mySearchField.getDocument().addDocumentListener(new DocumentAdapter() { |
| @Override |
| protected void textChanged(DocumentEvent e) { |
| scheduleSearch(); |
| } |
| }); |
| |
| myResultList.addTreeSelectionListener(new TreeSelectionListener() { |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| if (!myAlarm.isEmpty()) return; |
| |
| boolean hasSelection = !myResultList.isSelectionEmpty(); |
| myListener.canSelectStateChanged(MavenArtifactSearchPanel.this, hasSelection); |
| } |
| }); |
| |
| myResultList.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| if (e.getKeyCode() == KeyEvent.VK_ENTER && myResultList.getLastSelectedPathComponent() != null) { |
| myListener.itemSelected(); |
| e.consume(); |
| } |
| } |
| }); |
| |
| new DoubleClickListener() { |
| @Override |
| protected boolean onDoubleClick(MouseEvent e) { |
| final TreePath path = myResultList.getPathForLocation(e.getX(), e.getY()); |
| if (path != null && myResultList.isPathSelected(path)) { |
| Object sel = path.getLastPathComponent(); |
| if (sel != null && myResultList.getModel().isLeaf(sel)) { |
| myListener.itemSelected(); |
| return true; |
| } |
| } |
| return false; |
| } |
| }.installOn(myResultList); |
| } |
| |
| public void scheduleSearch() { |
| myListener.canSelectStateChanged(this, false); |
| myResultList.setPaintBusy(true); |
| |
| // evaluate text value in the swing thread |
| final String text = mySearchField.getText(); |
| |
| myAlarm.cancelAllRequests(); |
| myAlarm.addRequest(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| doSearch(text); |
| } |
| catch (Throwable e) { |
| MavenLog.LOG.warn(e); |
| } |
| } |
| }, 500); |
| } |
| |
| private void resortUsingDependencyVersionMap(List<MavenArtifactSearchResult> result) { |
| for (MavenArtifactSearchResult searchResult : result) { |
| if (searchResult.versions.isEmpty()) continue; |
| |
| MavenArtifactInfo artifactInfo = searchResult.versions.get(0); |
| final String managedVersion = myManagedDependenciesMap.get(Pair.create(artifactInfo.getGroupId(), artifactInfo.getArtifactId())); |
| if (managedVersion != null) { |
| Collections.sort(searchResult.versions, new Comparator<MavenArtifactInfo>() { |
| @Override |
| public int compare(MavenArtifactInfo o1, MavenArtifactInfo o2) { |
| String v1 = o1.getVersion(); |
| String v2 = o2.getVersion(); |
| if (Comparing.equal(v1, v2)) return 0; |
| if (managedVersion.equals(v1)) return -1; |
| if (managedVersion.equals(v2)) return 1; |
| return 0; |
| } |
| }); |
| } |
| } |
| } |
| |
| private void doSearch(String searchText) { |
| MavenSearcher searcher = myClassMode ? new MavenClassSearcher() : new MavenArtifactSearcher(); |
| List<MavenArtifactSearchResult> result = searcher.search(myProject, searchText, 200); |
| |
| resortUsingDependencyVersionMap(result); |
| |
| final TreeModel model = new MyTreeModel(result); |
| |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| if (!myDialog.isVisible()) return; |
| |
| myResultList.getEmptyText().setText("No results"); |
| myResultList.setModel(model); |
| myResultList.setSelectionRow(0); |
| myResultList.setPaintBusy(false); |
| } |
| }); |
| } |
| |
| @NotNull |
| public List<MavenId> getResult() { |
| List<MavenId> result = new ArrayList<MavenId>(); |
| |
| for (TreePath each : myResultList.getSelectionPaths()) { |
| Object sel = each.getLastPathComponent(); |
| MavenArtifactInfo info; |
| if (sel instanceof MavenArtifactInfo) { |
| info = (MavenArtifactInfo)sel; |
| } |
| else { |
| info = ((MavenArtifactSearchResult)sel).versions.get(0); |
| } |
| result.add(new MavenId(info.getGroupId(), info.getArtifactId(), info.getVersion())); |
| } |
| |
| return result; |
| } |
| |
| private static class MyTreeModel implements TreeModel { |
| List<? extends MavenArtifactSearchResult> myItems; |
| |
| private MyTreeModel(List<? extends MavenArtifactSearchResult> items) { |
| myItems = items; |
| } |
| |
| @Override |
| public Object getRoot() { |
| return myItems; |
| } |
| |
| @Override |
| public Object getChild(Object parent, int index) { |
| return getList(parent).get(index); |
| } |
| |
| @Override |
| public int getChildCount(Object parent) { |
| List list = getList(parent); |
| assert list != null : parent; |
| return list.size(); |
| } |
| |
| public List getList(Object parent) { |
| if (parent == myItems) return myItems; |
| if (parent instanceof MavenArtifactSearchResult) return ((MavenArtifactSearchResult)parent).versions; |
| return null; |
| } |
| |
| @Override |
| public boolean isLeaf(Object node) { |
| return node != myItems && (getList(node) == null || getChildCount(node) < 2); |
| } |
| |
| @Override |
| public int getIndexOfChild(Object parent, Object child) { |
| return getList(parent).indexOf(child); |
| } |
| |
| @Override |
| public void valueForPathChanged(TreePath path, Object newValue) { |
| } |
| |
| @Override |
| public void addTreeModelListener(TreeModelListener l) { |
| } |
| |
| @Override |
| public void removeTreeModelListener(TreeModelListener l) { |
| } |
| } |
| |
| private class MyArtifactCellRenderer extends JPanel implements TreeCellRenderer { |
| protected SimpleColoredComponent myLeftComponent = new SimpleColoredComponent(); |
| protected SimpleColoredComponent myRightComponent = new SimpleColoredComponent(); |
| |
| private MyArtifactCellRenderer(final Tree tree) { |
| myLeftComponent.setOpaque(false); |
| myRightComponent.setOpaque(false); |
| myLeftComponent.setIconOpaque(false); |
| myRightComponent.setIconOpaque(false); |
| add(myLeftComponent); |
| add(myRightComponent); |
| |
| Font font = EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.PLAIN); |
| myLeftComponent.setFont(font); |
| myRightComponent.setFont(font); |
| |
| setPreferredSize(new Dimension(2000, myLeftComponent.getPreferredSize().height)); |
| |
| setLayout(new AbstractLayoutManager() { |
| @Override |
| public Dimension preferredLayoutSize(Container parent) { |
| return new Dimension(getVisibleWidth(), myLeftComponent.getPreferredSize().height); |
| } |
| |
| @Override |
| public void layoutContainer(Container parent) { |
| int w = getVisibleWidth(); |
| |
| Dimension ls = myLeftComponent.getPreferredSize(); |
| Dimension rs = myRightComponent.getPreferredSize(); |
| |
| int lw = w - rs.width - 10; |
| int rw = rs.width; |
| |
| myLeftComponent.setBounds(0, 0, lw, ls.height); |
| myRightComponent.setBounds(w - rw, 0, rw, rs.height); |
| } |
| |
| private int getVisibleWidth() { |
| int w = tree.getVisibleRect().width - 10; |
| Insets insets = tree.getInsets(); |
| w -= insets.left + insets.right; |
| |
| JScrollPane scrollPane = JBScrollPane.findScrollPane(tree); |
| if (scrollPane != null) { |
| JScrollBar sb = scrollPane.getVerticalScrollBar(); |
| if (sb != null) { |
| w -= sb.getWidth(); |
| } |
| } |
| return w; |
| } |
| }); |
| } |
| |
| @Override |
| public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, |
| boolean hasFocus) { |
| myLeftComponent.clear(); |
| myRightComponent.clear(); |
| |
| setBackground(selected ? UIUtil.getTreeSelectionBackground() : tree.getBackground()); |
| |
| myLeftComponent.setForeground(selected ? UIUtil.getTreeSelectionForeground() : null); |
| myRightComponent.setForeground(selected ? UIUtil.getTreeSelectionForeground() : null); |
| |
| if (value == tree.getModel().getRoot()) { |
| myLeftComponent.append("Results", SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| } |
| else if (value instanceof MavenArtifactSearchResult) { |
| formatSearchResult(tree, (MavenArtifactSearchResult)value, selected); |
| } |
| else if (value instanceof MavenArtifactInfo) { |
| MavenArtifactInfo info = (MavenArtifactInfo)value; |
| String version = info.getVersion(); |
| |
| String managedVersion = myManagedDependenciesMap.get(Pair.create(info.getGroupId(), info.getArtifactId())); |
| |
| if (managedVersion != null && managedVersion.equals(version)) { |
| myLeftComponent.append(version, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); |
| myLeftComponent.append(" (from <dependencyManagement>)", SimpleTextAttributes.GRAYED_ATTRIBUTES); |
| } |
| else { |
| myLeftComponent.append(version, SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| } |
| } |
| |
| return this; |
| } |
| |
| protected void formatSearchResult(JTree tree, MavenArtifactSearchResult searchResult, boolean selected) { |
| MavenArtifactInfo info = searchResult.versions.get(0); |
| myLeftComponent.setIcon(AllIcons.Nodes.PpLib); |
| appendArtifactInfo(myLeftComponent, info, selected); |
| } |
| |
| protected void appendArtifactInfo(SimpleColoredComponent component, MavenArtifactInfo info, boolean selected) { |
| component.append(info.getGroupId() + ":", getGrayAttributes(selected)); |
| component.append(info.getArtifactId(), SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| component.append(":" + info.getVersion(), getGrayAttributes(selected)); |
| } |
| |
| protected SimpleTextAttributes getGrayAttributes(boolean selected) { |
| return !selected ? SimpleTextAttributes.GRAY_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES; |
| } |
| } |
| |
| private class MyClassCellRenderer extends MyArtifactCellRenderer { |
| |
| private MyClassCellRenderer(Tree tree) { |
| super(tree); |
| } |
| |
| @Override |
| protected void formatSearchResult(JTree tree, MavenArtifactSearchResult searchResult, boolean selected) { |
| MavenClassSearchResult classResult = (MavenClassSearchResult)searchResult; |
| MavenArtifactInfo info = searchResult.versions.get(0); |
| |
| myLeftComponent.setIcon(AllIcons.Nodes.Class); |
| myLeftComponent.append(classResult.className, SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| myLeftComponent.append(" (" + classResult.packageName + ")", getGrayAttributes(selected)); |
| |
| appendArtifactInfo(myRightComponent, info, selected); |
| } |
| } |
| |
| public interface Listener { |
| void itemSelected(); |
| |
| void canSelectStateChanged(MavenArtifactSearchPanel from, boolean canSelect); |
| } |
| } |