Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 193600 | Differences between
and this patch

Collapse All | Expand All

(-)schema/menus.exsd (-4 / +4 lines)
Lines 37-43 Link Here
37
<ul>
37
<ul>
38
<li><b>Scheme</b> - The 'type' of the UI component into which the contributions will be added. It may be either "menu", "popup" or "toolbar". While 'popup' is indeed a form of menu it is provided to allow a distinction between a view's 'chevron' menu (for which we use the "menu" scheme) and its default context menu which, by convention, should be registered using the "popup" scheme.</li>
38
<li><b>Scheme</b> - The 'type' of the UI component into which the contributions will be added. It may be either "menu", "popup" or "toolbar". While 'popup' is indeed a form of menu it is provided to allow a distinction between a view's 'chevron' menu (for which we use the "menu" scheme) and its default context menu which, by convention, should be registered using the "popup" scheme.</li>
39
<li><b>ID</b> - This is the id of menu or toolbar into which the contributions should be added. By convention views should use their view id as the id of the root of their chevron and default popup menu. Note that there is no explicit distinction between contributions supporting editors and 'normal' contributions into the Menu Menu or Toolbar; both global contributions and editor contributions would use the "org.eclipse.ui.main.menu" id or "org.eclipse.ui.main.toolbar". A special id used with popup:, "org.eclipse.ui.popup.any", is reserved to handle contributions which are candidates to appear on any (top level) context menu. Note that these contributions are expected to implement a 'visibleWhen' expression sufficient to limit their visibility to appropriate menus</li>
39
<li><b>ID</b> - This is the id of menu or toolbar into which the contributions should be added. By convention views should use their view id as the id of the root of their chevron and default popup menu. Note that there is no explicit distinction between contributions supporting editors and 'normal' contributions into the Menu Menu or Toolbar; both global contributions and editor contributions would use the "org.eclipse.ui.main.menu" id or "org.eclipse.ui.main.toolbar". A special id used with popup:, "org.eclipse.ui.popup.any", is reserved to handle contributions which are candidates to appear on any (top level) context menu. Note that these contributions are expected to implement a 'visibleWhen' expression sufficient to limit their visibility to appropriate menus</li>
40
<li><b>Query</b> - This field allows fine-grained definition of the specific location <i>within</i> a given menu. It has the form "[placement]=[id]" where placement is one of "before" or "after" and the id is expected to be the id of some IContributionItem in the menu.</li>
40
<li><b>Query</b> - This field allows fine-grained definition of the specific location <i>within</i> a given menu. It has the form "[placement]=[id]" where placement is one of "before", "after", or "endof" and the id is expected to be the id of some IContributionItem in the menu.</li>
41
</ul> 
41
</ul> 
42
<p>
42
<p>
43
This will define the location at which the contributions will appear in the eclipse UI. Once the insertion point has been defined the rest of the contributions describe the UI elements that will be added at that location. Each element supports a 'visibleWhen' expression that determines at run time whether a particular item should appear in the menu based on the system's current state (selection, active view/editor, context...).  See <code>org.eclipse.ui.ISources</code> for a list of currently
43
This will define the location at which the contributions will appear in the eclipse UI. Once the insertion point has been defined the rest of the contributions describe the UI elements that will be added at that location. Each element supports a 'visibleWhen' expression that determines at run time whether a particular item should appear in the menu based on the system's current state (selection, active view/editor, context...).  See <code>org.eclipse.ui.ISources</code> for a list of currently
Lines 531-538 Link Here
531
Scheme: One of "menu", "popup" or "toolbar. Indicates the type of the manager used to handle the contributions
531
Scheme: One of "menu", "popup" or "toolbar. Indicates the type of the manager used to handle the contributions
532
Id: This is either the id of an existing menu, a view id or the id of the editor 'type'
532
Id: This is either the id of an existing menu, a view id or the id of the editor 'type'
533
Query: The query format is <placement>=<id> where:
533
Query: The query format is <placement>=<id> where:
534
 <placement> is either "before" or "after" and
534
 <placement> is either "before", "after", or "endof" and
535
 <id> is the id of an existing menu item
535
 <id> is the id of an existing menu item.  The placement modifier is executed when this contribution is processed.  Following contributions may change the final shape of the menu when they are processed.
