com.bdaum.zoom.ui.internal.views.CalendarView.java Source code

Java tutorial

Introduction

Here is the source code for com.bdaum.zoom.ui.internal.views.CalendarView.java

Source

/*
 * This file is part of the ZoRa project: http://www.photozora.org.
 *
 * ZoRa is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * ZoRa is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ZoRa; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * (c) 2018 Berthold Daum  
 */
package com.bdaum.zoom.ui.internal.views;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

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.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
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.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

import com.bdaum.zoom.cat.model.asset.Asset;
import com.bdaum.zoom.cat.model.asset.AssetImpl;
import com.bdaum.zoom.cat.model.group.Criterion;
import com.bdaum.zoom.cat.model.group.CriterionImpl;
import com.bdaum.zoom.cat.model.group.SmartCollection;
import com.bdaum.zoom.cat.model.group.SmartCollectionImpl;
import com.bdaum.zoom.cat.model.group.SortCriterionImpl;
import com.bdaum.zoom.core.CatalogAdapter;
import com.bdaum.zoom.core.Constants;
import com.bdaum.zoom.core.Core;
import com.bdaum.zoom.core.QueryField;
import com.bdaum.zoom.core.Range;
import com.bdaum.zoom.core.db.IDbManager;
import com.bdaum.zoom.core.internal.CoreActivator;
import com.bdaum.zoom.ui.AssetSelection;
import com.bdaum.zoom.ui.Ui;
import com.bdaum.zoom.ui.internal.HelpContextIds;
import com.bdaum.zoom.ui.internal.Icons;
import com.bdaum.zoom.ui.internal.UiActivator;

@SuppressWarnings("restriction")
public class CalendarView extends BasicView implements PaintListener, MouseListener, MouseMoveListener {

    public class FindLimitsJob extends Job {

        public FindLimitsJob() {
            super(Messages.getString("CalendarView.find_limits")); //$NON-NLS-1$
            setSystem(true);
        }

        @Override
        protected IStatus run(IProgressMonitor monitor) {
            Date min = null;
            Date max = null;
            Date dmin = null;
            Date dmax = null;
            for (SmartCollectionImpl sm : Core.getCore().getDbManager().obtainObjects(SmartCollectionImpl.class,
                    false, "system", true, QueryField.EQUALS, "album", false, QueryField.EQUALS, Constants.OID, //$NON-NLS-1$ //$NON-NLS-2$
                    IDbManager.DATETIMEKEY, QueryField.STARTSWITH)) {
                List<Criterion> criteria = sm.getCriterion();
                if (criteria != null && !criteria.isEmpty()) {
                    Criterion crit = criteria.get(0);
                    Range range = (Range) crit.getValue();
                    Date from = (Date) range.getFrom();
                    Date to = (Date) range.getTo();
                    if (to.getTime() - from.getTime() < D100) {
                        if (dmin == null || dmin.after(from))
                            dmin = from;
                        if (dmax == null || dmax.before(to))
                            dmax = to;
                    } else {
                        if (min == null || min.after(from))
                            min = from;
                        if (max == null || max.before(to))
                            max = to;
                    }
                }
            }
            if (dmin != null)
                min = dmin;
            if (dmax != null)
                max = dmax;
            minDate = new GregorianCalendar(1826, 0, 1);
            if (min != null) {
                minDate.setTime(min);
                alignCal(minDate);
            }
            maxDate = new GregorianCalendar();
            if (max != null) {
                maxDate.setTime(max);
                alignCal(maxDate);
                maxDate.add(GregorianCalendar.MONTH, 1);
                maxDate.add(GregorianCalendar.MILLISECOND, -1);
            }
            return Status.OK_STATUS;
        }
    }

    private class CalendarAction extends Action {
        private int month;

        public CalendarAction(String label, ImageDescriptor icon, int month) {
            super(label, icon);
            setToolTipText(label);
            this.month = month;
        }

        @Override
        public void run() {
            currentCal.add(GregorianCalendar.MONTH, month);
            if (currentCal.before(minDate) || currentCal.after(maxDate))
                currentCal.add(GregorianCalendar.MONTH, -month);
            else
                refresh();
        }
    }

    public static final String ID = "com.bdaum.zoom.ui.views.CalendarView"; //$NON-NLS-1$
    private static final long D100 = 8640000000L;

