diff options
author | Tor Norbye <tnorbye@google.com> | 2012-03-22 21:38:45 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2012-03-22 21:38:45 -0700 |
commit | b13cc3c964fee8f16bfbf4bd42ad0631c4cf7889 (patch) | |
tree | 98a407a56524827aaee16c2fd90208c07db79edf /propertysheet/src/org/eclipse/wb/internal/core/model/property | |
parent | c0ecc89c7fa48c6e5ab3c7635863a6e8d2b28137 (diff) | |
download | eclipse-windowbuilder-b13cc3c964fee8f16bfbf4bd42ad0631c4cf7889.tar.gz |
Add WindowBuilder propertysheet code. See README.txt for details.
This reverts commit c0ecc89c7fa48c6e5ab3c7635863a6e8d2b28137.
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/internal/core/model/property')
52 files changed, 6760 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/ComplexProperty.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/ComplexProperty.java new file mode 100644 index 0000000..69bb455 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/ComplexProperty.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property; + +import org.eclipse.wb.internal.core.model.property.editor.TextDisplayPropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.complex.IComplexPropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.presentation.PropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipProvider; +import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipTextProvider; + +import org.eclipse.swt.graphics.Point; + +import java.util.List; + +/** + * Implementation of {@link Property} that shows given inner {@link Property}'s using + * {@link IComplexPropertyEditor}. + * + * @author scheglov_ke + * @coverage core.model.property + */ +public class ComplexProperty extends Property { + private final String m_title; + private String m_text; + private String m_tooltip; + private boolean m_modified; + private Property[] m_properties; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + //////////////////////////////////////////////////////////////////////////// + public ComplexProperty(String title, String text) { + this(title, text, new Property[0]); + } + + public ComplexProperty(String title, String text, Property[] properties) { + super(new ComplexPropertyEditor()); + m_title = title; + m_text = text; + setText(text); + setProperties(properties); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the text. + */ + public void setText(String text) { + m_text = text; + } + + /** + * @return the text to display as value. + */ + public String getText() throws Exception { + return m_text; + } + + /** + * Sets the tooltip text. + */ + public void setTooltip(String tooltip) { + m_tooltip = tooltip; + } + + /** + * Specifies the {@link PropertyEditorPresentation}, for example to displaying "..." button. + */ + public void setEditorPresentation(PropertyEditorPresentation presentation) { + ((ComplexPropertyEditor) getEditor()).m_presentation = presentation; + } + + /** + * @return the sub-properties. + */ + public Property[] getProperties() { + return m_properties; + } + + /** + * Sets the sub-properties. + */ + public void setProperties(Property[] properties) { + m_properties = properties; + } + + /** + * Sets the sub-properties. + */ + public void setProperties(List<Property> properties) { + Property[] propertiesArray = properties.toArray(new Property[properties.size()]); + setProperties(propertiesArray); + } + + /** + * Sets the "modified" flag. + */ + public void setModified(boolean modified) { + m_modified = modified; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Property + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getTitle() { + return m_title; + } + + @Override + public boolean isModified() throws Exception { + return m_modified; + } + + @Override + public Object getValue() throws Exception { + return null; + } + + @Override + public void setValue(Object value) throws Exception { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Adapter + // + //////////////////////////////////////////////////////////////////////////// + @Override + public <T> T getAdapter(Class<T> adapter) { + if (adapter == PropertyTooltipProvider.class && m_tooltip != null) { + return adapter.cast(new PropertyTooltipTextProvider() { + @Override + protected String getText(Property property) throws Exception { + return m_tooltip; + } + }); + } + return super.getAdapter(adapter); + } + + //////////////////////////////////////////////////////////////////////////// + // + // ComplexPropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + private static final class ComplexPropertyEditor extends TextDisplayPropertyEditor + implements + IComplexPropertyEditor { + private PropertyEditorPresentation m_presentation; + + //////////////////////////////////////////////////////////////////////////// + // + // IComplexPropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + public Property[] getProperties(Property property) throws Exception { + return ((ComplexProperty) property).getProperties(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // TextDisplayPropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getText(Property property) throws Exception { + return ((ComplexProperty) property).getText(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + @Override + public boolean activate(PropertyTable propertyTable, Property property, Point location) + throws Exception { + return false; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public PropertyEditorPresentation getPresentation() { + return m_presentation; + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/EmptyProperty.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/EmptyProperty.java new file mode 100644 index 0000000..0f12a5a --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/EmptyProperty.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property; + +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.string.StringPropertyEditor; + +/** + * Empty {@link Property}, that has no title or value. + * + * @author scheglov_ke + * @coverage core.model.property + */ +public class EmptyProperty extends Property { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public EmptyProperty() { + super(StringPropertyEditor.INSTANCE); + } + + public EmptyProperty(PropertyEditor editor) { + super(editor); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Property + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getTitle() { + return null; + } + + @Override + public boolean isModified() throws Exception { + return false; + } + + @Override + public Object getValue() throws Exception { + return UNKNOWN_VALUE; + } + + @Override + public void setValue(Object value) throws Exception { + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/Property.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/Property.java new file mode 100644 index 0000000..28afcd3 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/Property.java @@ -0,0 +1,232 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property; + +import com.google.common.collect.Maps; + +import org.eclipse.wb.internal.core.model.property.category.PropertyCategory; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; + +import java.util.Comparator; +import java.util.Map; + +/** + * {@link Property} is used to display/change properties of ObjectInfo's. + * + * @author scheglov_ke + * @coverage core.model.property + */ +public abstract class Property { + /** + * The value that should be used when we don't know real value of {@link Property}. We can not use + * <code>null</code> because <code>null</code> can be valid value. + */ + public static final Object UNKNOWN_VALUE = new Object() { + @Override + public String toString() { + return "UNKNOWN_VALUE"; + } + }; + //////////////////////////////////////////////////////////////////////////// + // + // Instance fields + // + //////////////////////////////////////////////////////////////////////////// + protected final PropertyEditor m_editor; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public Property(PropertyEditor editor) { + m_category = PropertyCategory.NORMAL; + m_editor = editor; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the title displayed to the user to identify the property. + */ + public abstract String getTitle(); + + /** + * @return <code>true</code> if this property has a non-default value + */ + public abstract boolean isModified() throws Exception; + + //////////////////////////////////////////////////////////////////////////// + // + // Category + // + //////////////////////////////////////////////////////////////////////////// + private PropertyCategory m_category; + + /** + * @return current {@link PropertyCategory}. + */ + public final PropertyCategory getCategory() { + return m_category; + } + + /** + * Sets the {@link PropertyCategory} for this {@link Property}. + */ + public final void setCategory(PropertyCategory category) { + m_category = category; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Value + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the current value of this {@link Property} or {@link #UNKNOWN_VALUE}. + */ + public abstract Object getValue() throws Exception; + + /** + * Sets the new value of this {@link Property}. + * + * @param the + * new value of {@link Property} or {@link #UNKNOWN_VALUE} if {@link Property} + * modification should be removed. + */ + public abstract void setValue(Object value) throws Exception; + + //////////////////////////////////////////////////////////////////////////// + // + // Editor + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link PropertyEditor}. + */ + public final PropertyEditor getEditor() { + return m_editor; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Composite + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the composite {@link Property} for given array of {@link Property}'s or + * <code>null</code> if no composite {@link Property} can be created. + */ + public Property getComposite(Property[] properties) { + return null; + } + + public <T> T getAdapter(Class<T> adapter) { + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Arbitrary values map + // + //////////////////////////////////////////////////////////////////////////// + private Map<Object, Object> m_arbitraryMap; + + /** + * Associates the given value with the given key. + */ + public final void putArbitraryValue(Object key, Object value) { + if (m_arbitraryMap == null) { + m_arbitraryMap = Maps.newHashMap(); + } + m_arbitraryMap.put(key, value); + } + + /** + * @return the value to which the given key is mapped, or <code>null</code>. + */ + public final Object getArbitraryValue(Object key) { + if (m_arbitraryMap != null) { + return m_arbitraryMap.get(key); + } + return null; + } + + /** + * Removes the mapping for a key. + */ + public final void removeArbitraryValue(Object key) { + if (m_arbitraryMap != null) { + m_arbitraryMap.remove(key); + } + } + + // BEGIN ADT MODIFICATIONS + + /** + * Returns the name of the property (which is not always the same as the + * title; for example, the "maxWidth" property has title "Max Width" and + * name "maxWidth". + * <p> + * This is shown in tooltips to users etc to make it clear what they should + * use in their own code. + * + * @return the name of the property + */ + public String getName() { + return getTitle(); + } + + private int mPriority; + + /** + * Gets the custom sort priority of this property + * + * @return the sort priority + */ + public int getPriority() { + return mPriority; + } + + /** + * Sets the custom sort priority of this property + * + * @param priority the new priority to use + */ + public void setPriority(int priority) { + this.mPriority = priority; + } + + /** Sort {@link Property} instances alphabetically by property name */ + public static final Comparator<Property> ALPHABETICAL = new Comparator<Property>() { + @Override + public int compare(Property p1, Property p2) { + return p1.getName().compareTo(p2.getName()); + } + }; + + /** Sort {@link Property} instances by priority */ + public static final Comparator<Property> PRIORITY = new Comparator<Property>() { + @Override + public int compare(Property p1, Property p2) { + int delta = p1.mPriority - p2.mPriority; + if (delta != 0) { + return delta; + } + + return p1.getName().compareTo(p2.getName()); + } + }; + // END ADT MODIFICATIONS +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/PropertyManager.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/PropertyManager.java new file mode 100644 index 0000000..7152999 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/PropertyManager.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property; + +import org.eclipse.wb.internal.core.model.property.category.PropertyCategory; + + +/** + * {@link PropertyManager} is used to get/set attributes of {@link Property}. + * + * @author scheglov_ke + * @coverage core.model.property + */ +public final class PropertyManager { + public static PropertyCategory getCategory(Property property) { + // Note: In WindowBuilder there was a bunch of support for loading custom + // categories here based on toolkits; in ADT we'll need to do it differently + // so this code was all stripped out. + return property.getCategory(); + } + + /** + * @return the forced {@link PropertyCategory} of given Property, may be <code>null</code>. + */ + public static PropertyCategory getCategoryForced(Property property) { + return null; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategory.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategory.java new file mode 100644 index 0000000..a135b03 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategory.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.category; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.check.Assert; + +/** + * Describes category of {@link Property}. + * + * @author scheglov_ke + * @coverage core.model.property + */ +public final class PropertyCategory { + /** + * "Normal" category, used for properties that should be displayed without any effect. + */ + public static final PropertyCategory NORMAL = new PropertyCategory(0, "NORMAL"); + /** + * "Preferred" category, for properties that are most useful for component. + */ + public static final PropertyCategory PREFERRED = new PropertyCategory(-1, "PREFERRED"); + /** + * "Advanced" category, for properties that are rarely used, visible if modified, even if not + * enabled. + */ + public static final PropertyCategory ADVANCED = new PropertyCategory(1, "ADVANCED"); + /** + * "Advanced" category, for properties that are rarely used, visible only if enabled. + */ + public static final PropertyCategory ADVANCED_REALLY = new PropertyCategory(2, "ADVANCED_REALLY"); + /** + * "Hidden" category, for properties that should not be displayed. + */ + public static final PropertyCategory HIDDEN = new PropertyCategory(3, "HIDDEN"); + + //////////////////////////////////////////////////////////////////////////// + // + // System + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the system {@link PropertyCategory} with given priority. + */ + public static final PropertyCategory system(int priority) { + return new PropertyCategory(SYSTEM_BASE + priority, "SYSTEM:" + priority); + } + + /** + * @return the system {@link PropertyCategory} with priority + * <code>system.getPriority() + additional</code>. + */ + public static final PropertyCategory system(PropertyCategory system, int additional) { + Assert.isTrue(system.isSystem()); + return system(system.getPriority() - SYSTEM_BASE + additional); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Instance fields + // + //////////////////////////////////////////////////////////////////////////// + private static final int SYSTEM_BASE = 1000; + private final int m_priority; + private final String m_string; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private PropertyCategory(int priority, String string) { + m_priority = priority; + m_string = string; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Object + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String toString() { + return m_string; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof PropertyCategory) { + PropertyCategory category = (PropertyCategory) obj; + return m_priority == category.m_priority; + } + // unknown class + return false; + } + + @Override + public int hashCode() { + return m_priority; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return <code>true</code> if this property is preferred. + */ + public boolean isPreferred() { + return this == PREFERRED; + } + + /** + * @return <code>true</code> if this property is advanced. + */ + public boolean isAdvanced() { + return this == ADVANCED; + } + + /** + * @return <code>true</code> if this property is really advanced. + */ + public boolean isAdvancedReally() { + return this == ADVANCED_REALLY; + } + + /** + * @return <code>true</code> if this property is hidden. + */ + public boolean isHidden() { + return this == HIDDEN; + } + + /** + * @return <code>true</code> if this property is system. + */ + public boolean isSystem() { + return m_priority >= 900; + } + + /** + * @return the priority of this category. + */ + public int getPriority() { + return m_priority; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProvider.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProvider.java new file mode 100644 index 0000000..b435576 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.category; + +import org.eclipse.wb.internal.core.model.property.Property; + +/** + * This interface is used to get {@link PropertyCategory} for {@link Property}. + * + * @author scheglov_ke + * @coverage core.model.property + */ +public interface PropertyCategoryProvider { + /** + * @return the {@link PropertyCategory} of given Property, not <code>null</code>. + */ + PropertyCategory getCategory(Property property); +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProviders.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProviders.java new file mode 100644 index 0000000..83aaebb --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProviders.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.category; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.PropertyManager; + +/** + * Factory for {@link PropertyCategoryProvider} instances. + * + * @author scheglov_ke + * @coverage core.model.property + */ +public final class PropertyCategoryProviders { + //////////////////////////////////////////////////////////////////////////// + // + // Simple providers + // + //////////////////////////////////////////////////////////////////////////// + private static final PropertyCategoryProvider FROM_PROPERTY = new PropertyCategoryProvider() { + public PropertyCategory getCategory(Property property) { + return property.getCategory(); + } + }; + + /** + * Returns result of {@link Property#getCategory()}, never <code>null</code>. + */ + public static PropertyCategoryProvider fromProperty() { + return FROM_PROPERTY; + } + + private static final PropertyCategoryProvider FORCED_BY_USER = new PropertyCategoryProvider() { + public PropertyCategory getCategory(Property property) { + return PropertyManager.getCategoryForced(property); + } + }; + + /** + * Returns category forced by user, may be <code>null</code>. + */ + public static PropertyCategoryProvider forcedByUser() { + return FORCED_BY_USER; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Compound + // + //////////////////////////////////////////////////////////////////////////// + /** + * Returns first not <code>null</code> category returned by provider. + */ + public static PropertyCategoryProvider combine(final PropertyCategoryProvider... providers) { + return new PropertyCategoryProvider() { + public PropertyCategory getCategory(Property property) { + for (PropertyCategoryProvider provider : providers) { + PropertyCategory category = provider.getCategory(property); + if (category != null) { + return category; + } + } + throw new IllegalStateException("Can not provide category for " + property.getTitle()); + } + }; + } + + private static final PropertyCategoryProvider DEF = combine(forcedByUser(), fromProperty()); + + /** + * Returns the default combination of {@link PropertyCategoryProvider}s - first + * {@link #forcedByUser()}, then {@link #fromProperty()}. + */ + public static PropertyCategoryProvider def() { + return DEF; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboBoxPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboBoxPropertyEditor.java new file mode 100644 index 0000000..f122381 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboBoxPropertyEditor.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.core.controls.CCombo3; +import org.eclipse.wb.core.controls.CComboBox; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; +import org.eclipse.wb.internal.core.utils.execution.RunnableEx; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; + +/** + * The {@link PropertyEditor} for selecting single value using {@link CComboBox}. This editor has + * in-line search-feature and is more suitable (vs {@link AbstractComboPropertyEditor}) for + * properties with large lists of value items. + * + * @author sablin_aa + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public abstract class AbstractComboBoxPropertyEditor extends TextDisplayPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + private CComboBox m_combo; + private String m_dropDelayedText; + + @Override + public final boolean activate(final PropertyTable propertyTable, + final Property property, + Point location) throws Exception { + m_combo = new CComboBox(propertyTable, SWT.NONE); + // initialize + addItems(property, m_combo); + selectItem(property, m_combo); + // install listeners + m_combo.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + handleKeyPressed(propertyTable, property, e); + } + }); + m_combo.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + propertyTable.deactivateEditor(true); + } + }); + m_combo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + propertyTable.deactivateEditor(true); + } + }); + m_combo.setFocus(); + // schedule showing drop-down, because we don't have bounds yet + ExecutionUtils.runAsync(new RunnableEx() { + public void run() throws Exception { + m_combo.comboDropDown(true); + if (m_dropDelayedText != null) { + m_combo.setEditText(m_dropDelayedText); + m_combo.setEditSelection(m_dropDelayedText.length(), m_dropDelayedText.length()); + m_dropDelayedText = null; + } + } + }); + // keep editor active + return true; + } + + private void handleKeyPressed(PropertyTable propertyTable, Property property, KeyEvent e) { + if (e.keyCode == SWT.ESC) { + propertyTable.deactivateEditor(false); + } else if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN) { + e.doit = false; + propertyTable.deactivateEditor(true); + propertyTable.navigate(e); + } + } + + @Override + public final void deactivate(PropertyTable propertyTable, Property property, boolean save) { + if (save) { + toProperty(propertyTable, property); + } + if (m_combo != null) { + m_combo.dispose(); + m_combo = null; + } + } + + private void toProperty(PropertyTable propertyTable, Property property) { + try { + toPropertyEx(property, m_combo); + } catch (Throwable e) { + propertyTable.handleException(e); + } + } + + @Override + public void setBounds(Rectangle bounds) { + m_combo.setBounds(bounds); + } + + @Override + public void keyDown(PropertyTable propertyTable, Property property, KeyEvent event) + throws Exception { + boolean withAlt = (event.stateMask & SWT.ALT) != 0; + boolean withCtrl = (event.stateMask & SWT.CTRL) != 0; + if (event.character > 0x20 && !(withAlt || withCtrl)) { + propertyTable.activateEditor(property, null); + m_dropDelayedText = "" + event.character; + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Abstract methods + // + //////////////////////////////////////////////////////////////////////////// + /** + * Adds items to given {@link CComboBox}. + */ + protected abstract void addItems(Property property, CComboBox combo) throws Exception; + + /** + * Selects current item in given {@link CCombo3}. + */ + protected void selectItem(Property property, CComboBox combo) throws Exception { + } + + /** + * Transfers data from widget to {@link Property}. + */ + protected abstract void toPropertyEx(Property property, CComboBox combo) throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboPropertyEditor.java new file mode 100644 index 0000000..a225f45 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboPropertyEditor.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.core.controls.CCombo3; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** + * The {@link PropertyEditor} for selecting single value using {@link CCombo3}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public abstract class AbstractComboPropertyEditor extends TextDisplayPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + private CCombo3 m_combo; + private boolean m_doDropDown; + + @Override + public boolean activate(final PropertyTable propertyTable, final Property property, Point location) + throws Exception { + // create combo + { + m_combo = new CCombo3(propertyTable, SWT.NONE); + m_doDropDown = true; + // add items + addItems(property, m_combo); + // select item + selectItem(property, m_combo); + } + // add listeners + m_combo.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + propertyTable.deactivateEditor(true); + } + }); + m_combo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + int index = m_combo.getSelectionIndex(); + toProperty(propertyTable, property, index); + } + }); + m_combo.addListener(SWT.KeyDown, new Listener() { + public void handleEvent(Event event) { + switch (event.keyCode) { + case SWT.ESC : + propertyTable.deactivateEditor(false); + break; + case SWT.DEL : + try { + property.setValue(Property.UNKNOWN_VALUE); + event.doit = false; + selectItem(property, m_combo); + } catch (Throwable e) { + propertyTable.handleException(e); + propertyTable.deactivateEditor(false); + } + m_combo.doDropDown(false); + break; + } + } + }); + m_combo.addMouseListener(new MouseAdapter() { + @Override + public void mouseDoubleClick(MouseEvent e) { + int index = (m_combo.getSelectionIndex() + 1) % m_combo.getItemCount(); + toProperty(propertyTable, property, index); + } + }); + // keep editor active + return true; + } + + @Override + public final void setBounds(Rectangle bounds) { + m_combo.setBounds(bounds); + // editor created without bounds, so activate it after first setBounds() + if (m_doDropDown) { + m_doDropDown = false; + m_combo.setFocus(); + m_combo.doDropDown(true); + m_combo.startDrag(); + } + } + + @Override + public final void deactivate(PropertyTable propertyTable, Property property, boolean save) { + if (m_combo != null) { + m_combo.dispose(); + m_combo = null; + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Abstract methods + // + //////////////////////////////////////////////////////////////////////////// + /** + * Adds items to given {@link CCombo3}. + */ + protected abstract void addItems(Property property, CCombo3 combo) throws Exception; + + /** + * Selects current item in given {@link CCombo3}. + */ + protected abstract void selectItem(Property property, CCombo3 combo) throws Exception; + + /** + * Transfers data from widget to {@link Property}. + */ + protected abstract void toPropertyEx(Property property, CCombo3 combo, int index) + throws Exception; + + /** + * Transfers data from widget to {@link Property}. + */ + private void toProperty(PropertyTable propertyTable, Property property, int index) { + try { + toPropertyEx(property, m_combo, index); + } catch (Throwable e) { + propertyTable.handleException(e); + } + propertyTable.deactivateEditor(false); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractListPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractListPropertyEditor.java new file mode 100644 index 0000000..ba34103 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractListPropertyEditor.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.core.controls.CCombo3; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.check.Assert; + +import java.util.List; +import java.util.Map; + +/** + * The {@link PropertyEditor} for selecting single expression from given set. + * + * @author sablin_aa + * @coverage core.model.property.editor + */ +public abstract class AbstractListPropertyEditor extends AbstractComboPropertyEditor + implements + IValueSourcePropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // TextDisplayPropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + // return title for value + Object value = property.getValue(); + if (value != Property.UNKNOWN_VALUE) { + int index = getValueIndex(value); + if (index >= 0) { + return getTitle(index); + } else { + if (value instanceof String) { + return (String) value; + } + } + } + // unknown value + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // IValueSourcePropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + @Override +public String getValueSource(Object value) throws Exception { + // return expression for value + if (value != Property.UNKNOWN_VALUE) { + int index = getValueIndex(value); + if (index >= 0) { + return getExpression(index); + } + } + // unknown value + return null; + } + +// //////////////////////////////////////////////////////////////////////////// +// // +// // IClipboardSourceProvider +// // +// //////////////////////////////////////////////////////////////////////////// +// @Override +//public String getClipboardSource(GenericProperty property) throws Exception { +// Object value = property.getValue(); +// return getValueSource(value); +// } + + //////////////////////////////////////////////////////////////////////////// + // + // Combo + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void addItems(Property property, CCombo3 combo) throws Exception { + for (int i = 0; i < getCount(); i++) { + combo.add(getTitle(i)); + } + } + + @Override + protected void selectItem(Property property, CCombo3 combo) throws Exception { + combo.setText(getText(property)); + } + + @Override + protected void toPropertyEx(Property property, CCombo3 combo, int index) throws Exception { +// if (property instanceof GenericProperty) { +// GenericProperty genericProperty = (GenericProperty) property; +// String expression = getExpression(index); +// Object evaluatedExpression = evaluateExpression(genericProperty, expression); +// // apply expression +// genericProperty.setExpression(expression, evaluatedExpression); +// } else { + toPropertyEx_simpleProperty(property, combo, index); +// } + } + +// private static Object evaluateExpression(final GenericProperty genericProperty, +// final String expression) { +// return ExecutionUtils.runObjectIgnore(new RunnableObjectEx<Object>() { +// public Object runObject() throws Exception { +// JavaInfo javaInfo = genericProperty.getJavaInfo(); +// ClassLoader classLoader = JavaInfoUtils.getClassLoader(javaInfo); +// return ScriptUtils.evaluate(classLoader, expression); +// } +// }, Property.UNKNOWN_VALUE); +// System.out.println("HACK 1234"); +// return Property.UNKNOWN_VALUE; +// } + + /** + * Sets value of simple {@link Property}, not {@link GenericProperty}. + */ + protected void toPropertyEx_simpleProperty(Property property, CCombo3 combo, int index) + throws Exception { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access to list items + // + //////////////////////////////////////////////////////////////////////////// + abstract protected int getCount(); + + abstract protected int getValueIndex(Object value); + + abstract protected String getTitle(int index); + + abstract protected String getExpression(int index) throws Exception; + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * Extract string array from parameters. + */ + protected static String[] getParameterAsArray(Map<String, Object> parameters, String name) { + return getParameterAsArray(parameters, name, false); + } + + @SuppressWarnings("unchecked") + protected static String[] getParameterAsArray(Map<String, Object> parameters, + String name, + boolean noAssert) { + String[] values = null; + if (parameters.containsKey(name)) { + List<String> list = (List<String>) parameters.get(name); + values = list.toArray(new String[list.size()]); + } else { + if (noAssert) { + values = null; + } else { + Assert.fail(String.format("No parameter %s in %s.", name, parameters)); + } + } + return values; + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractTextPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractTextPropertyEditor.java new file mode 100644 index 0000000..1cf9574 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractTextPropertyEditor.java @@ -0,0 +1,306 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.jface.bindings.keys.KeyStroke; +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.fieldassist.IContentProposalProvider; +import org.eclipse.jface.fieldassist.IControlContentAdapter; +import org.eclipse.jface.fieldassist.TextContentAdapter; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Text; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +/** + * Abstract {@link PropertyEditor} for that uses {@link Text} as control. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public abstract class AbstractTextPropertyEditor extends TextDisplayPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + private Text m_textControl; + private boolean m_ignoreFocusLost; + + // BEGIN ADT MODIFICATIONS + // ContentProposalAdapter which exposes the openProposalPopup method such + // that we can open the dialog up immediately on focus gain to show all available + // alternatives (the default implementation requires at least one keytroke before + // it shows up) + private static class ImmediateProposalAdapter extends ContentProposalAdapter { + public ImmediateProposalAdapter(Text control, + IControlContentAdapter controlContentAdapter, + IContentProposalProvider proposalProvider, KeyStroke keyStroke, + char[] autoActivationCharacters) { + super(control, controlContentAdapter, proposalProvider, keyStroke, + autoActivationCharacters); + + // On focus gain, start completing + control.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent event) { + openIfNecessary(); + } + + @Override + public void focusLost(FocusEvent event) { + } + }); + + /* Triggering on empty is disabled for now: it has the unfortunate side-effect + that it's impossible to enter a blank text field - blank matches everything, + so the first item will automatically be selected when you press return. + + + // If you edit the text and delete everything, the normal implementation + // will close the popup; we'll reopen it + control.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent event) { + if (((Text) getControl()).getText().isEmpty()) { + openIfNecessary(); + } + } + }); + */ + } + + private void openIfNecessary() { + getControl().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + if (!isProposalPopupOpen()) { + openProposalPopup(); + } + } + }); + } + } + // END ADT MODIFICATIONS + + @Override + public boolean activate(final PropertyTable propertyTable, final Property property, Point location) + throws Exception { + // create Text + { + m_textControl = new Text(propertyTable, SWT.NONE); + new TextControlActionsManager(m_textControl); + m_textControl.setEditable(isEditable()); + + // BEGIN ADT MODIFICATIONS + // Add support for field completion, if the property provides an IContentProposalProvider + // via its the getAdapter method. + IContentProposalProvider completion = property.getAdapter(IContentProposalProvider.class); + if (completion != null) { + ImmediateProposalAdapter adapter = new ImmediateProposalAdapter( + m_textControl, new TextContentAdapter(), completion, null, null); + adapter.setFilterStyle(ContentProposalAdapter.FILTER_NONE); + adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); + ILabelProvider labelProvider = property.getAdapter(ILabelProvider.class); + if (labelProvider != null) { + adapter.setLabelProvider(labelProvider); + } + } + // END ADT MODIFICATIONS + m_textControl.setFocus(); + } + // add listeners + m_textControl.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + try { + handleKeyPressed(propertyTable, property, e); + } catch (Throwable ex) { + propertyTable.deactivateEditor(false); + propertyTable.handleException(ex); + } + } + }); + m_textControl.addListener(SWT.FocusOut, new Listener() { + @Override + public void handleEvent(Event event) { + if (!m_ignoreFocusLost) { + propertyTable.deactivateEditor(true); + } + } + }); + // set data + toWidget(property); + // keep us active + return true; + } + + @Override + public final void setBounds(Rectangle bounds) { + m_textControl.setBounds(bounds); + } + + @Override + public final void deactivate(PropertyTable propertyTable, Property property, boolean save) { + if (save) { + try { + toProperty(property); + } catch (Throwable e) { + propertyTable.deactivateEditor(false); + propertyTable.handleException(e); + } + } + // dispose Text widget + if (m_textControl != null) { + m_textControl.dispose(); + m_textControl = null; + } + } + + @Override + public void keyDown(PropertyTable propertyTable, Property property, KeyEvent event) + throws Exception { + boolean withAlt = (event.stateMask & SWT.ALT) != 0; + boolean withCtrl = (event.stateMask & SWT.CTRL) != 0; + if (event.character != 0 && !(withAlt || withCtrl)) { + propertyTable.activateEditor(property, null); + postKeyEvent(SWT.KeyDown, event); + postKeyEvent(SWT.KeyUp, event); + } + } + + /** + * Posts low-level {@link SWT.KeyDown} or {@link SWT.KeyUp} event. + */ + private static void postKeyEvent(int type, KeyEvent event) { + Event lowEvent = new Event(); + lowEvent.type = type; + lowEvent.keyCode = event.keyCode; + lowEvent.character = event.character; + // post event + Display.getCurrent().post(lowEvent); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Events + // + //////////////////////////////////////////////////////////////////////////// + /** + * Handles {@link KeyListener#keyPressed(KeyEvent)}. + */ + private void handleKeyPressed(PropertyTable propertyTable, Property property, KeyEvent e) + throws Exception { + if (e.keyCode == SWT.CR) { + toProperty(property); + + // BEGIN ADT MODIFICATIONS + // After pressing return, dismiss the text cursor to make the value "committed". + // I'm not sure why this is necessary here and not in WindowBuilder proper. + propertyTable.deactivateEditor(true); + // END ADT MODIFICATIONS + } else if (e.keyCode == SWT.ESC) { + propertyTable.deactivateEditor(false); + } else if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN) { + e.doit = false; + boolean success = toProperty(property); + // don't allow navigation if current text can not be transferred to property + if (!success) { + return; + } + // OK, deactivate and navigate + propertyTable.deactivateEditor(true); + propertyTable.navigate(e); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation + // + //////////////////////////////////////////////////////////////////////////// + private String m_currentText; + + /** + * Transfers data from {@link Property} to widget. + */ + private void toWidget(Property property) throws Exception { + // prepare text + String text = getEditorText(property); + if (text == null) { + text = ""; + } + // set text + m_currentText = text; + m_textControl.setText(text); + m_textControl.selectAll(); + } + + /** + * Transfers data from widget to {@link Property}. + * + * @return <code>true</code> if transfer was successful. + */ + private boolean toProperty(Property property) throws Exception { + String text = m_textControl.getText(); + // change property only if text was changed + if (!m_currentText.equals(text)) { + m_ignoreFocusLost = true; + try { + boolean success = setEditorText(property, text); + if (!success) { + return false; + } + } finally { + m_ignoreFocusLost = false; + } + // if value was successfully changed, update current text + m_currentText = text; + } + // OK, success + return true; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Operations + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return <code>true</code> if this editor can modify text. + */ + protected boolean isEditable() { + return true; + } + + /** + * @return the text to display in {@link Text} control. + */ + protected abstract String getEditorText(Property property) throws Exception; + + /** + * Modifies {@link Property} using given text. + * + * @return <code>true</code> if {@link Property} was successfully modified. + */ + protected abstract boolean setEditorText(Property property, String text) throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanObjectPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanObjectPropertyEditor.java new file mode 100644 index 0000000..4f80bb9 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanObjectPropertyEditor.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.ui.DrawUtils; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; + +/** + * The {@link PropertyEditor} for <code>Boolean</code>. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class BooleanObjectPropertyEditor extends PropertyEditor { + private static final Image m_nullImage = DesignerPlugin.getImage("properties/BooleanNull.png"); + private static final Image m_trueImage = DesignerPlugin.getImage("properties/true.png"); + private static final Image m_falseImage = DesignerPlugin.getImage("properties/false.png"); + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new BooleanObjectPropertyEditor(); + + private BooleanObjectPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public void paint(Property property, GC gc, int x, int y, int width, int height) throws Exception { + Object value = property.getValue(); + if (value instanceof Boolean) { + boolean booleanValue = ((Boolean) value).booleanValue(); + Image image = booleanValue ? m_trueImage : m_falseImage; + String text = Boolean.toString(booleanValue); + paint(gc, x, y, width, height, text, image); + } + if (value == null) { + Image image = m_nullImage; + String text = "null"; + paint(gc, x, y, width, height, text, image); + } + } + + private void paint(GC gc, int x, int y, int width, int height, String text, Image image) { + // draw image + { + DrawUtils.drawImageCV(gc, image, x, y, height); + // prepare new position/width + int imageWidth = image.getBounds().width + 2; + x += imageWidth; + width -= imageWidth; + } + // draw text + { + DrawUtils.drawStringCV(gc, text, x, y, width, height); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + public boolean activate(PropertyTable propertyTable, Property property, Point location) + throws Exception { + // check that user clicked on image + if (location == null || location.x < m_trueImage.getBounds().width + 2) { + invertValue(property); + } + // don't activate + return false; + } + + @Override + public void doubleClick(Property property, Point location) throws Exception { + invertValue(property); + } + + /** + * Inverts the value of given boolean {@link Property}. + */ + private void invertValue(Property property) throws Exception { + Object value = property.getValue(); + // null + if (value == null) { + property.setValue(true); + return; + } + // boolean + if (value instanceof Boolean) { + boolean booleanValue = ((Boolean) value).booleanValue(); + property.setValue(!booleanValue); + return; + } + // unknown + property.setValue(true); + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanPropertyEditor.java new file mode 100644 index 0000000..6639afe --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanPropertyEditor.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.ui.DrawUtils; + +/** + * The {@link PropertyEditor} for <code>boolean</code>. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class BooleanPropertyEditor extends PropertyEditor { + private static final Image m_trueImage = DesignerPlugin.getImage("properties/true.png"); + private static final Image m_falseImage = DesignerPlugin.getImage("properties/false.png"); + private static final Image m_unknownImage = + DesignerPlugin.getImage("properties/BooleanUnknown.png"); + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new BooleanPropertyEditor(); + + private BooleanPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public void paint(Property property, GC gc, int x, int y, int width, int height) throws Exception { + Object value = property.getValue(); + if (value instanceof Boolean) { + boolean booleanValue = ((Boolean) value).booleanValue(); + Image image = booleanValue ? m_trueImage : m_falseImage; + String text = Boolean.toString(booleanValue); + paint(gc, x, y, width, height, image, text); + } else { + paint(gc, x, y, width, height, m_unknownImage, "unknown"); + } + } + + /** + * Paints {@link Image} and text. + */ + private void paint(GC gc, int x, int y, int width, int height, Image image, String text) { + // draw image + { + DrawUtils.drawImageCV(gc, image, x, y, height); + // prepare new position/width + int imageWidth = image.getBounds().width + 2; + x += imageWidth; + width -= imageWidth; + } + // draw text + DrawUtils.drawStringCV(gc, text, x, y, width, height); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + public boolean activate(PropertyTable propertyTable, Property property, Point location) + throws Exception { + // check that user clicked on image + if (location == null || location.x < m_trueImage.getBounds().width + 2) { + invertValue(property); + } + // don't activate + return false; + } + + @Override + public void doubleClick(Property property, Point location) throws Exception { + invertValue(property); + } + + /** + * Inverts the value of given boolean {@link Property}. + */ + private void invertValue(Property property) throws Exception { + // prepare current boolean value + boolean booleanValue = false; + { + Object value = property.getValue(); + if (value instanceof Boolean) { + booleanValue = ((Boolean) value).booleanValue(); + } + } + // set inverted value + property.setValue(!booleanValue); + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/CharacterPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/CharacterPropertyEditor.java new file mode 100644 index 0000000..7e6cfe8 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/CharacterPropertyEditor.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for {@link String}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class CharacterPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new CharacterPropertyEditor(); + + private CharacterPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof Character) { + return String.valueOf(((Character) value).charValue()); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + return true; + } + // only one character + if (text.length() > 1) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.CharacterPropertyEditor_notValid, text)); + return false; + } + // modify property + property.setValue(new Character(text.charAt(0))); + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoubleObjectPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoubleObjectPropertyEditor.java new file mode 100644 index 0000000..bb7dfc5 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoubleObjectPropertyEditor.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for {@link Double}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class DoubleObjectPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final DoubleObjectPropertyEditor INSTANCE = new DoubleObjectPropertyEditor(); + + private DoubleObjectPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value == null) { + return "null"; + } + if (value instanceof Double) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + return true; + } + // check for "null" + if (text.equals("null")) { + property.setValue(null); + return true; + } + // prepare value + Double value; + try { + value = Double.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.DoubleObjectPropertyEditor_notValidDouble, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoublePropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoublePropertyEditor.java new file mode 100644 index 0000000..40b73bf --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoublePropertyEditor.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for <code>double</code>. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class DoublePropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new DoublePropertyEditor(); + + private DoublePropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof Number) { + double doubleValue = ((Number) value).doubleValue(); + return Double.toString(doubleValue); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + } + // prepare value + Double value; + try { + value = Double.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.DoublePropertyEditor_notValidDouble, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/EnumerationValuesPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/EnumerationValuesPropertyEditor.java new file mode 100644 index 0000000..a715df3 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/EnumerationValuesPropertyEditor.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import com.google.common.base.Objects; + +import org.eclipse.wb.core.controls.CCombo3; +import org.eclipse.wb.internal.core.model.property.Property; + +import java.beans.PropertyDescriptor; + +/** + * {@link PropertyEditor} for "enumerationValues" attribute of {@link PropertyDescriptor}. + * <p> + * See http://javadude.com/articles/javabeanattributes.html for attributes. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class EnumerationValuesPropertyEditor extends AbstractComboPropertyEditor + implements + IValueSourcePropertyEditor { + private final String[] m_names; + private final Object[] m_values; + private final String[] m_sources; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public EnumerationValuesPropertyEditor(Object attributeValue) { + Object[] enumElements = (Object[]) attributeValue; + int items = enumElements.length / 3; + m_names = new String[items]; + m_values = new Object[items]; + m_sources = new String[items]; + for (int i = 0; i < items; i++) { + m_names[i] = (String) enumElements[3 * i + 0]; + m_values[i] = enumElements[3 * i + 1]; + m_sources[i] = (String) enumElements[3 * i + 2]; + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // TextDisplayPropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + // return name for value + if (value != Property.UNKNOWN_VALUE) { + for (int i = 0; i < m_values.length; i++) { + if (Objects.equal(m_values[i], value)) { + return m_names[i]; + } + } + } + // unknown value + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // IValueSourcePropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + @Override +public String getValueSource(Object value) throws Exception { + if (value != Property.UNKNOWN_VALUE) { + for (int i = 0; i < m_values.length; i++) { + if (Objects.equal(m_values[i], value)) { + return m_sources[i]; + } + } + } + // unknown value + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // IClipboardSourceProvider + // + //////////////////////////////////////////////////////////////////////////// +// public String getClipboardSource(GenericProperty property) throws Exception { +// Object value = property.getValue(); +// return getValueSource(value); +// } + + //////////////////////////////////////////////////////////////////////////// + // + // Combo + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void addItems(Property property, CCombo3 combo) throws Exception { + for (String title : m_names) { + combo.add(title); + } + } + + @Override + protected void selectItem(Property property, CCombo3 combo) throws Exception { + combo.setText(getText(property)); + } + + @Override + protected void toPropertyEx(Property property, CCombo3 combo, int index) throws Exception { + Object value = m_values[index]; +// if (property instanceof GenericProperty) { +// GenericProperty genericProperty = (GenericProperty) property; +// String source = getValueSource(value); +// genericProperty.setExpression(source, value); +// } else { + property.setValue(value); +// } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/FloatPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/FloatPropertyEditor.java new file mode 100644 index 0000000..d4b468e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/FloatPropertyEditor.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for <code>float</code>. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class FloatPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new FloatPropertyEditor(); + + protected FloatPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof Float) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + } + // prepare value + Float value; + try { + value = Float.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.FloatPropertyEditor_notValidFloat, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ITextValuePropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ITextValuePropertyEditor.java new file mode 100644 index 0000000..ed38c04 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ITextValuePropertyEditor.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.model.property.Property; + +/** + * Extension of {@link PropertyEditor} that can set value using its text presentation. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public interface ITextValuePropertyEditor { + /** + * Sets value that corresponds given text. + */ + void setText(Property property, String text) throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IValueSourcePropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IValueSourcePropertyEditor.java new file mode 100644 index 0000000..2999bf1 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IValueSourcePropertyEditor.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +/** + * Extension for {@link PropertyEditor} that can be used to convert {@link Object} value into Java + * source. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public interface IValueSourcePropertyEditor { + /** + * @return the Java source for given value. + */ + String getValueSource(Object value) throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerObjectPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerObjectPropertyEditor.java new file mode 100644 index 0000000..f488dff --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerObjectPropertyEditor.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for {@link Integer}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class IntegerObjectPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final IntegerObjectPropertyEditor INSTANCE = new IntegerObjectPropertyEditor(); + + private IntegerObjectPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value == null) { + return "null"; + } + if (value instanceof Integer) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + return true; + } + // check for "null" + if (text.equals("null")) { + property.setValue(null); + return true; + } + // prepare value + Integer value; + try { + value = Integer.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.IntegerObjectPropertyEditor_notValidInt, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerPropertyEditor.java new file mode 100644 index 0000000..5be13da --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerPropertyEditor.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for <code>int</code>. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class IntegerPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final IntegerPropertyEditor INSTANCE = new IntegerPropertyEditor(); + + private IntegerPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof Integer) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + return true; + } + // prepare value + Integer value; + try { + value = Integer.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.IntegerPropertyEditor_notValidInt, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LocalePropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LocalePropertyEditor.java new file mode 100644 index 0000000..9a6563d --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LocalePropertyEditor.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.model.property.Property; + +import java.util.Locale; + +/** + * {@link PropertyEditor} for {@link Locale}. + * + * @author sablin_aa + * @coverage core.model.property.editor + */ +public final class LocalePropertyEditor extends TextDialogPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new LocalePropertyEditor(); + + private LocalePropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof Locale) { + return ((Locale) value).getDisplayName(); + } + // unknown value + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void openDialog(Property property) throws Exception { + Object value = property.getValue(); +// ChooseLocaleDialog localeDialog; +// if (value instanceof Locale) { +// localeDialog = new ChooseLocaleDialog(DesignerPlugin.getShell(), (Locale) value); +// } else { +// localeDialog = new ChooseLocaleDialog(DesignerPlugin.getShell(), null); +// } +// // open dialog +// if (localeDialog.open() == Window.OK) { +// property.setValue(localeDialog.getSelectedLocale().getLocale()); +// } + System.out.println("TODO: Custom locale chooser here"); + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongObjectPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongObjectPropertyEditor.java new file mode 100644 index 0000000..7a74ded --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongObjectPropertyEditor.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for {@link Long}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class LongObjectPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final LongObjectPropertyEditor INSTANCE = new LongObjectPropertyEditor(); + + private LongObjectPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value == null) { + return "null"; + } + if (value instanceof Long) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + return true; + } + // check for "null" + if (text.equals("null")) { + property.setValue(null); + return true; + } + // prepare value + Long value; + try { + value = Long.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.LongObjectPropertyEditor_notValidLong, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongPropertyEditor.java new file mode 100644 index 0000000..8c17f83 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongPropertyEditor.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for <code>long</code>. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class LongPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new LongPropertyEditor(); + + private LongPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof Long) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + } + // prepare value + Long value; + try { + value = Long.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.LongPropertyEditor_notValidLong, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyDescriptorEditorProvider.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyDescriptorEditorProvider.java new file mode 100644 index 0000000..a60b698 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyDescriptorEditorProvider.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import java.beans.PropertyDescriptor; + +/** + * {@link PropertyEditorProvider} that creates editors based on {@link PropertyDescriptor} + * attributes, such as "enumerationValues". + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class PropertyDescriptorEditorProvider extends PropertyEditorProvider { + //////////////////////////////////////////////////////////////////////////// + // + // PropertyEditorProvider + // + //////////////////////////////////////////////////////////////////////////// + @Override + public PropertyEditor getEditorForPropertyDescriptor(PropertyDescriptor descriptor) + throws Exception { + { + Object attributeValue = descriptor.getValue("enumerationValues"); + if (isEnumerationProperty(descriptor)) { + return new EnumerationValuesPropertyEditor(attributeValue); + } + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return <code>true</code> if given {@link PropertyDescriptor} has attribute "enumerationValues" + * with valid value structure. + */ + private static boolean isEnumerationProperty(PropertyDescriptor descriptor) { + Object attributeValue = descriptor.getValue("enumerationValues"); + // should be Object[] + if (!(attributeValue instanceof Object[])) { + return false; + } + Object[] enumElements = (Object[]) attributeValue; + // should be multiple 3 + if (enumElements.length % 3 != 0) { + return false; + } + // elements should be sequence of [String,Object,String] + for (int i = 0; i < enumElements.length; i++) { + Object element = enumElements[i]; + if (i % 3 == 0 && !(element instanceof String)) { + return false; + } + if (i % 3 == 2 && !(element instanceof String)) { + return false; + } + } + // OK + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditor.java new file mode 100644 index 0000000..fd2fa8f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditor.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.presentation.PropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +/** + * Abstract editor for {@link Property}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public abstract class PropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the instance of {@link PropertyEditorPresentation}. + */ + public PropertyEditorPresentation getPresentation() { + return null; + } + + /** + * Paints given {@link Property} given rectangle <code>(x, y, width, height)</code> of {@link GC}. + */ + public abstract void paint(Property property, GC gc, int x, int y, int width, int height) + throws Exception; + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + /** + * Activates editor for given {@link Property} at given place of {@link Composite}. Activation + * happens when user selects property in {@link PropertyTable}. {@link PropertyEditor} should + * create here any {@link Control}'s required to edit {@link Property}. + * + * If any exception happens, {@link PropertyEditor} will be deactivated. + * + * @param location + * the mouse location, if editor is activated using mouse click, or <code>null</code> if + * it is activated using keyboard. + * + * @return <code>true</code> if editor should be remembered as active for future + * {@link #setBounds(Rectangle)} and {@link #deactivate(boolean)} invocation. Some editors + * need such local activation (for example for String), some - not (for boolean). + */ + public boolean activate(PropertyTable propertyTable, Property property, Point location) + throws Exception { + return false; + } + + /** + * Sets the new bounds for editor's control. + */ + public void setBounds(Rectangle bounds) { + } + + /** + * Deactivates editor for current {@link Property}. {@link PropertyEditor} should dispose any + * {@link Control}'s created before in {@link #activate(PropertyTable, Property, Point)}. + * + * If any exception happened during activation, editor still should be able to deactivate + * correctly. + * + * @param save + * is <code>true</code> if property should save value to {@link Property}. + */ + public void deactivate(PropertyTable propertyTable, Property property, boolean save) { + } + + /** + * Handles double click on {@link Property} value in {@link PropertyTable}. + * + * @param location + * the mouse location, relative to editor + */ + public void doubleClick(Property property, Point location) throws Exception { + } + + /** + * Handles {@link SWT#KeyDown} event in {@link PropertyTable}. + */ + public void keyDown(PropertyTable propertyTable, Property property, KeyEvent event) + throws Exception { + } + + //////////////////////////////////////////////////////////////////////////// + // + // IAdaptable + // + //////////////////////////////////////////////////////////////////////////// + public <T> T getAdapter(Class<T> adapter) { + return null; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditorProvider.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditorProvider.java new file mode 100644 index 0000000..2d11cbc --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditorProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import java.beans.PropertyDescriptor; + +/** + * Provider for creating {@link PropertyEditor}'s. + * + * @author lobas_av + * @coverage core.model.property.editor + */ +public class PropertyEditorProvider { + /** + * @return the {@link PropertyEditor} for given property type or <code>null</code>. + */ + public PropertyEditor getEditorForType(Class<?> propertyType) throws Exception { + return null; + } + + /** + * @return the {@link PropertyEditor} for given {@link java.beans.PropertyEditor} editor type or + * <code>null</code>. + */ + public PropertyEditor getEditorForEditorType(Class<?> editorType) throws Exception { + return null; + } + + /** + * @return the {@link PropertyEditor} for given {@link PropertyDescriptor} or <code>null</code>. + */ + public PropertyEditor getEditorForPropertyDescriptor(PropertyDescriptor descriptor) + throws Exception { + return null; + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortObjectPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortObjectPropertyEditor.java new file mode 100644 index 0000000..c1f8383 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortObjectPropertyEditor.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for {@link Short}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class ShortObjectPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final ShortObjectPropertyEditor INSTANCE = new ShortObjectPropertyEditor(); + + private ShortObjectPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value == null) { + return "null"; + } + if (value instanceof Short) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + return true; + } + // check for "null" + if (text.equals("null")) { + property.setValue(null); + return true; + } + // prepare value + Short value; + try { + value = Short.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.ShortObjectPropertyEditor_notValidShort, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortPropertyEditor.java new file mode 100644 index 0000000..dba61c9 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortPropertyEditor.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.UiUtils; + +import java.text.MessageFormat; + +/** + * The {@link PropertyEditor} for <code>short</code>. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class ShortPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new ShortPropertyEditor(); + + private ShortPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof Short) { + return value.toString(); + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + text = text.trim(); + // check for delete + if (text.length() == 0) { + property.setValue(Property.UNKNOWN_VALUE); + } + // prepare value + Short value; + try { + value = Short.valueOf(text); + } catch (Throwable e) { + UiUtils.openWarning( + DesignerPlugin.getShell(), + property.getTitle(), + MessageFormat.format(ModelMessages.ShortPropertyEditor_notValidShort, text)); + return false; + } + // modify property + property.setValue(value); + return true; + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringArrayPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringArrayPropertyEditor.java new file mode 100644 index 0000000..fd78e01 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringArrayPropertyEditor.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import com.google.common.base.Joiner; + +import org.eclipse.jface.window.Window; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.dialogs.StringsDialog; + +/** + * {@link PropertyEditor} for array of {@link String}'s. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public final class StringArrayPropertyEditor extends TextDialogPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new StringArrayPropertyEditor(); + + private StringArrayPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getText(Property property) throws Exception { + String[] items = getItems(property); + return "[" + Joiner.on(", ").join(items) + "]"; + } + + /** + * @return the items specified in value of given {@link Property}. + */ + private static String[] getItems(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof String[]) { + return (String[]) value; + } + // no items + return new String[0]; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void openDialog(Property property) throws Exception { + StringsDialog itemsDialog = + new StringsDialog(DesignerPlugin.getShell(), + DesignerPlugin.getDefault(), + property.getTitle(), + ModelMessages.StringArrayPropertyEditor_itemsLabel, + ModelMessages.StringArrayPropertyEditor_hint); + itemsDialog.setItems(getItems(property)); + // open dialog + if (itemsDialog.open() == Window.OK) { + property.setValue(itemsDialog.getItems()); + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringComboPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringComboPropertyEditor.java new file mode 100644 index 0000000..1861475 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringComboPropertyEditor.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.core.controls.CCombo3; +import org.eclipse.wb.internal.core.model.property.Property; + +/** + * The {@link PropertyEditor} for selecting single {@link String} value from given array. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class StringComboPropertyEditor extends AbstractComboPropertyEditor { + private final String[] m_items; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public StringComboPropertyEditor(String... items) { + m_items = items; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getText(Property property) throws Exception { + return (String) property.getValue(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // AbstractComboPropertyEditor + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void addItems(Property property, CCombo3 combo) throws Exception { + for (String item : m_items) { + combo.add(item); + } + } + + @Override + protected void selectItem(Property property, CCombo3 combo) throws Exception { + combo.setText(getText(property)); + } + + @Override + protected void toPropertyEx(Property property, CCombo3 combo, int index) throws Exception { + property.setValue(m_items[index]); + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringListPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringListPropertyEditor.java new file mode 100644 index 0000000..1bb8cd8 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringListPropertyEditor.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.core.controls.CCombo3; +import org.eclipse.wb.internal.core.model.property.Property; + +/** + * The {@link PropertyEditor} for selecting single string from given set. + * + * @author sablin_aa + * @coverage core.model.property.editor + */ +public final class StringListPropertyEditor extends AbstractListPropertyEditor { + private boolean m_ignoreCase; + private String[] m_strings; + + //////////////////////////////////////////////////////////////////////////// + // + // Combo + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void toPropertyEx_simpleProperty(Property property, CCombo3 combo, int index) + throws Exception { + property.setValue(m_strings[index]); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access to list items + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected int getCount() { + return m_strings.length; + } + + @Override + protected int getValueIndex(Object value) { + if (value instanceof String) { + String string = (String) value; + for (int i = 0; i < getCount(); i++) { + if (m_ignoreCase) { + if (string.equalsIgnoreCase(m_strings[i])) { + return i; + } + } else { + if (string.equals(m_strings[i])) { + return i; + } + } + } + } + return -1; + } + + @Override + protected String getTitle(int index) { + return m_strings[index]; + } + + @Override + protected String getExpression(int index) throws Exception { + //return StringConverter.INSTANCE.toJavaSource(null, m_strings[index]); + // HACK!! + System.out.println("HACK!"); + return m_strings[index]; + } +// +// //////////////////////////////////////////////////////////////////////////// +// // +// // IConfigurablePropertyObject +// // +// //////////////////////////////////////////////////////////////////////////// +// public void configure(EditorState state, Map<String, Object> parameters) throws Exception { +// m_strings = getParameterAsArray(parameters, "strings"); +// m_ignoreCase = "true".equals(parameters.get("ignoreCase")); +// } +// +// /** +// * Configures this editor externally. +// */ +// public void configure(String[] strings) { +// m_strings = strings; +// } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextControlActionsManager.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextControlActionsManager.java new file mode 100644 index 0000000..dc7ba74 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextControlActionsManager.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.utils.binding.editors.controls.DefaultControlActionsManager; + +import org.eclipse.swt.widgets.Text; + +/** + * Manager for installing/unistalling global handlers for {@link Text} actions commands. + * + * @author mitin_aa + * @author sablin_aa + * @coverage core.model.property.editor + */ +public final class TextControlActionsManager extends DefaultControlActionsManager { + private final Text m_text; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public TextControlActionsManager(final Text text) { + super(text); + m_text = text; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Handlers + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void selectAllExecuted() { + m_text.selectAll(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDialogPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDialogPropertyEditor.java new file mode 100644 index 0000000..885d4ef --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDialogPropertyEditor.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.presentation.ButtonPropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.editor.presentation.PropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import org.eclipse.swt.graphics.Point; + +/** + * Abstract {@link PropertyEditor} that displays text and button to open dialog. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public abstract class TextDialogPropertyEditor extends TextDisplayPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + private final PropertyEditorPresentation m_presentation = new ButtonPropertyEditorPresentation() { + @Override + protected void onClick(PropertyTable propertyTable, Property property) throws Exception { + openDialog(property); + } + }; + + @Override + public final PropertyEditorPresentation getPresentation() { + return m_presentation; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + public boolean activate(PropertyTable propertyTable, Property property, Point location) + throws Exception { + // activate using keyboard + if (location == null) { + openDialog(property); + } + // don't activate + return false; + } + + /** + * Opens editing dialog. + */ + protected abstract void openDialog(Property property) throws Exception; +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDisplayPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDisplayPropertyEditor.java new file mode 100644 index 0000000..9cf3703 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDisplayPropertyEditor.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor; + +import org.eclipse.swt.graphics.GC; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipProvider; +import org.eclipse.wb.internal.core.utils.ui.DrawUtils; + +/** + * Abstract {@link PropertyEditor} for displaying text as {@link Property} value in + * {@link PropertyTable}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public abstract class TextDisplayPropertyEditor extends PropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public void paint(Property property, GC gc, int x, int y, int width, int height) throws Exception { + String text = getText(property); + if (text != null) { + DrawUtils.drawStringCV(gc, text, x, y, width, height); + } + } + + /** + * @return the text for displaying value of given {@link Property} or <code>null</code> if value + * of {@link Property} is unknown. + */ + protected abstract String getText(Property property) throws Exception; + + //////////////////////////////////////////////////////////////////////////// + // + // IAdaptable + // + //////////////////////////////////////////////////////////////////////////// + @Override + public <T> T getAdapter(Class<T> adapter) { + // tooltip for value text + if (adapter == PropertyTooltipProvider.class) { + return adapter.cast(createPropertyTooltipProvider()); + } + return super.getAdapter(adapter); + } + + /** + * @return the {@link PropertyTooltipProvider} to display value tooltip. + */ + protected PropertyTooltipProvider createPropertyTooltipProvider() { + return null; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/complex/IComplexPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/complex/IComplexPropertyEditor.java new file mode 100644 index 0000000..0634cfe --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/complex/IComplexPropertyEditor.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.complex; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; + +/** + * Extension for {@link PropertyEditor} that specifies that it has sub-properties. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public interface IComplexPropertyEditor { + /** + * @return sub-properties of given complex property. + */ + Property[] getProperties(Property property) throws Exception; +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentation.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentation.java new file mode 100644 index 0000000..e33970d --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentation.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.presentation; + +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.EnvironmentUtils; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Button; + +/** + * Implementation of {@link PropertyEditorPresentation} for displaying {@link Button}. + * + * @author scheglov_ke + * @author mitin_aa + * @coverage core.model.property.editor + */ +public abstract class ButtonPropertyEditorPresentation extends PropertyEditorPresentation { + private final int m_style; + private final ButtonPropertyEditorPresentationImpl m_impl; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + //////////////////////////////////////////////////////////////////////////// + public ButtonPropertyEditorPresentation() { + this(SWT.NONE); + } + + public ButtonPropertyEditorPresentation(int style) { + m_style = style; + m_impl = + EnvironmentUtils.IS_MAC + ? new ButtonPropertyEditorPresentationImplMac(this) + : new ButtonPropertyEditorPresentationImpl(this); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets "selection" property of {@link Button}. + */ + public final void setSelection(PropertyTable propertyTable, Property property, boolean selected) { + m_impl.setSelection(propertyTable, property, selected); + } + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyEditorPresentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public final int show(final PropertyTable propertyTable, + final Property property, + final int x, + final int y, + final int width, + final int height) { + return m_impl.show(propertyTable, property, x, y, width, height); + } + + @Override + public final void hide(PropertyTable propertyTable, Property property) { + m_impl.hide(propertyTable, property); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + final int getStyle() { + return m_style; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Image} to display on {@link Button}. + */ + protected Image getImage() { + return DesignerPlugin.getImage("properties/dots.gif"); + } + + /** + * @return the tooltip text to display for {@link Button}. + */ + protected String getTooltip() { + return null; + } + + /** + * Handles click on {@link Button}. + */ + protected abstract void onClick(PropertyTable propertyTable, Property property) throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImpl.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImpl.java new file mode 100644 index 0000000..209bf39 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImpl.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.presentation; + +import com.google.common.collect.Maps; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.Pair; + +import java.util.Map; + +/** + * Internal implementation of {@link PropertyEditorPresentation} for displaying {@link Button}. + * + * @author scheglov_ke + * @author mitin_aa + * @coverage core.model.property.editor + */ +class ButtonPropertyEditorPresentationImpl extends PropertyEditorPresentation { + protected final PropertyToControlMap m_propertyToControl = new PropertyToControlMap(); + private final ButtonPropertyEditorPresentation m_presentation; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ButtonPropertyEditorPresentationImpl(ButtonPropertyEditorPresentation presentation) { + m_presentation = presentation; + } + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyEditorPresentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public final void hide(PropertyTable propertyTable, Property property) { + Control control = m_propertyToControl.remove(propertyTable, property); + if (control != null) { + control.dispose(); + } + } + + @Override + public final int show(PropertyTable propertyTable, + Property property, + int x, + int y, + int width, + int height) { + // prepare control + Control control = m_propertyToControl.get(propertyTable, property); + if (control == null) { + control = createControl(propertyTable, property); + } + // set bounds + final int controlWidth = height; + final int controlX = x + width - controlWidth; + setBounds(control, controlX, y, controlWidth, height); + return controlWidth; + } + + /** + * Finds and select the appropriate {@link Control} belonging to given property. + */ + public void setSelection(PropertyTable propertyTable, Property property, boolean selected) { + Button button = (Button) m_propertyToControl.get(propertyTable, property); + if (button != null) { + button.setSelection(selected); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Control + // + //////////////////////////////////////////////////////////////////////////// + /** + * Creates the control for given property and initializes newly created control. + */ + private Control createControl(final PropertyTable propertyTable, final Property property) { + Control control = createControlImpl(propertyTable, property); + m_propertyToControl.put(propertyTable, property, control); + // when Control disposed, remove Control/Property from map to avoid memory leak + control.addListener(SWT.Dispose, new Listener() { + @Override + public void handleEvent(Event e) { + m_propertyToControl.remove(propertyTable, property); + } + }); + // activate property on mouse down + control.addListener(SWT.MouseDown, new Listener() { + @Override + public void handleEvent(Event event) { + propertyTable.deactivateEditor(true); + propertyTable.setActiveProperty(property); + } + }); + // return focus on propertyTable after click + control.addListener(SWT.MouseUp, new Listener() { + @Override + public void handleEvent(Event event) { + propertyTable.forceFocus(); + } + }); + // handle selection + control.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + try { + getPresentation().onClick(propertyTable, property); + } catch (Throwable e) { + propertyTable.deactivateEditor(false); + propertyTable.handleException(e); + } + } + }); + return control; + } + + /** + * Creates the {@link Control} instance. By default, {@link Button} instance created. + */ + protected Control createControlImpl(final PropertyTable propertyTable, final Property property) { + Button button = new Button(propertyTable, getPresentation().getStyle()); + button.setImage(getPresentation().getImage()); + button.setToolTipText(getPresentation().getTooltip()); + return button; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the 'parent' presentation. Internal usage only. + */ + protected final ButtonPropertyEditorPresentation getPresentation() { + return m_presentation; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets new bounds for {@link Control}, optimizing when possible. + */ + private static void setBounds(Control control, int newX, int newY, int newWidth, int newHeight) { + // check, may be Control is invisible, so no reason to change bounds + { + // is in negative zone + if (newY + newHeight < 0) { + control.setVisible(false); + return; + } + // is out of client area height + Rectangle parentArea = control.getParent().getClientArea(); + if (newY > parentArea.height) { + control.setVisible(false); + return; + } + } + // well, now we sure that Control is visible + if (!control.getVisible()) { + control.setVisible(true); + } + // prepare old size, remember new + Integer oldWidthObject = (Integer) control.getData("oldWidth"); + Integer oldHeightObject = (Integer) control.getData("oldHeight"); + control.setData("oldWidth", newWidth); + control.setData("oldHeight", newHeight); + // check, may be same size + if (oldWidthObject != null) { + int oldWidth = oldWidthObject.intValue(); + int oldHeight = oldHeightObject.intValue(); + if (oldWidth == newWidth && oldHeight == newHeight) { + control.setLocation(newX, newY); + return; + } + } + // no any optimization possible, just set bounds + control.setBounds(newX, newY, newWidth, newHeight); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Controls map + // + //////////////////////////////////////////////////////////////////////////// + protected static final class PropertyToControlMap { + private final Map<Pair<PropertyTable, Property>, Control> m_map = Maps.newHashMap(); + + void put(PropertyTable propertyTable, Property property, Control control) { + m_map.put(Pair.create(propertyTable, property), control); + } + + Control remove(PropertyTable propertyTable, Property property) { + return m_map.remove(Pair.create(propertyTable, property)); + } + + Control get(PropertyTable propertyTable, Property property) { + return m_map.get(Pair.create(propertyTable, property)); + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImplMac.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImplMac.java new file mode 100644 index 0000000..d61a606 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImplMac.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.presentation; + +import org.eclipse.wb.core.controls.CFlatButton; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; + +/** + * Internal implementation of {@link PropertyEditorPresentation} for displaying special owner-draw + * button for Mac OSX. + * + * @author mitin_aa + * @coverage core.model.property.editor + */ +final class ButtonPropertyEditorPresentationImplMac extends ButtonPropertyEditorPresentationImpl { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ButtonPropertyEditorPresentationImplMac(ButtonPropertyEditorPresentation presentation) { + super(presentation); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Control + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected final Control createControlImpl(final PropertyTable propertyTable, Property property) { + CFlatButton button = new CFlatButton(propertyTable, SWT.NONE); + button.setImage(getPresentation().getImage()); + button.setToolTipText(getPresentation().getTooltip()); + return button; + } + + @Override + public final void setSelection(PropertyTable propertyTable, Property property, boolean selected) { + CFlatButton button = (CFlatButton) m_propertyToControl.get(propertyTable, property); + if (button != null) { + button.setSelected(selected); + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/CompoundPropertyEditorPresentation.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/CompoundPropertyEditorPresentation.java new file mode 100644 index 0000000..39bfa7a --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/CompoundPropertyEditorPresentation.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.presentation; + +import com.google.common.collect.Lists; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import java.util.List; + +/** + * Implementation of {@link PropertyEditorPresentation} that contains zero or more other + * {@link PropertyEditorPresentation}'s. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class CompoundPropertyEditorPresentation extends PropertyEditorPresentation { + private final List<PropertyEditorPresentation> m_presentations = Lists.newArrayList(); + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Adds child {@link PropertyEditorPresentation}.<br> + * Child {@link PropertyEditorPresentation}'s are displayed from right to left. + */ + public void add(PropertyEditorPresentation presentation) { + m_presentations.add(presentation); + } + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyEditorPresentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public int show(PropertyTable propertyTable, + Property property, + int x, + int y, + int width, + int height) { + int sumWidth = 0; + for (PropertyEditorPresentation presentation : m_presentations) { + int presentationWidth = presentation.show(propertyTable, property, x, y, width, height); + sumWidth += presentationWidth; + width -= presentationWidth; + } + return sumWidth; + } + + @Override + public void hide(PropertyTable propertyTable, Property property) { + for (PropertyEditorPresentation presentation : m_presentations) { + presentation.hide(propertyTable, property); + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/PropertyEditorPresentation.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/PropertyEditorPresentation.java new file mode 100644 index 0000000..843338e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/PropertyEditorPresentation.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.presentation; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +/** + * Implementations of {@link PropertyEditorPresentation} are used to show some presentation for + * visible, but not activated yet {@link PropertyEditor}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public abstract class PropertyEditorPresentation { + /** + * Shows presentation for given {@link Property}. + * + * @return the width that this presentation occupies on the right of given rectangle. + */ + public abstract int show(PropertyTable propertyTable, + Property property, + int x, + int y, + int width, + int height); + + /** + * Hides presentation. + */ + public abstract void hide(PropertyTable propertyTable, Property property); +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyDialog.java new file mode 100644 index 0000000..2b0d47e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyDialog.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.string; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.ModelMessages; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; +import org.eclipse.wb.internal.core.utils.execution.RunnableEx; +import org.eclipse.wb.internal.core.utils.ui.GridDataFactory; +import org.eclipse.wb.internal.core.utils.ui.dialogs.ResizableDialog; + +/** + * {@link Dialog} for editing value in {@link StringPropertyEditor}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class StringPropertyDialog extends ResizableDialog { + // NOTE: In WindowBuilder this class had a lot of support for + // editing Java strings, dealing with automatic localization + // etc. This will need to be done differently in ADT (and had hooks + // into a bunch of other parts of WindowBuilder we're not including) + // so this was all stripped down to a plain String editor. + + //////////////////////////////////////////////////////////////////////////// + // + // Final fields + // + //////////////////////////////////////////////////////////////////////////// + private final Property m_property; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public StringPropertyDialog(Shell parentShell, Property property) throws Exception { + super(parentShell, DesignerPlugin.getDefault()); + m_property = property; + } + + //////////////////////////////////////////////////////////////////////////// + // + // GUI + // + //////////////////////////////////////////////////////////////////////////// + private Text m_valueText; + + @Override + public void create() { + super.create(); + m_valueText.selectAll(); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite area = (Composite) super.createDialogArea(parent); + // value + { + // BEGIN ADT MODIFICATIONS + if (isMultiLine()) { + // END ADT MODIFICATIONS + m_valueText = new Text(area, SWT.BORDER | SWT.MULTI | SWT.WRAP); + GridDataFactory.create(m_valueText).grab().hintC(80, 8).fill(); + // BEGIN ADT MODIFICATIONS + } else { + m_valueText = new Text(area, SWT.BORDER | SWT.SINGLE); + GridDataFactory.create(m_valueText).grab().hintC(50, 1).fill(); + } + // END ADT MODIFICATIONS + // initial value + ExecutionUtils.runLog(new RunnableEx() { + @Override + public void run() throws Exception { + Object value = m_property.getValue(); + if (value instanceof String) { + m_valueText.setText((String) value); + } + } + }); + // handle Ctrl+Enter as OK + m_valueText.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.stateMask == SWT.CTRL && e.keyCode == SWT.CR) { + okPressed(); + } + } + }); + } + + return area; + } + + // BEGIN ADT MODIFICATIONS + protected boolean isMultiLine() { + return true; + } + // END ADT MODIFICATIONS + + //////////////////////////////////////////////////////////////////////////// + // + // Shell + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(ModelMessages.StringPropertyDialog_title); + } + + @Override + protected void okPressed() { + final String value = m_valueText.getText(); + ExecutionUtils.runLog(new RunnableEx() { + @Override + public void run() throws Exception { + m_property.setValue(value); + } + }); + // close dialog + super.okPressed(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyEditor.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyEditor.java new file mode 100644 index 0000000..0202fe5 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyEditor.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.editor.string; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.AbstractTextPropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.presentation.ButtonPropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.editor.presentation.PropertyEditorPresentation; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; + +import org.eclipse.jface.window.Window; + +/** + * The {@link PropertyEditor} for {@link String}. + * + * @author scheglov_ke + * @coverage core.model.property.editor + */ +public class StringPropertyEditor extends AbstractTextPropertyEditor { + //////////////////////////////////////////////////////////////////////////// + // + // Instance + // + //////////////////////////////////////////////////////////////////////////// + public static final PropertyEditor INSTANCE = new StringPropertyEditor(); + + private StringPropertyEditor() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + private final PropertyEditorPresentation m_presentation = new ButtonPropertyEditorPresentation() { + @Override + protected void onClick(PropertyTable propertyTable, Property property) throws Exception { + openDialog(propertyTable, property); + } + }; + + @Override + public PropertyEditorPresentation getPresentation() { + return m_presentation; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + //////////////////////////////////////////////////////////////////////////// + @Override + public String getText(Property property) throws Exception { + Object value = property.getValue(); + if (value instanceof String) { + return (String) value; + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected String getEditorText(Property property) throws Exception { + return getText(property); + } + + @Override + protected boolean setEditorText(Property property, String text) throws Exception { + property.setValue(text); + return true; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editing in dialog + // + //////////////////////////////////////////////////////////////////////////// + /** + * Opens editing dialog. + */ + private void openDialog(PropertyTable propertyTable, Property property) throws Exception { + StringPropertyDialog dialog = new StringPropertyDialog(propertyTable.getShell(), property); + if (dialog.open() == Window.OK) { + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/HtmlTooltipHelper.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/HtmlTooltipHelper.java new file mode 100644 index 0000000..5e05549 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/HtmlTooltipHelper.java @@ -0,0 +1,332 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.table; + +import com.google.common.base.Charsets; +import com.google.common.base.Joiner; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.browser.LocationAdapter; +import org.eclipse.swt.browser.LocationEvent; +import org.eclipse.swt.browser.ProgressAdapter; +import org.eclipse.swt.browser.ProgressEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.browser.IWebBrowser; +import org.eclipse.ui.browser.IWorkbenchBrowserSupport; +import org.eclipse.wb.draw2d.IColorConstants; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.EnvironmentUtils; +import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils; +import org.eclipse.wb.internal.core.utils.ui.GridDataFactory; +import org.eclipse.wb.internal.core.utils.ui.PixelConverter; + +import java.io.StringReader; +import java.net.URL; +import java.text.MessageFormat; + +/** + * Helper for displaying HTML tooltips. + * + * @author scheglov_ke + * @coverage core.model.property.table + */ +public final class HtmlTooltipHelper { + public static Control createTooltipControl(Composite parent, String header, String details) { + return createTooltipControl(parent, header, details, 0); + } + + public static Control createTooltipControl(Composite parent, + String header, + String details, + int heightLimit) { + // prepare Control + Control control; + try { + String html = "<table cellspacing=2 cellpadding=0 border=0 margins=0 id=_wbp_tooltip_body>"; + if (header != null) { + html += "<tr align=center><td><b>" + header + "</b></td></tr>"; + } + html += "<tr><td align=justify>" + details + "</td></tr>"; + html += "</table>"; + control = createTooltipControl_Browser(parent, html, heightLimit); + } catch (Throwable e) { + control = createTooltipControl_Label(parent, details); + } + // set listeners + { + Listener listener = new Listener() { + @Override + public void handleEvent(Event event) { + Control tooltipControl = (Control) event.widget; + hideTooltip(tooltipControl); + } + }; + control.addListener(SWT.MouseExit, listener); + } + // done + return control; + } + + /** + * Creates {@link Browser} for displaying tooltip. + */ + private static Control createTooltipControl_Browser(Composite parent, + String html, + final int heightLimitChars) { + // prepare styles + String styles; + try { + styles = DesignerPlugin.readFile(PropertyTable.class.getResourceAsStream("Tooltip.css"), + Charsets.US_ASCII); + if (styles == null) { + styles = ""; + } + } catch (Throwable e) { + styles = ""; + } + // prepare HTML with styles and tags + String wrappedHtml; + { + String bodyAttributes = + MessageFormat.format( + "text=''{0}'' bgcolor=''{1}''", + getColorWebString(IColorConstants.tooltipForeground), + getColorWebString(IColorConstants.tooltipBackground)); + String closeElement = + EnvironmentUtils.IS_LINUX + ? " <a href='' style='position:absolute;right:1em;' id=_wbp_close>Close</a>" + : ""; + wrappedHtml = + /*CodeUtils.*/getSource( + "<html>", + " <style CHARSET='ISO-8859-1' TYPE='text/css'>", + styles, + " </style>", + " <body " + bodyAttributes + ">", + closeElement, + html, + " </body>", + "</html>"); + } + // prepare Browser + final Browser browser = new Browser(parent, SWT.NONE); + browser.setText(wrappedHtml); + // open URLs in new window + browser.addLocationListener(new LocationAdapter() { + @Override + public void changing(LocationEvent event) { + event.doit = false; + hideTooltip((Browser) event.widget); + if (!"about:blank".equals(event.location)) { + try { + IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport(); + IWebBrowser browserSupport = support.createBrowser("wbp.browser"); + browserSupport.openURL(new URL(event.location)); + } catch (Throwable e) { + DesignerPlugin.log(e); + } + } + } + }); + // set size + { + int textLength = getTextLength(html); + // horizontal hint + int hintH = 50; + if (textLength < 100) { + hintH = 40; + } + // vertical hint + int hintV = textLength / hintH + 3; + hintV = Math.min(hintV, 8); + // do set + GridDataFactory.create(browser).hintC(hintH, hintV); + } + // tweak size after rendering HTML + browser.addProgressListener(new ProgressAdapter() { + @Override + public void completed(ProgressEvent event) { + browser.removeProgressListener(this); + tweakBrowserSize(browser, heightLimitChars); + browser.getShell().setVisible(true); + } + }); + // done + return browser; + } + + private static void tweakBrowserSize(Browser browser, int heightLimitChars) { + GridDataFactory.create(browser).grab().fill(); + // limit height + if (heightLimitChars != 0) { + PixelConverter pixelConverter = new PixelConverter(browser); + int maxHeight = pixelConverter.convertHeightInCharsToPixels(heightLimitChars); + expandShellToShowFullPage_Height(browser, maxHeight); + } + // if no limit, then show all, so make as tall as required + if (heightLimitChars == 0) { + expandShellToShowFullPage_Height(browser, Integer.MAX_VALUE); + } + } + + private static void expandShellToShowFullPage_Height(Browser browser, int maxHeight) { + try { + Shell shell = browser.getShell(); + // calculate required + int contentHeight; + { + getContentOffsetHeight(browser); + contentHeight = getContentScrollHeight(browser); + } + // apply height + int useHeight = Math.min(contentHeight + ((EnvironmentUtils.IS_LINUX) ? 2 : 10), maxHeight); + shell.setSize(shell.getSize().x, useHeight); + // trim height to content + { + int offsetHeight = getBodyOffsetHeight(browser); + int scrollHeight = getBodyScrollHeight(browser); + int delta = scrollHeight - offsetHeight; + if (delta != 0 && delta < 10) { + Point size = shell.getSize(); + shell.setSize(size.x, size.y + delta + 1); + } + } + // trim width to content + { + int offsetWidth = getContentOffsetWidth(browser); + { + Point size = shell.getSize(); + shell.setSize(offsetWidth + ((EnvironmentUtils.IS_MAC) ? 6 : 10), size.y); + } + } + // hide 'Close' if too narrow + if (EnvironmentUtils.IS_LINUX) { + if (shell.getSize().y < 30) { + hideCloseElement(browser); + } + } + } catch (Throwable e) { + } + } + + private static int getContentOffsetWidth(Browser browser) throws Exception { + return evaluateScriptInt( + browser, + "return document.getElementById('_wbp_tooltip_body').offsetWidth;"); + } + + private static int getContentOffsetHeight(Browser browser) throws Exception { + return evaluateScriptInt( + browser, + "return document.getElementById('_wbp_tooltip_body').offsetHeight;"); + } + + private static int getContentScrollHeight(Browser browser) throws Exception { + return evaluateScriptInt( + browser, + "return document.getElementById('_wbp_tooltip_body').scrollHeight;"); + } + + private static int getBodyOffsetHeight(Browser browser) throws Exception { + return evaluateScriptInt(browser, "return document.body.offsetHeight;"); + } + + private static int getBodyScrollHeight(Browser browser) throws Exception { + return evaluateScriptInt(browser, "return document.body.scrollHeight;"); + } + + private static int evaluateScriptInt(Browser browser, String script) throws Exception { + Object o = ReflectionUtils.invokeMethod(browser, "evaluate(java.lang.String)", script); + return ((Number) o).intValue(); + } + + private static void hideCloseElement(Browser browser) throws Exception { + String script = "document.getElementById('_wbp_close').style.display = 'none'"; + ReflectionUtils.invokeMethod(browser, "evaluate(java.lang.String)", script); + } + + /** + * @return the length of text in given HTML. Uses internal class, so may fail, in this case + * returns length on HTML. + */ + private static int getTextLength(String html) { + StringReader htmlStringReader = new StringReader(html); + try { + ClassLoader classLoader = PropertyTable.class.getClassLoader(); + Class<?> readerClass = + classLoader.loadClass("org.eclipse.jface.internal.text.html.HTML2TextReader"); + Object reader = readerClass.getConstructors()[0].newInstance(htmlStringReader, null); + String text = (String) ReflectionUtils.invokeMethod(reader, "getString()"); + return text.length(); + } catch (Throwable e) { + return html.length(); + } + } + + /** + * Returns a string representation of {@link Color} suitable for web pages. + * + * @param color + * the {@link Color} instance, not <code>null</code>. + * @return a string representation of {@link Color} suitable for web pages. + */ + private static String getColorWebString(final Color color) { + String colorString = "#" + Integer.toHexString(color.getRed()); + colorString += Integer.toHexString(color.getGreen()); + colorString += Integer.toHexString(color.getBlue()); + return colorString; + } + + /** + * Creates {@link Label} if {@link Browser} can not be used. + */ + private static Control createTooltipControl_Label(Composite parent, String html) { + // prepare Label + final Label label = new Label(parent, SWT.WRAP); + label.setText(html); + // set size + int requiredWidth = label.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + GridDataFactory.create(label).hintHC(50).hintHMin(requiredWidth); + // copy colors + label.setForeground(parent.getForeground()); + label.setBackground(parent.getBackground()); + // done + parent.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + Shell shell = label.getShell(); + shell.setVisible(true); + } + }); + return label; + } + + private static void hideTooltip(Control tooltip) { + tooltip.getShell().dispose(); + } + + // Copied from CodeUtils.java: CodeUtils.getSource() + /** + * @return the source as single {@link String}, lines joined using "\n". + */ + public static String getSource(String... lines) { + return Joiner.on('\n').join(lines); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyExceptionHandler.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyExceptionHandler.java new file mode 100644 index 0000000..879f699 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyExceptionHandler.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.table; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; + +/** + * Handler for any {@link Exception} that happens in {@link PropertyTable}, i.e. exceptions during + * {@link Property} modifications using {@link PropertyEditor}'s. + * + * @author scheglov_ke + * @coverage core.model.property.table + */ +public interface IPropertyExceptionHandler { + void handle(Throwable e); +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyTooltipSite.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyTooltipSite.java new file mode 100644 index 0000000..e2fee14 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyTooltipSite.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.table; + +/** + * Interface that allows control of {@link PropertyTooltipProvider} interact with + * {@link PropertyTableTooltipHelper}. + * + * @author scheglov_ke + * @coverage core.model.property.table + */ +public interface IPropertyTooltipSite { + /** + * @return the {@link PropertyTable} of this site. + */ + PropertyTable getTable(); + + /** + * Hides current tooltip. + */ + void hideTooltip(); +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTable.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTable.java new file mode 100644 index 0000000..7a49cb3 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTable.java @@ -0,0 +1,1602 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.table; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.wb.draw2d.IColorConstants; +import org.eclipse.wb.draw2d.ICursorConstants; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.category.PropertyCategory; +import org.eclipse.wb.internal.core.model.property.category.PropertyCategoryProvider; +import org.eclipse.wb.internal.core.model.property.category.PropertyCategoryProviders; +import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.complex.IComplexPropertyEditor; +import org.eclipse.wb.internal.core.model.property.editor.presentation.PropertyEditorPresentation; +import org.eclipse.wb.internal.core.utils.check.Assert; +import org.eclipse.wb.internal.core.utils.ui.DrawUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Control that can display {@link Property}'s and edit them using {@link PropertyEditor}'s. + * + * @author scheglov_ke + * @author lobas_av + * @coverage core.model.property.table + */ +public class PropertyTable extends Canvas implements ISelectionProvider { + //////////////////////////////////////////////////////////////////////////// + // + // Colors + // + //////////////////////////////////////////////////////////////////////////// + private static final Color COLOR_BACKGROUND = IColorConstants.listBackground; + private static final Color COLOR_NO_PROPERTIES = IColorConstants.gray; + private static final Color COLOR_LINE = IColorConstants.lightGray; + private static final Color COLOR_COMPLEX_LINE = DrawUtils.getShiftedColor( + IColorConstants.lightGray, + -32); + private static final Color COLOR_PROPERTY_BG = DrawUtils.getShiftedColor(COLOR_BACKGROUND, -12); + private static final Color COLOR_PROPERTY_BG_MODIFIED = COLOR_BACKGROUND; + private static final Color COLOR_PROPERTY_FG_TITLE = IColorConstants.listForeground; + private static final Color COLOR_PROPERTY_FG_VALUE = + DrawUtils.isDarkColor(IColorConstants.listBackground) + ? IColorConstants.lightBlue + : IColorConstants.darkBlue; + private static final Color COLOR_PROPERTY_BG_SELECTED = IColorConstants.listSelection; + private static final Color COLOR_PROPERTY_FG_SELECTED = IColorConstants.listSelectionText; + private static final Color COLOR_PROPERTY_FG_ADVANCED = IColorConstants.gray; + // BEGIN ADT MODIFICATIONS + public static final Color COLOR_PROPERTY_FG_DEFAULT = IColorConstants.darkGray; + // END ADT MODIFICATIONS + //////////////////////////////////////////////////////////////////////////// + // + // Sizes + // + //////////////////////////////////////////////////////////////////////////// + private static final int MIN_COLUMN_WIDTH = 75; + private static final int MARGIN_LEFT = 2; + private static final int MARGIN_RIGHT = 1; + private static final int STATE_IMAGE_MARGIN_RIGHT = 4; + //////////////////////////////////////////////////////////////////////////// + // + // Images + // + //////////////////////////////////////////////////////////////////////////// + private static final Image m_plusImage = DesignerPlugin.getImage("properties/plus.gif"); + private static final Image m_minusImage = DesignerPlugin.getImage("properties/minus.gif"); + private static int m_stateWidth = 9; + //////////////////////////////////////////////////////////////////////////// + // + // Instance fields + // + //////////////////////////////////////////////////////////////////////////// + private final PropertyTableTooltipHelper m_tooltipHelper; + private boolean m_showAdvancedProperties; + private Property[] m_rawProperties; + private List<PropertyInfo> m_properties; + private final Set<String> m_expandedIds = Sets.newTreeSet(); + // BEGIN ADT MODIFICATIONS + // Add support for "expand by default" : If you specify ids to be collapsed by + // default, then any *other* ids will be expanded by default. + private Set<String> m_collapsedIds = null; + + /** + * Sets a set of ids that should be collapsed by default. Everything else + * will be expanded by default. If this method is not called, ids are + * collapsed by default instead. + * + * @param names set of ids to be collapsed + */ + public void setDefaultCollapsedNames(Collection<String> names) { + m_collapsedIds = Sets.newTreeSet(); + for (String name : names) { + m_collapsedIds.add("|" + name); // See PropertyInfo constructor for id syntax + } + } + // END ADT MODIFICATIONS + + private Image m_bufferedImage; + private int m_rowHeight; + private int m_selection; + private int m_page; + private int m_splitter = -1; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public PropertyTable(Composite parent, int style) { + super(parent, style | SWT.V_SCROLL | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE); + hookControlEvents(); + // calculate sizes + { + GC gc = new GC(this); + try { + m_rowHeight = 1 + gc.getFontMetrics().getHeight() + 1; + } finally { + gc.dispose(); + } + } + // install tooltip helper + m_tooltipHelper = new PropertyTableTooltipHelper(this); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Events + // + //////////////////////////////////////////////////////////////////////////// + /** + * Adds listeners for events. + */ + private void hookControlEvents() { + addListener(SWT.Dispose, new Listener() { + @Override + public void handleEvent(Event event) { + disposeBufferedImage(); + } + }); + addListener(SWT.Resize, new Listener() { + @Override + public void handleEvent(Event event) { + handleResize(); + } + }); + addListener(SWT.Paint, new Listener() { + @Override + public void handleEvent(Event event) { + handlePaint(event.gc, event.x, event.y, event.width, event.height); + } + }); + getVerticalBar().addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + handleVerticalScrolling(); + } + }); + addMouseListener(new MouseAdapter() { + @Override + public void mouseDown(MouseEvent event) { + forceFocus(); + handleMouseDown(event); + } + + @Override + public void mouseUp(MouseEvent event) { + handleMouseUp(event); + } + + @Override + public void mouseDoubleClick(MouseEvent event) { + handleMouseDoubleClick(event); + } + }); + addMouseMoveListener(new MouseMoveListener() { + @Override + public void mouseMove(MouseEvent event) { + handleMouseMove(event); + } + }); + // keyboard + addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + handleKeyDown(e); + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Events: dispose, resize, scroll + // + //////////////////////////////////////////////////////////////////////////// + /** + * Disposes image used for double buffered painting. + */ + private void disposeBufferedImage() { + if (m_bufferedImage != null) { + m_bufferedImage.dispose(); + m_bufferedImage = null; + } + } + + /** + * Handles {@link SWT#Resize} event. + */ + private void handleResize() { + disposeBufferedImage(); + configureScrolling(); + // splitter + { + // set default value for splitter + if (m_splitter <= MIN_COLUMN_WIDTH) { + m_splitter = Math.max((int) (getClientArea().width * 0.4), MIN_COLUMN_WIDTH); + } + configureSplitter(); + } + } + + /** + * Handles {@link SWT#Selection} event for vertical {@link ScrollBar}. + */ + private void handleVerticalScrolling() { + ScrollBar verticalBar = getVerticalBar(); + if (verticalBar.getEnabled()) { + // update selection + m_selection = verticalBar.getSelection(); + // redraw (but not include vertical bar to avoid flashing) + { + Rectangle clientArea = getClientArea(); + redraw(clientArea.x, clientArea.y, clientArea.width, clientArea.height, false); + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Keyboard + // + //////////////////////////////////////////////////////////////////////////// + /** + * Handles {@link SWT#KeyDown} event. + */ + private void handleKeyDown(KeyEvent e) { + if (m_activePropertyInfo != null) { + try { + Property property = m_activePropertyInfo.getProperty(); + // expand/collapse + if (m_activePropertyInfo.isComplex()) { + if (!m_activePropertyInfo.isExpanded() + && (e.character == '+' || e.keyCode == SWT.ARROW_RIGHT)) { + m_activePropertyInfo.expand(); + configureScrolling(); + return; + } + if (m_activePropertyInfo.isExpanded() + && (e.character == '-' || e.keyCode == SWT.ARROW_LEFT)) { + m_activePropertyInfo.collapse(); + configureScrolling(); + return; + } + } + // navigation + if (navigate(e)) { + return; + } + // editor activation + if (e.character == ' ' || e.character == SWT.CR) { + activateEditor(property, null); + return; + } + // DEL + if (e.keyCode == SWT.DEL) { + e.doit = false; + property.setValue(Property.UNKNOWN_VALUE); + return; + } + // send to editor + property.getEditor().keyDown(this, property, e); + } catch (Throwable ex) { + DesignerPlugin.log(ex); + } + } + } + + /** + * @return <code>true</code> if given {@link KeyEvent} was navigation event, so new + * {@link PropertyInfo} was selected. + */ + public boolean navigate(KeyEvent e) { + int index = m_properties.indexOf(m_activePropertyInfo); + Rectangle clientArea = getClientArea(); + // + int newIndex = index; + if (e.keyCode == SWT.HOME) { + newIndex = 0; + } else if (e.keyCode == SWT.END) { + newIndex = m_properties.size() - 1; + } else if (e.keyCode == SWT.PAGE_UP) { + newIndex = Math.max(index - m_page + 1, 0); + } else if (e.keyCode == SWT.PAGE_DOWN) { + newIndex = Math.min(index + m_page - 1, m_properties.size() - 1); + } else if (e.keyCode == SWT.ARROW_UP) { + newIndex = Math.max(index - 1, 0); + } else if (e.keyCode == SWT.ARROW_DOWN) { + newIndex = Math.min(index + 1, m_properties.size() - 1); + } + // activate new property + if (newIndex != index && newIndex < m_properties.size()) { + setActivePropertyInfo(m_properties.get(newIndex)); + // check for scrolling + int y = m_rowHeight * (newIndex - m_selection); + if (y < 0) { + m_selection = newIndex; + configureScrolling(); + } else if (y + m_rowHeight > clientArea.height) { + m_selection = newIndex - m_page + 1; + configureScrolling(); + } + // repaint + redraw(); + return true; + } + // no navigation change + return false; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Events: mouse + // + //////////////////////////////////////////////////////////////////////////// + private boolean m_splitterResizing; + /** + * We do expand/collapse on to events: click on state sign and on double click. But when we double + * click on state sign, we will have <em>two</em> events, so we should ignore double click. + */ + private long m_lastExpandCollapseTime; + + /** + * Handles {@link SWT#MouseDown} event. + */ + private void handleMouseDown(MouseEvent event) { + m_splitterResizing = event.button == 1 && m_properties != null && isLocationSplitter(event.x); + // click in property + if (!m_splitterResizing && m_properties != null) { + int propertyIndex = getPropertyIndex(event.y); + if (propertyIndex >= m_properties.size()) { + return; + } + // prepare property + setActivePropertyInfo(m_properties.get(propertyIndex)); + Property property = m_activePropertyInfo.getProperty(); + // de-activate current editor + deactivateEditor(true); + redraw(); + // activate editor + if (isLocationValue(event.x)) { + activateEditor(property, getValueRelativeLocation(event.x, event.y)); + } + } + } + + /** + * Handles {@link SWT#MouseUp} event. + */ + private void handleMouseUp(MouseEvent event) { + if (event.button == 1) { + // resize splitter + if (m_splitterResizing) { + m_splitterResizing = false; + return; + } + // if out of bounds, then ignore + if (!getClientArea().contains(event.x, event.y)) { + return; + } + // update + if (m_properties != null) { + int index = getPropertyIndex(event.y); + if (index < m_properties.size()) { + PropertyInfo propertyInfo = m_properties.get(index); + // check for expand/collapse + if (isLocationState(propertyInfo, event.x)) { + try { + m_lastExpandCollapseTime = System.currentTimeMillis(); + propertyInfo.flip(); + configureScrolling(); + } catch (Throwable e) { + DesignerPlugin.log(e); + } + } + } + } + } + } + + /** + * Handles {@link SWT#MouseDoubleClick} event. + */ + private void handleMouseDoubleClick(MouseEvent event) { + if (System.currentTimeMillis() - m_lastExpandCollapseTime > getDisplay().getDoubleClickTime()) { + try { + if (m_activePropertyInfo != null) { + if (m_activePropertyInfo.isComplex()) { + m_activePropertyInfo.flip(); + configureScrolling(); + } else { + Property property = m_activePropertyInfo.getProperty(); + property.getEditor().doubleClick(property, getValueRelativeLocation(event.x, event.y)); + } + } + } catch (Throwable e) { + handleException(e); + } + } + } + + /** + * Handles {@link SWT#MouseMove} event. + */ + private void handleMouseMove(MouseEvent event) { + int x = event.x; + // resize splitter + if (m_splitterResizing) { + m_splitter = x; + configureSplitter(); + redraw(); + return; + } + // if out of bounds, then ignore + if (!getClientArea().contains(event.x, event.y)) { + return; + } + // update + if (m_properties != null) { + // update cursor + if (isLocationSplitter(x)) { + setCursor(ICursorConstants.SIZEWE); + } else { + setCursor(null); + } + // update tooltip helper + updateTooltip(event); + } else { + updateTooltipNoProperty(); + } + } + + /** + * Updates {@link PropertyTableTooltipHelper}. + */ + private void updateTooltip(MouseEvent event) { + int x = event.x; + int propertyIndex = getPropertyIndex(event.y); + // + if (propertyIndex < m_properties.size()) { + PropertyInfo propertyInfo = m_properties.get(propertyIndex); + Property property = propertyInfo.getProperty(); + int y = (propertyIndex - m_selection) * m_rowHeight; + // check for title + { + int titleX = getTitleTextX(propertyInfo); + int titleRight = m_splitter - 2; + if (titleX <= x && x < titleRight) { + m_tooltipHelper.update(property, true, false, titleX, titleRight, y, m_rowHeight); + return; + } + } + // check for value + { + int valueX = m_splitter + 3; + if (x > valueX) { + m_tooltipHelper.update( + property, + false, + true, + valueX, + getClientArea().width, + y, + m_rowHeight); + } + } + } else { + updateTooltipNoProperty(); + } + } + + private void updateTooltipNoProperty() { + m_tooltipHelper.update(null, false, false, 0, 0, 0, 0); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Editor + // + //////////////////////////////////////////////////////////////////////////// + private PropertyInfo m_activePropertyInfo; + private String m_activePropertyId; + private PropertyEditor m_activeEditor; + + /** + * Tries to activate editor for {@link PropertyInfo} under cursor. + * + * @param location + * the mouse location, if editor is activated using mouse click, or <code>null</code> if + * it is activated using keyboard. + */ + public void activateEditor(Property property, Point location) { + try { + // de-activate old editor + deactivateEditor(true); + // activate editor + PropertyEditor editor = property.getEditor(); + try { + if (editor.activate(this, property, location)) { + m_activeEditor = editor; + } + } catch (Throwable e) { + handleException(e); + } + // set bounds + setActiveEditorBounds(); + } catch (Throwable e) { + DesignerPlugin.log(e); + } + } + + /** + * Deactivates current {@link PropertyEditor}. + */ + public void deactivateEditor(boolean save) { + if (m_activeEditor != null) { + PropertyEditor activeEditor = m_activeEditor; + m_activeEditor = null; + if (m_activePropertyInfo != null && m_activePropertyInfo.m_property != null) { + activeEditor.deactivate(this, m_activePropertyInfo.m_property, save); + } + } + } + + /** + * Sets correct bounds for active editor, for example we need update bounds after scrolling. + */ + private void setActiveEditorBounds() { + if (m_activeEditor != null) { + int index = m_properties.indexOf(m_activePropertyInfo); + if (index == -1) { + // it is possible that active property was hidden because its parent was collapsed + deactivateEditor(true); + } else { + // prepare bounds for editor + Rectangle bounds; + { + Rectangle clientArea = getClientArea(); + int x = m_splitter + 1; + int width = clientArea.width - x - MARGIN_RIGHT; + int y = m_rowHeight * (index - m_selection) + 1; + int height = m_rowHeight - 1; + bounds = new Rectangle(x, y, width, height); + } + // update bounds using presentation + { + PropertyEditorPresentation presentation = m_activeEditor.getPresentation(); + if (presentation != null) { + int presentationWidth = + presentation.show( + this, + m_activePropertyInfo.m_property, + bounds.x, + bounds.y, + bounds.width, + bounds.height); + bounds.width -= presentationWidth; + } + } + // set editor bounds + m_activeEditor.setBounds(bounds); + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Exceptions + // + //////////////////////////////////////////////////////////////////////////// + private IPropertyExceptionHandler m_exceptionHandler; + + /** + * Sets {@link IPropertyExceptionHandler} for handling all exceptions. + */ + public void setExceptionHandler(IPropertyExceptionHandler exceptionHandler) { + m_exceptionHandler = exceptionHandler; + } + + /** + * Handles given {@link Throwable}.<br> + * Right now it just logs it, but in future we can open some dialog here. + */ + public void handleException(Throwable e) { + m_exceptionHandler.handle(e); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Scrolling + // + //////////////////////////////////////////////////////////////////////////// + /** + * Configures vertical {@link ScrollBar}. + */ + private void configureScrolling() { + ScrollBar verticalBar = getVerticalBar(); + if (m_properties == null) { + verticalBar.setEnabled(false); + } else { + m_page = getClientArea().height / m_rowHeight; + m_selection = Math.max(0, Math.min(m_properties.size() - m_page, m_selection)); + verticalBar.setValues(m_selection, 0, m_properties.size(), m_page, 1, m_page); + // enable/disable scrolling + if (m_properties.size() <= m_page) { + verticalBar.setEnabled(false); + } else { + verticalBar.setEnabled(true); + } + } + // redraw, we reconfigure scrolling only if list of properties was changed, so we should redraw + redraw(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Location/size utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the <code>X</code> position for first pixel of {@link PropertyInfo} title (location of + * state image). + */ + private int getTitleX(PropertyInfo propertyInfo) { + return MARGIN_LEFT + getLevelIndent() * propertyInfo.getLevel(); + } + + /** + * @return the <code>X</code> position for first pixel of {@link PropertyInfo} title text. + */ + private int getTitleTextX(PropertyInfo propertyInfo) { + return getTitleX(propertyInfo) + getLevelIndent(); + } + + /** + * @return the indentation for single level. + */ + private int getLevelIndent() { + return m_stateWidth + STATE_IMAGE_MARGIN_RIGHT; + } + + /** + * Checks horizontal splitter value to boundary values. + */ + private void configureSplitter() { + Rectangle clientArea = getClientArea(); + // check title width + if (m_splitter < MIN_COLUMN_WIDTH) { + m_splitter = MIN_COLUMN_WIDTH; + } + // check value width + if (clientArea.width - m_splitter < MIN_COLUMN_WIDTH) { + m_splitter = clientArea.width - MIN_COLUMN_WIDTH; + } + } + + /** + * @return the index in {@link #m_properties} corresponding given <code>y</code> location. + */ + private int getPropertyIndex(int y) { + return m_selection + y / m_rowHeight; + } + + /** + * @return <code>true</code> if given <code>x</code> coordinate is on state (plus/minus) image. + */ + private boolean isLocationState(PropertyInfo propertyInfo, int x) { + int levelX = getTitleX(propertyInfo); + return propertyInfo.isComplex() && levelX <= x && x <= levelX + m_stateWidth; + } + + /** + * Returns <code>true</code> if <code>x</code> coordinate is on splitter. + */ + private boolean isLocationSplitter(int x) { + return Math.abs(m_splitter - x) < 2; + } + + /** + * @return <code>true</code> if given <code>x</code> is on value part of property. + */ + private boolean isLocationValue(int x) { + return x > m_splitter + 2; + } + + /** + * @param x + * the {@link PropertyTable} relative coordinate. + * @param y + * the {@link PropertyTable} relative coordinate. + * + * @return the location relative to the value part of property. + */ + private Point getValueRelativeLocation(int x, int y) { + return new Point(x - (m_splitter + 2), y - m_rowHeight * getPropertyIndex(y)); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Shows or hides {@link Property}-s with {@link PropertyCategory#ADVANCED}. + */ + public void setShowAdvancedProperties(boolean showAdvancedProperties) { + m_showAdvancedProperties = showAdvancedProperties; + setInput0(); + } + + /** + * Sets the array of {@link Property}'s to display/edit. + */ + public void setInput(Property[] properties) { + m_rawProperties = properties; + setInput0(); + } + + private void setInput0() { + // send "hide" to all PropertyEditorPresentation's + if (m_properties != null) { + for (PropertyInfo propertyInfo : m_properties) { + Property property = propertyInfo.getProperty(); + // hide presentation + { + PropertyEditorPresentation presentation = property.getEditor().getPresentation(); + if (presentation != null) { + presentation.hide(this, property); + } + } + } + } + // set new properties + if (m_rawProperties == null || m_rawProperties.length == 0) { + deactivateEditor(false); + m_properties = Lists.newArrayList(); + } else { + try { + // add PropertyInfo for each Property + m_properties = Lists.newArrayList(); + for (Property property : m_rawProperties) { + if (rawProperties_shouldShow(property)) { + PropertyInfo propertyInfo = new PropertyInfo(property); + m_properties.add(propertyInfo); + } + } + // expand properties using history + while (true) { + boolean expanded = false; + List<PropertyInfo> currentProperties = Lists.newArrayList(m_properties); + for (PropertyInfo propertyInfo : currentProperties) { + expanded |= propertyInfo.expandFromHistory(); + } + // stop + if (!expanded) { + break; + } + } + } catch (Throwable e) { + DesignerPlugin.log(e); + } + } + // update active property + if (m_activePropertyId != null) { + PropertyInfo newActivePropertyInfo = null; + // try to find corresponding PropertyInfo + if (m_properties != null) { + for (PropertyInfo propertyInfo : m_properties) { + if (propertyInfo.m_id.equals(m_activePropertyId)) { + newActivePropertyInfo = propertyInfo; + break; + } + } + } + // set new PropertyInfo + setActivePropertyInfo(newActivePropertyInfo); + } + // update scroll bar + configureScrolling(); + } + + /** + * @return <code>true</code> if given {@link Property} should be displayed. + */ + private boolean rawProperties_shouldShow(Property property) throws Exception { + PropertyCategory category = getCategory(property); + // filter out hidden properties + if (category.isHidden()) { + return false; + } + // filter out advanced properties + if (category.isAdvanced()) { + if (!m_showAdvancedProperties && !property.isModified()) { + return false; + } + } + if (category.isAdvancedReally()) { + return m_showAdvancedProperties; + } + // OK + return true; + } + + /** + * Activates given {@link Property}. + */ + public void setActiveProperty(Property property) { + for (PropertyInfo propertyInfo : m_properties) { + if (propertyInfo.m_property == property) { + setActivePropertyInfo(propertyInfo); + break; + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access: only for testing + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the count of properties in "expanded" list. + */ + public int forTests_getPropertiesCount() { + return m_properties.size(); + } + + /** + * @return the {@link Property} from "expanded" list. + */ + public Property forTests_getProperty(int index) { + return m_properties.get(index).getProperty(); + } + + /** + * Expands the {@link PropertyInfo} with given index. + */ + public void forTests_expand(int index) throws Exception { + m_properties.get(index).expand(); + } + + /** + * @return the position of splitter. + */ + public int forTests_getSplitter() { + return m_splitter; + } + + /** + * @return the location of state image (plus/minus) for given {@link Property}. + */ + public Point forTests_getStateLocation(Property property) { + PropertyInfo propertyInfo = getPropertyInfo(property); + if (propertyInfo != null) { + int index = m_properties.indexOf(propertyInfo); + int x = getTitleX(propertyInfo); + int y = m_rowHeight * (index - m_selection) + 1; + return new Point(x, y); + } + return null; + } + + /** + * @return the location of state image (plus/minus) for given {@link Property}. + */ + public Point forTests_getValueLocation(Property property) { + PropertyInfo propertyInfo = getPropertyInfo(property); + if (propertyInfo != null) { + int index = m_properties.indexOf(propertyInfo); + int x = m_splitter + 5; + int y = m_rowHeight * (index - m_selection) + 1; + return new Point(x, y); + } + return null; + } + + /** + * @return the active {@link PropertyEditor}. + */ + public PropertyEditor forTests_getActiveEditor() { + return m_activeEditor; + } + + /** + * @return the {@link PropertyCategory} that is used by this {@link PropertyTable} to display. + */ + public PropertyCategory forTests_getCategory(Property property) { + return getCategory(property); + } + + /** + * @return the {@link PropertyInfo}for given {@link Property}. + */ + private PropertyInfo getPropertyInfo(Property property) { + for (PropertyInfo propertyInfo : m_properties) { + if (propertyInfo.getProperty() == property) { + return propertyInfo; + } + } + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // ISelectionProvider + // + //////////////////////////////////////////////////////////////////////////// + private final List<ISelectionChangedListener> m_selectionListeners = Lists.newArrayList(); + + @Override +public void addSelectionChangedListener(ISelectionChangedListener listener) { + if (!m_selectionListeners.contains(listener)) { + m_selectionListeners.add(listener); + } + } + + @Override +public void removeSelectionChangedListener(ISelectionChangedListener listener) { + m_selectionListeners.add(listener); + } + + @Override +public ISelection getSelection() { + if (m_activePropertyInfo != null) { + return new StructuredSelection(m_activePropertyInfo.getProperty()); + } else { + return StructuredSelection.EMPTY; + } + } + + @Override + public void setSelection(ISelection selection) { + throw new UnsupportedOperationException(); + } + + /** + * Sets the new active {@link PropertyInfo} and sends event to {@link ISelectionChangedListener} + * 's. + */ + private void setActivePropertyInfo(PropertyInfo activePropertyInfo) { + m_activePropertyInfo = activePropertyInfo; + // update m_activePropertyId only when really select property, + // not just remove selection because there are no corresponding property for old active + // so, later for some other component, if we don't select other property, old active will be selected + if (m_activePropertyInfo != null) { + m_activePropertyId = m_activePropertyInfo.m_id; + } + // make sure that active property is visible + if (m_activePropertyInfo != null) { + int row = m_properties.indexOf(m_activePropertyInfo); + if (m_selection <= row && row < m_selection + m_page) { + } else { + m_selection = row; + configureScrolling(); + } + } + // send events + SelectionChangedEvent selectionEvent = new SelectionChangedEvent(this, getSelection()); + for (ISelectionChangedListener listener : m_selectionListeners) { + listener.selectionChanged(selectionEvent); + } + // re-draw + redraw(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Painting + // + //////////////////////////////////////////////////////////////////////////// + private boolean m_painting; + private Font m_baseFont; + private Font m_boldFont; + private Font m_italicFont; + + /** + * Handles {@link SWT#Paint} event. + */ + private void handlePaint(GC gc, int x, int y, int width, int height) { + // sometimes we disable Eclipse Shell to prevent user actions, but we do this for short time + if (!isEnabled()) { + return; + } + // prevent recursion + if (m_painting) { + return; + } + m_painting = true; + // + try { + setActiveEditorBounds(); + // prepare buffered image + if (m_bufferedImage == null || m_bufferedImage.isDisposed()) { + Point size = getSize(); + m_bufferedImage = new Image(DesignerPlugin.getStandardDisplay(), size.x, size.y); + } + // prepare buffered GC + GC bufferedGC = null; + try { + // perform some drawing + { + bufferedGC = new GC(m_bufferedImage); + bufferedGC.setClipping(x, y, width, height); + bufferedGC.setBackground(gc.getBackground()); + bufferedGC.setForeground(gc.getForeground()); + bufferedGC.setFont(gc.getFont()); + bufferedGC.setLineStyle(gc.getLineStyle()); + bufferedGC.setLineWidth(gc.getLineWidth()); + } + // fill client area + { + Rectangle clientArea = getClientArea(); + bufferedGC.setBackground(COLOR_BACKGROUND); + bufferedGC.fillRectangle(clientArea); + } + // draw content + if (m_properties == null || m_properties.size() == 0) { + drawEmptyContent(bufferedGC); + } else { + drawContent(bufferedGC); + } + } finally { + // flush image + if (bufferedGC != null) { + bufferedGC.dispose(); + } + } + gc.drawImage(m_bufferedImage, 0, 0); + } finally { + m_painting = false; + } + } + + /** + * Draws content when there are no properties. + */ + private void drawEmptyContent(GC gc) { + Rectangle area = getClientArea(); + // draw message + gc.setForeground(COLOR_NO_PROPERTIES); + DrawUtils.drawStringCHCV( + gc, + "<No properties>", + 0, + 0, + area.width, + area.height); + } + + /** + * Draws all {@link PropertyInfo}'s, separators, etc. + */ + private void drawContent(GC gc) { + Rectangle clientArea = getClientArea(); + // prepare fonts + m_baseFont = gc.getFont(); + m_boldFont = DrawUtils.getBoldFont(m_baseFont); + m_italicFont = DrawUtils.getItalicFont(m_baseFont); + // show presentations + int[] presentationsWidth = showPresentations(clientArea); + // draw properties + { + int y = clientArea.y - m_rowHeight * m_selection; + for (int i = 0; i < m_properties.size(); i++) { + // skip, if not visible yet + if (y + m_rowHeight < 0) { + y += m_rowHeight; + continue; + } + // stop, if already invisible + if (y > clientArea.height) { + break; + } + // draw single property + { + PropertyInfo propertyInfo = m_properties.get(i); + drawProperty(gc, propertyInfo, y + 1, m_rowHeight - 1, clientArea.width + - presentationsWidth[i]); + y += m_rowHeight; + } + // draw row separator + gc.setForeground(COLOR_LINE); + gc.drawLine(0, y, clientArea.width, y); + } + } + // draw expand line + drawExpandLines(gc, clientArea); + // draw rectangle around table + gc.setForeground(COLOR_LINE); + gc.drawRectangle(0, 0, clientArea.width - 1, clientArea.height - 1); + // draw splitter + gc.setForeground(COLOR_LINE); + gc.drawLine(m_splitter, 0, m_splitter, clientArea.height); + // dispose font + m_boldFont.dispose(); + m_italicFont.dispose(); + } + + /** + * Shows {@link PropertyEditorPresentation}'s for all {@link Property}'s, i.e. updates also their + * bounds. So, some {@link PropertyEditorPresentation}'s become invisible because they are moved + * above or below visible client area. + * + * @return the array of width for each {@link PropertyEditorPresentation}'s, consumed on the + * right. + */ + private int[] showPresentations(Rectangle clientArea) { + int[] presentationsWidth = new int[m_properties.size()]; + // prepare value rectangle + int x = m_splitter + 4; + int w = clientArea.width - x - MARGIN_RIGHT; + // show presentation's for all properties + int y = clientArea.y - m_rowHeight * m_selection; + for (int i = 0; i < m_properties.size(); i++) { + PropertyInfo propertyInfo = m_properties.get(i); + Property property = propertyInfo.getProperty(); + PropertyEditorPresentation presentation = property.getEditor().getPresentation(); + if (presentation != null) { + presentationsWidth[i] = presentation.show(this, property, x, y + 1, w, m_rowHeight - 1); + } + y += m_rowHeight; + } + return presentationsWidth; + } + + /** + * Draws lines from expanded complex property to its last sub-property. + */ + private void drawExpandLines(GC gc, Rectangle clientArea) { + int height = m_rowHeight - 1; + int xOffset = m_plusImage.getBounds().width / 2; + int yOffset = (height - m_plusImage.getBounds().width) / 2; + // + int y = clientArea.y - m_selection * m_rowHeight; + gc.setForeground(COLOR_COMPLEX_LINE); + for (int i = 0; i < m_properties.size(); i++) { + PropertyInfo propertyInfo = m_properties.get(i); + // + if (propertyInfo.isExpanded()) { + int index = m_properties.indexOf(propertyInfo); + // prepare index of last sub-property + int index2 = index; + for (; index2 < m_properties.size(); index2++) { + PropertyInfo nextPropertyInfo = m_properties.get(index2); + if (nextPropertyInfo != propertyInfo + && nextPropertyInfo.getLevel() <= propertyInfo.getLevel()) { + break; + } + } + index2--; + // draw line if there are children + if (index2 > index) { + int x = getTitleX(propertyInfo) + xOffset; + int y1 = y + height - yOffset; + int y2 = y + m_rowHeight * (index2 - index) + m_rowHeight / 2; + gc.drawLine(x, y1, x, y2); + gc.drawLine(x, y2, x + m_rowHeight / 3, y2); + } + } + // + y += m_rowHeight; + } + } + + /** + * Draws single {@link PropertyInfo} in specified rectangle. + */ + private void drawProperty(GC gc, PropertyInfo propertyInfo, int y, int height, int width) { + // remember colors + Color oldBackground = gc.getBackground(); + Color oldForeground = gc.getForeground(); + // draw property + try { + Property property = propertyInfo.getProperty(); + boolean isActiveProperty = + m_activePropertyInfo != null && m_activePropertyInfo.getProperty() == property; + // set background + boolean modified = property.isModified(); + { + if (isActiveProperty) { + gc.setBackground(COLOR_PROPERTY_BG_SELECTED); + } else { + if (modified) { + gc.setBackground(COLOR_PROPERTY_BG_MODIFIED); + } else { + gc.setBackground(COLOR_PROPERTY_BG); + } + } + gc.fillRectangle(0, y, width, height); + } + // draw state image + if (propertyInfo.isShowComplex()) { + Image stateImage = propertyInfo.isExpanded() ? m_minusImage : m_plusImage; + DrawUtils.drawImageCV(gc, stateImage, getTitleX(propertyInfo), y, height); + } + // draw title + { + // configure GC + { + gc.setForeground(COLOR_PROPERTY_FG_TITLE); + // check category + if (getCategory(property).isAdvanced()) { + gc.setForeground(COLOR_PROPERTY_FG_ADVANCED); + gc.setFont(m_italicFont); + } else if (getCategory(property).isPreferred() || getCategory(property).isSystem()) { + gc.setFont(m_boldFont); + } + // check for active + if (isActiveProperty) { + gc.setForeground(COLOR_PROPERTY_FG_SELECTED); + } + } + // paint title + int x = getTitleTextX(propertyInfo); + DrawUtils.drawStringCV(gc, property.getTitle(), x, y, m_splitter - x, height); + } + // draw value + { + // configure GC + gc.setFont(m_baseFont); + if (!isActiveProperty) { + gc.setForeground(COLOR_PROPERTY_FG_VALUE); + } + // prepare value rectangle + int x = m_splitter + 4; + int w = width - x - MARGIN_RIGHT; + // paint value + + // BEGIN ADT MODIFICATIONS + if (!modified) { + gc.setForeground(COLOR_PROPERTY_FG_DEFAULT); + } + // END ADT MODIFICATIONS + + property.getEditor().paint(property, gc, x, y, w, height); + } + } catch (Throwable e) { + DesignerPlugin.log(e); + } finally { + // restore colors + gc.setBackground(oldBackground); + gc.setForeground(oldForeground); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyCategory + // + //////////////////////////////////////////////////////////////////////////// + private PropertyCategoryProvider m_propertyCategoryProvider = + PropertyCategoryProviders.fromProperty(); + + /** + * Sets the {@link PropertyCategoryProvider} that can be used to tweak usual + * {@link PropertyCategory}. + */ + public void setPropertyCategoryProvider(PropertyCategoryProvider propertyCategoryProvider) { + m_propertyCategoryProvider = propertyCategoryProvider; + } + + /** + * @return the {@link PropertyCategory} that is used by this {@link PropertyTable} to display. + */ + private PropertyCategory getCategory(Property property) { + return m_propertyCategoryProvider.getCategory(property); + } + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyInfo + // + //////////////////////////////////////////////////////////////////////////// + /** + * Class with information about single {@link Property}. + * + * @author scheglov_ke + */ + private final class PropertyInfo { + private final String m_id; + private final int m_level; + private final Property m_property; + private final boolean m_stateComplex; + private boolean m_stateExpanded; + private List<PropertyInfo> m_children; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public PropertyInfo(Property property) { + this(property, "", 0); + } + + private PropertyInfo(Property property, String idPrefix, int level) { + // BEGIN ADT MODIFICATIONS + //m_id = idPrefix + "|" + property.getTitle(); + m_id = idPrefix + "|" + property.getName(); + // END ADT MODIFICATIONS + m_level = level; + m_property = property; + m_stateComplex = property.getEditor() instanceof IComplexPropertyEditor; + } + + //////////////////////////////////////////////////////////////////////////// + // + // State + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return <code>true</code> if this property is complex. + */ + public boolean isComplex() { + return m_stateComplex; + } + + public boolean isShowComplex() throws Exception { + if (m_stateComplex) { + prepareChildren(); + return m_children != null && !m_children.isEmpty(); + } + return false; + } + + /** + * @return <code>true</code> if this complex property is expanded. + */ + public boolean isExpanded() { + return m_stateExpanded; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the level of this property, i.e. on which level of complex property it is located. + */ + public int getLevel() { + return m_level; + } + + /** + * @return the {@link Property}. + */ + public Property getProperty() { + return m_property; + } + + /** + * Flips collapsed/expanded state and adds/removes sub-properties. + */ + public void flip() throws Exception { + Assert.isTrue(m_stateComplex); + if (m_stateExpanded) { + collapse(); + } else { + expand(); + } + } + + /** + * Expands this property. + */ + public void expand() throws Exception { + Assert.isTrue(m_stateComplex); + Assert.isTrue(!m_stateExpanded); + // + m_stateExpanded = true; + m_expandedIds.add(m_id); + // BEGIN ADT MODIFICATIONS + if (m_collapsedIds != null) { + m_collapsedIds.remove(m_id); + } + // END ADT MODIFICATIONS + prepareChildren(); + // + int index = m_properties.indexOf(this); + addChildren(index + 1); + } + + /** + * Collapses this property. + */ + public void collapse() throws Exception { + Assert.isTrue(m_stateComplex); + Assert.isTrue(m_stateExpanded); + // + m_stateExpanded = false; + m_expandedIds.remove(m_id); + // BEGIN ADT MODIFICATIONS + if (m_collapsedIds != null) { + m_collapsedIds.add(m_id); + } + // END ADT MODIFICATIONS + prepareChildren(); + // + int index = m_properties.indexOf(this); + removeChildren(index + 1); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Internal + // + //////////////////////////////////////////////////////////////////////////// + /** + * Adds children properties. + * + * @return the index for new properties to add. + */ + private int addChildren(int index) throws Exception { + prepareChildren(); + for (PropertyInfo child : m_children) { + // skip if should not display raw Property + if (!rawProperties_shouldShow(child.m_property)) { + continue; + } + // add child + m_properties.add(index++, child); + // add children of current child + if (child.isExpanded()) { + index = child.addChildren(index); + } + } + return index; + } + + /** + * Removes children properties. + */ + private void removeChildren(int index) throws Exception { + prepareChildren(); + for (PropertyInfo child : m_children) { + // skip if should not display raw Property + if (!rawProperties_shouldShow(child.m_property)) { + continue; + } + // hide presentation + { + PropertyEditorPresentation presentation = + child.getProperty().getEditor().getPresentation(); + if (presentation != null) { + presentation.hide(PropertyTable.this, child.getProperty()); + } + } + // remove child + m_properties.remove(index); + // remove children of current child + if (child.isExpanded()) { + child.removeChildren(index); + } + } + } + + /** + * Prepares children {@link PropertyInfo}'s, for sub-properties. + */ + private void prepareChildren() throws Exception { + if (m_children == null) { + m_children = Lists.newArrayList(); + for (Property subProperty : getSubProperties()) { + PropertyInfo subPropertyInfo = createSubPropertyInfo(subProperty); + m_children.add(subPropertyInfo); + } + } + } + + private PropertyInfo createSubPropertyInfo(Property subProperty) { + return new PropertyInfo(subProperty, m_id, m_level + 1); + } + + private Property[] getSubProperties() throws Exception { + IComplexPropertyEditor complexEditor = (IComplexPropertyEditor) m_property.getEditor(); + List<Property> subProperties = Lists.newArrayList(); + for (Property subProperty : complexEditor.getProperties(m_property)) { + if (getCategory(subProperty).isHidden() && !subProperty.isModified()) { + // skip hidden properties + continue; + } + subProperties.add(subProperty); + } + return subProperties.toArray(new Property[subProperties.size()]); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Persistent expanding support + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return <code>true</code> if this {@link PropertyInfo} was expanded from history. + */ + public boolean expandFromHistory() throws Exception { + if (isComplex() && !isExpanded() && m_expandedIds.contains(m_id)) { + expand(); + return true; + } + // BEGIN ADT MODIFICATIONS + if (m_collapsedIds != null && isComplex() && !isExpanded() + && !m_collapsedIds.contains(m_id)) { + expand(); + return true; + } + // END ADT MODIFICATIONS + return false; + } + } + + // BEGIN ADT MODIFICATIONS + /** Collapse all top-level properties */ + public void collapseAll() { + try { + m_lastExpandCollapseTime = System.currentTimeMillis(); + if (m_collapsedIds != null) { + m_collapsedIds.addAll(m_expandedIds); + } + m_expandedIds.clear(); + setInput(m_rawProperties); + redraw(); + } catch (Throwable e) { + DesignerPlugin.log(e); + } + } + + /** Expand all top-level properties */ + public void expandAll() { + try { + m_lastExpandCollapseTime = System.currentTimeMillis(); + if (m_collapsedIds != null) { + m_collapsedIds.clear(); + } + m_expandedIds.clear(); + for (PropertyInfo info : m_properties) { + if (info.m_stateComplex) { + m_expandedIds.add(info.m_id); + } + } + setInput(m_rawProperties); + redraw(); + } catch (Throwable e) { + DesignerPlugin.log(e); + } + } + // END ADT MODIFICATIONS +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTableTooltipHelper.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTableTooltipHelper.java new file mode 100644 index 0000000..16b9d8f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTableTooltipHelper.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.table; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.wb.internal.core.EnvironmentUtils; +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.utils.ui.GridLayoutFactory; + +/** + * Helper class for displaying tooltips. + * + * @author scheglov_ke + * @coverage core.model.property.table + */ +class PropertyTableTooltipHelper implements IPropertyTooltipSite { + private final PropertyTable m_table; + private Shell m_tooltip; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public PropertyTableTooltipHelper(PropertyTable table) { + m_table = table; + m_table.addListener(SWT.MouseHover, new Listener() { + @Override + public void handleEvent(Event event) { + if (event.stateMask == 0) { + showTooltip(); + } + } + }); + m_table.addListener(SWT.MouseExit, new Listener() { + @Override + public void handleEvent(Event event) { + // check, may be cursor is now on tooltip, so ignore this MouseExit + { + Control control = Display.getCurrent().getCursorControl(); + while (control != null) { + if (control == m_tooltip) { + return; + } + control = control.getParent(); + } + } + // no, we should hide tooltip + hideTooltip(); + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + private Property m_property; + private boolean m_onTitle; + private boolean m_onValue; + private int m_beginX; + private int m_endX; + private int m_y; + private int m_rowHeight; + + /** + * {@link PropertyTable} call this method to inform that cursor location was changed. + */ + public void update(Property property, + boolean onTitle, + boolean onValue, + int beginX, + int endX, + int y, + int rowHeight) { + m_property = property; + m_onTitle = onTitle; + m_onValue = onValue; + m_beginX = beginX; + m_endX = endX; + m_y = y; + m_rowHeight = rowHeight; + } + + //////////////////////////////////////////////////////////////////////////// + // + // IPropertyTooltipSite + // + //////////////////////////////////////////////////////////////////////////// + @Override +public PropertyTable getTable() { + return m_table; + } + + @Override +public void hideTooltip() { + if (m_tooltip != null && !m_tooltip.isDisposed()) { + m_tooltip.dispose(); + } + m_tooltip = null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Showing tooltip + // + //////////////////////////////////////////////////////////////////////////// + private void showTooltip() { + hideTooltip(); + // check for property + if (m_property == null) { + return; + } + // + if (m_onTitle) { + showTooltip(m_property.getAdapter(PropertyTooltipProvider.class), m_beginX, m_endX); + } + if (m_onValue) { + showTooltip(m_property.getEditor().getAdapter(PropertyTooltipProvider.class), + m_beginX, m_endX); + } + } + + private void showTooltip(PropertyTooltipProvider provider, int startX, int endX) { + if (provider == null) { + return; + } + // create Shell + { + m_tooltip = new Shell(m_table.getShell(), SWT.NO_FOCUS | SWT.ON_TOP | SWT.TOOL | SWT.SINGLE); + configureColors(m_tooltip); + GridLayoutFactory.create(m_tooltip).noMargins(); + } + // prepare control + Control control = provider.createTooltipControl(m_property, m_tooltip, endX - startX, this); + if (control == null) { + hideTooltip(); + return; + } + // show Shell + { + // prepare tooltip location + Point tooltipLocation; + if (provider.getTooltipPosition() == PropertyTooltipProvider.ON) { + tooltipLocation = m_table.toDisplay(new Point(startX, m_y)); + } else { + tooltipLocation = m_table.toDisplay(new Point(startX, m_y + m_rowHeight)); + } + // set location/size and open + m_tooltip.setLocation(tooltipLocation.x, tooltipLocation.y); + // for non-windows systems the tooltip may have invalid tooltip bounds + // because some widget's API functions may fail if tooltip content is not visible + // ex., on MacOSX tree widget's items has zero bounds since they are not yet visible. + // the workaround is to preset tooltip size to big values before any computeSize called. + if (!EnvironmentUtils.IS_WINDOWS) { + m_tooltip.setSize(1000, 1000); + } + m_tooltip.setSize(m_tooltip.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + provider.show(m_tooltip); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets given {@link Control} correct background/foreground for tooltips. + */ + private void configureColors(Control control) { + Display display = Display.getCurrent(); + control.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); + control.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipProvider.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipProvider.java new file mode 100644 index 0000000..1c3d4fe --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipProvider.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.table; + +import org.eclipse.wb.internal.core.model.property.Property; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; + +/** + * Provider for tooltip controls. + * + * @author scheglov_ke + * @coverage core.model.property.table + */ +public abstract class PropertyTooltipProvider { + /** + * Show tooltip directly on property row. + */ + public static final int ON = 0; + /** + * Show tooltip below property row. + */ + public static final int BELOW = 1; + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyTooltipProvider + // + //////////////////////////////////////////////////////////////////////////// + /** + * Create tooltip control. + */ + public abstract Control createTooltipControl(Property property, + Composite parent, + int availableWidth, + IPropertyTooltipSite site); + + /** + * Shows tooltip {@link Shell}. + */ + public void show(Shell shell) { + shell.setVisible(true); + } + + /** + * Returns position for tooltip control. Usually we should show directly on same row, because we + * use tooltip to show just longer (full) text of property. But for "class" property we show + * hierarchy, so it is better show it below and allow user see also property row. + */ + public int getTooltipPosition() { + return ON; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Tooltip listener + // + //////////////////////////////////////////////////////////////////////////// + /** + * {@link Listener} that hides tooltip on mouse exit or click. + */ + protected static final class HideListener implements Listener { + private final IPropertyTooltipSite m_site; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public HideListener(IPropertyTooltipSite site) { + m_site = site; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Listener + // + //////////////////////////////////////////////////////////////////////////// + public void handleEvent(Event event) { + Control tooltipControl = (Control) event.widget; + switch (event.type) { + case SWT.MouseDown : { + PropertyTable table = m_site.getTable(); + // convert location from tooltip to table + Point p = new Point(event.x, event.y); + p = tooltipControl.toDisplay(p); + p = table.toControl(p); + // send MouseDown to table + Event newEvent = new Event(); + newEvent.x = p.x; + newEvent.y = p.y; + table.notifyListeners(SWT.MouseDown, newEvent); + // hide tooltip + m_site.hideTooltip(); + break; + } + case SWT.MouseExit : + m_site.hideTooltip(); + break; + } + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipTextProvider.java b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipTextProvider.java new file mode 100644 index 0000000..b2e9b69 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipTextProvider.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.model.property.table; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.wb.internal.core.model.property.Property; + +/** + * Implementation of {@link PropertyTooltipProvider} for text. + * + * @author scheglov_ke + * @coverage core.model.property.table + */ +public abstract class PropertyTooltipTextProvider extends PropertyTooltipProvider { + //////////////////////////////////////////////////////////////////////////// + // + // PropertyTooltipProvider + // + //////////////////////////////////////////////////////////////////////////// + @Override + public Control createTooltipControl(Property property, + Composite parent, + int availableWidth, + IPropertyTooltipSite site) { + // prepare header and content + String header = null; + String content = null; + try { + // BEGIN ADT MODIFICATIONS + // was: header = property.getTitle(); + header = property.getName(); + // END ADT MODIFICATIONS + content = getText(property); + } catch (Throwable e) { + } + if (header == null || content == null) { + return null; + } + // create tooltip Control + return HtmlTooltipHelper.createTooltipControl(parent, header, content, 8); + } + + @Override + public void show(Shell shell) { + // do nothing, Shell will be displayed when Browser will complete rendering + } + + //////////////////////////////////////////////////////////////////////////// + // + // Text + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the text to show as tooltip. + */ + protected abstract String getText(Property property) throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/Tooltip.css b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/Tooltip.css new file mode 100644 index 0000000..2ba2a02 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/property/table/Tooltip.css @@ -0,0 +1,35 @@ +/* Font definitions */ +html { font-family: 'Segoe UI','Verdana','Helvetica',sans-serif; font-size: 9pt; font-style: normal; font-weight: normal; } +body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt { font-size: 1em; } +pre { font-family: monospace; } + +/* Margins */ +body { overflow: auto; margin-top: 0px; margin-bottom: 0px; margin-left: 0.3em; margin-right: 0.3em; } +div { margin: 0px; } +h1 { margin-top: 0.3em; margin-bottom: 0.04em; } +h2 { margin-top: 2em; margin-bottom: 0.25em; } +h3 { margin-top: 1.7em; margin-bottom: 0.25em; } +h4 { margin-top: 2em; margin-bottom: 0.3em; } +h5 { margin-top: 0px; margin-bottom: 0px; } +p { margin-top: 0em; margin-bottom: 0em; } +pre { margin-left: 0.6em; } +ul { margin-top: 0px; margin-bottom: 1em; } +li { margin-top: 0px; margin-bottom: 0px; } +li p { margin-top: 0px; margin-bottom: 0px; } +ol { margin-top: 0px; margin-bottom: 1em; } +dl { margin-top: 0px; margin-bottom: 1em; } +dt { margin-top: 0px; margin-bottom: 0px; font-weight: bold; } +dd { margin-top: 0px; margin-bottom: 0px; } + +/* Styles and colors */ +a:link { color: #0000FF; } +a:hover { color: #000080; } +a:visited { text-decoration: underline; } +a.header:link { text-decoration: none; color: InfoText } +a.header:visited { text-decoration: none; color: InfoText } +a.header:hover { text-decoration: underline; color: #000080; } +h4 { font-style: italic; } +strong { font-weight: bold; } +em { font-style: italic; } +var { font-style: italic; } +th { font-weight: bold; } |