net.enilink.commons.ui.editor.EditorPartBook.java Source code

Java tutorial

Introduction

Here is the source code for net.enilink.commons.ui.editor.EditorPartBook.java

Source

/*******************************************************************************
 * Copyright (c) 2009 Fraunhofer IWU 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:
 *     Fraunhofer IWU - initial API and implementation
 *******************************************************************************/
package net.enilink.commons.ui.editor;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.
 */
public class EditorPartBook extends AbstractEditorPart implements ISelectionChangedListener {
    private Composite pageBook;

    private IStructuredSelection lastSelection, currentSelection;
    private Map<Object, PartBag> parts;
    private IEditorPartProvider partProvider;
    private int partLimit = Integer.MAX_VALUE;
    private boolean commitIfDirty;
    private int style = SWT.NONE;

    private static class PartBag {
        private static int counter;
        private int ticket;
        private IEditorPart part;
        private boolean fixed;

        public PartBag(IEditorPart part, boolean fixed) {
            this.part = part;
            this.fixed = fixed;
            this.ticket = ++counter;
        }

        public int getTicket() {
            return ticket;
        }

        public IEditorPart getPart() {
            return part;
        }

        public void dispose() {
            part.dispose();
            part = null;
        }

        public boolean isDisposed() {
            return part == 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 scrolledPageBook
     *            the page book to wrap
     */
    public EditorPartBook(boolean commitIfDirty) {
        this(commitIfDirty, SWT.V_SCROLL | SWT.H_SCROLL);
    }

    public EditorPartBook(boolean commitIfDirty, int style) {
        this.commitIfDirty = commitIfDirty;
        this.style = style;
        parts = new HashMap<Object, PartBag>();
    }

    /**
     * Registers the details part 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 registerPart(Class<?> objectClass, IEditorPart page) {
        registerPart(objectClass, page, true);
    }

    private void registerPart(Object objectClass, IEditorPart page, boolean fixed) {
        parts.put(objectClass, new PartBag(page, fixed));
        page.initialize(getForm());
    }

    /**
     * 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 setPartProvider(IEditorPartProvider provider) {
        this.partProvider = 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) {
        IEditorPart page = getCurrentPart();
        if (page != null) {
            commitPart(currentSelection, page, false);
        }
    }

    /**
     * Returns the current page visible in the part.
     * 
     * @return the current page
     */
    public IEditorPart getCurrentPart() {
        Control control = pageBook instanceof ScrolledPageBook ? ((ScrolledPageBook) pageBook).getCurrentPage()
                : ((PageBook) pageBook).getCurrentPage();
        if (control != null) {
            Object data = control.getData();
            if (data instanceof IEditorPart)
                return (IEditorPart) data;
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.forms.IFormPart#dispose()
     */
    public void dispose() {
        for (PartBag pageBag : parts.values()) {
            pageBag.dispose();
        }
    }

    /**
     * Tests if the currently visible page is dirty.
     * 
     * @return <code>true</code> if the page is dirty, <code>false</code>
     *         otherwise.
     */
    public boolean isDirty() {
        IEditorPart page = getCurrentPart();
        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() {
        IEditorPart page = getCurrentPart();
        if (page != null) {
            return page.isStale();
        }
        return false;
    }

    /**
     * Refreshes the current page.
     */
    public void refresh() {
        IEditorPart page = getCurrentPart();
        if (page != null) {
            page.refresh();
        }
    }

    /**
     * Sets the focus to the currently visible page.
     */
    public boolean setFocus() {
        IEditorPart page = getCurrentPart();
        if (page != null) {
            page.setFocus();
            return true;
        }
        return false;
    }

    public Control getControl() {
        return pageBook;
    }

    public boolean setEditorInput(Object input) {
        return false;
    }

    public void selectionChanged(SelectionChangedEvent event) {
        lastSelection = currentSelection;

        selectionChanged(event.getSelection());
    }

    public void selectionChanged(ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            currentSelection = (IStructuredSelection) selection;
        } else {
            currentSelection = null;
        }
        update();
    }

    private void update() {
        Object key = null;
        if (currentSelection != null && !currentSelection.isEmpty()) {
            Object firstElement = currentSelection.getFirstElement();
            if (firstElement != null) {
                key = getKey(firstElement);
            }
        }
        showPart(key);
    }

    private Object getKey(Object object) {
        if (partProvider != null) {
            Object key = partProvider.getPartKey(object);
            if (key != null) {
                return key;
            }
        }
        return object.getClass();
    }

    private void showPart(final Object key) {
        checkLimit();
        final IEditorPart oldPart = getCurrentPart();
        if (key != null) {
            PartBag pageBag = (PartBag) parts.get(key);
            IEditorPart part = pageBag != null ? pageBag.getPart() : null;
            if (part == null) {
                // try to get the page dynamically from the provider
                if (partProvider != null) {
                    part = partProvider.getPart(key);
                    if (part != null) {
                        registerPart(key, part, false);
                    }
                }
            }
            if (part != null) {
                final IEditorPart fpart = part;
                BusyIndicator.showWhile(pageBook.getDisplay(), new Runnable() {
                    public void run() {
                        if (!hasPage(key)) {
                            Composite parent = createPage(key);
                            fpart.createContents(parent);
                            parent.setData(fpart);
                        }
                        // commit the current part
                        if (commitIfDirty && oldPart != null && oldPart.isDirty()) {
                            commitPart(lastSelection, oldPart, false);
                        }
                        fpart.setInput(currentSelection.getFirstElement());
                        if (oldPart != null) {
                            oldPart.deactivate();
                        }
                        fpart.activate();
                        // refresh the new part
                        refreshPart(fpart);
                        showPage(key);
                    }
                });
                return;
            }
        }
        // If we are switching from an old page to nothing,
        // don't loose data
        if (commitIfDirty && oldPart != null && oldPart.isDirty()) {
            commitPart(lastSelection, oldPart, false);
        }
        if (oldPart != null) {
            oldPart.deactivate();
        }
        showEmptyPage();
    }

    private Composite createPage(Object key) {
        if (pageBook instanceof ScrolledPageBook) {
            return ((ScrolledPageBook) pageBook).createPage(key);
        }
        return ((PageBook) pageBook).createPage(key);
    }

    private boolean hasPage(Object key) {
        if (pageBook instanceof ScrolledPageBook) {
            return ((ScrolledPageBook) pageBook).hasPage(key);
        }
        return ((PageBook) pageBook).hasPage(key);
    }

    private void showEmptyPage() {
        if (pageBook instanceof ScrolledPageBook) {
            ((ScrolledPageBook) pageBook).showEmptyPage();
        } else {
            ((PageBook) pageBook).showEmptyPage();
        }

    }

    private void showPage(Object key) {
        if (pageBook instanceof ScrolledPageBook) {
            ((ScrolledPageBook) pageBook).showPage(key);
        } else {
            ((PageBook) pageBook).showPage(key);
        }
    }

    private void removePage(Object key, boolean showEmptyPage) {
        if (pageBook instanceof ScrolledPageBook) {
            ((ScrolledPageBook) pageBook).removePage(key, showEmptyPage);
        } else {
            ((PageBook) pageBook).removePage(key, showEmptyPage);
        }
    }

    protected void refreshPart(IEditorPart detailsPart) {
        detailsPart.refresh();
    }

    protected void commitPart(ISelection selection, IEditorPart detailsPart, boolean onSave) {
        detailsPart.commit(onSave);
    }

    private void checkLimit() {
        if (parts.size() <= getPartLimit())
            return;
        // overflow
        int currentTicket = PartBag.getCurrentTicket();
        int cutoffTicket = currentTicket - getPartLimit();
        for (Map.Entry<Object, PartBag> entry : parts.entrySet()) {
            Object key = entry.getKey();
            PartBag pageBag = entry.getValue();
            if (pageBag.getTicket() <= cutoffTicket) {
                // candidate - see if it is active and not fixed
                if (!pageBag.isFixed() && !pageBag.getPart().equals(getCurrentPart())) {
                    // drop it
                    pageBag.dispose();
                    parts.remove(key);
                    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 getPartLimit() {
        return partLimit;
    }

    /**
     * Sets the page limit for this part.
     * 
     * @see #getPartLimit()
     * @param partLimit
     *            the maximum number of pages that should be maintained in this
     *            part.
     */
    public void setPartLimit(int partLimit) {
        this.partLimit = partLimit;
        checkLimit();
    }

    @Override
    public void createContents(Composite parent) {
        if ((style & (SWT.V_SCROLL | SWT.H_SCROLL)) != 0) {
            this.pageBook = getWidgetFactory().createScrolledPageBook(parent, style);
        } else {
            this.pageBook = getWidgetFactory().createPageBook(parent, style);
        }
    }

    @Override
    public void setInput(Object input) {
        if (input != null) {
            selectionChanged(new StructuredSelection(input));
        } else {
            selectionChanged(StructuredSelection.EMPTY);
        }
    }
}