    private Canvas canvas;
    private final GregorianCalendar currentCal = new GregorianCalendar();
    private final GregorianCalendar workingCal = new GregorianCalendar();
    private GregorianCalendar minDate = new GregorianCalendar(1826, 0, 1);
    private GregorianCalendar maxDate = new GregorianCalendar();
    private final Asset[] calendarAssets = new Asset[31];
    private int daysInMonth;
    private CalendarAction nextMonthAction, previousMonthAction, nextYearAction, previousYearAction;
    private Rectangle titleRect = new Rectangle(0, 0, 0, 0);
    private Rectangle dayRect = new Rectangle(0, 0, 0, 0);
    private Rectangle weekRect = new Rectangle(0, 0, 0, 0);
    private int size, yoff, wxo, coff, pending = -1;
    private Timer timer;
    private Point mouseDown;

    @Override
    public ISelection getSelection() {
        return AssetSelection.EMPTY;
    }

    @Override
    public void setSelection(ISelection selection) {
        // do nothing
    }

    @Override
    public Control getControl() {
        return canvas;
    }

    @Override
    public boolean selectionChanged() {
        return false;
    }

    @Override
    public void assetsChanged(IWorkbenchPart part, AssetSelection selectedAssets) {
        for (Asset asset : selectedAssets) {
            Date dateCreated = asset.getDateCreated();
            if (dateCreated != null) {
                setCurrentCal(dateCreated);
                break;
            }
        }
    }

    private void setCurrentCal(Date dateCreated) {
        int year = currentCal.get(GregorianCalendar.YEAR);
        int month = currentCal.get(GregorianCalendar.MONTH);
        currentCal.setTime(dateCreated);
        alignCal(currentCal);
        if (year != currentCal.get(GregorianCalendar.YEAR) || month != currentCal.get(GregorianCalendar.MONTH)) {
            if (isVisible())
                refreshBusy();
            else
                isDirty = true;
        }
    }

    private static void alignCal(GregorianCalendar cal) {
        cal.set(GregorianCalendar.DAY_OF_MONTH, 1);
        cal.set(GregorianCalendar.HOUR_OF_DAY, 0);
        cal.set(GregorianCalendar.MINUTE, 0);
        cal.set(GregorianCalendar.SECOND, 0);
        cal.set(GregorianCalendar.MILLISECOND, 0);
    }

    @Override
    public boolean assetsChanged() {
        return false;
    }

    @Override
    public void collectionChanged(IWorkbenchPart part, IStructuredSelection sel) {
        if (sel.size() == 1) {
            Object firstElement = sel.getFirstElement();
            if (firstElement instanceof SmartCollection) {
                SmartCollectionImpl currentCollection = (SmartCollectionImpl) firstElement;
                if (currentCollection.getStringId().startsWith(IDbManager.DATETIMEKEY))
                    setCurrentCal((Date) ((Range) currentCollection.getCriterion(0).getValue()).getFrom());
            }
        }
    }

    @Override
    public boolean collectionChanged() {
        return false;
    }

    @Override
    public void refresh() {
        IDbManager dbManager = Core.getCore().getDbManager();
        workingCal.setTime(currentCal.getTime());
        workingCal.add(GregorianCalendar.MONTH, 1);
        workingCal.add(GregorianCalendar.DAY_OF_MONTH, -1);
        daysInMonth = workingCal.get(GregorianCalendar.DAY_OF_MONTH);
        workingCal.setTime(currentCal.getTime());
        for (int i = 0; i < daysInMonth; i++) {
            Date from = workingCal.getTime();
            workingCal.add(GregorianCalendar.DAY_OF_MONTH, 1);
            Iterator<AssetImpl> it = dbManager.obtainObjects(AssetImpl.class, false,
                    QueryField.IPTC_DATECREATED.getKey(), from, QueryField.NOTSMALLER,
                    QueryField.IPTC_DATECREATED.getKey(), workingCal.getTime(), QueryField.SMALLER).iterator();
            calendarAssets[i] = it.hasNext() ? it.next() : null;
        }
        canvas.redraw();
        updateActions(true);
    }

    @Override
    public void createPartControl(Composite parent) {
        canvas = new Canvas(parent, SWT.DOUBLE_BUFFERED);
        canvas.addPaintListener(this);
        installListeners();
        PlatformUI.getWorkbench().getHelpSystem().setHelp(canvas, HelpContextIds.CALENDAR_VIEW);
        makeActions();
        contributeToActionBars();
        updateActions(true);
        CoreActivator.getDefault().addCatalogListener(new CatalogAdapter() {
            @Override
            public void catalogOpened(boolean newDb) {
                new FindLimitsJob().schedule();
            }
        });
    }

