Restore state: change editor, undo, redo, external psi update

This commit is contained in:
Alexander Lobas
2012-04-03 22:04:34 +04:00
parent da4cf0af05
commit a9d4539e56
11 changed files with 324 additions and 53 deletions
@@ -16,14 +16,12 @@
package com.intellij.android.designer;
import com.intellij.android.designer.designSurface.AndroidDesignerEditorPanel;
import com.intellij.designer.componentTree.TreeComponentDecorator;
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
import com.intellij.designer.DesignerEditor;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.fileEditor.FileEditorStateLevel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.designer.DesignerEditor;
import org.jetbrains.annotations.NotNull;
/**
@@ -35,6 +33,7 @@ public final class AndroidDesignerEditor extends DesignerEditor {
}
@Override
@NotNull
protected DesignerEditorPanel createDesignerPanel(Module module, VirtualFile file) {
return new AndroidDesignerEditorPanel(module, file);
}
@@ -46,29 +45,7 @@ public final class AndroidDesignerEditor extends DesignerEditor {
}
@Override
public boolean isValid() {
return true; // TODO: Auto-generated method stub
}
@NotNull
@Override
public FileEditorState getState(@NotNull FileEditorStateLevel level) {
// TODO: Auto-generated method stub
return new FileEditorState() {
@Override
public boolean canBeMergedWith(FileEditorState otherState, FileEditorStateLevel level) {
return false;
}
};
}
@Override
public void setState(@NotNull FileEditorState state) {
// TODO: Auto-generated method stub
}
@Override
public boolean isModified() {
return false;
public BackgroundEditorHighlighter getBackgroundHighlighter() {
return null; // TODO: Auto-generated method stub
}
}
@@ -154,4 +154,8 @@ public class ProfileAction {
updateActions();
}
}
public int getVersion() {
return myVersion;
}
}
@@ -64,6 +64,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
private final XmlFile myXmlFile;
private final ExternalPSIChangeListener myPSIChangeListener;
private final ProfileAction myProfileAction;
private int myProfileLastVersion;
private volatile RenderSession mySession;
private boolean myParseTime;
@@ -79,6 +80,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
myPSIChangeListener = new ExternalPSIChangeListener(this, myXmlFile, 100, new Runnable() {
@Override
public void run() {
System.out.println("=== Full update ===");
reparseFile();
}
});
@@ -92,10 +94,11 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
myPSIChangeListener.start();
myPSIChangeListener.addRequest();
}
else {
else if (myProfileLastVersion != myProfileAction.getVersion()) {
myPSIChangeListener.addRequest(new Runnable() {
@Override
public void run() {
System.out.println("=== Light update ===");
updateRenderer(true);
}
});
@@ -168,16 +171,13 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
}
private void reparseFile() {
myToolProvider.loadDefaultTool();
mySurfaceArea.deselectAll();
storeState();
parseFile(new Runnable() {
@Override
public void run() {
showDesignerCard();
myLayeredPane.repaint();
DesignerToolWindowManager.getInstance(getProject()).refresh(true);
restoreState();
}
});
}
@@ -292,6 +292,8 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
@Override
public void run() {
try {
myProfileLastVersion = myProfileAction.getVersion();
AndroidPlatform platform = AndroidPlatform.getInstance(myModule);
if (platform == null) {
throw new AndroidSdkNotConfiguredException();
@@ -390,6 +392,11 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
myProfileAction.externalUpdate();
}
@Override
public void deactivate() {
// TODO: Auto-generated method stub
}
@Override
public void dispose() {
myPSIChangeListener.stop();
@@ -26,7 +26,6 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.android.facet.AndroidFacet;
@@ -239,7 +238,7 @@ public class ProfileManager {
myThemeAction.clearSelection();
}
else {
update(null);
update(mySdk);
}
}
@@ -15,17 +15,18 @@
*/
package com.intellij.designer;
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorLocation;
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.fileEditor.FileEditorStateLevel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -48,6 +49,7 @@ public abstract class DesignerEditor extends UserDataHolderBase implements FileE
myDesignerPanel = createDesignerPanel(module, file);
}
@NotNull
protected abstract DesignerEditorPanel createDesignerPanel(Module module, VirtualFile file);
public final DesignerEditorPanel getDesignerPanel() {
@@ -80,6 +82,26 @@ public abstract class DesignerEditor extends UserDataHolderBase implements FileE
myDesignerPanel.deactivate();
}
@Override
public boolean isValid() {
return myDesignerPanel.isEditorValid();
}
@Override
public boolean isModified() {
return false;
}
@Override
@NotNull
public FileEditorState getState(@NotNull FileEditorStateLevel level) {
return myDesignerPanel.createState();
}
@Override
public void setState(@NotNull FileEditorState state) {
}
@Override
public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
}
@@ -88,11 +110,6 @@ public abstract class DesignerEditor extends UserDataHolderBase implements FileE
public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
}
@Override
public BackgroundEditorHighlighter getBackgroundHighlighter() {
return null;
}
@Override
public FileEditorLocation getCurrentLocation() {
return null;
@@ -0,0 +1,55 @@
/*
* Copyright 2000-2012 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.designer;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorState;
import com.intellij.openapi.fileEditor.FileEditorStateLevel;
import com.intellij.openapi.vfs.VirtualFile;
/**
* @author Alexander Lobas
*/
public class DesignerEditorState implements FileEditorState {
private final long myModificationStamp;
public DesignerEditorState(VirtualFile file) {
Document document = FileDocumentManager.getInstance().getCachedDocument(file);
myModificationStamp = document != null ? document.getModificationStamp() : file.getModificationStamp();
}
@Override
public int hashCode() {
return (int)(myModificationStamp ^ (myModificationStamp >>> 32));
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof DesignerEditorState) {
return myModificationStamp == ((DesignerEditorState)object).myModificationStamp;
}
return false;
}
@Override
public boolean canBeMergedWith(FileEditorState otherState, FileEditorStateLevel level) {
return otherState instanceof DesignerEditorState;
}
}
@@ -118,6 +118,12 @@ public final class DesignerToolWindowManager implements ProjectComponent {
return project.getComponent(DesignerToolWindowManager.class);
}
public void expandFromState() {
if (myTreeBuilder != null) {
myTreeBuilder.expandFromState();
}
}
public void refresh(final boolean updateProperties) {
if (myTreeBuilder != null) {
myTreeBuilder.queueUpdate().doWhenDone(new Runnable() {
@@ -29,6 +29,7 @@ public final class ComponentTreeBuilder extends AbstractTreeBuilder implements C
private final EditableArea mySurfaceArea;
private final TreeEditableArea myTreeArea;
private final TreeGlassLayer myGlassLayer;
private final ExpandStateHandler myExpandStateHandler;
public ComponentTreeBuilder(ComponentTree tree, DesignerEditorPanel designer) {
super(tree, (DefaultTreeModel)tree.getModel(), new TreeContentProvider(designer), null);
@@ -38,13 +39,15 @@ public final class ComponentTreeBuilder extends AbstractTreeBuilder implements C
mySurfaceArea = designer.getSurfaceArea();
myTreeArea = new TreeEditableArea(tree, this);
myGlassLayer = new TreeGlassLayer(tree, designer.getToolProvider(), myTreeArea);
myExpandStateHandler = new ExpandStateHandler(tree, designer, this);
designer.updateTreeArea(myTreeArea);
// TODO: restore expanded state
select(mySurfaceArea.getSelection().toArray(), null);
expandFromState();
addListeners();
myExpandStateHandler.hookListener();
}
public TreeEditableArea getTreeArea() {
@@ -56,6 +59,7 @@ public final class ComponentTreeBuilder extends AbstractTreeBuilder implements C
removeListeners();
myTreeArea.unhookSelection();
myGlassLayer.dispose();
myExpandStateHandler.unhookListener();
super.dispose();
}
@@ -84,4 +88,8 @@ public final class ComponentTreeBuilder extends AbstractTreeBuilder implements C
addListeners();
}
}
public void expandFromState() {
expand(myExpandStateHandler.getExpanded(), null);
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2012 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.designer.componentTree;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.designer.model.RadComponent;
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.util.ArrayUtil;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import java.util.Iterator;
import java.util.List;
/**
* @author Alexander Lobas
*/
public class ExpandStateHandler implements TreeExpansionListener {
private final ComponentTree myTree;
private final DesignerEditorPanel myDesigner;
private final AbstractTreeBuilder myTreeBuilder;
public ExpandStateHandler(ComponentTree tree, DesignerEditorPanel designer, AbstractTreeBuilder treeBuilder) {
myTree = tree;
myDesigner = designer;
myTreeBuilder = treeBuilder;
}
public void hookListener() {
myTree.addTreeExpansionListener(this);
}
public void unhookListener() {
myTree.removeTreeExpansionListener(this);
}
public Object[] getExpanded() {
List<?> components = myDesigner.getExpandedComponents();
return components == null ? ArrayUtil.EMPTY_OBJECT_ARRAY : components.toArray();
}
private void setExpanded() {
List<Object> elements = myTreeBuilder.getUi().getExpandedElements();
// remove root Object
for (Iterator<Object> I = elements.iterator(); I.hasNext(); ) {
Object element = I.next();
if (!(element instanceof RadComponent)) {
I.remove();
}
}
myDesigner.setExpandedComponents(elements);
}
@Override
public void treeExpanded(TreeExpansionEvent event) {
setExpanded();
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
setExpanded();
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.designer.designSurface;
import com.intellij.designer.DesignerEditorState;
import com.intellij.designer.DesignerToolWindowManager;
import com.intellij.designer.actions.DesignerActionPanel;
import com.intellij.designer.componentTree.TreeComponentDecorator;
@@ -23,6 +24,7 @@ import com.intellij.designer.designSurface.tools.*;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.model.RadComponentVisitor;
import com.intellij.designer.palette.Item;
import com.intellij.designer.propertyTable.Property;
import com.intellij.ide.palette.impl.PaletteManager;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
@@ -37,6 +39,7 @@ import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.ui.AsyncProcessIcon;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -49,6 +52,7 @@ import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
/**
@@ -66,11 +70,11 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
protected static final Integer LAYER_INPLACE_EDITING = LAYER_BUTTONS + 100;
private static final Integer LAYER_PROGRESS = LAYER_INPLACE_EDITING + 100;
@NonNls private final static String DESIGNER_CARD = "designer";
@NonNls private final static String ERROR_CARD = "error";
private final static String DESIGNER_CARD = "designer";
private final static String ERROR_CARD = "error";
@NotNull protected final Module myModule;
@NotNull protected final VirtualFile myFile;
protected final Module myModule;
protected final VirtualFile myFile;
private final CardLayout myLayout = new CardLayout();
private JPanel myDesignerCard;
@@ -90,6 +94,13 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
protected ToolProvider myToolProvider;
protected EditableArea mySurfaceArea;
protected RadComponent myRootComponent;
private List<?> myExpandedComponents;
private Property mySelectionProperty;
private int[][] myExpandedState;
private int[][] mySelectionState;
private JPanel myErrorPanel;
private JLabel myErrorMessage;
private JTextArea myErrorStack;
@@ -98,8 +109,6 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
private AsyncProcessIcon myProgressIcon;
private JLabel myProgressMessage;
protected RadComponent myRootComponent;
public DesignerEditorPanel(@NotNull Module module, @NotNull VirtualFile file) {
myModule = module;
myFile = file;
@@ -336,6 +345,7 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
protected abstract void configureError(ErrorInfo info);
protected void showErrorPage(ErrorInfo info) {
storeState();
hideProgress();
myRootComponent = null;
@@ -381,6 +391,93 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
public void updateTreeArea(EditableArea area) {
}
@Nullable
public List<?> getExpandedComponents() {
return myExpandedComponents;
}
public void setExpandedComponents(@Nullable List<?> expandedComponents) {
myExpandedComponents = expandedComponents;
}
public Property getSelectionProperty() {
return mySelectionProperty;
}
public void setSelectionProperty(Property selectionProperty) {
mySelectionProperty = selectionProperty;
}
protected void storeState() {
if (myRootComponent != null && myExpandedState == null && mySelectionState == null) {
myExpandedState = new int[myExpandedComponents == null ? 0 : myExpandedComponents.size()][];
for (int i = 0; i < myExpandedState.length; i++) {
IntArrayList path = new IntArrayList();
componentToPath((RadComponent)myExpandedComponents.get(i), path);
myExpandedState[i] = path.toArray();
}
List<RadComponent> selection = mySurfaceArea.getSelection();
mySelectionState = new int[selection.size()][];
for (int i = 0; i < mySelectionState.length; i++) {
IntArrayList path = new IntArrayList();
componentToPath(selection.get(i), path);
mySelectionState[i] = path.toArray();
}
myExpandedComponents = null;
myToolProvider.loadDefaultTool();
mySurfaceArea.deselectAll();
}
}
private static void componentToPath(RadComponent component, IntArrayList path) {
RadComponent parent = component.getParent();
if (parent != null) {
path.add(0, parent.getChildren().indexOf(component));
componentToPath(parent, path);
}
}
protected void restoreState() {
DesignerToolWindowManager toolManager = DesignerToolWindowManager.getInstance(getProject());
if (myExpandedState == null || mySelectionProperty == null || myRootComponent == null) {
toolManager.refresh(true);
}
else {
List<RadComponent> expanded = new ArrayList<RadComponent>();
for (int[] path : myExpandedState) {
pathToComponent(expanded, myRootComponent, path, 0);
}
myExpandedComponents = expanded;
toolManager.expandFromState();
List<RadComponent> selection = new ArrayList<RadComponent>();
for (int[] path : mySelectionState) {
pathToComponent(selection, myRootComponent, path, 0);
}
mySurfaceArea.setSelection(selection);
}
myExpandedState = null;
mySelectionState = null;
}
private static void pathToComponent(List<RadComponent> components, RadComponent component, int[] path, int index) {
if (index == path.length) {
components.add(component);
}
else {
List<RadComponent> children = component.getChildren();
int componentIndex = path[index];
if (0 <= componentIndex && componentIndex < children.size()) {
pathToComponent(components, children.get(componentIndex), path, index + 1);
}
}
}
public ToolProvider getToolProvider() {
return myToolProvider;
}
@@ -410,6 +507,15 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
public void deactivate() {
}
@NotNull
public DesignerEditorState createState() {
return new DesignerEditorState(myFile);
}
public boolean isEditorValid() {
return myFile.isValid();
}
@Override
public Object getData(@NonNls String dataId) {
// TODO: support keys
@@ -37,6 +37,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.TableUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
@@ -67,8 +69,9 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi
private final TableCellRenderer myCellRenderer = new PropertyCellRenderer();
private boolean mySkipUpdate;
@Nullable private EditableArea myArea;
@Nullable private DesignerEditorPanel myDesigner;
private EditableArea myArea;
private DesignerEditorPanel myDesigner;
private Property myInitialSelection;
private boolean myShowExpert;
@@ -77,6 +80,14 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
addMouseListener(new MouseTableListener());
getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (myDesigner != null) {
myDesigner.setSelectionProperty(getSelectionProperty());
}
}
});
// TODO: ShowJavadocAction
@@ -138,6 +149,7 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi
public void setArea(@Nullable DesignerEditorPanel designer, @Nullable EditableArea area) {
myDesigner = designer;
myInitialSelection = designer == null ? null : designer.getSelectionProperty();
finishEditing();
@@ -214,11 +226,15 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi
}
else {
Property selection = getSelectionProperty();
myComponents = new ArrayList<RadComponent>(myArea.getSelection());
fillProperties();
myModel.fireTableDataChanged();
if (myInitialSelection != null && !myComponents.isEmpty()) {
selection = myInitialSelection;
myInitialSelection = null;
}
restoreSelection(selection);
}
}