com.bdaum.zoom.ui.internal.UiUtilities.java Source code

Java tutorial

Introduction

Here is the source code for com.bdaum.zoom.ui.internal.UiUtilities.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) 2009 Berthold Daum  
 */

package com.bdaum.zoom.ui.internal;

import java.awt.Color;
import java.awt.Font;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.Device;
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.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

import com.bdaum.aoModeling.runtime.AomList;
import com.bdaum.zoom.cat.model.SimilarityOptions_type;
import com.bdaum.zoom.cat.model.TextSearchOptions_type;
import com.bdaum.zoom.cat.model.artworkOrObjectShown.ArtworkOrObjectImpl;
import com.bdaum.zoom.cat.model.asset.Asset;
import com.bdaum.zoom.cat.model.asset.Region;
import com.bdaum.zoom.cat.model.asset.RegionImpl;
import com.bdaum.zoom.cat.model.creatorsContact.ContactImpl;
import com.bdaum.zoom.cat.model.group.Criterion;
import com.bdaum.zoom.cat.model.group.GroupImpl;
import com.bdaum.zoom.cat.model.group.SmartCollection;
import com.bdaum.zoom.cat.model.group.SmartCollectionImpl;
import com.bdaum.zoom.cat.model.group.SortCriterion;
import com.bdaum.zoom.cat.model.location.LocationImpl;
import com.bdaum.zoom.cat.model.meta.Meta;
import com.bdaum.zoom.cat.model.meta.WatchedFolder;
import com.bdaum.zoom.core.Constants;
import com.bdaum.zoom.core.Core;
import com.bdaum.zoom.core.Format;
import com.bdaum.zoom.core.IFormatter;
import com.bdaum.zoom.core.IScoreFormatter;
import com.bdaum.zoom.core.IVolumeManager;
import com.bdaum.zoom.core.QueryField;
import com.bdaum.zoom.core.Range;
import com.bdaum.zoom.core.TetheredRange;
import com.bdaum.zoom.core.db.ICollectionProcessor;
import com.bdaum.zoom.core.db.IDbManager;
import com.bdaum.zoom.core.internal.Utilities;
import com.bdaum.zoom.core.internal.lire.Algorithm;
import com.bdaum.zoom.core.internal.peer.IPeerService;
import com.bdaum.zoom.fileMonitor.internal.filefilter.WildCardFilter;
import com.bdaum.zoom.ui.dialogs.AcousticMessageDialog;
import com.bdaum.zoom.ui.internal.Icons.Icon;
import com.bdaum.zoom.ui.internal.codes.CodeParser;
import com.bdaum.zoom.ui.internal.codes.Topic;
import com.bdaum.zoom.ui.internal.views.ImageRegion;
import com.bdaum.zoom.ui.preferences.PreferenceConstants;
import com.bdaum.zoom.ui.widgets.CGroup;

@SuppressWarnings("restriction")
public class UiUtilities {

    public static final Comparator<String> stringComparator = new Comparator<String>() {
        public int compare(String s1, String s2) {
            if (s1 == s2)
                return 0;
            if (s1 == null)
                return -1;
            if (s2 == null)
                return 1;
            return s1.compareToIgnoreCase(s2);
        }
    };

    private static final int USHRT_MAXV = 65535;
    private static final Integer MINUSONE = Integer.valueOf(-1);

    /**
     * Creates a labeled group
     *
     * @param parent
     *            - parent composite
     * @param columns
     *            - number of columns within group
     * @param label
     *            - group label
     * @return - group control
     */
    public static CGroup createGroup(Composite parent, int columns, String label) {
        CGroup group = new CGroup(parent, SWT.NONE);
        group.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
        group.setLayout(new GridLayout(columns, false));
        group.setText(label);
        return group;
    }

    /**
     * Creates a tab item
     *
     * @param tabFolder
     *            - tab folder
     * @param text
     *            - tab item label
     * @param tooltip
     *            - tool tip
     * @return - tab item
     */
    public static CTabItem createTabItem(CTabFolder tabFolder, String text, String tooltip) {
        CTabItem tabItem = new CTabItem(tabFolder, SWT.NONE);
        tabItem.setFont(JFaceResources.getBannerFont());
        tabItem.setText(' ' + text + ' ');
        if (tooltip != null)
            tabItem.setToolTipText(tooltip);
        return tabItem;
    }

    /**
     * Creates a tab page with item
     *
     * @param tabFolder
     *            - tab folder
     * @param text
     *            - tab item label
     * @param tooltip
     *            - tool tip
     * @return - tab item
     */
    public static Composite createTabPage(CTabFolder tabFolder, String text, String tooltip) {
        CTabItem item = createTabItem(tabFolder, text, tooltip);
        Composite comp = new Composite(tabFolder, SWT.NONE);
        item.setControl(comp);
        return comp;
    }

    public static String createSlideTitle(Asset asset) {
        String tit = asset.getTitle();
        if (tit == null || tit.trim().isEmpty())
            tit = asset.getHeadline();
        if (tit == null || tit.trim().isEmpty())
            return Core.getFileName(asset.getUri(), true);
        return tit;
    }

    public static String addExplanation(QueryField qfield, String[] value, String text) {
        Object enumeration = qfield.getEnumeration();
        if (enumeration instanceof Integer) {
            CodeParser codeParser = UiActivator.getDefault().getCodeParser((Integer) enumeration);
            if (codeParser.canParse()) {
                int n = 0;
                for (String s : value) {
                    Topic topic = codeParser.findTopic(s);
                    if (topic != null) {
                        StringBuilder sb = new StringBuilder(text).append(" (") //$NON-NLS-1$
                                .append(topic.getName());
                        if (++n < value.length)
                            sb.append(",..."); //$NON-NLS-1$
                        return sb.append(')').toString();
                    }
                }
            }
        }
        return text;
    }

