blob: e3435d05316fcab3f065adcbc6deac79fddf2c69 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials!
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Dan Rubel <dan_rubel@instantiations.com>
* - Fix for bug 11490 - define hidden view (placeholder for view) in plugin.xml
************************************************************************/
package org.eclipse.ui.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.internal.runtime.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IFolderLayout;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPlaceholderFolderLayout;
import org.eclipse.ui.IViewLayout;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.activities.WorkbenchActivityHelper;
import org.eclipse.ui.internal.registry.ActionSetRegistry;
import org.eclipse.ui.internal.registry.IActionSetDescriptor;
import org.eclipse.ui.internal.registry.IViewDescriptor;
/**
* This factory is used to define the initial layout of a part sash container.
* <p>
* Design notes: The design of <code>IPageLayout</code> is a reflection of
* three requirements:
* <ol>
* <li>A mechanism is required to define the initial layout for a page. </li>
* <li>The views and editors within a page will be persisted between
* sessions.</li>
* <li>The view and editor lifecycle for (1) and (2) should be identical.</li>
* </ol>
* </p>
* <p>
* In reflection of these requirements, the following strategy has been
* implemented for layout definition.
* <ol>
* <li>A view extension is added to the workbench registry for the view.
* This extension defines the extension id and extension class. </li>
* <li>A view is added to a page by invoking one of the add methods
* in <code>IPageLayout</code>. The type of view is passed as an
* extension id, rather than a handle. The page layout will map
* the extension id to a view class, create an instance of the class,
* and then add the view to the page.</li>
* </ol>
* </p>
*/
public class PageLayout implements IPageLayout {
private ArrayList actionSets = new ArrayList(3);
private IPerspectiveDescriptor descriptor;
private LayoutPart editorFolder;
private boolean editorVisible = true;
private boolean fixed;
private ArrayList fastViews = new ArrayList(3);
private Map mapIDtoFolder = new HashMap(10);
private Map mapIDtoPart = new HashMap(10);
private Map mapIDtoViewLayoutRec = new HashMap(10);
private ArrayList newWizardActionIds = new ArrayList(3);
private ArrayList perspectiveActionIds = new ArrayList(3);
private ViewSashContainer rootLayoutContainer;
private ArrayList showInPartIds = new ArrayList(3);
private ArrayList showViewActionIds = new ArrayList(3);
private ViewFactory viewFactory;
/**
* Constructs a new PageLayout for other purposes.
*/
public PageLayout() {
//no-op
}
/**
* Constructs a new PageLayout for the normal case of creating a new
* perspective.
*/
public PageLayout(
ViewSashContainer container,
ViewFactory viewFactory,
LayoutPart editorFolder,
IPerspectiveDescriptor descriptor) {
super();
this.viewFactory = viewFactory;
this.rootLayoutContainer = container;
this.editorFolder = editorFolder;
this.descriptor = descriptor;
prefill();
}
/**
* Adds the editor to a layout.
*/
private void addEditorArea() {
try {
// Create the part.
LayoutPart newPart = createView(ID_EDITOR_AREA);
if (newPart == null)
// this should never happen as long as newID is the editor ID.
return;
setRefPart(ID_EDITOR_AREA, newPart);
// Add it to the layout.
rootLayoutContainer.add(newPart);
} catch (PartInitException e) {
WorkbenchPlugin.log(e.getMessage());
}
}
/**
* Adds an action set to the page.
*
* @param actionSetID Identifies the action set extension to use. It must
* exist within the workbench registry.
*/
public void addActionSet(String actionSetID) {
if (!actionSets.contains(actionSetID)) {
actionSets.add(actionSetID);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#addFastView(java.lang.String)
*/
public void addFastView(String id) {
addFastView(id, INVALID_RATIO);
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#addFastView(java.lang.String, float)
*/
public void addFastView(String id, float ratio) {
if (checkPartInLayout(id))
return;
if (id != null) {
try {
IViewReference ref = viewFactory.createView(id);
fastViews.add(ref);
// force creation of the view layout rec
ViewLayoutRec rec = getViewLayoutRec(id, true);
// remember the ratio, if valid
if (ratio >= IPageLayout.RATIO_MIN
&& ratio <= IPageLayout.RATIO_MAX) {
rec.fastViewWidthRatio = ratio;
}
} catch (PartInitException e) {
WorkbenchPlugin.log(e.getMessage());
}
}
}
/**
* Returns the view layout record for the given view id, or null if not found.
* If create is true, the record is created if it doesn't already exist.
*
* @since 3.0
*/
ViewLayoutRec getViewLayoutRec(String id, boolean create) {
Assert.isNotNull(getRefPart(id));
ViewLayoutRec rec = (ViewLayoutRec) mapIDtoViewLayoutRec.get(id);
if (rec == null && create) {
rec = new ViewLayoutRec();
// set up the view layout appropriately if the page layout is fixed
if (isFixed()) {
rec.isCloseable = false;
rec.isMoveable = false;
}
mapIDtoViewLayoutRec.put(id, rec);
}
return rec;
}
/**
* Adds a creation wizard to the File New menu.
* The id must name a new wizard extension contributed to the
* workbench's extension point (named <code>"org.eclipse.ui.newWizards"</code>).
*
* @param id the wizard id
*/
public void addNewWizardShortcut(String id) {
if (!newWizardActionIds.contains(id)) {
newWizardActionIds.add(id);
}
}
/**
* Add the layout part to the page's layout
*/
private void addPart(
LayoutPart newPart,
String partId,
int relationship,
float ratio,
String refId) {
setRefPart(partId, newPart);
// If the referenced part is inside a folder,
// then use the folder as the reference part.
LayoutPart refPart = getFolderPart(refId);
if (refPart == null)
refPart = getRefPart(refId);
// Add it to the layout.
if (refPart != null) {
ratio = normalizeRatio(ratio);
rootLayoutContainer.add(
newPart,
getPartSashConst(relationship),
ratio,
refPart);
} else {
WorkbenchPlugin.log(WorkbenchMessages.format("PageLayout.missingRefPart", new Object[] { refId })); //$NON-NLS-1$
rootLayoutContainer.add(newPart);
}
}
/**
* Adds a perspective shortcut to the Perspective menu.
* The id must name a perspective extension contributed to the
* workbench's extension point (named <code>"org.eclipse.ui.perspectives"</code>).
*
* @param id the perspective id
*/
public void addPerspectiveShortcut(String id) {
if (!perspectiveActionIds.contains(id)) {
perspectiveActionIds.add(id);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#addPlaceholder(java.lang.String, int, float, java.lang.String)
*/
public void addPlaceholder(
String viewId,
int relationship,
float ratio,
String refId) {
if (checkPartInLayout(viewId))
return;
// Create the placeholder.
PartPlaceholder newPart = new PartPlaceholder(viewId);
addPart(newPart, viewId, relationship, ratio, refId);
// force creation of the view layout rec
getViewLayoutRec(viewId, true);
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#addShowInPart(java.lang.String)
*/
public void addShowInPart(String id) {
if (!showInPartIds.contains(id)) {
showInPartIds.add(id);
}
}
/**
* Adds a view to the Show View menu. The id must name a view extension
* contributed to the workbench's extension point (named <code>"org.eclipse.ui.views"</code>).
*
* @param id the view id
*/
public void addShowViewShortcut(String id) {
if (!showViewActionIds.contains(id)) {
showViewActionIds.add(id);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#addView(java.lang.String, int, float, java.lang.String)
*/
public void addView(
String viewId,
int relationship,
float ratio,
String refId) {
addView(viewId, relationship, ratio, refId, false, true);
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#addView(java.lang.String, int, float, java.lang.String)
*/
private void addView(
String viewId,
int relationship,
float ratio,
String refId,
boolean standalone,
boolean showTitle) {
if (checkPartInLayout(viewId))
return;
try {
// Create the part.
LayoutPart newPart = createView(viewId);
if (newPart == null) {
addPlaceholder(viewId, relationship, ratio, refId);
LayoutHelper.addViewActivator(this, viewId);
} else {
ViewStack newFolder = new ViewStack(rootLayoutContainer.page);
newFolder.setStandalone(standalone, showTitle);
newFolder.add(newPart);
setFolderPart(viewId, newFolder);
addPart(newFolder, viewId, relationship, ratio, refId);
// force creation of the view layout rec
getViewLayoutRec(viewId, true);
}
} catch (PartInitException e) {
WorkbenchPlugin.log(e.getMessage());
}
}
/**
* Verify that the part is already present in the layout
* and cannot be added again. Log a warning message.
*/
/*package*/
boolean checkPartInLayout(String partId) {
if (getRefPart(partId) != null) {
WorkbenchPlugin.log(WorkbenchMessages.format("PageLayout.duplicateRefPart", new Object[] { partId })); //$NON-NLS-1$
return true;
}
for (int i = 0; i < fastViews.size(); i++) {
if (((IViewReference) fastViews.get(i)).getId().equals(partId))
return true;
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#createFolder(java.lang.String, int, float, java.lang.String)
*/
public IFolderLayout createFolder(
String folderId,
int relationship,
float ratio,
String refId) {
if (checkPartInLayout(folderId))
return new FolderLayout(
this,
(ViewStack) getRefPart(folderId),
viewFactory);
// Create the folder.
ViewStack folder = new ViewStack(rootLayoutContainer.page);
folder.setID(folderId);
addPart(folder, folderId, relationship, ratio, refId);
// Create a wrapper.
return new FolderLayout(this, folder, viewFactory);
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#createPlaceholderFolder(java.lang.String, int, float, java.lang.String)
*/
public IPlaceholderFolderLayout createPlaceholderFolder(
String folderId,
int relationship,
float ratio,
String refId) {
if (checkPartInLayout(folderId))
return new PlaceholderFolderLayout(
this,
(ContainerPlaceholder) getRefPart(folderId));
// Create the folder.
ContainerPlaceholder folder = new ContainerPlaceholder(null);
folder.setContainer(rootLayoutContainer);
folder.setRealContainer(new ViewStack(rootLayoutContainer.page));
folder.setID(folderId);
addPart(folder, folderId, relationship, ratio, refId);
// Create a wrapper.
return new PlaceholderFolderLayout(this, folder);
}
/**
* Create a new <code>LayoutPart</code>.
*
* @param partID the id of the part to create.
* @return the <code>LayoutPart</code>, or <code>null</code> if it should not be
* created because of activity filtering.
* @throws PartInitException thrown if there is a problem creating the part.
*/
private LayoutPart createView(String partID)
throws PartInitException {
if (partID.equals(ID_EDITOR_AREA)) {
return editorFolder;
} else {
IViewDescriptor viewDescriptor =
viewFactory.getViewRegistry().find(partID);
if (WorkbenchActivityHelper.filterItem(viewDescriptor))
return null;
return LayoutHelper.createView(getViewFactory(), partID);
}
}
/**
* @return the action set list for the page. This is <code>List</code> of
* <code>String</code>s.
*/
public ArrayList getActionSets() {
return actionSets;
}
/**
* @return Returns the <code>IPerspectiveDescriptor</code> that is driving
* the creation of this <code>PageLayout</code>.
*/
public IPerspectiveDescriptor getDescriptor() {
return descriptor;
}
/**
* @return an identifier for the editor area. The editor area is
* automatically added to each layout before any other part. It should be
* used as a reference part for other views.
*/
public String getEditorArea() {
return ID_EDITOR_AREA;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#getEditorReuseThreshold()
*/
public int getEditorReuseThreshold() {
return -1;
}
/**
* @return <code>ArrayList</code>
*/
public ArrayList getFastViews() {
return fastViews;
}
/**
* @return the folder part containing the given view ID or <code>null</code>
* if none (i.e. part of the page layout instead of a folder layout).
*/
private ViewStack getFolderPart(String viewId) {
return (ViewStack) mapIDtoFolder.get(viewId);
}
/**
* @return the new wizard actions the page. This is <code>List</code> of
* <code>String</code>s.
*/
public ArrayList getNewWizardActionIds() {
return newWizardActionIds;
}
/**
* @return the part sash container const for a layout value.
*/
private int getPartSashConst(int nRelationship) {
return nRelationship;
}
/**
* @return the perspective actions. This is <code>List</code> of
* <code>String</code>s.
*/
public ArrayList getPerspectiveActionIds() {
return perspectiveActionIds;
}
/**
* @return the part for a given ID.
*/
/*package*/
LayoutPart getRefPart(String partID) {
return (LayoutPart) mapIDtoPart.get(partID);
}
/**
* @return the top level layout container.
*/
public ViewSashContainer getRootLayoutContainer() {
return rootLayoutContainer;
}
/**
* @return the ids of the parts to list in the Show In... prompter. This is
* a <code>List</code> of <code>String</code>s.
*/
public ArrayList getShowInPartIds() {
return showInPartIds;
}
/**
* @return the ids of the views to list in the Show View shortcuts. This is
* a <code>List</code> of <code>String</code>s.
*/
public ArrayList getShowViewActionIds() {
return showViewActionIds;
}
/**
* @return the <code>ViewFactory</code> for this <code>PageLayout</code>.
* @since 3.0
*/
/* package */
ViewFactory getViewFactory() {
return viewFactory;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#isEditorAreaVisible()
*/
public boolean isEditorAreaVisible() {
return editorVisible;
}
/**
* Trim the ratio so that direct manipulation of parts is easy.
*
* @param in the initial ratio.
* @return the normalized ratio.
*/
private float normalizeRatio(float in) {
if (in < RATIO_MIN)
in = RATIO_MIN;
if (in > RATIO_MAX)
in = RATIO_MAX;
return in;
}
/**
* Prefill the layout with required parts.
*/
private void prefill() {
addEditorArea();
// Add default action sets.
ActionSetRegistry reg =
WorkbenchPlugin.getDefault().getActionSetRegistry();
IActionSetDescriptor[] array = reg.getActionSets();
int count = array.length;
for (int nX = 0; nX < count; nX++) {
IActionSetDescriptor desc = array[nX];
if (desc.isInitiallyVisible())
addActionSet(desc.getId());
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#setEditorAreaVisible(boolean)
*/
public void setEditorAreaVisible(boolean showEditorArea) {
editorVisible = showEditorArea;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#setEditorReuseThreshold(int)
*/
public void setEditorReuseThreshold(int openEditors) {
//no-op
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#setFixed(boolean)
*/
public void setFixed(boolean fixed) {
this.fixed = fixed;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#getFixed()
*/
public boolean isFixed() {
return fixed;
}
/**
* Map the folder part containing the given view ID.
*
* @param viewId the part ID.
* @param container the <code>ContainerPlaceholder</code>.
*/
/*package*/
void setFolderPart(String viewId, ContainerPlaceholder container) {
LayoutPart tabFolder = container.getRealContainer();
mapIDtoFolder.put(viewId, tabFolder);
}
/**
* Map the folder part containing the given view ID.
*
* @param viewId the part ID.
* @param folder the <code>ViewStack</code>.
*/
/*package*/
void setFolderPart(String viewId, ViewStack folder) {
mapIDtoFolder.put(viewId, folder);
}
/**
* Map an ID to a part.
*
* @param partId the part ID.
* @param part the <code>LayoutPart</code>.
*/
/*package*/
void setRefPart(String partID, LayoutPart part) {
mapIDtoPart.put(partID, part);
}
// stackPart(Layoutpart, String, String) added by dan_rubel@instantiations.com
/**
* Stack a part on top of another.
*
* @param newPart the new part.
* @param viewId the view ID.
* @param refId the reference ID.
*/
private void stackPart(LayoutPart newPart, String viewId, String refId) {
setRefPart(viewId, newPart);
// force creation of the view layout rec
getViewLayoutRec(viewId, true);
// If ref part is in a folder than just add the
// new view to that folder.
ViewStack folder = getFolderPart(refId);
if (folder != null) {
folder.add(newPart);
setFolderPart(viewId, folder);
return;
}
// If the ref part is in the page layout then create
// a new folder and add the new view.
LayoutPart refPart = getRefPart(refId);
if (refPart != null) {
ViewStack newFolder = new ViewStack(rootLayoutContainer.page);
rootLayoutContainer.replace(refPart, newFolder);
newFolder.add(refPart);
newFolder.add(newPart);
setFolderPart(refId, newFolder);
setFolderPart(viewId, newFolder);
return;
}
// If ref part is not found then just do add.
WorkbenchPlugin.log(WorkbenchMessages.format("PageLayout.missingRefPart", new Object[] { refId })); //$NON-NLS-1$
rootLayoutContainer.add(newPart);
}
// stackPlaceholder(String, String) added by dan_rubel@instantiations.com
/**
* Stack a placeholder on top of another.
*
* @param viewId the view ID.
* @param refId the reference ID.
*/
public void stackPlaceholder(String viewId, String refId) {
if (checkPartInLayout(viewId))
return;
// Create the placeholder.
PartPlaceholder newPart = new PartPlaceholder(viewId);
LayoutPart refPart = getRefPart(refId);
if (refPart != null) {
newPart.setContainer(refPart.getContainer());
}
stackPart(newPart, viewId, refId);
}
// stackView(String, String) modified by dan_rubel@instantiations.com
/**
* Stack one view on top of another.
*
* @param viewId the view ID.
* @param refId the reference ID.
*/
public void stackView(String viewId, String refId) {
if (checkPartInLayout(viewId))
return;
// Create the new part.
try {
LayoutPart newPart = createView(viewId);
if (newPart == null) {
stackPlaceholder(viewId, refId);
LayoutHelper.addViewActivator(this, viewId);
}
else
stackPart(newPart, viewId, refId);
} catch (PartInitException e) {
WorkbenchPlugin.log(e.getMessage());
}
}
/**
* Converts SWT position constants into layout position constants.
*
* @param swtConstant one of SWT.TOP, SWT.BOTTOM, SWT.LEFT, or SWT.RIGHT
* @return one of IPageLayout.TOP, IPageLayout.BOTTOM, IPageLayout.LEFT, IPageLayout.RIGHT, or -1 indicating an
* invalid input
*
* @since 3.0
*/
public static int swtConstantToLayoutPosition(int swtConstant) {
switch(swtConstant) {
case SWT.TOP: return IPageLayout.TOP;
case SWT.BOTTOM: return IPageLayout.BOTTOM;
case SWT.RIGHT: return IPageLayout.RIGHT;
case SWT.LEFT: return IPageLayout.LEFT;
}
return -1;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#addStandaloneView(java.lang.String, boolean, int, float, java.lang.String)
* @since 3.0
*/
public void addStandaloneView(String viewId, boolean showTitle, int relationship, float ratio, String refId) {
addView(viewId, relationship, ratio, refId, true, showTitle);
ViewLayoutRec rec = getViewLayoutRec(viewId, true);
rec.isStandalone = true;
rec.showTitle = showTitle;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPageLayout#getViewLayout(java.lang.String)
* @since 3.0
*/
public IViewLayout getViewLayout(String viewId) {
ViewLayoutRec rec = getViewLayoutRec(viewId, true);
if (rec == null) {
return null;
}
return new ViewLayout(this, rec);
}
/**
* @since 3.0
*/
public Map getIDtoViewLayoutRecMap() {
return mapIDtoViewLayoutRec;
}
}