    @Override
    protected void installListeners() {
        super.installListeners();
        canvas.addMouseListener(this);
        canvas.addMouseMoveListener(this);
        canvas.addKeyListener(this);
        Ui.getUi().getNavigationHistory(getSite().getWorkbenchWindow()).addSelectionListener(this);
    }

    @Override
    protected void uninstallListeners() {
        super.uninstallListeners();
        if (!canvas.isDisposed()) {
            canvas.removeMouseListener(this);
            canvas.removeMouseMoveListener(this);
            canvas.removeKeyListener(this);
        }
        Ui.getUi().getNavigationHistory(getSite().getWorkbenchWindow()).removeSelectionListener(this);
    }

    private void contributeToActionBars() {
        IActionBars bars = getViewSite().getActionBars();
        fillLocalPullDown(bars.getMenuManager());
        fillLocalToolBar(bars.getToolBarManager());
    }

    private void fillLocalToolBar(IToolBarManager toolBarManager) {
        toolBarManager.add(nextMonthAction);
        toolBarManager.add(previousMonthAction);
        toolBarManager.add(nextYearAction);
        toolBarManager.add(previousYearAction);
    }

    private void fillLocalPullDown(IMenuManager menuManager) {
        menuManager.add(nextMonthAction);
        menuManager.add(previousMonthAction);
        menuManager.add(nextYearAction);
        menuManager.add(previousYearAction);
    }

    @Override
    protected void makeActions() {
        nextMonthAction = new CalendarAction(Messages.getString("CalendarView.next_month"), //$NON-NLS-1$
                Icons.forwards.getDescriptor(), 1);
        previousMonthAction = new CalendarAction(Messages.getString("CalendarView.previous_month"), //$NON-NLS-1$
                Icons.backwards.getDescriptor(), -1);
        nextYearAction = new CalendarAction(Messages.getString("CalendarView.next_year"), //$NON-NLS-1$
                Icons.down.getDescriptor(), 12);
        previousYearAction = new CalendarAction(Messages.getString("CalendarView.previous_year"), //$NON-NLS-1$
                Icons.up.getDescriptor(), -12);
    }

    @Override
    public void setFocus() {
        canvas.setFocus();
    }

    @Override
    public void updateActions(boolean force) {
        workingCal.setTime(currentCal.getTime());
        workingCal.add(GregorianCalendar.MONTH, -1);
        previousMonthAction.setEnabled(!workingCal.before(minDate));
        workingCal.add(GregorianCalendar.MONTH, -11);
        previousYearAction.setEnabled(!workingCal.before(minDate));
        workingCal.setTime(currentCal.getTime());
        workingCal.add(GregorianCalendar.MONTH, 1);
        nextMonthAction.setEnabled(workingCal.before(maxDate));
        workingCal.add(GregorianCalendar.MONTH, 11);
        nextYearAction.setEnabled(workingCal.before(maxDate));
    }