    public static void showFileIsOffline(Shell shell, Asset asset) {
        if (asset.getFileState() == IVolumeManager.PEER || !asset.getUri().startsWith(Constants.FILESCHEME + ':'))
            AcousticMessageDialog.openInformation(shell, Messages.UiUtilities_remote_image,
                    NLS.bind(Messages.UiUtilities_the_image_is_remote, asset.getName()));
        else {
            String volume = asset.getVolume();
            if (Core.getCore().getVolumeManager().isOffline(volume))
                AcousticMessageDialog.openInformation(shell, Messages.UiUtilities_File_is_offline,
                        NLS.bind(Messages.UiUtilities_File_is_offline_mount_volume, asset.getName(), volume));
            else
                AcousticMessageDialog.openWarning(shell, Messages.UiUtilities_File_does_not_exist,
                        NLS.bind(Messages.UiUtilities_File_does_not_exist_delete_entry, asset.getName()));
        }
    }

    public static void showFilesAreOffline(Shell shell, List<Asset> errands, boolean all, String msgTemplate) {
        if (errands.size() == 1)
            showFileIsOffline(shell, errands.get(0));
        else {
            Set<String> volumes = new HashSet<String>();
            for (Asset asset : errands)
                if (asset.getFileState() != IVolumeManager.PEER) {
                    String volume = asset.getVolume();
                    if (volume != null && !volume.isEmpty())
                        volumes.add(volume);
                }
            String[] vols = volumes.toArray(new String[volumes.size()]);
            Arrays.sort(vols);
            String stringlist = Core.toStringList(vols, ", "); //$NON-NLS-1$
            AcousticMessageDialog.openWarning(shell, Messages.UiUtilities_Files_missing,
                    (all) ? NLS.bind(msgTemplate, Messages.UiUtilities_All, stringlist)
                            : NLS.bind(msgTemplate, errands.size(), stringlist));
        }
    }

    public static String[] updateComboHistory(Combo combo) {
        String[] items = combo.getItems();
        String s = combo.getText();
        return s.isEmpty() ? items : addToHistoryList(items, s);
    }

    public static String[] addToHistoryList(String[] items, String text) {
        List<String> list = new ArrayList<>(items.length + 1);
        list.add(text);
        for (String s : items) {
            if (!s.equals(text))
                list.add(s);
            if (list.size() >= 8)
                break;
        }
        return list.toArray(new String[list.size()]);
    }

    public static Point snapToGrid(int x, int y, int w, int h, int yOff, int gridSize, int gridTolerance) {
        int rx = (x + gridSize / 2) / gridSize * gridSize;
        if (Math.abs(rx - x) < gridTolerance)
            x = rx;
        else {
            int newX2 = x + w;
            rx = (newX2 + gridSize / 2) / gridSize * gridSize;
            if (Math.abs(rx - newX2) < gridTolerance)
                x = rx - w;
        }
        int newY1 = yOff - y;
        int ry = (newY1 + gridSize / 2) / gridSize * gridSize;
        if (Math.abs(ry - newY1) < gridTolerance)
            newY1 = ry;
        else {
            int newY2 = newY1 - h;
            ry = (newY2 + gridSize / 2) / gridSize * gridSize;
            if (Math.abs(ry - newY2) < gridTolerance)
                newY1 = ry + h;
        }
        return new Point(x, yOff - newY1);
    }

    public static String getFilters(WatchedFolder folder) {
        String filters = folder.getFilters();
        return (filters == null) ? UiActivator.getDefault().getDefaultWatchFilters() : filters;
    }

    public static Font getAwtFont(Control control, String family, int style, int size) {
        org.eclipse.swt.graphics.Font font = null;
        while (control != null) {
            font = control.getFont();
            if (font != null)
                break;
            control = control.getParent();
        }
        if (font != null) {
            FontData fontData = font.getFontData()[0];
            int s = fontData.getStyle();
            s = (s == SWT.BOLD ? Font.BOLD : s == SWT.ITALIC ? Font.ITALIC : Font.PLAIN);
            return new Font(family != null ? family : fontData.getName(), style >= 0 ? style : s,
                    size >= 0 ? size : fontData.getHeight());
        }
        return new Font(family != null ? family : "Arial", //$NON-NLS-1$
                style >= 0 ? style : Font.PLAIN, size >= 0 ? size : 9);
    }

    public static Color getAwtForeground(Control control, org.eclipse.swt.graphics.Color color) {
        RGB rgb = (color != null ? color : control.getForeground()).getRGB();
        return new Color(rgb.red, rgb.green, rgb.blue);
    }

    public static Color getAwtBackground(Control control, org.eclipse.swt.graphics.Color color) {
        RGB rgb = (color != null ? color : control.getBackground()).getRGB();
        return new Color(rgb.red, rgb.green, rgb.blue);
    }

    public static String computeImageCaption(Asset asset, IScoreFormatter scoreFormatter, Integer cardinality,
            WildCardFilter filter, String template) {
        if (asset == null)
            return ""; //$NON-NLS-1$
        StringBuilder sb;
        if (template != null) {
            String s = Utilities.evaluateTemplate(template, Constants.TH_ALL, "", null, -1, -1, -1, null, asset, //$NON-NLS-1$
                    "", Integer.MAX_VALUE, false, true); //$NON-NLS-1$
            if (scoreFormatter == null)
                return s;
            sb = new StringBuilder(s);
        } else if (cardinality != null) {
            String name = asset.getName();
            if (filter != null) {
                String s = Core.getFileName(asset.getUri(), true);
                String[] capture = filter.capture(s);
                for (String c : capture)
                    if (c.length() < s.length())
                        name = c;
            }
            sb = new StringBuilder(name).append(" (").append(cardinality.intValue()).append(')'); //$NON-NLS-1$
        } else {
            String s = Core.getFileName(asset.getUri(), true);
            if (scoreFormatter == null)
                return s;
            sb = new StringBuilder(s);
        }
        if (scoreFormatter != null) {
            String remark = scoreFormatter.format(asset.getScore());
            if (!remark.isEmpty())
                sb.append(" (").append(remark).append(')'); //$NON-NLS-1$
        }
        return sb.toString();
    }

