# Sirius – Provide tab-bar extensions

## Goals

The top area of all Sirius diagram editors is filled with the tab-bar, which provides access to many operations on diagrams and their elements. The content of the tab-bar will depends on whether the current selection is the diagram itself (i.e. no element is selected) or one or several diagram elements.

When the diagram itself (and not a specific element) is selected, the tab-bar contains the following buttons:

The tab-bar contains a different set of actions when at least one element is selected:

Sirius allows to define new operations and to add them at the end of the tab-bar.

## Add a new tab-bar item

Sirius asks the workbench’s `IMenuService` service to populate the tab-bar with provided contributions. This service leans on the `org.eclipse.ui.menus` extension point.

Sirius defines the tab-bar as a toolbar with the id org.eclipse.sirius.diagram.ui.tabbar.
Menu contributions to tab-bar must be declared with the toolbar scheme and the Sirius tab-bar id.

You can use the insertion point part of the locationUri to organize your contributions. Sirius defines the additions group, this group and all contribution will be placed at the end of the tab-bar.

You can choose when to make your contribution visible (see the example) with the visibleWhen element and some property testers: show a menu for diagram selection or only on some diagram with a specific description, ... Sirius defines several property testers like org.eclipse.sirius.diagram.ui.isDDiagram and org.eclipse.sirius.diagram.ui.isDDiagramElement, but you can define your own domain specific testers.

### Provide your tab-bar items using the `org.eclipse.ui.menus` extension point

Please refer to this extension point documentation for more details.

### Provide your operation using the `org.eclipse.ui.commands` extension point

Please refer to this extension point documentation for more details.

### Example

The following example defines a new push button for the tab-bar. The contribution will only be visible when an element is selected on diagram which correspond to a specific description.

You have to add your menu contribution in the plugin.xml file.

The first step is to create the menu contribution to reference an existing command:

     <extension point="org.eclipse.ui.menus">
        <menuContribution
              allPopups="false"
              locationURI="toolbar:org.eclipse.sirius.diagram.ui.tabbar?after=additions">
            <command
                  commandId="org.example.myproject.command.id"
                  icon="images/action.gif"
                  id="org.eclipse.sirius.diagram.tabbar.test.action.on.diagramelement"
                  label="Action on DDiagramElement"
                  tooltip="Action on DDiagramElement">
       <!-- [...] a visible when element will be added later to control the visibility -->
            </command>
        </menuContribution>
     </extension>

The locationURI references the Sirius tabbar with `toolbar:org.eclipse.sirius.diagram.ui.tabbar` and and insertion point `?after=additions`.
The command element references an existing commandID, and defines its own menu id (can be used as insertion/organization point in other contributions locationURI), its label and tooltip. If a key binding exists on the referenced command, the workbench will add it to the tooltip.

Then you can control the visibility of your contribution with a visible when element.

       <!-- [...] continued from the example above -->
       <visibleWhen checkEnabled="false">
          <and>
             <with variable="activeEditorId">
                <equals value="org.eclipse.sirius.diagram.ui.part.SiriusDiagramEditorID"/>
             </with>
             <with variable="activeEditor">
                 <test property="org.eclipse.sirius.sample.tabbar.isConcernedEditor"/>
             </with>
             <with variable="selection">
                <iterate ifEmpty="false" operator="or">
                   <and>
                      <test property="org.eclipse.sirius.diagram.ui.isDDiagramElement"/>
                      <test property="org.eclipse.sirius.sample.tabbar.shouldActivateTabbarExtension"/>
                   </and>
                </iterate>
             </with>
          </and>
       </visibleWhen>
    </command>

The sample contribution declares four conditions to enable its visibility:

- it checks that the active editor is a Sirius Diagram Editor using the workbench variable activeEditorId and the Sirius Diagram Editor id.
- it asks a property tester if the active editor should trigger the contribution visibility (see next section for the sample property tester explanation)
- it iterates over the selection with a “OR” operator between elements and a false result for empty selection:
  - it uses the Sirius predefined property tester to check that a selected element is a diagram element.
  - it asks a property tester if the current element should trigger the contribution visibility (see next section for the sample property tester explanation)