    @Override
    public void paintControl(PaintEvent e) {
        Rectangle clientArea = canvas.getClientArea();
        int h = clientArea.height / 6;
        int ww = h / 5;
        int w = (clientArea.width - ww) / 7;
        size = Math.min(w, h);
        GC gc = e.gc;
        gc.setTextAntialias(SWT.ON);
        h = size * 6;
        w = size * 7;
        ww = size / 5;
        yoff = (clientArea.height - h) / 2;
        int xoff = (clientArea.width - w) / 2;
        dayRect.x = wxo = xoff + ww;
        dayRect.y = yoff;
        dayRect.width = w;
        dayRect.height = h;
        weekRect.x = xoff;
        weekRect.y = yoff;
        weekRect.width = ww;
        weekRect.height = h;
        workingCal.setTime(currentCal.getTime());
        coff = (workingCal.get(GregorianCalendar.DAY_OF_WEEK) - workingCal.getFirstDayOfWeek() + 7) % 7;
        gc.setBackground(canvas.getForeground());
        gc.setAlpha(64);
        gc.fillRectangle(xoff, yoff, ww, size * 6);
        gc.setAlpha(128);
        if (coff > 0)
            gc.fillRectangle(xoff + ww, yoff, coff * size, size);
        int obsolete = 42 - coff - daysInMonth;
        if (obsolete > 7) {
            gc.fillRectangle(wxo + (14 - obsolete) * size, yoff + 4 * size, (obsolete % 7) * size, size);
            gc.fillRectangle(wxo, yoff + 5 * size, 7 * size, size);
        } else
            gc.fillRectangle(wxo + (7 - obsolete) * size, yoff + 5 * size, obsolete * size, size);
        gc.setBackground(canvas.getBackground());
        gc.setAlpha(255);
        gc.drawLine(xoff, yoff, xoff, yoff + h);
        for (int i = 0; i <= 7; i++)
            gc.drawLine(wxo + i * size, yoff, wxo + i * size, yoff + h);
        for (int i = 0; i <= 6; i++)
            gc.drawLine(xoff, yoff + i * size, wxo + w, yoff + i * size);
        for (int i = 0; i < daysInMonth; i++) {
            Asset asset = calendarAssets[i];
            if (asset != null) {
                Image image = getImage(asset);
                Rectangle bounds = image.getBounds();
                int isize = Math.max(bounds.width, bounds.height);
                double fac = (double) (size - 2) / isize;
                int iw = (int) (bounds.width * fac);
                int ih = (int) (bounds.height * fac);
                int x = (i + coff) % 7;
                int y = (i + coff) / 7;
                gc.drawImage(image, 0, 0, bounds.width, bounds.height, wxo + x * size + (size - iw) / 2,
                        yoff + y * size + (size - ih) / 2, iw, ih);
            }
        }
        workingCal.add(GregorianCalendar.DAY_OF_MONTH, -coff);
        FontData fontData = e.display.getSystemFont().getFontData()[0];
        Font dayFont = new Font(e.display, fontData.getName(), size / 6, SWT.BOLD);
        gc.setAlpha(128);
        for (int i = 0; i < 6; i++) {
            gc.setFont(e.display.getSystemFont());
            String s = String.valueOf(workingCal.get(GregorianCalendar.WEEK_OF_YEAR));
            Point tx = gc.textExtent(s);
            int y = yoff + i * size;
            gc.drawText(s, xoff + (ww - tx.x) / 2, y, true);
            gc.setFont(dayFont);
            y += size;
            for (int j = 1; j <= 7; j++) {
                s = String.valueOf(workingCal.get(GregorianCalendar.DAY_OF_MONTH));
                tx = gc.textExtent(s);
                gc.drawText(s, wxo + j * size - tx.x, y - tx.y, true);
                workingCal.add(GregorianCalendar.DAY_OF_MONTH, 1);
            }
        }
        dayFont.dispose();
        Date date = currentCal.getTime();
        int fontsize;
        SimpleDateFormat df;
        if (xoff > yoff * 3) {
            df = new SimpleDateFormat(Messages.getString("CalendarView.LLL_nl_yyyy")); //$NON-NLS-1$
            fontsize = size / 2;
            titleRect.x = 0;
            titleRect.y = yoff;
        } else {
            df = new SimpleDateFormat(Messages.getString("CalendarView.LLL_yyyy")); //$NON-NLS-1$
            fontsize = size / 4;
            titleRect.x = xoff;
            titleRect.y = 0;
        }
        String s = df.format(date);
        Font monthFont = new Font(e.display, fontData.getName(), fontsize, SWT.BOLD);
        gc.setFont(monthFont);
        gc.drawText(s, titleRect.x, titleRect.y, true);
        Point tx = gc.textExtent(s);
        titleRect.width = tx.x;
        titleRect.height = tx.y;
        monthFont.dispose();
    }

    @Override
    public void mouseDoubleClick(MouseEvent e) {
        // not used
    }

    @Override
    public void mouseDown(MouseEvent e) {
        mouseDown = new Point(e.x, e.y);
    }

    @Override
    public void mouseUp(MouseEvent e) {
        if (mouseDown != null) {
            boolean gesture = processGesture(e.x - mouseDown.x, e.y - mouseDown.y);
            mouseDown = null;
            if (gesture)
                return;
        }
        if (titleRect.contains(e.x, e.y))
            showMonth();
        else if (weekRect.contains(e.x, e.y))
            showWeek(((e.y - yoff) / size));
        else if (dayRect.contains(e.x, e.y))
            showDay(((e.y - yoff) / size) * 7 + (e.x - wxo) / size - coff);

    }

