Home » Eclipse Projects » GEF » Setting tool from a tool bar
Setting tool from a tool bar [message #162770] |
Mon, 27 December 2004 11:53  |
Eclipse User |
|
|
|
Originally posted by: bo.n0spam.majewski.com
Could any of the GEF gurus tell me what is the proper way of setting a
new tool on the edit domain from a tool bar action. What I did was to
create the an abstract action that can be used for that purpose (see
code below). The trouble is that it relies on a custom GraphicalEditor,
in which the getEditDomain method is re-exposed (see code for the run
method). Is there a better way of setting a new tool on the edit domain
that does not involve exposing getEditDomain method? I know of palettes,
but I do not want to use it. All my objects are dragged from a resource
tree, which leaves me with just two buttons that would go into a
palette: selection and connections. I'd rather have those on a tool bar.
public abstract class AbstractToolAction
implements IEditorActionDelegate {
private CustomGraphicalEditor editor;
private CreationFactory factory;
public AbstractToolAction(CreationFactory factory) {
this.factory = factory;
}
public void setActiveEditor(IAction action,
IEditorPart targetEditor) {
if (targetEditor instanceof CustomGraphicalEditor ) {
this.editor = (CustomGraphicalEditor ) targetEditor;
}
}
public void run(IAction action) {
if (this.editor != null) {
this.editor.
getEditDomain().
setActiveTool(this.createTool(this.factory));
}
}
public void selectionChanged(IAction action, ISelection selection) {
}
protected abstract AbstractTool createTool(CreationFactory factory);
}
|
|
| | |
Re: Setting tool from a tool bar [message #163067 is a reply to message #163021] |
Thu, 30 December 2004 12:13   |
Eclipse User |
|
|
|
Originally posted by: bo.n0spam.majewski.com
As my firewall does not allow me to put the updated version I decided to
post it (in text version) directly here.
Introduction
In certain applications, all objects shown in the graphical editor may
come from a resource tree. This leaves the issue of where to place
buttons activating selection and connection tools. Using a palette is
not a very pleasing solution. It introduces yet another area of which
the user must be aware. A more natural solution is to utilize the
workbench tool bar. In this article I describe how to create an action
that activates the connection creation tool and how to add it to the
tool bar.
Tools and edit domains
Tools, such as selection tool or connection creation tool are set on the
edit domain. The first obstacle we encounter is that the access to edit
domain is restricted. A solution is to use a retargetable action^1
<#foot1> , whose handler is an action internal to the editor and thus
having direct access to the edit domain. The alternative solution, where
a workbench part action gains access to the edit domain through the
|IAdaptable| interface is also given in this article.
Laying groundwork
To make the exercise a bit more generic we start by preparing some basic
classes. First we define the |IToolActionConstants| interface, which
plays the similar role to the |IWorkbenchConstants|. In it we define the
IDs for various tools we might activate from the tool bar.
public interface IToolActionConstants {
public static final String SELECTION = "selectionTool";
public static final String CONNECTION = "connectionTool";
public static final String MARQUEE = "marqueeTool";
}
Next we create a base class for actions activating tools. It defines two
constructors, one for actions that require factories and one for other
actions. A connection tool is an example of the former, while the
selection tool falls into the latter category. The base class requires
the edit domain on which the tools are set to be given in the
constructor. In addition, it defines the |createTool| method that must
return the tool to be set. The |init| method should be used by
overriding classes to set the ID and perform any other setup needed by
them.
public abstract class AbstractToolAction extends Action {
private CreationFactory factory;
private EditDomain domain;
public AbstractToolAction(EditDomain domain) {
this(domain, null);
}
public AbstractToolAction(EditDomain domain,
CreationFactory factory) {
this.factory = factory;
this.domain = domain;
this.init();
}
protected void init() {}
public void run() {
this.getEditDomain().setActiveTool(this.createTool(this.fact ory));
}
protected EditDomain getEditDomain() {
return this.domain;
}
protected abstract Tool createTool(CreationFactory factory);
}
Implementing tool action and retarget tool action
As an example we are going to implement a tool action for the
|ConnectionCreationTool|. A similar, although simpler action, as it does
not require a factory, can be implemented for the selection tool by
following steps outlined here.
The |ConnectionToolAction| provides two simple extensions to the
|AbstractToolAction| defined above. First, it sets the action ID in the
|init| method. Second, it implements the abstract method, |createTool|.
In this particular example I decided to re-use the same action, although
creating a new instance for each call is also a possibility.
public class ConnectionToolAction extends AbstractToolAction {
private ConnectionCreationTool tool;
public ConnectionToolAction(EditDomain domain,
CreationFactory factory) {
super(domain, factory);
}
protected void init() {
this.setId(IToolActionConstants.CONNECTION);
}
protected Tool createTool(CreationFactory factory) {
if (this.tool == null) {
this.tool = new ConnectionCreationTool(factory);
}
return this.tool;
}
}
In addition to the regular action we need to create the retargetable
action. I extend the |RetargetAction| rather than |LabelRetargetAction|,
as tools do not need to dynamically update the action label. In real
implementation text and tool tip should be externalized to allow for
application internationalization. Also, one needs to replace ? with a
code that returns an appropriate image description.
public class ConnectionToolRetargetAction extends RetargetAction {
public ConnectionToolRetargetAction() {
super(IToolActionConstants.CONNECTION, "", AS_PUSH_BUTTON);
this.setImageDescriptor(...);
this.setText("Connect");
this.setToolTipText("Connect");
}
}
Putting it all together
Now that the action and retargetable actions are defined we need to
assemble all parts into a working editor. First we start by adding the
connection action to registered editor actions. This is done by
overriding the |createActions| method of the |GraphicalEditor|. Just
remember to call the parent's class method, as otherwise you will lose
undo, redo and all other actions defined by default by the graphical
editor.
public class MyGraphicalEditor extends GraphicalEditor {
...
protected void createActions() {
super.createActions();
this.getActionRegistry().registerAction(
new ConnectionToolAction(this.getEditDomain(),
new SimpleFactory(?)));
}
}
Next we define an editor action contributor. The easiest way to do so is
by extending the |ActionBarContributor| class. In it you must define two
methods, |buildActions| and |declareGlobalActionKeys|. We are going to
override the third method, the |contributeToToolBar| method, so that we
can add the retargetable action to the editor tool bar. Typical
applications also add undo, redo, save, and such actions in the
|buildActions| method. For brevity I, however, omit this part.
public class MyEditorContributor extends ActionBarContributor {
private RetargetAction setConnectionTool;
protected void buildActions() {
... // build undo, redo and other actions
this.setConnectionTool = new ConnectionToolRetargetAction();
this.addRetargetAction(this.setConnectionTool);
}
public void contributeToToolBar(IToolBarManager toolBarManager) {
super.contributeToToolBar(toolBarManager);
toolBarManager.add(this.setConnectionTool);
}
protected void declareGlobalActionKeys() {}
}
The final piece of the puzzle is added in the |plugin.xml| file. There
in the |org.eclipse.ui.editor| extension point we specify both the
editor and the above created contributor class.
<extension
point="org.eclipse.ui.editors">
<editor
*class="MyGraphicalEditor"*
*contributorClass="MyEditorContributor"*
id="MyGraphicalEditor"
name="..."
icon="..."
extensions="..."/>
</extension>
Why does it work
It all may look a bit magical at the first sight. We create a few
classes, which contain no references to each other, yet when a new
instance of |MyGraphicalEditor| is created the connection tool can be
activated directly from the tool bar. This section aims at providing a
short explanation of how this happens.
When an instance of the |MyGraphicalEditor| is created the Eclipse
framework also creates an instance of the |MyEditorContributor|. It then
asks the contributor to initialize action bars. This in turn calls the
methods overridden by |MyEditorContributor|. When the editor part is
activated the retargetable action asks it to provide a handler. The
handler is fetched based on the ID specified at the construction time.
This is why it was important for the connection action and its
retargetable counterpart to use the same ID. The edit part searches a
map for the action with the given ID. The map is populated by the action
registry, when the |registerAction| call is made. Hence an instance of
the |ConnectionToolRegargetAction| receives the instance of the
|ConnectionToolAction| created and registered in the |createActions|
method of the |MyGraphicalEditor|. When the user presses the button
created for the retargetable action, it delegates the task to the
handler, which in turn sets the connection creation tool on the editor's
edit domain.
Using the WorkbenchPartAction
An alternative approach to accessing EditDomain is to use the
|WorkbenchPartAction| as the basic for tool actions. Unlike regular
actions, workbench part actions have access to the workbench part, or,
in our case, the editor. This allows the action to get access to the
editor's domain through the |getAdapter| call. Naturally, for this
solution to work, the editor must override the |getAdapter| method and
return its edit domain when the adapter is either the |EditDomain| or
|DefaultEditDomain| class. The |ConnectionToolAction| code, adjusted for
the workbench part type action is shown below. Note that I am taking
into account that the adapter returned by the editor is null. Also, a
workbench part action must compute its enabled flag. For simplicity this
action claims to be always enabled.
public abstract class ConnectionToolAction extends WorkbenchPartAction {
private ConnectionCreationTool tool;
private CreationFactory factory;
public ConnectionToolAction(IEditorPart editor,
CreationFactory factory) {
super(editor);
this.factory = factory;
}
protected void init() {
this.setId(IToolActionConstants.CONNECTION);
}
protected Tool createTool(CreationFactory factory) {
if (this.tool == null) {
this.tool = new ConnectionCreationTool(factory);
}
return this.tool;
}
public void run() {
EditDomain domain = (EditDomain)
this.getWorkbenchPart().getAdapter(DefaultEditDomain.class);
if (domain != null) {
domain.setActiveTool(this.createTool(this.factory));
}
}
protected boolean calculateEnabled() {
return true;
}
}
The code for the retargetable action does not change. However, the
editor needs to return the edit domain in the |getAdapter| method, as
shown below.
public class MyGraphicalEditor extends GraphicalEditor {
...
protected void createActions() {
super.createActions();
this.getActionRegistry().registerAction(
new ConnectionToolAction(this, new SimpleFactory(...)));
}
public Object getAdapter(Class adapter) {
if (EditDomain.class == adapter ||
DefaultEditDomain.class == adapter) {
return this.getEditDomain();
} else {
// handle other adapter types
}
}
}
The remaining pieces, retargetable action, editor contributor and the
|org.eclipse.ui.editors| extensions remain as in the first solution. The
choice of the solutions is up to you. The second one has the side effect
of making edit domain publicly accessible to any caller of the
|getAdapter| method. So if you want to keep things well protected, you
might opt for the first approach. If, for some other reasons, you
already have exposed the edit domain through the |IAdaptable| interface,
the second solution may be slightly easier.
------------------------------------------------------------ ------------
^1 The suggestion to use retargetable action came originally from Randy
Hudson, the technical lead for the Graphical Editing Framework
|
|
| |
Goto Forum:
Current Time: Sat Jul 12 22:42:00 EDT 2025
Powered by FUDForum. Page generated in 0.04549 seconds
|