    public static Rectangle computeFrame(String regionId, int x, int y, int width, int height, int rotation) {
        try {
            int l = regionId.length();
            int leadingZeros = l > 12 ? Math.max(0, 16 - l) : Math.max(0, 12 - l);
            int h1 = Utilities.parseHex(regionId, 0 - leadingZeros, 4 - leadingZeros);
            int h2 = Utilities.parseHex(regionId, 4 - leadingZeros, 8 - leadingZeros);
            int h3 = Utilities.parseHex(regionId, 8 - leadingZeros, 12 - leadingZeros);
            int h4 = (l <= 12) ? -1 : Utilities.parseHex(regionId, 12 - leadingZeros, 16 - leadingZeros);
            switch (rotation) {
            case 90: {
                int x1 = width * (USHRT_MAXV - h4) / USHRT_MAXV;
                int y1 = height * h1 / USHRT_MAXV;
                int y2 = height * h3 / USHRT_MAXV;
                if (h4 < 0) {
                    int x2 = width * (USHRT_MAXV - h3) / USHRT_MAXV;
                    return new Rectangle(x1 + x, y1 + y, x2, -1);
                }
                int x2 = width * (USHRT_MAXV - h2) / USHRT_MAXV;
                return new Rectangle(x1 + x, y1 + y, x2 - x1, y2 - y1);
            }
            case 180: {
                int x1 = width * h1 / USHRT_MAXV;
                int y1 = height * h2 / USHRT_MAXV;
                int x2 = width * h3 / USHRT_MAXV;
                if (h4 < 0) {
                    int y2 = height * h3 / USHRT_MAXV;
                    return new Rectangle(width - x2 + x, height - y2 + y, x, -1);
                }
                int y2 = height * h4 / USHRT_MAXV;
                return new Rectangle(width - x2 + x, height - y2 + y, x2 - x1, y2 - y1);
            }
            case 270: {
                int x1 = width * (USHRT_MAXV - h4) / USHRT_MAXV;
                int y1 = height * h1 / USHRT_MAXV;
                int y2 = height * h3 / USHRT_MAXV;
                if (h4 < 0) {
                    int x2 = width * (USHRT_MAXV - h3) / USHRT_MAXV;
                    return new Rectangle(width - x2 + x, height - y2 + y, x2, -1);
                }
                int x2 = width * (USHRT_MAXV - h2) / USHRT_MAXV;
                return new Rectangle(width - x2 + x, height - y2 + y, x2 - x1, y2 - y1);
            }
            default: {
                int x1 = width * h1 / USHRT_MAXV;
                int y1 = height * h2 / USHRT_MAXV;
                int x2 = width * h3 / USHRT_MAXV;
                if (h4 < 0)
                    return new Rectangle(x1 + x, y1 + y, x2, -1);
                int y2 = height * h4 / USHRT_MAXV;
                return new Rectangle(x1 + x, y1 + y, x2 - x1, y2 - y1);
            }
            }
        } catch (NumberFormatException e) {
            return null;
        }
    }

    public static boolean isDoubledRegion(Collection<Rectangle> rectangles, Rectangle r1) {
        boolean c1 = r1.height < 0;
        for (Rectangle r2 : rectangles)
            if (c1 == r2.height < 0) {
                if (c1) {
                    if (r2.contains(r1.x, r1.y) && r2.contains(r1.x + r1.width, r1.y + r1.width)
                            || r1.contains(r2.x, r2.y) && r1.contains(r2.x + r2.width, r2.y + r2.width))
                        return true;
                } else if (r2.contains(r1.x, r1.y) && r2.contains(r1.x + r1.width, r1.y + r1.height)
                        || r1.contains(r2.x, r2.y) && r1.contains(r2.x + r2.width, r2.y + r2.height))
                    return true;
            }
        return false;
    }

    public static GroupImpl obtainUserGroup(final IDbManager dbManager) {
        GroupImpl user = dbManager.obtainById(GroupImpl.class, Constants.GROUP_ID_USER);
        if (user == null) {
            user = new GroupImpl(Messages.UiUtilities_user_defined, false, Constants.INHERIT_LABEL, null, 0, null);
            user.setStringId(Constants.GROUP_ID_USER);
        }
        return user;
    }

    public static String computeFieldStringValue(FieldDescriptor fd, Object value) {
        IFormatter formatter = fd.getDetailQueryField().getFormatter();
        if (formatter != null)
            return formatter.toString(value);
        if (value == null)
            return ""; //$NON-NLS-1$
        switch (fd.getDetailQueryField().getType()) {
        case QueryField.T_FLOAT:
        case QueryField.T_FLOATB:
        case QueryField.T_POSITIVEFLOAT:
            NumberFormat af = (NumberFormat.getNumberInstance());
            af.setMaximumFractionDigits(fd.getDetailQueryField().getMaxlength());
            return af.format(value);
        default:
            return String.valueOf(value);
        }
    }

    @SuppressWarnings("fallthrough")
    public static Object computeCookedValue(FieldDescriptor fd, String text) {
        IFormatter formatter = fd.getDetailQueryField().getFormatter();
        String trim = text == null ? "" : text.trim(); //$NON-NLS-1$
        boolean undefined = trim.isEmpty() || trim.equals("-"); //$NON-NLS-1$
        if (formatter != null)
            try {
                return formatter.fromString(trim);
            } catch (ParseException ex) {
                undefined = true;
            }
        try {
            switch (fd.getDetailQueryField().getType()) {
            case QueryField.T_POSITIVEINTEGER:
                if (undefined)
                    return MINUSONE;
            case QueryField.T_INTEGER:
                return Integer.parseInt(trim);
            case QueryField.T_POSITIVELONG:
                if (undefined)
                    return MINUSONE;
            case QueryField.T_LONG:
                return Long.parseLong(trim);
            case QueryField.T_POSITIVEFLOAT:
            case QueryField.T_FLOAT:
            case QueryField.T_FLOATB:
                if (UiConstants.INFINITE.equals(trim))
                    return Double.POSITIVE_INFINITY;
                if (undefined)
                    return Double.NaN;
                NumberFormat af = (NumberFormat.getNumberInstance());
                af.setMaximumFractionDigits(8);
                return af.parse(trim).doubleValue();
            case QueryField.T_CURRENCY:
                if (undefined)
                    return Double.NaN;
                af = (NumberFormat.getNumberInstance());
                af.setMaximumFractionDigits(Format.getCurrencyDigits());
                return af.parse(trim).doubleValue();
            case QueryField.T_BOOLEAN:
                return Boolean.parseBoolean(trim);
            case QueryField.T_STRING:
                return text;
            }
        } catch (NumberFormatException e) {
            // do nothing
        } catch (ParseException e) {
            // do nothing
        }
        return null;
    }

