ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / plugins / ui-designer / src / com / intellij / uiDesigner / actions / PreviewFormAction.java
blob14f6071efa4a9675db823a219b390e4942ff0bc5
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.
16 package com.intellij.uiDesigner.actions;
18 import com.intellij.CommonBundle;
19 import com.intellij.compiler.PsiClassWriter;
20 import com.intellij.compiler.impl.FileSetCompileScope;
21 import com.intellij.execution.*;
22 import com.intellij.execution.configurations.*;
23 import com.intellij.execution.executors.DefaultRunExecutor;
24 import com.intellij.execution.filters.TextConsoleBuilderFactory;
25 import com.intellij.execution.runners.ExecutionEnvironment;
26 import com.intellij.execution.runners.ProgramRunner;
27 import com.intellij.lang.properties.PropertiesFileType;
28 import com.intellij.lang.properties.PropertiesReferenceManager;
29 import com.intellij.lang.properties.psi.PropertiesFile;
30 import com.intellij.openapi.actionSystem.AnAction;
31 import com.intellij.openapi.actionSystem.AnActionEvent;
32 import com.intellij.openapi.actionSystem.DataContext;
33 import com.intellij.openapi.actionSystem.PlatformDataKeys;
34 import com.intellij.openapi.application.PathManager;
35 import com.intellij.openapi.compiler.CompileContext;
36 import com.intellij.openapi.compiler.CompileStatusNotification;
37 import com.intellij.openapi.compiler.CompilerManager;
38 import com.intellij.openapi.diagnostic.Logger;
39 import com.intellij.openapi.editor.Document;
40 import com.intellij.openapi.fileEditor.FileDocumentManager;
41 import com.intellij.openapi.fileTypes.FileTypeManager;
42 import com.intellij.openapi.fileTypes.StdFileTypes;
43 import com.intellij.openapi.module.Module;
44 import com.intellij.openapi.module.ModuleUtil;
45 import com.intellij.openapi.project.Project;
46 import com.intellij.openapi.roots.ProjectClasspathTraversing;
47 import com.intellij.openapi.roots.ProjectRootsTraversing;
48 import com.intellij.openapi.ui.Messages;
49 import com.intellij.openapi.util.io.FileUtil;
50 import com.intellij.openapi.vfs.VirtualFile;
51 import com.intellij.openapi.wm.WindowManager;
52 import com.intellij.uiDesigner.FormEditingUtil;
53 import com.intellij.uiDesigner.UIDesignerBundle;
54 import com.intellij.uiDesigner.compiler.AsmCodeGenerator;
55 import com.intellij.uiDesigner.compiler.FormErrorInfo;
56 import com.intellij.uiDesigner.compiler.Utils;
57 import com.intellij.uiDesigner.designSurface.GuiEditor;
58 import com.intellij.uiDesigner.lw.*;
59 import com.intellij.uiDesigner.make.CopyResourcesUtil;
60 import com.intellij.uiDesigner.make.Form2ByteCodeCompiler;
61 import com.intellij.uiDesigner.make.PreviewNestedFormLoader;
62 import com.intellij.util.PathsList;
63 import com.intellij.util.containers.HashSet;
64 import org.jetbrains.annotations.NonNls;
65 import org.jetbrains.annotations.NotNull;
66 import org.jetbrains.annotations.Nullable;
68 import javax.swing.*;
69 import java.io.File;
70 import java.io.IOException;
71 import java.util.List;
72 import java.util.Locale;
74 /**
75 * @author Anton Katilin
76 * @author Vladimir Kondratyev
78 public final class PreviewFormAction extends AnAction{
79 private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.actions.PreviewFormAction");
81 /**
82 * The problem is that this class is in a default package so it's not
83 * import this class to refer
85 private static final String CLASS_TO_BIND_NAME = "FormPreviewFrame";
86 @NonNls private static final String RUNTIME_BUNDLE_PREFIX = "RuntimeBundle";
87 @NonNls public static final String PREVIEW_BINDING_FIELD = "myComponent";
89 public void actionPerformed(final AnActionEvent e) {
90 final GuiEditor editor = FormEditingUtil.getActiveEditor(e.getDataContext());
91 if (editor != null) {
92 showPreviewFrame(editor.getModule(), editor.getFile(), e.getDataContext(), editor.getStringDescriptorLocale());
96 public void update(final AnActionEvent e) {
97 final GuiEditor editor = FormEditingUtil.getActiveEditor(e.getDataContext());
99 if(editor == null){
100 e.getPresentation().setVisible(false);
101 return;
104 final VirtualFile file = editor.getFile();
105 e.getPresentation().setVisible(
106 FileDocumentManager.getInstance().getDocument(file) != null &&
107 FileTypeManager.getInstance().getFileTypeByFile(file) == StdFileTypes.GUI_DESIGNER_FORM
111 private static void showPreviewFrame(@NotNull final Module module, @NotNull final VirtualFile formFile,
112 final DataContext dataContext, @Nullable final Locale stringDescriptorLocale) {
113 final String tempPath;
114 try {
115 final File tempDirectory = FileUtil.createTempDirectory("FormPreview", "");
116 tempPath = tempDirectory.getAbsolutePath();
118 CopyResourcesUtil.copyFormsRuntime(tempPath, true);
120 catch (IOException e) {
121 Messages.showErrorDialog(
122 module.getProject(),
123 UIDesignerBundle.message("error.cannot.preview.form", formFile.getPath().replace('/', File.separatorChar), e.toString()),
124 CommonBundle.getErrorTitle()
126 return;
129 final PathsList sources = ProjectRootsTraversing.collectRoots(module, ProjectRootsTraversing.PROJECT_SOURCES);
130 final String classPath =
131 ProjectRootsTraversing.collectRoots(module, ProjectClasspathTraversing.FULL_CLASSPATH_RECURSIVE).getPathsString() + File.pathSeparator +
132 sources.getPathsString() + File.pathSeparator + /* resources bundles */
133 tempPath;
134 final ClassLoader loader = Form2ByteCodeCompiler.createClassLoader(classPath);
136 final Document doc = FileDocumentManager.getInstance().getDocument(formFile);
137 final LwRootContainer rootContainer;
138 try {
139 rootContainer = Utils.getRootContainer(doc.getText(), new CompiledClassPropertiesProvider(loader));
141 catch (Exception e) {
142 Messages.showErrorDialog(
143 module.getProject(),
144 UIDesignerBundle.message("error.cannot.read.form", formFile.getPath().replace('/', File.separatorChar), e.getMessage()),
145 CommonBundle.getErrorTitle()
147 return;
150 if (rootContainer.getComponentCount() == 0) {
151 Messages.showErrorDialog(
152 module.getProject(),
153 UIDesignerBundle.message("error.cannot.preview.empty.form", formFile.getPath().replace('/', File.separatorChar)),
154 CommonBundle.getErrorTitle()
156 return;
159 setPreviewBindings(rootContainer, CLASS_TO_BIND_NAME);
161 // 2. Copy previewer class and all its superclasses into TEMP directory and instrument it.
162 try {
163 PreviewNestedFormLoader nestedFormLoader = new PreviewNestedFormLoader(module, tempPath, loader);
165 final File tempFile = CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME, true);
166 //CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$1", true);
167 CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MyExitAction", true);
168 CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MyPackAction", true);
169 CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MySetLafAction", true);
171 Locale locale = Locale.getDefault();
172 if (locale.getCountry().length() > 0 && locale.getLanguage().length() > 0) {
173 CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() +
174 "_" + locale.getCountry() + PropertiesFileType.DOT_DEFAULT_EXTENSION);
176 if (locale.getLanguage().length() > 0) {
177 CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + PropertiesFileType.DOT_DEFAULT_EXTENSION);
179 CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + PropertiesFileType.DOT_DEFAULT_EXTENSION);
180 CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + PropertiesFileType.DOT_DEFAULT_EXTENSION);
182 final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, loader, nestedFormLoader, true,
183 new PsiClassWriter(module));
184 codeGenerator.patchFile(tempFile);
185 final FormErrorInfo[] errors = codeGenerator.getErrors();
186 if(errors.length != 0){
187 Messages.showErrorDialog(
188 module.getProject(),
189 UIDesignerBundle.message("error.cannot.preview.form",
190 formFile.getPath().replace('/', File.separatorChar),
191 errors[0].getErrorMessage()),
192 CommonBundle.getErrorTitle()
194 return;
197 catch (Exception e) {
198 LOG.debug(e);
199 Messages.showErrorDialog(
200 module.getProject(),
201 UIDesignerBundle.message("error.cannot.preview.form", formFile.getPath().replace('/', File.separatorChar),
202 e.getMessage() != null ? e.getMessage() : e.toString()),
203 CommonBundle.getErrorTitle()
205 return;
208 // 2.5. Copy up-to-date properties files to the output directory.
209 final HashSet<String> bundleSet = new HashSet<String>();
210 FormEditingUtil.iterateStringDescriptors(
211 rootContainer,
212 new FormEditingUtil.StringDescriptorVisitor<IComponent>() {
213 public boolean visit(final IComponent component, final StringDescriptor descriptor) {
214 if (descriptor.getBundleName() != null) {
215 bundleSet.add(descriptor.getDottedBundleName());
217 return true;
221 if (bundleSet.size() > 0) {
222 HashSet<VirtualFile> virtualFiles = new HashSet<VirtualFile>();
223 HashSet<Module> modules = new HashSet<Module>();
224 PropertiesReferenceManager manager = PropertiesReferenceManager.getInstance(module.getProject());
225 for(String bundleName: bundleSet) {
226 for(PropertiesFile propFile: manager.findPropertiesFiles(module, bundleName)) {
227 virtualFiles.add(propFile.getVirtualFile());
228 modules.add(ModuleUtil.findModuleForFile(propFile.getVirtualFile(), module.getProject()));
231 FileSetCompileScope scope = new FileSetCompileScope(virtualFiles, modules.toArray(new Module[]{}));
233 CompilerManager.getInstance(module.getProject()).make(scope, new CompileStatusNotification() {
234 public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) {
235 if (!aborted && errors == 0) {
236 runPreviewProcess(tempPath, sources, module, formFile, dataContext, stringDescriptorLocale);
241 else {
242 runPreviewProcess(tempPath, sources, module, formFile, dataContext, stringDescriptorLocale);
246 public static void setPreviewBindings(final LwRootContainer rootContainer, final String classToBindName) {
247 // 1. Prepare form to preview. We have to change container so that it has only one binding.
248 rootContainer.setClassToBind(classToBindName);
249 FormEditingUtil.iterate(
250 rootContainer,
251 new FormEditingUtil.ComponentVisitor<LwComponent>() {
252 public boolean visit(final LwComponent iComponent) {
253 iComponent.setBinding(null);
254 return true;
258 if (rootContainer.getComponentCount() == 1) {
259 //noinspection HardCodedStringLiteral
260 ((LwComponent)rootContainer.getComponent(0)).setBinding(PREVIEW_BINDING_FIELD);
264 private static void runPreviewProcess(final String tempPath, final PathsList sources, final Module module, final VirtualFile formFile,
265 final DataContext dataContext, @Nullable final Locale stringDescriptorLocale) {
266 // 3. Now we are ready to launch Java process
267 final JavaParameters parameters = new JavaParameters();
268 parameters.getClassPath().add(tempPath);
269 parameters.getClassPath().add(PathManager.findFileInLibDirectory("jgoodies-forms.jar").getAbsolutePath());
270 final List<String> paths = sources.getPathList();
271 for (final String path : paths) {
272 parameters.getClassPath().add(path);
274 try {
275 parameters.configureByModule(module, JavaParameters.JDK_AND_CLASSES);
277 catch (CantRunException e) {
278 Messages.showErrorDialog(
279 module.getProject(),
280 UIDesignerBundle.message("error.cannot.preview.form", formFile.getPath().replace('/', File.separatorChar), e.getMessage()),
281 CommonBundle.getErrorTitle()
283 return;
285 parameters.setMainClass("FormPreviewFrame");
286 parameters.setWorkingDirectory(tempPath);
287 if (stringDescriptorLocale != null && stringDescriptorLocale.getDisplayName().length() > 0) {
288 parameters.getVMParametersList().add("-Duser.language=" + stringDescriptorLocale.getLanguage());
291 try {
292 final RunProfile profile = new MyRunProfile(module, parameters, UIDesignerBundle.message("progress.preview.started", formFile.getPresentableUrl()));
293 ProgramRunner defaultRunner = RunnerRegistry.getInstance().getRunner(DefaultRunExecutor.EXECUTOR_ID, profile);
294 LOG.assertTrue(defaultRunner != null);
295 defaultRunner.execute(DefaultRunExecutor.getRunExecutorInstance(), new ExecutionEnvironment(profile, new DataContext() { // IDEADEV-3596
296 public Object getData(String dataId) {
297 if (PlatformDataKeys.PROJECT.is(dataId)) {
298 return module.getProject();
300 return dataContext.getData(dataId);
302 }));
304 catch (ExecutionException e) {
305 Messages.showErrorDialog(
306 module.getProject(),
307 UIDesignerBundle.message("error.cannot.preview.form", formFile.getPath().replace('/', File.separatorChar), e.getMessage()),
308 CommonBundle.getErrorTitle()
313 private static final class MyRunProfile implements ModuleRunProfile {
314 private final Module myModule;
315 private final JavaParameters myParams;
316 private final String myStatusbarMessage;
318 public MyRunProfile(final Module module, final JavaParameters params, final String statusbarMessage) {
319 myModule = module;
320 myParams = params;
321 myStatusbarMessage = statusbarMessage;
324 public Icon getIcon() {
325 return null;
328 public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
329 final JavaCommandLineState state = new JavaCommandLineState(env) {
330 protected JavaParameters createJavaParameters() {
331 return myParams;
334 public ExecutionResult execute(@NotNull final Executor executor, @NotNull final ProgramRunner runner) throws ExecutionException {
335 try {
336 return super.execute(executor, runner);
338 finally {
339 final Project project = myModule.getProject();
340 SwingUtilities.invokeLater(new Runnable() {
341 public void run() {
342 WindowManager.getInstance().getStatusBar(project).setInfo(myStatusbarMessage);
348 state.setConsoleBuilder(TextConsoleBuilderFactory.getInstance().createBuilder(myModule.getProject()));
349 return state;
352 public String getName() {
353 return UIDesignerBundle.message("title.form.preview");
356 public void checkConfiguration() throws RuntimeConfigurationException {
359 @NotNull
360 public Module[] getModules() {
361 return new Module[] {myModule};