From 765e52e2d30d0754625b8c7af6c36e93612f15be Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Wed, 4 Apr 2012 18:36:45 -0700 Subject: Add WindowBuilder propertysheet code. See README.txt for details. This reverts commit b33fa4c8ce6b3d2c10eaa92c5c26019d7326bd4c. Change-Id: I763357c42f933a52eda9b52988b785e092eac424 --- propertysheet/.classpath | 17 + propertysheet/.gitignore | 2 + propertysheet/.project | 17 + propertysheet/.settings/org.eclipse.jdt.core.prefs | 94 ++ propertysheet/Android.mk | 39 + propertysheet/MODULE_LICENSE_EPL | 0 propertysheet/NOTICE | 224 +++ propertysheet/README.txt | 105 ++ .../src/org/eclipse/wb/core/controls/CCombo3.java | 510 +++++++ .../org/eclipse/wb/core/controls/CComboBox.java | 664 ++++++++ .../org/eclipse/wb/core/controls/CFlatButton.java | 160 ++ .../org/eclipse/wb/core/controls/CImageLabel.java | 140 ++ .../src/org/eclipse/wb/core/controls/CSpinner.java | 569 +++++++ .../src/org/eclipse/wb/core/controls/Messages.java | 16 + .../eclipse/wb/core/controls/messages.properties | 2 + .../src/org/eclipse/wb/draw2d/IColorConstants.java | 97 ++ .../org/eclipse/wb/draw2d/ICursorConstants.java | 27 + .../eclipse/wb/internal/core/DesignerPlugin.java | 229 +++ .../eclipse/wb/internal/core/EnvironmentUtils.java | 33 + .../property/PropertyListIntersector.java | 136 ++ .../internal/core/icons/properties/BooleanNull.png | Bin 0 -> 213 bytes .../core/icons/properties/BooleanUnknown.png | Bin 0 -> 362 bytes .../wb/internal/core/icons/properties/dots.gif | Bin 0 -> 825 bytes .../wb/internal/core/icons/properties/down.png | Bin 0 -> 141 bytes .../wb/internal/core/icons/properties/false.png | Bin 0 -> 155 bytes .../wb/internal/core/icons/properties/minus.gif | Bin 0 -> 872 bytes .../wb/internal/core/icons/properties/plus.gif | Bin 0 -> 873 bytes .../wb/internal/core/icons/properties/true.png | Bin 0 -> 214 bytes .../wb/internal/core/model/ModelMessages.java | 27 + .../internal/core/model/ModelMessages.properties | 13 + .../core/model/property/ComplexProperty.java | 209 +++ .../core/model/property/EmptyProperty.java | 59 + .../wb/internal/core/model/property/Property.java | 232 +++ .../core/model/property/PropertyManager.java | 36 + .../model/property/category/PropertyCategory.java | 159 ++ .../category/PropertyCategoryProvider.java | 26 + .../category/PropertyCategoryProviders.java | 85 ++ .../editor/AbstractComboBoxPropertyEditor.java | 156 ++ .../editor/AbstractComboPropertyEditor.java | 153 ++ .../editor/AbstractListPropertyEditor.java | 173 +++ .../editor/AbstractTextPropertyEditor.java | 306 ++++ .../editor/BooleanObjectPropertyEditor.java | 118 ++ .../property/editor/BooleanPropertyEditor.java | 112 ++ .../property/editor/CharacterPropertyEditor.java | 80 + .../editor/DoubleObjectPropertyEditor.java | 92 ++ .../property/editor/DoublePropertyEditor.java | 84 + .../editor/EnumerationValuesPropertyEditor.java | 129 ++ .../model/property/editor/FloatPropertyEditor.java | 83 + .../property/editor/ITextValuePropertyEditor.java | 26 + .../editor/IValueSourcePropertyEditor.java | 25 + .../editor/IntegerObjectPropertyEditor.java | 92 ++ .../property/editor/IntegerPropertyEditor.java | 84 + .../property/editor/LocalePropertyEditor.java | 69 + .../property/editor/LongObjectPropertyEditor.java | 92 ++ .../model/property/editor/LongPropertyEditor.java | 83 + .../editor/PropertyDescriptorEditorProvider.java | 73 + .../core/model/property/editor/PropertyEditor.java | 117 ++ .../property/editor/PropertyEditorProvider.java | 44 + .../property/editor/ShortObjectPropertyEditor.java | 92 ++ .../model/property/editor/ShortPropertyEditor.java | 83 + .../property/editor/StringArrayPropertyEditor.java | 80 + .../property/editor/StringComboPropertyEditor.java | 65 + .../property/editor/StringListPropertyEditor.java | 95 ++ .../property/editor/TextControlActionsManager.java | 46 + .../property/editor/TextDialogPropertyEditor.java | 64 + .../property/editor/TextDisplayPropertyEditor.java | 66 + .../editor/complex/IComplexPropertyEditor.java | 27 + .../ButtonPropertyEditorPresentation.java | 114 ++ .../ButtonPropertyEditorPresentationImpl.java | 224 +++ .../ButtonPropertyEditorPresentationImplMac.java | 57 + .../CompoundPropertyEditorPresentation.java | 70 + .../presentation/PropertyEditorPresentation.java | 41 + .../editor/string/StringPropertyDialog.java | 141 ++ .../editor/string/StringPropertyEditor.java | 99 ++ .../model/property/table/HtmlTooltipHelper.java | 332 ++++ .../property/table/IPropertyExceptionHandler.java | 25 + .../model/property/table/IPropertyTooltipSite.java | 30 + .../core/model/property/table/PropertyTable.java | 1602 ++++++++++++++++++++ .../property/table/PropertyTableTooltipHelper.java | 191 +++ .../property/table/PropertyTooltipProvider.java | 117 ++ .../table/PropertyTooltipTextProvider.java | 67 + .../internal/core/model/property/table/Tooltip.css | 35 + .../org/eclipse/wb/internal/core/utils/Pair.java | 81 + .../controls/AbstractControlActionsManager.java | 174 +++ .../controls/DefaultControlActionsManager.java | 70 + .../wb/internal/core/utils/check/Assert.java | 361 +++++ .../core/utils/check/AssertionFailedException.java | 37 + .../core/utils/execution/ExecutionUtils.java | 292 ++++ .../internal/core/utils/execution/RunnableEx.java | 24 + .../core/utils/execution/RunnableObjectEx.java | 26 + .../core/utils/reflect/ClassLoaderLocalMap.java | 192 +++ .../wb/internal/core/utils/reflect/ClassMap.java | 75 + .../core/utils/reflect/ReflectionUtils.java | 327 ++++ .../wb/internal/core/utils/ui/DrawUtils.java | 337 ++++ .../wb/internal/core/utils/ui/GridDataFactory.java | 541 +++++++ .../internal/core/utils/ui/GridLayoutFactory.java | 123 ++ .../core/utils/ui/ImageImageDescriptor.java | 44 + .../wb/internal/core/utils/ui/PixelConverter.java | 72 + .../eclipse/wb/internal/core/utils/ui/UiUtils.java | 42 + .../core/utils/ui/dialogs/ResizableDialog.java | 221 +++ .../core/utils/ui/dialogs/StringsDialog.java | 77 + .../internal/core/utils/ui/dialogs/TextDialog.java | 118 ++ 102 files changed, 13115 insertions(+) create mode 100644 propertysheet/.classpath create mode 100644 propertysheet/.gitignore create mode 100644 propertysheet/.project create mode 100644 propertysheet/.settings/org.eclipse.jdt.core.prefs create mode 100644 propertysheet/Android.mk create mode 100644 propertysheet/MODULE_LICENSE_EPL create mode 100644 propertysheet/NOTICE create mode 100644 propertysheet/README.txt create mode 100644 propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java create mode 100644 propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java create mode 100644 propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java create mode 100644 propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java create mode 100644 propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java create mode 100644 propertysheet/src/org/eclipse/wb/core/controls/Messages.java create mode 100644 propertysheet/src/org/eclipse/wb/core/controls/messages.properties create mode 100644 propertysheet/src/org/eclipse/wb/draw2d/IColorConstants.java create mode 100644 propertysheet/src/org/eclipse/wb/draw2d/ICursorConstants.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/DesignerPlugin.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/EnvironmentUtils.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/editor/structure/property/PropertyListIntersector.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanNull.png create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanUnknown.png create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/dots.gif create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/down.png create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/false.png create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/minus.gif create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/plus.gif create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/icons/properties/true.png create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.properties create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/ComplexProperty.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/EmptyProperty.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/Property.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/PropertyManager.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategory.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProvider.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/category/PropertyCategoryProviders.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboBoxPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractComboPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractListPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/AbstractTextPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanObjectPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/BooleanPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/CharacterPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoubleObjectPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/DoublePropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/EnumerationValuesPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/FloatPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ITextValuePropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IValueSourcePropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerObjectPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/IntegerPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LocalePropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongObjectPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/LongPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyDescriptorEditorProvider.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/PropertyEditorProvider.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortObjectPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/ShortPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringArrayPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringComboPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/StringListPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextControlActionsManager.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDialogPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/TextDisplayPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/complex/IComplexPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentation.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImpl.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/ButtonPropertyEditorPresentationImplMac.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/CompoundPropertyEditorPresentation.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/presentation/PropertyEditorPresentation.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyDialog.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/editor/string/StringPropertyEditor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/HtmlTooltipHelper.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyExceptionHandler.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/IPropertyTooltipSite.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTable.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTableTooltipHelper.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipProvider.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/PropertyTooltipTextProvider.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/model/property/table/Tooltip.css create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java create mode 100644 propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java diff --git a/propertysheet/.classpath b/propertysheet/.classpath new file mode 100644 index 0000000..9d19fa9 --- /dev/null +++ b/propertysheet/.classpath @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/propertysheet/.gitignore b/propertysheet/.gitignore new file mode 100644 index 0000000..fe99505 --- /dev/null +++ b/propertysheet/.gitignore @@ -0,0 +1,2 @@ +bin + diff --git a/propertysheet/.project b/propertysheet/.project new file mode 100644 index 0000000..2c3adbb --- /dev/null +++ b/propertysheet/.project @@ -0,0 +1,17 @@ + + + propertysheet + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/propertysheet/.settings/org.eclipse.jdt.core.prefs b/propertysheet/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..4318953 --- /dev/null +++ b/propertysheet/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,94 @@ +#Sat Mar 17 18:51:09 PDT 2012 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.nonnull=com.android.annotations.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.android.annotations.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullisdefault=disabled +org.eclipse.jdt.core.compiler.annotation.nullable=com.android.annotations.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/propertysheet/Android.mk b/propertysheet/Android.mk new file mode 100644 index 0000000..b02fc80 --- /dev/null +++ b/propertysheet/Android.mk @@ -0,0 +1,39 @@ +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_JAVA_RESOURCE_DIRS := src + +LOCAL_JAVA_LIBRARIES := \ + swt \ + org.eclipse.core.commands_3.6.0.I20100512-1500 \ + org.eclipse.core.expressions_3.4.200.v20100505 \ + org.eclipse.core.runtime_3.6.0.v20100505 \ + org.eclipse.equinox.common_3.6.0.v20100503 \ + org.eclipse.jface_3.6.2.M20110210-1200 \ + org.eclipse.osgi_3.6.2.R36x_v20110210 \ + org.eclipse.ui.workbench.texteditor_3.6.1.r361_v20100714-0800 \ + org.eclipse.ui.workbench_3.6.2.M20110210-1200 \ + asm-tools \ + guava-tools + +LOCAL_MODULE := propertysheet + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/propertysheet/MODULE_LICENSE_EPL b/propertysheet/MODULE_LICENSE_EPL new file mode 100644 index 0000000..e69de29 diff --git a/propertysheet/NOTICE b/propertysheet/NOTICE new file mode 100644 index 0000000..0d347ab --- /dev/null +++ b/propertysheet/NOTICE @@ -0,0 +1,224 @@ + + Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF +THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +*1. DEFINITIONS* + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and +documentation distributed under this Agreement, and + +b) in the case of each subsequent Contributor: + +i) changes to the Program, and + +ii) additions to the Program; + +where such changes and/or additions to the Program originate from and +are distributed by that particular Contributor. A Contribution +'originates' from a Contributor if it was added to the Program by such +Contributor itself or anyone acting on such Contributor's behalf. +Contributions do not include additions to the Program which: (i) are +separate modules of software distributed in conjunction with the Program +under their own license agreement, and (ii) are not derivative works of +the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which +are necessarily infringed by the use or sale of its Contribution alone +or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +*2. GRANT OF RIGHTS* + +a) Subject to the terms of this Agreement, each Contributor hereby +grants Recipient a non-exclusive, worldwide, royalty-free copyright +license to reproduce, prepare derivative works of, publicly display, +publicly perform, distribute and sublicense the Contribution of such +Contributor, if any, and such derivative works, in source code and +object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby +grants Recipient a non-exclusive, worldwide, royalty-free patent license +under Licensed Patents to make, use, sell, offer to sell, import and +otherwise transfer the Contribution of such Contributor, if any, in +source code and object code form. This patent license shall apply to the +combination of the Contribution and the Program if, at the time the +Contribution is added by the Contributor, such addition of the +Contribution causes such combination to be covered by the Licensed +Patents. The patent license shall not apply to any other combinations +which include the Contribution. No hardware per se is licensed hereunder. + +c) Recipient understands that although each Contributor grants the +licenses to its Contributions set forth herein, no assurances are +provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient +copyright rights in its Contribution, if any, to grant the copyright +license set forth in this Agreement. + +*3. REQUIREMENTS* + +A Contributor may choose to distribute the Program in object code form +under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties +and conditions, express and implied, including warranties or conditions +of title and non-infringement, and implied warranties or conditions of +merchantability and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for +damages, including direct, indirect, special, incidental and +consequential damages, such as lost profits; + +iii) states that any provisions which differ from this Agreement are +offered by that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such +Contributor, and informs licensees how to obtain it in a reasonable +manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained +within the Program. + +Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution. + +*4. COMMERCIAL DISTRIBUTION* + +Commercial distributors of software may accept certain responsibilities +with respect to end users, business partners and the like. While this +license is intended to facilitate the commercial use of the Program, the +Contributor who includes the Program in a commercial product offering +should do so in a manner which does not create potential liability for +other Contributors. Therefore, if a Contributor includes the Program in +a commercial product offering, such Contributor ("Commercial +Contributor") hereby agrees to defend and indemnify every other +Contributor ("Indemnified Contributor") against any losses, damages and +costs (collectively "Losses") arising from claims, lawsuits and other +legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such +Commercial Contributor in connection with its distribution of the +Program in a commercial product offering. The obligations in this +section do not apply to any claims or Losses relating to any actual or +alleged intellectual property infringement. In order to qualify, an +Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial +Contributor to control, and cooperate with the Commercial Contributor +in, the defense and any related settlement negotiations. The Indemnified +Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages. + +*5. NO WARRANTY* + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED +ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES +OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR +A PARTICULAR PURPOSE. Each Recipient is solely responsible for +determining the appropriateness of using and distributing the Program +and assumes all risks associated with its exercise of rights under this +Agreement , including but not limited to the risks and costs of program +errors, compliance with applicable laws, damage to or loss of data, +programs or equipment, and unavailability or interruption of operations. + +*6. DISCLAIMER OF LIABILITY* + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR +ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +*7. GENERAL* + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including +a cross-claim or counterclaim in a lawsuit) alleging that the Program +itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails +to comply with any of the material terms or conditions of this Agreement +and does not cure such failure in a reasonable period of time after +becoming aware of such noncompliance. If all Recipient's rights under +this Agreement terminate, Recipient agrees to cease use and distribution +of the Program as soon as reasonably practicable. However, Recipient's +obligations under this Agreement and any licenses granted by Recipient +relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, +but in order to avoid inconsistency the Agreement is copyrighted and may +only be modified in the following manner. The Agreement Steward reserves +the right to publish new versions (including revisions) of this +Agreement from time to time. No one other than the Agreement Steward has +the right to modify this Agreement. The Eclipse Foundation is the +initial Agreement Steward. The Eclipse Foundation may assign the +responsibility to serve as the Agreement Steward to a suitable separate +entity. Each new version of the Agreement will be given a distinguishing +version number. The Program (including Contributions) may always be +distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is +published, Contributor may elect to distribute the Program (including +its Contributions) under the new version. Except as expressly stated in +Sections 2(a) and 2(b) above, Recipient receives no rights or licenses +to the intellectual property of any Contributor under this Agreement, +whether expressly, by implication, estoppel or otherwise. All rights in +the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to +this Agreement will bring a legal action under this Agreement more than +one year after the cause of action arose. Each party waives its rights +to a jury trial in any resulting litigation. + diff --git a/propertysheet/README.txt b/propertysheet/README.txt new file mode 100644 index 0000000..6c2feab --- /dev/null +++ b/propertysheet/README.txt @@ -0,0 +1,105 @@ +WINDOWBUILDER PROPERTY SHEET LIBRARY +------------------------------------- + +This project is a fork of a subset of the WindowBuilder Eclipse +plugin: http://www.eclipse.org/windowbuilder/ + +Specifically, it contains the subset of WindowBuilder related to the +propertysheet, intended for reuse in AOSP by the ADT plugin. + +The fork was modified as follows: +* Started with revision 424 from the trunk: + http://dev.eclipse.org/svnroot/tools/org.eclipse.windowbuilder/trunk + +* Extracted the property package from org.eclipse.wb.core: + src/org/eclipse/wb/internal/core/model/property + and then everything it transitively references. This turns out to + be a lot. I then started pruning out references to code we don't + need, such as support for editing Java constructs such as enums, or + dealing with a Java code model, etc. This means some of the files + have been edited to remove methods and fields. For example, the + property category code was modified to no longer support the + persistent storage of categories. + +* The WindowBuilder code depended on a number of Apache Commons + libraries such as collections, lang, etc. Since ADT already uses + Guava, which provides a lot of the same functionality, I replaced + all the Commons calls with Guava calls in order to avoid having to + make ADT depend on (and load at runtime) the Commons libraries. + +* Finally, the propertysheet code was made into a library instead of a + plugin, such that it can be loaded into the ADT plugin. This meant + mostly rewriting the DesignerPlugin class. It has kept its name + (since a lot of code references it for logging, resource loading + etc), but it is no longer an actual plugin. Instead it has init and + dispose methods for use by the AdtPlugin, and for logging it + delegates to the ADT plugin, etc. + +* Icons were moved into the DesignerPlugin package such that the + resource loading code could use a relative path, since with an + absolute path it would be looking in the embedding plugin's + resources. + +* To be consistent with the ADT codebase, I converted the files from + \r\n to \n newlines. Other than that, all formatting was left + unmodified. + +* Removed unused resources such as unreferences colors from + IColorConstants, unneeded messages from ModelMessages, and so on. + +* Note also that this Eclipse project is using a modified version of + the standard ADT Eclipse compiler settings: methods overriding other + methods and interfaces *without* using an @Override annotation are + ignored, since they were not using @Override annotations in the + WindowBuilder source base. + + +ADT ENHANCEMENTS +------------------ +* I also modified the propertysheet in a few ways to add features + needed by ADT. These are all bracketed in the codebase with + // BEGIN ADT MODIFICATIONS + ... + // END ADT MODIFICATIONS + + Specifically, I made the property table able to expand all and + collapse all. Properties have sorting priorities, and have separate + name and title attributes (and tooltips show the property name + rather than the title.) Text property editors allow field completion + by providing IContentProposalProvider (and optionally + ILabelProvider) instances via their getAdapter method. And the + property table will color values differently based on whether the + property is modified. (This allows us to draw default attributes + differently). Finally, the propertysheet now supports "expand by + default" (and for certain categories to be excluded, such as + deprecations). + + +UPDATES +-------- + +We should keep an eye on the propertysheet code in WindowBuilder and +migrate bug fixes and feature enhancements. To do that, first check +out revision 424 from +http://dev.eclipse.org/svnroot/tools/org.eclipse.windowbuilder/trunk +That's the same baseline that this fork was based on. +You can limit the checkout to just the org.eclipse.wb.core tree. + +Then check out the newest revision of WindowBuilder in a separate +directory. + +Now diff the two trees. Look for diffs in the packages related to the +propertysheet; this is going to be the packages that are present in +this library. If any of the diffs are related to the propertysheet or +supporting code, apply them to this library, and then update this +document to contain the new baseline revision (use 'svnversion .' to +get the number). Note that the diffs may need some rewriting if they +reference Apache Commons code. + +Note that the ComponentsPropertiesPage.java class which is the main +window in WindowBuilder is not used in our implementation; we instead +have the PropertySheetPage class in ADT, so changes in that class +should be checked to see whether they apply to our property sheet page +(which uses the PropertyTable in a similar way, but obviously is based +around our own UI model rather than the WindowBuilder ObjectInfo +model. diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java b/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java new file mode 100644 index 0000000..8782e96 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java @@ -0,0 +1,510 @@ +/******************************************************************************* + * 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.core.controls; + +import org.eclipse.wb.draw2d.IColorConstants; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.binding.editors.controls.DefaultControlActionsManager; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.TypedListener; +import org.eclipse.swt.widgets.Widget; + +/** + * Combo control for {@link PropertyTable} and combo property editors. + * + * @author scheglov_ke + * @coverage core.control + */ +public class CCombo3 extends Composite { + private final long m_createTime = System.currentTimeMillis(); + private final CImageLabel m_text; + private final Button m_arrow; + private final Shell m_popup; + private final Table m_table; + private boolean m_fullDropdownTableSize = false; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public CCombo3(Composite parent, int style) { + super(parent, style); + addEvents(this, m_comboListener, new int[]{SWT.Dispose, SWT.Move, SWT.Resize}); + // create label + { + m_text = new CImageLabel(this, SWT.NONE); + new DefaultControlActionsManager(m_text); + addEvents(m_text, m_textListener, new int[]{ + SWT.KeyDown, + SWT.KeyUp, + SWT.MouseDown, + SWT.MouseUp, + SWT.MouseMove, + SWT.MouseDoubleClick, + SWT.Traverse, + SWT.FocusIn, + SWT.FocusOut}); + } + // create arrow + { + m_arrow = new Button(this, SWT.ARROW | SWT.DOWN); + addEvents(m_arrow, m_arrowListener, new int[]{SWT.Selection, SWT.FocusIn, SWT.FocusOut}); + } + // create popup Shell + { + Shell shell = getShell(); + m_popup = new Shell(shell, SWT.NONE); + m_popup.setLayout(new FillLayout()); + } + // create table for items + { + m_table = new Table(m_popup, SWT.FULL_SELECTION); + addEvents(m_table, m_tableListener, new int[]{SWT.Selection, SWT.FocusIn, SWT.FocusOut}); + // + new TableColumn(m_table, SWT.NONE); + } + // Focus tracking filter + { + final Listener filter = new Listener() { + private boolean hasFocus; + + public void handleEvent(Event event) { + boolean old_hasFocus = hasFocus; + hasFocus = + m_text.isFocusControl() + || m_arrow.isFocusControl() + || m_popup.isFocusControl() + || m_table.isFocusControl(); + // configure colors + if (hasFocus) { + m_text.setBackground(IColorConstants.listSelection); + m_text.setForeground(IColorConstants.listSelectionText); + } else { + m_text.setBackground(IColorConstants.listBackground); + m_text.setForeground(IColorConstants.listForeground); + } + // send FocusOut event + if (old_hasFocus && !hasFocus) { + Event e = new Event(); + e.widget = CCombo3.this; + e.time = event.time; + notifyListeners(SWT.FocusOut, e); + } + } + }; + getDisplay().addFilter(SWT.FocusIn, filter); + addListener(SWT.Dispose, new Listener() { + public void handleEvent(Event event) { + getDisplay().removeFilter(SWT.FocusIn, filter); + } + }); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Events handling + // + //////////////////////////////////////////////////////////////////////////// + private final Listener m_comboListener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Dispose : + if (!m_popup.isDisposed()) { + m_popup.dispose(); + } + break; + case SWT.Move : + doDropDown(false); + break; + case SWT.Resize : + doResize(); + break; + } + } + }; + private final Listener m_textListener = new Listener() { + public void handleEvent(final Event event) { + switch (event.type) { + case SWT.MouseDown : + if (System.currentTimeMillis() - m_createTime < 400) { + // send "logical" double click for case when we just activated combo + // and almost right away click second time (but first time on editor) + event.detail = -1; + notifyListeners(SWT.MouseDoubleClick, event); + // when we use "auto drop on editor activation" option, this click is + // is "logically" second one, so it should close combo + if (!isDisposed()) { + doDropDown(false); + } + } else { + m_text.setCapture(true); + doDropDown(!isDropped()); + } + break; + case SWT.MouseUp : { + m_text.setCapture(false); + TableItem item = getItemUnderCursor(event); + if (item != null) { + doDropDown(false); + sendSelectionEvent(event); + } + break; + } + case SWT.MouseDoubleClick : + // prevent resending MouseDoubleClick that we sent on fast MouseDown + if (event.detail != -1) { + notifyListeners(SWT.MouseDoubleClick, event); + } + break; + case SWT.MouseMove : { + TableItem item = getItemUnderCursor(event); + if (item != null) { + m_table.setSelection(new TableItem[]{item}); + } + break; + } + case SWT.KeyDown : { + // check for keyboard navigation and selection + { + int selectionIndex = m_table.getSelectionIndex(); + if (event.keyCode == SWT.ARROW_UP) { + selectionIndex--; + if (selectionIndex < 0) { + selectionIndex = m_table.getItemCount() - 1; + } + m_table.setSelection(selectionIndex); + return; + } else if (event.keyCode == SWT.ARROW_DOWN) { + m_table.setSelection((selectionIndex + 1) % m_table.getItemCount()); + return; + } else if (event.character == SWT.CR || event.character == ' ') { + sendSelectionEvent(event); + return; + } + } + // be default just resend event + resendKeyEvent(event); + break; + } + case SWT.KeyUp : + resendKeyEvent(event); + break; + } + } + + private TableItem getItemUnderCursor(Event event) { + Point displayLocation = m_text.toDisplay(new Point(event.x, event.y)); + Point tableLocation = m_table.toControl(displayLocation); + return m_table.getItem(tableLocation); + } + }; + private final Listener m_arrowListener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + /*case SWT.FocusIn : { + resendFocusEvent(event); + break; + }*/ + case SWT.Selection : { + doDropDown(!isDropped()); + break; + } + } + } + }; + private final Listener m_tableListener = new Listener() { + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Selection : { + doDropDown(false); + // show selected item in text + { + int index = m_table.getSelectionIndex(); + select(index); + } + // send selection event + sendSelectionEvent(event); + break; + } + } + } + }; + + //////////////////////////////////////////////////////////////////////////// + // + // Events utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sends selection event. + */ + private void sendSelectionEvent(Event event) { + Event e = new Event(); + e.time = event.time; + e.stateMask = event.stateMask; + notifyListeners(SWT.Selection, e); + } + + /** + * Resends KeyDown/KeyUp events. + */ + private void resendKeyEvent(Event event) { + Event e = new Event(); + e.time = event.time; + e.character = event.character; + e.keyCode = event.keyCode; + e.stateMask = event.stateMask; + notifyListeners(event.type, e); + } + + /** + * Adds given listener as handler for events in given widget. + */ + private void addEvents(Widget widget, Listener listener, int[] events) { + for (int i = 0; i < events.length; i++) { + widget.addListener(events[i], listener); + } + } + + /** + * Adds the listener to receive events. + */ + public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection, typedListener); + addListener(SWT.DefaultSelection, typedListener); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Activity + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets drop state of combo. + */ + public void doDropDown(boolean drop) { + // check, may be we already in this drop state + if (drop == isDropped()) { + return; + } + // close combo + if (!drop) { + m_popup.setVisible(false); + m_text.setFocus(); + return; + } + // open combo + { + // prepare popup location + Point comboSize = getSize(); + Point popupLocation; + { + //popupLocation = getParent().toDisplay(getLocation()); + popupLocation = toDisplay(new Point(0, 0)); + popupLocation.y += comboSize.y; + } + // calculate and set popup location + { + TableColumn tableColumn = m_table.getColumn(0); + // pack everything + tableColumn.pack(); + m_table.pack(); + m_popup.pack(); + // calculate bounds + Rectangle tableBounds = m_table.getBounds(); + tableBounds.height = Math.min(tableBounds.height, m_table.getItemHeight() * 20); // max 20 items without scrolling + m_table.setBounds(tableBounds); + // calculate size + int remainingDisplayHeight = getDisplay().getClientArea().height - popupLocation.y - 10; + int preferredHeight = Math.min(tableBounds.height, remainingDisplayHeight); + int remainingDisplayWidth = getDisplay().getClientArea().width - popupLocation.x - 5; + int preferredWidth = + isFullDropdownTableWidth() + ? Math.min(tableBounds.width, remainingDisplayWidth) + : comboSize.x; + // set popup bounds calculated as computeTrim basing on combo width and table height paying attention on remaining display space + Rectangle popupBounds = + m_popup.computeTrim(popupLocation.x, popupLocation.y, preferredWidth, preferredHeight); + m_popup.setBounds(popupBounds); + // adjust column size + tableColumn.setWidth(m_table.getClientArea().width); + } + m_popup.setVisible(true); + // scroll to selection if needed + m_table.showSelection(); + } + } + + /** + * Initiates "press-hold-drag" sequence. + */ + public void startDrag() { + m_text.setCapture(true); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + public void setFullDropdownTableWidth(boolean freeTableSize) { + m_fullDropdownTableSize = freeTableSize; + } + + public boolean isFullDropdownTableWidth() { + return m_fullDropdownTableSize; + } + + public boolean isDropped() { + return m_popup.isVisible(); + } + + public void setQuickSearch(boolean value) { + // TODO + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access: items + // + //////////////////////////////////////////////////////////////////////////// + /** + * Removes all items. + */ + public void removeAll() { + TableItem[] items = m_table.getItems(); + for (int index = 0; index < items.length; index++) { + TableItem item = items[index]; + item.dispose(); + } + } + + /** + * Adds new item with given text. + */ + public void add(String text) { + add(text, null); + } + + /** + * Adds new item with given text and image. + */ + public void add(String text, Image image) { + checkWidget(); + TableItem item = new TableItem(m_table, SWT.NONE); + item.setText(text); + item.setImage(image); + } + + /** + * @return an item at given index + */ + public String getItem(int index) { + checkWidget(); + return m_table.getItem(index).getText(); + } + + /** + * @return the number of items + */ + public int getItemCount() { + checkWidget(); + return m_table.getItemCount(); + } + + /** + * @return the index of the selected item + */ + public int getSelectionIndex() { + checkWidget(); + return m_table.getSelectionIndex(); + } + + /** + * Selects an item with given index. + */ + public void select(int index) { + checkWidget(); + if (index == -1) { + m_table.deselectAll(); + m_text.setText(null); + m_text.setImage(null); + return; + } else { + TableItem item = m_table.getItem(index); + m_text.setText(item.getText()); + m_text.setImage(item.getImage()); + m_table.select(index); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access: text and image + // + //////////////////////////////////////////////////////////////////////////// + /** + * Selects item with given text. + */ + public void setText(String text) { + // try to find item with given text + TableItem[] items = m_table.getItems(); + for (int index = 0; index < items.length; index++) { + TableItem item = items[index]; + if (item.getText().equals(text)) { + select(index); + return; + } + } + // not found, remove selection + select(-1); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Resize support + // TODO: computeSize + // + //////////////////////////////////////////////////////////////////////////// + protected void doResize() { + Rectangle clientArea = getClientArea(); + int areaWidth = clientArea.width; + int areaHeight = clientArea.height; + // compute sizes of controls + Point buttonSize = m_arrow.computeSize(areaHeight, areaHeight); + Point textSize = m_text.computeSize(areaWidth - buttonSize.x, areaHeight); + // set controls location/size + m_arrow.setLocation(areaWidth - buttonSize.x, 0); + m_arrow.setSize(buttonSize); + m_text.setSize(areaWidth - buttonSize.x, Math.max(textSize.y, areaHeight)); + } +} diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java b/propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java new file mode 100644 index 0000000..9f0c8f9 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java @@ -0,0 +1,664 @@ +/******************************************************************************* + * 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.core.controls; + +import com.google.common.collect.Lists; + +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TypedEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +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.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.TypedListener; +import org.eclipse.wb.internal.core.model.property.editor.TextControlActionsManager; +import org.eclipse.wb.internal.core.model.property.table.PropertyTable; +import org.eclipse.wb.internal.core.utils.check.Assert; + +import java.util.ArrayList; + +/** + * Extended ComboBox control for {@link PropertyTable} and combo property editors. + * + * @author sablin_aa + * @coverage core.control + */ +public class CComboBox extends Composite { + private Text m_text; + private Button m_button; + private Canvas m_canvas; + private Shell m_popup; + private TableViewer m_table; + private boolean m_fullDropdownTableWidth = false; + private boolean m_wasFocused; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public CComboBox(Composite parent, int style) { + super(parent, style); + createContents(this); + m_wasFocused = isComboFocused(); + // add display hook + final Listener displayFocusInHook = new Listener() { + @Override + public void handleEvent(Event event) { + boolean focused = isComboFocused(); + if (m_wasFocused && !focused) { + // close DropDown on focus out ComboBox + comboDropDown(false); + } + if (event.widget != CComboBox.this) { + // forward to ComboBox listeners + if (!m_wasFocused && focused) { + event.widget = CComboBox.this; + notifyListeners(SWT.FocusIn, event); + } + if (m_wasFocused && !focused) { + event.widget = CComboBox.this; + notifyListeners(SWT.FocusOut, event); + } + } + m_wasFocused = focused; + } + }; + final Listener displayFocusOutHook = new Listener() { + @Override + public void handleEvent(Event event) { + m_wasFocused = isComboFocused(); + } + }; + { + Display display = getDisplay(); + display.addFilter(SWT.FocusIn, displayFocusInHook); + display.addFilter(SWT.FocusOut, displayFocusOutHook); + } + // combo listeners + addControlListener(new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + resizeInner(); + } + }); + addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + { + // remove Display hooks + Display display = getDisplay(); + display.removeFilter(SWT.FocusIn, displayFocusInHook); + display.removeFilter(SWT.FocusOut, displayFocusOutHook); + } + disposeInner(); + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Contents + // + //////////////////////////////////////////////////////////////////////////// + protected void createContents(Composite parent) { + createText(parent); + createButton(parent); + createImage(parent); + createPopup(parent); + } + + /** + * Create Text widget. + */ + protected void createText(Composite parent) { + m_text = new Text(parent, SWT.NONE); + new TextControlActionsManager(m_text); + // key press processing + m_text.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + switch (e.keyCode) { + case SWT.ESC : + if (isDroppedDown()) { + // close dropdown + comboDropDown(false); + e.doit = false; + } else { + // forward to ComboBox listeners + notifyListeners(SWT.KeyDown, convert2event(e)); + } + break; + case SWT.ARROW_UP : + if (isDroppedDown()) { + // prev item in dropdown list + Table table = m_table.getTable(); + int index = table.getSelectionIndex() - 1; + table.setSelection(index < 0 ? table.getItemCount() - 1 : index); + e.doit = false; + } else { + // forward to ComboBox listeners + notifyListeners(SWT.KeyDown, convert2event(e)); + } + break; + case SWT.ARROW_DOWN : + if (isDroppedDown()) { + // next item in dropdown list + Table table = m_table.getTable(); + int index = table.getSelectionIndex() + 1; + table.setSelection(index == table.getItemCount() ? 0 : index); + e.doit = false; + } else if ((e.stateMask & SWT.ALT) != 0) { + // force drop down combo + comboDropDown(true); + e.doit = false; + // return focus to text + setFocus2Text(false); + } else { + // forward to ComboBox listeners + notifyListeners(SWT.KeyDown, convert2event(e)); + } + break; + case '\r' : + Table table = m_table.getTable(); + if (isDroppedDown() && table.getSelectionIndex() != -1) { + // forward to Table listeners + table.notifyListeners(SWT.Selection, convert2event(e)); + } else { + m_text.selectAll(); + setSelectionText(getEditText()); + // forward to ComboBox listeners + notifyListeners(SWT.Selection, convert2event(e)); + } + break; + } + } + }); + // modifications processing + m_text.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + if (isDroppedDown()) { + m_table.refresh(); + } else { + // force drop down combo + if (m_text.isFocusControl()) { + comboDropDown(true); + // return focus to text + setFocus2Text(false); + } + } + } + }); + } + + /** + * Create arrow button. + */ + protected void createButton(Composite parent) { + m_button = new Button(parent, SWT.ARROW | SWT.DOWN); + m_button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + comboDropDown(!isDroppedDown()); + // return focus to text + setFocus2Text(true); + } + }); + } + + /** + * Create image canvas. + */ + protected void createImage(Composite parent) { + m_canvas = new Canvas(parent, SWT.BORDER); + m_canvas.addPaintListener(new PaintListener() { + @Override + public void paintControl(PaintEvent e) { + Image selectionImage = getSelectionImage(); + if (selectionImage != null) { + e.gc.drawImage(selectionImage, 0, 0); + } else { + e.gc.fillRectangle(m_canvas.getClientArea()); + } + } + }); + } + + /** + * Create popup shell with table. + */ + protected void createPopup(Composite parent) { + m_popup = new Shell(getShell(), SWT.BORDER); + m_popup.setLayout(new FillLayout()); + createTable(m_popup); + } + + /** + * Create table. + */ + protected void createTable(Composite parent) { + m_table = new TableViewer(parent, SWT.FULL_SELECTION); + new TableViewerColumn(m_table, SWT.LEFT); + m_table.getTable().addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + int selectionIndex = m_table.getTable().getSelectionIndex(); + setSelectionIndex(selectionIndex); + comboDropDown(false); + // forward to ComboBox listeners + notifyListeners(SWT.Selection, convert2event(e)); + } + }); + m_table.setContentProvider(getContentProvider()); + m_table.setLabelProvider(getLabelProvider()); + m_table.addFilter(getFilterProvider()); + } + + /** + * Placement inner widgets. + */ + protected void resizeInner() { + Rectangle clientArea = getClientArea(); + int rightOccupied = 0; + int leftOccupied = 0; + { + // button + m_button.setBounds( + clientArea.width - clientArea.height, + 0, + clientArea.height, + clientArea.height); + rightOccupied = clientArea.height; + } + { + Image selectionImage = getSelectionImage(); + if (selectionImage != null) { + // image + m_canvas.setSize(clientArea.height, clientArea.height); + leftOccupied = clientArea.height; + } else { + m_canvas.setSize(1, clientArea.height); + leftOccupied = 1; + } + } + { + // text + m_text.setBounds( + leftOccupied, + 0, + clientArea.width - rightOccupied - leftOccupied, + clientArea.height); + } + } + + /** + * Dispose inner widgets. + */ + protected void disposeInner() { + if (!m_popup.isDisposed()) { + m_popup.dispose(); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Providers + // + //////////////////////////////////////////////////////////////////////////// + protected IContentProvider getContentProvider() { + return new IStructuredContentProvider() { + @Override + public Object[] getElements(Object inputElement) { + return m_items.toArray(new ComboBoxItem[m_items.size()]); + } + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + }; + } + + protected IBaseLabelProvider getLabelProvider() { + return new LabelProvider() { + @Override + public Image getImage(Object element) { + ComboBoxItem item = (ComboBoxItem) element; + return item.m_image; + } + + @Override + public String getText(Object element) { + ComboBoxItem item = (ComboBoxItem) element; + return item.m_label; + } + }; + } + + protected ViewerFilter getFilterProvider() { + return new ViewerFilter() { + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + String lookingString = m_text.getText().toLowerCase(); + if (isDroppedDown() && lookingString.length() > 0) { + ComboBoxItem item = (ComboBoxItem) element; + return item.m_label.toLowerCase().indexOf(lookingString) != -1; + } + return true; + } + }; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Items + // + //////////////////////////////////////////////////////////////////////////// + protected static class ComboBoxItem { + public final String m_label; + public final Image m_image; + + public ComboBoxItem(String label, Image image) { + m_label = label; + m_image = image; + } + } + + ArrayList m_items = Lists.newArrayList(); + + /** + * Add new item. + */ + public void addItem(String label, Image image) { + Assert.isTrue(!isDroppedDown()); + m_items.add(new ComboBoxItem(label, image)); + } + + public void addItem(String label) { + addItem(label, null); + } + + public void removeAll() { + m_items.clear(); + } + + public int getItemCount() { + return m_items.size(); + } + + public String getItemLabel(int index) { + return m_items.get(index).m_label; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + public boolean isComboFocused() { + return isFocusControl() + || m_text.isFocusControl() + || m_button.isFocusControl() + || m_canvas.isFocusControl() + || m_popup.isFocusControl() + || m_table.getTable().isFocusControl(); + } + + /** + * Edit text. + */ + public String getEditText() { + return m_text.getText(); + } + + public void setEditText(String text) { + m_text.setText(text == null ? "" : text); + m_text.selectAll(); + } + + public void setEditSelection(int start, int end) { + m_text.setSelection(start, end); + } + + /** + * Read only. + */ + public void setReadOnly(boolean value) { + m_text.setEditable(!value); + m_button.setEnabled(!value); + } + + /** + * Drop down width. + */ + public boolean isFullDropdownTableWidth() { + return m_fullDropdownTableWidth; + } + + public void setFullDropdownTableWidth(boolean value) { + Assert.isTrue(!isDroppedDown()); + m_fullDropdownTableWidth = value; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Selection + // + //////////////////////////////////////////////////////////////////////////// + private int m_selectionIndex = -1; + + /** + * Selection index. + */ + public int getSelectionIndex() { + return m_selectionIndex; + } + + public void setSelectionIndex(int index) { + m_selectionIndex = index; + if (isDroppedDown()) { + m_table.getTable().setSelection(m_selectionIndex); + } + setEditText(getSelectionText()); + } + + /** + * Selection text. + */ + private String getSelectionText() { + if (m_selectionIndex != -1 && isDroppedDown()) { + Object itemData = m_table.getTable().getItem(m_selectionIndex).getData(); + return ((ComboBoxItem) itemData).m_label; + } + return null; + } + + /** + * Selection image. + */ + private Image getSelectionImage() { + return m_selectionIndex != -1 ? m_items.get(m_selectionIndex).m_image : null; + } + + public void setSelectionText(String label) { + TableItem[] items = m_table.getTable().getItems(); + for (int i = 0; i < items.length; i++) { + TableItem item = items[i]; + if (item.getText().equals(label)) { + setSelectionIndex(i); + return; + } + } + // no such item + setSelectionIndex(-1); + setEditText(label); + } + + /** + * Adds the listener to receive events. + */ + public void addSelectionListener(SelectionListener listener) { + checkWidget(); + if (listener == null) { + SWT.error(SWT.ERROR_NULL_ARGUMENT); + } + TypedListener typedListener = new TypedListener(listener); + addListener(SWT.Selection, typedListener); + addListener(SWT.DefaultSelection, typedListener); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Popup + // + //////////////////////////////////////////////////////////////////////////// + public boolean isDroppedDown() { + return m_popup.isVisible(); + } + + public void comboDropDown(boolean dropdown) { + // check, may be we already in this drop state + if (dropdown == isDroppedDown()) { + return; + } + // close combo + if (dropdown) { + // initialize + m_table.setInput(m_items); + Table table = m_table.getTable(); + TableColumn column = table.getColumn(0); + column.pack(); + table.pack(); + m_popup.pack(); + // compute table size + Rectangle tableBounds = table.getBounds(); + tableBounds.height = Math.min(tableBounds.height, table.getItemHeight() * 15);// max 15 items without scrolling + table.setBounds(tableBounds); + // prepare popup point + Point comboLocation = toDisplay(new Point(0, 0)); + Point comboSize = getSize(); + // compute popup size + Display display = getDisplay(); + Rectangle clientArea = display.getClientArea(); + int remainingDisplayHeight = clientArea.height - comboLocation.y - comboSize.y - 10; + int preferredHeight = Math.min(tableBounds.height, remainingDisplayHeight); + int remainingDisplayWidth = clientArea.width - comboLocation.x - 10; + int preferredWidth = + isFullDropdownTableWidth() + ? Math.min(tableBounds.width, remainingDisplayWidth) + : comboSize.x; + Rectangle popupBounds = + new Rectangle(comboLocation.x, + comboLocation.y + comboSize.y, + preferredWidth, + preferredHeight); + Rectangle trimBounds = + m_popup.computeTrim(popupBounds.x, popupBounds.y, popupBounds.width, popupBounds.height); + m_popup.setBounds(popupBounds.x, popupBounds.y, 2 * popupBounds.width - trimBounds.width, 2 + * popupBounds.height + - trimBounds.height); + // adjust column size + column.setWidth(table.getClientArea().width); + // show popup + m_popup.setVisible(true); + table.setSelection(getSelectionIndex()); + } else { + // hide popup + m_popup.setVisible(false); + } + } + + protected final void setFocus2Text(final boolean selectAll) { + getDisplay().asyncExec(new Runnable() { + final boolean m_selectAll = selectAll; + + @Override + public void run() { + if (!m_text.isDisposed()) { + m_text.setFocus(); + if (m_selectAll) { + m_text.selectAll(); + } + } + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utilities + // + //////////////////////////////////////////////////////////////////////////// + protected static Event convert2event(TypedEvent tEvent) { + Event event = new Event(); + event.widget = tEvent.widget; + event.display = tEvent.display; + event.widget = tEvent.widget; + event.time = tEvent.time; + event.data = tEvent.data; + if (tEvent instanceof KeyEvent) { + KeyEvent kEvent = (KeyEvent) tEvent; + event.character = kEvent.character; + event.keyCode = kEvent.keyCode; + event.stateMask = kEvent.stateMask; + event.doit = kEvent.doit; + } + if (tEvent instanceof SelectionEvent) { + SelectionEvent sEvent = (SelectionEvent) tEvent; + event.item = sEvent.item; + event.x = sEvent.x; + event.y = sEvent.y; + event.width = sEvent.width; + event.height = sEvent.height; + event.detail = sEvent.detail; + event.stateMask = sEvent.stateMask; + event.text = sEvent.text; + event.doit = sEvent.doit; + } + return event; + } +} diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java b/propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java new file mode 100644 index 0000000..156cf5e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * 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.core.controls; + +import org.eclipse.wb.draw2d.IColorConstants; +import org.eclipse.wb.internal.core.utils.ui.DrawUtils; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +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; + +/** + * Class representing flat push button as it looks in Mac OSX. + * + * It doesn't draw text, not need for now. ;-) + * + * @author mitin_aa + */ +public final class CFlatButton extends Canvas { + // colors + private static final Color COLOR_FACE = DrawUtils.getShiftedColor(IColorConstants.button, 12); + private static final Color COLOR_FACE_SELECTED = IColorConstants.buttonDarker; + private static final Color COLOR_BORDER_GRADIENT1 = DrawUtils.getShiftedColor( + IColorConstants.button, + -12); + private static final Color COLOR_BORDER_GRADIENT1_SELECTED = DrawUtils.getShiftedColor( + IColorConstants.buttonDarker, + 64); + private static final Color COLOR_BORDER_GRADIENT2 = DrawUtils.getShiftedColor(COLOR_FACE, -8); + private static final Color COLOR_BORDER_GRADIENT2_SELECTED = DrawUtils.getShiftedColor( + COLOR_FACE_SELECTED, + -8); + // fields + private Image m_image; + private boolean m_down; + private boolean m_selected; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public CFlatButton(Composite parent, int style) { + super(parent, style); + addPaintListener(new PaintListener() { + public void paintControl(PaintEvent e) { + boolean isSelected = m_down | m_selected; + Color faceColor = isSelected ? COLOR_FACE_SELECTED : COLOR_FACE; + Color borderGradientColor1 = + isSelected ? COLOR_BORDER_GRADIENT1_SELECTED : COLOR_BORDER_GRADIENT1; + Color borderGradientColor2 = + isSelected ? COLOR_BORDER_GRADIENT2_SELECTED : COLOR_BORDER_GRADIENT2; + GC gc = e.gc; + Rectangle ca = getClientArea(); + // draw client area + // dark border + gc.setForeground(IColorConstants.buttonDarker); + gc.drawRectangle(ca.x, ca.y, ca.width - 1, ca.height - 1); + cropClientArea(ca); + // gradient border + gc.setForeground(borderGradientColor1); + gc.setBackground(borderGradientColor2); + gc.fillGradientRectangle(ca.x, ca.y, ca.width, ca.height, true); + cropClientArea(ca); + // fill background + gc.setBackground(faceColor); + gc.fillRectangle(ca); + // draw face upper-half gradient + Rectangle ca1 = getClientArea(); + cropClientArea(ca1); + gc.setForeground(faceColor); + gc.setBackground(borderGradientColor1); + gc.fillGradientRectangle(ca1.x, ca1.y, ca1.width, ca1.height / 4, true); + // draw face down-half gradient + ca1.x += 1; + ca1.width -= 2; + gc.setForeground(borderGradientColor1); + gc.setBackground(faceColor); + gc.fillGradientRectangle(ca1.x, ca1.y + ca1.height / 4 - 1, ca1.width, ca1.height / 2, true); + // draw image + Image image = getImage(); + if (image != null) { + Rectangle imageBounds = image.getBounds(); + // center it in client area + int x = ca.x + (ca.width - imageBounds.width) / 2; + int y = ca.y + (ca.height - imageBounds.height) / 2; + gc.drawImage(image, x, y); + } + } + }); + addListener(SWT.MouseDown, new Listener() { + public void handleEvent(Event e) { + m_down = true; + redraw(); + } + }); + addListener(SWT.MouseUp, new Listener() { + public void handleEvent(Event e) { + m_down = false; + redraw(); + update(); + if (getClientArea().contains(e.x, e.y)) { + fireSelectionEvent(e.time, e.stateMask); + } + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + private void fireSelectionEvent(int time, int stateMask) { + Event event = new Event(); + event.time = time; + event.stateMask = stateMask; + notifyListeners(SWT.Selection, event); + } + + private void cropClientArea(Rectangle ca) { + ca.x += 1; + ca.y += 1; + ca.width -= 2; + ca.height -= 2; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + public final Image getImage() { + return m_image; + } + + public void setImage(Image image) { + m_image = image; + } + + public void setSelected(boolean selected) { + m_selected = selected; + } +} diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java b/propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java new file mode 100644 index 0000000..eb5bce4 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * 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.core.controls; + +import org.eclipse.swt.SWT; +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; + +/** + * Simple control for displaying image and text. + * + * For unknown reason CLabel shows such things not very good - vertical text alignment is strange + * (bottom?). + * + * @author scheglov_ke + * @coverage core.control + */ +public class CImageLabel extends Canvas { + private static final int SPACE = 5; + private Image m_image; + private String m_text; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public CImageLabel(Composite parent, int style) { + super(parent, style | SWT.NO_BACKGROUND); + addListener(SWT.Dispose, new Listener() { + public void handleEvent(Event event) { + if (m_backImage != null) { + m_backImage.dispose(); + m_backImage = null; + } + } + }); + addListener(SWT.Paint, new Listener() { + public void handleEvent(Event event) { + doPaint(event.gc); + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + public Image getImage() { + return m_image; + } + + public void setImage(Image image) { + m_image = image; + redraw(); + } + + public String getText() { + return m_text; + } + + public void setText(String text) { + m_text = text; + redraw(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Paint + // + //////////////////////////////////////////////////////////////////////////// + private Image m_backImage; + + private void doPaint(GC paintGC) { + Rectangle clientArea = getClientArea(); + // prepare back image + GC gc; + { + if (m_backImage == null || !m_backImage.getBounds().equals(clientArea)) { + if (m_backImage != null) { + m_backImage.dispose(); + } + m_backImage = new Image(getDisplay(), clientArea.width, clientArea.height); + } + // + gc = new GC(m_backImage); + gc.setBackground(paintGC.getBackground()); + gc.setForeground(paintGC.getForeground()); + gc.fillRectangle(clientArea); + } + // + Point textExtent = m_text == null ? new Point(0, 0) : gc.textExtent(m_text); + Rectangle imageBounds = m_image == null ? new Rectangle(0, 0, 0, 0) : m_image.getBounds(); + // + if (m_image != null) { + int x = clientArea.x; + int y = clientArea.y + (clientArea.height - imageBounds.height) / 2; + gc.drawImage(m_image, x, y); + } + if (m_text != null) { + int x = clientArea.x + imageBounds.width + SPACE; + int y = clientArea.y + (clientArea.height - textExtent.y) / 2; + gc.drawText(m_text, x, y); + } + // flush back image + { + paintGC.drawImage(m_backImage, 0, 0); + gc.dispose(); + } + } + + @Override + public Point computeSize(int wHint, int hHint, boolean changed) { + // prepare text size + GC gc = new GC(this); + Point textExtent = m_text == null ? new Point(0, 0) : gc.textExtent(m_text); + gc.dispose(); + // prepare image size + Rectangle imageBounds = m_image == null ? new Rectangle(0, 0, 0, 0) : m_image.getBounds(); + // calculate control size + int width = imageBounds.width + SPACE + textExtent.x; + int height = Math.max(imageBounds.height, textExtent.y); + return new Point(width, height); + } +} diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java b/propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java new file mode 100644 index 0000000..d414df9 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java @@ -0,0 +1,569 @@ +/******************************************************************************* + * 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.core.controls; + +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.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Spinner; +import org.eclipse.swt.widgets.Text; + +import java.text.DecimalFormat; +import java.text.MessageFormat; +import java.text.ParseException; + +/** + * Custom implementation of {@link Spinner}. + * + * @author scheglov_ke + * @coverage core.control + */ +public class CSpinner extends Composite { + private static final Color COLOR_VALID = Display.getCurrent().getSystemColor( + SWT.COLOR_LIST_BACKGROUND); + private static final Color COLOR_INVALID = new Color(null, 255, 230, 230); + private int m_minimum = 0; + private int m_maximum = 100; + private int m_increment = 1; + private int m_value = 0; + private int m_multiplier = 1; + private String m_formatPattern = "0"; + private DecimalFormat m_format = new DecimalFormat(m_formatPattern); + //////////////////////////////////////////////////////////////////////////// + // + // GUI fields + // + //////////////////////////////////////////////////////////////////////////// + private final Button m_button; + private final Text m_text; + private final Spinner m_spinner; + private Composite win32Hack; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public CSpinner(Composite parent, int style) { + super(parent, style); + m_button = new Button(this, SWT.ARROW | SWT.DOWN); + { + int textStyle = SWT.SINGLE | SWT.RIGHT; + if (IS_OS_MAC_OSX_COCOA) { + textStyle |= SWT.BORDER; + } + m_text = new Text(this, textStyle); + m_text.setText("" + m_value); + m_text.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN) { + e.doit = false; + updateValue(e.keyCode); + } + } + + @Override + public void keyReleased(KeyEvent e) { + try { + m_value = (int) (m_format.parse(m_text.getText()).doubleValue() * m_multiplier); + if (m_value < m_minimum || m_value > m_maximum) { + m_text.setBackground(COLOR_INVALID); + setState(MessageFormat.format( + Messages.CSpinner_outOfRange, + m_value, + m_minimum, + m_maximum)); + notifySelectionListeners(false); + } else { + setState(null); + notifySelectionListeners(true); + } + } catch (ParseException ex) { + setState(MessageFormat.format( + Messages.CSpinner_canNotParse, + m_text.getText(), + m_formatPattern)); + notifySelectionListeners(false); + } + } + }); + } + if (!IS_OS_MAC_OSX) { + win32Hack = new Composite(this, SWT.NONE); + win32Hack.setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE)); + win32Hack.moveAbove(null); + win32Hack.moveBelow(m_text); + } + { + m_spinner = new Spinner(this, SWT.VERTICAL); + m_spinner.setMinimum(0); + m_spinner.setMaximum(50); + m_spinner.setIncrement(1); + m_spinner.setPageIncrement(1); + m_spinner.setSelection(25); + m_spinner.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + setFocus(); + } + }); + m_spinner.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + m_text.forceFocus(); + if (m_spinner.getSelection() > 25) { + updateValue(SWT.ARROW_UP); + } else { + updateValue(SWT.ARROW_DOWN); + } + m_spinner.setSelection(25); + } + }); + setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE)); + if (IS_OS_WINDOWS_XP || IS_OS_WINDOWS_2003) { + setLayout(new WindowsXpLayout()); + } else if (IS_OS_WINDOWS_VISTA || IS_OS_WINDOWS_7) { + setLayout(new WindowsVistaLayout()); + } else if (IS_OS_LINUX) { + setLayout(new LinuxLayout()); + } else if (IS_OS_MAC_OSX) { + if (IS_OS_MAC_OSX_COCOA) { + setLayout(new MacCocoaLayout()); + } else { + setLayout(new MacLayout()); + } + } else { + setLayout(new WindowsXpLayout()); + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + m_text.setEnabled(enabled); + m_spinner.setEnabled(enabled); + } + + /** + * Sets the number of decimal places used by the receiver. + *