536
               </documentation>
536
               </documentation>
537
            </annotation>
537
            </annotation>
538
         </attribute>
538
         </attribute>
Lines 790-796 Link Here
790
               <documentation>
790
               <documentation>
791
                  The &apos;id&apos; of this contribution. If defined then it can be  used as a reference in the Query part of the location defining whether the additions are to go before or after this element (or at the end of the logical group containing this element using the &apos;endof&apos; value).
791
                  The &apos;id&apos; of this contribution. If defined then it can be  used as a reference in the Query part of the location defining whether the additions are to go before or after this element (or at the end of the logical group containing this element using the &apos;endof&apos; value).
792
&lt;p&gt;
792
&lt;p&gt;
793
Separator contributions that have an id define the start of a logical group so the result of using the &apos;endof&apos; value for placement is to search forward in the current menu to locate the next id&apos;d separator and to place the inserted elements before that element. If no trailing separator is found then the items are placed at the end of the menu.
793
Separator contributions that have an id define the start of a logical group so the result of using the &apos;endof&apos; value for placement is to search forward in the current menu to locate the next separator and to place the inserted elements before that element. If no trailing separator is found then the items are placed at the end of the menu.
794
&lt;/p&gt;
794
&lt;/p&gt;
795
               </documentation>
795
               </documentation>
796
            </annotation>
796
            </annotation>
(-)Eclipse UI Tests/org/eclipse/ui/tests/menus/MenuPopulationTest.java (-2 / +183 lines)
Lines 14-27 Link Here
14
import java.lang.reflect.Field;
14
import java.lang.reflect.Field;
15
15
16
import org.eclipse.jface.action.Action;
16
import org.eclipse.jface.action.Action;
17
import org.eclipse.jface.action.GroupMarker;
17
import org.eclipse.jface.action.IContributionItem;
18
import org.eclipse.jface.action.IContributionItem;
18
import org.eclipse.jface.action.MenuManager;
19
import org.eclipse.jface.action.MenuManager;
19
import org.eclipse.jface.action.ToolBarManager;
20
import org.eclipse.jface.action.ToolBarManager;
20
import org.eclipse.jface.resource.ImageDescriptor;
21
import org.eclipse.jface.resource.ImageDescriptor;
21
import org.eclipse.ui.IPageLayout;
22
import org.eclipse.ui.IPageLayout;
22
import org.eclipse.ui.IViewPart;
23
import org.eclipse.ui.IViewPart;
24
import org.eclipse.ui.IWorkbenchCommandConstants;
23
import org.eclipse.ui.menus.AbstractContributionFactory;
25
import org.eclipse.ui.menus.AbstractContributionFactory;
24
import org.eclipse.ui.menus.CommandContributionItem;
26
import org.eclipse.ui.menus.CommandContributionItem;
27
import org.eclipse.ui.menus.CommandContributionItemParameter;
25
import org.eclipse.ui.menus.IContributionRoot;
28
import org.eclipse.ui.menus.IContributionRoot;
26
import org.eclipse.ui.menus.IMenuService;
29
import org.eclipse.ui.menus.IMenuService;
27
import org.eclipse.ui.services.IServiceLocator;
30
import org.eclipse.ui.services.IServiceLocator;
Lines 41-46 Link Here
41
	public static final String ID_DEFAULT = "org.eclipse.ui.tests.menus.iconsDefault";
44
	public static final String ID_DEFAULT = "org.eclipse.ui.tests.menus.iconsDefault";
42
	public static final String ID_ALL = "org.eclipse.ui.tests.menus.iconsAll";
45
	public static final String ID_ALL = "org.eclipse.ui.tests.menus.iconsAll";
43
	public static final String ID_TOOLBAR = "org.eclipse.ui.tests.menus.iconsToolbarOnly";
46
	public static final String ID_TOOLBAR = "org.eclipse.ui.tests.menus.iconsToolbarOnly";
47
	private AbstractContributionFactory afterOne;
48
	private AbstractContributionFactory beforeOne;
49
	private AbstractContributionFactory endofOne;
50
	private CommandContributionItem usefulContribution;
