blob: 8c92608bd87594bbbe2d5f65ee0c6fad2e422aa6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.forms;
import java.util.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.forms.widgets.ScrolledPageBook;
/**
* This managed form part handles the 'details' portion of the
* 'master/details' block. It has a page book that manages pages
* of details registered for the current selection.
* <p>By default, details part accepts any number of pages.
* If dynamic page provider is registered, this number may
* be excessive. To avoid running out of steam (by creating
* a large number of pages with widgets on each), maximum
* number of pages can be set to some reasonable value (e.g. 10).
* When this number is reached, old pages (those created first)
* will be removed and disposed as new ones are added. If
* the disposed pages are needed again after that, they
* will be created again.
*
* @since 3.0
*/
public final class DetailsPart implements IFormPart, IPartSelectionListener {
private IManagedForm managedForm;
private ScrolledPageBook pageBook;
private IFormPart masterPart;
private IStructuredSelection currentSelection;
private Hashtable pages;
private IDetailsPageProvider pageProvider;
private int pageLimit=Integer.MAX_VALUE;
private static class PageBag {
private static int counter;
private int ticket;
private IDetailsPage page;
private boolean fixed;
public PageBag(IDetailsPage page, boolean fixed) {
this.page= page;
this.fixed = fixed;
this.ticket = ++counter;
}
public int getTicket() {
return ticket;
}
public IDetailsPage getPage() {
return page;
}
public void dispose() {
page.dispose();
page=null;
}
public boolean isDisposed() {
return page==null;
}
public boolean isFixed() {
return fixed;
}
public static int getCurrentTicket() {
return counter;
}
}
/**
* Creates a details part by wrapping the provided page book.
* @param mform the parent form
* @param pageBook the page book to wrap
*/
public DetailsPart(IManagedForm mform, ScrolledPageBook pageBook) {
this.pageBook = pageBook;
pages = new Hashtable();
initialize(mform);
}
/**
* Creates a new details part in the provided form by creating
* the page book.
* @param mform the parent form
* @param parent the composite to create the page book in
* @param style the style for the page book
*/
public DetailsPart(IManagedForm mform, Composite parent, int style) {
this(mform, mform.getToolkit().createPageBook(parent, style|SWT.V_SCROLL|SWT.H_SCROLL));
}
/**
* Registers the details page to be used for all the objects of
* the provided object class.
* @param objectClass an object of type 'java.lang.Class' to be used
* as a key for the provided page
* @param page the page to show for objects of the provided object class
*/
public void registerPage(Object objectClass, IDetailsPage page) {
registerPage(objectClass, page, true);
}
private void registerPage(Object objectClass, IDetailsPage page, boolean fixed) {
pages.put(objectClass, new PageBag(page, fixed));
page.initialize(managedForm);
}
/**
* Sets the dynamic page provider. The dynamic provider can return
* different pages for objects of the same class based on their state.
* @param provider the provider to use
*/
public void setPageProvider(IDetailsPageProvider provider) {
this.pageProvider = provider;
}
/**
* Commits the part by committing the current page.
* @param onSave <code>true</code> if commit is requested as a result
* of the 'save' action, <code>false</code> otherwise.
*/
public void commit(boolean onSave) {
IDetailsPage page = getCurrentPage();
if (page != null)
page.commit(onSave);
}
/**
* Returns the current page visible in the part.
* @return the current page
*/
public IDetailsPage getCurrentPage() {
Control control = pageBook.getCurrentPage();
if (control != null) {
Object data = control.getData();
if (data instanceof IDetailsPage)
return (IDetailsPage) data;
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.forms.IFormPart#dispose()
*/
public void dispose() {
for (Enumeration enm = pages.elements(); enm.hasMoreElements();) {
PageBag pageBag = (PageBag) enm.nextElement();
pageBag.dispose();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
*/
public void initialize(IManagedForm form) {
this.managedForm = form;
}
/**
* Tests if the currently visible page is dirty.
* @return <code>true</code> if the page is dirty, <code>false</code> otherwise.
*/
public boolean isDirty() {
IDetailsPage page = getCurrentPage();
if (page != null)
return page.isDirty();
return false;
}
/**
* Tests if the currently visible page is stale and needs refreshing.
* @return <code>true</code> if the page is stale, <code>false</code> otherwise.
*/
public boolean isStale() {
IDetailsPage page = getCurrentPage();
if (page != null)
return page.isStale();
return false;
}
/**
* Refreshes the current page.
*/
public void refresh() {
IDetailsPage page = getCurrentPage();
if (page != null)
page.refresh();
}
/**
* Sets the focus to the currently visible page.
*/
public void setFocus() {
IDetailsPage page = getCurrentPage();
if (page != null)
page.setFocus();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
*/
public boolean setFormInput(Object input) {
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.forms.IPartSelectionListener#selectionChanged(org.eclipse.ui.forms.IFormPart,
* org.eclipse.jface.viewers.ISelection)
*/
public void selectionChanged(IFormPart part, ISelection selection) {
this.masterPart = part;
if (currentSelection != null) {
}
if (selection instanceof IStructuredSelection)
currentSelection = (IStructuredSelection) selection;
else
currentSelection = null;
update();
}
private void update() {
Object key = null;
if (currentSelection != null) {
for (Iterator iter = currentSelection.iterator(); iter.hasNext();) {
Object obj = iter.next();
if (key == null)
key = getKey(obj);
else if (getKey(obj).equals(key) == false) {
key = null;
break;
}
}
}
showPage(key);
}
private Object getKey(Object object) {
if (pageProvider!=null) {
Object key = pageProvider.getPageKey(object);
if (key!=null)
return key;
}
return object.getClass();
}
private void showPage(final Object key) {
checkLimit();
final IDetailsPage oldPage = getCurrentPage();
if (key != null) {
PageBag pageBag = (PageBag)pages.get(key);
IDetailsPage page = pageBag!=null?pageBag.getPage():null;
if (page==null) {
// try to get the page dynamically from the provider
if (pageProvider!=null) {
page = pageProvider.getPage(key);
if (page!=null) {
registerPage(key, page, false);
}
}
}
if (page != null) {
final IDetailsPage fpage = page;
BusyIndicator.showWhile(pageBook.getDisplay(), new Runnable() {
public void run() {
if (!pageBook.hasPage(key)) {
Composite parent = pageBook.createPage(key);
fpage.createContents(parent);
parent.setData(fpage);
}
//commit the current page
if (oldPage!=null && oldPage.isDirty())
oldPage.commit(false);
//refresh the new page
if (fpage.isStale())
fpage.refresh();
fpage.selectionChanged(masterPart, currentSelection);
pageBook.showPage(key);
}
});
return;
}
}
// If we are switching from an old page to nothing,
// don't loose data
if (oldPage!=null && oldPage.isDirty())
oldPage.commit(false);
pageBook.showEmptyPage();
}
private void checkLimit() {
if (pages.size() <= getPageLimit()) return;
// overflow
int currentTicket = PageBag.getCurrentTicket();
int cutoffTicket = currentTicket - getPageLimit();
for (Enumeration enm=pages.keys(); enm.hasMoreElements();) {
Object key = enm.nextElement();
PageBag pageBag = (PageBag)pages.get(key);
if (pageBag.getTicket()<=cutoffTicket) {
// candidate - see if it is active and not fixed
if (!pageBag.isFixed() && !pageBag.getPage().equals(getCurrentPage())) {
// drop it
pageBag.dispose();
pages.remove(key);
pageBook.removePage(key, false);
}
}
}
}
/**
* Returns the maximum number of pages that should be
* maintained in this part. When an attempt is made to
* add more pages, old pages are removed and disposed
* based on the order of creation (the oldest pages
* are removed). The exception is made for the
* page that should otherwise be disposed but is
* currently active.
* @return maximum number of pages for this part
*/
public int getPageLimit() {
return pageLimit;
}
/**
* Sets the page limit for this part.
* @see #getPageLimit()
* @param pageLimit the maximum number of pages that
* should be maintained in this part.
*/
public void setPageLimit(int pageLimit) {
this.pageLimit = pageLimit;
checkLimit();
}
}