org.rssowl.ui.internal.search.SearchConditionList.java Source code

Java tutorial

Introduction

Here is the source code for org.rssowl.ui.internal.search.SearchConditionList.java

Source

/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2005-2009 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   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.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     RSSOwl Development Team - initial API and implementation             **
 **                                                                          **
 **  **********************************************************************  */

package org.rssowl.ui.internal.search;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
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.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.rssowl.core.Owl;
import org.rssowl.core.persist.IEntity;
import org.rssowl.core.persist.IModelFactory;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.ISearchCondition;
import org.rssowl.core.persist.ISearchField;
import org.rssowl.core.persist.ISearchMark;
import org.rssowl.core.persist.SearchSpecifier;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.util.JobRunner;
import org.rssowl.ui.internal.util.LayoutUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * The <code>SearchConditionList</code> is a scrolled composite that allows to
 * define a set of <code>ISearchCondition</code>s. The UI allows to add, remove
 * and edit single Conditions.
 *
 * @author bpasero
 */
public class SearchConditionList extends ScrolledComposite {
    private List<SearchConditionItem> fItems;
    private final List<Integer> fFieldsToExclude;
    private Composite fContainer;
    private LocalResourceManager fResources;
    private Image fAddIcon;
    private Image fAddDisabledIcon;
    private Image fDeleteIcon;
    private Image fDeleteDisabledIcon;
    private boolean fModified;
    private int fVisibleItemCount = 3;

    /**
     * @param parent The parent Composite.
     * @param style The Style as defined by SWT constants.
     * @param conditions The initial conditions this List is showing.
     */
    public SearchConditionList(Composite parent, int style, List<ISearchCondition> conditions) {
        this(parent, style, conditions, null);
    }

    /**
     * @param parent The parent Composite.
     * @param style The Style as defined by SWT constants.
     * @param conditions The initial conditions this List is showing.
     * @param fieldsToExclude A list of search fields to exclude from the UI.
     */
    public SearchConditionList(Composite parent, int style, List<ISearchCondition> conditions,
            List<Integer> fieldsToExclude) {
        super(parent, style | SWT.V_SCROLL);
        fItems = new ArrayList<SearchConditionItem>();
        fFieldsToExclude = fieldsToExclude;
        fResources = new LocalResourceManager(JFaceResources.getResources(), this);

        initResources();
        initComponents(conditions);
    }

    /**
     * Sets the number of <code>SearchConditionItem</code>s that should be visible
     * in the List. If the number of items is higher, scrollbars will be shown
     * automatically.
     *
     * @param count the number of <code>SearchConditionItem</code>s that should be
     * visible in the List.
     */
    public void setVisibleItemCount(int count) {
        Assert.isLegal(count >= 0);
        fVisibleItemCount = count;
    }

    /**
     * Returns <code>TRUE</code> when this List has no items with a specific
     * value, and <code>FALSE</code> otherwise.
     *
     * @return <code>TRUE</code> when this List has no items with a specific
     * value, and <code>FALSE</code> otherwise.
     */
    public boolean isEmpty() {
        for (SearchConditionItem item : fItems) {
            if (item.hasValue())
                return false;
        }

        return true;
    }

    /*
     * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean)
     */
    @Override
    public Point computeSize(int wHint, int hHint, boolean changed) {
        Point point = super.computeSize(wHint, hHint, changed);

        /* Compute from Condition Item */
        if (fVisibleItemCount > 0 && fItems.size() > 0) {
            int itemHeight = fItems.get(0).computeSize(wHint, hHint).y + 4;
            point.y = fVisibleItemCount * itemHeight;
        }

        return point;
    }

    /**
     * Removes all but the first Items from the List.
     */
    public void reset() {
        setRedraw(false);
        try {

            /* Remove all Items */
            if (fItems.size() > 0) {
                List<SearchConditionItem> itemsToRemove = new ArrayList<SearchConditionItem>(fItems);
                for (SearchConditionItem itemToRemove : itemsToRemove) {
                    itemToRemove.getParent().dispose();
                    removeItem(itemToRemove);
                }
            }

            /* Add default */
            addItem(getDefaultCondition());
        } finally {
            setRedraw(true);
        }

        focusInput();
        fModified = true;
    }

    /**
     * Passes focus to the first item of this List.
     */
    public void focusInput() {
        focusInput(0);
    }