44
51
45
	/**
52
	/**
46
	 * @param testName
53
	 * @param testName
Lines 161-167 Link Here
161
168
162
	public void testFactoryScopePopulation() throws Exception {
169
	public void testFactoryScopePopulation() throws Exception {
163
		AbstractContributionFactory factory = new AbstractContributionFactory(
170
		AbstractContributionFactory factory = new AbstractContributionFactory(
164
				"menu:the.population.menu?after=additions", "org.eclipse.ui.tests") {
171
				"menu:the.population.menu?after=additions",
172
				"org.eclipse.ui.tests") {
165
173
166
			public void createContributionItems(IServiceLocator serviceLocator,
174
			public void createContributionItems(IServiceLocator serviceLocator,
167
					IContributionRoot additions) {
175
					IContributionRoot additions) {
Lines 178-184 Link Here
178
		assertNotNull(view);
186
		assertNotNull(view);
179
		IMenuService service = (IMenuService) view.getSite().getService(
187
		IMenuService service = (IMenuService) view.getSite().getService(
180
				IMenuService.class);
188
				IMenuService.class);
181
		service.populateContributionManager(testManager, "menu:the.population.menu");
189
		service.populateContributionManager(testManager,
190
				"menu:the.population.menu");
182
		assertEquals(0, testManager.getSize());
191
		assertEquals(0, testManager.getSize());
183
		service.addContributionFactory(factory);
192
		service.addContributionFactory(factory);
184
		assertEquals(1, testManager.getSize());
193
		assertEquals(1, testManager.getSize());
Lines 186-189 Link Here
186
		processEvents();
195
		processEvents();
187
		assertEquals(0, testManager.getSize());
196
		assertEquals(0, testManager.getSize());
188
	}
197
	}
198
199
	public void testAfterQueryInvalid() throws Exception {
200
		MenuManager manager = new MenuManager();
201
		menuService.populateContributionManager(manager, "menu:after.menu");
202
		assertEquals(0, manager.getSize());
203
	}
204
205
	public void testAfterQueryOneGroup() throws Exception {
206
		MenuManager manager = new MenuManager();
207
		manager.add(new GroupMarker("after.one"));
208
		assertEquals(1, manager.getSize());
209
		menuService.populateContributionManager(manager, "menu:after.menu");
210
		assertEquals(2, manager.getSize());
211
		assertEquals("after.insert", manager.getItems()[1].getId());
212
	}
213
214
	public void testAfterQueryTwoGroups() throws Exception {
215
		MenuManager manager = new MenuManager();
216
		manager.add(new GroupMarker("after.one"));
217
		manager.add(new GroupMarker("after.two"));
218
		assertEquals(2, manager.getSize());
219
		menuService.populateContributionManager(manager, "menu:after.menu");
220
		assertEquals(3, manager.getSize());
221
		assertEquals("after.insert", manager.getItems()[1].getId());
222
	}
223
224
	public void testBeforeQueryInvalid() throws Exception {
225
		MenuManager manager = new MenuManager();
226
		menuService.populateContributionManager(manager, "menu:before.menu");
227
		assertEquals(0, manager.getSize());
228
	}
229
230
	public void testBeforeQueryOneGroup() throws Exception {
231
		MenuManager manager = new MenuManager();
232
		manager.add(new GroupMarker("before.one"));
233
		assertEquals(1, manager.getSize());
234
		menuService.populateContributionManager(manager, "menu:before.menu");
235
		assertEquals(2, manager.getSize());
236
		assertEquals("before.insert", manager.getItems()[0].getId());
237
	}
238
239
	public void testBeforeQueryTwoGroups() throws Exception {
240
		MenuManager manager = new MenuManager();
241
		manager.add(new GroupMarker("before.one"));
242
		manager.add(new GroupMarker("before.two"));
243
		assertEquals(2, manager.getSize());
244
		menuService.populateContributionManager(manager, "menu:before.menu");
245
		assertEquals(3, manager.getSize());
246
		assertEquals("before.insert", manager.getItems()[0].getId());
247
	}
248
249
	public void testBeforeQueryTwoGroups2() throws Exception {
250
		MenuManager manager = new MenuManager();
251
		manager.add(new GroupMarker("before.two"));
252
		manager.add(new GroupMarker("before.one"));
253
		assertEquals(2, manager.getSize());
254
		menuService.populateContributionManager(manager, "menu:before.menu");
255
		assertEquals(3, manager.getSize());
256
		assertEquals("before.insert", manager.getItems()[1].getId());
257
	}
258
259
	public void testEndofQueryInvalid() throws Exception {
260
		MenuManager manager = new MenuManager();
261
		menuService.populateContributionManager(manager, "menu:endof.menu");
262
		assertEquals(0, manager.getSize());
263
	}
264
265
	public void testEndofQueryOneGroup() throws Exception {
266
		MenuManager manager = new MenuManager();
267
		manager.add(new GroupMarker("endof.one"));
268
		assertEquals(1, manager.getSize());
269
		menuService.populateContributionManager(manager, "menu:endof.menu");
270
		assertEquals(2, manager.getSize());
271
		assertEquals("endof.insert", manager.getItems()[1].getId());
272
	}
273
274
	public void testEndofQueryTwoGroups() throws Exception {
275
		MenuManager manager = new MenuManager();
276
		manager.add(new GroupMarker("endof.one"));
277
		manager.add(new GroupMarker("endof.two"));
278
		assertEquals(2, manager.getSize());
279
		menuService.populateContributionManager(manager, "menu:endof.menu");
280
		assertEquals(3, manager.getSize());
281
		assertEquals("endof.insert", manager.getItems()[1].getId());
282
	}
283
284
	public void testEndofQueryTwoGroups2() throws Exception {
285
		MenuManager manager = new MenuManager();
286
		manager.add(new GroupMarker("endof.one"));
287
		manager.add(usefulContribution);
288
		manager.add(new GroupMarker("endof.two"));
289
		assertEquals(3, manager.getSize());
290
		menuService.populateContributionManager(manager, "menu:endof.menu");
291
		assertEquals(4, manager.getSize());
292
		assertEquals("endof.insert", manager.getItems()[2].getId());
293
	}
294
295
	public void testEndofQueryTwoGroups3() throws Exception {
296
		MenuManager manager = new MenuManager();
297
		manager.add(new GroupMarker("endof.two"));
298
		manager.add(new GroupMarker("endof.one"));
299
		assertEquals(2, manager.getSize());
300
		menuService.populateContributionManager(manager, "menu:endof.menu");
301
		assertEquals(3, manager.getSize());
302
		assertEquals("endof.insert", manager.getItems()[2].getId());
303
	}
304
305
	public void testEndofQueryTwoGroups4() throws Exception {
306
		MenuManager manager = new MenuManager();
307
		manager.add(new GroupMarker("endof.two"));
308
		manager.add(new GroupMarker("endof.one"));
309
		manager.add(usefulContribution);
310
		assertEquals(3, manager.getSize());
311
		menuService.populateContributionManager(manager, "menu:endof.menu");
312
		assertEquals(4, manager.getSize());
313
		assertEquals("endof.insert", manager.getItems()[3].getId());
314
	}
315
316
	/*
317
	 * (non-Javadoc)
318
	 * 
319
	 * @see org.eclipse.ui.tests.menus.MenuTestCase#doSetUp()
320
	 */