    private boolean processGesture(int difx, int dify) {
        Rectangle area = canvas.getClientArea();
        if (Math.abs(difx) > Math.abs(dify)) {
            if (Math.abs(difx) > area.width / 4) {
                (difx > 0 ? previousMonthAction : nextMonthAction).run();
                return true;
            }
        } else if (Math.abs(dify) > area.height / 4) {
            (dify > 0 ? previousYearAction : nextYearAction).run();
            return true;
        }
        return false;
    }

    private void showWeek(int week) {
        pending = -1;
        for (int i = 0; i < 7; i++) {
            int day = i + week * 7 - coff;
            if (day >= 0 && day < daysInMonth && calendarAssets[day] != null) {
                StringBuilder ksb = new StringBuilder(IDbManager.DATETIMEKEY);
                ksb.append(currentCal.get(GregorianCalendar.YEAR)).append("-W") //$NON-NLS-1$
                        .append(week + currentCal.get(GregorianCalendar.WEEK_OF_YEAR));
                SmartCollectionImpl sm = Core.getCore().getDbManager().obtainById(SmartCollectionImpl.class,
                        ksb.toString());
                if (sm != null) {
                    showCollection(sm);
                    return;
                }
                workingCal.setTime(currentCal.getTime());
                workingCal.add(GregorianCalendar.DAY_OF_MONTH, week * 7 - coff);
                Date from = workingCal.getTime();
                workingCal.add(GregorianCalendar.DAY_OF_MONTH, 7);
                workingCal.add(GregorianCalendar.MILLISECOND, -1);
                SimpleDateFormat df = new SimpleDateFormat(Messages.getString("CalendarView.week_w_yyyy")); //$NON-NLS-1$
                postQuery(from, workingCal.getTime(), df.format(from));
                break;
            }
        }
    }

    private void showDay(final int day) {
        pending = -1;
        if (day >= 0 && day < daysInMonth) {
            if (calendarAssets[day] != null) {
                IDbManager dbManager = Core.getCore().getDbManager();
                workingCal.setTime(currentCal.getTime());
                workingCal.add(GregorianCalendar.DAY_OF_MONTH, day);
                StringBuilder ksb = new StringBuilder(IDbManager.DATETIMEKEY);
                ksb.append(workingCal.get(GregorianCalendar.YEAR)).append('-')
                        .append(workingCal.get(GregorianCalendar.MONTH)).append('-')
                        .append(workingCal.get(GregorianCalendar.DAY_OF_MONTH));
                SmartCollectionImpl sm = dbManager.obtainById(SmartCollectionImpl.class, ksb.toString());
                if (sm != null) {
                    showCollection(sm);
                    return;
                }
                ksb.setLength(IDbManager.DATETIMEKEY.length());
                ksb.append(workingCal.get(GregorianCalendar.YEAR)).append("W-") //$NON-NLS-1$
                        .append(workingCal.get(GregorianCalendar.WEEK_OF_YEAR)).append('-')
                        .append(workingCal.get(GregorianCalendar.DAY_OF_WEEK));
                sm = dbManager.obtainById(SmartCollectionImpl.class, ksb.toString());
                if (sm != null) {
                    showCollection(sm);
                    return;
                }
                Date from = workingCal.getTime();
                workingCal.add(GregorianCalendar.DAY_OF_MONTH, 1);
                workingCal.add(GregorianCalendar.MILLISECOND, -1);
                SimpleDateFormat df = new SimpleDateFormat(Messages.getString("CalendarView.LLLLLLLLLLLL_dd_yyyy")); //$NON-NLS-1$
                postQuery(from, workingCal.getTime(), df.format(from));
            }
        }
    }

    private void showMonth() {
        pending = -1;
        StringBuilder ksb = new StringBuilder(IDbManager.DATETIMEKEY);
        ksb.append(currentCal.get(GregorianCalendar.YEAR)).append('-')
                .append(currentCal.get(GregorianCalendar.MONTH));
        SmartCollectionImpl sm = Core.getCore().getDbManager().obtainById(SmartCollectionImpl.class,
                ksb.toString());
        if (sm != null) {
            showCollection(sm);
            return;
        }
        Date from = currentCal.getTime();
        workingCal.setTime(from);
        workingCal.add(GregorianCalendar.MONTH, 1);
        workingCal.add(GregorianCalendar.MILLISECOND, -1);
        SimpleDateFormat df = new SimpleDateFormat(Messages.getString("CalendarView.LLLLLLLLLLLL_yyyy")); //$NON-NLS-1$
        postQuery(from, workingCal.getTime(), df.format(from));
    }