+ * See {@link Spinner#setDigits(int)}. + */ + public void setDigits(int digits) { + m_formatPattern = "0."; + m_multiplier = 1; + for (int i = 0; i < digits; i++) { + m_formatPattern += "0"; + m_multiplier *= 10; + } + m_format = new DecimalFormat(m_formatPattern); + updateText(); + } + + /** + * Sets minimum and maximum using single invocation. + */ + public void setRange(int minimum, int maximum) { + setMinimum(minimum); + setMaximum(maximum); + } + + /** + * @return the minimum value that the receiver will allow. + */ + public int getMinimum() { + return m_minimum; + } + + /** + * Sets the minimum value that the receiver will allow. + */ + public void setMinimum(int minimum) { + m_minimum = minimum; + setSelection(Math.max(m_value, m_minimum)); + } + + /** + * Sets the maximum value that the receiver will allow. + */ + public void setMaximum(int maximum) { + m_maximum = maximum; + setSelection(Math.min(m_value, m_maximum)); + } + + /** + * Sets the amount that the receiver's value will be modified by when the up/down arrows are + * pressed to the argument, which must be at least one. + */ + public void setIncrement(int increment) { + m_increment = increment; + } + + /** + * Sets the value, which is the receiver's position, to the argument. If the argument is + * not within the range specified by minimum and maximum, it will be adjusted to fall within this + * range. + */ + public void setSelection(int newValue) { + newValue = Math.min(Math.max(m_minimum, newValue), m_maximum); + if (newValue != m_value) { + m_value = newValue; + updateText(); + // set valid state + setState(null); + } + } + + private void updateText() { + String text = m_format.format((double) m_value / m_multiplier); + m_text.setText(text); + m_text.selectAll(); + } + + /** + * @return the selection, which is the receiver's position. + */ + public int getSelection() { + return m_value; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Update + // + //////////////////////////////////////////////////////////////////////////// + /** + * Updates {@link #m_value} into given direction. + */ + private void updateValue(int direction) { + // prepare new value + int newValue; + { + newValue = m_value; + if (direction == SWT.ARROW_UP) { + newValue += m_increment; + } + if (direction == SWT.ARROW_DOWN) { + newValue -= m_increment; + } + } + // update value + setSelection(newValue); + notifySelectionListeners(true); + } + + /** + * Sets the valid/invalid state. + * + * @param message + * the message to show, or null if valid. + */ + private void setState(String message) { + m_text.setToolTipText(message); + if (message == null) { + m_text.setBackground(COLOR_VALID); + } else { + m_text.setBackground(COLOR_INVALID); + } + } + + /** + * Notifies {@link SWT#Selection} listeners with value and state. + */ + private void notifySelectionListeners(boolean valid) { + Event event = new Event(); + event.detail = m_value; + event.doit = valid; + notifyListeners(SWT.Selection, event); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Windows XP + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for Windows XP. + */ + private class WindowsXpLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // add Text widget margin + size.y += 2; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + // set bounds for Spinner and Text + m_spinner.setBounds( + cRect.x + cRect.width - sSize.x + 1, + cRect.y - 1, + sSize.x, + cRect.height + 2); + m_text.setBounds(cRect.x, cRect.y + 1, cRect.width - arrowWidth, tSize.y); + win32Hack.setBounds(cRect.x, cRect.y, cRect.width - arrowWidth, sSize.y); + } + } + //////////////////////////////////////////////////////////////////////////// + // + // Windows Vista + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for Windows Vista. + */ + private class WindowsVistaLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // add Text widget margin + size.y += 3; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + // set bounds for Spinner and Text + m_spinner.setBounds( + cRect.x + cRect.width - sSize.x + 1, + cRect.y - 1, + sSize.x, + cRect.height + 2); + m_text.setBounds(cRect.x, cRect.y + 1, cRect.width - arrowWidth, tSize.y); + win32Hack.setBounds(cRect.x, cRect.y, cRect.width - arrowWidth, sSize.y); + } + } + //////////////////////////////////////////////////////////////////////////// + // + // Linux + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for Linux. + */ + private class LinuxLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth; + { + m_spinner.setSize(sSize); + arrowWidth = sSize.x - m_spinner.getClientArea().width; + } + // set bounds for Spinner and Text + m_spinner.setBounds(cRect.x + cRect.width - sSize.x, cRect.y - 2, sSize.x, cRect.height + 4); + m_text.setBounds(cRect.x, cRect.y, cRect.width - arrowWidth, tSize.y); + } + } + //////////////////////////////////////////////////////////////////////////// + // + // MacOSX + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for MacOSX. + */ + private class MacLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // add Text widget margin + size.y += 4; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + tSize.y += 4; + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(-1, -1).x; + // set bounds for Spinner and Text + m_spinner.setBounds(cRect.x + cRect.width - sSize.x, cRect.y, sSize.x, cRect.height); + m_text.setBounds(cRect.x, cRect.y + 2, cRect.width - arrowWidth - 2, tSize.y); + } + } + /** + * Implementation of {@link Layout} for MacOSX Cocoa. + */ + private class MacCocoaLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point textSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + Point spinnerSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + int width = textSize.x + arrowWidth; + int height = Math.max(spinnerSize.y, textSize.y); + // apply hints + if (wHint != SWT.DEFAULT) { + width = Math.min(width, wHint); + } + if (hHint != SWT.DEFAULT) { + height = Math.min(height, hHint); + } + return new Point(width, height); + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle clientArea = composite.getClientArea(); + if (clientArea.isEmpty()) { + return; + } + // prepare size of Spinner + Point spinnerSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + m_spinner.setBounds(clientArea.x + clientArea.width - arrowWidth - 1, clientArea.y + + clientArea.height + - spinnerSize.y, arrowWidth + 2, spinnerSize.y); + m_text.setBounds( + clientArea.x + 2, + clientArea.y + 2, + clientArea.width - arrowWidth - 5, + clientArea.y + clientArea.height - 4); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // System utils + // + //////////////////////////////////////////////////////////////////////////// + private static final String OS_NAME = System.getProperty("os.name"); + private static final String OS_VERSION = System.getProperty("os.version"); + private static final String WS_TYPE = SWT.getPlatform(); + private static final boolean IS_OS_MAC_OSX = isOS("Mac OS X"); + private static final boolean IS_OS_MAC_OSX_COCOA = IS_OS_MAC_OSX && "cocoa".equals(WS_TYPE); + private static final boolean IS_OS_LINUX = isOS("Linux") || isOS("LINUX"); + private static final boolean IS_OS_WINDOWS_XP = isWindowsVersion("5.1"); + private static final boolean IS_OS_WINDOWS_2003 = isWindowsVersion("5.2"); + private static final boolean IS_OS_WINDOWS_VISTA = isWindowsVersion("6.0"); + private static final boolean IS_OS_WINDOWS_7 = isWindowsVersion("6.1"); + + private static boolean isOS(String osName) { + return OS_NAME != null && OS_NAME.startsWith(osName); + } + + private static boolean isWindowsVersion(String windowsVersion) { + return isOS("Windows") && OS_VERSION != null && OS_VERSION.startsWith(windowsVersion); + } +} diff --git a/propertysheet/src/org/eclipse/wb/core/controls/Messages.java b/propertysheet/src/org/eclipse/wb/core/controls/Messages.java new file mode 100644 index 0000000..3d83ffc --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/Messages.java @@ -0,0 +1,16 @@ +package org.eclipse.wb.core.controls; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.wb.core.controls.messages"; //$NON-NLS-1$ + public static String CSpinner_canNotParse; + public static String CSpinner_outOfRange; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/propertysheet/src/org/eclipse/wb/core/controls/messages.properties b/propertysheet/src/org/eclipse/wb/core/controls/messages.properties new file mode 100644 index 0000000..75a1ca0 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/messages.properties @@ -0,0 +1,2 @@ +CSpinner_canNotParse=Text "{0}"does not satisfy pattern "{1}" +CSpinner_outOfRange=Value {0} is out of range [{1}, {2}] diff --git a/propertysheet/src/org/eclipse/wb/draw2d/IColorConstants.java b/propertysheet/src/org/eclipse/wb/draw2d/IColorConstants.java new file mode 100644 index 0000000..f12d94e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/draw2d/IColorConstants.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * 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.draw2d; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; + +/** + * A collection of color-related constants. + * + * @author lobas_av + * @coverage gef.draw2d + */ +public interface IColorConstants { + /** + * System color used to paint highlight shadow areas. + */ + Color buttonLightest = Utils.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); + /** + * System color used to paint background areas. + */ + Color button = Utils.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + /** + * System color used to paint normal shadow areas. + */ + Color buttonDarker = Utils.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); +// /** +// * System color used to paint dark shadow areas. +// */ +// Color buttonDarkest = Utils.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW); + /** + * System color used to paint list background areas. + */ + Color listBackground = Utils.getSystemColor(SWT.COLOR_LIST_BACKGROUND); + /** + * System color used to paint list foreground areas. + */ + Color listForeground = Utils.getSystemColor(SWT.COLOR_LIST_FOREGROUND); + /** + * System color used to paint list selection area. + */ + Color listSelection = Utils.getSystemColor(SWT.COLOR_LIST_SELECTION); + /** + * System color used to paint list selection text. + */ + Color listSelectionText = Utils.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT); + /** + * System color used to paint tooltip text. + */ + Color tooltipForeground = Utils.getSystemColor(SWT.COLOR_INFO_FOREGROUND); + /** + * System color used to paint tooltip background areas. + */ + Color tooltipBackground = Utils.getSystemColor(SWT.COLOR_INFO_BACKGROUND); + /** + * Miscellaneous colors. + */ + Color lightGray = new Color(null, 192, 192, 192); + Color gray = new Color(null, 128, 128, 128); + Color darkGray = new Color(null, 64, 64, 64); + Color lightBlue = new Color(null, 127, 127, 255); + Color darkBlue = new Color(null, 0, 0, 127); + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * Internal helper. + */ + public static class Utils { + /** + * Invokes {@link Display#getSystemColor(int)} in UI thread. + */ + private static Color getSystemColor(final int id) { + final Color[] color = new Color[1]; + final Display display = Display.getDefault(); + display.syncExec(new Runnable() { + @Override + public void run() { + color[0] = display.getSystemColor(id); + } + }); + return color[0]; + } + } +} \ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/draw2d/ICursorConstants.java b/propertysheet/src/org/eclipse/wb/draw2d/ICursorConstants.java new file mode 100644 index 0000000..b05035d --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/draw2d/ICursorConstants.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.draw2d; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Cursor; + +/** + * A collection of cursors. + * + * @author lobas_av + * @coverage gef.draw2d + */ +public interface ICursorConstants { + /** + * System resize west-east cursor + */ + Cursor SIZEWE = new Cursor(null, SWT.CURSOR_SIZEWE); +} \ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/DesignerPlugin.java b/propertysheet/src/org/eclipse/wb/internal/core/DesignerPlugin.java new file mode 100644 index 0000000..564a068 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/DesignerPlugin.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * 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; + +import com.google.common.collect.Maps; +import com.google.common.io.CharStreams; +import com.google.common.io.Closeables; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +import java.io.Closeable; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Map; + +/** + * The DesignerPlugin class is the "nexus" of the propertysheet. In WindowBuilder, + * it's the plugin activator, and contains a number of important utility methods, such + * as resource loading, logging, obtaining a display and shell, etc. + *

+ * In the AOSP fork, most of the functionality has been ripped out, except for the + * above mentioned pieces, and this class is no longer a plugin. Instead, it *delegates* + * to the plugin which initializes it via the {@link #initialize} method for things + * like logging. For things like image loading, it has its own local code such that + * it can find its image resources locally instead of requiring the embedding plugin + * to copy the images into its own jar. + *

+ * "DesignerPlugin" is not a very good name for this class since it is not a plugin, + * but it was left that way to avoid modifying all the various propertysheet classes; + * we'd like to keep those as unmodified as possible to make absorbing future + * WindowBuilder improvements as easy as possible. + */ +public class DesignerPlugin { + private static AbstractUIPlugin sPlugin; + private static String sPluginId; + + /** + * Initialize the property sheet for use in the ADT plugin + * + * @param hostPlugin the plugin to embed the property sheet + * @param pluginId the id of the plugin to use in status messages etc + * @param isWindows whether we're running on Windows + * @param isMac whether we're running on Mac + * @param isLinux whether we're running on Linux + */ + public static void initialize(AbstractUIPlugin hostPlugin, String pluginId, + boolean isWindows, boolean isMac, boolean isLinux) { + assert sPlugin == null; // Can only be used by one client in the same classloader + sPlugin = hostPlugin; + sPluginId = pluginId; + EnvironmentUtils.IS_WINDOWS = isWindows; + EnvironmentUtils.IS_MAC = isMac; + EnvironmentUtils.IS_LINUX = isLinux; + } + + /** + * Dispose the propertysheet library: free up images from the cache, unregister the + * plugin reference etc. + */ + public static void dispose() { + sPlugin = null; + for (Image image : sImageCache.values()) { + image.dispose(); + } + sImageCache.clear(); + sDescriptorCache.clear(); + } + + /** + * Reads the contents of an {@link InputStreamReader} using the default + * platform encoding and return it as a String. This method will close the + * input stream. + * + * @param inputStream the input stream to be read from + * @param charset the charset to use + * @return the String read from the stream, or null if there was an error + */ + public static String readFile(InputStream inputStream, Charset charset) { + if (inputStream == null) { + return null; + } + Closeable closeMe = inputStream; + try { + final InputStreamReader isr = new InputStreamReader(inputStream, charset); + closeMe = isr; + try { + return CharStreams.toString(isr); + } catch (Exception ioe) { + // pass -- ignore files we can't read + return null; + } + } finally { + Closeables.closeQuietly(closeMe); + } + } + + /** + * @return the instance of {@link DesignerPlugin} + */ + public static AbstractUIPlugin getDefault() { + assert sPlugin != null; + return sPlugin; + } + + // ////////////////////////////////////////////////////////////////////////// + // + // Display/Shell + // + // ////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Display} instance, current (if in GUI thread) or + * default. + */ + public static Display getStandardDisplay() { + Display display = Display.getCurrent(); + if (display == null) { + display = Display.getDefault(); + } + return display; + } + + /** + * @return the active {@link IWorkbenchWindow}. + */ + public static IWorkbenchWindow getActiveWorkbenchWindow() { + return getDefault().getWorkbench().getActiveWorkbenchWindow(); + } + + /** + * @return the {@link Shell} of active {@link IWorkbenchWindow}. + */ + public static Shell getShell() { + if (getActiveWorkbenchWindow() != null) { + return getActiveWorkbenchWindow().getShell(); + } + return null; + } + + /** + * Logs given {@link IStatus} into Eclipse .log. + */ + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + + /** + * Logs {@link IStatus} with given message into Eclipse .log. + */ + public static void log(String message) { + log(new Status(IStatus.INFO, sPluginId, IStatus.INFO, message, null)); + } + + /** + * Logs {@link IStatus} with given exception into Eclipse .log. + */ + public static void log(Throwable e) { + Status status = new Status(IStatus.ERROR, sPluginId, "", e); + getDefault().getLog().log(status); + } + + /** + * Logs {@link IStatus} with given message and exception into Eclipse .log. + */ + public static void log(String message, Throwable e) { + log(createStatus(message, e)); + } + + /** + * Creates {@link IStatus} for given message and exception. + */ + public static Status createStatus(String message, Throwable e) { + return new Status(IStatus.ERROR, "wb", IStatus.ERROR, message, e) { + @Override + public boolean isMultiStatus() { + return true; + } + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // + // Resources + // + // ////////////////////////////////////////////////////////////////////////// + private static Map sDescriptorCache = Maps.newHashMap(); + private static Map sImageCache = Maps.newHashMap(); + + public static Image getImage(String path) { + Image image = sImageCache.get(path); + if (image == null) { + ImageDescriptor descriptor = getImageDescriptor(path); + if (descriptor != null) { + return descriptor.createImage(); + } + sImageCache.put(path, image); + } + return image; + } + + public static ImageDescriptor getImageDescriptor(String path) { + ImageDescriptor descriptor = sDescriptorCache.get(path); + if (descriptor == null) { + URL url = DesignerPlugin.class.getResource("icons/" + path); //$NON-NLS-1$ + if (url != null) { + descriptor = ImageDescriptor.createFromURL(url); + sDescriptorCache.put(path, descriptor); + } + } + return descriptor; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/EnvironmentUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/EnvironmentUtils.java new file mode 100644 index 0000000..cedbbc0 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/EnvironmentUtils.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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; + +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * Helper for environment state access. + * + * @author scheglov_ke + * @coverage core + */ +public final class EnvironmentUtils extends AbstractUIPlugin { + //////////////////////////////////////////////////////////////////////////// + // + // Operating systems + // + //////////////////////////////////////////////////////////////////////////// + /** True if this is running on Windows */ + public static boolean IS_WINDOWS; + /** True if this is running on Mac */ + public static boolean IS_MAC; + /** True if this is running on Linux */ + public static boolean IS_LINUX; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/editor/structure/property/PropertyListIntersector.java b/propertysheet/src/org/eclipse/wb/internal/core/editor/structure/property/PropertyListIntersector.java new file mode 100644 index 0000000..24aea2f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/editor/structure/property/PropertyListIntersector.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * 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.editor.structure.property; + +import com.google.common.collect.Lists; + +import org.eclipse.wb.internal.core.model.property.Property; +import org.eclipse.wb.internal.core.model.property.PropertyManager; + +import java.util.Iterator; +import java.util.List; + +/** + * Helper for computing intersection of {@link Property} arrays. + * + * @author scheglov_ke + * @coverage core.editor.structure + */ +public final class PropertyListIntersector { + private List m_intersection; + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Updates intersection by intersecting with new given array. + */ + public void intersect(Property[] properties) { + if (m_intersection == null) { + m_intersection = Lists.newArrayList(); + for (int i = 0; i < properties.length; i++) { + Property property = properties[i]; + m_intersection.add(new PropertyGroup(property)); + } + } else { + for (Iterator I = m_intersection.iterator(); I.hasNext();) { + PropertyGroup propertyGroup = I.next(); + if (!propertyGroup.add(properties)) { + I.remove(); + } + } + } + } + + /** + * @return the array of matched composite {@link Property}'s. + */ + public Property[] getProperties() { + List properties = Lists.newArrayList(); + for (PropertyGroup propertyGroup : m_intersection) { + Property compositeProperty = propertyGroup.getCompositeProperty(); + if (compositeProperty != null) { + properties.add(compositeProperty); + } + } + // + return properties.toArray(new Property[properties.size()]); + } + + //////////////////////////////////////////////////////////////////////////// + // + // PropertyGroup + // + //////////////////////////////////////////////////////////////////////////// + /** + * The group of {@link Property}'s that match. + */ + private static final class PropertyGroup { + private final List m_properties = Lists.newArrayList(); + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public PropertyGroup(Property property) { + m_properties.add(property); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return true if new matched {@link Property} from given array was added. + */ + public boolean add(Property[] properties) { + for (Property property : properties) { + if (add(property)) { + return true; + } + } + // no match + return false; + } + + /** + * @return the composite {@link Property} for this group. + */ + public Property getCompositeProperty() { + Property properties[] = m_properties.toArray(new Property[m_properties.size()]); + return properties[0].getComposite(properties); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Internal + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return true if given {@link Property} matches and was added. + */ + private boolean add(Property property) { + Property example = m_properties.get(0); + if (example.getClass() == property.getClass() + && example.getTitle().equals(property.getTitle()) + && PropertyManager.getCategory(example) == PropertyManager.getCategory(property)) { + m_properties.add(property); + return true; + } + // no match + return false; + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanNull.png b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanNull.png new file mode 100644 index 0000000..437b546 Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanNull.png differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanUnknown.png b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanUnknown.png new file mode 100644 index 0000000..e92cbf0 Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/BooleanUnknown.png differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/dots.gif b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/dots.gif new file mode 100644 index 0000000..e3c6e83 Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/dots.gif differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/down.png b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/down.png new file mode 100644 index 0000000..57147b8 Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/down.png differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/false.png b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/false.png new file mode 100644 index 0000000..7e1094f Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/false.png differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/minus.gif b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/minus.gif new file mode 100644 index 0000000..e6579a6 Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/minus.gif differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/plus.gif b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/plus.gif new file mode 100644 index 0000000..3944fed Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/plus.gif differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/true.png b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/true.png new file mode 100644 index 0000000..7908e7a Binary files /dev/null and b/propertysheet/src/org/eclipse/wb/internal/core/icons/properties/true.png differ diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.java b/propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.java new file mode 100644 index 0000000..58bc631 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.java @@ -0,0 +1,27 @@ +package org.eclipse.wb.internal.core.model; + +import org.eclipse.osgi.util.NLS; + +public class ModelMessages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.wb.internal.core.model.ModelMessages"; //$NON-NLS-1$ + public static String CharacterPropertyEditor_notValid; + public static String DoubleObjectPropertyEditor_notValidDouble; + public static String DoublePropertyEditor_notValidDouble; + public static String FloatPropertyEditor_notValidFloat; + public static String IntegerObjectPropertyEditor_notValidInt; + public static String IntegerPropertyEditor_notValidInt; + public static String LongObjectPropertyEditor_notValidLong; + public static String LongPropertyEditor_notValidLong; + public static String ShortObjectPropertyEditor_notValidShort; + public static String ShortPropertyEditor_notValidShort; + public static String StringArrayPropertyEditor_hint; + public static String StringArrayPropertyEditor_itemsLabel; + public static String StringPropertyDialog_title; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, ModelMessages.class); + } + + private ModelMessages() { + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.properties b/propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.properties new file mode 100644 index 0000000..6801adb --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/model/ModelMessages.properties @@ -0,0 +1,13 @@ +CharacterPropertyEditor_notValid="{0}" is not a valid character. +DoubleObjectPropertyEditor_notValidDouble="{0}" is not a valid double. +DoublePropertyEditor_notValidDouble="{0}" is not a valid double. +FloatPropertyEditor_notValidFloat="{0}" is not a valid float. +IntegerObjectPropertyEditor_notValidInt="{0}" is not a valid int. +IntegerPropertyEditor_notValidInt="{0}" is not a valid int. +LongObjectPropertyEditor_notValidLong="{0}" is not a valid long. +LongPropertyEditor_notValidLong="{0}" is not a valid long. +ShortObjectPropertyEditor_notValidShort="{0}" is not a valid short. +ShortPropertyEditor_notValidShort="{0}" is not a valid short. +StringArrayPropertyEditor_hint=Each line in the above text field represents single element. +StringArrayPropertyEditor_itemsLabel=&Elements: +StringPropertyDialog_title=String editor 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 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 getAdapter(Class 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 + * null because null 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 true 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 + * null if no composite {@link Property} can be created. + */ + public Property getComposite(Property[] properties) { + return null; + } + + public T getAdapter(Class adapter) { + return null; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Arbitrary values map + // + //////////////////////////////////////////////////////////////////////////// + private Map 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 null. + */ + 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". + *

+ * 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 ALPHABETICAL = new Comparator() { + @Override + public int compare(Property p1, Property p2) { + return p1.getName().compareTo(p2.getName()); + } + }; + + /** Sort {@link Property} instances by priority */ + public static final Comparator PRIORITY = new Comparator() { + @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 null. + */ + 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 + * system.getPriority() + additional. + */ + 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 true if this property is preferred. + */ + public boolean isPreferred() { + return this == PREFERRED; + } + + /** + * @return true if this property is advanced. + */ + public boolean isAdvanced() { + return this == ADVANCED; + } + + /** + * @return true if this property is really advanced. + */ + public boolean isAdvancedReally() { + return this == ADVANCED_REALLY; + } + + /** + * @return true if this property is hidden. + */ + public boolean isHidden() { + return this == HIDDEN; + } + + /** + * @return true 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 null. + */ + 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 null. + */ + 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 null. + */ + public static PropertyCategoryProvider forcedByUser() { + return FORCED_BY_USER; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Compound + // + //////////////////////////////////////////////////////////////////////////// + /** + * Returns first not null 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() { +// 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 parameters, String name) { + return getParameterAsArray(parameters, name, false); + } + + @SuppressWarnings("unchecked") + protected static String[] getParameterAsArray(Map parameters, + String name, + boolean noAssert) { + String[] values = null; + if (parameters.containsKey(name)) { + List list = (List) 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 true 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 true 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 true 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 Boolean. + * + * @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 boolean. + * + * @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 double. + * + * @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}. + *

+ * 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 float. + * + * @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 int. + * + * @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 long. + * + * @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 true 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 (x, y, width, height) 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 null if + * it is activated using keyboard. + * + * @return true 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 true 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 getAdapter(Class 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 null. + */ + public PropertyEditor getEditorForType(Class propertyType) throws Exception { + return null; + } + + /** + * @return the {@link PropertyEditor} for given {@link java.beans.PropertyEditor} editor type or + * null. + */ + public PropertyEditor getEditorForEditorType(Class editorType) throws Exception { + return null; + } + + /** + * @return the {@link PropertyEditor} for given {@link PropertyDescriptor} or null. + */ + 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 short. + * + * @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 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 null if value + * of {@link Property} is unknown. + */ + protected abstract String getText(Property property) throws Exception; + + //////////////////////////////////////////////////////////////////////////// + // + // IAdaptable + // + //////////////////////////////////////////////////////////////////////////// + @Override + public T getAdapter(Class 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, 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 m_presentations = Lists.newArrayList(); + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Adds child {@link PropertyEditorPresentation}.
+ * 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 = ""; + if (header != null) { + html += ""; + } + html += ""; + html += "
" + header + "
" + details + "
"; + 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 + ? " Close" + : ""; + wrappedHtml = + /*CodeUtils.*/getSource( + "", + " ", + " ", + closeElement, + 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 null. + * @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 m_properties; + private final Set 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 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 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 true 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 two 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 null 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}.
+ * 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 X 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 X 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 y location. + */ + private int getPropertyIndex(int y) { + return m_selection + y / m_rowHeight; + } + + /** + * @return true if given x 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 true if x coordinate is on splitter. + */ + private boolean isLocationSplitter(int x) { + return Math.abs(m_splitter - x) < 2; + } + + /** + * @return true if given x 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 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 true 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 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, + "", + 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 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 true 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 true 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 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 true 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; } diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java new file mode 100644 index 0000000..3df0e9b --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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.utils; + +import com.google.common.base.Objects; + +/** + * Pair of two objects. + * + * @author scheglov_ke + * @coverage core.util + */ +public final class Pair { + private final L left; + private final R right; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public Pair(L left, R right) { + this.left = left; + this.right = right; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Object + // + //////////////////////////////////////////////////////////////////////////// + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Pair)) { + return false; + } + Pair other = (Pair) o; + return Objects.equal(getLeft(), other.getLeft()) + && Objects.equal(getRight(), other.getRight()); + } + + @Override + public int hashCode() { + int hLeft = getLeft() == null ? 0 : getLeft().hashCode(); + int hRight = getRight() == null ? 0 : getRight().hashCode(); + return hLeft + 37 * hRight; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + public L getLeft() { + return left; + } + + public R getRight() { + return right; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Factory + // + //////////////////////////////////////////////////////////////////////////// + public static Pair create(L left, R right) { + return new Pair(left, right); + } +} \ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java new file mode 100644 index 0000000..155b55c --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * 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.utils.binding.editors.controls; + +import com.google.common.collect.Lists; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.expressions.EvaluationResult; +import org.eclipse.core.expressions.Expression; +import org.eclipse.core.expressions.ExpressionInfo; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.ISources; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; + +import java.util.List; + +/** + * Manager for installing/unistalling global handlers for {@link Control} actions commands. + * + * @author sablin_aa + * @author mitin_aa + */ +public abstract class AbstractControlActionsManager { + @SuppressWarnings("deprecation") + protected final Object[] COMMAND_HANDLER_IDS = new Object[]{ + IWorkbenchActionDefinitionIds.COPY, + IWorkbenchActionDefinitionIds.CUT, + IWorkbenchActionDefinitionIds.PASTE, + IWorkbenchActionDefinitionIds.DELETE, + IWorkbenchActionDefinitionIds.SELECT_ALL, + IWorkbenchActionDefinitionIds.UNDO, + IWorkbenchActionDefinitionIds.REDO}; + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + protected final Control m_control; + private boolean m_active = false; + + public AbstractControlActionsManager(final Control control) { + m_control = control; + m_control.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + activateHandlers(); + m_active = true; + } + + @Override + public void focusLost(FocusEvent e) { + deactivateHandlers(); + m_active = false; + } + }); + m_control.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + if (m_active) { + // deactivate on dispose + deactivateHandlers(); + } + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Handlers + // + //////////////////////////////////////////////////////////////////////////// + protected static final IHandler EMPTY_HANDLER = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + // do nothing + return null; + } + + @Override + public boolean isEnabled() { + // of course, it is enabled ;) + return true; + } + + @Override + public boolean isHandled() { + // we do not handle the actions; since action not handled, its' underlying SWT event + // would not be filtered by workbench, so it get a chance to be handled by SWT code + // or to be passed to the OS; see WorkbenchKeyboard.press() method. + return false; + } + }; + + /** + * @returns the global handler service. + */ + public static IHandlerService getHandlerService() { + return (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); + } + + /** + * Activates the handlers for list of commands (see COMMAND_HANDLERS) with:
+ * 1. The empty handler (except 'selectAll'), so underlying SWT event wouldn't be filtered by the + * workbench;
+ * 2. Highest priority {@link Expression}, so this handler has a chance to be set. + */ + protected void activateHandlers() { + IHandlerService service = getHandlerService(); + for (int i = 0; i < COMMAND_HANDLER_IDS.length; ++i) { + String actionName = (String) COMMAND_HANDLER_IDS[i]; + IHandler handler = getHandlerFor(actionName); + activateHandler(actionName, service, handler, new Expression() { + @Override + public EvaluationResult evaluate(IEvaluationContext context) throws CoreException { + return EvaluationResult.TRUE; + } + + @Override + public void collectExpressionInfo(ExpressionInfo info) { + // get the highest priority + // note, if someone else has such priority, there will be key-binding conflicts logged. + info.markSystemPropertyAccessed(); + info.markDefaultVariableAccessed(); + info.addVariableNameAccess(ISources.ACTIVE_MENU_NAME); + } + }); + } + } + + protected IHandler getHandlerFor(String actionName) { + return EMPTY_HANDLER; + } + + /** + * Activates handler and stores it into a collection for further deactivation. + */ + private final List m_activations = Lists.newLinkedList(); + + private void activateHandler(String actionName, + IHandlerService service, + IHandler handler, + Expression highPriorityExpression) { + // activate handler and store it into a collection for further deactivation + m_activations.add(service.activateHandler(actionName, handler, highPriorityExpression)); + } + + /** + * Deactivates all handlers and clears handlers collection. + */ + protected void deactivateHandlers() { + getHandlerService().deactivateHandlers(m_activations); + m_activations.clear(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java new file mode 100644 index 0000000..ff048bc --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.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.utils.binding.editors.controls; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; + +/** + * Default manager for installing/unistalling global handlers for {@link Control} actions commands. + * + * @author sablin_aa + */ +public class DefaultControlActionsManager extends AbstractControlActionsManager { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public DefaultControlActionsManager(final Control control) { + super(control); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Handlers + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected IHandler getHandlerFor(String actionName) { + if (actionName.equalsIgnoreCase(IWorkbenchActionDefinitionIds.SELECT_ALL)) { + return SELECTALL_HANDLER; + } + return super.getHandlerFor(actionName); + } + + /** + * Handler for process "Select all" action. + */ + private final IHandler SELECTALL_HANDLER = new AbstractHandler() { + public Object execute(ExecutionEvent event) throws ExecutionException { + selectAllExecuted(); + return null; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean isHandled() { + return true; + } + }; + + protected void selectAllExecuted() { + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java new file mode 100644 index 0000000..28de0b6 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java @@ -0,0 +1,361 @@ +/******************************************************************************* + * 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.utils.check; + +import java.text.MessageFormat; + +/** + * Assert is useful for for embedding runtime sanity checks in code. The predicate + * methods all test a condition and throw some type of unchecked exception if the condition does not + * hold. + *

+ * Assertion failure exceptions, like most runtime exceptions, are thrown when something is + * misbehaving. Assertion failures are invariably unspecified behavior; consequently, clients should + * never rely on these being thrown (and certainly should not being catching them specifically). + * + * @author scheglov_ke + * @coverage core.util + */ +public final class Assert { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private Assert() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // "legal" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that an argument is legal. If the given boolean is not true, an + * IllegalArgumentException is thrown. + * + * @param expression + * the boolean expression of the check + * @return true if the check passes (does not return if the check fails) + * @exception IllegalArgumentException + * if the legality test failed + */ + public static boolean isLegal(boolean expression) { + return isLegal(expression, ""); //$NON-NLS-1$ + } + + /** + * Asserts that an argument is legal. If the given boolean is not true, an + * IllegalArgumentException is thrown. The given message is included in that + * exception, to aid debugging. + * + * @param expression + * the boolean expression of the check + * @param message + * the message to include in the exception + * @return true if the check passes (does not return if the check fails) + * @exception IllegalArgumentException + * if the legality test failed + */ + public static boolean isLegal(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + return expression; + } + + //////////////////////////////////////////////////////////////////////////// + // + // "null" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that the given object is null. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param object + * the value to test + */ + public static void isNull(Object object) { + isNull(object, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given object is null. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param message + * the message to include in the exception + */ + public static void isNull(Object object, String message) { + if (object != null) { + throw new AssertionFailedException("null argument expected: " + message); //$NON-NLS-1$ + } + } + + /** + * Asserts that the given object is not null. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link String#format(String, Object...)}. For example + * "Execution flow problem. %s expected, but %s found.". + * @param args + * the arguments for {@code errorFormat} + */ + public static void isNull(Object object, String errorFormat, Object... args) { + if (object != null) { + fail("null argument expected: " + String.format(errorFormat, args)); //$NON-NLS-1$ + } + } + + /** + * @param errorFormat + * the format of error message suitable for {@link MessageFormat}. + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example + * "Execution flow problem. {0} expected, but {1} found.". + */ + public static void isNull2(Object object, String errorFormat, Object... args) { + if (object != null) { + String message = "null argument expected: " + MessageFormat.format(errorFormat, args); //$NON-NLS-1$ + fail(message); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // not "null" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that the given object is not null. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param object + * the value to test + */ + public static void isNotNull(Object object) { + isNotNull(object, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given object is not null. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param message + * the message to include in the exception + */ + public static void isNotNull(Object object, String message) { + if (object == null) { + fail("null argument: " + message); //$NON-NLS-1$ + } + } + + /** + * Asserts that the given object is not null. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link String#format(String, Object...)}. For example + * "Execution flow problem. %s expected, but %s found.". + * @param args + * the arguments for {@code errorFormat} + */ + public static void isNotNull(Object object, String errorFormat, Object... args) { + if (object == null) { + fail("null argument: " + String.format(errorFormat, args)); //$NON-NLS-1$ + } + } + + /** + * @param errorFormat + * the format of error message suitable for {@link MessageFormat}. + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example + * "Execution flow problem. {0} expected, but {1} found.". + */ + public static void isNotNull2(Object object, String errorFormat, Object... args) { + if (object == null) { + String message = "null argument: " + MessageFormat.format(errorFormat, args); //$NON-NLS-1$ + fail(message); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Fail + // + //////////////////////////////////////////////////////////////////////////// + /** + * Fails with given message. + * + * @param message + * the message to include in the exception + */ + public static void fail(String message) { + throw new AssertionFailedException(message); + } + + /** + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example "{0} expected, but {1} found.". + */ + public static void fail(String errorFormat, Object... args) { + String message = MessageFormat.format(errorFormat, args); + throw new AssertionFailedException(message); + } + + //////////////////////////////////////////////////////////////////////////// + // + // "true" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that the given boolean is true. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param expression + * the boolean expression of the check + * @return true if the check passes (does not return if the check fails) + */ + public static boolean isTrue(boolean expression) { + return isTrue(expression, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given boolean is true. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the boolean expression of the check + * @param message + * the message to include in the exception + * @return true if the check passes (does not return if the check fails) + */ + public static boolean isTrue(boolean expression, String message) { + if (!expression) { + fail("assertion failed: " + message); //$NON-NLS-1$ + } + return expression; + } + + /** + * Asserts that the given boolean is true. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the boolean expression of the check + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link String#format(String, Object...)}. For example + * "Execution flow problem. %s expected, but %s found.". + * @param args + * the arguments for {@code errorFormat} + * @return true if the check passes (does not return if the check fails) + */ + public static boolean isTrue(boolean expression, String errorFormat, Object... args) { + if (!expression) { + fail("assertion failed: " + String.format(errorFormat, args)); //$NON-NLS-1$ + } + return expression; + } + + /** + * Asserts that the given boolean is true. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the boolean expression to check. + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example "{0} expected, but {1} found.". + */ + public static boolean isTrue2(boolean expression, String errorFormat, Object... args) { + if (!expression) { + fail(errorFormat, args); + } + return expression; + } + + //////////////////////////////////////////////////////////////////////////// + // + // equals + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that given actual value equals expected value. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param expected + * the expected value + * @param the + * actual value to check + */ + public static void equals(int expected, int actual) { + equals(expected, actual, expected + " expected, but " + actual + " found"); + } + + /** + * Asserts that given actual value equals expected value. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expected + * the expected value + * @param the + * actual value to check + * @param message + * the message to include in the exception + */ + public static void equals(int expected, int actual, String message) { + if (expected != actual) { + fail("assertation failed: " + message); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // instanceOf + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that given object is not null and has class compatible with given. + */ + public static void instanceOf(Class expectedClass, Object o) { + if (o == null) { + fail(expectedClass.getName() + " expected, but 'null' found."); + } + if (!expectedClass.isAssignableFrom(o.getClass())) { + fail(expectedClass.getName() + " expected, but " + o.getClass().getName() + " found."); + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java new file mode 100644 index 0000000..d0c9794 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * 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.utils.check; + +/** + * AssertionFailedException is a runtime exception thrown by some of the methods in + * Assert. + * + * @author scheglov_ke + * @coverage core.util + */ +public final class AssertionFailedException extends RuntimeException { + private static final long serialVersionUID = 0L; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + /** + * Constructs a new exception with the given message. + * + * @param detail + * the message + */ + public AssertionFailedException(String detail) { + super(detail); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java new file mode 100644 index 0000000..d80ff94 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * 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.utils.execution; + +import org.eclipse.swt.widgets.Display; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils; + +import java.beans.Beans; + +/** + * Utilities for executing actions, such as {@link RunnableEx}. + * + * @author scheglov_ke + * @coverage core.util + */ +public class ExecutionUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private ExecutionUtils() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Sleep + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sleeps given number of milliseconds, ignoring exceptions. + */ + public static void sleep(final int millis) { + runIgnore(new RunnableEx() { + @Override + public void run() throws Exception { + Thread.sleep(millis); + } + }); + } + + /** + * Waits given number of milliseconds and runs events loop every 1 millisecond. At least one + * events loop will be executed. If current thread is not UI thread, then this method works just + * as {@link #sleep(int)}. + */ + public static void waitEventLoop(int millis) { + Display display = Display.getCurrent(); + if (display != null) { + long nanos = millis * 1000000L; + long start = System.nanoTime(); + do { + sleep(0); + while (display.readAndDispatch()) { + // do nothing + } + } while (System.nanoTime() - start < nanos); + } else { + sleep(millis); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // void + // + //////////////////////////////////////////////////////////////////////////// + /** + * Runs given {@link RunnableEx} and ignores exceptions. + * + * @return true if execution was finished without exception. + */ + public static boolean runIgnore(RunnableEx runnable) { + try { + runnable.run(); + return true; + } catch (Throwable e) { + return false; + } + } + + /** + * Runs given {@link RunnableEx} and logs exceptions using {@link DesignerPlugin#log(Throwable)}. + * + * @return true if execution was finished without exception. + */ + public static boolean runLog(RunnableEx runnable) { + try { + runnable.run(); + return true; + } catch (Throwable e) { + DesignerPlugin.log(e); + return false; + } + } + + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + */ + public static void runRethrow(RunnableEx runnable) { + try { + runnable.run(); + } catch (Throwable e) { + throw ReflectionUtils.propagate(e); + } + } + + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + */ + public static void runRethrow(RunnableEx runnable, String format, Object... args) { + try { + runnable.run(); + } catch (Throwable e) { + String message = String.format(format, args); + throw new RuntimeException(message, e); + } + } + + /** + * Ensures that {@link Beans#isDesignTime()} returns true and runs given + * {@link RunnableEx}. + */ + public static void runDesignTime(RunnableEx runnable) throws Exception { + boolean old_designTime = Beans.isDesignTime(); + try { + Beans.setDesignTime(true); + runnable.run(); + } finally { + Beans.setDesignTime(old_designTime); + } + } + + /** + * Ensures that {@link Beans#isDesignTime()} returns true and runs given + * {@link RunnableEx}. + */ + public static T runDesignTime(RunnableObjectEx runnable) throws Exception { + boolean old_designTime = Beans.isDesignTime(); + try { + Beans.setDesignTime(true); + return runnable.runObject(); + } finally { + Beans.setDesignTime(old_designTime); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // UI + // + //////////////////////////////////////////////////////////////////////////// + /** + * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}. + * + * @return true if {@link RunnableEx} was executed without any {@link Exception}. + */ + public static boolean runLogUI(final RunnableEx runnable) { + final boolean[] success = new boolean[1]; + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + success[0] = ExecutionUtils.runLog(runnable); + } + }); + return success[0]; + } + + /** + * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}. + */ + public static void runRethrowUI(final RunnableEx runnable) { + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + ExecutionUtils.runRethrow(runnable); + } + }); + } + + /** + * Runs given {@link RunnableEx} within UI thread using {@link Display#asyncExec(Runnable)}. Logs + * a {@link Throwable} which may occur. + */ + public static void runAsync(final RunnableEx runnable) { + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + ExecutionUtils.runLog(runnable); + } + }); + } + + /** + * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}. + */ + @SuppressWarnings("unchecked") + public static T runObjectUI(final RunnableObjectEx runnable) { + final Object[] result = new Object[1]; + runRethrowUI(new RunnableEx() { + @Override + public void run() throws Exception { + result[0] = runObject(runnable); + } + }); + return (T) result[0]; + } + + /** + * Runs given {@link RunnableEx} as {@link #runLog(RunnableEx)}, but using + * {@link Display#asyncExec(Runnable)}. + */ + public static void runLogLater(final RunnableEx runnable) { + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + ExecutionUtils.runLog(runnable); + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Object + // + //////////////////////////////////////////////////////////////////////////// + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + * + * @return the {@link Object} returned by {@link RunnableEx#run()}. + */ + public static T runObject(RunnableObjectEx runnable) { + try { + return runnable.runObject(); + } catch (Throwable e) { + throw ReflectionUtils.propagate(e); + } + } + + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + * + * @return the {@link Object} returned by {@link RunnableEx#run()}. + */ + public static T runObject(RunnableObjectEx runnable, String format, Object... args) { + try { + return runnable.runObject(); + } catch (Throwable e) { + String message = String.format(format, args); + throw new Error(message, e); + } + } + + /** + * Runs given {@link RunnableEx} and ignores exceptions. + * + * @return the {@link Object} returned by {@link RunnableEx#run()} or defaultValue if + * exception happened. + */ + public static T runObjectIgnore(RunnableObjectEx runnable, T defaultValue) { + try { + return runnable.runObject(); + } catch (Throwable e) { + return defaultValue; + } + } + + /** + * Runs given {@link RunnableEx} and logs exceptions using {@link DesignerPlugin#log(Throwable)}. + * + * @return the {@link Object} returned by {@link RunnableEx#run()} or defaultValue if + * exception was logged. + */ + public static T runObjectLog(RunnableObjectEx runnable, T defaultValue) { + try { + return runnable.runObject(); + } catch (Throwable e) { + DesignerPlugin.log(e); + return defaultValue; + } + } + +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java new file mode 100644 index 0000000..18c141f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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.utils.execution; + +/** + * Analog of {@link Runnable} where method run can throw {@link Exception}. + * + * @author scheglov_ke + * @coverage core.util + */ +public interface RunnableEx { + /** + * Executes operation that can cause {@link Exception}. + */ + void run() throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java new file mode 100644 index 0000000..02a2865 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.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.utils.execution; + +/** + * Analog of {@link Runnable} where method run can throw {@link Exception}. + * + * @author scheglov_ke + * @coverage core.util + */ +public interface RunnableObjectEx { + /** + * Executes operation that can cause {@link Exception}. + * + * @return some {@link Object} result for caller. + */ + T runObject() throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java new file mode 100644 index 0000000..b74ba88 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * 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.utils.reflect; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Helper for setting properties for {@link ClassLoader}. + *

+ * http://java.dzone.com/articles/classloaderlocal-how-avoid + * + * @author Jevgeni Kabanov + * @author scheglov_ke + * @coverage core.util + */ +@SuppressWarnings("unchecked") +public class ClassLoaderLocalMap implements Opcodes { + private static final String NAME = "GEN$$ClassLoaderProperties"; + private static final Map globalMap = + Collections.synchronizedMap(new WeakHashMap()); + private static Method defineMethod; + private static Method findLoadedClass; + static { + try { + defineMethod = + ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{ + String.class, + byte[].class, + int.class, + int.class}); + defineMethod.setAccessible(true); + findLoadedClass = + ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[]{String.class}); + findLoadedClass.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Map + // + //////////////////////////////////////////////////////////////////////////// + public static boolean containsKey(ClassLoader cl, Object key) { + if (cl == null) { + return globalMap.containsKey(key); + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + if (!hasHolder(cl)) { + return false; + } + return getLocalMap(cl).containsKey(key); + } + } + + public static void put(ClassLoader cl, Object key, Object value) { + if (cl == null) { + globalMap.put(key, value); + return; + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + getLocalMap(cl).put(key, value); + } + } + + public static Object get(ClassLoader cl, Object key) { + if (cl == null) { + return globalMap.get(key); + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + return getLocalMap(cl).get(key); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation + // + //////////////////////////////////////////////////////////////////////////// + private static boolean hasHolder(ClassLoader cl) { + String propertiesClassName = NAME; + try { + Class clazz = (Class) findLoadedClass.invoke(cl, new Object[]{propertiesClassName}); + if (clazz == null) { + return false; + } + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getTargetException()); + } + return true; + } + + private static Map getLocalMap(ClassLoader cl) { + String holderClassName = NAME; + Class holderClass; + try { + holderClass = (Class) findLoadedClass.invoke(cl, new Object[]{holderClassName}); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getTargetException()); + } + if (holderClass == null) { + byte[] classBytes = buildHolderByteCode(holderClassName); + try { + holderClass = + (Class) defineMethod.invoke( + cl, + new Object[]{ + holderClassName, + classBytes, + Integer.valueOf(0), + Integer.valueOf(classBytes.length)}); + } catch (InvocationTargetException e1) { + throw new RuntimeException(e1.getTargetException()); + } catch (Throwable e1) { + throw new RuntimeException(e1); + } + } + try { + return (Map) holderClass.getDeclaredField("localMap").get(null); + } catch (Throwable e1) { + throw new RuntimeException(e1); + } + } + + private static byte[] buildHolderByteCode(String holderClassName) { + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER, holderClassName, null, "java/lang/Object", null); + { + fv = + cw.visitField( + ACC_PUBLIC + ACC_FINAL + ACC_STATIC, + "localMap", + "Ljava/util/Map;", + null, + null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_STATIC, "", "()V", null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, "java/util/WeakHashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/WeakHashMap", "", "()V"); + mv.visitFieldInsn(PUTSTATIC, holderClassName, "localMap", "Ljava/util/Map;"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 0); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java new file mode 100644 index 0000000..ce0d339 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.utils.reflect; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@link Map}-like interface for mapping {@link Class} to value. + * + * @author scheglov_ke + * @coverage core.util + */ +public final class ClassMap { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + /** + * Creates new instance of {@link ClassMap}. + */ + public static ClassMap create() { + return new ClassMap(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Map + // + //////////////////////////////////////////////////////////////////////////// + public void put(Class key, V value) { + getMap(key).put(key, value); + } + + public V get(Class key) { + return getMap(key).get(key); + } + + public void remove(Class key) { + getMap(key).remove(key); + } + + public void clear(ClassLoader classLoader) { + getMap(classLoader).clear(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation + // + //////////////////////////////////////////////////////////////////////////// + private Map, V> getMap(Class key) { + ClassLoader classLoader = key.getClassLoader(); + return getMap(classLoader); + } + + @SuppressWarnings("unchecked") + private Map, V> getMap(ClassLoader classLoader) { + Object map = ClassLoaderLocalMap.get(classLoader, this); + if (map == null) { + map = new HashMap, V>(); + ClassLoaderLocalMap.put(classLoader, this, map); + } + return (Map, V>) map; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java new file mode 100644 index 0000000..b362211 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * 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.utils.reflect; + +import com.google.common.collect.Maps; + +import org.eclipse.wb.internal.core.utils.check.Assert; + +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Map; + +/** + * Contains different Java reflection utilities. + * + * @author scheglov_ke + * @coverage core.util + */ +public class ReflectionUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private ReflectionUtils() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Signature + // + //////////////////////////////////////////////////////////////////////////// + /** + * @param runtime + * is true if we need name for class loading, false if we need + * name for source generation. + * + * @return the fully qualified name of given {@link Type}. + */ + public static String getFullyQualifiedName(Type type, boolean runtime) { + Assert.isNotNull(type); + // Class + if (type instanceof Class) { + Class clazz = (Class) type; + // array + if (clazz.isArray()) { + return getFullyQualifiedName(clazz.getComponentType(), runtime) + "[]"; + } + // object + String name = clazz.getName(); + if (!runtime) { + name = name.replace('$', '.'); + } + return name; + } + // GenericArrayType + if (type instanceof GenericArrayType) { + GenericArrayType genericArrayType = (GenericArrayType) type; + return getFullyQualifiedName(genericArrayType.getGenericComponentType(), runtime) + "[]"; + } + // ParameterizedType + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Type rawType = parameterizedType.getRawType(); + // raw type + StringBuilder sb = new StringBuilder(); + sb.append(getFullyQualifiedName(rawType, runtime)); + // type arguments + sb.append("<"); + boolean firstTypeArgument = true; + for (Type typeArgument : parameterizedType.getActualTypeArguments()) { + if (!firstTypeArgument) { + sb.append(","); + } + firstTypeArgument = false; + sb.append(getFullyQualifiedName(typeArgument, runtime)); + } + sb.append(">"); + // done + return sb.toString(); + } + // WildcardType + if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + return "? extends " + getFullyQualifiedName(wildcardType.getUpperBounds()[0], runtime); + } + // TypeVariable + TypeVariable typeVariable = (TypeVariable) type; + return typeVariable.getName(); + } + + /** + * Appends fully qualified names of given parameter types (appends also "()"). + */ + private static void appendParameterTypes(StringBuilder buffer, Type[] parameterTypes) { + buffer.append('('); + boolean firstParameter = true; + for (Type parameterType : parameterTypes) { + if (firstParameter) { + firstParameter = false; + } else { + buffer.append(','); + } + buffer.append(getFullyQualifiedName(parameterType, false)); + } + buffer.append(')'); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Method + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return all declared {@link Method}'s, including protected and private. + */ + public static Map getMethods(Class clazz) { + Map methods = Maps.newHashMap(); + // process classes + for (Class c = clazz; c != null; c = c.getSuperclass()) { + for (Method method : c.getDeclaredMethods()) { + String signature = getMethodSignature(method); + if (!methods.containsKey(signature)) { + method.setAccessible(true); + methods.put(signature, method); + } + } + } + // process interfaces + for (Class interfaceClass : clazz.getInterfaces()) { + for (Method method : interfaceClass.getDeclaredMethods()) { + String signature = getMethodSignature(method); + if (!methods.containsKey(signature)) { + method.setAccessible(true); + methods.put(signature, method); + } + } + } + // done + return methods; + } + + /** + * @return signature for given {@link Method}. This signature is not same signature as in JVM or + * JDT, just some string that unique identifies method in its {@link Class}. + */ + public static String getMethodSignature(Method method) { + Assert.isNotNull(method); + return getMethodSignature(method.getName(), method.getParameterTypes()); + } + + /** + * Returns the signature of {@link Method} with given combination of name and parameter types. + * This signature is not same signature as in JVM or JDT, just some string that unique identifies + * method in its {@link Class}. + * + * @param name + * the name of {@link Method}. + * @param parameterTypes + * the types of {@link Method} parameters. + * + * @return signature of {@link Method}. + */ + public static String getMethodSignature(String name, Type... parameterTypes) { + Assert.isNotNull(name); + Assert.isNotNull(parameterTypes); + // + StringBuilder buffer = new StringBuilder(); + buffer.append(name); + appendParameterTypes(buffer, parameterTypes); + return buffer.toString(); + } + + private static final ClassMap> m_getMethodBySignature = ClassMap.create(); + + /** + * Returns the {@link Method} defined in {@link Class}. This method can have any visibility, i.e. + * we can find even protected/private methods. Can return null if no method with + * given signature found. + * + * @param clazz + * the {@link Class} to get method from it, or its superclass. + * @param signature + * the signature of method in same format as {@link #getMethodSignature(Method)}. + * + * @return the {@link Method} for given signature, or null if no such method found. + */ + public static Method getMethodBySignature(Class clazz, String signature) { + Assert.isNotNull(clazz); + Assert.isNotNull(signature); + // prepare cache + Map cache = m_getMethodBySignature.get(clazz); + if (cache == null) { + cache = getMethods(clazz); + m_getMethodBySignature.put(clazz, cache); + } + // use cache + return cache.get(signature); + } + + /** + * @return the {@link Object} result of invoking method with given signature. + */ + public static Object invokeMethod(Object object, String signature, Object... arguments) + throws Exception { + Assert.isNotNull(object); + Assert.isNotNull(arguments); + // prepare class/object + Class refClass = getRefClass(object); + Object refObject = getRefObject(object); + // prepare method + Method method = getMethodBySignature(refClass, signature); + Assert.isNotNull(method, "Can not find method " + signature + " in " + refClass); + // do invoke + try { + return method.invoke(refObject, arguments); + } catch (InvocationTargetException e) { + throw propagate(e.getCause()); + } + } + + /** + * Invokes method by name and parameter types. + * + * @param object + * the object to call, may be {@link Class} for invoking static method. + * @param name + * the name of method. + * @param parameterTypes + * the types of parameters. + * @param arguments + * the values of argument for invocation. + * + * @return the {@link Object} result of invoking method. + */ + public static Object invokeMethod2(Object object, + String name, + Class[] parameterTypes, + Object[] arguments) throws Exception { + Assert.equals(parameterTypes.length, arguments.length); + String signature = getMethodSignature(name, parameterTypes); + return invokeMethod(object, signature, arguments); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Class} of given {@link Object} or casted object, if it is {@link Class} + * itself. + */ + private static Class getRefClass(Object object) { + return object instanceof Class ? (Class) object : object.getClass(); + } + + /** + * @return the {@link Object} that should be used as argument for {@link Field#get(Object)} and + * {@link Method#invoke(Object, Object[])}. + */ + private static Object getRefObject(Object object) { + return object instanceof Class ? null : object; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Throwable propagation + // + //////////////////////////////////////////////////////////////////////////// + /** + * Helper class used in {@link #propagate(Throwable)}. + */ + private static class ExceptionThrower { + private static Throwable throwable; + + private ExceptionThrower() throws Throwable { + if (System.getProperty("wbp.ReflectionUtils.propagate().InstantiationException") != null) { + throw new InstantiationException(); + } + if (System.getProperty("wbp.ReflectionUtils.propagate().IllegalAccessException") != null) { + throw new IllegalAccessException(); + } + throw throwable; + } + + public static synchronized void spit(Throwable t) { + if (System.getProperty("wbp.ReflectionUtils.propagate().dontThrow") == null) { + ExceptionThrower.throwable = t; + try { + ExceptionThrower.class.newInstance(); + } catch (InstantiationException e) { + } catch (IllegalAccessException e) { + } finally { + ExceptionThrower.throwable = null; + } + } + } + } + + /** + * Propagates {@code throwable} as-is without any wrapping. This is trick. + * + * @return nothing will ever be returned; this return type is only for your convenience, to use + * this method in "throw" statement. + */ + public static RuntimeException propagate(Throwable throwable) { + if (System.getProperty("wbp.ReflectionUtils.propagate().forceReturn") == null) { + ExceptionThrower.spit(throwable); + } + return null; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java new file mode 100644 index 0000000..f7cc09d --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java @@ -0,0 +1,337 @@ +/******************************************************************************* + * 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.utils.ui; + +import com.google.common.io.Closeables; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.wb.draw2d.IColorConstants; + +import java.io.InputStream; +import java.net.URL; + +/** + * Utilities for drawing on {@link GC}. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class DrawUtils { + private static final String DOTS = "..."; + + //////////////////////////////////////////////////////////////////////////// + // + // Drawing + // + //////////////////////////////////////////////////////////////////////////// + /** + * Draws given text clipped horizontally and centered vertically. + */ + public static final void drawStringCV(GC gc, String text, int x, int y, int width, int height) { + Rectangle oldClipping = gc.getClipping(); + try { + gc.setClipping(new Rectangle(x, y, width, height)); + // + int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2; + gc.drawString(clipString(gc, text, width), x, textStartY, true); + } finally { + gc.setClipping(oldClipping); + } + } + + /** + * Draws given text clipped or centered horizontally and centered vertically. + */ + public static final void drawStringCHCV(GC gc, String text, int x, int y, int width, int height) { + int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2; + Point textSize = gc.stringExtent(text); + // + if (textSize.x > width) { + gc.drawString(clipString(gc, text, width), x, textStartY); + } else { + gc.drawString(text, x + (width - textSize.x) / 2, textStartY); + } + } + + /** + * Draws image at given x and centered vertically. + */ + public static final void drawImageCV(GC gc, Image image, int x, int y, int height) { + if (image != null) { + Rectangle imageBounds = image.getBounds(); + gc.drawImage(image, x, y + (height - imageBounds.height) / 2); + } + } + + /** + * Draws image at given x and centered vertically. + */ + public static final void drawImageCHCV(GC gc, Image image, int x, int y, int width, int height) { + if (image != null) { + Rectangle imageBounds = image.getBounds(); + int centerX = (width - imageBounds.width) / 2; + int centerY = y + (height - imageBounds.height) / 2; + gc.drawImage(image, x + centerX, centerY); + } + } + + /** + * Draws {@link Image} on {@link GC} centered in given {@link Rectangle}. If {@link Image} is + * bigger that {@link Rectangle}, {@link Image} will be scaled down as needed with keeping + * proportions. + */ + public static void drawScaledImage(GC gc, Image image, Rectangle targetRectangle) { + int imageWidth = image.getBounds().width; + int imageHeight = image.getBounds().height; + // prepare scaled image size + int newImageWidth; + int newImageHeight; + if (imageWidth <= targetRectangle.width && imageHeight <= targetRectangle.height) { + newImageWidth = imageWidth; + newImageHeight = imageHeight; + } else { + // prepare minimal scale + double k; + { + double k_w = targetRectangle.width / (double) imageWidth; + double k_h = targetRectangle.height / (double) imageHeight; + k = Math.min(k_w, k_h); + } + // calculate scaled image size + newImageWidth = (int) (imageWidth * k); + newImageHeight = (int) (imageHeight * k); + } + // draw image centered in target rectangle + int destX = targetRectangle.x + (targetRectangle.width - newImageWidth) / 2; + int destY = targetRectangle.y + (targetRectangle.height - newImageHeight) / 2; + gc.drawImage(image, 0, 0, imageWidth, imageHeight, destX, destY, newImageWidth, newImageHeight); + } + + /** + * @return the string clipped to have width less than given. Clipping is done as trailing "...". + */ + public static String clipString(GC gc, String text, int width) { + if (width <= 0) { + return ""; + } + // check if text already fits in given width + if (gc.stringExtent(text).x <= width) { + return text; + } + // use average count of characters as base + int count = Math.min(width / gc.getFontMetrics().getAverageCharWidth(), text.length()); + if (gc.stringExtent(text.substring(0, count) + DOTS).x > width) { + while (count > 0 && gc.stringExtent(text.substring(0, count) + DOTS).x > width) { + count--; + } + } else { + while (count < text.length() - 1 + && gc.stringExtent(text.substring(0, count + 1) + DOTS).x < width) { + count++; + } + } + return text.substring(0, count) + DOTS; + } + + /** + * Draws {@link String} in rectangle, wraps at any character (not by words). + */ + public static void drawTextWrap(GC gc, String text, int x, int y, int width, int height) { + int y_ = y; + int x_ = x; + int lineHeight = 0; + for (int i = 0; i < text.length(); i++) { + String c = text.substring(i, i + 1); + Point extent = gc.stringExtent(c); + if (x_ + extent.x > x + width) { + y_ += lineHeight; + if (y_ > y + height) { + return; + } + x_ = x; + } + gc.drawText(c, x_, y_); + x_ += extent.x; + lineHeight = Math.max(lineHeight, extent.y); + } + } + + /** + * Draws 3D highlight rectangle. + */ + public static void drawHighlightRectangle(GC gc, int x, int y, int width, int height) { + int right = x + width - 1; + int bottom = y + height - 1; + // + Color oldForeground = gc.getForeground(); + try { + gc.setForeground(IColorConstants.buttonLightest); + gc.drawLine(x, y, right, y); + gc.drawLine(x, y, x, bottom); + // + gc.setForeground(IColorConstants.buttonDarker); + gc.drawLine(right, y, right, bottom); + gc.drawLine(x, bottom, right, bottom); + } finally { + gc.setForeground(oldForeground); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Images + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Image} loaded relative to given {@link Class}. + */ + public static Image loadImage(Class clazz, String path) { + try { + URL resource = clazz.getResource(path); + if (resource != null) { + InputStream stream = resource.openStream(); + try { + return new Image(null, stream); + } finally { + Closeables.closeQuietly(stream); + } + } + } catch (Throwable e) { + } + return null; + } + + /** + * @return the thumbnail {@link Image} of required size for given "big" {@link Image}, centered or + * scaled down. + */ + public static Image getThubmnail(Image image, + int minWidth, + int minHeight, + int maxWidth, + int maxHeight) { + Rectangle imageBounds = image.getBounds(); + int imageWidth = imageBounds.width; + int imageHeight = imageBounds.height; + if (imageWidth < minWidth && imageHeight < minHeight) { + // create "thumbnail" Image with required size + Image thumbnail = new Image(null, minWidth, minHeight); + GC gc = new GC(thumbnail); + try { + drawImageCHCV(gc, image, 0, 0, minWidth, minHeight); + } finally { + gc.dispose(); + } + // recreate "thumbnail" Image with transparent pixel + try { + ImageData thumbnailData = thumbnail.getImageData(); + thumbnailData.transparentPixel = thumbnailData.getPixel(0, 0); + return new Image(null, thumbnailData); + } finally { + thumbnail.dispose(); + } + } else if (imageWidth <= maxWidth && imageHeight <= maxHeight) { + return new Image(null, image, SWT.IMAGE_COPY); + } else { + double kX = (double) maxWidth / imageWidth; + double kY = (double) maxHeight / imageHeight; + double k = Math.max(kX, kY); + int dWidth = (int) (imageWidth * k); + int dHeight = (int) (imageHeight * k); + ImageData scaledImageData = image.getImageData().scaledTo(dWidth, dHeight); + return new Image(null, scaledImageData); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Colors + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return new {@link Color} based on given {@link Color} and shifted on given value to make it + * darker or lighter. + */ + public static Color getShiftedColor(Color color, int delta) { + int r = Math.max(0, Math.min(color.getRed() + delta, 255)); + int g = Math.max(0, Math.min(color.getGreen() + delta, 255)); + int b = Math.max(0, Math.min(color.getBlue() + delta, 255)); + return new Color(color.getDevice(), r, g, b); + } + + /** + * @return true if the given color is dark. + */ + public static boolean isDarkColor(Color c) { + int value = + (int) Math.sqrt(c.getRed() + * c.getRed() + * .241 + + c.getGreen() + * c.getGreen() + * .691 + + c.getBlue() + * c.getBlue() + * .068); + return value < 130; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Fonts + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the bold version of given {@link Font}. + */ + public static Font getBoldFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the italic version of given {@link Font}. + */ + public static Font getBoldItalicFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD | SWT.ITALIC); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the italic version of given {@link Font}. + */ + public static Font getItalicFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.ITALIC); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the array of {@link FontData} with the specified style. + */ + private static FontData[] getModifiedFontData(Font baseFont, int style) { + FontData[] baseData = baseFont.getFontData(); + FontData[] styleData = new FontData[baseData.length]; + for (int i = 0; i < styleData.length; i++) { + FontData base = baseData[i]; + styleData[i] = new FontData(base.getName(), base.getHeight(), base.getStyle() | style); + } + return styleData; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java new file mode 100644 index 0000000..4ccda40 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java @@ -0,0 +1,541 @@ +/******************************************************************************* + * Copyright (c) 2005, 2011 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Control; + +/** + * This class provides a convienient shorthand for creating and initializing GridData. This offers + * several benefits over creating GridData normal way: + * + *

    + *
  • The same factory can be used many times to create several GridData instances
  • + *
  • The setters on GridDataFactory all return "this", allowing them to be chained
  • + *
  • GridDataFactory uses vector setters (it accepts Points), making it easy to set X and Y values + * together
  • + *
+ * + *

+ * GridDataFactory instances are created using one of the static methods on this class. + *

+ * + *

+ * Example usage: + *

+ * + * + * //////////////////////////////////////////////////////////// + * // Example 1: Typical grid data for a non-wrapping label + * + * // GridDataFactory version + * GridDataFactory.fillDefaults().applyTo(myLabel); + * + * // Equivalent SWT version + * GridData labelData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL); + * myLabel.setLayoutData(labelData); + * + * /////////////////////////////////////////////////////////// + * // Example 2: Typical grid data for a wrapping label + * + * // GridDataFactory version + * GridDataFactory.fillDefaults() + * .align(SWT.FILL, SWT.CENTER) + * .hint(150, SWT.DEFAULT) + * .grab(true, false) + * .applyTo(wrappingLabel); + * + * // Equivalent SWT version + * GridData wrappingLabelData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER); + * wrappingLabelData.minimumWidth = 1; + * wrappingLabelData.widthHint = 150; + * wrappingLabel.setLayoutData(wrappingLabelData); + * + * ////////////////////////////////////////////////////////////// + * // Example 3: Typical grid data for a scrollable control (a list box, tree, table, etc.) + * + * // GridDataFactory version + * GridDataFactory.fillDefaults().grab(true, true).hint(150, 150).applyTo(listBox); + * + * // Equivalent SWT version + * GridData listBoxData = new GridData(GridData.FILL_BOTH); + * listBoxData.widthHint = 150; + * listBoxData.heightHint = 150; + * listBoxData.minimumWidth = 1; + * listBoxData.minimumHeight = 1; + * listBox.setLayoutData(listBoxData); + * + * ///////////////////////////////////////////////////////////// + * // Example 4: Typical grid data for a button + * + * // GridDataFactory version + * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize); + * GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).hint(hint).applyTo(button); + * + * // Equivalent SWT version + * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize); + * GridData buttonData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); + * buttonData.widthHint = hint.x; + * buttonData.heightHint = hint.y; + * button.setLayoutData(buttonData); + * + * + *

+ * IMPORTANT: WHEN ASSIGNING LAYOUT DATA TO A CONTROL, BE SURE TO USE + * gridDataFactory.applyTo(control) AND NEVER control.setLayoutData(gridDataFactory). + *

+ * + * @since 3.2 + */ +public final class GridDataFactory { + private final Control m_control; + private final PixelConverter m_pixelConverter; + private final GridData m_data; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private GridDataFactory(Control control, GridData gridData) { + m_control = control; + m_pixelConverter = new PixelConverter(m_control); + // + m_data = gridData; + if (m_control.getLayoutData() != m_data) { + m_control.setLayoutData(m_data); + } + } + + /** + * Creates new {@link GridDataFactory} with new {@link GridData}. + */ + public static GridDataFactory create(Control control) { + return new GridDataFactory(control, new GridData()); + } + + /** + * Creates new {@link GridDataFactory} for modifying {@link GridData} already installed in + * control. + */ + public static GridDataFactory modify(Control control) { + GridData gridData; + { + Object existingLayoutData = control.getLayoutData(); + if (existingLayoutData instanceof GridData) { + gridData = (GridData) existingLayoutData; + } else { + gridData = new GridData(); + } + } + return new GridDataFactory(control, gridData); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Span + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param hSpan + * number of columns spanned by the control + * @param vSpan + * number of rows spanned by the control + * @return this + */ + public GridDataFactory span(int hSpan, int vSpan) { + m_data.horizontalSpan = hSpan; + m_data.verticalSpan = vSpan; + return this; + } + + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param hSpan + * number of columns spanned by the control + * @return this + */ + public GridDataFactory spanH(int hSpan) { + m_data.horizontalSpan = hSpan; + return this; + } + + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param vSpan + * number of rows spanned by the control + * @return this + */ + public GridDataFactory spanV(int vSpan) { + m_data.verticalSpan = vSpan; + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Hint + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the width and height hints. The width and height hints override the control's preferred + * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used. + * + * @param xHint + * horizontal hint (pixels), or SWT.DEFAULT to use the control's preferred size + * @param yHint + * vertical hint (pixels), or SWT.DEFAULT to use the control's preferred size + * @return this + */ + public GridDataFactory hint(int xHint, int yHint) { + m_data.widthHint = xHint; + m_data.heightHint = yHint; + return this; + } + + /** + * Sets hint in chars. + */ + public GridDataFactory hintC(int xHintInChars, int yHintInChars) { + hintHC(xHintInChars); + hintVC(yHintInChars); + return this; + } + + /** + * Sets the width hint. + * + * @return this + */ + public GridDataFactory hintH(int xHint) { + m_data.widthHint = xHint; + return this; + } + + /** + * Sets the width hint to the minimum of current hint and given otherHint. + * + * @return this + */ + public GridDataFactory hintHMin(int otherHint) { + m_data.widthHint = Math.min(m_data.widthHint, otherHint); + return this; + } + + /** + * Sets the width hint in chars. + * + * @return this + */ + public GridDataFactory hintHC(int hintInChars) { + return hintH(m_pixelConverter.convertWidthInCharsToPixels(hintInChars)); + } + + /** + * Sets the width hint. + * + * @return this + */ + public GridDataFactory hintHU(int hintInDLU) { + return hintH(m_pixelConverter.convertHorizontalDLUsToPixels(hintInDLU)); + } + + /** + * Sets the height hint. + * + * @return this + */ + public GridDataFactory hintV(int yHint) { + m_data.heightHint = yHint; + return this; + } + + /** + * Sets the height hint in chars. + * + * @return this + */ + public GridDataFactory hintVC(int hintInChars) { + return hintV(m_pixelConverter.convertHeightInCharsToPixels(hintInChars)); + } + + /** + * Increments horizontal hint on given value. + * + * @return this + */ + public GridDataFactory hintHAdd(int increment) { + return hintV(m_data.widthHint + increment); + } + + /** + * Increments vertical hint on given value. + * + * @return this + */ + public GridDataFactory hintVAdd(int increment) { + return hintV(m_data.heightHint + increment); + } + + /** + * Sets the width and height hints. The width and height hints override the control's preferred + * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used. + * + * @param hint + * size (pixels) to be used instead of the control's preferred size. If the x or y values + * are set to SWT.DEFAULT, the control's computeSize() method will be used to obtain that + * dimension of the preferred size. + * @return this + */ + public GridDataFactory hint(Point hint) { + m_data.widthHint = hint.x; + m_data.heightHint = hint.y; + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Minimum size + // + //////////////////////////////////////////////////////////////////////////// + public GridDataFactory minH(int minimumWidth) { + m_data.minimumWidth = minimumWidth; + return this; + } + + public GridDataFactory minHC(int widthInChars) { + return minH(m_pixelConverter.convertWidthInCharsToPixels(widthInChars)); + } + + public GridDataFactory minV(int minimumHeight) { + m_data.minimumHeight = minimumHeight; + return this; + } + + public GridDataFactory minVC(int heightInChars) { + return minV(m_pixelConverter.convertHeightInCharsToPixels(heightInChars)); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Alignment + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the alignment of the control within its cell. + * + * @param hAlign + * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @param vAlign + * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory align(int hAlign, int vAlign) { + m_data.horizontalAlignment = hAlign; + m_data.verticalAlignment = vAlign; + return this; + } + + /** + * Sets the horizontal and vertical alignment to GridData.FILL. + */ + public GridDataFactory fill() { + return align(GridData.FILL, GridData.FILL); + } + + /** + * Sets the horizontal alignment of the control within its cell. + * + * @param hAlign + * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory alignH(int hAlign) { + m_data.horizontalAlignment = hAlign; + return this; + } + + /** + * Sets the horizontal alignment of the control to GridData.BEGINNING + * + * @return this + */ + public GridDataFactory alignHL() { + return alignH(GridData.BEGINNING); + } + + /** + * Sets the horizontal alignment of the control to GridData.CENTER + * + * @return this + */ + public GridDataFactory alignHC() { + return alignH(GridData.CENTER); + } + + /** + * Sets the horizontal alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory alignHF() { + return alignH(GridData.FILL); + } + + /** + * Sets the horizontal alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory fillH() { + return alignHF(); + } + + /** + * Sets the horizontal alignment of the control to GridData.END + * + * @return this + */ + public GridDataFactory alignHR() { + return alignH(GridData.END); + } + + /** + * Sets the vertical alignment of the control within its cell. + * + * @param vAlign + * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory alignV(int vAlign) { + m_data.verticalAlignment = vAlign; + return this; + } + + /** + * Sets the vertical alignment of the control to GridData.BEGINNING + * + * @return this + */ + public GridDataFactory alignVT() { + return alignV(GridData.BEGINNING); + } + + /** + * Sets the vertical alignment of the control to GridData.CENTER + * + * @return this + */ + public GridDataFactory alignVM() { + return alignV(GridData.CENTER); + } + + /** + * Sets the vertical alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory alignVF() { + return alignV(GridData.FILL); + } + + /** + * Sets the vertical alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory fillV() { + return alignVF(); + } + + /** + * Sets the vertical alignment of the control to GridData.END + * + * @return this + */ + public GridDataFactory alignVB() { + return alignV(GridData.END); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Indent + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the indent of the control within the cell in pixels. + */ + public GridDataFactory indentH(int hIndent) { + m_data.horizontalIndent = hIndent; + return this; + } + + /** + * Sets the indent of the control within the cell in characters. + */ + public GridDataFactory indentHC(int hIndent) { + m_data.horizontalIndent = m_pixelConverter.convertWidthInCharsToPixels(hIndent); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Grab + // + //////////////////////////////////////////////////////////////////////////// + /** + * Determines whether extra horizontal or vertical space should be allocated to this control's + * column when the layout resizes. If any control in the column is set to grab horizontal then the + * whole column will grab horizontal space. If any control in the row is set to grab vertical then + * the whole row will grab vertical space. + * + * @param horizontal + * true if the control's column should grow horizontally + * @param vertical + * true if the control's row should grow vertically + * @return this + */ + public GridDataFactory grab(boolean horizontal, boolean vertical) { + m_data.grabExcessHorizontalSpace = horizontal; + m_data.grabExcessVerticalSpace = vertical; + return this; + } + + public GridDataFactory grabH() { + m_data.grabExcessHorizontalSpace = true; + return this; + } + + public GridDataFactory grabV() { + m_data.grabExcessVerticalSpace = true; + return this; + } + + public GridDataFactory grab() { + return grab(true, true); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Exclude + // + //////////////////////////////////////////////////////////////////////////// + public GridDataFactory exclude(boolean value) { + m_data.exclude = value; + return this; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java new file mode 100644 index 0000000..f6dc58e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * 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.utils.ui; + +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; + +/** + * GridLayoutFactory provides a convenient shorthand for creating and initializing GridLayout. + * + * @author scheglov_ke + */ +public final class GridLayoutFactory { + private final GridLayout m_layout; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private GridLayoutFactory(Composite composite, GridLayout layout) { + m_layout = layout; + composite.setLayout(m_layout); + } + + public static GridLayoutFactory create(Composite composite) { + return new GridLayoutFactory(composite, new GridLayout()); + } + + public static GridLayoutFactory modify(Composite composite) { + Layout layout = composite.getLayout(); + if (layout instanceof GridLayout) { + return new GridLayoutFactory(composite, (GridLayout) layout); + } + return create(composite); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets number of columns in {@link GridLayout}. + */ + public GridLayoutFactory columns(int numColumns) { + m_layout.numColumns = numColumns; + return this; + } + + /** + * Specifies whether all columns in the layout will be forced to have the same width. + */ + public GridLayoutFactory equalColumns() { + m_layout.makeColumnsEqualWidth = true; + return this; + } + + /** + * Sets the horizontal margins. + */ + public GridLayoutFactory marginsH(int margins) { + m_layout.marginWidth = margins; + return this; + } + + /** + * Sets the vertical margins. + */ + public GridLayoutFactory marginsV(int margins) { + m_layout.marginHeight = margins; + return this; + } + + /** + * Sets the horizontal/vertical margins. + */ + public GridLayoutFactory margins(int margins) { + m_layout.marginWidth = m_layout.marginHeight = margins; + return this; + } + + /** + * Sets zero horizontal and vertical margins. + */ + public GridLayoutFactory noMargins() { + m_layout.marginWidth = m_layout.marginHeight = 0; + return this; + } + + /** + * Sets zero horizontal and vertical spacing. + */ + public GridLayoutFactory noSpacing() { + m_layout.horizontalSpacing = m_layout.verticalSpacing = 0; + return this; + } + + /** + * Sets horizontal spacing. + */ + public GridLayoutFactory spacingH(int spacing) { + m_layout.horizontalSpacing = spacing; + return this; + } + + /** + * Sets vertical spacing. + */ + public GridLayoutFactory spacingV(int spacing) { + m_layout.verticalSpacing = spacing; + return this; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java new file mode 100644 index 0000000..d5fc39f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.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.utils.ui; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * Implementation of {@link ImageDescriptor} for {@link Image} instance. + * + * @author scheglov_ke + * @coverage core.ui + */ +public final class ImageImageDescriptor extends ImageDescriptor { + private final Image m_Image; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ImageImageDescriptor(Image image) { + m_Image = image; + } + + //////////////////////////////////////////////////////////////////////////// + // + // ImageDescriptor + // + //////////////////////////////////////////////////////////////////////////// + @Override + public ImageData getImageData() { + return m_Image == null ? null : m_Image.getImageData(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java new file mode 100644 index 0000000..1e3699f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * 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.utils.ui; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.widgets.Control; + +/** + * Helper class for converting DLU and char size into pixels. + * + * Based on code from JDT UI. + * + * @author scheglov_ke + */ +public class PixelConverter { + private final FontMetrics fFontMetrics; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + //////////////////////////////////////////////////////////////////////////// + public PixelConverter(Control control) { + GC gc = new GC(control); + gc.setFont(control.getFont()); + fFontMetrics = gc.getFontMetrics(); + gc.dispose(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Conversions + // + //////////////////////////////////////////////////////////////////////////// + /** + * see org.eclipse.jface.dialogs.DialogPage#convertHeightInCharsToPixels(int) + */ + public int convertHeightInCharsToPixels(int chars) { + return Dialog.convertHeightInCharsToPixels(fFontMetrics, chars); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertHorizontalDLUsToPixels(int) + */ + public int convertHorizontalDLUsToPixels(int dlus) { + return Dialog.convertHorizontalDLUsToPixels(fFontMetrics, dlus); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertVerticalDLUsToPixels(int) + */ + public int convertVerticalDLUsToPixels(int dlus) { + return Dialog.convertVerticalDLUsToPixels(fFontMetrics, dlus); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertWidthInCharsToPixels(int) + */ + public int convertWidthInCharsToPixels(int chars) { + return Dialog.convertWidthInCharsToPixels(fFontMetrics, chars); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java new file mode 100644 index 0000000..126efba --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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.utils.ui; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Shell; + +/** + * Utilities for UI. + * + * @author scheglov_ke + */ +public class UiUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Message dialogs + // + //////////////////////////////////////////////////////////////////////////// + /** + * Opens standard warning dialog. + */ + public static void openWarning(Shell parent, String title, String message) { + MessageDialog dialog = + new MessageDialog(parent, + title, + null, + message, + MessageDialog.WARNING, + new String[]{IDialogConstants.OK_LABEL}, + 0); + dialog.open(); + } +} \ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java new file mode 100644 index 0000000..307c2f6 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * 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.utils.ui.dialogs; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * {@link Dialog} that remembers location/size between usage sessions. + * + * @author scheglov_ke + * @coverage core.ui + */ +public abstract class ResizableDialog extends Dialog { + /** + * Key for accessing {@link Dialog} from its {@link Shell}. + */ + public static final String KEY_DIALOG = "KEY_DIALOG"; + //////////////////////////////////////////////////////////////////////////// + // + // Internal constants + // + //////////////////////////////////////////////////////////////////////////// + private static final String X = "x"; + private static final String Y = "y"; + private static final String WIDTH = "width"; + private static final String HEIGHT = "height"; + //////////////////////////////////////////////////////////////////////////// + // + // Instance fields + // + //////////////////////////////////////////////////////////////////////////// + private final AbstractUIPlugin m_plugin; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ResizableDialog(Shell parentShell, AbstractUIPlugin plugin) { + super(parentShell); + m_plugin = plugin; + setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Size + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected Point getInitialSize() { + // track the current dialog bounds + installDialogBoundsTracker(); + // answer the size from the previous incarnation + Point defaultSize = getDefaultSize(); + if ((getShellStyle() & SWT.RESIZE) != 0) { + Rectangle oldBounds = loadBounds(); + if (oldBounds != null) { + Rectangle displayBounds = getShell().getDisplay().getBounds(); + int width = Math.min(displayBounds.width, Math.max(oldBounds.width, defaultSize.x)); + int height = Math.min(displayBounds.height, Math.max(oldBounds.height, defaultSize.y)); + return new Point(width, height); + } + } + // use default size + return defaultSize; + } + + /** + * @return the default size of dialog. + */ + protected Point getDefaultSize() { + return super.getInitialSize(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Location + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected Point getInitialLocation(Point initialSize) { + Rectangle windowBounds; + { + Shell windowShell = m_plugin.getWorkbench().getActiveWorkbenchWindow().getShell(); + windowBounds = windowShell.getBounds(); + } + // answer the location from the previous incarnation + Rectangle bounds = loadBounds(); + if (bounds != null) { + int x = bounds.x; + int y = bounds.y; + int maxX = windowBounds.x + windowBounds.width - initialSize.x; + int maxY = windowBounds.y + windowBounds.height - initialSize.y; + if (x > maxX) { + x = maxX; + } + if (y > maxY) { + y = maxY; + } + if (x < windowBounds.x) { + x = windowBounds.x; + } + if (y < windowBounds.y) { + y = windowBounds.y; + } + return new Point(x, y); + } + // default location - centered on workbench window + int x = windowBounds.x + (windowBounds.width - initialSize.x) / 2; + int y = windowBounds.y + (windowBounds.height - initialSize.y) / 2; + return new Point(x, y); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Bounds + // + //////////////////////////////////////////////////////////////////////////// + /** + * Loads bounds from {@link IDialogSettings}. + */ + private Rectangle loadBounds() { + IDialogSettings settings = getDialogSettings(); + try { + return new Rectangle(settings.getInt(X), + settings.getInt(Y), + settings.getInt(WIDTH), + settings.getInt(HEIGHT)); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Saves bounds to {@link IDialogSettings}. + */ + private void saveBounds(Rectangle bounds) { + IDialogSettings settings = getDialogSettings(); + settings.put(X, bounds.x); + settings.put(Y, bounds.y); + settings.put(WIDTH, bounds.width); + settings.put(HEIGHT, bounds.height); + } + + /** + * @return the {@link IDialogSettings} for this dialog with this type. + */ + protected IDialogSettings getDialogSettings() { + IDialogSettings settings = m_plugin.getDialogSettings(); + String sectionName = getDialogSettingsSectionName(); + if (settings.getSection(sectionName) == null) { + return settings.addNewSection(sectionName); + } + return settings.getSection(sectionName); + } + + /** + * @return the name of section for dialog specific bounds. By default uses name of {@link Class}, + * but if same dialog is used for displaying different content, then may be overridden. + */ + protected String getDialogSettingsSectionName() { + return getClass().getName(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Size tracking + // + //////////////////////////////////////////////////////////////////////////// + protected Rectangle cachedBounds; + + private void installDialogBoundsTracker() { + getShell().addControlListener(new ControlListener() { + public void controlMoved(ControlEvent e) { + cachedBounds = getShell().getBounds(); + } + + public void controlResized(ControlEvent e) { + cachedBounds = getShell().getBounds(); + } + }); + } + + @Override + public boolean close() { + boolean shellMaximized = getShell().getMaximized(); + boolean closed = super.close(); + if (closed && !shellMaximized && cachedBounds != null) { + saveBounds(cachedBounds); + } + return closed; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Shell + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setData(KEY_DIALOG, this); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java new file mode 100644 index 0000000..745bcc3 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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.utils.ui.dialogs; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; +import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.List; + +/** + * The dialog for editing array of {@link String}'s. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class StringsDialog extends TextDialog { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public StringsDialog(Shell parentShell, + AbstractUIPlugin plugin, + String titleText, + String headerText, + String footerText) { + super(parentShell, plugin, titleText, headerText, footerText); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Items + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the items to edit. + */ + public void setItems(String[] items) { + setText(Joiner.on('\n').join(items)); + } + + /** + * @return the edited items. + */ + public String[] getItems() { + return ExecutionUtils.runObjectLog(new RunnableObjectEx() { + @Override + public String[] runObject() throws Exception { + List strings = Lists.newArrayList(); + BufferedReader br = new BufferedReader(new StringReader(getText())); + while (true) { + String s = br.readLine(); + if (s == null) { + break; + } + strings.add(s); + } + return strings.toArray(new String[strings.size()]); + } + }, new String[0]); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java new file mode 100644 index 0000000..320fa32 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.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.utils.ui.dialogs; + +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.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.wb.internal.core.utils.ui.GridDataFactory; +import org.eclipse.wb.internal.core.utils.ui.GridLayoutFactory; + +/** + * The dialog for editing multiline text. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class TextDialog extends ResizableDialog { + private final String m_titleText; + private final String m_headerText; + private final String m_footerText; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public TextDialog(Shell parentShell, + AbstractUIPlugin plugin, + String titleText, + String headerText, + String footerText) { + super(parentShell, plugin); + m_titleText = titleText; + m_headerText = headerText; + m_footerText = footerText; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Text + // + //////////////////////////////////////////////////////////////////////////// + private String m_text; + + /** + * Sets the text to edit. + */ + public final void setText(String text) { + m_text = text; + } + + /** + * @return the edited text. + */ + public final String getText() { + return m_text; + } + + //////////////////////////////////////////////////////////////////////////// + // + // GUI + // + //////////////////////////////////////////////////////////////////////////// + protected Text m_textWidget; + + @Override + protected Control createDialogArea(Composite parent) { + Composite area = (Composite) super.createDialogArea(parent); + GridLayoutFactory.create(area); + // header + new Label(area, SWT.NONE).setText(m_headerText); + // Text widget + { + m_textWidget = new Text(area, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + GridDataFactory.create(m_textWidget).grab().fill().hintVC(10); + m_textWidget.setText(m_text); + // handle Ctrl+Enter as OK + m_textWidget.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.stateMask == SWT.CTRL && e.keyCode == SWT.CR) { + okPressed(); + } + } + }); + } + // footer + new Label(area, SWT.NONE).setText(m_footerText); + // + return area; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(m_titleText); + } + + @Override + protected void okPressed() { + m_text = m_textWidget.getText(); + super.okPressed(); + } +} -- cgit v1.2.3