blob: dd38b1775b47b0dfb004ef6119560f91e549fb0e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Red Hat, Inc.
* All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
*
* @author Bob Brodt
******************************************************************************/
package org.eclipse.bpmn2.modeler.ui.editor;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.bpmn2.BaseElement;
import org.eclipse.bpmn2.di.BPMNDiagram;
import org.eclipse.bpmn2.modeler.core.adapters.ExtendedPropertiesProvider;
import org.eclipse.bpmn2.modeler.core.utils.ModelUtil;
import org.eclipse.bpmn2.modeler.ui.Bpmn2DiagramEditorInput;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabFolder2Listener;
import org.eclipse.swt.custom.CTabFolderEvent;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.part.MultiPageEditorSite;
/**
* This class implements a multi-page version of the BPMN2 Modeler (BPMN2Editor class).
* To revert back to the original, single-page version simply change the editor extension
* point in plugin.xml (see comments there).
*
* This is still in the experimental phase and currently only supports a single diagram
* per .bpmn file. An optional second page, which displays the XML source, can be created
* from the context menu. The source view is not yet synchronized to the design view and
* can only show the XML as of the last "Save" i.e. the current state of the file on disk,
* not the in-memory model. Design/Source view synchronization will be implemented in a
* future version, but direct editing of the XML will not be supported - it will remain
* "view only".
*
* Future versions will support multiple diagrams per .bpmn file with the ability to add
* and remove pages containing different diagram types. It should be possible for the user
* to create a single file that contains a mix of Process, Collaboration and Choreography
* diagrams. Whether or not these types of files are actually deployable and/or executable
* is another story ;)
*/
public class BPMN2MultiPageEditor extends MultiPageEditorPart implements IGotoMarker {
DesignEditor designEditor;
SourceViewer sourceViewer;
private CTabFolder tabFolder;
private int defaultTabHeight;
private List<BPMNDiagram> bpmnDiagrams = new ArrayList<BPMNDiagram>();
private List<PictogramElement[]> currentSelections = new ArrayList<PictogramElement[]>();
private int currentPageIndex = -1;
public BPMN2MultiPageEditor() {
super();
}
@Override
protected IEditorSite createSite(IEditorPart editor) {
if (editor instanceof DesignEditor)
return new DesignEditorSite(this, editor);
return new MultiPageEditorSite(this, editor);
}
@Override
public String getTitle() {
if (designEditor!=null)
return designEditor.getTitle();
return super.getTitle();
}
@Override
public String getPartName() {
if (designEditor!=null)
return designEditor.getPartName();
return super.getPartName();
}
@Override
public void setInput(IEditorInput input) {
super.setInput(input);
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
setTitleToolTip( getTitleToolTip() );
}
});
}
/**
* Method declared on IEditorPart.
*
* @param marker Marker to look for
*/
@Override
public void gotoMarker(IMarker marker) {
if (getActivePage() < 0) {
setActivePage(0);
}
IDE.gotoMarker(getEditor(getActivePage()), marker);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.MultiPageEditorPart#createPages()
*/
@Override
protected void createPages() {
tabFolder = (CTabFolder)getContainer();
tabFolder.addCTabFolder2Listener( new CTabFolder2Listener() {
@Override
public void close(CTabFolderEvent event) {
if (event.item.getData() == sourceViewer)
removeSourceViewer();
}
@Override
public void minimize(CTabFolderEvent event) {
}
@Override
public void maximize(CTabFolderEvent event) {
}
@Override
public void restore(CTabFolderEvent event) {
}
@Override
public void showList(CTabFolderEvent event) {
}
});
tabFolder.addSelectionListener( new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
int pageIndex = tabFolder.getSelectionIndex();
if (pageIndex>=0 && pageIndex<bpmnDiagrams.size() && designEditor!=null) {
BPMNDiagram bpmnDiagram = bpmnDiagrams.get(pageIndex);
designEditor.selectBpmnDiagram(bpmnDiagram);
}
}
});
// defer editor layout until all pages have been created
tabFolder.setLayoutDeferred(true);
createDesignEditor();
Display.getDefault().asyncExec(new Runnable() {
public void run() {
setActivePage(0);
designEditor.selectBpmnDiagram(bpmnDiagrams.get(0));
tabFolder.setLayoutDeferred(false);
tabFolder.setTabPosition(SWT.TOP);
updateTabs();
}
});
}
protected void createDesignEditor() {
if (designEditor==null) {
designEditor = new DesignEditor(this, this);
try {
int pageIndex = tabFolder.getItemCount();
if (sourceViewer!=null)
--pageIndex;
addPage(pageIndex, designEditor, BPMN2MultiPageEditor.this.getEditorInput());
defaultTabHeight = tabFolder.getTabHeight();
setPageText(pageIndex,ExtendedPropertiesProvider.getTextValue( designEditor.getBpmnDiagram() ));
defaultTabHeight = tabFolder.getTabHeight();
updateTabs();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
public DesignEditor getDesignEditor() {
return designEditor;
}
protected void addDesignPage(final BPMNDiagram bpmnDiagram) {
createDesignEditor();
Display.getDefault().asyncExec(new Runnable() {
public void run() {
try {
int pageIndex = tabFolder.getItemCount();
if (sourceViewer!=null)
--pageIndex;
Bpmn2DiagramEditorInput input = (Bpmn2DiagramEditorInput)designEditor.getEditorInput();
input.setBpmnDiagram(bpmnDiagram);
addPage(pageIndex, designEditor, input);
CTabItem oldItem = tabFolder.getItem(pageIndex-1);
CTabItem newItem = tabFolder.getItem(pageIndex);
newItem.setControl( oldItem.getControl() );
BaseElement bpmnElement = bpmnDiagram.getPlane().getBpmnElement();
String name = ModelUtil.getName(bpmnElement);
if (name==null)
name = ExtendedPropertiesProvider.getTextValue(bpmnDiagram);
setPageText(pageIndex,name);
setActivePage(pageIndex);
updateTabs();
}
catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void showDesignPage(final BPMNDiagram bpmnDiagram) {
final int pageIndex = bpmnDiagrams.indexOf(bpmnDiagram);
if (pageIndex>=0) {
if (getDesignEditor().getBpmnDiagram()!=bpmnDiagram) {
setActivePage(pageIndex);
}
}
else {
designEditor.showDesignPage(bpmnDiagram);
}
}
protected void removeDesignPage(final BPMNDiagram bpmnDiagram) {
final int pageIndex = bpmnDiagrams.indexOf(bpmnDiagram);
if (pageIndex>0) {
// go back to "Design" page - the only page that can't be removed
Display.getCurrent().asyncExec( new Runnable() {
@Override
public void run() {
setActivePage(0);
IEditorPart editor = getEditor(pageIndex);
if (editor instanceof DesignEditor) {
((DesignEditor)editor).deleteBpmnDiagram(bpmnDiagram);
}
// need to manage this ourselves so that the CTabFolder doesn't
// dispose our editor site (a child of the CTabItem.control)
tabFolder.getItem(pageIndex).setControl(null);
removePage(pageIndex);
tabFolder.getSelection().getControl().setVisible(true);
}
});
}
}
public int getDesignPageCount() {
int count = getPageCount();
if (sourceViewer!=null)
--count;
return count;
}
protected void createSourceViewer() {
if (sourceViewer==null) {
sourceViewer = new SourceViewer(this);
try {
int pageIndex = tabFolder.getItemCount();
FileEditorInput input = new FileEditorInput(designEditor.getModelFile());
addPage(pageIndex, sourceViewer, input);
tabFolder.getItem(pageIndex).setShowClose(true);
setPageText(pageIndex,Messages.BPMN2MultiPageEditor_Source_Tab);
setActivePage(pageIndex);
updateTabs();
}
catch (Exception e) {
e.printStackTrace();
if (sourceViewer!=null)
sourceViewer.dispose();
}
}
}
public SourceViewer getSourceViewer() {
return sourceViewer;
}
public void removeSourceViewer() {
// there will only be one source page and it will always be the last page in the tab folder
if (sourceViewer!=null) {
int pageIndex = tabFolder.getItemCount() - 1;
if (pageIndex>0) {
removePage(pageIndex);
sourceViewer = null;
}
}
}
public void addPage(int pageIndex, IEditorPart editor, IEditorInput input)
throws PartInitException {
super.addPage(pageIndex,editor,input);
if (editor instanceof DesignEditor) {
bpmnDiagrams.add(pageIndex,((DesignEditor)editor).getBpmnDiagram());
currentSelections.add(new PictogramElement[]{});
}
}
@Override
public void removePage(int pageIndex) {
Object page = tabFolder.getItem(pageIndex).getData();
super.removePage(pageIndex);
updateTabs();
if (page instanceof DesignEditor) {
bpmnDiagrams.remove(pageIndex);
currentSelections.remove(pageIndex);
}
}
@Override
protected void pageChange(final int newPageIndex) {
if (currentPageIndex>=0 && currentPageIndex<tabFolder.getItemCount()) {
IEditorPart editor = getEditor(currentPageIndex);
if (editor instanceof DesignEditor) {
final DesignEditor de = (DesignEditor) editor;
PictogramElement selections[] = de.getSelectedPictogramElements();
currentSelections.set(currentPageIndex, selections);
}
}
currentPageIndex = newPageIndex;
super.pageChange(newPageIndex);
IEditorPart editor = getEditor(newPageIndex);
if (editor instanceof DesignEditor) {
final DesignEditor de = (DesignEditor) editor;
BPMNDiagram bpmnDiagram = bpmnDiagrams.get(newPageIndex);
final PictogramElement selections[] = currentSelections.get(newPageIndex);
de.pageChange(bpmnDiagram);
if (selections!=null) {
Display.getCurrent().asyncExec(new Runnable() {
@Override
public void run() {
de.selectPictogramElements(selections);
}
});
}
}
}
public int getPageCount() {
return tabFolder.getItemCount();
}
public CTabItem getTabItem(int pageIndex) {
return tabFolder.getItem(pageIndex);
}
public BPMNDiagram getBpmnDiagram(int i) {
if (i>=0 && i<bpmnDiagrams.size()) {
return bpmnDiagrams.get(i);
}
return null;
}
private void updateTabs() {
if (!tabFolder.getLayoutDeferred()) {
if (tabFolder.getItemCount()==1) {
tabFolder.setTabHeight(0);
}
else
tabFolder.setTabHeight(defaultTabHeight);
}
tabFolder.layout();
}
public CTabFolder getTabFolder() {
return tabFolder;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void doSave(IProgressMonitor monitor) {
designEditor.doSave(monitor);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#doSaveAs()
*/
@Override
public void doSaveAs() {
IEditorPart activeEditor = getActiveEditor();
activeEditor.doSaveAs();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
*/
@Override
public boolean isSaveAsAllowed() {
/*
* Depending upon the active page in multipage editor, call the saveAsAllowed.
* It helps to see whether a particular editor allows 'save as' feature
*/
IEditorPart activeEditor = getActiveEditor();
if (activeEditor!=null)
return activeEditor.isSaveAsAllowed();
return false;
}
@Override
public void dispose() {
designEditor.dispose();
if (sourceViewer!=null)
sourceViewer.dispose();
}
@Override
public IEditorPart getActiveEditor() {
return super.getActiveEditor();
}
}