321
	protected void doSetUp() throws Exception {
322
		super.doSetUp();
323
		afterOne = new AbstractContributionFactory(
324
				"menu:after.menu?after=after.one", "org.eclipse.ui.tests") {
325
			public void createContributionItems(IServiceLocator serviceLocator,
326
					IContributionRoot additions) {
327
				additions.addContributionItem(new GroupMarker("after.insert"),
328
						null);
329
			}
330
		};
331
		menuService.addContributionFactory(afterOne);
332
333
		beforeOne = new AbstractContributionFactory(
334
				"menu:before.menu?before=before.one", "org.eclipse.ui.tests") {
335
			public void createContributionItems(IServiceLocator serviceLocator,
336
					IContributionRoot additions) {
337
				additions.addContributionItem(new GroupMarker("before.insert"),
338
						null);
339
			}
340
		};
341
		menuService.addContributionFactory(beforeOne);
342
343
		endofOne = new AbstractContributionFactory(
344
				"menu:endof.menu?endof=endof.one", "org.eclipse.ui.tests") {
345
			public void createContributionItems(IServiceLocator serviceLocator,
346
					IContributionRoot additions) {
347
				additions.addContributionItem(new GroupMarker("endof.insert"),
348
						null);
349
			}
350
		};
351
		menuService.addContributionFactory(endofOne);
352
		usefulContribution = new CommandContributionItem(
353
				new CommandContributionItemParameter(window, null,
354
						IWorkbenchCommandConstants.HELP_ABOUT, 0));
355
	}