    public static String verifyText(FieldDescriptor fieldDescriptor, String text, int rel) {
        String errorMessage = null;
        if (!text.isEmpty()) {
            IFormatter formatter = fieldDescriptor.getDetailQueryField().getFormatter();
            if (formatter != null)
                try {
                    formatter.fromString(text);
                    return null;
                } catch (ParseException ex) {
                    // do nothing
                }
            String trim = text.trim();
            switch (fieldDescriptor.getDetailQueryField().getType()) {
            case QueryField.T_INTEGER:
                try {
                    Integer.parseInt(text);
                } catch (NumberFormatException e1) {
                    errorMessage = Messages.UiUtilities_not_a_valid_integer;
                }
                break;
            case QueryField.T_LONG:
                try {
                    Long.parseLong(text);
                } catch (NumberFormatException e1) {
                    errorMessage = Messages.UiUtilities_not_a_valid_integer;
                }
                break;
            case QueryField.T_POSITIVEINTEGER:
                try {
                    int v = Integer.parseInt(text);
                    if (v <= 0)
                        errorMessage = Messages.UiUtilities_value_must_be_greater_0;
                } catch (NumberFormatException e1) {
                    errorMessage = Messages.UiUtilities_not_a_valid_integer;
                }
                break;
            case QueryField.T_POSITIVELONG:
                try {
                    long v = Long.parseLong(text);
                    if (v <= 0)
                        errorMessage = Messages.UiUtilities_value_must_be_greater_0;
                } catch (NumberFormatException e1) {
                    errorMessage = Messages.UiUtilities_not_a_valid_integer;
                }
                break;
            case QueryField.T_FLOAT:
            case QueryField.T_FLOATB:
                try {
                    if (!UiConstants.INFINITE.equals(trim)) {
                        NumberFormat af = (NumberFormat.getNumberInstance());
                        af.setMaximumFractionDigits(8);
                        af.parse(text);
                    }
                } catch (ParseException e1) {
                    errorMessage = Messages.UiUtilities_not_a_valid_fp;
                }
                break;
            case QueryField.T_POSITIVEFLOAT:
                try {
                    if (!UiConstants.INFINITE.equals(trim)) {
                        NumberFormat af = (NumberFormat.getNumberInstance());
                        af.setMaximumFractionDigits(8);
                        double v = af.parse(text).doubleValue();
                        if (v <= 0)
                            errorMessage = Messages.UiUtilities_value_must_be_greater_0;
                    }
                } catch (ParseException e1) {
                    errorMessage = Messages.UiUtilities_not_a_valid_fp;
                }
                break;
            case QueryField.T_CURRENCY:
                try {
                    NumberFormat af = (NumberFormat.getNumberInstance());
                    af.setMaximumFractionDigits(Format.getCurrencyDigits());
                    double v = af.parse(text).doubleValue();
                    if (v <= 0)
                        errorMessage = Messages.UiUtilities_value_must_be_greater_0;
                } catch (ParseException e1) {
                    errorMessage = Messages.UiUtilities_not_a_valid_fp;
                }
                break;
            case QueryField.T_STRING:
                if (rel == QueryField.WILDCARDS || rel == QueryField.NOTWILDCARDS)
                    errorMessage = WildCardFilter.validate(text);
                else {
                    int maxLength = fieldDescriptor.getDetailQueryField().getMaxlength();
                    if (maxLength >= 0 && text.length() > maxLength)
                        errorMessage = NLS.bind(Messages.UiUtilities_string_too_long, maxLength);
                }
                break;
            }
        }
        return errorMessage;
    }