    @Override
    public void keyReleased(KeyEvent e) {
        switch (e.keyCode) {
        case SWT.ARROW_RIGHT:
            nextMonthAction.run();
            break;
        case SWT.ARROW_LEFT:
            previousMonthAction.run();
            break;
        case SWT.ARROW_UP:
            previousYearAction.run();
            break;
        case SWT.ARROW_DOWN:
            nextYearAction.run();
            break;
        case SWT.HOME:
            currentCal.set(GregorianCalendar.MONTH, 0);
            if (currentCal.before(minDate))
                currentCal.setTime(minDate.getTime());
            refresh();
            break;
        case SWT.END:
            currentCal.set(GregorianCalendar.MONTH, 11);
            if (currentCal.after(maxDate))
                currentCal.setTime(maxDate.getTime());
            refresh();
            break;
        case SWT.PAGE_UP:
            currentCal.setTime(minDate.getTime());
            refresh();
            break;
        case SWT.PAGE_DOWN:
            currentCal.setTime(maxDate.getTime());
            alignCal(currentCal);
            refresh();
            break;
        default:
            char c = e.character;
            if (c == '*')
                showMonth();
            else if (c >= '0' && c <= '9') {
                if (pending < 0) {
                    if (c >= '1' && c <= '3') {
                        pending = c - '0';
                        timer = new Timer();
                        timer.schedule(new TimerTask() {
                            @Override
                            public void run() {
                                if (!canvas.isDisposed())
                                    e.display.asyncExec(() -> {
                                        if (!canvas.isDisposed())
                                            showDay(pending);
                                    });
                                timer = null;
                            }
                        }, 800L);
                    } else if (c != '0')
                        showDay(c - '0');
                } else
                    showDay(pending * 10 + c - '0');
            }
            break;
        }

    }

    private void postQuery(Date from, Date to, String s) {
        SmartCollectionImpl sm = new SmartCollectionImpl(s, false, false, false, true, null, 0, null, 0, null,
                Constants.INHERIT_LABEL, null, 0, null);
        sm.addCriterion(new CriterionImpl(QueryField.IPTC_DATECREATED.getKey(), null, new Range(from, to),
                QueryField.BETWEEN, true));
        sm.addSortCriterion(new SortCriterionImpl(QueryField.IPTC_DATECREATED.getKey(), null, false));
        showCollection(sm);
    }

    private void showCollection(SmartCollectionImpl sm) {
        BusyIndicator.showWhile(canvas.getDisplay(), () -> {
            bringGalleryToTop();
            Ui.getUi().getNavigationHistory(getSite().getWorkbenchWindow())
                    .postSelection(new StructuredSelection(sm));
        });
    }

    private void bringGalleryToTop() {
        String perspId = getSite().getPage().getPerspective().getId();
        String viewId = UiActivator.getDefault().getPerspectiveGallery(perspId);
        if (viewId != null) {
            try {
                getSite().getPage().showView(viewId);
            } catch (PartInitException e) {
                // ignore
            }
        }
    }

    @Override
    public void mouseMove(MouseEvent e) {
        if (titleRect.contains(e.x, e.y)) {
            for (int day = 0; day < daysInMonth; day++)
                if (calendarAssets[day] != null) {
                    canvas.setCursor(e.display.getSystemCursor(SWT.CURSOR_HAND));
                    return;
                }
        } else if (dayRect.contains(e.x, e.y)) {
            int day = ((e.y - yoff) / size) * 7 + (e.x - wxo) / size - coff;
            if (day >= 0 && day < daysInMonth && calendarAssets[day] != null) {
                canvas.setCursor(e.display.getSystemCursor(SWT.CURSOR_HAND));
                return;
            }
        } else if (weekRect.contains(e.x, e.y)) {
            int week = ((e.y - yoff) / size);
            for (int i = 0; i < 7; i++) {
                int day = week * 7 + i - coff;
                if (day >= 0 && day < daysInMonth && calendarAssets[day] != null) {
                    canvas.setCursor(e.display.getSystemCursor(SWT.CURSOR_HAND));
                    return;
                }
            }
        }
        canvas.setCursor(e.display.getSystemCursor(SWT.CURSOR_ARROW));
    }

}