org.eclipse.tcf.te.ui.notifications.internal.popup.AbstractNotificationPopup.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.tcf.te.ui.notifications.internal.popup.AbstractNotificationPopup.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2014 Tasktop Technologies 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:
 *     Benjamin Pasero - initial API and implementation
 *     Tasktop Technologies - initial API and implementation
 *     Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
 *******************************************************************************/

package org.eclipse.tcf.te.ui.notifications.internal.popup;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
import org.eclipse.tcf.te.ui.notifications.interfaces.IPreferenceKeys;
import org.eclipse.tcf.te.ui.notifications.interfaces.ImageConsts;
import org.eclipse.tcf.te.ui.notifications.internal.popup.AnimationUtil.FadeJob;
import org.eclipse.tcf.te.ui.notifications.internal.popup.AnimationUtil.IFadeListener;
import org.eclipse.tcf.te.ui.notifications.nls.Messages;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

/**
 * A popup window with a title bar and message area for displaying notifications.
 *
 * @author Benjamin Pasero
 * @author Mik Kersten
 * @author Steffen Pingel
 * @since 3.7
 */
public abstract class AbstractNotificationPopup extends Window {

    private static final int TITLE_HEIGHT = 24;

    private static final String LABEL_NOTIFICATION = Messages.AbstractNotificationPopup_Notification;

    private static final String LABEL_JOB_CLOSE = Messages.AbstractNotificationPopup_Close_Notification_Job;

    private static final int MAX_WIDTH = 400;

    private static final int MIN_HEIGHT = 100;

    private static final long DEFAULT_DELAY_CLOSE = 8 * 1000;

    private static final int PADDING_EDGE = 5;

    private long delayClose = DEFAULT_DELAY_CLOSE;

    protected LocalResourceManager resources;

    /* default */ GradientColors color;

    private Shell shell;

    private Region lastUsedRegion;

    /* default */ Image lastUsedBgImage;

    private boolean closed;