    //   private static void compileCategories(Map<String, Category> categories, Set<String> result) {
    //      for (Category cat : categories.values())
    //         if (cat != null) {
    //            String label = cat.getLabel();
    //            result.add(label);
    //            result.add(label);
    //            compileCategories(cat.getSubCategory(), result);
    //         }
    //   }
    //
    public static Set<String> getValueProposals(IDbManager db, String field, String subfield) {
        Set<String> result = null;
        Meta meta = db.getMeta(false);
        if (meta != null) {
            QueryField qfield = QueryField.findQuerySubField(field, subfield);
            //         if (qfield == QueryField.IPTC_KEYWORDS)
            //            return meta.getKeywords();
            //         if (qfield == QueryField.IPTC_CATEGORY || qfield == QueryField.IPTC_SUPPLEMENTALCATEGORIES)
            //            compileCategories(meta.getCategory(), result = new HashSet<String>(30));
            //         else
            if (qfield == QueryField.LOCATION_WORLDREGION) {
                result = new HashSet<String>(10);
                for (LocationImpl loc : db.obtainObjects(LocationImpl.class)) {
                    String worldRegion = loc.getWorldRegion();
                    if (worldRegion != null && !worldRegion.isEmpty())
                        result.add(worldRegion);
                }
            } else if (qfield == QueryField.LOCATION_WORLDREGIONCODE) {
                result = new HashSet<String>(10);
                for (LocationImpl loc : db.obtainObjects(LocationImpl.class)) {
                    String worldRegionCode = loc.getWorldRegionCode();
                    if (worldRegionCode != null && !worldRegionCode.isEmpty())
                        result.add(worldRegionCode);
                }
            } else if (qfield == QueryField.LOCATION_COUNTRYNAME) {
                result = new HashSet<String>(100);
                for (LocationImpl loc : db.obtainObjects(LocationImpl.class)) {
                    String country = loc.getCountryName();
                    if (country != null && !country.isEmpty())
                        result.add(country);
                }
            } else if (qfield == QueryField.LOCATION_COUNTRYCODE) {
                result = new HashSet<String>(100);
                for (LocationImpl loc : db.obtainObjects(LocationImpl.class)) {
                    String code = loc.getCountryISOCode();
                    if (code != null && !code.isEmpty())
                        result.add(code);
                }
            } else if (qfield == QueryField.LOCATION_STATE) {
                result = new HashSet<String>(100);
                for (LocationImpl loc : db.obtainObjects(LocationImpl.class)) {
                    String state = loc.getProvinceOrState();
                    if (state != null && !state.isEmpty())
                        result.add(state);
                }
            } else if (qfield == QueryField.LOCATION_CITY) {
                result = new HashSet<String>(200);
                for (LocationImpl loc : db.obtainObjects(LocationImpl.class)) {
                    String city = loc.getCity();
                    if (city != null && !city.isEmpty())
                        result.add(city);
                }
            } else if (qfield == QueryField.LOCATION_SUBLOCATION) {
                result = new HashSet<String>(300);
                for (LocationImpl loc : db.obtainObjects(LocationImpl.class)) {
                    String sub = loc.getSublocation();
                    if (sub != null && !sub.isEmpty())
                        result.add(sub);
                }
            } else if (qfield == QueryField.CONTACT_COUNTRY) {
                result = new HashSet<String>(100);
                for (ContactImpl contact : db.obtainObjects(ContactImpl.class)) {
                    String country = contact.getCountry();
                    if (country != null && !country.isEmpty())
                        result.add(country);
                }
            } else if (qfield == QueryField.CONTACT_STATE) {
                result = new HashSet<String>(50);
                for (ContactImpl contact : db.obtainObjects(ContactImpl.class)) {
                    String state = contact.getState();
                    if (state != null && !state.isEmpty())
                        result.add(state);
                }
            } else if (qfield == QueryField.CONTACT_CITY) {
                result = new HashSet<String>(50);
                for (ContactImpl contact : db.obtainObjects(ContactImpl.class)) {
                    String city = contact.getCity();
                    if (city != null && !city.isEmpty())
                        result.add(city);
                }
            } else if (qfield == QueryField.CONTACT_POSTALCODE) {
                result = new HashSet<String>(50);
                for (ContactImpl contact : db.obtainObjects(ContactImpl.class)) {
                    String zip = contact.getPostalCode();
                    if (zip != null && !zip.isEmpty())
                        result.add(zip);
                }
            } else if (qfield == QueryField.CONTACT_EMAIL) {
                result = new HashSet<String>(50);
                for (ContactImpl contact : db.obtainObjects(ContactImpl.class)) {
                    String[] emails = contact.getEmail();
                    if (emails != null)
                        for (String email : emails)
                            if (email != null && !email.isEmpty())
                                result.add(email);
                }
            } else if (qfield == QueryField.CONTACT_WEBURL) {
                result = new HashSet<String>(50);
                for (ContactImpl contact : db.obtainObjects(ContactImpl.class)) {
                    String[] urls = contact.getWebUrl();
                    if (urls != null)
                        for (String url : urls)
                            if (url != null && !url.isEmpty())
                                result.add(url);
                }
            } else if (qfield == QueryField.CONTACT_ADDRESS) {
                result = new HashSet<String>(50);
                for (ContactImpl contact : db.obtainObjects(ContactImpl.class)) {
                    String[] lines = contact.getAddress();
                    if (lines != null)
                        for (String line : lines)
                            if (line != null && !line.isEmpty())
                                result.add(line);
                }
            } else if (qfield == QueryField.ARTWORKOROBJECT_COPYRIGHT) {
                result = new HashSet<String>(200);
                for (ArtworkOrObjectImpl art : db.obtainObjects(ArtworkOrObjectImpl.class)) {
                    String copy = art.getCopyrightNotice();
                    if (copy != null && !copy.isEmpty())
                        result.add(copy);
                }
            } else if (qfield == QueryField.ARTWORKOROBJECT_INVENTORYNUMBER) {
                result = new HashSet<String>(200);
                for (ArtworkOrObjectImpl art : db.obtainObjects(ArtworkOrObjectImpl.class)) {
                    String no = art.getSourceInventoryNumber();
                    if (no != null && !no.isEmpty())
                        result.add(no);
                }
            } else if (qfield == QueryField.ARTWORKOROBJECT_TITLE) {
                result = new HashSet<String>(200);
                for (ArtworkOrObjectImpl art : db.obtainObjects(ArtworkOrObjectImpl.class)) {
                    String title = art.getTitle();
                    if (title != null && !title.isEmpty())
                        result.add(title);
                }
            } else if (qfield == QueryField.ARTWORKOROBJECT_SOURCE) {
                result = new HashSet<String>(200);
                for (ArtworkOrObjectImpl art : db.obtainObjects(ArtworkOrObjectImpl.class)) {
                    String copy = art.getSource();
                    if (copy != null && !copy.isEmpty())
                        result.add(copy);
                }
            } else if (qfield == QueryField.ARTWORKOROBJECT_CREATOR) {
                result = new HashSet<String>(200);
                for (ArtworkOrObjectImpl art : db.obtainObjects(ArtworkOrObjectImpl.class)) {
                    String[] creators = art.getCreator();
                    if (creators != null)
                        for (String creator : creators)
                            if (creator != null && !creator.isEmpty())
                                result.add(creator);
                }
            }
        }
        return result;
    }