    /**
     * Passes focus to the Item in the list at the given index.
     *
     * @param index the index of the item to focus.
     */
    public void focusInput(int index) {
        if (fItems.size() > index)
            fItems.get(index).focusInput();
    }

    /**
     * @return Returns a List of <code>ISearchCondition</code> representing the
     * selected states.
     * @see SearchConditionList#createConditions(ISearchMark)
     */
    public List<ISearchCondition> createConditions() {
        return createConditions(null);
    }

    /**
     * @param searchmark The parent of the <code>ISearchCondition</code>s that are
     * being created.
     * @return Returns a List of <code>ISearchCondition</code> representing the
     * selected states.
     * @see SearchConditionList#createConditions()
     */
    public List<ISearchCondition> createConditions(ISearchMark searchmark) {
        List<ISearchCondition> conditions = new ArrayList<ISearchCondition>();

        /* For each Item */
        for (SearchConditionItem item : fItems) {
            ISearchCondition condition = item.createCondition(searchmark, true);
            if (condition != null)
                conditions.add(condition);
        }

        return conditions;
    }

    /**
     * Shows the List of <code>ISearchCondition</code> in this List.
     *
     * @param conditions the List of <code>ISearchCondition</code> to show in this
     * List.
     */
    public void showConditions(List<ISearchCondition> conditions) {
        fModified = true;
        setRedraw(false);
        try {

            /* Remove all */
            List<SearchConditionItem> itemsToRemove = new ArrayList<SearchConditionItem>(fItems);
            for (SearchConditionItem itemToRemove : itemsToRemove) {
                itemToRemove.getParent().dispose();
                removeItem(itemToRemove);
            }

            /* Add Conditions */
            if (conditions != null && !conditions.isEmpty()) {
                for (ISearchCondition condition : conditions)
                    addItem(condition);
            } else
                addItem(getDefaultCondition());
        } finally {
            setRedraw(true);
        }
    }

    /**
     * Optimization: In order to check weather conditions in the list have been
     * modified, this method can be used. Note that this method will also return
     * <code>TRUE</code> if a condition was modified and then reset to its initial
     * value.
     *
     * @return Returns <code>TRUE</code> if the list of conditions was potentially
     * modified (conditions added, removed or updated) and <code>FALSE</code>
     * otherwise.
     */
    public boolean isModified() {
        if (fModified)
            return true;

        for (SearchConditionItem item : fItems) {
            if (item.isModified())
                return true;
        }

        return false;
    }

    private void initResources() {
        fAddIcon = OwlUI.getImage(fResources, "icons/etool16/add.gif"); //$NON-NLS-1$
        fAddDisabledIcon = OwlUI.getImage(fResources, "icons/dtool16/add.gif"); //$NON-NLS-1$
        fDeleteIcon = OwlUI.getImage(fResources, "icons/etool16/remove.gif"); //$NON-NLS-1$
        fDeleteDisabledIcon = OwlUI.getImage(fResources, "icons/dtool16/remove.gif"); //$NON-NLS-1$
    }

    private void initComponents(List<ISearchCondition> conditions) {

        /* Adjust Scrolled Composite */
        setLayout(new GridLayout(1, false));
        setExpandHorizontal(true);
        setExpandVertical(true);
        if (getVerticalBar() != null)
            getVerticalBar().setIncrement(10);

        /* Create the Container */
        fContainer = new Composite(this, SWT.NONE);
        fContainer.setLayout(LayoutUtils.createGridLayout(1, 0, 0));
        fContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        setContent(fContainer);

        /* Add Conditions */
        if (conditions != null && !conditions.isEmpty()) {
            for (ISearchCondition condition : conditions)
                addItem(condition);
        } else
            addItem(getDefaultCondition());

        /* Update Size */
        updateSize();
    }

    private ISearchCondition getDefaultCondition() {
        IModelFactory factory = Owl.getModelFactory();

        ISearchField field = factory.createSearchField(IEntity.ALL_FIELDS, INews.class.getName());
        ISearchCondition condition = factory.createSearchCondition(field, SearchSpecifier.CONTAINS_ALL, ""); //$NON-NLS-1$

        return condition;
    }

    SearchConditionItem addItem(ISearchCondition condition) {
        return addItem(condition, fItems.size());
    }

    SearchConditionItem addItem(ISearchCondition condition, int index) {
        return addItem(condition, index, false);
    }

