diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/ExistingTemplatesComponent.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/ExistingTemplatesComponent.java index 7c13eab4c88f..b40c48c72982 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/ExistingTemplatesComponent.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/ExistingTemplatesComponent.java @@ -12,6 +12,7 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.structuralsearch.MatchVariableConstraint; import com.intellij.structuralsearch.SSRBundle; @@ -22,13 +23,17 @@ import com.intellij.ui.treeStructure.Tree; import com.intellij.util.Function; import com.intellij.util.SmartList; import com.intellij.util.text.DateFormatUtil; +import com.intellij.util.ui.TextTransferable; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; +import org.jdom.Element; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.*; import java.awt.*; +import java.awt.datatransfer.Transferable; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -214,7 +219,29 @@ public class ExistingTemplatesComponent { tree.setDragEnabled(false); tree.setEditable(false); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + tree.setTransferHandler(new TransferHandler() { + @Nullable + @Override + protected Transferable createTransferable(JComponent c) { + final Object selection = tree.getLastSelectedPathComponent(); + if (!(selection instanceof DefaultMutableTreeNode)) { + return null; + } + final DefaultMutableTreeNode node = (DefaultMutableTreeNode)selection; + if (!(node.getUserObject() instanceof Configuration)) { + return null; + } + final Configuration configuration = (Configuration)node.getUserObject(); + final Element element = new Element(configuration instanceof SearchConfiguration ? "searchConfiguration" : "replaceConfiguration"); + configuration.writeExternal(element); + return new TextTransferable(JDOMUtil.writeElement(element)); + } + @Override + public int getSourceActions(JComponent c) { + return COPY; + } + }); final TreeSpeedSearch speedSearch = new TreeSpeedSearch( tree, diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java index 874b949ab999..f5efb03fd646 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/StructuralSearchDialog.java @@ -22,6 +22,7 @@ import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.*; import com.intellij.openapi.ui.DialogWrapper; @@ -29,10 +30,7 @@ import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.DimensionService; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -59,14 +57,19 @@ import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.textCompletion.TextCompletionUtil; import com.intellij.util.ui.LafIconLookup; +import com.intellij.util.ui.TextTransferable; +import org.jdom.Element; +import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.datatransfer.DataFlavor; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -108,6 +111,7 @@ public class StructuralSearchDialog extends DialogWrapper { private JCheckBox myReformat; // replace private JCheckBox myUseStaticImport; // replace FileTypeSelector myFileTypesComboBox; + ActionToolbarImpl myOptionsToolbar; EditorTextField mySearchCriteriaEdit; EditorTextField myReplaceCriteriaEdit; OnePixelSplitter mySearchEditorPanel; @@ -263,7 +267,7 @@ public class StructuralSearchDialog extends DialogWrapper { myFileType = StructuralSearchUtil.getDefaultFileType(); } - public Configuration createConfiguration(Configuration template) { + private Configuration createConfiguration(Configuration template) { if (myReplace) { return (template == null) ? new ReplaceConfiguration(USER_DEFINED, USER_DEFINED) : new ReplaceConfiguration(template); } @@ -273,6 +277,7 @@ public class StructuralSearchDialog extends DialogWrapper { } private void setText(String text) { + text = text.trim(); setTextForEditor(text, mySearchCriteriaEdit); if (myReplace) { setTextForEditor(text, myReplaceCriteriaEdit); @@ -495,6 +500,8 @@ public class StructuralSearchDialog extends DialogWrapper { ConfigurationManager.getInstance(getProject()).showSaveTemplateAsDialog(getConfiguration()); } }, + new CopyConfigurationAction(), + new PasteConfigurationAction(), new DumbAwareAction(SSRBundle.message("copy.existing.template.button")) { @Override public void actionPerformed(@NotNull AnActionEvent e) { @@ -533,10 +540,9 @@ public class StructuralSearchDialog extends DialogWrapper { } }; final DefaultActionGroup optionsActionGroup = new DefaultActionGroup(filterAction, templateActionGroup); - final ActionToolbarImpl optionsToolbar = - (ActionToolbarImpl)actionManager.createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, optionsActionGroup, true); - optionsToolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY); - optionsToolbar.setForceMinimumSize(true); + myOptionsToolbar = (ActionToolbarImpl)actionManager.createActionToolbar(ActionPlaces.EDITOR_TOOLBAR, optionsActionGroup, true); + myOptionsToolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY); + myOptionsToolbar.setForceMinimumSize(true); final JPanel northPanel = new JPanel(null); final GroupLayout layout = new GroupLayout(northPanel); @@ -554,7 +560,7 @@ public class StructuralSearchDialog extends DialogWrapper { .addComponent(fileTypeLabel) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 5, 5) .addComponent(myFileTypesComboBox, 125, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addComponent(optionsToolbar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(myOptionsToolbar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) ); layout.setVerticalGroup( layout.createParallelGroup(GroupLayout.Alignment.BASELINE) @@ -564,7 +570,7 @@ public class StructuralSearchDialog extends DialogWrapper { .addComponent(myMatchCase) .addComponent(fileTypeLabel) .addComponent(myFileTypesComboBox) - .addComponent(optionsToolbar) + .addComponent(myOptionsToolbar) ); return northPanel; @@ -616,9 +622,9 @@ public class StructuralSearchDialog extends DialogWrapper { if (editor != null) { final SelectionModel selectionModel = editor.getSelectionModel(); - - if (selectionModel.hasSelection()) { - setText(selectionModel.getSelectedText()); + final String selectedText = selectionModel.getSelectedText(); + if (selectedText != null && !loadConfiguration(selectedText)) { + setText(selectedText); myScopePanel.setScope(null); setSomeText = true; } @@ -743,7 +749,7 @@ public class StructuralSearchDialog extends DialogWrapper { return myEditConfigOnly || myScopePanel.getScope() != null; } - private void reportMessage(String message, boolean error, JComponent component) { + void reportMessage(String message, boolean error, JComponent component) { com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(() -> { component.putClientProperty("JComponent.outline", (!error || message == null) ? null : "error"); component.repaint(); @@ -752,7 +758,7 @@ public class StructuralSearchDialog extends DialogWrapper { final Balloon balloon = JBPopupFactory.getInstance() .createHtmlTextBalloonBuilder(message, error ? MessageType.ERROR : MessageType.WARNING, null) .createBalloon(); - if (component == mySearchCriteriaEdit) { + if (component != myScopePanel) { balloon.show(new RelativePoint(component, new Point(component.getWidth() / 2, component.getHeight())), Balloon.Position.below); } else { @@ -809,6 +815,32 @@ public class StructuralSearchDialog extends DialogWrapper { } } + boolean loadConfiguration(String text) { + if (text == null) { + return false; + } + text = text.trim(); + final Configuration configuration; + if (text.startsWith("")) { + configuration = new SearchConfiguration(); + } + else if (text.startsWith("")) { + configuration = new ReplaceConfiguration(); + } + else { + return false; + } + try { + final Element element = JDOMUtil.load(text); + configuration.readExternal(element); + loadConfiguration(configuration); + return true; + } + catch (IOException | JDOMException ignored) { + return false; + } + } + public void loadConfiguration(Configuration configuration) { myConfiguration = createConfiguration(configuration); final MatchOptions matchOptions = myConfiguration.getMatchOptions(); @@ -974,4 +1006,34 @@ public class StructuralSearchDialog extends DialogWrapper { registerCustomShortcutSet(shortcutSet, getRootPane()); } } + + private class CopyConfigurationAction extends AnAction implements DumbAware { + + CopyConfigurationAction() { + super("Copy Template to Clipboard"); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + final Element element = new Element(myConfiguration instanceof SearchConfiguration ? "searchConfiguration" : "replaceConfiguration"); + myConfiguration.writeExternal(element); + final TextTransferable transferable = new TextTransferable(JDOMUtil.writeElement(element)); + CopyPasteManager.getInstance().setContents(transferable); + } + } + + private class PasteConfigurationAction extends AnAction implements DumbAware { + + PasteConfigurationAction() { + super("Paste Template from Clipboard"); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + final String contents = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor); + if (!loadConfiguration(contents)) { + reportMessage("No template found on the clipboard", false, myOptionsToolbar); + } + } + } }