Property testers are defined in the plug-in.xml file. They define namespace, some properties and the type of elements they can test.

    <extension point="org.eclipse.core.expressions.propertyTesters">
       <propertyTester
             class="org.eclipse.sirius.sample.tabbar.SampleTestTabbarExtensionActivationPropertyTester"
             id="org.eclipse.sirius.sample.tabbar.ActiveExtensionTester"
             namespace="org.eclipse.sirius.sample.tabbar"
             properties="shouldActivateTabbarExtension"
             type="org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart">
       </propertyTester>
       <propertyTester
             class="org.eclipse.sirius.sample.tabbar.SampleTestTabbarExtensionActivationPropertyTester"
             id="org.eclipse.sirius.sample.tabbar.ConcernEditorTester"
             namespace="org.eclipse.sirius.sample.tabbar"
             properties="isConcernedEditor"
             type="org.eclipse.ui.IEditorPart">
       </propertyTester>
    </extension>

Here is the implementation of the corresponding property tester. The isConcerned property will lead to check the `DiagramDescription` id of the current editor. The shouldActivateTabbarExtension will lead to check a property of the selected domain object.

    package org.eclipse.sirius.sample.tabbar;

    import org.eclipse.core.expressions.PropertyTester;
    import org.eclipse.emf.ecore.EClass;
    import org.eclipse.emf.ecore.EObject;

    import org.eclipse.sirius.diagram.DDiagram;
    import org.eclipse.sirius.diagram.DRepresentation;
    import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart;
    import org.eclipse.sirius.ui.business.api.dialect.DialectEditor;

    /**
     * Sample property tester.
     *
     * @author mporhel
     */
    public class SampleTestTabbarExtensionActivationPropertyTester extends PropertyTester {
        public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
            if ("isConcernedEditor".equals(property)) {
                // called in a with activeEditor element
                if (receiver instanceof DialectEditor) {
                    DRepresentation activeRepresentation = ((DialectEditor) receiver).getRepresentation();
                    if (activeRepresentation instanceof DDiagram) {
                        // the id property in the VSM editor : name in the meta
                        // model.
                        return "diagram.description.sample.id".equals(((DDiagram) activeRepresentation).getDescription().getName());
                    }
                }
            } else if ("shouldActivateTabbarExtension".equals(property)) {
                if (receiver instanceof IDiagramElementEditPart) {
                    EObject domainElement = ((IDiagramElementEditPart) receiver).resolveTargetSemanticElement();
                    // sample condition
                    return domainElement instanceof EClass && ((EClass) domainElement).isAbstract();
                }
            }
            return false;
        }
    }

The a new button is added to the tab-bar only when the selection contains an abstract `EClass` is selected and the active editor corresponds to the expected description id:

## Contribute a full customized tab-bar

Sirius lets the possibility to contribute an entire customized tab-bar. To do so, the developer has to contribute an `org.eclipse.sirius.diagram.ui.tools.api.editor.tabbar.ITabbarContributor` to the `org.eclipse.sirius.diagram.ui.tabbarContributor` extension-point. An abstract implementation of `ITabbarContributor` exists: `org.eclipse.sirius.diagram.ui.tools.api.editor.tabbar.AbstractTabbarContributor`. This implementation contains protected methods that allows creating one or several contributions of the default Sirius tab-bar. See the Tab-bar section in user manual for more details about each items.