    private final Job closeJob = new Job(LABEL_JOB_CLOSE) {

        @Override
        protected IStatus run(IProgressMonitor monitor) {
            if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench() != null
                    && PlatformUI.getWorkbench().getDisplay() != null) {
                final Display display = PlatformUI.getWorkbench().getDisplay();
                if (!display.isDisposed()) {
                    display.asyncExec(new Runnable() {
                        @Override
                        public void run() {
                            Shell shell = AbstractNotificationPopup.this.getShell();
                            if (shell == null || shell.isDisposed()) {
                                return;
                            }

                            if (isMouseOver(shell)) {
                                scheduleAutoClose();
                                return;
                            }

                            AbstractNotificationPopup.this.closeFade();
                        }

                    });
                }
            }
            if (monitor.isCanceled()) {
                return Status.CANCEL_STATUS;
            }

            return Status.OK_STATUS;
        }
    };

    private final boolean respectDisplayBounds = true;

    private final boolean respectMonitorBounds = true;

    /* default */ FadeJob fadeJob;

    private final boolean fadingEnabled;

    /**
     * Constructor
     *
     * @param parent The parent shell or <code>null</code> to create a top level shell.
     * @param style The shell style.
     */
    public AbstractNotificationPopup(Shell parent) {
        this(parent, SWT.NO_TRIM | SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
    }

    /**
     * Constructor
     *
     * @param parent The parent shell or <code>null</code> to create a top level shell.
     * @param style The shell style.
     */
    public AbstractNotificationPopup(Shell parent, int style) {
        super(parent);
        setShellStyle(style);

        resources = new LocalResourceManager(JFaceResources.getResources());

        color = new GradientColors(PlatformUI.getWorkbench().getDisplay(), resources);

        closeJob.setSystem(true);

        // Initialize the fadingEnabled flag
        fadingEnabled = UIPlugin.getScopedPreferences().getBoolean(IPreferenceKeys.PREF_ENABLE_FADING);

        closed = false;
    }

    /**
     * Override to return a customized name.
     *
     * @return The name to be used in the title of the popup.
     */
    protected String getPopupShellTitle() {
        return LABEL_NOTIFICATION;
    }

    /**
     * Override to return a customized image. Defaults to the workbench window image.
     *
     * @param maximumHeight The maximum height of the image.
     * @return The image or <code>null</code>.
     */
    protected Image getPopupShellImage(int maximumHeight) {
        // always use the launching workbench window
        IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
        if (windows != null && windows.length > 0) {
            IWorkbenchWindow workbenchWindow = windows[0];
            if (workbenchWindow != null && !workbenchWindow.getShell().isDisposed()) {
                Image image = workbenchWindow.getShell().getImage();
                int diff = Integer.MAX_VALUE;
                if (image != null && image.getBounds().height <= maximumHeight) {
                    diff = maximumHeight - image.getBounds().height;
                } else {
                    image = null;
                }

                Image[] images = workbenchWindow.getShell().getImages();
                if (images != null && images.length > 0) {
                    // find the icon that is closest in size, but not larger than maximumHeight
                    for (Image image2 : images) {
                        int newDiff = maximumHeight - image2.getBounds().height;
                        if (newDiff >= 0 && newDiff <= diff) {
                            diff = newDiff;
                            image = image2;
                        }
                    }
                }

                return image;
            }
        }
        return null;
    }

    /**
     * Override to populate with notifications.
     *
     * @param parent
     */
    protected void createContentArea(Composite parent) {
        // empty by default
    }

    /**
     * Override to customize the title bar
     */
    protected void createTitleArea(Composite parent) {
        ((GridData) parent.getLayoutData()).heightHint = TITLE_HEIGHT;

        Label titleImageLabel = new Label(parent, SWT.NONE);
        titleImageLabel.setImage(getPopupShellImage(TITLE_HEIGHT));

        Label titleTextLabel = new Label(parent, SWT.NONE);
        titleTextLabel.setText(getPopupShellTitle());
        titleTextLabel.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
        titleTextLabel.setForeground(getTitleForeground());
        titleTextLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
        titleTextLabel.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_HAND));

        final Label button = new Label(parent, SWT.NONE);
        button.setImage(UIPlugin.getImage(ImageConsts.NOTIFICATION_CLOSE));
        button.addMouseTrackListener(new MouseTrackAdapter() {
            @Override
            public void mouseEnter(MouseEvent e) {
                button.setImage(UIPlugin.getImage(ImageConsts.NOTIFICATION_CLOSE_HOVER));
            }

            @Override
            public void mouseExit(MouseEvent e) {
                button.setImage(UIPlugin.getImage(ImageConsts.NOTIFICATION_CLOSE));
            }
        });
        button.addMouseListener(new MouseAdapter() {

            @SuppressWarnings("synthetic-access")
            @Override
            public void mouseUp(MouseEvent e) {
                close();
                setReturnCode(CANCEL);
            }

        });
    }

    protected Color getTitleForeground() {
        return color.getTitleText();
    }

    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);

        shell = newShell;
        newShell.setBackground(color.getBorder());
    }

    @Override
    public void create() {
        super.create();
        addRegion(shell);
    }

    private void addRegion(Shell shell) {
        Region region = new Region();
        Point s = shell.getSize();

        /* Add entire Shell */
        region.add(0, 0, s.x, s.y);

        /* Subtract Top-Left Corner */
        region.subtract(0, 0, 5, 1);
        region.subtract(0, 1, 3, 1);
        region.subtract(0, 2, 2, 1);
        region.subtract(0, 3, 1, 1);
        region.subtract(0, 4, 1, 1);

        /* Subtract Top-Right Corner */
        region.subtract(s.x - 5, 0, 5, 1);
        region.subtract(s.x - 3, 1, 3, 1);
        region.subtract(s.x - 2, 2, 2, 1);
        region.subtract(s.x - 1, 3, 1, 1);
        region.subtract(s.x - 1, 4, 1, 1);

        /* Subtract Bottom-Left Corner */
        region.subtract(0, s.y, 5, 1);
        region.subtract(0, s.y - 1, 3, 1);
        region.subtract(0, s.y - 2, 2, 1);
        region.subtract(0, s.y - 3, 1, 1);
        region.subtract(0, s.y - 4, 1, 1);

        /* Subtract Bottom-Right Corner */
        region.subtract(s.x - 5, s.y - 0, 5, 1);
        region.subtract(s.x - 3, s.y - 1, 3, 1);
        region.subtract(s.x - 2, s.y - 2, 2, 1);
        region.subtract(s.x - 1, s.y - 3, 1, 1);
        region.subtract(s.x - 1, s.y - 4, 1, 1);

        /* Dispose old first */
        if (shell.getRegion() != null) {
            shell.getRegion().dispose();
        }

        /* Apply Region */
        shell.setRegion(region);

        /* Remember to dispose later */
        lastUsedRegion = region;
    }

    /* default */ boolean isMouseOver(Shell shell) {
        if (PlatformUI.isWorkbenchRunning() && PlatformUI.getWorkbench() != null
                && PlatformUI.getWorkbench().getDisplay() != null
                && !PlatformUI.getWorkbench().getDisplay().isDisposed()) {
            return shell.getBounds().contains(PlatformUI.getWorkbench().getDisplay().getCursorLocation());
        }
        return false;
    }

    @Override
    public int open() {
        if (shell == null || shell.isDisposed()) {
            shell = null;
            create();
        }

        constrainShellSize();
        shell.setLocation(fixupDisplayBounds(shell.getSize(), shell.getLocation()));

        if (fadingEnabled) {
            shell.setAlpha(0);
        }
        shell.setVisible(true);
        fadeJob = AnimationUtil.fadeIn(shell, new IFadeListener() {
            @Override
            public void faded(Shell shell, int alpha) {
                if (shell.isDisposed()) {
                    return;
                }

                if (alpha == 255) {
                    scheduleAutoClose();
                }
            }
        });

        return Window.OK;
    }

    protected void scheduleAutoClose() {
        if (delayClose > 0) {
            closeJob.schedule(delayClose);
        }
    }

    @Override
    protected Control createContents(Composite parent) {
        ((GridLayout) parent.getLayout()).marginWidth = 1;
        ((GridLayout) parent.getLayout()).marginHeight = 1;

        /* Outer Composite holding the controls */
        final Composite outerCircle = new Composite(parent, SWT.NO_FOCUS);
        outerCircle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        outerCircle.setBackgroundMode(SWT.INHERIT_FORCE);

        outerCircle.addControlListener(new ControlAdapter() {

            @Override
            public void controlResized(ControlEvent e) {
                Rectangle clArea = outerCircle.getClientArea();
                lastUsedBgImage = new Image(outerCircle.getDisplay(), clArea.width, clArea.height);
                GC gc = new GC(lastUsedBgImage);

                /* Gradient */
                drawGradient(gc, clArea);

                /* Fix Region Shape */
                fixRegion(gc, clArea);

                gc.dispose();

                Image oldBGImage = outerCircle.getBackgroundImage();
                outerCircle.setBackgroundImage(lastUsedBgImage);

                if (oldBGImage != null) {
                    oldBGImage.dispose();
                }
            }

            private void drawGradient(GC gc, Rectangle clArea) {
                gc.setForeground(color.getGradientBegin());
                gc.setBackground(color.getGradientEnd());
                gc.fillGradientRectangle(clArea.x, clArea.y, clArea.width, clArea.height, true);
            }

            private void fixRegion(GC gc, Rectangle clArea) {
                gc.setForeground(color.getBorder());

                /* Fill Top Left */
                gc.drawPoint(2, 0);
                gc.drawPoint(3, 0);
                gc.drawPoint(1, 1);
                gc.drawPoint(0, 2);
                gc.drawPoint(0, 3);

                /* Fill Top Right */
                gc.drawPoint(clArea.width - 4, 0);
                gc.drawPoint(clArea.width - 3, 0);
                gc.drawPoint(clArea.width - 2, 1);
                gc.drawPoint(clArea.width - 1, 2);
                gc.drawPoint(clArea.width - 1, 3);

                /* Fill Bottom Left */
                gc.drawPoint(2, clArea.height - 0);
                gc.drawPoint(3, clArea.height - 0);
                gc.drawPoint(1, clArea.height - 1);
                gc.drawPoint(0, clArea.height - 2);
                gc.drawPoint(0, clArea.height - 3);

                /* Fill Bottom Right */
                gc.drawPoint(clArea.width - 4, clArea.height - 0);
                gc.drawPoint(clArea.width - 3, clArea.height - 0);
                gc.drawPoint(clArea.width - 2, clArea.height - 1);
                gc.drawPoint(clArea.width - 1, clArea.height - 2);
                gc.drawPoint(clArea.width - 1, clArea.height - 3);
            }
        });

        GridLayout layout = new GridLayout(1, false);
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        layout.verticalSpacing = 0;

        outerCircle.setLayout(layout);

        /* Title area containing label and close button */
        final Composite titleCircle = new Composite(outerCircle, SWT.NO_FOCUS);
        titleCircle.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        titleCircle.setBackgroundMode(SWT.INHERIT_FORCE);

        layout = new GridLayout(4, false);
        layout.marginWidth = 3;
        layout.marginHeight = 0;
        layout.verticalSpacing = 5;
        layout.horizontalSpacing = 3;

        titleCircle.setLayout(layout);

        /* Create Title Area */
        createTitleArea(titleCircle);

        /* Outer composite to hold content controls */
        Composite outerContentCircle = new Composite(outerCircle, SWT.NONE);
        outerContentCircle.setBackgroundMode(SWT.INHERIT_FORCE);

        layout = new GridLayout(1, false);
        layout.marginWidth = 0;
        layout.marginHeight = 0;

        outerContentCircle.setLayout(layout);
        outerContentCircle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        outerContentCircle.setBackground(outerCircle.getBackground());

        /* Middle composite to show a 1px black line around the content controls */
        Composite middleContentCircle = new Composite(outerContentCircle, SWT.NO_FOCUS);
        middleContentCircle.setBackgroundMode(SWT.INHERIT_FORCE);

        layout = new GridLayout(1, false);
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        layout.marginTop = 1;

        middleContentCircle.setLayout(layout);
        middleContentCircle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        middleContentCircle.setBackground(color.getBorder());

        /* Inner composite containing the content controls */
        Composite innerContent = new Composite(middleContentCircle, SWT.NO_FOCUS);
        innerContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        innerContent.setBackgroundMode(SWT.INHERIT_FORCE);

        layout = new GridLayout(1, false);
        layout.marginWidth = 0;
        layout.marginHeight = 5;
        layout.marginLeft = 5;
        layout.marginRight = 5;
        innerContent.setLayout(layout);

        innerContent.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WHITE));

        /* Content Area */
        createContentArea(innerContent);

        return outerCircle;
    }

    @Override
    protected void initializeBounds() {
        Rectangle clArea = getPrimaryClientArea();
        Point initialSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        int height = Math.max(initialSize.y, MIN_HEIGHT);
        int width = Math.min(initialSize.x, MAX_WIDTH);

        Point size = new Point(width, height);
        shell.setLocation(clArea.width + clArea.x - size.x - PADDING_EDGE,
                clArea.height + clArea.y - size.y - PADDING_EDGE);
        shell.setSize(size);
    }

    private Rectangle getPrimaryClientArea() {
        Monitor primaryMonitor = shell.getDisplay().getPrimaryMonitor();
        return (primaryMonitor != null) ? primaryMonitor.getClientArea() : shell.getDisplay().getClientArea();
    }

    public void closeFade() {
        if (fadeJob != null) {
            fadeJob.cancelAndWait(false);
        }
        fadeJob = AnimationUtil.fadeOut(getShell(), new IFadeListener() {
            @Override
            public void faded(Shell shell, int alpha) {
                if (shell != null && !shell.isDisposed()) {
                    if (alpha == 0) {
                        close();
                    } else if (isMouseOver(shell)) {
                        if (fadeJob != null) {
                            fadeJob.cancelAndWait(false);
                        }
                        fadeJob = AnimationUtil.fastFadeIn(shell, new IFadeListener() {
                            @Override
                            public void faded(Shell shell, int alpha) {
                                if (shell.isDisposed()) {
                                    return;
                                }

                                if (alpha == 255) {
                                    scheduleAutoClose();
                                }
                            }
                        });
                    }
                }
            }
        });
    }

    @Override
    public boolean close() {
        closed = true;

        if (resources != null)
            resources.dispose();
        if (lastUsedRegion != null && !lastUsedRegion.isDisposed()) {
            lastUsedRegion.dispose();
        }
        if (lastUsedBgImage != null && !lastUsedBgImage.isDisposed()) {
            lastUsedBgImage.dispose();
        }

        return super.close();
    }

    public final boolean isClosed() {
        return closed;
    }

    public long getDelayClose() {
        return delayClose;
    }

    public void setDelayClose(long delayClose) {
        this.delayClose = delayClose >= 0 ? delayClose : DEFAULT_DELAY_CLOSE;
    }

    private Point fixupDisplayBounds(Point tipSize, Point location) {
        if (respectDisplayBounds) {
            Rectangle bounds;
            Point rightBounds = new Point(tipSize.x + location.x, tipSize.y + location.y);

            if (respectMonitorBounds) {
                bounds = shell.getDisplay().getPrimaryMonitor().getBounds();
            } else {
                bounds = getPrimaryClientArea();
            }

            if (!(bounds.contains(location) && bounds.contains(rightBounds))) {
                if (rightBounds.x > bounds.x + bounds.width) {
                    location.x -= rightBounds.x - (bounds.x + bounds.width);
                }

                if (rightBounds.y > bounds.y + bounds.height) {
                    location.y -= rightBounds.y - (bounds.y + bounds.height);
                }

                if (location.x < bounds.x) {
                    location.x = bounds.x;
                }

                if (location.y < bounds.y) {
                    location.y = bounds.y;
                }
            }
        }

        return location;
    }

}