/******************************************************************************* * Copyright (c) 2007- VTT Technical Research Centre of Finland. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.proconf.g3d.shapeeditor.tools; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.vecmath.AxisAngle4d; import javax.vecmath.Quat4d; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CCombo; 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.layout.FillLayout; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; 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.Slider; import org.eclipse.swt.widgets.Text; import org.simantics.db.ContextGraph; import org.simantics.db.Graph; import org.simantics.db.GraphRequestAdapter; import org.simantics.db.GraphRequestStatus; import org.simantics.db.GraphRequestWithResult; import org.simantics.db.Resource; import org.simantics.animation.stubs.Animation; import org.simantics.animation.stubs.Interpolator; import org.simantics.animation.stubs.ScalarInterpolator; import org.simantics.animation.stubs.SlerpInterpolator; import org.simantics.layer0.utils.EntityFactory; import org.simantics.layer0.utils.IEntity; import org.simantics.layer0.utils.Property; import org.simantics.animation.curve.SlerpCurve; import org.simantics.animation.curve.TCBCurve; import org.simantics.proconf.g3d.actions.ContextAction; import org.simantics.proconf.g3d.actions.RotateAction; import org.simantics.proconf.g3d.actions.TranslateAction; import org.simantics.proconf.g3d.animation.Animatable; import org.simantics.proconf.g3d.base.EditorContribution; import org.simantics.proconf.g3d.base.G3DTools; import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase; import org.simantics.proconf.g3d.base.VisualizationScheduler; import org.simantics.proconf.g3d.common.StructuredResourceSelection; import org.simantics.proconf.g3d.scenegraph.IGraphicsNode; import org.simantics.proconf.g3d.scenegraph.ISelectableNode; import org.simantics.proconf.g3d.shapeeditor.ShapeEditorResources; import org.simantics.proconf.g3d.shapeeditor.dialogs.PropertySelectionDialog; import org.simantics.proconf.g3d.shapeeditor.views.ShapeEditorBase; import org.simantics.proconf.g3d.stubs.G3DModel; import org.simantics.proconf.g3d.stubs.Orientation; import org.simantics.utils.ui.ErrorLogger; public class AnimationContribution implements EditorContribution { List actions = new ArrayList(); private ShapeEditorBase parent; private final String NO_ANIMATION = "None"; private Slider timeSlider; private double key = 0; private Composite buttonComposite; private Button insertKeyFrameButton; private Button clearKeyFrameButton; private CCombo animationCombo; private Button addAnimationButton; private Button removeAnimationButton; private Button clearAnimationButton; private Button animateButton; private Button usePrecalculation; private Text timeText; private Composite infoComposite; private Text infoText; private Resource animationResource; private ContextAction translateAction; private ContextAction rotateAction; public AnimationContribution(ThreeDimensionalEditorBase parent) { this.parent = (ShapeEditorBase)parent; } @Override public String getName() { return "Animator"; } @Override public void createControl(Composite parent) { FormLayout flayout = new FormLayout(); parent.setLayout(flayout); infoComposite = new Composite(parent, SWT.BORDER); FormData data = new FormData(); data.top = new FormAttachment(0, 0); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.bottom = new FormAttachment(infoComposite, 0, SWT.TOP); this.parent.getRenderingComposite().setLayoutData(data); infoComposite.setLayout(new FillLayout(SWT.VERTICAL)); infoText = new Text(infoComposite, SWT.NONE); GridLayout layout = new GridLayout(2, false); layout.horizontalSpacing = 2; layout.verticalSpacing = 2; layout.marginWidth = 1; layout.marginHeight = 1; infoComposite.setLayout(layout); GridData gdata = new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 2, 1); // FIXME : allows text widget to take all available space (horizontal / width) gdata.widthHint = 2000; infoText.setLayoutData(gdata); timeSlider = new Slider(infoComposite, SWT.NONE); timeSlider.setValues(0, 0, 100, 1, 1, 1); timeSlider.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { key = ((double) timeSlider.getSelection()) / 100.0; updateTime(); } }); timeSlider.setLayoutData(new GridData(SWT.FILL, 1, true, false)); timeText = new Text(infoComposite, SWT.SINGLE); timeText.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { updateTime(); } }); timeText.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (e.keyCode == SWT.CR) updateTime(); } }); buttonComposite = new Composite(infoComposite, SWT.NONE); buttonComposite.setLayout(new FillLayout(SWT.HORIZONTAL)); buttonComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 2, 1)); insertKeyFrameButton = new Button(buttonComposite, SWT.PUSH); insertKeyFrameButton.setText("Insert Keyframe"); insertKeyFrameButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { insertKeyFrame(); } }); clearKeyFrameButton = new Button(buttonComposite, SWT.PUSH); clearKeyFrameButton.setText("Clear Keyframe"); animationCombo = new CCombo(buttonComposite, SWT.NONE); animationCombo.add(NO_ANIMATION); animationCombo.select(0); animationCombo.setEditable(false); animationCombo.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { selectAnimation(); } }); addAnimationButton = new Button(buttonComposite, SWT.PUSH); addAnimationButton.setText("New Animation"); addAnimationButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { addAnimation(); } }); clearAnimationButton = new Button(buttonComposite, SWT.PUSH); clearAnimationButton.setText("Clear Animation"); clearAnimationButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { clearAnimation(); } }); removeAnimationButton = new Button(buttonComposite, SWT.PUSH); removeAnimationButton.setText("Remove Animation"); removeAnimationButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { removeAnimation(); } }); animateButton = new Button(buttonComposite, SWT.TOGGLE); animateButton.setText("Animate"); animateButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { animate(); } }); usePrecalculation = new Button(buttonComposite, SWT.CHECK); usePrecalculation.setText("Precalc"); data = new FormData(); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.bottom = new FormAttachment(100, 0); // FIXME : take account font size data.height = 20 * 3; infoComposite.setLayoutData(data); this.parent.getSelectionAdapter().addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { updateUI(); } }); updateUI(); this.parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { G3DModel m = new G3DModel(g,AnimationContribution.this.parent.getModelResource()); Collection animations = m.getAnimation(); final List animationNames = new ArrayList(); for (Animation a : animations) { animationNames.add(a.getName()); } AnimationContribution.this.parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() { public void run() { for (String s : animationNames) animationCombo.add(s); } }); return GraphRequestStatus.transactionComplete(); } }); } @Override public void disposeControl() { if (animateRunnable != null) VisualizationScheduler.getInstance().removeVisualization(animateRunnable); animateRunnable = null; infoComposite.dispose(); } @Override public void fillContextMenu(Graph graph, IMenuManager manager, StructuredResourceSelection selection) { } @Override public Collection getActions() { return actions; } @Override public void initialize(Graph graph) { actions.add(translateAction = new TranslateAction(parent) { @Override public void setInfoText(String text) { infoText.setText(text); } }); actions.add(rotateAction = new RotateAction(parent){ @Override public void setInfoText(String text) { infoText.setText(text); } }); } private double getCurrentKey() { return key; } private void updateTime() { final double t = getCurrentKey(); timeText.setText(Double.toString(t)); if (animationResource == null) return; if (usePrecalculation.getSelection()) { for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) { if (n instanceof Animatable) { // TODO : frame-rate dependent animations ((Animatable)n).animate(t,0.0); } parent.setViewChanged(true); } } else { parent.getSession().asyncWrite(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { Collection interpolators = getAnimation(g).getRelatedObjects(ShapeEditorResources.animationResource.HasInterpolator); for (IEntity i : interpolators) { if (i.isInstanceOf(ShapeEditorResources.animationResource.ScalarInterpolator)) { // TODO : creating curve each time when time is set is // slow. Curve should be cached TCBCurve c = (TCBCurve) ShapeEditorResources.curveBuilder.loadInterpolator(i); double out = c.evaluate(t); g.setScalarDouble(i.getSingleRelatedObject(ShapeEditorResources.animationResource.HasTarget).getResource(), out); } else if (i.isInstanceOf(ShapeEditorResources.animationResource.SlerpInterpolator)) { // TODO : creating curve each time when time is set is slow. // Curve should be cached SlerpCurve c = (SlerpCurve) ShapeEditorResources.curveBuilder.loadInterpolator(i); Quat4d out = c.evaluate(t); Orientation r = new Orientation(i.getSingleRelatedObject(ShapeEditorResources.animationResource.HasTarget)); AxisAngle4d aa = new AxisAngle4d(); aa.set(out); G3DTools.setOrientation(r, aa); } } return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { parent.getScenegraphAdapter().setChanged(true); } }); } } private IEntity findPropertyInterpolator(Graph g, Resource property) { Collection interpolators = getAnimation(g).getRelatedObjects(ShapeEditorResources.animationResource.HasInterpolator); for (IEntity i : interpolators) { IEntity e = i.getAtMostOneRelatedObject(ShapeEditorResources.animationResource.HasTarget); if (e == null) continue; if (e.getResource().equals(property)) { return i; } } return null; } private void insertKeyFrame() { ArrayList instances = new ArrayList(); for (Resource rs : parent.getSelectionAdapter().getCurrentSelection().getSelectionList()) { instances.add(rs); } PropertySelectionDialog dialog = new PropertySelectionDialog(parent.getRenderingComposite().getShell(),"Select property","Select animated property",parent.getSession(),instances); if (dialog.open() == Dialog.CANCEL) { return; } final List properties = dialog.getSelectedPropertyInstances(); parent.getSession().asyncWrite(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { for (Resource r : properties) { IEntity t = EntityFactory.create(g,r); IEntity current = findPropertyInterpolator(g, r); if (t.isInstanceOf(ShapeEditorResources.g3dResource.Position)) { for (int i = 0; i < 3; i++) { IEntity d = null; switch (i) { case 0: d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasX); break; case 1: d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasY); break; case 2: d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasZ); break; } current = findPropertyInterpolator(g, d.getResource()); addScalarKey(current,d); } } else if (t.isInstanceOf(ShapeEditorResources.g3dResource.Orientation)) { Orientation rot = new Orientation(t); addSlerpKey(current, rot); } else if (t.isInstanceOf(ShapeEditorResources.g3dResource.Color)) { for (int i = 0; i < 3; i++) { IEntity d = null; switch (i) { case 0: d = t.getSingleRelatedObject(ShapeEditorResources.g3dResource.HasRed); break; case 1: break; case 2: break; } current = findPropertyInterpolator(g, d.getResource()); addScalarKey(current,d); } } else if (t.isInstanceOf(g.getBuiltins().Double)) { addScalarKey(current,t); } else { // TODO: basic cases are handled, only way to support // is to find all doubles from property structure and // interpolators attached to them, if there's any ErrorLogger.getDefault().logWarning("Default keyframe adding has not been implemented", null); } } return GraphRequestStatus.transactionComplete(); } }); } private void addScalarKey(IEntity current, IEntity d) { Graph graph = d.getGraph(); if (current == null) { current = ScalarInterpolator.createDefault(graph).toInterpolator(); // FIXME : stubcast getAnimation(graph).addStatement(ShapeEditorResources.animationResource.HasInterpolator, current); current.addStatement(ShapeEditorResources.animationResource.HasTarget, d); } ShapeEditorResources.curveBuilder.addKey(current,getCurrentKey(),new double[]{d.getGraph().getScalarDouble(d.getResource()),0.0,0.0,0.0}); } private void addSlerpKey(IEntity current, Orientation r) { Graph graph = r.getGraph(); if (current == null) { current = SlerpInterpolator.createDefault(graph).toInterpolator(); // FIXME : stubcast getAnimation(graph).addStatement(ShapeEditorResources.animationResource.HasInterpolator, current); current.addStatement(ShapeEditorResources.animationResource.HasTarget, r); } AxisAngle4d aa = G3DTools.getOrientation(r); Quat4d q = new Quat4d(); q.set(aa); ShapeEditorResources.curveBuilder.addKey(current,getCurrentKey(),new double[]{q.w,q.x,q.y,q.z}); } private void selectAnimation() { if (animationCombo.getSelectionIndex() == 0) { animationResource = null; updateUI(); } else { final String name = animationCombo.getItem(animationCombo.getSelectionIndex()); parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { Resource modelResource = parent.getModelResource(); G3DModel m = new G3DModel(g, modelResource); Collection animations = m.getAnimation(); boolean found = false; for (Animation a : animations) { if (a.getName().startsWith(name) && a.getName().length() == name.length()) { animationResource = a.getResource(); found = true; break; } } if (!found) { ErrorLogger.defaultLogError("Could find animation " + name + " for model " + m.getResource(), null); animationResource = null; } return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() { @Override public void run() { updateUI(); } }); } }); } } private Animation getAnimation(Graph graph) { return new Animation(graph, animationResource); } private void addAnimation() { InputDialog d = new InputDialog(parent.getRenderingComposite().getShell(),"Animation name","Animation name","",null); if (d.open() == InputDialog.CANCEL) { return; } final String name = d.getValue(); if (name == null || name.length() == 0) { return; } parent.getSession().asyncWrite(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { G3DModel m = parent.getModel(g); Collection animations = m.getRelatedObjects(ShapeEditorResources.animationResource.HasAnimation); for (IEntity a : animations) { if (a.getName().startsWith(name) && a.getName().length() == name.length()) { ErrorLogger.getDefault().logWarning("Cannot add animation with the same name " + name, null); return GraphRequestStatus.transactionCancel(); } } Animation newAnimation = Animation.createDefault(g); newAnimation.setName(name); m.addStatement(ShapeEditorResources.animationResource.HasAnimation, newAnimation); return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() { public void run() { animationCombo.add(name); animationCombo.select(animationCombo.indexOf(name)); selectAnimation(); } }); } }); } private void removeAnimation() { assert(animationResource != null); GraphRequestWithResult r = new GraphRequestWithResult() { public String performWithResult(Graph g) throws Exception { return getAnimation(g).getName(); }; }; parent.getSession().syncRead(r); MessageDialog dialog = new MessageDialog(parent.getRenderingComposite().getShell(),"Confirm",null,"Do you want to remove animation " + r.getResult() ,MessageDialog.QUESTION,new String[]{"Yes","No"},1); if (dialog.open() == 1) return; parent.getSession().asyncWrite(new GraphRequestAdapter() { String name; @Override public GraphRequestStatus perform(Graph g) throws Exception { Animation currentAnimation = getAnimation(g); name = currentAnimation.getName(); parent.getModel(g).removeStatement(ShapeEditorResources.animationResource.HasAnimation, currentAnimation); return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() { public void run() { animationCombo.remove(animationCombo.indexOf(name)); } }); } }); } private void clearAnimation() { assert(animationResource != null); parent.getSession().asyncWrite(new GraphRequestAdapter() { boolean proceed; @Override public GraphRequestStatus perform(Graph g) throws Exception { int size = getAnimation(g).getInterpolator().size(); if (size == 0) return GraphRequestStatus.transactionCancel(); final String name = getAnimation(g).getName(); parent.getRenderingComposite().getDisplay().syncExec(new Runnable() { public void run() { MessageDialog dialog = new MessageDialog(parent.getRenderingComposite().getShell(),"Confirm",null,"Do you want to clear animation " + name ,MessageDialog.QUESTION,new String[]{"Yes","No"},1); proceed = (dialog.open() != MessageDialog.CANCEL); } }); if (proceed) { //getAnimation(g).getInterpolator().clear(); getAnimation(g).removeRelatedStatements(ShapeEditorResources.animationResource.HasInterpolator); return GraphRequestStatus.transactionComplete(); } else { return GraphRequestStatus.transactionCancel(); } } }); } public Graph createAnimationParameterization(Graph g) { if (g.getObjects(parent.getModelResource(), ShapeEditorResources.g3dResource.HasSizingParameter).size() > 0) { ContextGraph graph; if (!(g instanceof ContextGraph)) { graph = new ContextGraph(g); graph.setContext(parent.getModelResource()); } else { graph = (ContextGraph)g; } Animation animation = getAnimation(graph); Collection interpolators = animation.getInterpolator(); for (org.simantics.animation.stubs.Interpolator interpolator : interpolators) { IEntity target = interpolator.getTarget(); // check all model properties G3DModel model = parent.getModel(graph); Collection modelProperties = model.getRelatedProperties(ShapeEditorResources.g3dResource.HasSizingParameter); for (Property p : modelProperties) { IEntity t = EntityFactory.create(graph,p.getResource()); // get parameterization equations Collection equations = t.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget); // get parameterized values Collection parameterTargets = new ArrayList(); for (IEntity eq : equations) { Collection tgts = eq.getRelatedObjects(ShapeEditorResources.equationResource.HasTarget); assert(tgts.size() == 1); parameterTargets.add(tgts.iterator().next()); } // do matching between interpolator targets and parameterized values // TODO : old system did not have inverse relations but current system does. // it is possible to take interpolation target and find if it is connected to an equation // this would make code much faster (no more stupid loops over everything) for (IEntity d : parameterTargets) { if (d.getResource().equals(target.getResource())) { // get default value for sizing property Collection prop = t.getRelatedObjects(ShapeEditorResources.g3dResource.HasDefaultDoubleValue); if (prop.size() == 1) { ShapeEditorResources.curveBuilder.parameterize(interpolator, prop.iterator().next().toProperty().getDoubleArray(), p.getDoubleArray()); } else { ErrorLogger.defaultLogError("Cannot parameterize interpolator " + interpolator.getResource() + " of animation " + animation.getResource() + " since parameter " + p.getResource() + " has no default value", null); } } } } } return graph; } else { return g; } } private AnimateRunnable animateRunnable = null; private void animate() { updateUI(); if (animateButton.getSelection()) { if (animateRunnable != null) return; if (usePrecalculation.getSelection()) { parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { Graph graph = parent.createParameterization(g); createAnimationParameterization(graph); for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) { if (n instanceof ISelectableNode) { if (!((ISelectableNode)n).isVisible()) continue; } if (n instanceof Animatable) { ((Animatable)n).setAnimation(graph,animationResource); } } return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { animateRunnable = new AnimateRunnable(); VisualizationScheduler.getInstance().addVisualization(animateRunnable); } }); } else { animateRunnable = new AnimateRunnable(); VisualizationScheduler.getInstance().addVisualization(animateRunnable); } } else { if (animateRunnable == null) return; VisualizationScheduler.getInstance().removeVisualization(animateRunnable); animateRunnable = null; if (usePrecalculation.getSelection()) { // updateTime updates values to graph if precalculation is not used. // we must store current values from animation to synchronize view and graph // information so that use can modify animation properly. usePrecalculation.setSelection(false); updateTime(); usePrecalculation.setSelection(true); for (IGraphicsNode n : parent.getScenegraphAdapter().getNodes()) { if (n instanceof Animatable) { ((Animatable)n).setAnimation(null,null); } } } } } private void updateUI() { if (animationResource != null) { animateButton.setEnabled(true); timeSlider.setEnabled(true); if (animateButton.getSelection()) { addAnimationButton.setEnabled(false); insertKeyFrameButton.setEnabled(false); removeAnimationButton.setEnabled(false); clearKeyFrameButton.setEnabled(false); animationCombo.setEnabled(false); usePrecalculation.setEnabled(false); clearAnimationButton.setEnabled(false); } else { addAnimationButton.setEnabled(true); insertKeyFrameButton.setEnabled(!parent.getSelectionAdapter().getCurrentSelection().isEmpty()); removeAnimationButton.setEnabled(true); clearKeyFrameButton.setEnabled(false); //FIXME : detect keyframes animationCombo.setEnabled(true); usePrecalculation.setEnabled(true); clearAnimationButton.setEnabled(true); } } else { timeSlider.setEnabled(false); addAnimationButton.setEnabled(true); insertKeyFrameButton.setEnabled(false); removeAnimationButton.setEnabled(false); clearKeyFrameButton.setEnabled(false); animateButton.setEnabled(false); animateButton.setSelection(false); animationCombo.setEnabled(true); usePrecalculation.setEnabled(false); clearAnimationButton.setEnabled(false); } } private void updateKey(double k) { key = k; if (key >= 1.0) key = 0.0; else if (key < 0.0) key = 1.0; timeSlider.setSelection((int)(key*100.0)); } private class AnimateRunnable implements Runnable { public void run() { try { updateKey(key + 0.01); updateTime(); } catch (Exception e) { VisualizationScheduler.getInstance().removeVisualization(animateRunnable); } } } @Override public void fillLocalToolBar(IToolBarManager manager) { } @Override public void fillLocalPullDown(IMenuManager manager) { } @Override public void dispose() { } @Override public void run() { } }