diff options
author | Brian de Alwis | 2011-12-05 02:58:36 +0000 |
---|---|---|
committer | Brian de Alwis | 2011-12-05 02:58:36 +0000 |
commit | 06c7ad375100fb67c527a72bce9e49656a3347ac (patch) | |
tree | 50eea6e590da61c951e66d359a160fc4b59887f0 | |
parent | 803897d9e64563377509d623c19efb8f333b42a1 (diff) | |
parent | bc9605c14340508a5d8fc6bdfcb34dbffa7ca634 (diff) | |
download | org.eclipse.e4.tools-06c7ad375100fb67c527a72bce9e49656a3347ac.tar.gz org.eclipse.e4.tools-06c7ad375100fb67c527a72bce9e49656a3347ac.tar.xz org.eclipse.e4.tools-06c7ad375100fb67c527a72bce9e49656a3347ac.zip |
Merge CSS Spy branch
17 files changed, 1282 insertions, 0 deletions
diff --git a/bundles/org.eclipse.e4.tools.css.spy/.classpath b/bundles/org.eclipse.e4.tools.css.spy/.classpath new file mode 100644 index 00000000..64c5e31b --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/bundles/org.eclipse.e4.tools.css.spy/.project b/bundles/org.eclipse.e4.tools.css.spy/.project new file mode 100644 index 00000000..f29584da --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.e4.tools.css.spy</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/bundles/org.eclipse.e4.tools.css.spy/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.e4.tools.css.spy/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..af0f20f9 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/bundles/org.eclipse.e4.tools.css.spy/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.tools.css.spy/META-INF/MANIFEST.MF new file mode 100644 index 00000000..3dcd3fd6 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/META-INF/MANIFEST.MF @@ -0,0 +1,22 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: CSS Spy +Bundle-SymbolicName: org.eclipse.e4.tools.css.spy;singleton:=true +Bundle-Version: 0.0.0.qualifier +Bundle-Activator: org.eclipse.e4.tools.css.spy.Activator +Bundle-Vendor: Eclipse.org +Require-Bundle: org.eclipse.core.runtime;bundle-version="3.6.0", + org.eclipse.e4.ui.workbench;bundle-version="0.9.0";resolution:=optional, + org.eclipse.e4.ui.css.core;bundle-version="0.9.0", + org.eclipse.swt;bundle-version="3.6.0", + org.eclipse.jface;bundle-version="3.8.0", + org.eclipse.e4.ui.css.swt;bundle-version="0.10.0", + org.eclipse.e4.ui.css.swt.theme;bundle-version="0.9.1" +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Bundle-ActivationPolicy: lazy +Import-Package: javax.inject;version="1.0.0", + org.eclipse.e4.core.contexts, + org.eclipse.e4.core.di.annotations, + org.eclipse.e4.core.services.statusreporter, + org.eclipse.e4.ui.services, + org.w3c.css.sac;version="1.3.0" diff --git a/bundles/org.eclipse.e4.tools.css.spy/build.properties b/bundles/org.eclipse.e4.tools.css.spy/build.properties new file mode 100644 index 00000000..e9863e28 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/bundles/org.eclipse.e4.tools.css.spy/plugin.xml b/bundles/org.eclipse.e4.tools.css.spy/plugin.xml new file mode 100644 index 00000000..c442ab42 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/plugin.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> +<plugin> + <extension + id="css.spy" + point="org.eclipse.e4.workbench.model"> + <processor + beforefragment="false" + class="org.eclipse.e4.tools.css.spy.SpyInstaller"> + </processor> + </extension> + +</plugin> diff --git a/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/Activator.java b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/Activator.java new file mode 100644 index 00000000..cf8f4c62 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/Activator.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2011 Manumitting Technologies, 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: + * Brian de Alwis (MT) - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.tools.css.spy; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + private static BundleContext context; + + static BundleContext getContext() { + return context; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext bundleContext) throws Exception { + Activator.context = bundleContext; + } + + /* + * (non-Javadoc) + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext bundleContext) throws Exception { + Activator.context = null; + } + + public static String join(String[] elements, String glue) { + StringBuilder sb = new StringBuilder(); + join(sb, elements, glue); + return sb.toString(); + } + + public static void join(StringBuilder sb, String[] elements, String glue) { + for (int i = 0; i < elements.length; i++) { + sb.append(elements[i]); + if (i < elements.length - 1) { + sb.append(glue); + } + } + } + +} diff --git a/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CSSPropertiesContentProvider.java b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CSSPropertiesContentProvider.java new file mode 100644 index 00000000..6442e268 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CSSPropertiesContentProvider.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2011 Manumitting Technologies, 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: + * Brian de Alwis (MT) - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.tools.css.spy; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; +import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyHandler; +import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.swt.dom.WidgetElement; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Widget; + +public class CSSPropertiesContentProvider implements IStructuredContentProvider { + + protected CSSEngine cssEngine; + protected CSSStylableElement input; + + public void dispose() { + cssEngine = null; + input = null; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput instanceof CSSStylableElement) { + this.input = (CSSStylableElement) newInput; + this.cssEngine = WidgetElement.getEngine((Widget) input.getNativeWidget()); + } else if (newInput instanceof Widget) { + this.cssEngine = WidgetElement.getEngine((Widget) newInput); + this.input = (CSSStylableElement) cssEngine.getElement((Widget) newInput); + } + } + + public Object[] getElements(Object inputElement) { + Map<String, ICSSPropertyHandler> handlerMap = cssEngine + .getCSSPropertyHandlers(input); + if (handlerMap == null) { + return null; + } + List<CSSPropertyProvider> properties = new ArrayList<CSSPropertyProvider>(handlerMap.size()); + for (Entry<String, ICSSPropertyHandler> entry : handlerMap.entrySet()) { + properties.add(new CSSPropertyProvider(entry.getKey(), input, entry.getValue(), cssEngine)); + } + return properties.toArray(); + } + +} diff --git a/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CSSPropertyProvider.java b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CSSPropertyProvider.java new file mode 100644 index 00000000..ffca1dda --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CSSPropertyProvider.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2011 Manumitting Technologies, 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: + * Brian de Alwis (MT) - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.tools.css.spy; + +import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; +import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyHandler; +import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.w3c.dom.css.CSSValue; + +/** + * A getter and setter of a particular CSS property for a particular element. + */ +public class CSSPropertyProvider { + + private String propertyName; + private CSSStylableElement element; + private ICSSPropertyHandler handler; + private CSSEngine engine; + + public CSSPropertyProvider(String propertyName, CSSStylableElement element, + ICSSPropertyHandler handler, CSSEngine engine) { + this.propertyName = propertyName; + this.element = element; + this.handler = handler; + this.engine = engine; + } + + public String getPropertyName() { + return propertyName; + } + + public String getValue() throws Exception { + return handler.retrieveCSSProperty(element, propertyName, "", engine); + } + + public void setValue(String value) throws Exception { + CSSValue v = engine.parsePropertyValue(value); + handler.applyCSSProperty(element, propertyName, v, "", engine); + } + + + @Override + public String toString() { + return propertyName; + } + +} diff --git a/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CssSpyDialog.java b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CssSpyDialog.java new file mode 100644 index 00000000..4eb11336 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/CssSpyDialog.java @@ -0,0 +1,705 @@ +/******************************************************************************* + * Copyright (c) 2011 Manumitting Technologies, 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: + * Brian de Alwis (MT) - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.tools.css.spy; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; +import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.swt.dom.WidgetElement; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.layout.TableColumnLayout; +import org.eclipse.jface.layout.TreeColumnLayout; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ColumnViewerEditor; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TableViewerEditor; +import org.eclipse.jface.viewers.TableViewerFocusCellManager; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +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.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseMoveListener; +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.graphics.Region; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.swt.widgets.Widget; +import org.w3c.css.sac.SelectorList; +import org.w3c.dom.NodeList; +import org.w3c.dom.css.CSSStyleDeclaration; + +public class CssSpyDialog extends Dialog { + /** @return the CSS element corresponding to the argument, or null if none */ + public static CSSStylableElement getCSSElement(Object o) { + if (o instanceof CSSStylableElement) { + return (CSSStylableElement) o; + } else if (o instanceof Widget) { + CSSEngine engine = WidgetElement.getEngine((Widget) o); + return (CSSStylableElement) engine.getElement(o); + } + return null; + } + + /** @return the CSS engine governing the argument, or null if none */ + public static CSSEngine getCSSEngine(Object o) { + if (o instanceof CSSStylableElement) { + CSSStylableElement element = (CSSStylableElement) o; + return WidgetElement.getEngine((Widget) element.getNativeWidget()); + } else if (o instanceof Widget) { + return WidgetElement.getEngine((Widget) o); + } + return null; + } + + private Display display; + private Widget specimen; + private Widget shown; + + private TableViewer cssPropertiesViewer; + private TreeViewer widgetTreeViewer; + private Text cssRules; + + private List<Shell> highlights = new LinkedList<Shell>(); + private List<Region> highlightRegions = new LinkedList<Region>(); + private Text cssSearchBox; + private Button showUnsetProperties; + + protected ViewerFilter unsetPropertyFilter = new ViewerFilter() { + + @Override + public boolean select(Viewer viewer, Object parentElement, + Object element) { + if (element instanceof CSSPropertyProvider) { + try { + return ((CSSPropertyProvider) element).getValue() != null; + } catch (Exception e) { + return false; + } + } + return false; + } + }; + + /** + * Create the dialog. + * + * @param parentShell + */ + public CssSpyDialog(Shell parentShell) { + super(parentShell); + display = parentShell.getDisplay(); + setShellStyle(SWT.DIALOG_TRIM | SWT.RESIZE/* | SWT.PRIMARY_MODAL */); + } + + public Widget getSpecimen() { + return specimen; + } + + private boolean isLive() { + return specimen == null; + } + + public void setSpecimen(Widget specimen) { + this.specimen = specimen; + update(); + } + + private Widget getActiveSpecimen() { + if (specimen != null) { + return specimen; + } + return display.getCursorControl(); + } + + protected boolean shouldDismissOnLostFocus() { + return false; + } + + protected void update() { + if (getShell() == null) { + return; + } + Widget current = getActiveSpecimen(); + if (shown == current) { + return; + } + shown = current; + + CSSEngine engine = WidgetElement.getEngine(shown); + CSSStylableElement element = (CSSStylableElement) engine + .getElement(shown); + if (element == null) { + return; + } + + widgetTreeViewer + .setInput(new Object[] { shown instanceof Control ? ((Control) shown) + .getShell() : shown }); + widgetTreeViewer.reveal(shown); + widgetTreeViewer.setSelection(new StructuredSelection(shown)); + + populate(shown); + } + + protected void populate(Widget selected) { + CSSStylableElement element = getCSSElement(selected); + if (element == null) { + return; + } + + cssPropertiesViewer.setInput(selected); + + StringBuilder sb = new StringBuilder(); + CSSEngine engine = getCSSEngine(element); + CSSStyleDeclaration decl = engine.getViewCSS().getComputedStyle( + element, null); + + if (element.getCSSStyle() != null) { + sb.append("\nCSS Inline Style(s):\n "); + Activator.join(sb, element.getCSSStyle().split(";"), ";\n "); + } + + if (decl != null) { + sb.append("\n\nCSS Properties:\n"); + try { + if (decl != null) { + sb.append(decl.getCssText()); + } + } catch (Throwable e) { + sb.append(e); + } + } + if (element.getStaticPseudoInstances().length > 0) { + sb.append("\nStatic Pseudoinstances:\n "); + Activator.join(sb, element.getStaticPseudoInstances(), "\n "); + } + + if (element.getCSSClass() != null) { + sb.append("\n\nCSS Classes:\n "); + Activator.join(sb, element.getCSSClass().split(" +"), "\n "); + } + + // FIXME: shouldn't this be getCSSStyle? + if (element.getAttribute("style") != null) { + sb.append("\n\nSWT Style Bits:\n "); + Activator.join(sb, element.getAttribute("style").split(" +"), + "\n "); + } + + sb.append("\n\nCSS Class Element:\n ").append( + element.getClass().getName()); + + // this is useful for diagnosing issues + if (element.getNativeWidget() instanceof Composite) { + sb.append("\n\nSWT Layout:\n ").append( + ((Composite) element.getNativeWidget()).getLayout()); + } + + cssRules.setText(sb.toString().trim()); + + disposeHighlights(); + highlightWidget(selected); + } + + private void highlightWidget(Widget selected) { + widgetTreeViewer.reveal(selected); + + Rectangle bounds = getBounds(selected); // relative to absolute display, + // not the widget + if (bounds == null /* || bounds.height == 0 || bounds.width == 0 */) { + return; + } + // emulate a transparent background as per SWT Snippet180 + Shell selectedShell = getShell(selected); + if (selectedShell != null) { + // bounds = slectedShell.getDisplay().map(null, selectedShell, + // bounds); + } + Shell highlight = new Shell(selectedShell, SWT.NO_TRIM | SWT.MODELESS); // appears + // on + // top + highlight.setBackground(display.getSystemColor(SWT.COLOR_RED)); + Region highlightRegion = new Region(); + highlightRegion.add(0, 0, 1, bounds.height + 2); + highlightRegion.add(0, 0, bounds.width + 2, 1); + highlightRegion.add(bounds.width + 1, 0, 1, bounds.height + 2); + highlightRegion.add(0, bounds.height + 1, bounds.width + 2, 1); + highlight.setRegion(highlightRegion); + highlight.setBounds(bounds.x - 1, bounds.y - 1, bounds.width + 2, + bounds.height + 2); + highlight.setEnabled(false); + highlight.setVisible(true); // not open(): setVisible() prevents taking + // focus + + highlights.add(highlight); + highlightRegions.add(highlightRegion); + } + + private Shell getShell(Widget widget) { + if (widget instanceof Control) { + return ((Control) widget).getShell(); + } + return null; + } + + private void disposeHighlights() { + for (Shell highlight : highlights) { + highlight.dispose(); + } + highlights.clear(); + for (Region region : highlightRegions) { + region.dispose(); + } + highlightRegions.clear(); + } + + private Rectangle getBounds(Widget widget) { + if (widget instanceof Shell) { + // Shell bounds are already in display coordinates + return ((Shell) widget).getBounds(); + } else if (widget instanceof Control) { + Control control = (Control) widget; + Rectangle bounds = control.getBounds(); + return control.getDisplay().map(control.getParent(), null, bounds); + } else if (widget instanceof ToolItem) { + ToolItem item = (ToolItem) widget; + Rectangle bounds = item.getBounds(); + return item.getDisplay().map(item.getParent(), null, bounds); + } + // FIXME: figure out how to map items to a position + return null; + } + + /** + * Create contents of the dialog. + * + * @param parent + */ + @Override + protected Control createDialogArea(Composite parent) { + Composite outer = (Composite) super.createDialogArea(parent); + + cssSearchBox = new Text(outer, SWT.BORDER | SWT.SEARCH + | SWT.ICON_SEARCH | SWT.ICON_CANCEL); + cssSearchBox.setMessage("CSS Selector"); + cssSearchBox.setToolTipText("Highlight matching widgets"); + cssSearchBox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, + false, 1, 1)); + + SashForm sashForm = new SashForm(outer, SWT.VERTICAL); + sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, + 1)); + + // / THE WIDGET TREE + Composite widgetsComposite = new Composite(sashForm, SWT.NONE); + + widgetTreeViewer = new TreeViewer(widgetsComposite, SWT.BORDER); + widgetTreeViewer.setContentProvider(new WidgetTreeProvider()); + widgetTreeViewer.setAutoExpandLevel(0); + widgetTreeViewer.getTree().setLinesVisible(true); + widgetTreeViewer.getTree().setHeaderVisible(true); + ColumnViewerToolTipSupport.enableFor(widgetTreeViewer); + + TreeViewerColumn widgetTypeColumn = new TreeViewerColumn( + widgetTreeViewer, SWT.NONE); + widgetTypeColumn.getColumn().setWidth(100); + widgetTypeColumn.getColumn().setText("Widget"); + widgetTypeColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object item) { + CSSStylableElement element = CssSpyDialog.getCSSElement(item); + return element.getLocalName() + " (" + + element.getNamespaceURI() + ")"; + } + }); + + TreeViewerColumn widgetClassColumn = new TreeViewerColumn( + widgetTreeViewer, SWT.NONE); + widgetClassColumn.getColumn().setText("CSS Class"); + widgetClassColumn.getColumn().setWidth(100); + widgetClassColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object item) { + CSSStylableElement element = CssSpyDialog.getCSSElement(item); + if (element.getCSSClass() == null) { + return null; + } + String classes[] = element.getCSSClass().split(" +"); + return classes.length <= 1 ? classes[0] : classes[0] + " (+" + + (classes.length - 1) + " others)"; + } + + @Override + public String getToolTipText(Object item) { + CSSStylableElement element = CssSpyDialog.getCSSElement(item); + if (element == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + sb.append(element.getLocalName()).append(" (") + .append(element.getNamespaceURI()).append(")"); + if (element.getCSSClass() != null) { + sb.append("\nClasses:\n "); + Activator.join(sb, element.getCSSClass().split(" +"), + "\n "); + } + return sb.toString(); + } + }); + + TreeViewerColumn widgetIdColumn = new TreeViewerColumn( + widgetTreeViewer, SWT.NONE); + widgetIdColumn.getColumn().setWidth(100); + widgetIdColumn.getColumn().setText("CSS Id"); + widgetIdColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object item) { + CSSStylableElement element = CssSpyDialog.getCSSElement(item); + return element.getCSSId(); + } + }); + + TreeColumnLayout widgetsTableLayout = new TreeColumnLayout(); + widgetsTableLayout.setColumnData(widgetTypeColumn.getColumn(), + new ColumnWeightData(50)); + widgetsTableLayout.setColumnData(widgetIdColumn.getColumn(), + new ColumnWeightData(40)); + widgetsTableLayout.setColumnData(widgetClassColumn.getColumn(), + new ColumnWeightData(40)); + widgetsComposite.setLayout(widgetsTableLayout); + + // / HEADERS + Composite container = new Composite(sashForm, SWT.NONE); + container.setLayout(new GridLayout(2, true)); + + Label lblCssProperties = new Label(container, SWT.NONE); + lblCssProperties.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, + false, false, 1, 1)); + lblCssProperties.setText("CSS Properties"); + + Label lblCssRules = new Label(container, SWT.NONE); + lblCssRules.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, + false, 1, 1)); + lblCssRules.setText("CSS Rules"); + + // // THE CSS PROPERTIES TABLE + Composite propsComposite = new Composite(container, SWT.BORDER + | SWT.H_SCROLL | SWT.V_SCROLL); + GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1); + gridData.minimumHeight = 50; + propsComposite.setLayoutData(gridData); + + cssPropertiesViewer = new TableViewer(propsComposite, SWT.BORDER + | SWT.V_SCROLL | SWT.FULL_SELECTION); + cssPropertiesViewer + .setContentProvider(new CSSPropertiesContentProvider()); + cssPropertiesViewer.getTable().setLinesVisible(true); + cssPropertiesViewer.getTable().setHeaderVisible(true); + cssPropertiesViewer.setComparator(new ViewerComparator()); + + final TextCellEditor textCellEditor = new TextCellEditor( + cssPropertiesViewer.getTable()); + TableViewerEditor + .create(cssPropertiesViewer, + new TableViewerFocusCellManager(cssPropertiesViewer, + new FocusCellOwnerDrawHighlighter( + cssPropertiesViewer)), + new ColumnViewerEditorActivationStrategy( + cssPropertiesViewer), + ColumnViewerEditor.TABBING_HORIZONTAL + | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR + | ColumnViewerEditor.TABBING_VERTICAL + | ColumnViewerEditor.KEYBOARD_ACTIVATION); + + + TableViewerColumn propName = new TableViewerColumn(cssPropertiesViewer, + SWT.NONE); + propName.getColumn().setWidth(100); + propName.getColumn().setText("Property"); + propName.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((CSSPropertyProvider) element).getPropertyName(); + } + }); + + TableViewerColumn propValue = new TableViewerColumn( + cssPropertiesViewer, SWT.NONE); + propValue.getColumn().setWidth(100); + propValue.getColumn().setText("Value"); + propValue.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + try { + return ((CSSPropertyProvider) element).getValue(); + } catch (Exception e) { + System.err.println("Error fetching property: " + element + + ": " + e); + return null; + } + } + }); + propValue.setEditingSupport(new EditingSupport(cssPropertiesViewer) { + @Override + protected CellEditor getCellEditor(Object element) { + // do the fancy footwork here to return an appropriate editor to + // the value-type + return textCellEditor; + } + + @Override + protected boolean canEdit(Object element) { + return true; + } + + @Override + protected Object getValue(Object element) { + try { + String value = ((CSSPropertyProvider) element).getValue(); + return value == null ? "" : value; + } catch (Exception e) { + return ""; + } + } + + @Override + protected void setValue(Object element, Object value) { + try { + if(value == null || ((String)value).trim().isEmpty()) { return; } + CSSPropertyProvider provider = (CSSPropertyProvider) element; + provider.setValue((String) value); + } catch (Throwable e) { + MessageDialog.openError(getShell(), "Error", + "Unable to set property:\n\n" + + e.getMessage()); + } + cssPropertiesViewer.update(element, null); + } + }); + + TableColumnLayout propsTableLayout = new TableColumnLayout(); + propsTableLayout.setColumnData(propName.getColumn(), + new ColumnWeightData(50)); + propsTableLayout.setColumnData(propValue.getColumn(), + new ColumnWeightData(50)); + propsComposite.setLayout(propsTableLayout); + + // / THE CSS RULES + cssRules = new Text(container, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL + | SWT.MULTI); + cssRules.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, + 1)); + + // / THE CSS PROPERTIES TABLE (again) + showUnsetProperties = new Button(container, SWT.CHECK); + showUnsetProperties.setText("Show unset properties"); + + // and for balance + new Label(container, SWT.NONE); + + // / The listeners + + cssSearchBox.addModifyListener(new ModifyListener() { + private Runnable updater; + + public void modifyText(ModifyEvent e) { + display.timerExec(200, updater = new Runnable() { + public void run() { + if (updater == this) { + performCSSSearch(cssSearchBox.getText()); + } + } + }); + } + }); + cssSearchBox.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.keyCode == SWT.ARROW_DOWN + && (e.stateMask & SWT.MODIFIER_MASK) == 0) { + widgetTreeViewer.getControl().setFocus(); + } + } + }); + + widgetTreeViewer + .addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!event.getSelection().isEmpty()) { + populate((Widget) ((StructuredSelection) event + .getSelection()).getFirstElement()); + } + } + }); + if (isLive()) { + container.addMouseMoveListener(new MouseMoveListener() { + public void mouseMove(MouseEvent e) { + update(); + } + }); + } + + if (shouldDismissOnLostFocus()) { + container.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + setReturnCode(Window.OK); + close(); + } + }); + } + container.addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + if (e.character == SWT.ESC) { + cancelPressed(); + } else if (e.character == SWT.CR | e.character == SWT.LF) { + okPressed(); + } + } + }); + + outer.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + dispose(); + } + }); + + showUnsetProperties.setSelection(true); + showUnsetProperties.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (showUnsetProperties.getSelection()) { + cssPropertiesViewer.removeFilter(unsetPropertyFilter); + } else { + cssPropertiesViewer.addFilter(unsetPropertyFilter); + } + } + }); + + update(); + sashForm.setWeights(new int[] { 50, 50 }); + widgetTreeViewer.getControl().setFocus(); + return outer; + } + + private String searchInProgress; + + private void performCSSSearch(String text) { + disposeHighlights(); + widgetTreeViewer.collapseAll(); + if (text.trim().isEmpty()) { + return; + } + searchInProgress = text; + CSSStylableElement element = getCSSElement(getShell(shown)); + if (element == null) { + return; + } + + CSSEngine engine = WidgetElement.getEngine(shown); + try { + SelectorList selectors = engine.parseSelectors(text); + processCSSSearch(text, null, engine, selectors, element); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private void processCSSSearch(String text, String pseudo, CSSEngine engine, + SelectorList selectors, CSSStylableElement element) { + if (text != searchInProgress) { + return; + } + boolean matched = false; + for (int i = 0; i < selectors.getLength(); i++) { + if (matched = engine.matches(selectors.item(i), element, pseudo)) { + break; + } + } + if (matched) { + highlightWidget((Widget) element.getNativeWidget()); + } + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + if (text != searchInProgress) { + return; + } + processCSSSearch(text, pseudo, engine, selectors, + (CSSStylableElement) children.item(i)); + } + } + + protected void dispose() { + disposeHighlights(); + } + + /** + * Create contents of the button bar. + * + * @param parent + */ + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, + true); + createButton(parent, IDialogConstants.CANCEL_ID, + IDialogConstants.CANCEL_LABEL, false); + } + + /** + * Return the initial size of the dialog. + */ + @Override + protected Point getInitialSize() { + return new Point(600, 500); + } +} diff --git a/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/OpenSpyHandler.java b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/OpenSpyHandler.java new file mode 100644 index 00000000..7da7582d --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/OpenSpyHandler.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2011 Manumitting Technologies, 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: + * Brian de Alwis (MT) - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.tools.css.spy; + +import javax.inject.Inject; +import javax.inject.Provider; + +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.core.di.annotations.Optional; +import org.eclipse.e4.core.services.statusreporter.StatusReporter; +import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl; +import org.eclipse.e4.ui.css.swt.internal.theme.ThemeEngine; +import org.eclipse.e4.ui.css.swt.theme.IThemeEngine; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; + +public class OpenSpyHandler { + + @Inject + @Optional + protected Display display; + + @Inject + protected IEclipseContext context; + + @Inject + protected Provider<StatusReporter> reporter; + + @Execute + public void openSpy() { + Control control = display.getCursorControl(); + // it may be that only the shell was selected + if (control == null) { + control = display.getActiveShell(); + } + CssSpyDialog spy = new CssSpyDialog(control.getShell()); + spy.setSpecimen(control); + spy.open(); + } + + private CSSEngine findCSSEngine() { + if(display != null) { + Object themeEngine = display.getData("org.eclipse.e4.ui.css.swt.theme"); + if(themeEngine instanceof ThemeEngine) { return ((ThemeEngine)themeEngine) + .getCSSEngine(); } + } + if(context != null) { + Object themeEngine = context.get(IThemeEngine.class); + if(themeEngine instanceof ThemeEngine) { return ((ThemeEngine)themeEngine) + .getCSSEngine(); } + } + // otherwise just create a copy of the engine. + return new CSSSWTEngineImpl(display, true); + } +} diff --git a/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/SpyInstaller.java b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/SpyInstaller.java new file mode 100644 index 00000000..87f65e54 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/SpyInstaller.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2011 Manumitting Technologies, 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: + * Brian de Alwis (MT) - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.tools.css.spy; + +import javax.inject.Inject; + +import org.eclipse.e4.core.di.annotations.Execute; +import org.eclipse.e4.ui.model.application.MApplication; +import org.eclipse.e4.ui.model.application.commands.MBindingContext; +import org.eclipse.e4.ui.model.application.commands.MBindingTable; +import org.eclipse.e4.ui.model.application.commands.MCommand; +import org.eclipse.e4.ui.model.application.commands.MCommandsFactory; +import org.eclipse.e4.ui.model.application.commands.MHandler; +import org.eclipse.e4.ui.model.application.commands.MKeyBinding; + +public class SpyInstaller { + public static final String OPEN_SPY_COMMAND_ID = "org.eclipse.e4.css.OpenSpy"; + private static final String SPY_HANDLER_ID = OpenSpyHandler.class.getName(); + private static final String SPY_HANDLER_URI = "platform:/plugin/org.eclipse.e4.tools.css.spy/" + + OpenSpyHandler.class.getName(); + + @Inject + protected MApplication app; + + @Execute + public void execute() { + MCommand cmd = installSpyCommand(); + installSpyHandler(cmd); + installSpyBinding("org.eclipse.ui.contexts.dialogAndWindow", cmd, "M2+M3+F4"); + } + + private MCommand installSpyCommand() { + for(MCommand cmd : app.getCommands()) { + if(OPEN_SPY_COMMAND_ID.equals(cmd.getElementId())) { + System.err.println("CSS Spy command already setup"); + return cmd; + } + } + + MCommand cmd = MCommandsFactory.INSTANCE.createCommand(); + cmd.setCommandName("Open CSS Spy"); + cmd.setElementId(OPEN_SPY_COMMAND_ID); + app.getCommands().add(cmd); + return cmd; + } + + private MHandler installSpyHandler(MCommand cmd) { + for(MHandler hdlr : app.getHandlers()) { + if(SPY_HANDLER_ID.equals(hdlr.getElementId())) { + System.err.println("CSS Spy handler already setup"); + return hdlr; + } + } + + MHandler hdlr = MCommandsFactory.INSTANCE.createHandler(); + hdlr.setElementId(SPY_HANDLER_ID); + hdlr.setContributionURI(SPY_HANDLER_URI); + hdlr.setCommand(cmd); + app.getHandlers().add(hdlr); + return hdlr; + } + + private void installSpyBinding(String bindingContextId, MCommand cmd, String keySeq) { + for(MBindingTable table : app.getBindingTables()) { + for(MKeyBinding binding : table.getBindings()) { + if(binding.getCommand() == cmd) { + System.err.println("Spy binding already installed"); + return; + } + } + } + + MBindingContext context = null; + for(MBindingContext ctxt : app.getBindingContexts()) { + if(ctxt.getElementId().equals(bindingContextId)) { + context = ctxt; + break; + } + } + if(context == null) { + System.err.println("Cannot find binding context: " + bindingContextId); + return; + } + + MBindingTable bindingTable = null; + String tableId = "bt." + cmd.getElementId(); + for(MBindingTable table : app.getBindingTables()) { + if(tableId.equals(table.getElementId())) { + bindingTable = table; + } + } + + if(bindingTable == null) { + bindingTable = MCommandsFactory.INSTANCE.createBindingTable(); + bindingTable.setElementId(tableId); + bindingTable.setBindingContext(context); + app.getBindingTables().add(bindingTable); + } + + MKeyBinding binding = MCommandsFactory.INSTANCE.createKeyBinding(); + binding.setCommand(cmd); + binding.setKeySequence(keySeq); + binding.setElementId("kb." + cmd.getElementId()); + bindingTable.getBindings().add(binding); + } + +} diff --git a/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/WidgetTreeProvider.java b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/WidgetTreeProvider.java new file mode 100644 index 00000000..605ffb81 --- /dev/null +++ b/bundles/org.eclipse.e4.tools.css.spy/src/org/eclipse/e4/tools/css/spy/WidgetTreeProvider.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2011 Manumitting Technologies, 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: + * Brian de Alwis (MT) - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.tools.css.spy; + +import java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Control; +import org.w3c.dom.NodeList; + +public class WidgetTreeProvider implements ITreeContentProvider { + private static final Object[] EMPTY_ARRAY = new Object[0]; + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof Object[]) { + return (Object[]) inputElement; + } else if (inputElement instanceof Collection< ? >) { + return ((Collection< ? >) inputElement).toArray(); + } + return getChildren(inputElement); + } + + + public Object[] getChildren(Object parentElement) { + CSSStylableElement element = CssSpyDialog.getCSSElement(parentElement); + if (element == null) { + return new Object[0]; + } + NodeList kids = element.getChildNodes(); + ArrayList<Object> children = new ArrayList<Object>(kids.getLength()); + for (int i = 0; i < kids.getLength(); i++) { + children.add(((CSSStylableElement) kids.item(i)).getNativeWidget()); + } + // if (parentElement instanceof Shell) { + // // ToolBar is part of the children + // //Collections.addAll(children, ((Shell) parentElement).getToolBar()); + // Collections.addAll(children, ((Shell) parentElement).getMenuBar()); + // } + // if (parentElement instanceof Menu) { + // Collections.addAll(children, ((Menu) parentElement).getItems()); + // } + // if (parentElement instanceof ToolBar) { + // Collections.addAll(children, ((ToolBar) parentElement).getItems()); + // } + // if (parentElement instanceof Composite) { + // Collections.addAll(children, ((Composite) parentElement).getChildren()); + // } + return children.toArray(); + } + + public Object getParent(Object element) { + // if(element instanceof Item) { + // return ((Item)element).get??? + // } + return ((Control) element).getParent(); + } + + public boolean hasChildren(Object element) { + return getChildren(element).length > 0; + } + + public void dispose() { + } + +} diff --git a/features/org.eclipse.e4.tools.css.spy.feature/.classpath b/features/org.eclipse.e4.tools.css.spy.feature/.classpath new file mode 100644 index 00000000..fb501163 --- /dev/null +++ b/features/org.eclipse.e4.tools.css.spy.feature/.classpath @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/features/org.eclipse.e4.tools.css.spy.feature/.project b/features/org.eclipse.e4.tools.css.spy.feature/.project new file mode 100644 index 00000000..5f79d73d --- /dev/null +++ b/features/org.eclipse.e4.tools.css.spy.feature/.project @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.e4.tools.css.spy.feature</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.FeatureBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.FeatureNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/features/org.eclipse.e4.tools.css.spy.feature/build.properties b/features/org.eclipse.e4.tools.css.spy.feature/build.properties new file mode 100644 index 00000000..3dca147b --- /dev/null +++ b/features/org.eclipse.e4.tools.css.spy.feature/build.properties @@ -0,0 +1,4 @@ +source.Eclipse.org/ = src/ +output.Eclipse.org/ = bin/ +bin.includes = feature.xml,\ + Eclipse.org/ diff --git a/features/org.eclipse.e4.tools.css.spy.feature/feature.xml b/features/org.eclipse.e4.tools.css.spy.feature/feature.xml new file mode 100644 index 00000000..62e8b9ab --- /dev/null +++ b/features/org.eclipse.e4.tools.css.spy.feature/feature.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feature + id="org.eclipse.e4.tools.css.spy.feature" + label="CSS Spy Feature" + version="0.0.0.qualifier"> + <install-handler library="Eclipse.org/"/> + + <description url="http://www.example.com/description"> + [Enter Feature Description here.] + </description> + + <copyright url="http://www.example.com/copyright"> + [Enter Copyright Description here.] + </copyright> + + <license url="http://www.example.com/license"> + [Enter License Description here.] + </license> + + <requires> + <import plugin="org.eclipse.e4.ui.css.core" version="0.9.0" match="greaterOrEqual"/> + <import plugin="org.eclipse.swt" version="3.6.0" match="greaterOrEqual"/> + <import plugin="org.eclipse.jface" version="3.8.0" match="greaterOrEqual"/> + <import plugin="org.w3c.css.sac"/> + </requires> + + <plugin + id="org.eclipse.e4.tools.css.spy" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + +</feature> |