update copyright
[fedora-idea.git] / plugins / ui-designer / src / com / intellij / uiDesigner / snapShooter / CreateSnapShotAction.java
blobc0d8760bfaf8287db7ec226c914d0c8f9d624234
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.uiDesigner.snapShooter;
19 import com.intellij.execution.ExecutionException;
20 import com.intellij.execution.RunManagerEx;
21 import com.intellij.execution.RunnerRegistry;
22 import com.intellij.execution.application.ApplicationConfiguration;
23 import com.intellij.execution.application.ApplicationConfigurationType;
24 import com.intellij.execution.executors.DefaultRunExecutor;
25 import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl;
26 import com.intellij.execution.runners.ExecutionEnvironment;
27 import com.intellij.execution.runners.ProgramRunner;
28 import com.intellij.execution.util.JreVersionDetector;
29 import com.intellij.ide.IdeView;
30 import com.intellij.uiDesigner.GuiFormFileType;
31 import com.intellij.openapi.actionSystem.AnAction;
32 import com.intellij.openapi.actionSystem.AnActionEvent;
33 import com.intellij.openapi.actionSystem.LangDataKeys;
34 import com.intellij.openapi.actionSystem.PlatformDataKeys;
35 import com.intellij.openapi.application.ApplicationManager;
36 import com.intellij.openapi.command.CommandProcessor;
37 import com.intellij.openapi.diagnostic.Logger;
38 import com.intellij.openapi.editor.SyntaxHighlighterColors;
39 import com.intellij.openapi.editor.colors.EditorColorsManager;
40 import com.intellij.openapi.editor.colors.EditorColorsScheme;
41 import com.intellij.openapi.editor.markup.TextAttributes;
42 import com.intellij.openapi.progress.ProgressManager;
43 import com.intellij.openapi.project.Project;
44 import com.intellij.openapi.roots.ProjectFileIndex;
45 import com.intellij.openapi.roots.ProjectRootManager;
46 import com.intellij.openapi.ui.DialogWrapper;
47 import com.intellij.openapi.ui.Messages;
48 import com.intellij.openapi.util.IconLoader;
49 import com.intellij.openapi.util.Ref;
50 import com.intellij.openapi.vfs.CharsetToolkit;
51 import com.intellij.psi.*;
52 import com.intellij.psi.search.GlobalSearchScope;
53 import com.intellij.ui.ColoredTreeCellRenderer;
54 import com.intellij.ui.DocumentAdapter;
55 import com.intellij.ui.SimpleTextAttributes;
56 import com.intellij.uiDesigner.UIDesignerBundle;
57 import com.intellij.uiDesigner.designSurface.InsertComponentProcessor;
58 import com.intellij.uiDesigner.palette.ComponentItem;
59 import com.intellij.uiDesigner.palette.Palette;
60 import com.intellij.uiDesigner.radComponents.LayoutManagerRegistry;
61 import com.intellij.uiDesigner.radComponents.RadComponentFactory;
62 import com.intellij.uiDesigner.radComponents.RadContainer;
63 import com.intellij.util.IncorrectOperationException;
64 import org.jetbrains.annotations.NonNls;
65 import org.jetbrains.annotations.Nullable;
67 import javax.swing.*;
68 import javax.swing.event.DocumentEvent;
69 import javax.swing.event.TreeSelectionEvent;
70 import javax.swing.event.TreeSelectionListener;
71 import javax.swing.tree.TreePath;
72 import javax.swing.tree.TreeSelectionModel;
73 import java.io.IOException;
74 import java.util.ArrayList;
75 import java.util.List;
76 import java.util.Set;
77 import java.util.TreeSet;
79 /**
80 * @author yole
82 public class CreateSnapShotAction extends AnAction {
83 private static final Logger LOG = Logger.getInstance("com.intellij.uiDesigner.snapShooter.CreateSnapShotAction");
85 @Override
86 public void update(AnActionEvent e) {
87 final Project project = e.getData(PlatformDataKeys.PROJECT);
88 final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
89 e.getPresentation().setVisible(project != null && view != null && hasDirectoryInPackage(project, view));
92 private static boolean hasDirectoryInPackage(final Project project, final IdeView view) {
93 ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
94 PsiDirectory[] dirs = view.getDirectories();
95 for (PsiDirectory dir : dirs) {
96 if (projectFileIndex.isInSourceContent(dir.getVirtualFile()) && JavaDirectoryService.getInstance().getPackage(dir) != null) {
97 return true;
100 return false;
103 public void actionPerformed(AnActionEvent e) {
104 final Project project = e.getData(PlatformDataKeys.PROJECT);
105 final IdeView view = e.getData(LangDataKeys.IDE_VIEW);
106 if (project == null || view == null) {
107 return;
110 final PsiDirectory dir = view.getOrChooseDirectory();
111 if (dir == null) return;
113 final SnapShotClient client = new SnapShotClient();
114 List<RunnerAndConfigurationSettingsImpl> appConfigurations = new ArrayList<RunnerAndConfigurationSettingsImpl>();
115 RunnerAndConfigurationSettingsImpl snapshotConfiguration = null;
116 boolean connected = false;
118 ApplicationConfigurationType cfgType = ApplicationConfigurationType.getInstance();
119 RunnerAndConfigurationSettingsImpl[] racsi = RunManagerEx.getInstanceEx(project).getConfigurationSettings(cfgType);
121 for(RunnerAndConfigurationSettingsImpl config: racsi) {
122 if (config.getConfiguration() instanceof ApplicationConfiguration) {
123 ApplicationConfiguration appConfig = (ApplicationConfiguration) config.getConfiguration();
124 appConfigurations.add(config);
125 if (appConfig.ENABLE_SWING_INSPECTOR) {
126 SnapShooterConfigurationSettings settings = SnapShooterConfigurationSettings.get(appConfig);
127 snapshotConfiguration = config;
128 if (settings.getLastPort() > 0) {
129 try {
130 client.connect(settings.getLastPort());
131 connected = true;
133 catch(IOException ex) {
134 connected = false;
138 if (connected) break;
142 if (snapshotConfiguration == null) {
143 snapshotConfiguration = promptForSnapshotConfiguration(project, appConfigurations);
144 if (snapshotConfiguration == null) return;
147 if (!connected) {
148 int rc = Messages.showYesNoDialog(project, UIDesignerBundle.message("snapshot.run.prompt"),
149 UIDesignerBundle.message("snapshot.title"), Messages.getQuestionIcon());
150 if (rc == 1) return;
151 final ApplicationConfiguration appConfig = (ApplicationConfiguration) snapshotConfiguration.getConfiguration();
152 final SnapShooterConfigurationSettings settings = SnapShooterConfigurationSettings.get(appConfig);
153 settings.setNotifyRunnable(new Runnable() {
154 public void run() {
155 SwingUtilities.invokeLater(new Runnable() {
156 public void run() {
157 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.prepare.notice"),
158 UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon());
159 try {
160 client.connect(settings.getLastPort());
162 catch(IOException ex) {
163 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.connection.error"),
164 UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon());
165 return;
167 runSnapShooterSession(client, project, dir, view);
173 try {
174 final ProgramRunner runner = RunnerRegistry.getInstance().getRunner(DefaultRunExecutor.EXECUTOR_ID, appConfig);
175 LOG.assertTrue(runner != null, "Runner MUST not be null!");
176 runner.execute(DefaultRunExecutor.getRunExecutorInstance(),
177 new ExecutionEnvironment(runner, snapshotConfiguration, e.getDataContext()));
179 catch (ExecutionException ex) {
180 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.run.error", ex.getMessage()),
181 UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon());
184 else {
185 runSnapShooterSession(client, project, dir, view);
189 private static void runSnapShooterSession(final SnapShotClient client, final Project project, final PsiDirectory dir, final IdeView view) {
190 try {
191 client.suspendSwing();
193 catch (IOException e1) {
194 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.connection.error"),
195 UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon());
196 return;
199 final MyDialog dlg = new MyDialog(project, client, dir);
200 dlg.show();
201 if (dlg.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
202 final int id = dlg.getSelectedComponentId();
203 final Ref<Object> result = new Ref<Object>();
204 ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
205 public void run() {
206 try {
207 result.set(client.createSnapshot(id));
209 catch (Exception ex) {
210 result.set(ex);
213 }, UIDesignerBundle.message("progress.creating.snapshot"), false, project);
215 String snapshot = null;
216 if (result.get() instanceof String) {
217 snapshot = (String) result.get();
219 else {
220 Exception ex = (Exception) result.get();
221 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.create.error", ex.getMessage()),
222 UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon());
225 if (snapshot != null) {
226 final String snapshot1 = snapshot;
227 ApplicationManager.getApplication().runWriteAction(new Runnable() {
228 public void run() {
229 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
230 public void run() {
231 try {
232 PsiFile formFile = PsiFileFactory.getInstance(dir.getProject())
233 .createFileFromText(dlg.getFormName() + GuiFormFileType.DOT_DEFAULT_EXTENSION, snapshot1);
234 formFile = (PsiFile)dir.add(formFile);
235 formFile.getVirtualFile().setCharset(CharsetToolkit.UTF8_CHARSET);
236 formFile.getViewProvider().getDocument().setText(snapshot1);
237 view.selectElement(formFile);
239 catch (IncorrectOperationException ex) {
240 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.save.error", ex.getMessage()),
241 UIDesignerBundle.message("snapshot.title"), Messages.getErrorIcon());
244 }, "", null);
250 try {
251 client.resumeSwing();
253 catch (IOException ex) {
254 Messages.showErrorDialog(project, UIDesignerBundle.message("snapshot.connection.broken"),
255 UIDesignerBundle.message("snapshot.title"));
258 client.dispose();
261 @Nullable
262 private static RunnerAndConfigurationSettingsImpl promptForSnapshotConfiguration(final Project project,
263 final List<RunnerAndConfigurationSettingsImpl> configurations) {
264 if (configurations.isEmpty()) {
265 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.no.configuration.error"),
266 UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon());
267 return null;
270 for(int i=configurations.size()-1; i >= 0; i--) {
271 if (!new JreVersionDetector().isJre50Configured((ApplicationConfiguration) configurations.get(i).getConfiguration())) {
272 configurations.remove(i);
276 if (configurations.isEmpty()) {
277 Messages.showMessageDialog(project, UIDesignerBundle.message("snapshot.no.compatible.configuration.error"),
278 UIDesignerBundle.message("snapshot.title"), Messages.getInformationIcon());
279 return null;
282 final RunnerAndConfigurationSettingsImpl snapshotConfiguration;
283 if (configurations.size() == 1) {
284 final int rc = Messages.showYesNoDialog(
285 project,
286 UIDesignerBundle.message("snapshot.confirm.configuration.prompt", configurations.get(0).getConfiguration().getName()),
287 UIDesignerBundle.message("snapshot.title"),
288 Messages.getQuestionIcon());
289 if (rc == 1) {
290 return null;
292 snapshotConfiguration = configurations.get(0);
294 else {
295 String[] names = new String[configurations.size()];
296 for(int i=0; i<configurations.size(); i++) {
297 names [i] = configurations.get(i).getConfiguration().getName();
299 int rc = Messages.showChooseDialog(
300 project,
301 UIDesignerBundle.message("snapshot.choose.configuration.prompt"),
302 UIDesignerBundle.message("snapshot.title"),
303 Messages.getQuestionIcon(),
304 names,
305 names [0]
307 if (rc < 0) return null;
308 snapshotConfiguration = configurations.get(rc);
310 ((ApplicationConfiguration) snapshotConfiguration.getConfiguration()).ENABLE_SWING_INSPECTOR = true;
311 return snapshotConfiguration;
314 private static class MyDialog extends DialogWrapper {
315 private JPanel myRootPanel;
316 private JTree myComponentTree;
317 private JTextField myFormNameTextField;
318 private JLabel myErrorLabel;
319 private final Project myProject;
320 private final SnapShotClient myClient;
321 private final PsiDirectory myDirectory;
322 @NonNls private static final String SWING_PACKAGE = "javax.swing.";
324 private final Icon myUnknownComponentIcon = IconLoader.getIcon("/com/intellij/uiDesigner/icons/unknown.png");
325 private final Icon myFormIcon = IconLoader.getIcon("/fileTypes/uiForm.png");
327 private MyDialog(Project project, final SnapShotClient client, final PsiDirectory dir) {
328 super(project, true);
329 myProject = project;
330 myClient = client;
331 myDirectory = dir;
332 init();
333 setTitle(UIDesignerBundle.message("snapshot.title"));
334 final SnapShotTreeModel model = new SnapShotTreeModel(client);
335 myComponentTree.setModel(model);
336 myComponentTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
337 myComponentTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
338 public void valueChanged(TreeSelectionEvent e) {
339 updateOKAction();
342 for(int i=0; i<2; i++) {
343 for(int row=myComponentTree.getRowCount()-1; row >= 0; row--) {
344 myComponentTree.expandRow(row);
347 myComponentTree.getSelectionModel().setSelectionPath(myComponentTree.getPathForRow(0));
348 myFormNameTextField.setText(suggestFormName());
350 final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
351 final TextAttributes attributes = globalScheme.getAttributes(SyntaxHighlighterColors.STRING);
352 final SimpleTextAttributes titleAttributes =
353 new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, attributes.getForegroundColor());
355 myComponentTree.setCellRenderer(new ColoredTreeCellRenderer() {
356 public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
357 SnapShotRemoteComponent rc = (SnapShotRemoteComponent) value;
359 String className = rc.getClassName();
360 if (className.startsWith(SWING_PACKAGE)) {
361 append(className.substring(SWING_PACKAGE.length()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
363 else {
364 append(className, SimpleTextAttributes.REGULAR_ATTRIBUTES);
367 if (rc.getText().length() > 0) {
368 append(" \"" + rc.getText() + "\"", titleAttributes);
370 if (rc.getLayoutManager().length() > 0) {
371 append(" (" + rc.getLayoutManager() + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
374 if (rc.isTopLevel()) {
375 setIcon(myFormIcon);
377 else {
378 final Palette palette = Palette.getInstance(myProject);
379 final ComponentItem item = palette.getItem(rc.getClassName());
380 if (item != null) {
381 setIcon(item.getSmallIcon());
383 else {
384 setIcon(myUnknownComponentIcon);
389 myFormNameTextField.getDocument().addDocumentListener(new DocumentAdapter() {
390 protected void textChanged(DocumentEvent e) {
391 updateOKAction();
394 updateOKAction();
397 @NonNls
398 private String suggestFormName() {
399 int count = 0;
400 do {
401 count++;
403 while(myDirectory.findFile("Form" + count + GuiFormFileType.DOT_DEFAULT_EXTENSION) != null);
404 return "Form" + count;
407 private void updateOKAction() {
408 final boolean selectedComponentValid = isSelectedComponentValid();
409 setOKActionEnabled(isFormNameValid() && selectedComponentValid);
410 if (myComponentTree.getSelectionPath() != null && !selectedComponentValid) {
411 myErrorLabel.setText(UIDesignerBundle.message("snapshooter.invalid.container"));
413 else {
414 myErrorLabel.setText(" ");
418 private boolean isSelectedComponentValid() {
419 final TreePath selectionPath = myComponentTree.getSelectionPath();
420 if (selectionPath == null) return false;
421 SnapShotRemoteComponent rc = (SnapShotRemoteComponent) selectionPath.getLastPathComponent();
422 if (isValidComponent(rc)) return true;
423 if (selectionPath.getPathCount() == 2) {
424 // capture frame/dialog root pane when a frame or dialog itself is selected
425 final SnapShotRemoteComponent[] children = rc.getChildren();
426 return children != null && children.length > 0 && isValidComponent(children[0]);
428 return false;
431 private boolean isValidComponent(final SnapShotRemoteComponent rc) {
432 PsiClass componentClass =
433 JavaPsiFacade.getInstance(myProject).findClass(rc.getClassName().replace('$', '.'), GlobalSearchScope.allScope(myProject));
434 while(componentClass != null) {
435 if (JPanel.class.getName().equals(componentClass.getQualifiedName()) ||
436 JTabbedPane.class.getName().equals(componentClass.getQualifiedName()) ||
437 JScrollPane.class.getName().equals(componentClass.getQualifiedName()) ||
438 JSplitPane.class.getName().equals(componentClass.getQualifiedName())) {
439 return true;
441 componentClass = componentClass.getSuperClass();
444 return false;
447 private boolean isFormNameValid() {
448 return myFormNameTextField.getText().length() > 0;
451 @Override @NonNls
452 protected String getDimensionServiceKey() {
453 return "CreateSnapShotAction.MyDialog";
456 @Override
457 public JComponent getPreferredFocusedComponent() {
458 return myFormNameTextField;
461 @Override
462 protected Action getOKAction() {
463 final Action okAction = super.getOKAction();
464 okAction.putValue(Action.NAME, UIDesignerBundle.message("create.snapshot.button"));
465 return okAction;
468 @Override
469 protected void doOKAction() {
470 if (getOKAction().isEnabled()) {
471 try {
472 myDirectory.checkCreateFile(getFormName() + GuiFormFileType.DOT_DEFAULT_EXTENSION);
474 catch (IncorrectOperationException e) {
475 JOptionPane.showMessageDialog(myRootPanel, UIDesignerBundle.message("error.form.already.exists", getFormName()));
476 return;
478 if (!checkUnknownLayoutManagers(myDirectory.getProject())) return;
479 close(OK_EXIT_CODE);
483 private boolean checkUnknownLayoutManagers(final Project project) {
484 final Set<String> layoutManagerClasses = new TreeSet<String>();
485 final SnapShotRemoteComponent rc = (SnapShotRemoteComponent) myComponentTree.getSelectionPath().getLastPathComponent();
486 assert rc != null;
487 final Ref<Exception> err = new Ref<Exception>();
488 Runnable runnable = new Runnable() {
489 public void run() {
490 try {
491 collectUnknownLayoutManagerClasses(project, rc, layoutManagerClasses);
493 catch (IOException e) {
494 err.set(e);
498 if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable,
499 UIDesignerBundle.message("progress.validating.layout.managers"),
500 false, project)) {
501 return false;
503 if (!err.isNull()) {
504 Messages.showErrorDialog(myRootPanel, UIDesignerBundle.message("snapshot.connection.broken"), UIDesignerBundle.message("snapshot.title"));
505 return false;
507 if (!layoutManagerClasses.isEmpty()) {
508 StringBuilder builder = new StringBuilder(UIDesignerBundle.message("snapshot.unknown.layout.prefix"));
509 for(String layoutManagerClass: layoutManagerClasses) {
510 builder.append(layoutManagerClass).append("\n");
512 builder.append(UIDesignerBundle.message("snapshot.unknown.layout.prompt"));
513 return Messages.showYesNoDialog(myProject, builder.toString(),
514 UIDesignerBundle.message("snapshot.title"), Messages.getQuestionIcon()) == 0;
516 return true;
519 private void collectUnknownLayoutManagerClasses(final Project project, final SnapShotRemoteComponent rc,
520 final Set<String> layoutManagerClasses) throws IOException {
521 RadComponentFactory factory = InsertComponentProcessor.getRadComponentFactory(project, rc.getClassName());
522 if (factory instanceof RadContainer.Factory && rc.getLayoutManager().length() > 0 &&
523 !LayoutManagerRegistry.isKnownLayoutClass(rc.getLayoutManager())) {
524 layoutManagerClasses.add(rc.getLayoutManager());
527 SnapShotRemoteComponent[] children = rc.getChildren();
528 if (children == null) {
529 children = myClient.listChildren(rc.getId());
530 rc.setChildren(children);
532 for(SnapShotRemoteComponent child: children) {
533 collectUnknownLayoutManagerClasses(project, child, layoutManagerClasses);
537 @Nullable
538 protected JComponent createCenterPanel() {
539 return myRootPanel;
542 public int getSelectedComponentId() {
543 final TreePath selectionPath = myComponentTree.getSelectionPath();
544 SnapShotRemoteComponent rc = (SnapShotRemoteComponent) selectionPath.getLastPathComponent();
545 if (!isValidComponent(rc) && selectionPath.getPathCount() == 2) {
546 // capture frame/dialog root pane when a frame or dialog itself is selected
547 final SnapShotRemoteComponent[] children = rc.getChildren();
548 if (children != null && children.length > 0 && isValidComponent(children [0])) {
549 return children [0].getId();
552 return rc.getId();
555 public String getFormName() {
556 return myFormNameTextField.getText();