    public static String[] getValueProposals(IDbManager dbManager, QueryField qfield, QueryField subfield,
            boolean networked) {
        String ticket = null;
        IPeerService peerService = networked ? Core.getCore().getDbFactory().getPeerService() : null;
        try {
            if (peerService != null)
                ticket = peerService.askForValueProposals(qfield.getKey(),
                        subfield == null ? null : subfield.getKey());
            Set<String> valueProposals = getValueProposals(dbManager, qfield.getKey(),
                    subfield == null ? null : subfield.getKey());
            if (valueProposals == null)
                return null;
            if (ticket != null)
                valueProposals.addAll(peerService.getProposals(ticket));
            String[] array = valueProposals.toArray(new String[valueProposals.size()]);
            Arrays.sort(array, stringComparator);
            return array;
        } finally {
            if (ticket != null)
                peerService.discardTask(ticket);
        }
    }

    public static String composeContentDescription(SmartCollection sm, String sep, boolean compact, boolean sort) {
        StringBuilder sb = new StringBuilder();
        AomList<Criterion> criteria = sm.getCriterion();
        for (Criterion crit : criteria) {
            String field = crit.getField();
            Object value = crit.getValue();
            if (ICollectionProcessor.SIMILARITY.equals(field)) {
                if (compact)
                    return sm.getName();
                SimilarityOptions_type options = (SimilarityOptions_type) value;
                if (options != null) {
                    Algorithm algo = Core.getCore().getDbFactory().getLireService(true)
                            .getAlgorithmById(options.getMethod());
                    return NLS.bind(Messages.UiUtilities_based_on_similarity,
                            new Object[] { (int) (options.getMinScore() * 100f + 0.5f), options.getMaxResults(),
                                    sep, algo == null ? "" : " (" + algo.getLabel() + ')' }); //$NON-NLS-1$ //$NON-NLS-2$
                }
            }
            if (ICollectionProcessor.TEXTSEARCH.equals(field)) {
                TextSearchOptions_type options = (TextSearchOptions_type) value;
                if (options != null)
                    return compact ? NLS.bind(Messages.UiUtilities_text_search2, options.getQueryString())
                            : NLS.bind(Messages.UiUtilities_text_search, new Object[] { options.getQueryString(),
                                    (int) (options.getMinScore() * 100f + 0.5f), options.getMaxResults(), sep });
            }
            QueryField qfield = QueryField.findQueryField(field);
            if (qfield != null) {
                if (qfield == QueryField.IPTC_DATECREATED) {
                    SimpleDateFormat df = new SimpleDateFormat(
                            compact ? Messages.UiUtilities_date_format_compact : Messages.UiUtilities_date_format);
                    if (crit.getRelation() == QueryField.EQUALS && value instanceof Date)
                        return NLS.bind(Messages.UiUtilities_time_search, df.format((Date) value));
                    else if (crit.getRelation() == QueryField.BETWEEN && value instanceof Range) {
                        Range range = (Range) value;
                        if (range.getFrom() instanceof Date && range.getTo() instanceof Date)
                            return NLS.bind(Messages.UiUtilities_time_search2, df.format((Date) range.getFrom()),
                                    df.format((Date) range.getTo()));
                    }
                }
                if (sb.length() > 0)
                    sb.append(crit.getAnd() ? Messages.UiUtilities_and : Messages.UiUtilities_or);
                sb.append(qfield.getLabel());
                String subfield = crit.getSubfield();
                if (subfield != null) {
                    QueryField qsub = QueryField.findQuerySubField(field, subfield);
                    if (qsub != null)
                        sb.append(' ').append(qsub.getLabel());
                }
                sb.append(' ');
                int relation = crit.getRelation();
                for (int k = 0; k < QueryField.ALLRELATIONS.length; k++) {
                    int rel = QueryField.ALLRELATIONS[k];
                    if ((rel & relation) != 0) {
                        sb.append(QueryField.ALLRELATIONLABELS[k]);
                        break;
                    }
                }
                sb.append(" \""); //$NON-NLS-1$
                sb.append(value);
                sb.append('"');
            }
        }
        if (sort)
            composeSortDescription(sm, sb);
        return sb.toString();
    }

    public static void composeSortDescription(SmartCollection sm, StringBuilder sb) {
        List<SortCriterion> sortCriterion = sm.getSortCriterion();
        if (!sortCriterion.isEmpty()) {
            sb.append(Messages.UiUtilities_sorted_by);
            boolean and = false;
            for (SortCriterion sc : sortCriterion) {
                String field = sc.getField();
                QueryField qfield = QueryField.findQueryField(field);
                if (qfield != null) {
                    if (and)
                        sb.append(Messages.UiUtilities_and);
                    else
                        and = true;
                    sb.append(qfield.getLabel());
                    String subfield = sc.getSubfield();
                    if (subfield != null) {
                        QueryField qsub = QueryField.findQuerySubField(field, subfield);
                        if (qsub != null)
                            sb.append(' ').append(qsub.getLabel());
                    }
                    sb.append(
                            sc.getDescending() ? Messages.UiUtilities_descending : Messages.UiUtilities_ascending);
                }
            }
        }
    }