Here is an example of `ITabbarContributor` implementation. This sample provides two different tab-bar whether the selection is done on a diagram element (a Node or a Container for instance) or on the diagram (no specific selection). If the selection is done on an Edge, the tab-bar will be empty since the sample doesn’t return any contributionItem.

    public class TestTabbarExt extends AbstractTabbarContributor {

        private ArrayList<IContributionItem> diagramContributionItems;

        private ArrayList<IContributionItem> diagramElementContributionItems;

        @Override
        public List<IContributionItem> getContributionItems(ISelection selection, IDiagramWorkbenchPart part, ToolBarManager manager) {
            List<IContributionItem> contributionItems = new ArrayList<IContributionItem>();
            if (selection instanceof IStructuredSelection) {
                Object firstElement = ((IStructuredSelection) selection).getFirstElement();
                if (firstElement instanceof AbstractDDiagramEditPart) {
                    contributionItems = getDiagramContributionItem(part, manager);
                } else if (firstElement instanceof IAbstractDiagramNodeEditPart) {
                    contributionItems = getDiagramElementContributionItem(part, manager);
                }
            }
            return contributionItems;
        }

        @Override
        public boolean accept(ISelection selection) {
            boolean accept = false;
            if (selection == null) {
                accept = true;
            } else if (selection instanceof IStructuredSelection) {
                Object firstElement = ((IStructuredSelection) selection).getFirstElement();
                if (firstElement instanceof AbstractDDiagramEditPart || firstElement instanceof IAbstractDiagramNodeEditPart) {
                    accept = true;
                }
            }
            return accept;
        }

        @Override
        public List<IContributionItem> getContributionItems(IDiagramWorkbenchPart part, ToolBarManager manager) {
            return getDiagramContributionItem(part, manager);
        }

        private List<IContributionItem> getDiagramContributionItem(IDiagramWorkbenchPart part, ToolBarManager manager) {
            if (diagramContributionItems == null) {
                diagramContributionItems = new ArrayList<IContributionItem>();
                diagramContributionItems.add(createArrangeMenuManager(part));
                diagramContributionItems.add(createSelectMenuManager());
                diagramContributionItems.add(createLayerContribution(part, manager));
                diagramContributionItems.add(createZoomInContribution(part));
                diagramContributionItems.add(createZoomOutContribution(part));
                diagramContributionItems.add(createZoomContribution(part));
                diagramContributionItems.add(createSelectPinnedElementsContribution(part));
                diagramContributionItems.add(createSelectHiddenElementsContribution(part));
                diagramContributionItems.add(createFilterContribution(part, manager));
                diagramContributionItems.add(createPasteFormatContribution(part));
                diagramContributionItems.add(createRefreshContribution());
                diagramContributionItems.add(createSaveAsImageContributionItem());
                diagramContributionItems.add(createModeMenuManagerContributionItem(part));
                diagramContributionItems.add(createCopyAppearancePropertiesContribution(part));
                diagramContributionItems.add(createCopyFormatContribution(part));
            }
            return diagramContributionItems;
        }

        private List<IContributionItem> getDiagramElementContributionItem(IDiagramWorkbenchPart part, ToolBarManager manager) {
            if (diagramElementContributionItems == null) {
                diagramElementContributionItems = new ArrayList<IContributionItem>();
                diagramElementContributionItems.add(createArrangeMenuManager(part));
                diagramElementContributionItems.add(createAlignMenuManager());
                diagramElementContributionItems.add(createHideElementLabelContribution(part));
                diagramElementContributionItems.add(createHideElementContribution(part));
                diagramElementContributionItems.add(createDeleteFromDiagramContribution(part));
                diagramElementContributionItems.add(createDeleteFromModelContribution(part));
                IContributionItem pinElementContributionItem = createPinElementContribution(part);
                diagramElementContributionItems.add(pinElementContributionItem);
                diagramElementContributionItems.add(createUnPinElementContribution(part, pinElementContributionItem));
                diagramElementContributionItems.add(createFontColorContribution(part));
                diagramElementContributionItems.add(createPasteFormatContribution(part));
                diagramElementContributionItems.add(createBoldFontStyleContribution(part));
                diagramElementContributionItems.add(createItalicFontStyleContribution(part));
                diagramElementContributionItems.add(createFontDialogContribution(part));
                diagramElementContributionItems.add(createFillColorContribution(part));
                diagramElementContributionItems.add(createLineColorPropertyContribution(part));
                diagramElementContributionItems.add(createResetStylePropertyContribution(part));
                diagramElementContributionItems.add(createSetStyleToWorkspaceImageContribution(part));
                diagramElementContributionItems.add(createSaveAsImageContributionItem());
                diagramElementContributionItems.add(createDistributeContribution());
                diagramElementContributionItems.add(createModeMenuManagerContributionItem(part));
                diagramElementContributionItems.add(createRouterContribution());
                diagramElementContributionItems.add(createCopyAppearancePropertiesContribution(part));
                diagramElementContributionItems.add(createCopyFormatContribution(part));
                diagramElementContributionItems.add(createSizeBothContribution(part));
                diagramElementContributionItems.add(createAutoSizeContribution(part));
            }
            return diagramElementContributionItems;
        }
    }