| /* |
| * Copyright 2000-2013 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 com.intellij.uiDesigner.snapShooter; |
| |
| import com.intellij.execution.ExecutionException; |
| import com.intellij.execution.RunManager; |
| import com.intellij.execution.RunnerAndConfigurationSettings; |
| import com.intellij.execution.application.ApplicationConfiguration; |
| import com.intellij.execution.application.ApplicationConfigurationType; |
| import com.intellij.execution.executors.DefaultRunExecutor; |
| import com.intellij.execution.runners.ExecutionEnvironmentBuilder; |
| import com.intellij.execution.runners.ExecutionUtil; |
| import com.intellij.execution.util.JreVersionDetector; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.IdeView; |
| import com.intellij.ide.highlighter.JavaHighlightingColors; |
| import com.intellij.openapi.actionSystem.AnAction; |
| import com.intellij.openapi.actionSystem.AnActionEvent; |
| import com.intellij.openapi.actionSystem.CommonDataKeys; |
| import com.intellij.openapi.actionSystem.LangDataKeys; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.colors.EditorColorsManager; |
| import com.intellij.openapi.editor.colors.EditorColorsScheme; |
| import com.intellij.openapi.editor.markup.TextAttributes; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.ProjectFileIndex; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.vfs.CharsetToolkit; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.ui.ColoredTreeCellRenderer; |
| import com.intellij.ui.DocumentAdapter; |
| import com.intellij.ui.SimpleTextAttributes; |
| import com.intellij.uiDesigner.GuiFormFileType; |
| import com.intellij.uiDesigner.UIDesignerBundle; |
| import com.intellij.uiDesigner.designSurface.InsertComponentProcessor; |
| import com.intellij.uiDesigner.palette.ComponentItem; |
| import com.intellij.uiDesigner.palette.Palette; |
| import com.intellij.uiDesigner.radComponents.LayoutManagerRegistry; |
| import com.intellij.uiDesigner.radComponents.RadComponentFactory; |
| import com.intellij.uiDesigner.radComponents.RadContainer; |
| import com.intellij.util.IncorrectOperationException; |
| import icons.UIDesignerIcons; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.TreePath; |
| import javax.swing.tree.TreeSelectionModel; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| /** |
| * @author yole |
| */ |
| public class CreateSnapShotAction extends AnAction { |
| private static final Logger LOG = Logger.getInstance("com.intellij.uiDesigner.snapShooter.CreateSnapShotAction"); |
| |
| @Override |
| public void update(AnActionEvent e) { |
| final Project project = e.getData(CommonDataKeys.PROJECT); |
| final IdeView view = e.getData(LangDataKeys.IDE_VIEW); |
| e.getPresentation().setVisible(project != null && view != null && hasDirectoryInPackage(project, view)); |
| } |
| |
| private static boolean hasDirectoryInPackage(final Project project, final IdeView view) { |
| ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); |
| PsiDirectory[] dirs = view.getDirectories(); |
| for (PsiDirectory dir : dirs) { |
| if (projectFileIndex.isInSourceContent(dir.getVirtualFile()) && JavaDirectoryService.getInstance().getPackage(dir) != null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public void actionPerformed(AnActionEvent e) { |
| final Project project = e.getData(CommonDataKeys.PROJECT); |
| final IdeView view = e.getData(LangDataKeys.IDE_VIEW); |
| if (project == null || view == null) { |
| return; |
| } |
| |
| final PsiDirectory dir = view.getOrChooseDirectory(); |
| if (dir == null) return; |
| |
| final SnapShotClient client = new SnapShotClient(); |
| List<RunnerAndConfigurationSettings> appConfigurations = new ArrayList<RunnerAndConfigurationSettings>(); |
| RunnerAndConfigurationSettings snapshotConfiguration = null; |
| boolean connected = false; |
| |
| ApplicationConfigurationType cfgType = ApplicationConfigurationType.getInstance(); |
| List<RunnerAndConfigurationSettings> racsi = RunManager.getInstance(project).getConfigurationSettingsList(cfgType); |
| |
| for(RunnerAndConfigurationSettings config: racsi) { |
| if (config.getConfiguration() instanceof ApplicationConfiguration) { |
| ApplicationConfiguration appConfig = (ApplicationConfiguration) config.getConfiguration(); |
| appConfigurations.add(config); |
| if (appConfig.ENABLE_SWING_INSPECTOR) { |
| SnapShooterConfigurationSettings settings = SnapShooterConfigurationSettings.get(appConfig); |
| snapshotConfiguration = config; |
| if (settings.getLastPort() > 0) { |
| try { |
| client.connect(settings.getLastPort()); |
| connected = true; |
| } |
| catch(IOException ex) { |
| connected = false; |
| } |
| } |
| } |
| if (connected) break; |
| } |
| } |
| |
| if (snapshotConfiguration == null) { |
| snapshotConfiguration = promptForSnapshotConfiguration(project, appConfigurations); |
| if (snapshotConfiguration == null) return; |
| } |
| |
| if (!connected) { |
| int rc = Messages.showYesNoDialog(project, UIDesignerBundle.message("snapshot.run.prompt"), |
| UIDesignerBundle.message("snapshot.title"), Messages.getQuestionIcon()); |
| if (rc == Messages.NO) return; |
| final ApplicationConfiguration appConfig = (ApplicationConfiguration) snapshotConfiguration.getConfiguration(); |
| final SnapShooterConfigurationSettings settings = SnapShooterConfigurationSettings.get(appConfig); |
| settings.setNotifyRunnable(new Runnable() { |
| public void run() { |
| SwingUtilities.invokeLater(new Runnable() { |
| public void run() { |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.prepare.notice"), |
| UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon()); |
| try { |
| client.connect(settings.getLastPort()); |
| } |
| catch(IOException ex) { |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.connection.error"), |
| UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon()); |
| return; |
| } |
| runSnapShooterSession(client, project, dir, view); |
| } |
| }); |
| } |
| }); |
| |
| try { |
| ExecutionEnvironmentBuilder.create(DefaultRunExecutor.getRunExecutorInstance(), snapshotConfiguration).buildAndExecute(); |
| } |
| catch (ExecutionException ex) { |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.run.error", ex.getMessage()), |
| UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon()); |
| } |
| } |
| else { |
| runSnapShooterSession(client, project, dir, view); |
| } |
| } |
| |
| private static void runSnapShooterSession(final SnapShotClient client, final Project project, final PsiDirectory dir, final IdeView view) { |
| try { |
| client.suspendSwing(); |
| } |
| catch (IOException e1) { |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.connection.error"), |
| UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon()); |
| return; |
| } |
| |
| final MyDialog dlg = new MyDialog(project, client, dir); |
| dlg.show(); |
| if (dlg.getExitCode() == DialogWrapper.OK_EXIT_CODE) { |
| final int id = dlg.getSelectedComponentId(); |
| final Ref<Object> result = new Ref<Object>(); |
| ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { |
| public void run() { |
| try { |
| result.set(client.createSnapshot(id)); |
| } |
| catch (Exception ex) { |
| result.set(ex); |
| } |
| } |
| }, UIDesignerBundle.message("progress.creating.snapshot"), false, project); |
| |
| String snapshot = null; |
| if (result.get() instanceof String) { |
| snapshot = (String) result.get(); |
| } |
| else { |
| Exception ex = (Exception) result.get(); |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.create.error", ex.getMessage()), |
| UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon()); |
| } |
| |
| if (snapshot != null) { |
| final String snapshot1 = snapshot; |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| public void run() { |
| CommandProcessor.getInstance().executeCommand(project, new Runnable() { |
| public void run() { |
| try { |
| PsiFile formFile = PsiFileFactory.getInstance(dir.getProject()) |
| .createFileFromText(dlg.getFormName() + GuiFormFileType.DOT_DEFAULT_EXTENSION, snapshot1); |
| formFile = (PsiFile)dir.add(formFile); |
| formFile.getVirtualFile().setCharset(CharsetToolkit.UTF8_CHARSET); |
| formFile.getViewProvider().getDocument().setText(snapshot1); |
| view.selectElement(formFile); |
| } |
| catch (IncorrectOperationException ex) { |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.save.error", ex.getMessage()), |
| UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon()); |
| } |
| } |
| }, "", null); |
| } |
| }); |
| } |
| } |
| |
| try { |
| client.resumeSwing(); |
| } |
| catch (IOException ex) { |
| Messages.showErrorDialog(project, UIDesignerBundle.message("snapshot.connection.broken"), |
| UIDesignerBundle.message("snapshot.title")); |
| } |
| |
| client.dispose(); |
| } |
| |
| @Nullable |
| private static RunnerAndConfigurationSettings promptForSnapshotConfiguration(final Project project, |
| final List<RunnerAndConfigurationSettings> configurations) { |
| if (configurations.isEmpty()) { |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.no.configuration.error"), |
| UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon()); |
| return null; |
| } |
| |
| for(int i=configurations.size()-1; i >= 0; i--) { |
| final JreVersionDetector detector = new JreVersionDetector(); |
| final ApplicationConfiguration configuration = (ApplicationConfiguration)configurations.get(i).getConfiguration(); |
| if (!detector.isJre50Configured(configuration) && !detector.isModuleJre50Configured(configuration)) { |
| configurations.remove(i); |
| } |
| } |
| |
| if (configurations.isEmpty()) { |
| Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.no.compatible.configuration.error"), |
| UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon()); |
| return null; |
| } |
| |
| final RunnerAndConfigurationSettings snapshotConfiguration; |
| if (configurations.size() == 1) { |
| final int rc = Messages.showYesNoDialog( |
| project, |
| UIDesignerBundle.message("snapshot.confirm.configuration.prompt", configurations.get(0).getConfiguration().getName()), |
| UIDesignerBundle.message("snapshot.title"), |
| Messages.getQuestionIcon()); |
| if (rc == Messages.NO) { |
| return null; |
| } |
| snapshotConfiguration = configurations.get(0); |
| } |
| else { |
| String[] names = new String[configurations.size()]; |
| for(int i=0; i<configurations.size(); i++) { |
| names [i] = configurations.get(i).getConfiguration().getName(); |
| } |
| int rc = Messages.showChooseDialog( |
| project, |
| UIDesignerBundle.message("snapshot.choose.configuration.prompt"), |
| UIDesignerBundle.message("snapshot.title"), |
| Messages.getQuestionIcon(), |
| names, |
| names [0] |
| ); |
| if (rc < 0) return null; |
| snapshotConfiguration = configurations.get(rc); |
| } |
| ((ApplicationConfiguration) snapshotConfiguration.getConfiguration()).ENABLE_SWING_INSPECTOR = true; |
| return snapshotConfiguration; |
| } |
| |
| private static class MyDialog extends DialogWrapper { |
| private JPanel myRootPanel; |
| private JTree myComponentTree; |
| private JTextField myFormNameTextField; |
| private JLabel myErrorLabel; |
| private final Project myProject; |
| private final SnapShotClient myClient; |
| private final PsiDirectory myDirectory; |
| @NonNls private static final String SWING_PACKAGE = "javax.swing."; |
| |
| private MyDialog(Project project, final SnapShotClient client, final PsiDirectory dir) { |
| super(project, true); |
| myProject = project; |
| myClient = client; |
| myDirectory = dir; |
| init(); |
| setTitle(UIDesignerBundle.message("snapshot.title")); |
| final SnapShotTreeModel model = new SnapShotTreeModel(client); |
| myComponentTree.setModel(model); |
| myComponentTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); |
| myComponentTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { |
| public void valueChanged(TreeSelectionEvent e) { |
| updateOKAction(); |
| } |
| }); |
| for(int i=0; i<2; i++) { |
| for(int row=myComponentTree.getRowCount()-1; row >= 0; row--) { |
| myComponentTree.expandRow(row); |
| } |
| } |
| myComponentTree.getSelectionModel().setSelectionPath(myComponentTree.getPathForRow(0)); |
| myFormNameTextField.setText(suggestFormName()); |
| |
| final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme(); |
| final TextAttributes attributes = globalScheme.getAttributes(JavaHighlightingColors.STRING); |
| final SimpleTextAttributes titleAttributes = |
| new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, attributes.getForegroundColor()); |
| |
| myComponentTree.setCellRenderer(new ColoredTreeCellRenderer() { |
| public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { |
| SnapShotRemoteComponent rc = (SnapShotRemoteComponent) value; |
| |
| String className = rc.getClassName(); |
| if (className.startsWith(SWING_PACKAGE)) { |
| append(className.substring(SWING_PACKAGE.length()), SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| } |
| else { |
| append(className, SimpleTextAttributes.REGULAR_ATTRIBUTES); |
| } |
| |
| if (rc.getText().length() > 0) { |
| append(" \"" + rc.getText() + "\"", titleAttributes); |
| } |
| if (rc.getLayoutManager().length() > 0) { |
| append(" (" + rc.getLayoutManager() + ")", SimpleTextAttributes.GRAY_ATTRIBUTES); |
| } |
| |
| if (rc.isTopLevel()) { |
| setIcon(AllIcons.FileTypes.UiForm); |
| } |
| else { |
| final Palette palette = Palette.getInstance(myProject); |
| final ComponentItem item = palette.getItem(rc.getClassName()); |
| if (item != null) { |
| setIcon(item.getSmallIcon()); |
| } |
| else { |
| setIcon(UIDesignerIcons.Unknown); |
| } |
| } |
| } |
| }); |
| myFormNameTextField.getDocument().addDocumentListener(new DocumentAdapter() { |
| protected void textChanged(DocumentEvent e) { |
| updateOKAction(); |
| } |
| }); |
| updateOKAction(); |
| } |
| |
| @NonNls |
| private String suggestFormName() { |
| int count = 0; |
| do { |
| count++; |
| } |
| while(myDirectory.findFile("Form" + count + GuiFormFileType.DOT_DEFAULT_EXTENSION) != null); |
| return "Form" + count; |
| } |
| |
| private void updateOKAction() { |
| final boolean selectedComponentValid = isSelectedComponentValid(); |
| setOKActionEnabled(isFormNameValid() && selectedComponentValid); |
| if (myComponentTree.getSelectionPath() != null && !selectedComponentValid) { |
| myErrorLabel.setText(UIDesignerBundle.message("snapshooter.invalid.container")); |
| } |
| else { |
| myErrorLabel.setText(" "); |
| } |
| } |
| |
| private boolean isSelectedComponentValid() { |
| final TreePath selectionPath = myComponentTree.getSelectionPath(); |
| if (selectionPath == null) return false; |
| SnapShotRemoteComponent rc = (SnapShotRemoteComponent) selectionPath.getLastPathComponent(); |
| if (isValidComponent(rc)) return true; |
| if (selectionPath.getPathCount() == 2) { |
| // capture frame/dialog root pane when a frame or dialog itself is selected |
| final SnapShotRemoteComponent[] children = rc.getChildren(); |
| return children != null && children.length > 0 && isValidComponent(children[0]); |
| } |
| return false; |
| } |
| |
| private boolean isValidComponent(final SnapShotRemoteComponent rc) { |
| PsiClass componentClass = |
| JavaPsiFacade.getInstance(myProject).findClass(rc.getClassName().replace('$', '.'), GlobalSearchScope.allScope(myProject)); |
| while(componentClass != null) { |
| if (JPanel.class.getName().equals(componentClass.getQualifiedName()) || |
| JTabbedPane.class.getName().equals(componentClass.getQualifiedName()) || |
| JScrollPane.class.getName().equals(componentClass.getQualifiedName()) || |
| JSplitPane.class.getName().equals(componentClass.getQualifiedName())) { |
| return true; |
| } |
| componentClass = componentClass.getSuperClass(); |
| } |
| |
| return false; |
| } |
| |
| private boolean isFormNameValid() { |
| return myFormNameTextField.getText().length() > 0; |
| } |
| |
| @Override @NonNls |
| protected String getDimensionServiceKey() { |
| return "CreateSnapShotAction.MyDialog"; |
| } |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| return myFormNameTextField; |
| } |
| |
| @NotNull |
| @Override |
| protected Action getOKAction() { |
| final Action okAction = super.getOKAction(); |
| okAction.putValue(Action.NAME, UIDesignerBundle.message("create.snapshot.button")); |
| return okAction; |
| } |
| |
| @Override |
| protected void doOKAction() { |
| if (getOKAction().isEnabled()) { |
| try { |
| myDirectory.checkCreateFile(getFormName() + GuiFormFileType.DOT_DEFAULT_EXTENSION); |
| } |
| catch (IncorrectOperationException e) { |
| JOptionPane.showMessageDialog(myRootPanel, UIDesignerBundle.message("error.form.already.exists", getFormName())); |
| return; |
| } |
| if (!checkUnknownLayoutManagers(myDirectory.getProject())) return; |
| close(OK_EXIT_CODE); |
| } |
| } |
| |
| private boolean checkUnknownLayoutManagers(final Project project) { |
| final Set<String> layoutManagerClasses = new TreeSet<String>(); |
| final SnapShotRemoteComponent rc = (SnapShotRemoteComponent) myComponentTree.getSelectionPath().getLastPathComponent(); |
| assert rc != null; |
| final Ref<Exception> err = new Ref<Exception>(); |
| Runnable runnable = new Runnable() { |
| public void run() { |
| try { |
| collectUnknownLayoutManagerClasses(project, rc, layoutManagerClasses); |
| } |
| catch (IOException e) { |
| err.set(e); |
| } |
| } |
| }; |
| if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, |
| UIDesignerBundle.message("progress.validating.layout.managers"), |
| false, project)) { |
| return false; |
| } |
| if (!err.isNull()) { |
| Messages.showErrorDialog(myRootPanel, UIDesignerBundle.message("snapshot.connection.broken"), UIDesignerBundle.message("snapshot.title")); |
| return false; |
| } |
| if (!layoutManagerClasses.isEmpty()) { |
| StringBuilder builder = new StringBuilder(UIDesignerBundle.message("snapshot.unknown.layout.prefix")); |
| for(String layoutManagerClass: layoutManagerClasses) { |
| builder.append(layoutManagerClass).append("\n"); |
| } |
| builder.append(UIDesignerBundle.message("snapshot.unknown.layout.prompt")); |
| return Messages.showYesNoDialog(myProject, builder.toString(), |
| UIDesignerBundle.message("snapshot.title"), Messages.getQuestionIcon()) == Messages.YES; |
| } |
| return true; |
| } |
| |
| private void collectUnknownLayoutManagerClasses(final Project project, final SnapShotRemoteComponent rc, |
| final Set<String> layoutManagerClasses) throws IOException { |
| RadComponentFactory factory = InsertComponentProcessor.getRadComponentFactory(project, rc.getClassName()); |
| if (factory instanceof RadContainer.Factory && rc.getLayoutManager().length() > 0 && |
| !LayoutManagerRegistry.isKnownLayoutClass(rc.getLayoutManager())) { |
| layoutManagerClasses.add(rc.getLayoutManager()); |
| } |
| |
| SnapShotRemoteComponent[] children = rc.getChildren(); |
| if (children == null) { |
| children = myClient.listChildren(rc.getId()); |
| rc.setChildren(children); |
| } |
| for(SnapShotRemoteComponent child: children) { |
| collectUnknownLayoutManagerClasses(project, child, layoutManagerClasses); |
| } |
| } |
| |
| @Nullable |
| protected JComponent createCenterPanel() { |
| return myRootPanel; |
| } |
| |
| public int getSelectedComponentId() { |
| final TreePath selectionPath = myComponentTree.getSelectionPath(); |
| SnapShotRemoteComponent rc = (SnapShotRemoteComponent) selectionPath.getLastPathComponent(); |
| if (!isValidComponent(rc) && selectionPath.getPathCount() == 2) { |
| // capture frame/dialog root pane when a frame or dialog itself is selected |
| final SnapShotRemoteComponent[] children = rc.getChildren(); |
| if (children != null && children.length > 0 && isValidComponent(children [0])) { |
| return children [0].getId(); |
| } |
| } |
| return rc.getId(); |
| } |
| |
| public String getFormName() { |
| return myFormNameTextField.getText(); |
| } |
| } |
| } |