diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/PropertyParser.java b/plugins/android-designer/src/com/intellij/android/designer/model/PropertyParser.java index 8566e60491c2..bcd3259b44b5 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/PropertyParser.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/PropertyParser.java @@ -193,9 +193,13 @@ public class PropertyParser { Class superComponentClass = componentClass.getSuperclass(); if (superComponentClass != null) { superComponentClass = configureClass(superComponentClass); + MetaModel superModel = myMetaManager.getModelByTarget(superComponentClass.getName()); - List superProperties = loadWidgetProperties(superComponentClass, - myMetaManager.getModelByTarget(superComponentClass.getName())); + if (model != null && superModel != null && model.getInplaceProperties().isEmpty()) { + model.setInplaceProperties(superModel.getInplaceProperties()); + } + + List superProperties = loadWidgetProperties(superComponentClass, superModel); for (Property superProperty : superProperties) { if (PropertyTable.findProperty(properties, superProperty) == -1) { if (model == null) { diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/RadCustomViewComponent.java b/plugins/android-designer/src/com/intellij/android/designer/model/RadCustomViewComponent.java index 6ce1ff5a0ced..986c1ecf75eb 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/RadCustomViewComponent.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/RadCustomViewComponent.java @@ -22,6 +22,7 @@ import com.intellij.designer.model.MetaManager; import com.intellij.designer.model.MetaModel; import com.intellij.designer.model.RadComponent; import com.intellij.designer.propertyTable.Property; +import com.intellij.designer.propertyTable.PropertyTable; import com.intellij.openapi.module.Module; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.text.StringUtil; @@ -50,32 +51,6 @@ public class RadCustomViewComponent extends RadViewComponent implements IConfigu "\"/>"; } - @Override - public MetaModel getMetaModelForProperties() throws Exception { - MetaModel metaModel = getClientProperty(MODEL_KEY); - - if (metaModel == null) { - Module module = getRoot().getClientProperty(ModelParser.MODULE_KEY); - MetaManager metaManager = ViewsMetaManager.getInstance(module.getProject()); - PsiClass viewClass = ChooseClassDialog.findClass(module, getViewClass()); - - while (viewClass != null) { - metaModel = metaManager.getModelByTarget(viewClass.getQualifiedName()); - if (metaModel != null) { - break; - } - viewClass = viewClass.getSuperClass(); - } - if (metaModel == null) { - metaModel = myMetaModel; - } - - setClientProperty(MODEL_KEY, metaModel); - } - - return metaModel; - } - @Override public void configure(RadComponent rootComponent) throws Exception { String view = chooseView(rootComponent); @@ -126,6 +101,52 @@ public class RadCustomViewComponent extends RadViewComponent implements IConfigu } } + @Override + public MetaModel getMetaModelForProperties() throws Exception { + MetaModel metaModel = getClientProperty(MODEL_KEY); + + if (metaModel == null) { + Module module = getRoot().getClientProperty(ModelParser.MODULE_KEY); + MetaManager metaManager = ViewsMetaManager.getInstance(module.getProject()); + PsiClass viewClass = ChooseClassDialog.findClass(module, getViewClass()); + + while (viewClass != null) { + metaModel = metaManager.getModelByTarget(viewClass.getQualifiedName()); + if (metaModel != null) { + break; + } + viewClass = viewClass.getSuperClass(); + } + if (metaModel == null) { + metaModel = myMetaModel; + } + + setClientProperty(MODEL_KEY, metaModel); + } + + return metaModel; + } + + @Override + public List getInplaceProperties() throws Exception { + List properties = new ArrayList(); + MetaModel metaModel = getMetaModelForProperties(); + List allProperties = getProperties(); + + properties.add(CLASS_PROPERTY); + + for (String name : metaModel.getInplaceProperties()) { + Property property = PropertyTable.findProperty(allProperties, name); + if (property != null) { + properties.add(property); + } + } + + properties.add(PropertyTable.findProperty(allProperties, "id")); + + return properties; + } + @Override public void setProperties(List properties) { List oldProperties = getProperties(); diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java b/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java index a472aa3aaf51..1242352fdc87 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java @@ -19,6 +19,7 @@ import com.android.ide.common.rendering.api.ViewInfo; import com.intellij.designer.model.RadComponent; import com.intellij.designer.model.RadVisualComponent; import com.intellij.designer.propertyTable.Property; +import com.intellij.designer.propertyTable.PropertyTable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.xml.XmlAttribute; @@ -196,6 +197,16 @@ public class RadViewComponent extends RadVisualComponent { myProperties = properties; } + @Override + public List getInplaceProperties() throws Exception { + List properties = super.getInplaceProperties(); + Property idProperty = PropertyTable.findProperty(myProperties, "id"); + if (idProperty != null) { + properties.add(idProperty); + } + return properties; + } + @Override public void copyTo(Element parent) throws Exception { // skip root diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml index 815dac81094a..ece7ef65ba27 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml +++ b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml @@ -27,6 +27,8 @@ + + + + @@ -70,6 +74,7 @@ tooltip="Displays text to the user and optionally allows them to edit it."/> @@ -95,6 +100,7 @@ tooltip="Represents a push-button widget."/> @@ -111,7 +117,8 @@ - + - + + + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1156,7 +1180,9 @@ class="android.widget.SlidingDrawer" tag="SlidingDrawer"> - + @@ -1195,7 +1221,8 @@ - @@ -1294,7 +1321,8 @@ class="android.widget.GridLayout" tag="GridLayout"> - @@ -1317,7 +1345,8 @@ class="android.widget.RelativeLayout" tag="RelativeLayout"> - + diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceEditor.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceEditor.java index bd6ccc16c2d7..d5c4269ed3d1 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceEditor.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/ResourceEditor.java @@ -18,6 +18,7 @@ package com.intellij.android.designer.propertyTable.editors; import com.android.resources.ResourceType; import com.intellij.android.designer.model.ModelParser; import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.InplaceContext; import com.intellij.designer.propertyTable.PropertyEditor; import com.intellij.designer.propertyTable.editors.ComboEditor; import com.intellij.openapi.module.Module; @@ -26,11 +27,14 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.ComboboxWithBrowseButton; +import com.intellij.ui.DocumentAdapter; import org.jetbrains.android.dom.attrs.AttributeFormat; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import java.awt.*; import java.awt.event.*; import java.util.EnumSet; import java.util.Set; @@ -51,7 +55,12 @@ public class ResourceEditor extends PropertyEditor { myTypes = types; if (formats.contains(AttributeFormat.Enum) || formats.contains(AttributeFormat.Boolean)) { - ComboboxWithBrowseButton editor = new ComboboxWithBrowseButton(); + ComboboxWithBrowseButton editor = new ComboboxWithBrowseButton() { + @Override + public Dimension getPreferredSize() { + return getComponentPreferredSize(); + } + }; final JComboBox comboBox = editor.getComboBox(); DefaultComboBoxModel model; @@ -78,18 +87,32 @@ public class ResourceEditor extends PropertyEditor { comboBox.setSelectedIndex(0); } else { - myEditor = new TextFieldWithBrowseButton(); + myEditor = new TextFieldWithBrowseButton() { + @Override + public Dimension getPreferredSize() { + return getComponentPreferredSize(); + } + }; myEditor.registerKeyboardAction(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { } }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - getComboText().addActionListener(new ActionListener() { + + JTextField textField = getComboText(); + textField.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fireValueCommitted(true, true); } }); + textField.getDocument().addDocumentListener( + new DocumentAdapter() { + protected void textChanged(final DocumentEvent e) { + preferredSizeChanged(); + } + } + ); } myEditor.addActionListener(new ActionListener() { @@ -106,6 +129,12 @@ public class ResourceEditor extends PropertyEditor { }); } + private Dimension getComponentPreferredSize() { + Dimension size1 = myEditor.getChildComponent().getPreferredSize(); + Dimension size2 = myEditor.getButton().getPreferredSize(); + return new Dimension(size1.width + size2.width + 5, size1.height); + } + private static ResourceType[] convertTypes(Set formats) { Set types = EnumSet.noneOf(ResourceType.class); for (AttributeFormat format : formats) { @@ -141,13 +170,24 @@ public class ResourceEditor extends PropertyEditor { @NotNull @Override - public JComponent getComponent(@NotNull RadComponent rootComponent, @Nullable RadComponent component, Object value) { + public JComponent getComponent(@NotNull RadComponent rootComponent, + @Nullable RadComponent component, + Object value, + @Nullable InplaceContext inplaceContext) { myRootComponent = rootComponent; JTextField text = getComboText(); text.setText((String)value); + if (inplaceContext != null && inplaceContext.isStartChar()) { + text.setText(inplaceContext.getText(text.getText())); + } return myEditor; } + @Override + public JComponent getPreferredFocusedComponent() { + return getComboText(); + } + @Override public Object getValue() throws Exception { String value = getComboText().getText(); diff --git a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/StringsComboEditor.java b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/StringsComboEditor.java index 0cff35b05c96..949b97356e1a 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/StringsComboEditor.java +++ b/plugins/android-designer/src/com/intellij/android/designer/propertyTable/editors/StringsComboEditor.java @@ -16,7 +16,7 @@ package com.intellij.android.designer.propertyTable.editors; import com.intellij.designer.model.RadComponent; -import com.intellij.designer.propertyTable.PropertyEditor; +import com.intellij.designer.propertyTable.InplaceContext; import com.intellij.designer.propertyTable.editors.ComboEditor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,7 +54,10 @@ public class StringsComboEditor extends ComboEditor { @NotNull @Override - public JComponent getComponent(@NotNull RadComponent rootComponent, @Nullable RadComponent component, Object value) { + public JComponent getComponent(@NotNull RadComponent rootComponent, + @Nullable RadComponent component, + Object value, + @Nullable InplaceContext inplaceContext) { myCombo.setSelectedItem(value); return myCombo; } diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java index a030df6bc597..926e87fb7dc5 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java @@ -19,7 +19,6 @@ import com.intellij.designer.componentTree.ComponentTree; import com.intellij.designer.componentTree.ComponentTreeBuilder; import com.intellij.designer.componentTree.TreeEditableArea; import com.intellij.designer.designSurface.DesignerEditorPanel; -import com.intellij.designer.propertyTable.PropertyTable; import com.intellij.designer.propertyTable.PropertyTablePanel; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -156,10 +155,6 @@ public final class DesignerToolWindowManager implements ProjectComponent { return editors.length > 0 ? getDesigner(editors[0]) : null; } - public PropertyTable getPropertyTable() { - return myPropertyTablePanel.getPropertyTable(); - } - private void bindToDesigner(final DesignerEditorPanel designer) { myWindowQueue.cancelAllUpdates(); myWindowQueue.queue(new Update("update") { @@ -177,14 +172,12 @@ public final class DesignerToolWindowManager implements ProjectComponent { clearTreeBuilder(); myComponentTree.newModel(); if (designer == null) { - myComponentTree.setDecorator(null); - myComponentTree.setActionPanel(null); + myComponentTree.setDesignerPanel(null); myPropertyTablePanel.getPropertyTable().setArea(null, null); myToolWindow.setAvailable(false, null); } else { - myComponentTree.setDecorator(designer.getTreeDecorator()); - myComponentTree.setActionPanel(designer.getActionPanel()); + myComponentTree.setDesignerPanel(designer); myTreeBuilder = new ComponentTreeBuilder(myComponentTree, designer); myPropertyTablePanel.getPropertyTable().setArea(designer, myTreeBuilder.getTreeArea()); myToolWindow.setAvailable(true, null); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java index bde7dfb0d5c2..7a6bf5ae69da 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java @@ -65,7 +65,9 @@ public class CommonEditActionsProvider implements DeleteProvider, CopyProvider, @Override public boolean canDeleteElement(@NotNull DataContext dataContext) { - // TODO: InplaceEditing + if (myDesigner.getInplaceEditingLayer().isEditing()) { + return false; + } List selection = getArea().getSelection(); if (selection.isEmpty()) { return false; @@ -143,8 +145,7 @@ public class CommonEditActionsProvider implements DeleteProvider, CopyProvider, @Override public boolean isCopyEnabled(@NotNull DataContext dataContext) { - // TODO: InplaceEditing - return !getArea().getSelection().isEmpty(); + return !myDesigner.getInplaceEditingLayer().isEditing() && !getArea().getSelection().isEmpty(); } @Override @@ -185,8 +186,7 @@ public class CommonEditActionsProvider implements DeleteProvider, CopyProvider, @Override public boolean isPasteEnabled(@NotNull DataContext dataContext) { - // TODO: InplaceEditing - return getSerializedComponentData() != null; + return !myDesigner.getInplaceEditingLayer().isEditing() && getSerializedComponentData() != null; } @Nullable diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/DesignerActionPanel.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/DesignerActionPanel.java index 8aeafbedd92a..6397254c02ed 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/DesignerActionPanel.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/DesignerActionPanel.java @@ -48,6 +48,8 @@ public class DesignerActionPanel implements DataProvider { myCommonEditActionsProvider = new CommonEditActionsProvider(designer); myShortcuts = shortcuts; + createInplaceEditingAction(myShortcuts).setDesignerPanel(designer); + myActionGroup.add(myStaticGroup); myActionGroup.add(myDynamicGroup); @@ -91,6 +93,12 @@ public class DesignerActionPanel implements DataProvider { }); } + public static StartInplaceEditing createInplaceEditingAction(JComponent shortcuts) { + StartInplaceEditing action = new StartInplaceEditing(); + action.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)), shortcuts); + return action; + } + private void registerAction(AnAction action, @NonNls String actionId) { action.registerCustomShortcutSet( ActionManager.getInstance().getAction(actionId).getShortcutSet(), diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/StartInplaceEditing.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/StartInplaceEditing.java new file mode 100644 index 000000000000..a450a651c58e --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/StartInplaceEditing.java @@ -0,0 +1,58 @@ +/* + * 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.actions; + +import com.intellij.designer.designSurface.DesignerEditorPanel; +import com.intellij.designer.model.RadComponent; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Alexander Lobas + */ +public class StartInplaceEditing extends AnAction { + private DesignerEditorPanel myDesigner; + + public void setDesignerPanel(@Nullable DesignerEditorPanel designer) { + myDesigner = designer; + } + + @Override + public void update(AnActionEvent event) { + boolean enabled = false; + + if (myDesigner != null) { + List selection = myDesigner.getSurfaceArea().getSelection(); + if (selection.size() == 1) { + try { + enabled = !selection.get(0).getInplaceProperties().isEmpty(); + } + catch (Exception e) { + } + } + } + + event.getPresentation().setEnabled(enabled); + } + + @Override + public void actionPerformed(AnActionEvent e) { + myDesigner.getInplaceEditingLayer().startEditing(null); + } +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTree.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTree.java index e0970a24af44..e39eb730ca18 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTree.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTree.java @@ -16,6 +16,8 @@ package com.intellij.designer.componentTree; import com.intellij.designer.actions.DesignerActionPanel; +import com.intellij.designer.actions.StartInplaceEditing; +import com.intellij.designer.designSurface.DesignerEditorPanel; import com.intellij.designer.designSurface.FeedbackTreeLayer; import com.intellij.designer.model.RadComponent; import com.intellij.openapi.actionSystem.DataProvider; @@ -35,6 +37,7 @@ import java.awt.*; * @author Alexander Lobas */ public final class ComponentTree extends Tree implements DataProvider { + private final StartInplaceEditing myInplaceEditingAction; private TreeComponentDecorator myDecorator; private DesignerActionPanel myActionPanel; private RadComponent myMarkComponent; @@ -55,20 +58,24 @@ public final class ComponentTree extends Tree implements DataProvider { // Install convenient keyboard navigation TreeUtil.installActions(this); - // TODO: F2 should start inplace editing + myInplaceEditingAction = DesignerActionPanel.createInplaceEditingAction(this); } public void newModel() { setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); } - public void setDecorator(@Nullable TreeComponentDecorator decorator) { - myDecorator = decorator; + public void setDesignerPanel(@Nullable DesignerEditorPanel designer) { + if (designer == null) { + myDecorator = null; + myActionPanel = null; + } + else { + myDecorator = designer.getTreeDecorator(); + myActionPanel = designer.getActionPanel(); + } myMarkComponent = null; - } - - public void setActionPanel(@Nullable DesignerActionPanel actionPanel) { - myActionPanel = actionPanel; + myInplaceEditingAction.setDesignerPanel(designer); } public void mark(RadComponent component, int feedback) { diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java index 805a75852f6e..2ee177594ddf 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/DesignerEditorPanel.java @@ -24,6 +24,7 @@ import com.intellij.designer.designSurface.tools.*; import com.intellij.designer.model.FindComponentVisitor; import com.intellij.designer.model.RadComponent; import com.intellij.designer.palette.Item; +import com.intellij.designer.propertyTable.InplaceContext; import com.intellij.designer.propertyTable.Property; import com.intellij.diagnostic.LogMessageEx; import com.intellij.diagnostic.errordialog.Attachment; @@ -97,6 +98,7 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider protected GlassLayer myGlassLayer; private DecorationLayer myDecorationLayer; private FeedbackLayer myFeedbackLayer; + private InplaceEditingLayer myInplaceEditingLayer; private ListSelectionListener myPaletteListener; protected ToolProvider myToolProvider; @@ -244,6 +246,11 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider }, command, null); } + @Override + public void startInplaceEditing(@Nullable InplaceContext inplaceContext) { + myInplaceEditingLayer.startEditing(inplaceContext); + } + @Override public void showError(@NonNls String message, Throwable e) { DesignerEditorPanel.this.showError(message, e); @@ -259,6 +266,9 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider myFeedbackLayer = new FeedbackLayer(); myLayeredPane.add(myFeedbackLayer, LAYER_FEEDBACK); + myInplaceEditingLayer = new InplaceEditingLayer(this); + myLayeredPane.add(myInplaceEditingLayer, LAYER_INPLACE_EDITING); + JPanel content = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -513,6 +523,10 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider return myActionPanel; } + public InplaceEditingLayer getInplaceEditingLayer() { + return myInplaceEditingLayer; + } + public JComponent getPreferredFocusedComponent() { return myDesignerCard.isVisible() ? myGlassLayer : myErrorPanel; } diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/InplaceEditingLayer.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/InplaceEditingLayer.java new file mode 100644 index 000000000000..560619fce313 --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/InplaceEditingLayer.java @@ -0,0 +1,256 @@ +/* + * 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.designSurface; + +import com.intellij.designer.designSurface.feedbacks.LineMarginBorder; +import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.InplaceContext; +import com.intellij.designer.propertyTable.Property; +import com.intellij.designer.propertyTable.PropertyEditor; +import com.intellij.designer.propertyTable.PropertyEditorListener; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.wm.FocusWatcher; +import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; +import com.intellij.openapi.wm.ex.LayoutFocusTraversalPolicyExt; +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Alexander Lobas + * @author Anton Katilin + * @author Vladimir Kondratyev + */ +public class InplaceEditingLayer extends JComponent { + private static final Logger LOG = Logger.getInstance("#com.intellij.designer.designSurface.InplaceEditingLayer"); + + private final FocusWatcher myFocusWatcher = new FocusWatcher() { + protected void focusLostImpl(FocusEvent e) { + Component opposite = e.getOppositeComponent(); + if (e.isTemporary() || opposite != null && SwingUtilities.isDescendingFrom(opposite, getTopComponent())) { + // Do nothing if focus moves inside top component hierarchy + return; + } + // [vova] we need LaterInvocator here to prevent write-access assertions + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + finishEditing(true); + } + }, ModalityState.NON_MODAL); + } + }; + private final ComponentSelectionListener mySelectionListener = new ComponentSelectionListener() { + @Override + public void selectionChanged(EditableArea area) { + finishEditing(true); + } + }; + private PropertyEditorListener myEditorListener = new PropertyEditorListener() { + @Override + public void valueCommitted(PropertyEditor source, boolean continueEditing, boolean closeEditorOnError) { + finishEditing(true); + } + + @Override + public void editingCanceled(PropertyEditor source) { + finishEditing(false); + } + + @Override + public void preferredSizeChanged(PropertyEditor source) { + adjustInplaceComponentSize(); + } + }; + private List myEditors; + + private final DesignerEditorPanel myDesigner; + private JComponent myInplaceComponent; + private int myPreferredWidth; + + public InplaceEditingLayer(DesignerEditorPanel designer) { + myDesigner = designer; + } + + public void startEditing(@Nullable InplaceContext inplaceContext) { + try { + List selection = myDesigner.getSurfaceArea().getSelection(); + if (selection.size() != 1) { + return; + } + + RadComponent radComponent = selection.get(0); + List inplaceProperties = radComponent.getInplaceProperties(); + if (inplaceProperties.isEmpty()) { + return; + } + + myInplaceComponent = new JPanel(new GridLayoutManager(inplaceProperties.size(), 2)); + myInplaceComponent.setBorder(new LineMarginBorder(5, 5, 5, 5)); + new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + finishEditing(false); + } + }.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)), myInplaceComponent); + + int row = 0; + + myEditors = new ArrayList(); + + JComponent componentToFocus = null; + Font font = null; + + if (inplaceContext == null) { + inplaceContext = new InplaceContext(); + } + + for (Property property : inplaceProperties) { + JLabel label = new JLabel(property.getName() + ":"); + if (font == null) { + font = label.getFont().deriveFont(Font.BOLD); + } + label.setFont(font); + + myInplaceComponent.add(label, + new GridConstraints(row, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, 0, 0, null, null, + null)); + + PropertyEditor editor = property.getEditor(); + myEditors.add(editor); + + JComponent component = editor.getComponent(radComponent.getRoot(), radComponent, property.getValue(radComponent), inplaceContext); + + myInplaceComponent.add(component, + new GridConstraints(row++, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, 0, 0, + null, null, null)); + + if (componentToFocus == null) { + componentToFocus = editor.getPreferredFocusedComponent(); + } + } + + for (PropertyEditor editor : myEditors) { + editor.addPropertyEditorListener(myEditorListener); + } + + Rectangle bounds = radComponent.getBounds(this); + Dimension size = myInplaceComponent.getPreferredSize(); + myPreferredWidth = size.width; + myInplaceComponent.setBounds(bounds.x, bounds.y, size.width, size.height); + add(myInplaceComponent); + + if (componentToFocus == null) { + componentToFocus = IdeFocusTraversalPolicy.getPreferredFocusedComponent(myInplaceComponent); + } + if (componentToFocus != null) { + componentToFocus.requestFocusInWindow(); + } + else { + myInplaceComponent.requestFocusInWindow(); + } + + myDesigner.getSurfaceArea().addSelectionListener(mySelectionListener); + myFocusWatcher.install(myInplaceComponent); + + enableEvents(AWTEvent.MOUSE_EVENT_MASK); + repaint(); + } + catch (Throwable e) { + LOG.error(e); + } + } + + private void finishEditing(boolean commit) { + myDesigner.getSurfaceArea().removeSelectionListener(mySelectionListener); + + if (myInplaceComponent != null) { + if (commit) { + // TODO: Auto-generated method stub + } + + for (PropertyEditor editor : myEditors) { + editor.removePropertyEditorListener(myEditorListener); + } + + removeInplaceComponent(); + myFocusWatcher.deinstall(myInplaceComponent); + myInplaceComponent = null; + } + + myEditors = null; + + myDesigner.getPreferredFocusedComponent().requestFocusInWindow(); + + disableEvents(AWTEvent.MOUSE_EVENT_MASK); + repaint(); + } + + private void adjustInplaceComponentSize() { + myInplaceComponent.revalidate(); + Dimension size = myInplaceComponent.getPreferredSize(); + int width = Math.max(size.width, myPreferredWidth); + width = Math.min(width, getWidth() - myInplaceComponent.getX()); + myInplaceComponent.setSize(width, myInplaceComponent.getHeight()); + myInplaceComponent.revalidate(); + System.out.println("size: " + width); + } + + private void removeInplaceComponent() { + // [vova] before removing component from Swing tree we have to + // request component into glass layer. Otherwise focus from component being removed + // can go to some RadComponent. + + LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(myDesigner.getPreferredFocusedComponent()); + try { + remove(myInplaceComponent); + } + finally { + LayoutFocusTraversalPolicyExt.setOverridenDefaultComponent(null); + } + } + + /** + * When there is an inplace editor we "listen" all mouse event + * and finish editing by any MOUSE_PRESSED or MOUSE_RELEASED event. + * We are acting like yet another glass pane over the standard glass layer. + */ + protected void processMouseEvent(MouseEvent e) { + if (myInplaceComponent != null && (MouseEvent.MOUSE_PRESSED == e.getID() || MouseEvent.MOUSE_RELEASED == e.getID())) { + finishEditing(true); + } + // [vova] this is very important! Without this code Swing doen't close popup menu on our + // layered pane. Swing adds MouseListeners to all component to close popup. If we do not + // invoke super then we lock all mouse listeners. + super.processMouseEvent(e); + } + + public boolean isEditing() { + return myInplaceComponent != null; + } +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java index 466a829ac9a1..7efcfb6c1f7c 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/SelectionTool.java @@ -17,10 +17,12 @@ package com.intellij.designer.designSurface.tools; import com.intellij.designer.designSurface.EditableArea; import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.InplaceContext; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionPopupMenu; import org.jetbrains.annotations.Nullable; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.List; @@ -85,6 +87,13 @@ public class SelectionTool extends InputTool { handleMove(); // hack: update cursor } + @Override + protected void handleDoubleClick(int button) { + if (button == MouseEvent.BUTTON1 && myToolProvider != null) { + myToolProvider.startInplaceEditing(null); + } + } + @Override protected void handleMove() { if (myState == STATE_INIT) { @@ -190,6 +199,11 @@ public class SelectionTool extends InputTool { if (myTracker != null) { myTracker.keyTyped(event, area); } + else if (myToolProvider != null && + Character.isLetterOrDigit(event.getKeyChar()) && + (event.getModifiers() & (InputEvent.ALT_MASK | InputEvent.CTRL_MASK)) == 0) { + myToolProvider.startInplaceEditing(new InplaceContext(event.getKeyChar())); + } } @Override diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ToolProvider.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ToolProvider.java index 4507942f5baf..a1827e4f8a74 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ToolProvider.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/designSurface/tools/ToolProvider.java @@ -17,6 +17,7 @@ package com.intellij.designer.designSurface.tools; import com.intellij.designer.designSurface.EditOperation; import com.intellij.designer.designSurface.EditableArea; +import com.intellij.designer.propertyTable.InplaceContext; import com.intellij.util.ThrowableRunnable; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; @@ -133,4 +134,6 @@ public abstract class ToolProvider { public abstract boolean execute(ThrowableRunnable operation, String command, boolean updateProperties); public abstract void execute(List operations, String command); + + public abstract void startInplaceEditing(@Nullable InplaceContext inplaceContext); } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java index 5f8316abf946..4337bcf8161e 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaManager.java @@ -114,6 +114,11 @@ public abstract class MetaManager { Element properties = element.getChild("properties"); if (properties != null) { + Attribute inplace = properties.getAttribute("inplace"); + if (inplace != null) { + meta.setInplaceProperties(StringUtil.split(inplace.getValue(), " ")); + } + Attribute top = properties.getAttribute("top"); if (top != null) { meta.setTopProperties(StringUtil.split(top.getValue(), " ")); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java index b86e227fc498..ab019ead955c 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/MetaModel.java @@ -39,6 +39,7 @@ public class MetaModel { private Icon myIcon; private String myCreation; private boolean myDelete = true; + private List myInplaceProperties = Collections.emptyList(); private List myTopProperties = Collections.emptyList(); private List myNormalProperties = Collections.emptyList(); private List myImportantProperties = Collections.emptyList(); @@ -116,6 +117,14 @@ public class MetaModel { myPaletteItem.setMetaModel(this); } + public List getInplaceProperties() { + return myInplaceProperties; + } + + public void setInplaceProperties(List inplaceProperties) { + myInplaceProperties = inplaceProperties; + } + public List getTopProperties() { return myTopProperties; } diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java index 749bbb16d2b3..c43400d71452 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java @@ -22,6 +22,7 @@ import com.intellij.designer.designSurface.StaticDecorator; import com.intellij.designer.designSurface.tools.DragTracker; import com.intellij.designer.designSurface.tools.InputTool; import com.intellij.designer.propertyTable.Property; +import com.intellij.designer.propertyTable.PropertyTable; import com.intellij.openapi.actionSystem.DefaultActionGroup; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -186,6 +187,22 @@ public abstract class RadComponent { return Collections.emptyList(); } + public List getInplaceProperties() throws Exception { + List properties = new ArrayList(); + + if (myMetaModel != null) { + List allProperties = getProperties(); + for (String name : myMetaModel.getInplaceProperties()) { + Property property = PropertyTable.findProperty(allProperties, name); + if (property != null) { + properties.add(property); + } + } + } + + return properties; + } + @SuppressWarnings("unchecked") public final T getClientProperty(@NotNull String key) { return (T)myClientProperties.get(key); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/InplaceContext.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/InplaceContext.java new file mode 100644 index 000000000000..63cff0814927 --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/InplaceContext.java @@ -0,0 +1,45 @@ +/* + * 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.propertyTable; + +/** + * @author Alexander Lobas + */ +public class InplaceContext { + private char myStartChar; + + public InplaceContext() { + this((char)0); + } + + public InplaceContext(char startChar) { + myStartChar = startChar; + } + + public boolean isStartChar() { + return myStartChar != 0; + } + + public String getText(String text) { + if (text == null) { + text = ""; + } + + text += myStartChar; + myStartChar = 0; + return text; + } +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java index c0892ab4cff2..3ef33c0d9909 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java @@ -29,10 +29,13 @@ public abstract class PropertyEditor { private final EventListenerList myListenerList = new EventListenerList(); @NotNull - public abstract JComponent getComponent(@NotNull RadComponent rootComponent, @Nullable RadComponent component, Object value); + public abstract JComponent getComponent(@NotNull RadComponent rootComponent, + @Nullable RadComponent component, + Object value, + @Nullable InplaceContext inplaceContext); @Nullable - public JComponent getPreferredFocusedComponent(@NotNull JComponent component) { + public JComponent getPreferredFocusedComponent() { return null; } diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java index f22f320262b2..eafb8dc3dc24 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java @@ -533,7 +533,7 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi editCellAt(index, convertColumnIndexToView(1)); LOG.assertTrue(editorComp != null); - JComponent preferredComponent = editor.getPreferredFocusedComponent((JComponent)editorComp); + JComponent preferredComponent = editor.getPreferredFocusedComponent(); if (preferredComponent == null) { preferredComponent = IdeFocusTraversalPolicy.getPreferredFocusedComponent((JComponent)editorComp); } @@ -855,7 +855,7 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { try { - JComponent component = myEditor.getComponent(myDesigner.getRootComponent(), getCurrentComponent(), getValue((Property)value)); + JComponent component = myEditor.getComponent(myDesigner.getRootComponent(), getCurrentComponent(), getValue((Property)value), null); if (component instanceof JComboBox) { component.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/BooleanEditor.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/BooleanEditor.java index bc57cc10b9da..b069d777add7 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/BooleanEditor.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/BooleanEditor.java @@ -16,6 +16,7 @@ package com.intellij.designer.propertyTable.editors; import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.InplaceContext; import com.intellij.designer.propertyTable.PropertyEditor; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -52,8 +53,16 @@ public class BooleanEditor extends PropertyEditor { return Boolean.valueOf(myCheckBox.isSelected()); } + @Override + public JComponent getPreferredFocusedComponent() { + return myCheckBox; + } + @NotNull - public JComponent getComponent(@NotNull RadComponent rootComponent, @Nullable RadComponent component, Object value) { + public JComponent getComponent(@NotNull RadComponent rootComponent, + @Nullable RadComponent component, + Object value, + @Nullable InplaceContext inplaceContext) { try { myInsideChange = true; myCheckBox.setBackground(UIUtil.getTableBackground()); diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/ComboEditor.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/ComboEditor.java index dc661ab879fd..f19eb16efed7 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/ComboEditor.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/ComboEditor.java @@ -52,6 +52,11 @@ public abstract class ComboEditor extends PropertyEditor { } } + @Override + public JComponent getPreferredFocusedComponent() { + return myCombo; + } + private static class ComboListeners implements PopupMenuListener, ActionListener { private final PropertyEditor myEditor; private boolean myCancelled; diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/TextEditor.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/TextEditor.java index e226de011224..592a859cd72f 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/TextEditor.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/editors/TextEditor.java @@ -16,11 +16,15 @@ package com.intellij.designer.propertyTable.editors; import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.InplaceContext; import com.intellij.designer.propertyTable.PropertyEditor; +import com.intellij.ui.DocumentAdapter; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.event.DocumentEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -37,13 +41,36 @@ public class TextEditor extends PropertyEditor { fireValueCommitted(true, false); } }); + myTextField.getDocument().addDocumentListener( + new DocumentAdapter() { + protected void textChanged(final DocumentEvent e) { + preferredSizeChanged(); + } + } + ); } @NotNull @Override - public JComponent getComponent(@NotNull RadComponent rootComponent, @Nullable RadComponent component, Object value) { + public JComponent getComponent(@NotNull RadComponent rootComponent, + @Nullable RadComponent component, + Object value, + @Nullable InplaceContext inplaceContext) { setEditorValue(component, value); - myTextField.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0)); + if (inplaceContext == null) { + myTextField.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0)); + } + else { + myTextField.setBorder(UIUtil.getTextFieldBorder()); + if (inplaceContext.isStartChar()) { + myTextField.setText(inplaceContext.getText(myTextField.getText())); + } + } + return myTextField; + } + + @Override + public JComponent getPreferredFocusedComponent() { return myTextField; }