    public static Icon getSmartCollectionIcon(SmartCollection coll) {
        if (coll.getAdhoc()) {
            String field = coll.getCriterion(0).getField();
            if (ICollectionProcessor.SIMILARITY.equals(field))
                return Icons.similar;
            if (ICollectionProcessor.TEXTSEARCH.equals(field))
                return Icons.textsearch;
            QueryField qfield = QueryField.findQueryField(field);
            if (qfield == QueryField.IPTC_DATECREATED)
                return Icons.time;
            if (qfield == QueryField.EXIF_GPSLOCATIONDISTANCE)
                return Icons.proximity;
            if (qfield == QueryField.IPTC_KEYWORDS)
                return Icons.goggles;
            return Icons.user_magnify;
        }
        if (coll.getSystem()) {
            if (coll.getStringId().startsWith(IDbManager.CATKEY))
                return Icons.folder_page;
            if (coll.getStringId().startsWith(IDbManager.RATINGKEY))
                return Icons.folder_star;
            if (coll.getStringId().startsWith(IDbManager.URIKEY))
                return Icons.folder_page_white;
            if (coll.getStringId().startsWith(IDbManager.VOLUMEKEY))
                return Icons.folder_database;
            if (coll.getStringId().startsWith(IDbManager.DATETIMEKEY))
                return Icons.folder_clock;
            if (coll.getStringId().startsWith(IDbManager.LOCATIONKEY))
                return Icons.folder_world;
            if (coll.getStringId().equals(Constants.LAST_IMPORT_ID))
                return Icons.lastImport;
            return coll.getAlbum() ? Icons.folder_person : Icons.folder_image;
        }
        if (!coll.getAlbum() && coll.getNetwork())
            return coll.getSmartCollection_subSelection_parent() == null ? Icons.folder_add_network
                    : Icons.folder_and_network;
        if (coll.getStringId().startsWith(IDbManager.IMPORTKEY)) {
            if (!coll.getCriterion().isEmpty()) {
                Criterion crit = coll.getCriterion().get(0);
                if (crit != null && crit.getValue() instanceof TetheredRange)
                    return Icons.folder_tethered;
            }
            return Icons.folder_import;
        }
        return coll.getAlbum() ? Icons.folder_album
                : coll.getSmartCollection_subSelection_parent() == null ? Icons.folder_add : Icons.folder_and;
    }

    public static boolean isImport(SmartCollection sm) {
        if (sm == null)
            return false;
        String group = sm.getGroup_rootCollection_parent();
        return (Constants.GROUP_ID_IMPORTS.equals(group) || Constants.GROUP_ID_RECENTIMPORTS.equals(group));
    }

    public static ArrayList<ImageRegion> drawRegions(GC gc, Asset asset, int xs, int ys, int width, int height,
            boolean selected, int showRegions, boolean onlyFaces, Object persId) {
        Device device = gc.getDevice();
        String[] regionIds = asset.getPerson();
        ArrayList<ImageRegion> regions = null;
        Set<Rectangle> rectangleOrOvalList = new HashSet<Rectangle>();
        if (regionIds != null && regionIds.length > 0) {
            int rotation = asset.getRotation();
            regions = new ArrayList<ImageRegion>(regionIds.length);
            gc.setClipping(xs, ys, width, height);
            int i = 0;
            boolean[] done = null;
            if (selected) {
                IDbManager dbManager = Core.getCore().getDbManager();
                for (RegionImpl region : dbManager.obtainObjects(RegionImpl.class, "asset_person_parent", //$NON-NLS-1$
                        asset.getStringId(), QueryField.EQUALS)) {
                    if (onlyFaces && region.getType() != null && !region.getType().equals(Region.type_face))
                        continue;
                    if (i++ > showRegions)
                        break;
                    String id = region.getStringId();
                    Rectangle frame = computeFrame(id, xs, ys, width, height, rotation);
                    if (frame != null && !isDoubledRegion(rectangleOrOvalList, frame)) {
                        for (int j = 0; j < regionIds.length; j++)
                            if (regionIds[j].equals(id)) {
                                if (done == null)
                                    done = new boolean[regionIds.length];
                                done[j] = true;
                            }
                        rectangleOrOvalList.add(frame);
                        gc.setForeground(JFaceResources.getColorRegistry().get(Constants.APPCOLOR_REGION_FACE));
                        if (frame.height < 0) {
                            int d = frame.width;
                            gc.drawOval(frame.x - d / 2, frame.y - d / 2, d, d);
                        } else
                            gc.drawRectangle(frame);
                        String type = region.getType();
                        String name = null;
                        int color = SWT.COLOR_RED;
                        if (type == null || type.isEmpty() || Region.type_face.equals(type)
                                || Region.type_pet.equals(type)) {
                            String albumId = region.getAlbum();
                            SmartCollectionImpl album = albumId == null ? null
                                    : dbManager.obtainById(SmartCollectionImpl.class, albumId);
                            name = album != null ? album.getName() : "?"; //$NON-NLS-1$
                            if (persId == null || persId.equals(albumId))
                                color = SWT.COLOR_YELLOW;
                        } else if (Region.type_barCode.equals(type)) {
                            name = region.getAlbum();
                            color = SWT.COLOR_GREEN;
                        }
                        regions.add(
                                new ImageRegion(frame, id, type == null ? Region.type_face : type, name, asset));
                        if (name != null) {
                            gc.setForeground(device.getSystemColor(SWT.COLOR_DARK_GRAY));
                            gc.drawText(name, frame.x + 5, frame.y + 3, true);
                            gc.setForeground(device.getSystemColor(color));
                            gc.drawText(name, frame.x + 4, frame.y + 2, true);
                        }
                    }
                }
            }
            gc.setForeground(JFaceResources.getColorRegistry().get(Constants.APPCOLOR_REGION_FACE));
            for (int j = 0; j < regionIds.length; j++) {
                if (done != null && done[j])
                    continue;
                if (i++ > showRegions)
                    break;
                Rectangle frame = computeFrame(regionIds[j], xs, ys, width, height, rotation);
                if (frame != null && !isDoubledRegion(rectangleOrOvalList, frame)) {
                    rectangleOrOvalList.add(frame);
                    if (frame.height < 0) {
                        int d = frame.width;
                        gc.drawOval(frame.x - d / 2, frame.y - d / 2, d, d);
                    } else
                        gc.drawRectangle(frame);
                    regions.add(new ImageRegion(frame, regionIds[j], null, null, asset));
                }
            }
            gc.setClipping((Rectangle) null);
        }
        return regions;
    }

    private static final int QUARTERIMAGE = 65536 / 4;

