no.resheim.elibrarium.epub.ui.reader.TOCOutlinePage.java Source code

Java tutorial

Introduction

Here is the source code for no.resheim.elibrarium.epub.ui.reader.TOCOutlinePage.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2013, 2014 Torkild U. Resheim.
 *
 * 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:
 *     Torkild U. Resheim - initial API and implementation
 *******************************************************************************/
package no.resheim.elibrarium.epub.ui.reader;

import no.resheim.elibrarium.epub.core.EpubUtil;
import no.resheim.elibrarium.epub.ui.EpubUiPlugin;
import no.resheim.elibrarium.library.Book;
import no.resheim.elibrarium.library.Bookmark;
import no.resheim.elibrarium.library.TextAnnotation;

import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OwnerDrawLabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.mylyn.docs.epub.core.Publication;
import org.eclipse.mylyn.docs.epub.ncx.NavPoint;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.ocpsoft.pretty.time.PrettyTime;

/**
 * Use to provide an outline page for EPUB table of contents.
 *
 * @author Torkild U. Resheim
 */
public class TOCOutlinePage extends Page
        implements IContentOutlinePage, ISelectionChangedListener, IDoubleClickListener, IPropertyChangeListener {

    private static final String TITLE_FONT = "no.resheim.elibrarium.epub.ui.titleFont";

    private static final String DATE_FONT = "no.resheim.elibrarium.epub.ui.dateFont";

    /**
     * Use to sort bookmarks by page numbers. The bookmark with the lowest
     * number will come first.
     */
    private final class BookmarksComparator extends ViewerComparator {
        @Override
        public int compare(Viewer viewer, Object e1, Object e2) {
            if (e1 instanceof Bookmark && e2 instanceof Bookmark) {
                int page_e1 = ((Bookmark) e1).getPage();
                int page_e2 = ((Bookmark) e2).getPage();
                return page_e1 - page_e2;
            }
            return super.compare(viewer, e1, e2);
        }
    }

    /**
     * Lists bookmarks
     */
    private final class BookmarksContentProvider implements IStructuredContentProvider {

        public void dispose() {
        }

        public Object[] getElements(Object parent) {
            if (parent instanceof Book) {
                Book book = (Book) parent;
                return book.getBookmarks().toArray();
            }
            return new Object[0];
        }

        public void inputChanged(Viewer v, Object oldInput, Object newInput) {
        }

    }

    private StackLayout layout;

    private TableViewer bookmarks;

    private final Publication ops;

    private Composite pagebook;

    private final EpubReader reader;

    private final ListenerList selectionChangedListeners = new ListenerList();

    private IAction showAnnotations;

    private IAction showTOC;

    private TreeViewer toc;

    public TOCOutlinePage(Publication epub, EpubReader reader) {
        this.ops = epub;
        this.reader = reader;
    }

    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        selectionChangedListeners.add(listener);
    }

    @Override
    public void dispose() {
        // No longer listen to events from this book
        book.eAdapters().remove(bookAdapter);
        super.dispose();
    }

    @Override
    public void createControl(Composite parent) {
        book = EpubUtil.getBook(ops);

        pagebook = new Composite(parent, SWT.NONE);
        layout = new StackLayout();
        pagebook.setLayout(layout);

        toc = new TreeViewer(pagebook, getTreeStyle());
        toc.setContentProvider(new TOCContentProvider());
        toc.setLabelProvider(new EpubLabelProvider());
        toc.addSelectionChangedListener(this);
        toc.addDoubleClickListener(this);
        bookmarks = new TableViewer(pagebook, SWT.NONE);
        bookmarks.setContentProvider(new BookmarksContentProvider());
        bookmarks.setComparator(new BookmarksComparator());
        bookmarks.addSelectionChangedListener(this);
        bookmarks.addDoubleClickListener(this);
        bookmarks.getTable().setHeaderVisible(false);
        final TableViewerColumn column = new TableViewerColumn(bookmarks, SWT.LEFT);
        // Special drawing of bookmarks table
        installLabelProvider(column);
        // Automatic layout of bookmarks table
        installControlAdapter(column);

        GridData data = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH);

        bookmarks.getControl().setLayoutData(data);
        // Create the context menu for the annotations
        hookContextMenu(bookmarks.getControl());

        // Set the input
        toc.setInput(ops);
        toc.expandAll();
        bookmarks.setInput(book);

        // Handles changes in the book and will refresh the bookmarks view.
        bookAdapter = new AdapterImpl() {
            @Override
            public void notifyChanged(Notification notification) {
                if (!bookmarks.getControl().isDisposed()) {
                    bookmarks.getControl().getDisplay().asyncExec(new Runnable() {
                        @Override
                        public void run() {
                            bookmarks.refresh(true);
                        }
                    });
                }
            }
        };
        book.eAdapters().add(bookAdapter);

        // Start by showing the table of contents
        showTOC.setChecked(true);
        layout.topControl = toc.getControl();
        pagebook.layout();
        JFaceResources.getFontRegistry().addListener(this);
        // Activate UI context for o.e.u.meny contributes items
        activateContext();
    }

    private void installControlAdapter(final TableViewerColumn column) {
        // Automatically adjust column and table sizes
        pagebook.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(ControlEvent e) {
                org.eclipse.swt.graphics.Rectangle area = pagebook.getClientArea();
                Point preferredSize = bookmarks.getTable().computeSize(SWT.DEFAULT, SWT.DEFAULT);
                int width = area.width - 2 * bookmarks.getTable().getBorderWidth();
                if (preferredSize.y > area.height + bookmarks.getTable().getHeaderHeight()) {
                    // Subtract the scrollbar width from the total column width
                    // if a vertical scrollbar will be required
                    Point vBarSize = bookmarks.getTable().getVerticalBar().getSize();
                    width -= vBarSize.x;
                }
                Point oldSize = bookmarks.getTable().getSize();
                if (oldSize.x > area.width) {
                    // table is getting smaller so make the columns
                    // smaller first and then resize the table to
                    // match the client area width
                    column.getColumn().setWidth(width);
                    bookmarks.getTable().setSize(area.width, area.height);
                } else {
                    // table is getting bigger so make the table
                    // bigger first and then make the columns wider
                    // to match the client area width
                    column.getColumn().setWidth(width);
                    bookmarks.getTable().setSize(area.width, area.height);
                }
            }
        });
    }

    private void installLabelProvider(final TableViewerColumn column) {
        final PrettyTime pt = new PrettyTime();
        column.setLabelProvider(new OwnerDrawLabelProvider() {

            @Override
            protected void paint(Event event, Object element) {
                GC gc = event.gc;
                Display display = column.getViewer().getControl().getDisplay();
                Bookmark bookmark = (Bookmark) element;
                String date = pt.format(bookmark.getTimestamp());
                int width = column.getColumn().getWidth();
                // Calculate the size of the date string
                Point size = event.gc.textExtent(date, SWT.DRAW_DELIMITER | SWT.DRAW_TAB);
                int halfHeight = size.y / 2;
                int height = size.y;
                // if ((event.detail & SWT.SELECTED) != 0) {
                // Region region = new Region();
                // gc.getClipping(region);
                // region.translate(10, 10);
                // // region.add(event.x, event.y - halfHeight, width,
                // // event.height - halfHeight);
                // gc.setClipping(region);
                // region.dispose();
                //
                // Rectangle rect = event.getBounds();
                // Color foreground = gc.getForeground();
                // Color background = gc.getBackground();
                // gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
                // gc.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
                // gc.fillGradientRectangle(0, rect.y, 500, rect.height, false);
                // // restore colors for subsequent drawing
                // gc.setForeground(foreground);
                // gc.setBackground(background);
                // event.detail &= ~SWT.SELECTED;
                // }

                // Draw the date
                gc.setFont(getFont(DATE_FONT));
                gc.setForeground(JFaceResources.getColorRegistry().get(JFacePreferences.QUALIFIER_COLOR));
                gc.drawText(date, width - size.x, event.y + size.y, true);
                // Paint the page number of the bookmark
                int pageNumber = bookmark.getPage();
                String page = Integer.toString(pageNumber);
                Point pageSize = event.gc.textExtent(page, SWT.DRAW_DELIMITER | SWT.DRAW_TAB);
                gc.drawText(page, width - 16 - pageSize.x, event.y, true);
                // Draw icon
                if (bookmark instanceof TextAnnotation) {
                } else {
                    int x = width - 9;
                    gc.drawImage(EpubUiPlugin.getDefault().getImageRegistry().get(EpubUiPlugin.IMG_BOOKMARK), x,
                            event.y + 1);
                }
                // Draw title
                gc.setFont(getFont(TITLE_FONT));
                gc.setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
                // Make sure text does not span to far
                Region region = new Region();
                gc.getClipping(region);
                region.subtract(width - 32 - pageSize.x, event.y, width, height);
                gc.setClipping(region);
                region.dispose();
                String text = bookmark.getText();
                if (text == null) {
                    text = "<missing text>";
                }
                if (bookmark instanceof TextAnnotation) {
                    if ((event.detail & SWT.SELECTED) == 0) {
                        gc.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
                        gc.drawText(text, event.x + 1, event.y, false);
                    } else {
                        gc.drawText(text, event.x + 1, event.y, true);
                    }
                } else {
                    gc.drawText(text, event.x + 1, event.y, true);
                }
                // Draw separator
                drawUnderline(event, gc, width, size, halfHeight);
            }

            public Font getFont(String fontName) {
                IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
                ITheme currentTheme = themeManager.getCurrentTheme();

                FontRegistry fontRegistry = currentTheme.getFontRegistry();
                Font font = fontRegistry.get(fontName);
                return font;
            }

            /**
             * Draws a separator between the rows.
             */
            public void drawUnderline(Event event, GC gc, int width, Point size, int halfHeight) {
                int y = (event.y + size.y * 2) + halfHeight - 2;
                int center = (width / 2);
                gc.setForeground(JFaceResources.getColorRegistry().get(JFacePreferences.QUALIFIER_COLOR));
                gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
                gc.fillGradientRectangle(center, y, center, 1, false);
                gc.setBackground(JFaceResources.getColorRegistry().get(JFacePreferences.QUALIFIER_COLOR));
                gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
                gc.fillGradientRectangle(0, y, center, 1, false);
            }

            @Override
            protected void measure(Event event, Object element) {
                String text = "A";
                Point size = event.gc.textExtent(text, SWT.DRAW_DELIMITER | SWT.DRAW_TAB);
                event.width = bookmarks.getTable().getColumn(event.index).getWidth();
                // we need two lines of text and some space
                int halfHeight = size.y / 2;
                event.height = size.y * 2 + halfHeight;
            }
        });
    }

    @Override
    public void doubleClick(DoubleClickEvent event) {
        IStructuredSelection s = (IStructuredSelection) event.getSelection();
        Object element = s.getFirstElement();
        if (element instanceof NavPoint) {
            reader.navigateTo((NavPoint) element);
        } else if (element instanceof TextAnnotation) {
            reader.navigateTo((TextAnnotation) element);
        } else if (element instanceof Bookmark) {
            reader.navigateTo((Bookmark) element);
        }
    }

    private void fillContextMenu(IMenuManager manager) {
        // Other plug-ins can contribute there actions here
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    /**
     * Fires a selection changed event.
     *
     * @param selection
     *            the new selection
     */
    protected void fireSelectionChanged(ISelection selection) {
        // create an event
        final SelectionChangedEvent event = new SelectionChangedEvent(this, selection);

        // fire the event
        Object[] listeners = selectionChangedListeners.getListeners();
        for (Object listener : listeners) {
            final ISelectionChangedListener l = (ISelectionChangedListener) listener;
            SafeRunner.run(new SafeRunnable() {
                public void run() {
                    l.selectionChanged(event);
                }
            });
        }
    }

    /*
     * (non-Javadoc) Method declared on IPage (and Page).
     */
    @Override
    public Control getControl() {
        if (pagebook == null) {
            return null;
        }
        return pagebook;
    }

    /*
     * (non-Javadoc) Method declared on ISelectionProvider.
     */
    public ISelection getSelection() {
        if (toc == null) {
            return StructuredSelection.EMPTY;
        }
        return toc.getSelection();
    }

    /**
     * A hint for the styles to use while constructing the TreeViewer.
     * <p>
     * Subclasses may override.
     * </p>
     *
     * @return the tree styles to use. By default, SWT.MULTI | SWT.H_SCROLL |
     *         SWT.V_SCROLL
     * @since 3.6
     */
    protected int getTreeStyle() {
        return SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL;
    }

    /**
     * Returns this page's tree viewer.
     *
     * @return this page's tree viewer, or <code>null</code> if
     *         <code>createControl</code> has not been called yet
     */
    protected TreeViewer getTreeViewer() {
        return toc;
    }

    private void hookContextMenu(Control control) {
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                TOCOutlinePage.this.fillContextMenu(manager);
            }
        });
        Menu menu = menuMgr.createContextMenu(bookmarks.getControl());
        control.setMenu(menu);
        getSite().registerContextMenu("no.resheim.elibrarium.bookmarks", menuMgr, bookmarks);
    }

    @Override
    public void init(IPageSite pageSite) {
        super.init(pageSite);
        pageSite.setSelectionProvider(this);
        makeActions();
        IToolBarManager toolbar = getSite().getActionBars().getToolBarManager();
        toolbar.add(showTOC);
        toolbar.add(showAnnotations);
    }

    private void makeActions() {

        showTOC = new Action("Contents", IAction.AS_RADIO_BUTTON) {
            @Override
            public void run() {
                layout.topControl = toc.getControl();
                pagebook.layout();
            }
        };
        showAnnotations = new Action("Notes", IAction.AS_RADIO_BUTTON) {
            @Override
            public void run() {
                layout.topControl = bookmarks.getControl();
                pagebook.layout();
            }
        };

        showTOC.setImageDescriptor(
                AbstractUIPlugin.imageDescriptorFromPlugin(EpubUiPlugin.PLUGIN_ID, "icons/contents.gif"));
        showAnnotations.setImageDescriptor(
                AbstractUIPlugin.imageDescriptorFromPlugin(EpubUiPlugin.PLUGIN_ID, "icons/marking.gif"));
    }

    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        selectionChangedListeners.remove(listener);
    }

    public void selectionChanged(SelectionChangedEvent event) {
        fireSelectionChanged(event.getSelection());
    }

    /**
     * Sets focus to a part in the page.
     */
    @Override
    public void setFocus() {
        toc.getControl().setFocus();
    }

    public void setSelection(ISelection selection) {
        if (toc != null) {
            toc.setSelection(selection);
        }
    }

    private static final String VIEW_CONTEXT_ID = "no.resheim.elibrarium.bookmarks"; //$NON-NLS-1$

    private Adapter bookAdapter;

    private Book book;

    /**
     * Activate a context that this view uses. It will be tied to this view
     * activation events and will be removed when the view is disposed.
     */
    private void activateContext() {
        IContextService contextService = (IContextService) getSite().getService(IContextService.class);
        contextService.activateContext(VIEW_CONTEXT_ID);
    }

    @Override
    public void propertyChange(PropertyChangeEvent event) {
        // titleFont =
        // JFaceResources.getFont("no.resheim.elibrarium.epub.ui.titleFont");
        // dateFont =
        // JFaceResources.getFont("no.resheim.elibrarium.epub.ui.dateFont");
    }
}