1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Alexander Fedorov <alexander.fedorov@arsysop.ru> - Bug 548799
14  *******************************************************************************/
15 package org.eclipse.ui.internal;
16 
17 import org.eclipse.core.runtime.IConfigurationElement;
18 import org.eclipse.jface.action.Action;
19 import org.eclipse.jface.action.ActionContributionItem;
20 import org.eclipse.jface.action.IAction;
21 import org.eclipse.jface.resource.ResourceLocator;
22 import org.eclipse.ui.IEditorPart;
23 import org.eclipse.ui.IPluginContribution;
24 import org.eclipse.ui.IViewPart;
25 import org.eclipse.ui.IWorkbenchActionConstants;
26 import org.eclipse.ui.IWorkbenchWindow;
27 import org.eclipse.ui.PlatformUI;
28 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
29 
30 /**
31  * When 'action' tag is found in the registry, an object of this class is
32  * created. It creates the appropriate action object and captures information
33  * that is later used to add this action object into menu/tool bar. This class
34  * is reused for global (workbench) menu/tool bar, popup menu actions, as well
35  * as view's pulldown and local tool bar.
36  */
37 public class ActionDescriptor implements IPluginContribution {
38 	private PluginAction action;
39 
40 	private String toolbarId;
41 
42 	private String menuPath;
43 
44 	private String id;
45 
46 	private String pluginId;
47 
48 	private String menuGroup;
49 
50 	private String toolbarGroupId;
51 
52 	private int mode = 0;
53 
54 	/**
55 	 * Popup constant. Value <code>0x1</code>.
56 	 */
57 	public static final int T_POPUP = 0x1;
58 
59 	/**
60 	 * View constant. Value <code>0x2</code>.
61 	 */
62 	public static final int T_VIEW = 0x2;
63 
64 	/**
65 	 * Workbench constant. Value <code>0x3</code>.
66 	 */
67 	public static final int T_WORKBENCH = 0x3;
68 
69 	/**
70 	 * Editor constant. Value <code>0x4</code>.
71 	 */
72 	public static final int T_EDITOR = 0x4;
73 
74 	/**
75 	 * Workbench pulldown constant. Value <code>0x5</code>.
76 	 */
77 	public static final int T_WORKBENCH_PULLDOWN = 0x5;
78 
79 	/**
80 	 * Push style constant. Value <code>push</code>.
81 	 */
82 	public static final String STYLE_PUSH = "push"; //$NON-NLS-1$
83 
84 	/**
85 	 * Radio style constant. Value <code>radio</code>.
86 	 */
87 	public static final String STYLE_RADIO = "radio"; //$NON-NLS-1$
88 
89 	/***
90 	 * Toggle style constant. Value <code>toggle</code>.
91 	 */
92 	public static final String STYLE_TOGGLE = "toggle"; //$NON-NLS-1$
93 
94 	/**
95 	 * Pulldown style constant. Value <code>pulldown</code>.
96 	 */
97 	public static final String STYLE_PULLDOWN = "pulldown"; //$NON-NLS-1$
98 
99 	/**
100 	 * Creates a new descriptor with the specified target.
101 	 *
102 	 * @param actionElement the configuration element
103 	 * @param targetType    the type of action
104 	 */
ActionDescriptor(IConfigurationElement actionElement, int targetType)105 	public ActionDescriptor(IConfigurationElement actionElement, int targetType) {
106 		this(actionElement, targetType, null);
107 	}
108 
109 	/**
110 	 * Creates a new descriptor with the target and destination workbench part it
111 	 * will go into.
112 	 *
113 	 * @param actionElement the configuration element
114 	 * @param targetType    the type of action
115 	 * @param target        the target object
116 	 */
ActionDescriptor(IConfigurationElement actionElement, int targetType, Object target)117 	public ActionDescriptor(IConfigurationElement actionElement, int targetType, Object target) {
118 		// Load attributes.
119 		id = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
120 		pluginId = actionElement.getContributor().getName();
121 		String label = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_LABEL);
122 		String tooltip = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_TOOLTIP);
123 		String helpContextId = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_HELP_CONTEXT_ID);
124 		String mpath = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_MENUBAR_PATH);
125 		String tpath = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_TOOLBAR_PATH);
126 		String style = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_STYLE);
127 		String icon = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_ICON);
128 		String hoverIcon = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_HOVERICON);
129 		String disabledIcon = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_DISABLEDICON);
130 		String description = actionElement.getAttribute(IWorkbenchRegistryConstants.TAG_DESCRIPTION);
131 		String accelerator = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_ACCELERATOR);
132 		if ("FORCE_TEXT".equals(actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_MODE))) { //$NON-NLS-1$
133 			mode = ActionContributionItem.MODE_FORCE_TEXT;
134 		}
135 
136 		// Verify input.
137 		if (label == null) {
138 			WorkbenchPlugin.log("Invalid action declaration (label == null): " + id); //$NON-NLS-1$
139 			label = WorkbenchMessages.ActionDescriptor_invalidLabel;
140 		}
141 
142 		// Calculate menu and toolbar paths.
143 		String mgroup = null;
144 		String tgroup = null;
145 		if (mpath != null) {
146 			int loc = mpath.lastIndexOf('/');
147 			if (loc != -1) {
148 				mgroup = mpath.substring(loc + 1);
149 				mpath = mpath.substring(0, loc);
150 			} else {
151 				mgroup = mpath;
152 				mpath = null;
153 			}
154 		}
155 		if (targetType == T_POPUP && mgroup == null) {
156 			mgroup = IWorkbenchActionConstants.MB_ADDITIONS;
157 		}
158 		if (tpath != null) {
159 			int loc = tpath.lastIndexOf('/');
160 			if (loc != -1) {
161 				tgroup = tpath.substring(loc + 1);
162 				tpath = tpath.substring(0, loc);
163 			} else {
164 				tgroup = tpath;
165 				tpath = null;
166 			}
167 		}
168 		menuPath = mpath;
169 		menuGroup = mgroup;
170 		if ((tpath != null) && tpath.equals("Normal")) { //$NON-NLS-1$
171 			tpath = ""; //$NON-NLS-1$
172 		}
173 		toolbarId = tpath;
174 		toolbarGroupId = tgroup;
175 
176 		// Create action.
177 		action = createAction(targetType, actionElement, target, style);
178 		if (action.getText() == null) {
179 			action.setText(label);
180 		}
181 		if (action.getToolTipText() == null && tooltip != null) {
182 			action.setToolTipText(tooltip);
183 		}
184 		if (helpContextId != null) {
185 			String fullID = helpContextId;
186 			if (helpContextId.indexOf('.') == -1) {
187 				// For backward compatibility we auto qualify the id if it is not
188 				// qualified)
189 				fullID = actionElement.getContributor().getName() + "." + helpContextId;//$NON-NLS-1$
190 			}
191 			PlatformUI.getWorkbench().getHelpSystem().setHelp(action, fullID);
192 		}
193 		if (description != null) {
194 			action.setDescription(description);
195 		}
196 
197 		if (style != null) {
198 			// Since 2.1, the "state" and "pulldown" attributes means something different
199 			// when the new "style" attribute has been set. See doc for more info.
200 			String state = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_STATE);
201 			if (state != null) {
202 				if (style.equals(STYLE_RADIO) || style.equals(STYLE_TOGGLE)) {
203 					action.setChecked(state.equals("true"));//$NON-NLS-1$
204 				}
205 			}
206 		} else {
207 			// Keep for backward compatibility for actions not using the
208 			// new style attribute.
209 			String state = actionElement.getAttribute(IWorkbenchRegistryConstants.ATT_STATE);
210 			if (state != null) {
211 				action.setChecked(state.equals("true"));//$NON-NLS-1$
212 			}
213 		}
214 
215 		String extendingPluginId = actionElement.getDeclaringExtension().getContributor().getName();
216 
217 		if (icon != null) {
218 			ResourceLocator.imageDescriptorFromBundle(extendingPluginId, icon).ifPresent(action::setImageDescriptor);
219 		}
220 		if (hoverIcon != null) {
221 			ResourceLocator.imageDescriptorFromBundle(extendingPluginId, hoverIcon)
222 					.ifPresent(action::setHoverImageDescriptor);
223 		}
224 		if (disabledIcon != null) {
225 			ResourceLocator.imageDescriptorFromBundle(extendingPluginId, disabledIcon)
226 					.ifPresent(action::setDisabledImageDescriptor);
227 		}
228 
229 		if (accelerator != null) {
230 			processAccelerator(action, accelerator);
231 		}
232 	}
233 
234 	/**
235 	 * Creates an instance of PluginAction. Depending on the target part, subclasses
236 	 * of this class may be created.
237 	 */
createAction(int targetType, IConfigurationElement actionElement, Object target, String style)238 	private PluginAction createAction(int targetType, IConfigurationElement actionElement, Object target,
239 			String style) {
240 		int actionStyle = IAction.AS_UNSPECIFIED;
241 		if (style != null) {
242 			switch (style) {
243 			case STYLE_RADIO:
244 				actionStyle = IAction.AS_RADIO_BUTTON;
245 				break;
246 			case STYLE_TOGGLE:
247 				actionStyle = IAction.AS_CHECK_BOX;
248 				break;
249 			case STYLE_PULLDOWN:
250 				actionStyle = IAction.AS_DROP_DOWN_MENU;
251 				break;
252 			case STYLE_PUSH:
253 				actionStyle = IAction.AS_PUSH_BUTTON;
254 				break;
255 			default:
256 				break;
257 			}
258 		}
259 
260 		switch (targetType) {
261 		case T_VIEW:
262 			return new ViewPluginAction(actionElement, (IViewPart) target, id, actionStyle);
263 		case T_EDITOR:
264 			return new EditorPluginAction(actionElement, (IEditorPart) target, id, actionStyle);
265 		case T_WORKBENCH:
266 			return new WWinPluginAction(actionElement, (IWorkbenchWindow) target, id, actionStyle);
267 		case T_WORKBENCH_PULLDOWN:
268 			actionStyle = IAction.AS_DROP_DOWN_MENU;
269 			return new WWinPluginPulldown(actionElement, (IWorkbenchWindow) target, id, actionStyle);
270 		case T_POPUP:
271 			return new ObjectPluginAction(actionElement, id, actionStyle);
272 		default:
273 			WorkbenchPlugin.log("Unknown Action Type: " + targetType);//$NON-NLS-1$
274 			return null;
275 		}
276 	}
277 
278 	/**
279 	 * Returns the action object held in this descriptor.
280 	 *
281 	 * @return the action
282 	 */
getAction()283 	public PluginAction getAction() {
284 		return action;
285 	}
286 
287 	/**
288 	 * Returns action's id as defined in the registry.
289 	 *
290 	 * @return the id
291 	 */
getId()292 	public String getId() {
293 		return id;
294 	}
295 
296 	/**
297 	 * Returns named slot (group) in the menu where this action should be added.
298 	 *
299 	 * @return the menu group
300 	 */
getMenuGroup()301 	public String getMenuGroup() {
302 		return menuGroup;
303 	}
304 
305 	/**
306 	 * Returns menu path where this action should be added. If null, the action will
307 	 * not be added into the menu.
308 	 *
309 	 * @return the menubar path
310 	 */
getMenuPath()311 	public String getMenuPath() {
312 		return menuPath;
313 	}
314 
315 	/**
316 	 * Returns the named slot (group) in the tool bar where this action should be
317 	 * added.
318 	 *
319 	 * @return the toolbar group id
320 	 */
getToolbarGroupId()321 	public String getToolbarGroupId() {
322 		return toolbarGroupId;
323 	}
324 
325 	/**
326 	 * Returns id of the tool bar where this action should be added. If null, action
327 	 * will not be added to the tool bar.
328 	 *
329 	 * @return the toolbar id
330 	 */
getToolbarId()331 	public String getToolbarId() {
332 		return toolbarId;
333 	}
334 
335 	/**
336 	 * For debugging only.
337 	 */
338 	@Override
toString()339 	public String toString() {
340 		return "ActionDescriptor(" + id + ")";//$NON-NLS-2$//$NON-NLS-1$
341 	}
342 
343 	/**
344 	 * Process the accelerator definition. If it is a number then process the code
345 	 * directly - if not then parse it and create the code
346 	 */
processAccelerator(IAction action, String acceleratorText)347 	private void processAccelerator(IAction action, String acceleratorText) {
348 
349 		if (acceleratorText.length() == 0) {
350 			return;
351 		}
352 
353 		// Is it a numeric definition?
354 		if (Character.isDigit(acceleratorText.charAt(0))) {
355 			try {
356 				action.setAccelerator(Integer.valueOf(acceleratorText).intValue());
357 			} catch (NumberFormatException e) {
358 				WorkbenchPlugin.log("Invalid accelerator declaration for action: " + id, e); //$NON-NLS-1$
359 			}
360 		} else {
361 			action.setAccelerator(Action.convertAccelerator(acceleratorText));
362 		}
363 	}
364 
365 	@Override
getLocalId()366 	public String getLocalId() {
367 		return getId();
368 	}
369 
370 	@Override
getPluginId()371 	public String getPluginId() {
372 		return pluginId;
373 	}
374 
getMode()375 	public int getMode() {
376 		return mode;
377 	}
378 }
379