    public static Image getFace(Device device, SmartCollection sm, int size, int margins,
            org.eclipse.swt.graphics.Color marginColor) {
        IDbManager dbManager = Core.getCore().getDbManager();
        int maxW = -1;
        RegionImpl maxRegion = null;
        List<RegionImpl> regions = dbManager.obtainObjects(RegionImpl.class, "album", sm.getStringId(), //$NON-NLS-1$
                QueryField.EQUALS);
        int noRegions = regions.size();
        int trials = Math.min(5, noRegions);
        while (noRegions > 0) {
            int j = (int) (Math.random() * noRegions);
            RegionImpl region = regions.get(j);
            String rect64 = region.getStringId();
            int l = rect64.length();
            if (l > 12) {
                int leadingZeros = Math.max(0, 16 - l);
                int h1 = Utilities.parseHex(rect64, 0 - leadingZeros, 4 - leadingZeros);
                int h2 = Utilities.parseHex(rect64, 4 - leadingZeros, 8 - leadingZeros);
                int h3 = Utilities.parseHex(rect64, 8 - leadingZeros, 12 - leadingZeros);
                int h4 = Utilities.parseHex(rect64, 12 - leadingZeros, 16 - leadingZeros);
                int w = Math.max(h4 - h2, h3 - h1);
                if (w > maxW) {
                    maxW = w;
                    maxRegion = region;
                }
            }
            if (maxW > QUARTERIMAGE || --trials <= 0)
                break;
        }
        Image image = null;
        Rectangle frame = null;
        if (maxRegion != null) {
            Asset asset = dbManager.obtainAsset(maxRegion.getAsset_person_parent());
            if (asset != null) {
                image = Core.getCore().getImageCache().getImage(asset);
                Rectangle bounds = image.getBounds();
                frame = UiUtilities.computeFrame(maxRegion.getStringId(), 0, 0, bounds.width, bounds.height,
                        asset.getRotation());
            }
        }
        if (image == null) {
            image = Icons.person64.getImage();
            frame = image.getBounds();
        }
        Image thumb = new Image(device, size + 2 * margins, size + 2 * margins);
        float f = size / (float) Math.max(Math.min(frame.width, frame.height), size / 2);
        int destWidth = (int) (frame.width * f + 0.5);
        int destHeight = (int) (frame.height * f + 0.5);
        int srcWidth = frame.width;
        int srcHeight = frame.height;
        int srcX = frame.x;
        int srcY = frame.y;
        if (destWidth < size || destHeight < size) {
            float fac2 = Math.max(size / (float) destWidth, size / (float) destHeight);
            srcWidth = (int) (srcWidth * fac2 + 0.5);
            srcHeight = (int) (srcHeight * fac2 + 0.5);
            destWidth = (int) (destWidth * fac2 + 0.5);
            destHeight = (int) (destHeight * fac2 + 0.5);
            srcX = Math.max(0, srcX - (srcWidth - frame.width) / 2);
            srcY = Math.max(0, srcY - (srcHeight - frame.height) / 2);
        }
        int destX = (size - destWidth) / 2;
        int destY = (size - destHeight) / 2;
        GC gc = new GC(thumb);
        if (margins > 0) {
            gc.setBackground(marginColor);
            gc.fillRectangle(0, 0, size + 2 * margins, size + 2 * margins);
        }
        if (Platform.getPreferencesService().getBoolean(UiActivator.PLUGIN_ID, PreferenceConstants.ADVANCEDGRAPHICS,
                false, null)) {
            gc.setAntialias(SWT.ON);
            gc.setInterpolation(SWT.HIGH);
        }
        gc.drawImage(image, srcX, srcY, srcWidth, srcHeight, destX + margins, destY + margins, destWidth,
                destHeight);
        gc.dispose();
        return thumb;
    }

    public static boolean isInterPunction(char c) {
        return "!.,:;?".indexOf(c) >= 0; //$NON-NLS-1$
    }

    public static void installDoubleClickExpansion(TreeViewer viewer) {
        viewer.addDoubleClickListener(new IDoubleClickListener() {
            @Override
            public void doubleClick(DoubleClickEvent event) {
                Object item = ((IStructuredSelection) event.getSelection()).getFirstElement();
                if (item != null && viewer.isExpandable(item)) {
                    if (viewer.getExpandedState(item))
                        viewer.collapseToLevel(item, 1);
                    else
                        viewer.expandToLevel(item, 1);
                }
            }
        });
    }

    public static IViewPart showView(String id) {
        IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        if (activeWorkbenchWindow != null) {
            IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage();
            if (activePage != null)
                try {
                    return activePage.showView(id);
                } catch (PartInitException e1) {
                    // do nothing
                }
        }
        return null;
    }

    public static String[] getComboHistory(Combo combo, char c) {
        String[] items = combo.getItems();
        List<String> list = new ArrayList<>(items.length);
        for (String s : items)
            if (s.indexOf(c) < 0)
                list.add(s);
        return list.toArray(new String[list.size()]);
    }

    public static Image getAnnotationIcon(Asset asset) {
        String voiceFileURI = asset.getVoiceFileURI();
        if (voiceFileURI != null && !voiceFileURI.isEmpty()) {
            int p = voiceFileURI.indexOf('\f');
            if (p < 0)
                return (voiceFileURI.startsWith("?") ? Icons.note : Icons.speaker).getImage(); //$NON-NLS-1$
            if (p > 0)
                return (voiceFileURI.length() > p + 2 ? Icons.speakerPlus : Icons.speaker).getImage();
            int q = voiceFileURI.indexOf('\f', p + 1);
            if (q > p + 1)
                return (voiceFileURI.length() > q + 1 ? Icons.notePlus : Icons.note).getImage();
            if (q < voiceFileURI.length() - 1)
                return Icons.drawing.getImage();
        }
        return null;
    }

    public static Date getCreationDate(Asset asset) {
        Date date = asset.getDateCreated();
        if (date == null)
            date = asset.getDateTimeOriginal();
        if (date == null)
            date = asset.getDateTime();
        return date;
    }

    private static final String ELLIPSIS = "..."; //$NON-NLS-1$

    public static String shortenText(String s, int maxchars) {
        if (s == null || s.length() <= maxchars)
            return s;
        return s.substring(0, maxchars) + ELLIPSIS;
    }

}