org.eclipse.launchbar.ui.controls.internal.CSelector.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.launchbar.ui.controls.internal.CSelector.java

Source

/*******************************************************************************
 * Copyright (c) 2014 QNX Software Systems 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:
 *     Doug Schaefer
 *******************************************************************************/
package org.eclipse.launchbar.ui.controls.internal;

import java.util.Comparator;

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.layout.GridLayoutFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.LineAttributes;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;

public abstract class CSelector extends Composite {
    private IStructuredContentProvider contentProvider;
    private ILabelProvider labelProvider;
    private Comparator<?> sorter;
    private Comparator<?> sorterTop;
    private Object input;
    private Composite buttonComposite;
    private static final int arrowMax = 2;
    private Transition arrowTransition;
    private Object selection;
    private boolean mouseOver;
    private Label currentIcon;
    private Label currentLabel;
    private Shell popup;
    private LaunchBarListViewer listViewer;
    private Job delayJob;
    private MouseListener mouseListener = new MouseAdapter() {
        @Override
        public void mouseUp(MouseEvent event) {
            if (popup == null || popup.isDisposed()) {
                setFocus();
                openPopup();
            } else {
                closePopup();
            }
        }
    };

    protected boolean myIsFocusAncestor(Control control) {
        while (control != null && control != this && !(control instanceof Shell)) {
            control = control.getParent();
        }
        return control == this;
    }

    private Listener focusOutListener = new Listener() {
        private Job closingJob;

        @Override
        public void handleEvent(Event event) {
            switch (event.type) {
            case SWT.FocusIn:
                if (closingJob != null)
                    closingJob.cancel();
                if (event.widget instanceof Control && myIsFocusAncestor((Control) event.widget)) {
                    break; // not closing
                }
                if (!isPopUpInFocus()) {
                    closePopup();
                }
                break;
            case SWT.FocusOut:
                if (isPopUpInFocus()) {
                    // we about to loose focus from popup children, but it may
                    // go to another child, lets schedule a job to wait before
                    // we close
                    if (closingJob != null)
                        closingJob.cancel();
                    closingJob = new Job(Messages.CSelector_0) {
                        @Override
                        protected IStatus run(IProgressMonitor monitor) {
                            if (monitor.isCanceled())
                                return Status.CANCEL_STATUS;
                            closePopup();
                            closingJob = null;
                            return Status.OK_STATUS;
                        }
                    };
                    closingJob.schedule(300);
                }
                break;
            case SWT.MouseUp:
                if (popup != null && !popup.isDisposed()) {
                    Point loc = getDisplay().getCursorLocation();
                    if (!popup.getBounds().contains(loc) && !getBounds().contains(getParent().toControl(loc))) {
                        closePopup();
                    }
                }
                break;
            default:
                break;
            }
        }
    };