    SearchConditionItem addItem(ISearchCondition condition, int index, boolean scroll) {
        boolean wasScrollbarShowing = getVerticalBar() != null ? getVerticalBar().isVisible() : false;

        /* Container for Item */
        final Composite itemContainer = new Composite(fContainer, SWT.NONE);
        itemContainer.setLayout(LayoutUtils.createGridLayout(2, 0, 0, 0, 0, false));
        itemContainer.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));

        /* Create Item */
        final SearchConditionItem item = new SearchConditionItem(itemContainer, SWT.NONE, condition,
                fFieldsToExclude);
        item.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));

        /* Create Button Box */
        final ToolBar buttonBar = new ToolBar(itemContainer, SWT.FLAT);
        buttonBar.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));

        /* Button to add Condition */
        ToolItem addButton = new ToolItem(buttonBar, SWT.DROP_DOWN);
        addButton.setImage(fAddIcon);
        addButton.setDisabledImage(fAddDisabledIcon);
        addButton.setToolTipText(Messages.SearchConditionList_ADD_CONDITION);

        /* Add Menu */
        final Menu conditionMenu = new Menu(buttonBar);
        createConditionMenu(conditionMenu, item);
        addButton.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                if (event.detail == SWT.ARROW) {
                    Rectangle rect = item.getBounds();
                    Point pt = new Point(rect.x, rect.y + rect.height);
                    pt = buttonBar.toDisplay(pt);
                    conditionMenu.setLocation(pt.x, pt.y);
                    conditionMenu.setVisible(true);
                } else
                    onAdd(item);
            }
        });

        buttonBar.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                OwlUI.safeDispose(conditionMenu);
            }
        });

        /* Button to delete Condition */
        ToolItem deleteButton = new ToolItem(buttonBar, SWT.PUSH);
        deleteButton.setImage(fDeleteIcon);
        deleteButton.setDisabledImage(fDeleteDisabledIcon);
        deleteButton.setToolTipText(Messages.SearchConditionList_DELETE_CONDITION);
        deleteButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                JobRunner.runInUIThread(0, true, buttonBar, new Runnable() {
                    public void run() {
                        onDelete(item, itemContainer);
                    }
                });
            }
        });

        /* Add to the End */
        boolean addedToEnd = false;
        if (index == fItems.size()) {
            addedToEnd = true;
            fItems.add(item);
        }

        /* Add to specific Index */
        else {
            SearchConditionItem oldItem = fItems.get(index);
            fItems.add(index, item);
            item.getParent().moveAbove(oldItem.getParent());
        }

        /* Force Layout */
        layout(true, true);
        update();

        /* Update Size */
        updateSize();
        OwlUI.adjustSizeForScrollbar(getShell(), getVerticalBar(), wasScrollbarShowing);

        /* Scroll to Bottom if added as last element */
        if (scroll && addedToEnd)
            setOrigin(0, getContent().getSize().y);

        return item;
    }

    private void createConditionMenu(Menu menu, SearchConditionItem item) {
        IModelFactory factory = Owl.getModelFactory();
        String news = INews.class.getName();

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(IEntity.ALL_FIELDS)) {
            MenuItem mItem = new MenuItem(menu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_ENTIRE_NEWS);
            hookSelectionListener(mItem, item, factory.createSearchField(IEntity.ALL_FIELDS, news));

            new MenuItem(menu, SWT.SEPARATOR);
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.STATE)) {
            MenuItem mItem = new MenuItem(menu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_STATE);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.STATE, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.LOCATION)) {
            MenuItem mItem = new MenuItem(menu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_LOCATION);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.LOCATION, news));

            new MenuItem(menu, SWT.SEPARATOR);
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.TITLE)) {
            MenuItem mItem = new MenuItem(menu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_TITLE);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.TITLE, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.DESCRIPTION)) {
            MenuItem mItem = new MenuItem(menu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_DESCRIPTION);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.DESCRIPTION, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.AUTHOR)) {
            MenuItem mItem = new MenuItem(menu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_AUTHOR);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.AUTHOR, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.CATEGORIES)) {
            MenuItem mItem = new MenuItem(menu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_CATEGORY);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.CATEGORIES, news));
        }

        MenuItem mItem = new MenuItem(menu, SWT.CASCADE);
        mItem.setText(Messages.SearchConditionList_DATE);

        Menu dateMenu = new Menu(mItem);
        mItem.setMenu(dateMenu);

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.AGE_IN_DAYS)) {
            mItem = new MenuItem(dateMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_AGE);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.AGE_IN_DAYS, news));

            new MenuItem(dateMenu, SWT.SEPARATOR);
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.MODIFIED_DATE)) {
            mItem = new MenuItem(dateMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_DATE_MODIFIED);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.MODIFIED_DATE, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.PUBLISH_DATE)) {
            mItem = new MenuItem(dateMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_DATE_PUBLISHED);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.PUBLISH_DATE, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.RECEIVE_DATE)) {
            mItem = new MenuItem(dateMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_DATE_RECEIVED);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.RECEIVE_DATE, news));
        }

        mItem = new MenuItem(menu, SWT.SEPARATOR);

        mItem = new MenuItem(menu, SWT.CASCADE);
        mItem.setText(Messages.SearchConditionList_OTHER);

        Menu otherMenu = new Menu(mItem);
        mItem.setMenu(otherMenu);

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.HAS_ATTACHMENTS)) {
            mItem = new MenuItem(otherMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_HAS_ATTACHMENTS);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.HAS_ATTACHMENTS, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.ATTACHMENTS_CONTENT)) {
            mItem = new MenuItem(otherMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_ATTACHMENT);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.ATTACHMENTS_CONTENT, news));
        }

        new MenuItem(otherMenu, SWT.SEPARATOR);

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.SOURCE)) {
            mItem = new MenuItem(otherMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_SOURCE);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.SOURCE, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.LINK)) {
            mItem = new MenuItem(otherMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_LINK);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.LINK, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.IS_FLAGGED)) {
            mItem = new MenuItem(otherMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_IS_STICKY);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.IS_FLAGGED, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.FEED)) {
            mItem = new MenuItem(otherMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_FEED);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.FEED, news));
        }

        if (fFieldsToExclude == null || !fFieldsToExclude.contains(INews.LABEL)) {
            mItem = new MenuItem(otherMenu, SWT.PUSH);
            mItem.setText(Messages.SearchConditionList_LABEL);
            hookSelectionListener(mItem, item, factory.createSearchField(INews.LABEL, news));
        }
    }

    private void hookSelectionListener(MenuItem item, final SearchConditionItem condition,
            final ISearchField field) {
        item.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onAdd(condition, field);
            }
        });
    }

    int indexOf(SearchConditionItem item) {
        return fItems.indexOf(item);
    }

    void onAdd(SearchConditionItem selectedItem, ISearchField field) {
        ISearchCondition condition = createCondition(selectedItem.createCondition(null, false));
        condition.setField(field);

        SearchConditionItem addedItem = addItem(condition, indexOf(selectedItem) + 1, true);
        addedItem.focusInput();

        fModified = true;
    }

    void onAdd(SearchConditionItem selectedItem) {
        ISearchCondition condition = createCondition(selectedItem.createCondition(null, false));
        SearchConditionItem addedItem = addItem(condition, indexOf(selectedItem) + 1, true);
        addedItem.focusInput();

        fModified = true;
    }

    void onDelete(final SearchConditionItem item, final Composite itemContainer) {
        boolean wasScrollbarShowing = getVerticalBar() != null ? getVerticalBar().isVisible() : false;

        /* Delete */
        itemContainer.dispose();
        removeItem(item);
        fModified = true;

        /* Restore Default if required */
        if (fItems.size() == 0)
            addItem(getDefaultCondition()).focusInput();

        OwlUI.adjustSizeForScrollbar(getShell(), getVerticalBar(), wasScrollbarShowing);
    }

    private ISearchCondition createCondition(ISearchCondition current) {
        IModelFactory factory = Owl.getModelFactory();
        ISearchField field = factory.createSearchField(current.getField().getId(),
                current.getField().getEntityName());
        return factory.createSearchCondition(field, current.getSpecifier(), ""); //$NON-NLS-1$
    }

    void removeItem(SearchConditionItem item) {

        /* Dispose and Remove */
        item.dispose();
        fItems.remove(item);

        /* Force Layout */
        layout(true, true);
        update();

        /* Update Size */
        updateSize();
    }

    private void updateSize() {
        setMinSize(fContainer.computeSize(SWT.DEFAULT, SWT.DEFAULT));
    }
}