356
357
	/*
358
	 * (non-Javadoc)
359
	 * 
360
	 * @see org.eclipse.ui.tests.menus.MenuTestCase#doTearDown()
361
	 */
362
	protected void doTearDown() throws Exception {
363
		menuService.removeContributionFactory(afterOne);
364
		menuService.removeContributionFactory(beforeOne);
365
		menuService.removeContributionFactory(endofOne);
366
		usefulContribution.dispose();
367
		usefulContribution = null;
368
		super.doTearDown();
369
	}
189
}
370
}
(-)Eclipse UI/org/eclipse/ui/internal/menus/WorkbenchMenuService.java (-12 / +27 lines)
Lines 19-25 Link Here
19
import java.util.List;
19
import java.util.List;
20
import java.util.Map;
20
import java.util.Map;
21
import java.util.Set;
21
import java.util.Set;
22
23
import org.eclipse.core.expressions.Expression;
22
import org.eclipse.core.expressions.Expression;
24
import org.eclipse.core.expressions.IEvaluationContext;
23
import org.eclipse.core.expressions.IEvaluationContext;
25
import org.eclipse.core.runtime.IConfigurationElement;
24
import org.eclipse.core.runtime.IConfigurationElement;
Lines 48-53 Link Here
48
import org.eclipse.swt.widgets.Control;
47
import org.eclipse.swt.widgets.Control;
49
import org.eclipse.swt.widgets.Display;
48
import org.eclipse.swt.widgets.Display;
50
import org.eclipse.ui.ISourceProvider;
49
import org.eclipse.ui.ISourceProvider;
50
import org.eclipse.ui.IWorkbenchActionConstants;
51
import org.eclipse.ui.IWorkbenchWindow;
51
import org.eclipse.ui.IWorkbenchWindow;
52
import org.eclipse.ui.PlatformUI;
52
import org.eclipse.ui.PlatformUI;
53
import org.eclipse.ui.activities.ActivityManagerEvent;
53
import org.eclipse.ui.activities.ActivityManagerEvent;
Lines 73-78 Link Here
73
import org.eclipse.ui.internal.util.Util;
73
import org.eclipse.ui.internal.util.Util;
74
import org.eclipse.ui.keys.IBindingService;
74
import org.eclipse.ui.keys.IBindingService;
75
import org.eclipse.ui.menus.AbstractContributionFactory;
75
import org.eclipse.ui.menus.AbstractContributionFactory;
76
import org.eclipse.ui.menus.MenuUtil;
76
import org.eclipse.ui.services.IEvaluationReference;
77
import org.eclipse.ui.services.IEvaluationReference;
77
import org.eclipse.ui.services.IEvaluationService;
78
import org.eclipse.ui.services.IEvaluationService;
78
import org.eclipse.ui.services.IServiceLocator;
79
import org.eclipse.ui.services.IServiceLocator;
Lines 91-96 Link Here
91
 */
92
 */