    public CSelector(Composite parent, int style) {
        super(parent, style);
        setBackground(getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

        GridLayout mainButtonLayout = new GridLayout();
        setLayout(mainButtonLayout);
        addPaintListener(new PaintListener() {
            @Override
            public void paintControl(PaintEvent e) {
                GC gc = e.gc;
                gc.setBackground(getBackground());
                gc.setForeground(getOutlineColor());
                Point size = getSize();
                final int arc = 3;
                gc.fillRoundRectangle(0, 0, size.x - 1, size.y - 1, arc, arc);
                gc.drawRoundRectangle(0, 0, size.x - 1, size.y - 1, arc, arc);
            }
        });
        addMouseListener(mouseListener);
    }

    private boolean isPopUpInFocus() {
        Control focusControl = getDisplay().getFocusControl();
        if (focusControl != null && focusControl.getShell() == popup) {
            return true;
        }
        return false;
    }

    @Override
    public void dispose() {
        super.dispose();
        if (popup != null)
            popup.dispose();
    }

    public void setDelayedSelection(final Object element, long millis) {
        if (delayJob != null)
            delayJob.cancel();
        delayJob = new Job(Messages.CSelector_1) {
            @Override
            protected IStatus run(final IProgressMonitor monitor) {
                if (monitor.isCanceled())
                    return Status.CANCEL_STATUS;
                if (isDisposed())
                    return Status.CANCEL_STATUS;
                getDisplay().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        if (monitor.isCanceled())
                            return;
                        setSelection(element);
                    }
                });
                return Status.OK_STATUS;
            }
        };
        delayJob.schedule(millis);
    }

    public void setSelection(Object element) {
        if (isDisposed())
            return;
        this.selection = element;
        if (buttonComposite != null)
            buttonComposite.dispose();
        String toolTipText = getToolTipText();
        boolean editable = false;
        int columns = 2;
        Image image = labelProvider.getImage(element);
        if (image != null)
            columns++;
        editable = isEditable(element);
        if (editable)
            columns++;
        buttonComposite = new Composite(this, SWT.NONE);
        GridLayout buttonLayout = new GridLayout(columns, false);
        buttonLayout.marginHeight = buttonLayout.marginWidth = 0;
        buttonComposite.setLayout(buttonLayout);
        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
        buttonComposite.setBackground(getBackground());
        buttonComposite.addMouseListener(mouseListener);
        buttonComposite.setToolTipText(toolTipText);
        if (element != null) {
            if (image != null) {
                Label icon = createImage(buttonComposite, image);
                icon.addMouseListener(mouseListener);
                currentIcon = icon;
                currentIcon.setToolTipText(toolTipText);
            }
            Label label = createLabel(buttonComposite, element);
            label.addMouseListener(mouseListener);
            currentLabel = label;
            currentLabel.setToolTipText(toolTipText);
        } else {
            Composite blank = new Composite(buttonComposite, SWT.NONE);
            blank.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
            currentIcon = null;
            currentLabel = null;
        }
        final Canvas arrow = new Canvas(buttonComposite, SWT.NONE) {
            @Override
            public Point computeSize(int wHint, int hHint, boolean changed) {
                return new Point(12, 16);
            }
        };
        arrow.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
        arrow.setBackground(getBackground());
        arrow.setForeground(getForeground());
        arrowTransition = new Transition(arrow, arrowMax, 80);
        arrow.addPaintListener(new PaintListener() {
            @Override
            public void paintControl(PaintEvent e) {
                final int hPadding = 2;
                GC gc = e.gc;
                LineAttributes attributes = new LineAttributes(2);
                attributes.cap = SWT.CAP_ROUND;
                gc.setLineAttributes(attributes);
                gc.setAlpha(mouseOver ? 255 : 100);
                Rectangle bounds = arrow.getBounds();
                int arrowWidth = bounds.width - hPadding * 2;
                int current = arrowTransition.getCurrent();
                gc.drawPolyline(new int[] { hPadding, bounds.height / 2 - current, hPadding + (arrowWidth / 2),
                        bounds.height / 2 + current, hPadding + arrowWidth, bounds.height / 2 - current });
            }
        });
        arrow.addMouseListener(mouseListener);
        if (editable) {
            final EditButton editButton = new EditButton(buttonComposite, SWT.NONE);
            editButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
            editButton.setBackground(getBackground());
            editButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    // Need to run this after the current event storm
                    // Or we get a disposed error.
                    getDisplay().asyncExec(new Runnable() {
                        @Override
                        public void run() {
                            if (CSelector.this.selection != null)
                                handleEdit(selection);
                        }
                    });
                }
            });
        }
        layout();
    }

    protected abstract void fireSelectionChanged();

    public Object getSelection() {
        return selection;
    }

    public MouseListener getMouseListener() {
        return mouseListener;
    }

    protected void openPopup() {
        Object[] elements = contentProvider.getElements(input);
        if (elements.length == 0 && !hasActionArea())
            return;
        arrowTransition.to(-arrowMax);
        if (popup != null && !popup.isDisposed()) {
            popup.dispose();
        }
        popup = new Shell(getShell(), SWT.TOOL | SWT.ON_TOP | SWT.RESIZE);
        popup.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create());
        popup.setBackground(getBackground());
        listViewer = new LaunchBarListViewer(popup);
        initializeListViewer(listViewer);
        listViewer.setFilterVisible(elements.length > 7);
        listViewer.setInput(input);
        listViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                if (!listViewer.isFinalSelection())
                    return;
                StructuredSelection ss = (StructuredSelection) event.getSelection();
                if (!ss.isEmpty()) {
                    setSelection(ss.getFirstElement());
                    fireSelectionChanged();
                }
                closePopup();
            }
        });
        if (hasActionArea())
            createActionArea(popup);
        Rectangle buttonBounds = getBounds();
        Point popupLocation = popup.getDisplay().map(this, null, 0, buttonBounds.height);
        popup.setLocation(popupLocation.x, popupLocation.y + 5);
        restoreShellSize();
        popup.setVisible(true);
        popup.setFocus();
        getDisplay().addFilter(SWT.FocusIn, focusOutListener);
        getDisplay().addFilter(SWT.FocusOut, focusOutListener);
        getDisplay().addFilter(SWT.MouseUp, focusOutListener);
        popup.addDisposeListener(new DisposeListener() {
            @Override
            public void widgetDisposed(DisposeEvent e) {
                getDisplay().removeFilter(SWT.FocusIn, focusOutListener);
                getDisplay().removeFilter(SWT.FocusOut, focusOutListener);
                getDisplay().removeFilter(SWT.MouseUp, focusOutListener);
                saveShellSize();
            }
        });
    }

    protected String getDialogPreferencePrefix() {
        return getClass().getSimpleName();
    }

    protected void restoreShellSize() {
        Point size = popup.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        Point buttonSize = getSize();
        size.x = Math.max(size.x, buttonSize.x);
        size.y = Math.min(size.y, 300);
        try {
            IPreferenceStore store = Activator.getDefault().getPreferenceStore();
            String prefName = getDialogPreferencePrefix();
            int w = store.getInt(prefName + ".shell.w"); //$NON-NLS-1$
            int h = store.getInt(prefName + ".shell.h"); //$NON-NLS-1$
            size.x = Math.max(size.x, w);
            size.y = Math.max(size.y, h);
        } catch (Exception e) {
            Activator.log(e);
        }
        popup.setSize(size);
    }

    protected void saveShellSize() {
        Point size = popup.getSize();
        IPreferenceStore store = Activator.getDefault().getPreferenceStore();
        String prefName = getDialogPreferencePrefix();
        store.setValue(prefName + ".shell.w", size.x); //$NON-NLS-1$
        store.setValue(prefName + ".shell.h", size.y); //$NON-NLS-1$
    }

    protected void initializeListViewer(LaunchBarListViewer listViewer) {
        listViewer.setContentProvider(contentProvider);
        listViewer.setLabelProvider(labelProvider);
        listViewer.setComparator(sorter);
        listViewer.setHistoryComparator(sorterTop);
    }

    private void closePopup() {
        getDisplay().asyncExec(new Runnable() {
            @Override
            public void run() {
                if (popup == null || popup.isDisposed())
                    return;
                arrowTransition.to(arrowMax);
                popup.setVisible(false);
                popup.dispose();
            }
        });
    }

    private Label createImage(Composite parent, Image image) {
        Rectangle bounds = image.getBounds();
        boolean disposeImage = false;
        if (bounds.height > 16 || bounds.width > 16) {
            Image buttonImage = new Image(getDisplay(), 16, 16);
            GC gc = new GC(buttonImage);
            gc.setAntialias(SWT.ON);
            gc.setInterpolation(SWT.HIGH);
            gc.drawImage(image, 0, 0, image.getBounds().width, image.getBounds().height, 0, 0, 16, 16);
            gc.dispose();
            image = buttonImage;
            disposeImage = true;
        }
        Label icon = new Label(parent, SWT.NONE);
        icon.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
        icon.setImage(image);
        if (disposeImage) {
            final Image disposableImage = image;
            icon.addDisposeListener(new DisposeListener() {
                @Override
                public void widgetDisposed(DisposeEvent e) {
                    disposableImage.dispose();
                }
            });
        }
        return icon;
    }

    private Label createLabel(Composite parent, Object element) {
        Label label = new Label(parent, SWT.NONE);
        label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
        label.setText(labelProvider.getText(element));
        label.setFont(getDisplay().getSystemFont());
        return label;
    }

    public void setContentProvider(IStructuredContentProvider contentProvider) {
        this.contentProvider = contentProvider;
    }

    public IStructuredContentProvider getContentProvider() {
        return contentProvider;
    }

    public void setLabelProvider(ILabelProvider labelProvider) {
        this.labelProvider = labelProvider;
    }

    public ILabelProvider getLabelProvider() {
        return labelProvider;
    }

    @Override
    public void setToolTipText(String toolTipText) {
        super.setToolTipText(toolTipText);
        if (buttonComposite != null) {
            buttonComposite.setToolTipText(toolTipText);
        }
        if (currentLabel != null && !currentLabel.isDisposed()) {
            currentLabel.setToolTipText(toolTipText);
        }
        if (currentIcon != null && !currentIcon.isDisposed()) {
            currentIcon.setToolTipText(toolTipText);
        }
    }

    /**
     * Set sorter for the bottom part of the selector
     *
     * @param sorter
     */
    public void setSorter(Comparator<?> sorter) {
        this.sorter = sorter;
    }

    /**
     * Set sorter for the "history" part of the selector
     *
     * @param sorter
     */
    public void setHistorySortComparator(Comparator<?> sorter) {
        this.sorterTop = sorter;
    }

    public void setInput(Object input) {
        this.input = input;
    }

    public Object getInput() {
        return input;
    }

    public void refresh() {
        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
            @Override
            public void run() {
                if (isDisposed())
                    return;
                update(selection); // update current selection - name or icon
                // may have changed
                if (popup != null && !popup.isDisposed()) {
                    listViewer.refresh(true); // update all labels in the popup
                }
            }
        });
    }

    public void update(Object element) {
        if (selection == element) {
            if (currentIcon != null && !currentIcon.isDisposed()) {
                currentIcon.setImage(labelProvider.getImage(element));
            }
            if (currentLabel != null && !currentLabel.isDisposed()) {
                currentLabel.setText(labelProvider.getText(element));
            }
        }
        if (popup != null && !popup.isDisposed()) {
            listViewer.update(element, null);
        }
    }

    /**
     * Returns the text currently visible in the selector
     */
    public String getText() {
        if (currentLabel != null) {
            currentLabel.getText();
        }
        return ""; //$NON-NLS-1$
    }

    protected boolean hasActionArea() {
        return false;
    }

    protected void createActionArea(Composite parent) {
        // empty
    }

    protected boolean isEditable(Object element) {
        return false;
    }

    protected void handleEdit(Object element) {
        // nothing to do here
    }

    public Color getOutlineColor() {
        return getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
    }

    public Color getHighlightColor() {
        return getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
    }
}