Tutorial Graphical Editor Rich Client Platform Original Thamwo QuickCompressor Alle Beispiele sind mit eclipse version keppler erstellt und getestet worden. Die Lizensbestimmungen von eclipse werden durch dieses Buch nicht in Frage gestellt. Kopie und Verwendung von Quellcode und Text geschieht auf eigene Gefahr. Jede Gewährleistung wird ausgeschlossen. Fehler in den Quellcodes und in den Texten können nicht ausgeschlossen werden. Für die Richtigkeit besteht keinerlei Gewähr. Copyright© 2014, thamwo [2] Thamwo QuickCompressor Inhalt Einführung ............................................................................................................................................... 4 Eclipse .................................................................................................................................................. 4 Rich Client Platform (RCP) ................................................................................................................... 4 Quick Graphical Editor............................................................................................................................. 5 Beschreibung ....................................................................................................................................... 5 Package mygef..................................................................................................................................... 7 Package editor ..................................................................................................................................... 8 Package model .................................................................................................................................. 13 Package factory ................................................................................................................................. 18 Package picture ................................................................................................................................. 20 Package rectangle.............................................................................................................................. 22 Package commands ........................................................................................................................... 26 Package tree ...................................................................................................................................... 28 MANIFEST.MF .................................................................................................................................... 31 plugin.xml .......................................................................................................................................... 32 Ergänzung weiterer Elemente ........................................................................................................... 34 [3] Thamwo QuickCompressor Einführung Eclipse Eclipse ist ein Framework, welches für die verschiedensten Umgebungen eine speziell angepasste Umgebung liefert, kurz eine DIE. IDE – Integrated Development Environment Aber Eclipse ist keine IDE, sondern ein Framework, dh. ein Werkzeugkasten für verschieden Anwendungen. Eine davon ist das Integrated Development Environment (IDE). Eine weiter ist eine Umgebung zur Erstellung eigener Anwendungen, auch Application genannt. Diese Umgebung ist die RCP – Rich Client Platform Die Komponenten dieser Rich Client Platform sind durch das Framework vorgegeben und sind wie folgt definiert. Workbench – ist ein Fenster/Window mit verschiedenen Elementen Rich Client Platform (RCP) Die Rich Client Platform, kurz RCP, ist eine Bezeichnung für ein Produkt, welches auf Eclipse basiert. Applikationen können aus Eclipse als RCP exportiert werden. Damit wird eine auslieferbare, transportable Version erzeugt, welche dann zum Beispiel auf einem PC installiert werden kann. [4] Thamwo QuickCompressor Quick Graphical Editor Beschreibung In diesem Tutorial wird ein graphischer Editor unter GEF implementiert. Im Editorbereich können Rechtecke erzeugt und gelöscht werden. Im ContentOutline View wird ein Tree mit den Namen der Rechtecke angezeigt. Durch Doppelklick auf das Element im Tree oder im Editorbereich wird das Property Sheet geöffnet und der Name des Elements kann geändert werden. Alle Funktionen sind vollständig als Beispiel in den Packages abgelegt. So befinden sich unter mygef der Code der Perspektive. Im Package editor sind die Sourcen für die Editorfunktionen zu finden. Das Packages model beinhalten die Sourcen für Abstrakte, während unter factory die Beispiele für Elemente zusammengeführt werden. Schließlich folgen unter picture und rectangle die konkreten Beispiele für die Realisierung von Zeichenelementen. Zuletzt kommen Kommandos und der Code für den TreeViewer im ContentOutline View. Nun folgt eine kurze Anweisung für die Erstellung des Projektes: 1. Anlegen einen neuen leeren Projektes als RCP als mygef 2. Einfügen der folgenden Packages unter Dependencies (via Add Button Required Plug Ins): org.eclipse.gef org.eclipse.draw2d 3. Einfügen der folgenden Packages unter Dependencies (via Add Button Imported Packages): [5] Thamwo QuickCompressor org.eclipse.views.contentoutline org.eclipse.views.properties 4. Anlegen der folgenden Packages unter src: mygef (wurde beim Anlegen des Projektes automatisch erzeugt) editor model factory picture rectangle commands tree 5. Erzeugen der folgenden Packages mit den beinhalteten Klassen. Alle Sourcen sind vollständig und bilden zusammen das gesamte Projekt. Alternativ ist das Projekt als Zip-File abgelegt und kann so unter Downloads gefunden werden. [6] Thamwo QuickCompressor Package mygef Im package mygef befindet sich die RCP Applikation. Für den graphischen Editor muss lediglich eine Klasse geändert werden. Diese teilt der Applikation mit welche Views angelegt werden sollen und wie der EditorPart behandelt werden soll. Perspective.java package mygef; import org.eclipse.ui.IFolderLayout; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.IPerspectiveFactory; public class Perspective implements IPerspectiveFactory { private static final String ID_TABS_FOLDER = "folder"; public void createInitialLayout(IPageLayout layout) { String editorArea = layout.getEditorArea(); layout.setEditorAreaVisible(true); IFolderLayout tabs = layout.createFolder(ID_TABS_FOLDER, IPageLayout.LEFT, 0.3f, editorArea); /********************************************/ /* Outline und Property Sheet */ /********************************************/ tabs.addView(IPageLayout.ID_OUTLINE); tabs.addPlaceholder(IPageLayout.ID_PROP_SHEET); } } [7] Thamwo QuickCompressor Package editor Nun kommen die Grundlagen für den Editor. Diese bestehen aus drei Komponenten. Dem Input für die Definition des Editorinhalts. Dem eigentlichen Editor für die Definition der Editorfunktionen und der Definiton der ActionBar. MyEditorInput.java package editor; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IPersistableElement; public class MyEditorInput implements IEditorInput { public String name = null; public MyEditorInput(String name) { this.name = name; } @Override public boolean exists() { return (this.name != null); } public boolean equals(Object o) { if (!(o instanceof MyEditorInput)) return false; return ((MyEditorInput)o).getName().equals(getName()); } @Override public ImageDescriptor getImageDescriptor() { return ImageDescriptor.getMissingImageDescriptor(); } @Override public String getName() { return this.name; } @Override public IPersistableElement getPersistable() { // TODO Auto-generated method stub return null; } @Override public String getToolTipText() { return this.name; } @Override public Object getAdapter(Class adapter) { // TODO Auto-generated method stub return null; } } [8] Thamwo QuickCompressor Der eigentliche Editor startet einen Graphical Viewer zur Darstellung der graphischen Elemente. Optional wurde die Funktion ZOOM und SELECTION unter configureGraphicalViewer hinzugefügt. Ferner wurde, wie in der Perspektive vorbereitet, der View ContentOutline definiert. Diese besteht aus einem Treeviewer und einer Miniatur. In getPaletteRoot wird die Palette definiert, die die Werkzeuge für die graphische Bearbeitung anbietet. MyGraphicalEditor.java package editor; import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import import java.util.ArrayList; org.eclipse.core.runtime.IProgressMonitor; org.eclipse.draw2d.LightweightSystem; org.eclipse.draw2d.Viewport; org.eclipse.draw2d.parts.ScrollableThumbnail; org.eclipse.gef.DefaultEditDomain; org.eclipse.gef.GraphicalViewer; org.eclipse.gef.LayerConstants; org.eclipse.gef.editparts.ScalableRootEditPart; org.eclipse.gef.editparts.ZoomManager; org.eclipse.gef.palette.CreationToolEntry; org.eclipse.gef.palette.MarqueeToolEntry; org.eclipse.gef.palette.PaletteGroup; org.eclipse.gef.palette.PaletteRoot; org.eclipse.gef.palette.PaletteSeparator; org.eclipse.gef.palette.SelectionToolEntry; org.eclipse.gef.ui.actions.ZoomInAction; org.eclipse.gef.ui.actions.ZoomOutAction; org.eclipse.gef.ui.parts.ContentOutlinePage; org.eclipse.gef.ui.parts.GraphicalEditorWithPalette; org.eclipse.gef.ui.parts.TreeViewer; org.eclipse.jface.viewers.ISelectionChangedListener; org.eclipse.jface.viewers.SelectionChangedEvent; org.eclipse.swt.SWT; org.eclipse.swt.custom.SashForm; org.eclipse.swt.events.DisposeEvent; org.eclipse.swt.events.DisposeListener; org.eclipse.swt.graphics.GC; org.eclipse.swt.graphics.Image; org.eclipse.swt.graphics.ImageData; org.eclipse.swt.graphics.ImageLoader; org.eclipse.swt.widgets.Canvas; org.eclipse.swt.widgets.Composite; org.eclipse.swt.widgets.Control; org.eclipse.ui.IEditorPart; org.eclipse.ui.views.contentoutline.IContentOutlinePage; factory.MyEditPartFactory; factory.ElementCreationFactory; picture.Picture; rectangle.RectElement; tree.TreeEditPartFactory; public class MyGraphicalEditor extends GraphicalEditorWithPalette { public static final String ID = "grapheditid"; private Picture picFrame; private boolean dirty; public MyGraphicalEditor() { setEditDomain(new DefaultEditDomain(this)); dirty = true; } [9] Thamwo QuickCompressor protected void configureGraphicalViewer() { double[] zoomLevels; ArrayList<String> zoomContributions; super.configureGraphicalViewer(); GraphicalViewer viewer = getGraphicalViewer(); viewer.setEditPartFactory(new MyEditPartFactory()); /********************************************/ /* ZOOM */ /********************************************/ ScalableRootEditPart rootEditPart = new ScalableRootEditPart(); viewer.setRootEditPart(rootEditPart); ZoomManager manager = rootEditPart.getZoomManager(); getActionRegistry().registerAction(new ZoomInAction(manager)); getActionRegistry().registerAction(new ZoomOutAction(manager)); zoomLevels = new double[] {0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 10.0, 20.0}; manager.setZoomLevels(zoomLevels); zoomContributions = new ArrayList<String>(); zoomContributions.add(ZoomManager.FIT_ALL); zoomContributions.add(ZoomManager.FIT_HEIGHT); zoomContributions.add(ZoomManager.FIT_WIDTH); manager.setZoomLevelContributions(zoomContributions); /********************************************/ /* Selection */ /********************************************/ viewer.addSelectionChangedListener(new ISelectionChangedListener () { @Override public void selectionChanged(SelectionChangedEvent event) { System.out.println ("Selection"); } }); } @Override public void doSave(IProgressMonitor monitor) { GraphicalViewer viewer = getGraphicalViewer(); GC gc = new GC(viewer.getControl()); Image image = new Image(viewer.getControl().getDisplay(), viewer .getControl().getSize().x, viewer.getControl().getSize().y); gc.copyArea(image, 0, 0); ImageLoader loader = new ImageLoader(); loader.data = new ImageData[] { image.getImageData() }; loader.save("d:/user/pic.bmp", SWT.IMAGE_BMP); gc.dispose(); System.out.println("Save"); dirty = false; firePropertyChange(IEditorPart.PROP_DIRTY); } /****************************************************/ /* org.eclipse.gef.ui.parts.ContentOutlinePage */ /****************************************************/ public class OverviewContentPage extends ContentOutlinePage { private private private private SashForm sash; ScrollableThumbnail thumbnail; DisposeListener disposeListener; GraphicalViewer viewer; public OverviewContentPage() { super (new TreeViewer ()); } @Override public void createControl(Composite parent) { sash = new SashForm(parent, SWT.VERTICAL); [10] Thamwo QuickCompressor /********************************************/ /* org.eclipse.gef.ui.parts.TreeViewer */ /********************************************/ getViewer().createControl(sash); getViewer().setEditDomain(getEditDomain()); getViewer().setEditPartFactory(new TreeEditPartFactory()); getViewer().setContents(picFrame); getSelectionSynchronizer().addViewer(getViewer()); /********************************************/ /* Miniatur */ /********************************************/ Canvas canvas = new Canvas(sash, SWT.BORDER); LightweightSystem lws = new LightweightSystem(canvas); thumbnail = new ScrollableThumbnail((Viewport) ((ScalableRootEditPart) getGraphicalViewer().getRootEditPart()).getFigure()); thumbnail.setSource(((ScalableRootEditPart) getGraphicalViewer().getRootEditPart()).getLayer(LayerConstant s.PRINTABLE_LAYERS)); lws.setContents(thumbnail); disposeListener = new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (thumbnail != null) { thumbnail.deactivate(); thumbnail = null; } } }; getGraphicalViewer().getControl().addDisposeListener(disposeListener); } @Override public void dispose() { if(viewer != null) { if (viewer.getControl() != null && !viewer.getControl().isDisposed()) viewer.getControl().removeDisposeListener(disposeListener); super.dispose(); } } @Override public Control getControl() { return sash; } } @Override protected PaletteRoot getPaletteRoot() { PaletteRoot root = new PaletteRoot(); PaletteGroup manipGroup = new PaletteGroup("Work"); root.add(manipGroup); SelectionToolEntry selectionToolEntry = new SelectionToolEntry(); manipGroup.add(selectionToolEntry); manipGroup.add(new MarqueeToolEntry()); PaletteSeparator sep2 = new PaletteSeparator(); root.add(sep2); PaletteGroup instGroup = new PaletteGroup("Create new elements"); root.add(instGroup); instGroup.add(new CreationToolEntry("Rectangle", "Create new rectangle", new ElementCreationFactory(RectElement.class), null, null)); root.setDefaultEntry(selectionToolEntry); return root; } [11] Thamwo QuickCompressor public void setDirty () { dirty = true; firePropertyChange(IEditorPart.PROP_DIRTY); } @Override public boolean isDirty () { return dirty; } public Picture CreatePicture(){ picFrame = new Picture(); picFrame.setName("Picture"); return picFrame; } protected void initializeGraphicalViewer() { GraphicalViewer viewer = getGraphicalViewer(); viewer.setContents(CreatePicture()); } @Override public Object getAdapter(Class type) { if(type == IContentOutlinePage.class) return new OverviewContentPage(/*getGraphicalViewer()*/); if (type == ZoomManager.class) return ((ScalableRootEditPart) getGraphicalViewer().getRootEditPart()).getZoomManager(); return super.getAdapter(type); } } Korrespondierend zum Editor warden die Controls in der ActionBar eingerichtet. MyGraphicalEditorActionBarContributor.java package editor; import import import import import import import import import org.eclipse.gef.ui.actions.ActionBarContributor; org.eclipse.gef.ui.actions.DeleteRetargetAction; org.eclipse.gef.ui.actions.GEFActionConstants; org.eclipse.gef.ui.actions.ZoomComboContributionItem; org.eclipse.gef.ui.actions.ZoomInRetargetAction; org.eclipse.gef.ui.actions.ZoomOutRetargetAction; org.eclipse.jface.action.IToolBarManager; org.eclipse.jface.action.Separator; org.eclipse.ui.actions.ActionFactory; public class MyGraphicalEditorActionBarContributor extends ActionBarContributor { @Override protected void buildActions() { addRetargetAction(new ZoomInRetargetAction()); addRetargetAction(new ZoomOutRetargetAction()); addRetargetAction(new DeleteRetargetAction()); } public void contributeToToolBar(IToolBarManager toolBarManager) { toolBarManager.add(new Separator()); toolBarManager.add(getAction(GEFActionConstants.ZOOM_IN)); toolBarManager.add(getAction(GEFActionConstants.ZOOM_OUT)); toolBarManager.add(new ZoomComboContributionItem(getPage())); toolBarManager.add(getAction(ActionFactory.DELETE.getId())); } @Override [12] Thamwo QuickCompressor protected void declareGlobalActionKeys() {} } Package model Als nächstes muss das Modell festgelegt werden. Dieses besteht aus einem Root Element und dessen rekursiven Children. Dafür wird zunächst ein abstraktes Element definiert. Dazu muss noch das abstrakte Layout und der abstrakte EditPart definiert werden. Optional kann ein Property Sheet ergänzt werden, wie es schon in der Perspektive vorgeleistet wurde. Das eigentliche Root Element wird erst später unter Picture festgelegt. Es ist eine einfache Instanz des abstrakten Elements. AbstractElement.java package model; import import import import import import import import import import import java.beans.PropertyChangeListener; java.beans.PropertyChangeSupport; java.util.ArrayList; java.util.List; org.eclipse.core.runtime.IAdaptable; org.eclipse.draw2d.geometry.Rectangle; org.eclipse.ui.IEditorPart; org.eclipse.ui.PlatformUI; org.eclipse.ui.views.properties.*; rectangle.RectElement; editor.MyGraphicalEditor; public class AbstractElement implements IAdaptable { private String name; private Rectangle layout; private List<AbstractElement> children; private AbstractElement parent; private PropertyChangeSupport listeners; public static final String PROPERTY_LAYOUT = "NodeLayout"; public static final String PROPERTY_NAME = "NODE"; public static final String PROPERTY_ADD = "NodeAddChild"; public static final String PROPERTY_REMOVE = "NodeRemoveChild"; private IPropertySource propertySource = null; public AbstractElement(){ this.name = "Unknown"; this.layout = new Rectangle(10, 10, 100, 100); this.children = new ArrayList<AbstractElement>(); [13] Thamwo QuickCompressor this.parent = null; this.listeners = new PropertyChangeSupport(this); } public PropertyChangeSupport getListeners() { return listeners; } public void addPropertyChangeListener(PropertyChangeListener listener) { listeners.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { listeners.removePropertyChangeListener(listener); } public void setName(String name) { String oldName = null; this.name = name; getListeners().firePropertyChange(PROPERTY_NAME, oldName, name); } public String getName() { return this.name; } public void setLayout(Rectangle newLayout) { Rectangle oldLayout = this.layout; this.layout = newLayout; getListeners().firePropertyChange(PROPERTY_LAYOUT, oldLayout, newLayout); IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow(). getActivePage().getActiveEditor(); ((MyGraphicalEditor) editor).setDirty (); } public Rectangle getLayout() { return this.layout; } public boolean addChild(AbstractElement child) { boolean b = this.children.add(child); if (b) { child.setParent(this); getListeners().firePropertyChange(PROPERTY_ADD, null, child); } return b; } public boolean removeChild(AbstractElement child) { boolean b = this.children.remove(child); if (b) getListeners().firePropertyChange(PROPERTY_REMOVE, child, null); IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow(). getActivePage().getActiveEditor(); ((MyGraphicalEditor) editor).setDirty (); return b; } public List<AbstractElement> getChildrenArray() { return this.children; } public void setParent(AbstractElement parent) { this.parent = parent; } public AbstractElement getParent() { return this.parent; } public boolean contains(RectElement child) { return children.contains(child); } /********************************************/ [14] Thamwo QuickCompressor /* Property Sheet */ /********************************************/ @Override public Object getAdapter(Class adapter) { if (adapter == IPropertySource.class) { if (propertySource == null) propertySource = new AbstractElementPropertySource(this); return propertySource; } return null; } } AbstractLayoutCommand.java package model; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.commands.Command; public abstract class AbstractLayoutCommand extends Command public abstract void setConstraint(Rectangle rect); public abstract void setModel(Object model); } { In AbstractEditPart wird das Property Sheet behandelt. Beim Doppelklick wird der entsprechende View geöffnet. Außerdem werden die Listerner gestartet, damit Änderungen jeweils an die anderen Bereiche mitgeteilt. AbstractEditPart.java package model; import import import import import import import import java.beans.PropertyChangeListener; org.eclipse.gef.Request; org.eclipse.gef.RequestConstants; org.eclipse.gef.editparts.AbstractGraphicalEditPart; org.eclipse.ui.IPageLayout; org.eclipse.ui.IWorkbenchPage; org.eclipse.ui.PartInitException; org.eclipse.ui.PlatformUI; public abstract class AbstractEditPart extends AbstractGraphicalEditPart implements PropertyChangeListener { /********************************************/ /* Listener for Property Sheet */ /********************************************/ public void activate() { super.activate(); ((AbstractElement) getModel()).addPropertyChangeListener(this); } public void deactivate() { super.deactivate(); ((AbstractElement) getModel()).removePropertyChangeListener(this); } /********************************************/ /* Open Property Sheet (douple click) */ /********************************************/ @Override public void performRequest(Request req) { if (req.getType().equals(RequestConstants.REQ_OPEN)) { try { [15] Thamwo QuickCompressor IWorkbenchPage page = PlatformUI.getWorkbench(). getActiveWorkbenchWindow().getActivePage(); page.showView(IPageLayout.ID_PROP_SHEET); } catch (PartInitException e) { e.printStackTrace(); } } } } Nun wird der Inhalt des Property Sheets festgelegt. Es wird bestimmt, welche Properties angezeigt werden und welche editierbar sind. AbstractElementPropertySource.java package model; import import import import import import import java.util.ArrayList; org.eclipse.ui.views.properties.IPropertyDescriptor; org.eclipse.ui.views.properties.IPropertySource; org.eclipse.ui.views.properties.PropertyDescriptor; org.eclipse.ui.views.properties.TextPropertyDescriptor; picture.Picture; rectangle.RectElement; public class AbstractElementPropertySource implements IPropertySource { private AbstractElement element; public AbstractElementPropertySource(AbstractElement node) { this.element = node; } @Override public Object getEditableValue() { return null; } @Override public IPropertyDescriptor[] getPropertyDescriptors() { ArrayList<IPropertyDescriptor> properties = new ArrayList<IPropertyDescriptor>(); if (element instanceof Picture) properties.add(new PropertyDescriptor(RectElement.PROPERTY_NAME, "Name")); if (element instanceof RectElement) { // editable TextPropertyDescriptor properties.add(new TextPropertyDescriptor(RectElement.PROPERTY_NAME, "Name")); // not editable PropertyDescriptor properties.add(new PropertyDescriptor(RectElement.PROPERTY_ATTR, "Attribut")); } return properties.toArray(new IPropertyDescriptor[0]); } @Override public Object getPropertyValue(Object id) { if (id.equals(RectElement.PROPERTY_NAME)) return element.getName(); if (id.equals(RectElement.PROPERTY_ATTR)) return ((RectElement) element).getAttr(); return null; } @Override public boolean isPropertySet(Object id) { return false; } @Override [16] Thamwo QuickCompressor public void resetPropertyValue(Object id) { } @Override public void setPropertyValue(Object id, Object value) { if (id.equals(RectElement.PROPERTY_NAME)) element.setName((String)value); if (id.equals(RectElement.PROPERTY_ATTR)) ((RectElement) element) .setAttr((String)value); } } [17] Thamwo QuickCompressor Package factory Nachdem das abstrakte Element, Layout und EditPart definiert wurden müssen alle konkreten Variationen in Factories behandelt werden. Dafür gibt es eine LayoutPolicy, eine ElementCreationFactory und eine MyEditPartFactory. LayoutPolicy.java package factory; import import import import import import import import import import import model.AbstractLayoutCommand; org.eclipse.draw2d.geometry.Rectangle; org.eclipse.gef.EditPart; org.eclipse.gef.commands.Command; org.eclipse.gef.editpolicies.XYLayoutEditPolicy; org.eclipse.gef.requests.CreateRequest; picture.PicturePart; rectangle.RectMoveCommand; rectangle.RectCreateCommand; rectangle.RectFigure; rectangle.RectPart; public class LayoutPolicy extends XYLayoutEditPolicy { /********************************************/ /* Move Element/Rectangel */ /********************************************/ @Override protected Command createChangeConstraintCommand(EditPart child, Object constraint) { AbstractLayoutCommand command = null; if (child instanceof RectPart) { command = new RectMoveCommand(); } command.setModel(child.getModel()); command.setConstraint((Rectangle)constraint); return command; } /********************************************/ /* Create Element */ /********************************************/ @Override protected Command getCreateCommand(CreateRequest request) { if (request.getType() == REQ_CREATE && getHost() instanceof PicturePart) { /* Create Rectangle */ RectCreateCommand cmd = new RectCreateCommand(); cmd.setFrame(getHost().getModel()); cmd.setNode(request.getNewObject()); Rectangle constraint = (Rectangle)getConstraintFor(request); constraint.x = (constraint.x < 0) ? 0 : constraint.x; constraint.y = (constraint.y < 0) ? 0 : constraint.y; constraint.width = (constraint.width <= 0) ? RectFigure.SERVICE_FIGURE_DEFWIDTH : constraint.width; constraint.height = (constraint.height <= 0) ? RectFigure.SERVICE_FIGURE_DEFHEIGHT : constraint.height; cmd.setLayout(constraint); return cmd; } return null; } } [18] Thamwo QuickCompressor ElementCreationFactory.java package factory; import org.eclipse.gef.requests.CreationFactory; import rectangle.RectElement; public class ElementCreationFactory implements CreationFactory { private Class<?> template; public ElementCreationFactory(Class<?> t) { this.template = t; } @Override public Object getNewObject() { if (template == null) return null; if (template == RectElement.class) { RectElement element = new RectElement(); element.setName("Element"); System.out.println("New Child"); return element; } return null; } @Override public Object getObjectType() { return template; } } MyEditPartFactory.java package factory; import import import import import import import org.eclipse.gef.EditPart; org.eclipse.gef.EditPartFactory; org.eclipse.gef.editparts.AbstractGraphicalEditPart; picture.Picture; picture.PicturePart; rectangle.RectElement; rectangle.RectPart; public class MyEditPartFactory implements EditPartFactory { @Override public EditPart createEditPart(EditPart context, Object model) { AbstractGraphicalEditPart part = null; if (model instanceof Picture) { part = new PicturePart(); } else if (model instanceof RectElement) { part = new RectPart(); } part.setModel(model); return part; [19] Thamwo QuickCompressor } } Package picture Nun kommt das Root Element. Hier heißt es einfach Picture. Picture.java package picture; import model.AbstractElement; public class Picture extends AbstractElement {} PictureFigure.java package picture; import import import import import import org.eclipse.draw2d.ColorConstants; org.eclipse.draw2d.Figure; org.eclipse.draw2d.Label; org.eclipse.draw2d.LineBorder; org.eclipse.draw2d.XYLayout; org.eclipse.draw2d.geometry.Rectangle; public class PictureFigure extends Figure { private Label labelName = new Label(); private XYLayout layout; public PictureFigure() { layout = new XYLayout(); setLayoutManager(layout); labelName.setForegroundColor(ColorConstants.blue); add(labelName); setConstraint(labelName, new Rectangle(5, 5, -1, -1)); setForegroundColor(ColorConstants.black); setBorder(new LineBorder(5)); } public void setLayout(Rectangle rect) { setBounds(rect); } public void setName(String text) { labelName.setText(text); } } PicturePart.java package picture; import import import import import import import import java.beans.PropertyChangeEvent; java.util.List; model.AbstractElement; model.AbstractEditPart; org.eclipse.draw2d.IFigure; org.eclipse.gef.EditPolicy; rectangle.RectElement; factory.LayoutPolicy; [20] Thamwo QuickCompressor public class PicturePart extends AbstractEditPart { @Override protected IFigure createFigure() { IFigure figure = new PictureFigure(); return figure; } @Override protected void createEditPolicies() { installEditPolicy(EditPolicy.LAYOUT_ROLE, new LayoutPolicy()); } protected void refreshVisuals(){ PictureFigure figure = (PictureFigure)getFigure(); Picture model = (Picture)getModel(); figure.setName(model.getName()); } public List<AbstractElement> getModelChildren() { return ((Picture)getModel()).getChildrenArray(); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(RectElement.PROPERTY_LAYOUT)) refreshVisuals(); if (evt.getPropertyName().equals(RectElement.PROPERTY_ADD)) refreshChildren(); if (evt.getPropertyName().equals(RectElement.PROPERTY_REMOVE)) refreshChildren(); if (evt.getPropertyName().equals(RectElement.PROPERTY_NAME)) refreshChildren(); } } [21] Thamwo QuickCompressor Package rectangle Ein weiteres konkretes Element ist das Rechteck. Analog zu Picture gibt es eine Definition, ein Layout und einen EditPart. Ergänzend kommen zwei Kommandos hinzu, um ein Rechteck hinzuzufügen und zu bewegen. RectElement.java package rectangle; import model.AbstractElement; public class RectElement extends AbstractElement { public static final String PROPERTY_ATTR = "ATTRIBUT"; private String attribut; public RectElement(){ this.attribut = "Unknown"; } public String getAttr() { return this.attribut; } public void setAttr(String attribut) { this.attribut = attribut; } } RectFigure.java package rectangle; import import import import import import import import org.eclipse.draw2d.ColorConstants; org.eclipse.draw2d.Figure; org.eclipse.draw2d.Label; org.eclipse.draw2d.LineBorder; org.eclipse.draw2d.ToolbarLayout; org.eclipse.draw2d.XYLayout; org.eclipse.draw2d.geometry.Rectangle; org.eclipse.swt.graphics.Color; public class RectFigure extends Figure { private Label labelName = new Label(); public static final int SERVICE_FIGURE_DEFWIDTH = 250; public static final int SERVICE_FIGURE_DEFHEIGHT = 150; public RectFigure() { XYLayout layout = new XYLayout(); setLayoutManager(layout); labelName.setForegroundColor(ColorConstants.darkGray); add(labelName, ToolbarLayout.ALIGN_CENTER); setConstraint(labelName, new Rectangle(5, 17, -1, -1)); /** Random color **/ setForegroundColor(new Color(null, (new Double(Math.random() * 128)).intValue() ,(new Double(Math.random() * 128)).intValue(), (new Double(Math.random() * 128)).intValue())); setBackgroundColor(new Color(null, (new Double(Math.random() * 128)).intValue() + 128 , (new Double(Math.random() * 128)) .intValue() + 128 , (new Double(Math.random() * 128)) .intValue() + 128 )); setBorder(new LineBorder(1)); setOpaque(true); [22] Thamwo QuickCompressor } public void setName(String text) { labelName.setText(text); } public void setLayout(Rectangle rect) { getParent().setConstraint(this, rect); } } RectPart.java package rectangle; import import import import import import import import import import import java.beans.PropertyChangeEvent; java.util.List; model.AbstractElement; model.AbstractEditPart; org.eclipse.draw2d.IFigure; org.eclipse.gef.EditPolicy; org.eclipse.ui.IEditorPart; org.eclipse.ui.PlatformUI; commands.DeletePolicy; editor.MyGraphicalEditor; factory.LayoutPolicy; public class RectPart extends AbstractEditPart { @Override protected IFigure createFigure() { IFigure figure = new RectFigure(); IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow() .getActivePage().getActiveEditor(); ((MyGraphicalEditor) editor).setDirty (); return figure; } @Override protected void createEditPolicies() { installEditPolicy(EditPolicy.LAYOUT_ROLE, new LayoutPolicy()); installEditPolicy(EditPolicy.COMPONENT_ROLE,new DeletePolicy()); } protected void refreshVisuals(){ RectFigure figure = (RectFigure)getFigure(); RectElement model = (RectElement)getModel(); figure.setName(model.getName()); figure.setLayout(model.getLayout()); } public List<AbstractElement> getModelChildren() { return ((RectElement)getModel()).getChildrenArray(); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(RectElement.PROPERTY_LAYOUT)) refreshVisuals(); if (evt.getPropertyName().equals(AbstractElement.PROPERTY_NAME)) refreshVisuals(); } } RectCreateCommand.java [23] Thamwo QuickCompressor package rectangle; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.commands.Command; import picture.Picture; public class RectCreateCommand extends Command { private Picture fr; private RectElement nd; public RectCreateCommand() { super(); fr = null; nd = null; } public void setNode(Object s) { if (s instanceof RectElement){ this.nd = (RectElement)s; } } public void setFrame(Object e) { if (e instanceof Picture){ this.fr = (Picture)e; } } public void setLayout(Rectangle r) { if (nd == null) return; nd.setLayout(r); } @Override public boolean canExecute() { if (nd == null || fr == null) return false; return true; } @Override public void execute() { fr.addChild(nd); } @Override public boolean canUndo() { if (fr == null || nd == null) return false; return fr.contains(nd); } @Override public void undo() { fr.removeChild(nd); } } RectMoveCommand.java package rectangle; import model.AbstractLayoutCommand; import org.eclipse.draw2d.geometry.Rectangle; /********************************************/ /* Move Rectangle */ /********************************************/ [24] Thamwo QuickCompressor public class RectMoveCommand extends AbstractLayoutCommand { private RectElement model; private Rectangle layout; public void execute() { model.setLayout(layout); } public void setConstraint(Rectangle rect) { this.layout = rect; } public void setModel(Object model) { this.model = (RectElement)model; } } [25] Thamwo QuickCompressor Package commands Damit ist der graphische Editor fertig. Rechtecke können zum Bild hinzugefügt und wieder gelöscht werden. Der Name des Rechtecks kann im Property Sheet geändert werden. Die folgenden Packages ergänzen den Editor, sind aber nicht erforderlich. newHandler.java package commands; import import import import import import import org.eclipse.core.commands.ExecutionEvent; org.eclipse.core.commands.ExecutionException; org.eclipse.core.commands.AbstractHandler; org.eclipse.ui.IWorkbenchPage; org.eclipse.ui.PlatformUI; editor.MyEditorInput; editor.MyGraphicalEditor; public class newHandler extends AbstractHandler { public newHandler () {} public Object execute(ExecutionEvent event) throws ExecutionException { try { IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); page.openEditor(new MyEditorInput("Mygef"), MyGraphicalEditor.ID, false); } catch (Exception e) { e.printStackTrace(); } return null; } } DeleteCommand.java package commands; import model.AbstractElement; import org.eclipse.gef.commands.Command; public class DeleteCommand extends Command { private AbstractElement model; private AbstractElement parentModel; public void execute() { this.parentModel.removeChild(model); } public void setModel(Object model) { this.model = (AbstractElement)model; } public void setParentModel(Object model) { parentModel = (AbstractElement)model; } public void undo() { this.parentModel.addChild(model); } } [26] Thamwo QuickCompressor DeletePolicy.java package commands; import org.eclipse.gef.commands.Command; import org.eclipse.gef.editpolicies.ComponentEditPolicy; import org.eclipse.gef.requests.GroupRequest; public class DeletePolicy extends ComponentEditPolicy { protected Command createDeleteCommand(GroupRequest deleteRequest) { DeleteCommand command = new DeleteCommand(); command.setModel(getHost().getModel()); command.setParentModel(getHost().getParent().getModel()); return command; } } [27] Thamwo QuickCompressor Package tree Durch die folgenden vier Klassen wird im ContentOutline View ein Tree angezeigt, in dem alle Children des Root Elements Picture angezeigt werden. Durch Doppelklick auf ein Element wird das entsprechende Rechteck im Editorbereich ausgewählt und das dazugehörige Property Sheet geöffnet. MyAbstractTreeEditPart.java package tree; import import import import import import import import import import import java.beans.PropertyChangeListener; model.AbstractElement; org.eclipse.gef.DragTracker; org.eclipse.gef.Request; org.eclipse.gef.RequestConstants; org.eclipse.gef.editparts.AbstractTreeEditPart; org.eclipse.gef.tools.SelectEditPartTracker; org.eclipse.ui.IPageLayout; org.eclipse.ui.IWorkbenchPage; org.eclipse.ui.PartInitException; org.eclipse.ui.PlatformUI; public abstract class MyAbstractTreeEditPart extends AbstractTreeEditPart implements PropertyChangeListener { /********************************************/ /* Listener for Property Sheet */ /********************************************/ public void activate() { super.activate(); ((AbstractElement) getModel()).addPropertyChangeListener(this); } public void deactivate() { ((AbstractElement) getModel()).removePropertyChangeListener(this); super.deactivate(); } @Override public DragTracker getDragTracker(Request req) { return new SelectEditPartTracker(this); } /********************************************/ /* Open Property Sheet (douple click) */ /********************************************/ @Override public void performRequest(Request req) { if (req.getType().equals(RequestConstants.REQ_OPEN)) { try { IWorkbenchPage page = PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getActivePage(); page.showView(IPageLayout.ID_PROP_SHEET); } catch (PartInitException e) { e.printStackTrace(); } } } } TreeEditPartFactory.java package tree; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartFactory; [28] Thamwo QuickCompressor import rectangle.RectElement; import picture.Picture; public class TreeEditPartFactory implements EditPartFactory { @Override public EditPart createEditPart(EditPart context, Object model) { EditPart part = null; if (model instanceof Picture) part = new PictureTreeEditPart(); else if (model instanceof RectElement) part = new RectTreeEditPart(); if (part != null) part.setModel(model); return part; } } PictureTreeEditPart.java package tree; import import import import java.beans.PropertyChangeEvent; model.AbstractElement; picture.Picture; java.util.List; public class PictureTreeEditPart extends MyAbstractTreeEditPart { protected List<AbstractElement> getModelChildren() { return ((Picture)getModel()).getChildrenArray(); } @Override public void propertyChange(PropertyChangeEvent evt) { if(evt.getPropertyName().equals(AbstractElement.PROPERTY_ADD)) refreshChildren(); if(evt.getPropertyName().equals(AbstractElement.PROPERTY_REMOVE)) refreshChildren(); if(evt.getPropertyName().equals(AbstractElement.PROPERTY_NAME)) refreshChildren(); } } RectTreeEditPart.java package tree; import import import import import import import import java.beans.PropertyChangeEvent; model.AbstractElement; rectangle.RectElement; java.util.List; org.eclipse.gef.EditPolicy; org.eclipse.ui.ISharedImages; org.eclipse.ui.PlatformUI; commands.DeletePolicy; public class RectTreeEditPart extends MyAbstractTreeEditPart { protected List<AbstractElement> getModelChildren() { return ((RectElement)getModel()).getChildrenArray(); } @Override protected void createEditPolicies() { [29] Thamwo QuickCompressor installEditPolicy(EditPolicy.COMPONENT_ROLE,new DeletePolicy()); } public void refreshVisuals(){ RectElement model = (RectElement)getModel(); setWidgetText(model.getName()); System.out.println(model.getName()); setWidgetImage(PlatformUI.getWorkbench().getSharedImages() .getImage(ISharedImages.IMG_DEF_VIEW)); } @Override public void propertyChange(PropertyChangeEvent evt) { if(evt.getPropertyName().equals(AbstractElement.PROPERTY_ADD)) refreshChildren(); if(evt.getPropertyName().equals(AbstractElement.PROPERTY_REMOVE)) refreshChildren(); if(evt.getPropertyName().equals(AbstractElement.PROPERTY_NAME)) refreshVisuals(); } } [30] Thamwo QuickCompressor MANIFEST.MF Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Mygef Bundle-SymbolicName: mygef;singleton:=true Bundle-Version: 1.0.0.qualifier Bundle-Activator: mygef.Activator Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.gef;bundle-version="3.9.0", org.eclipse.draw2d Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy Import-Package: org.eclipse.ui.views.contentoutline, org.eclipse.ui.views.properties [31] Thamwo QuickCompressor plugin.xml <?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.4"?> <plugin> <extension id="application" point="org.eclipse.core.runtime.applications"> <application> <run class="mygef.Application"> </run> </application> </extension> <extension point="org.eclipse.ui.perspectives"> <perspective name="RCP Perspective" class="mygef.Perspective" id="mygef.perspective"> </perspective> </extension> <extension point="org.eclipse.ui.editors"> <editor class="editor.MyGraphicalEditor" contributorClass="editor.MyGraphicalEditorActionBarContributor" default="true" id="grapheditid" name="mygraphedit"> </editor> </extension> <extension point="org.eclipse.ui.commands"> <command id="newid" name="newfile"> </command> <command id="saveid" name="savefile"> </command> </extension> <extension point="org.eclipse.ui.handlers"> <handler class="commands.newHandler" commandId="newid"> </handler> </extension> <extension point="org.eclipse.ui.menus"> <menuContribution allPopups="false" locationURI="menu:org.eclipse.ui.main.menu"> <menu commandId="newid" id="menuid" label="File"> <command commandId="newid" id="ncomid" label="New" style="push"> </command> <command commandId="org.eclipse.ui.file.save" id="scomid" label="Save" style="push"> </command> [32] Thamwo QuickCompressor </menu> </menuContribution> <menuContribution allPopups="false" locationURI="toolbar:org.eclipse.ui.main.toolbar?after=additions"> <toolbar id="mygef.toolbar" label="op"> <command commandId="newid" icon="icons/alt_window_16.gif" id="New" style="push"> </command> <command commandId="org.eclipse.ui.file.save" label="Save" style="push"> </command> </toolbar> </menuContribution> </extension> </plugin> [33] Thamwo QuickCompressor Ergänzung weiterer Elemente 1. Anlegen eines Packages, z.B. triangle 2. Erzeugen von 5 Klassen analog wie unter rectangle 3. Ergänzen von triangle in allen Klassen unter factory Uns schon ist ein weiteres Element verfügbar. [34] Thamwo QuickCompressor Von der leeren RCP mit View zum Jpeg Compressor Dieses Turorial zeigt auf, wie mit Eclipse eine kleine Anwendung erstellt werden kann. Die Rich Client Platform wird als Aufsetzpunkt gewählt, um einen simplen graphischen Editor zu erstellen. Grundlage ist hier GEF. Somit ist diese Tutorial auch ein ausgezeichneter Einstieg in die Programmierung mit dieser Umgebung. [35]