92
public final class WorkbenchMenuService extends InternalMenuService {
93
public final class WorkbenchMenuService extends InternalMenuService {
93
94
95
	private static final String INDEX_AFTER_ADDITIONS = "after=additions"; //$NON-NLS-1$
96
94
	/**
97
	/**
95
	 * A combined property and activity listener that updates the visibility of
98
	 * A combined property and activity listener that updates the visibility of
96
	 * contribution items in the new menu system.
99
	 * contribution items in the new menu system.
Lines 736-746 Link Here
736
		}
739
		}
737
	}
740
	}
738
741
739
	/**
740
	 * @param mgr
741
	 * @param uri
742
	 * @return
743
	 */
744
	private int getInsertionIndex(ContributionManager mgr, String location) {
742
	private int getInsertionIndex(ContributionManager mgr, String location) {
745
		MenuLocationURI uri = new MenuLocationURI(location);
743
		MenuLocationURI uri = new MenuLocationURI(location);
746
		String query = uri.getQuery();
744
		String query = uri.getQuery();
Lines 749-767 Link Here
749
747
750
		// No Query means 'after=additions' (if ther) or
748
		// No Query means 'after=additions' (if ther) or
751
		// the end of the menu
749
		// the end of the menu
752
		if (query.length() == 0 || query.equals("after=additions")) { //$NON-NLS-1$
750
		if (query.length() == 0 || query.equals(INDEX_AFTER_ADDITIONS)) {
753
			additionsIndex = mgr.indexOf("additions"); //$NON-NLS-1$
751
			additionsIndex = mgr
752
					.indexOf(IWorkbenchActionConstants.MB_ADDITIONS);
754
			if (additionsIndex == -1)
753
			if (additionsIndex == -1)
755
				additionsIndex = mgr.getItems().length;
754
				additionsIndex = mgr.getItems().length;
756
			else
755
			else
757
				++additionsIndex;
756
				++additionsIndex;
758
		} else {
757
		} else {
759
			// Should be in the form "[before|after]=id"
758
			// Should be in the form "[before|after|endof]=id"
760
			String[] queryParts = Util.split(query, '=');
759
			String[] queryParts = Util.split(query, '=');
761
			if (queryParts.length>1 && queryParts[1].length() > 0) {
760
			if (queryParts.length>1 && queryParts[1].length() > 0) {
762
				additionsIndex = mgr.indexOf(queryParts[1]);
761
				String modifier = queryParts[0];
763
				if (additionsIndex != -1 && queryParts[0].equals("after")) //$NON-NLS-1$
762
				String id = queryParts[1];
764
					additionsIndex++;
763
				additionsIndex = mgr.indexOf(id);
764
				if (additionsIndex != -1) {
765
					if (MenuUtil.QUERY_BEFORE.equals(modifier)) {
766
						// this is OK, the additionsIndex will either be correct
767
						// or -1 (which is a no-op)
768
					} else if (MenuUtil.QUERY_AFTER.equals(modifier)) {
769
						additionsIndex++;
770
					} else if (MenuUtil.QUERY_ENDOF.equals(modifier)) {
771
						// OK, this one is exciting
772
						IContributionItem[] items = mgr.getItems();
773
						for (additionsIndex++; additionsIndex < items.length; additionsIndex++) {
774
							if (items[additionsIndex].isGroupMarker()) {
775
								break;
776
							}
777
						}
778
					}
779
				}
765
			}
780
			}
766
		}
781
		}
767
782
(-)Eclipse UI/org/eclipse/ui/menus/MenuUtil.java (+27 lines)
Lines 39-44 Link Here
39
	public final static String TRIM_STATUS = "toolbar:org.eclipse.ui.trim.status"; //$NON-NLS-1$
39
	public final static String TRIM_STATUS = "toolbar:org.eclipse.ui.trim.status"; //$NON-NLS-1$
40
40
41
	/**
41
	/**
42
	 * Valid query attribute. Usage <b>menu:menu.id?before=contribution.id</b>.
43
	 * 
44
	 * @since 3.6
45
	 */
46
	public final static String QUERY_BEFORE = "before"; //$NON-NLS-1$
47
48
	/**
49
	 * Valid query attribute. Usage <b>menu:menu.id?after=contribution.id</b>.
50
	 * 
51
	 * @since 3.6
52
	 */
53
	public final static String QUERY_AFTER = "after"; //$NON-NLS-1$
54
55
	/**
56
	 * Valid query attribute. Usage <b>menu:menu.id?endof=contribution.id</b>.
57
	 * <p>
58
	 * This menu contribution will be placed at the end of the group defined by
59
	 * <b>contribution.id</b> (usually right in front of the next group marker
60
	 * or separator). Further contribution processing can still place other
61
	 * contributions after this one.
62
	 * </p>
63
	 * 
64
	 * @since 3.6
65
	 */
66
	public final static String QUERY_ENDOF = "endof"; //$NON-NLS-1$
67
68
	/**
42
	 * Contributions of targets to this location will be included with the show
69
	 * Contributions of targets to this location will be included with the show
43
	 * in menu.
70
	 * in menu.
44
	 * 
71
	 * 

Return to bug 193600