Package org.rssowl.ui.internal.dialogs

Source Code of org.rssowl.ui.internal.dialogs.SearchNewsDialog$FeedColumnToolTipSupport

/*   **********************************************************************  **
**   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.dialogs;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.SameShellProvider;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.URLTransfer;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
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.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.persist.pref.DefaultPreferences;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.IEntity;
import org.rssowl.core.persist.IFolderChild;
import org.rssowl.core.persist.ILabel;
import org.rssowl.core.persist.IModelFactory;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INewsBin;
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.core.persist.INews.State;
import org.rssowl.core.persist.dao.DynamicDAO;
import org.rssowl.core.persist.dao.INewsDAO;
import org.rssowl.core.persist.event.LabelAdapter;
import org.rssowl.core.persist.event.LabelEvent;
import org.rssowl.core.persist.event.NewsEvent;
import org.rssowl.core.persist.event.NewsListener;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.persist.reference.NewsReference;
import org.rssowl.core.persist.service.IModelSearch;
import org.rssowl.core.persist.service.PersistenceException;
import org.rssowl.core.util.CoreUtils;
import org.rssowl.core.util.DateUtils;
import org.rssowl.core.util.LoggingSafeRunnable;
import org.rssowl.core.util.Pair;
import org.rssowl.core.util.SearchHit;
import org.rssowl.core.util.StringUtils;
import org.rssowl.core.util.URIUtils;
import org.rssowl.ui.internal.Activator;
import org.rssowl.ui.internal.Application;
import org.rssowl.ui.internal.ApplicationActionBarAdvisor;
import org.rssowl.ui.internal.ApplicationWorkbenchWindowAdvisor;
import org.rssowl.ui.internal.ContextMenuCreator;
import org.rssowl.ui.internal.Controller;
import org.rssowl.ui.internal.EntityGroup;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.actions.AutomateFilterAction;
import org.rssowl.ui.internal.actions.MakeNewsStickyAction;
import org.rssowl.ui.internal.actions.MoveCopyNewsToBinAction;
import org.rssowl.ui.internal.actions.OpenInExternalBrowserAction;
import org.rssowl.ui.internal.actions.OpenNewsAction;
import org.rssowl.ui.internal.actions.ToggleReadStateAction;
import org.rssowl.ui.internal.actions.CreateFilterAction.PresetAction;
import org.rssowl.ui.internal.editors.feed.NewsBrowserLabelProvider;
import org.rssowl.ui.internal.editors.feed.NewsBrowserViewer;
import org.rssowl.ui.internal.editors.feed.NewsColumn;
import org.rssowl.ui.internal.editors.feed.NewsColumnViewModel;
import org.rssowl.ui.internal.editors.feed.NewsComparator;
import org.rssowl.ui.internal.editors.feed.NewsTableLabelProvider;
import org.rssowl.ui.internal.search.LocationControl;
import org.rssowl.ui.internal.search.SearchConditionList;
import org.rssowl.ui.internal.undo.NewsStateOperation;
import org.rssowl.ui.internal.undo.UndoStack;
import org.rssowl.ui.internal.util.CTable;
import org.rssowl.ui.internal.util.JobRunner;
import org.rssowl.ui.internal.util.LayoutUtils;
import org.rssowl.ui.internal.util.ModelUtils;
import org.rssowl.ui.internal.util.UIBackgroundJob;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
* The <code>SearchNewsDialog</code> allows to define a number of
* <code>ISearchCondition</code>s to search in all News. The result is given out
* in a Table-Control below.
*
* @author bpasero
*/
public class SearchNewsDialog extends TitleAreaDialog {

  /* Min width of the dialog in DLUs */
  private static final int DIALOG_MIN_WIDTH = 500;

  /* Sash Weights when Preview is invisible */
  private static final int[] TWO_SASH_WEIGHTS = new int[] { 40, 60, 0 };

  /* Sash Weights when Preview is visible */
  private static final int[] THREE_SASH_WEIGHTS = new int[] { 33, 33, 33 };

  /* Section for Dialogs Settings */
  private static final String SETTINGS_SECTION = "org.rssowl.ui.internal.dialogs.SearchNewsDialog"; //$NON-NLS-1$

  /* Preference: Sash Weights */
  private static final String PREF_SASH_WEIGHTS = "org.rssowl.ui.internal.dialogs.search.SashWeights"; //$NON-NLS-1$

  /* Columns Action */
  private static final String COLUMNS_ACTION = "org.rssowl.ui.internal.dialogs.search.ColumnsAction"; //$NON-NLS-1$

  /* Searches Action */
  private static final String SEARCHES_ACTION = "org.rssowl.ui.internal.dialogs.search.SearchesAction"; //$NON-NLS-1$

  /* Number of News to preload before showing as result */
  private static final int NUM_PRELOADED = 20;

  /* Count number of open Dialogs */
  private static int fgOpenDialogCount;

  /* Button IDs */
  private static final int BUTTON_SEARCH = 1000;
  private static final int BUTTON_CLEAR = 1001;

  /* Viewer and Controls */
  private Button fMatchAllRadio;
  private Button fMatchAnyRadio;
  private LocationControl fLocationControl;
  private SearchConditionList fSearchConditionList;
  private CTable fCustomTable;
  private TableViewer fResultViewer;
  private NewsColumnViewModel fColumnModel;
  private ScoredNewsComparator fNewsSorter;
  private Link fStatusLabel;
  private NewsBrowserViewer fBrowserViewer;
  private NewsTableLabelProvider fNewsTableLabelProvider;
  private int[] fCachedWeights;
  private boolean fUseLowScoreFilter;
  private AtomicInteger fLowScoreNewsFilteredCount = new AtomicInteger(0);

  /* Misc. */
  private LocalResourceManager fResources;
  private IDialogSettings fDialogSettings;
  private IModelSearch fModelSearch;
  private NewsListener fNewsListener;
  private boolean fFirstTimeOpen;
  private boolean fShowsHandCursor;
  private Cursor fHandCursor;
  private ISearchCondition fInitialScope;
  private List<ISearchCondition> fInitialConditions;
  private boolean fRunSearch;
  private boolean fMatchAllConditions;
  private INewsDAO fNewsDao;
  private IPreferenceScope fPreferences;
  private LabelAdapter fLabelListener;
  private boolean fIsPreviewVisible;
  private SashForm fSashForm;
  private Composite fBottomSash;
  private List<ISearchCondition> fCurrentSearchConditions;
  private long fLastColumnActionInvokedMillies;
  private Menu fAttachmentsMenu;

  /* Container for a search result */
  private static class ScoredNews {
    private NewsReference fNewsRef;
    private INews fResolvedNews;
    private Float fScore;
    private Relevance fRelevance;
    private final State fState;

    ScoredNews(NewsReference newsRef, INews.State state, Float score, Relevance relevance) {
      fNewsRef = newsRef;
      fState = state;
      fScore = score;
      fRelevance = relevance;
    }

    INews getNews() {
      if (fResolvedNews == null)
        fResolvedNews = fNewsRef.resolve();

      return fResolvedNews;
    }

    INews.State getState() {
      return fState;
    }

    NewsReference getNewsReference() {
      return fNewsRef;
    }

    Float getScore() {
      return fScore;
    }

    Relevance getRelevance() {
      return fRelevance;
    }
  }

  /* ScoredNews Relevance */
  private enum Relevance {

    /** Indicates Low Relevance */
    LOW,

    /** Indicates Medium Relevance */
    MEDIUM,

    /** Indicates High Relevance */
    HIGH;
  }

  /* Comparator for Scored News */
  private static class ScoredNewsComparator extends ViewerComparator implements Comparator<ScoredNews> {
    private NewsComparator fNewsComparator = new NewsComparator();

    /*
     * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
     * java.lang.Object, java.lang.Object)
     */
    @Override
    public int compare(Viewer viewer, Object e1, Object e2) {

      /* Unlikely to happen */
      if (!(e1 instanceof ScoredNews) || !(e2 instanceof ScoredNews))
        return 0;

      /* Proceed comparing Scored News */
      return compare((ScoredNews) e1, (ScoredNews) e2);
    }

    /*
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    public int compare(ScoredNews news1, ScoredNews news2) {

      /* Not sorting by Score */
      if (fNewsComparator.getSortBy() != NewsColumn.RELEVANCE)
        return fNewsComparator.compare(news1.getNews(), news2.getNews());

      /* Sort by Score */
      if (!news1.getScore().equals(news2.getScore())) {
        int result = news1.getScore().compareTo(news2.getScore());
        return fNewsComparator.isAscending() ? result : result * -1;
      }

      /* Default: Sort by Date */
      Date date1 = DateUtils.getRecentDate(news1.getNews());
      Date date2 = DateUtils.getRecentDate(news2.getNews());

      return date2.compareTo(date1);
    }

    void setAscending(boolean ascending) {
      fNewsComparator.setAscending(ascending);
    }

    void setSortBy(NewsColumn sortColumn) {
      fNewsComparator.setSortBy(sortColumn);
    }

    NewsColumn getSortBy() {
      return fNewsComparator.getSortBy();
    }

    boolean isAscending() {
      return fNewsComparator.isAscending();
    }
  }

  /* Filters out Low Score Hits for the first search running */
  private class FirstTimeLowScoreFilter extends ViewerFilter {
    private final AtomicBoolean fEnabled = new AtomicBoolean(true);

    /*
     * @see org.eclipse.jface.viewers.ViewerFilter#filter(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object[])
     */
    @Override
    public Object[] filter(Viewer viewer, Object parent, Object[] elements) {
      Object[] result = elements;

      if (fEnabled.get()) {
        result = super.filter(viewer, parent, elements);
        fEnabled.set(false);
      }

      fLowScoreNewsFilteredCount.set(elements.length - result.length);

      return result;
    }

    /*
     * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
     */
    @Override
    public boolean select(Viewer viewer, Object parentElement, Object element) {
      if (fEnabled.get() && element instanceof ScoredNews) {
        ScoredNews news = (ScoredNews) element;
        return (news.getRelevance() == Relevance.HIGH || news.getRelevance() == Relevance.MEDIUM);
      }

      return true;
    }
  }

  /* LabelProvider for Scored News */
  private static class ScoredNewsLabelProvider extends NewsTableLabelProvider {
    private Image fHighRelevanceIcon;
    private Image fMediumRelevanceIcon;
    private Image fLowRelevanceIcon;

    ScoredNewsLabelProvider(NewsColumnViewModel model) {
      super(model);
      createResources();
    }

    private void createResources() {
      fHighRelevanceIcon = OwlUI.getImage(fResources, "icons/obj16/high.gif"); //$NON-NLS-1$
      fMediumRelevanceIcon = OwlUI.getImage(fResources, "icons/obj16/medium.gif"); //$NON-NLS-1$
      fLowRelevanceIcon = OwlUI.getImage(fResources, "icons/obj16/low.gif"); //$NON-NLS-1$
    }

    /*
     * @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
     */
    @Override
    public void update(ViewerCell cell) {
      ScoredNews scoredNews = (ScoredNews) cell.getElement();
      NewsColumn column = fColumnModel.getColumn(cell.getColumnIndex());

      /* Text */
      cell.setText(getColumnText(scoredNews.getNews(), column, cell.getColumnIndex()));

      /* Image */
      cell.setImage(getColumnImage(scoredNews, column, cell.getColumnIndex()));

      /* Font */
      cell.setFont(getFont(scoredNews.getNews(), cell.getColumnIndex()));

      /* Foreground */
      Color foreground = getForeground(scoredNews.getNews(), cell.getColumnIndex());

      /* This is required to invalidate + redraw the entire TableItem! */
      if (!OwlUI.isHighContrast()) {
        Item item = (Item) cell.getItem();
        if (item instanceof TableItem)
          ((TableItem) cell.getItem()).setForeground(foreground);
      }

      /* Background */
      if (!OwlUI.isHighContrast())
        cell.setBackground(getBackground(scoredNews.getNews(), cell.getColumnIndex()));
    }

    /*
     * @see org.rssowl.ui.internal.editors.feed.NewsTableLabelProvider#getColumnImage(java.lang.Object, org.rssowl.ui.internal.editors.feed.NewsColumn, int)
     */
    @Override
    protected Image getColumnImage(Object element, NewsColumn column, int colIndex) {

      /* Score Column */
      if (column == NewsColumn.RELEVANCE) {
        ScoredNews scoredNews = (ScoredNews) element;
        switch (scoredNews.getRelevance()) {
          case HIGH:
            return fHighRelevanceIcon;
          case MEDIUM:
            return fMediumRelevanceIcon;
          case LOW:
            return fLowRelevanceIcon;
        }
      }

      /* Any other Column */
      return super.getColumnImage(((ScoredNews) element).getNews(), column, colIndex);
    }

    /*
     * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
     */
    @Override
    public String getToolTipText(Object element) {
      ScoredNews scoredNews = (ScoredNews) element;
      INews news = scoredNews.getNews();
      String feedRef = news.getFeedLinkAsText();
      IBookMark bookMark = CoreUtils.getBookMark(feedRef);

      String name = null;
      if (bookMark != null)
        name = bookMark.getName();
      else
        name = feedRef;

      if (news.getParentId() != 0) {
        INewsBin bin = DynamicDAO.load(INewsBin.class, news.getParentId());
        if (bin != null) {
          name = NLS.bind(Messages.SearchNewsDialog_BIN_NAME, bin.getName(), name);
        }
      }

      return StringUtils.replaceAll(name, "&", "&&"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    /*
     * @see org.rssowl.ui.internal.editors.feed.NewsTableLabelProvider#erase(org.eclipse.swt.widgets.Event,
     * java.lang.Object)
     */
    @Override
    public void erase(Event event, Object element) {
      super.erase(event, ((ScoredNews) element).getNews());
    }

    /*
     * @see org.rssowl.ui.internal.editors.feed.NewsTableLabelProvider#paint(org.eclipse.swt.widgets.Event,
     * java.lang.Object)
     */
    @Override
    protected void paint(Event event, Object element) {
      super.paint(event, ((ScoredNews) element).getNews());
    }

    /*
     * @see org.rssowl.ui.internal.editors.feed.NewsTableLabelProvider#measure(org.eclipse.swt.widgets.Event,
     * java.lang.Object)
     */
    @Override
    protected void measure(Event event, Object element) {
      super.measure(event, ((ScoredNews) element).getNews());
    }
  }

  /* Custom Tooltip Support for Feed Column */
  private static class FeedColumnToolTipSupport extends ColumnViewerToolTipSupport {
    FeedColumnToolTipSupport(ColumnViewer viewer, int style) {
      super(viewer, style, false);
    }

    /*
     * @see org.eclipse.jface.viewers.ColumnViewerToolTipSupport#getToolTipArea(org.eclipse.swt.widgets.Event)
     */
    @Override
    protected Object getToolTipArea(Event event) {
      Table table = (Table) event.widget;
      Point point = new Point(event.x, event.y);
      TableItem item = table.getItem(point);

      /* Only valid for Feed Column */
      if (item != null) {
        int feedIndex = indexOf(table, NewsColumn.FEED);
        if (feedIndex >= 0 && item.getBounds(feedIndex).contains(point))
          return super.getToolTipArea(event);
      }

      return null;
    }

    private static int indexOf(Table table, NewsColumn column) {
      if (table.isDisposed())
        return -1;

      TableColumn[] columns = table.getColumns();
      for (int i = 0; i < columns.length; i++)
        if (column == columns[i].getData(NewsColumnViewModel.COL_ID))
          return i;

      return -1;
    }

    public static void enableFor(ColumnViewer viewer) {
      new FeedColumnToolTipSupport(viewer, ToolTip.NO_RECREATE);
    }
  }

  /**
   * @param parentShell
   */
  public SearchNewsDialog(Shell parentShell) {
    this(parentShell, null, true, false);
  }

  /**
   * @param parentShell
   * @param searchScope
   */
  public SearchNewsDialog(Shell parentShell, List<IFolderChild> searchScope) {
    this(parentShell, toSearchConditions(searchScope), true, false);
  }

  private static List<ISearchCondition> toSearchConditions(List<IFolderChild> searchScope) {
    IModelFactory factory = Owl.getModelFactory();
    List<ISearchCondition> conditions = new ArrayList<ISearchCondition>(2);

    /* Add scope as condition if provided */
    if (!searchScope.isEmpty()) {
      ISearchField field = factory.createSearchField(INews.LOCATION, INews.class.getName());
      conditions.add(factory.createSearchCondition(field, SearchSpecifier.SCOPE, ModelUtils.toPrimitive(searchScope)));
    }

    /* Add default condition as well */
    ISearchField field = factory.createSearchField(IEntity.ALL_FIELDS, INews.class.getName());
    conditions.add(factory.createSearchCondition(field, SearchSpecifier.CONTAINS_ALL, "")); //$NON-NLS-1$

    return conditions;
  }

  /**
   * @param parentShell
   * @param initialConditions A List of Conditions that should show initially.
   * @param matchAllConditions If <code>TRUE</code>, require all conditions to
   * match, <code>FALSE</code> otherwise.
   * @param runSearch If <code>TRUE</code>, run the search after the dialog
   * opened.
   */
  public SearchNewsDialog(Shell parentShell, List<ISearchCondition> initialConditions, boolean matchAllConditions, boolean runSearch) {
    super(parentShell);

    fPreferences = Owl.getPreferenceService().getGlobalScope();
    fResources = new LocalResourceManager(JFaceResources.getResources());
    fDialogSettings = Activator.getDefault().getDialogSettings();
    fFirstTimeOpen = (fDialogSettings.getSection(SETTINGS_SECTION) == null);
    fIsPreviewVisible = fPreferences.getBoolean(DefaultPreferences.SEARCH_DIALOG_PREVIEW_VISIBLE);
    fCachedWeights = fPreferences.getIntegers(PREF_SASH_WEIGHTS);
    fModelSearch = Owl.getPersistenceService().getModelSearch();
    fHandCursor = parentShell.getDisplay().getSystemCursor(SWT.CURSOR_HAND);
    fMatchAllConditions = matchAllConditions;
    fRunSearch = runSearch;
    fNewsDao = DynamicDAO.getDAO(INewsDAO.class);

    /* Look for initial conditions and scope */
    if (initialConditions != null) {
      Pair<ISearchCondition, List<ISearchCondition>> conditions = CoreUtils.splitScope(initialConditions);
      fInitialScope = conditions.getFirst();
      fInitialConditions = conditions.getSecond();
    }
  }

  /**
   * @param useLowScoreFilter if <code>true</code>, filters the results of the
   * first run by score.
   */
  public void setUseLowScoreFilter(boolean useLowScoreFilter) {
    fUseLowScoreFilter = useLowScoreFilter;
  }

  /*
   * @see org.eclipse.jface.window.Window#open()
   */
  @Override
  public int open() {
    fgOpenDialogCount++;
    return super.open();
  }

  /*
   * @see org.eclipse.jface.dialogs.TrayDialog#close()
   */
  @Override
  public boolean close() {
    fgOpenDialogCount--;
    if (fAttachmentsMenu != null)
      fAttachmentsMenu.dispose();

    /* Store Column Model */
    if (!fResultViewer.getTable().isDisposed()) {
      NewsColumnViewModel model = NewsColumnViewModel.initializeFrom(fResultViewer.getTable());
      model.setSortColumn(fNewsSorter.getSortBy());
      model.setAscending(fNewsSorter.isAscending());
      model.saveTo(fPreferences, true);
    }

    /* Store Preferences */
    fPreferences.putBoolean(DefaultPreferences.SEARCH_DIALOG_PREVIEW_VISIBLE, fIsPreviewVisible);
    if (fCachedWeights != null)
      fPreferences.putIntegers(PREF_SASH_WEIGHTS, fCachedWeights);

    /*
     * Workaround for Eclipse Bug 186025: The Virtual Manager is not cleared
     * when the TableViewer is disposed. Due to the hookListener() call, a
     * reference to the TableViewer is held in Memory, so we need to explicitly
     * clear the virtual manager.
     */
    fResultViewer.setItemCount(0);

    boolean res = super.close();
    fResources.dispose();
    unregisterListeners();
    return res;
  }

  /*
   * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
   */
  @Override
  protected void configureShell(Shell shell) {
    super.configureShell(shell);
    shell.setText(Messages.SearchNewsDialog_SEARCH_NEWS);
  }

  /*
   * @see org.eclipse.jface.dialogs.Dialog#create()
   */
  @Override
  public void create() {
    super.create();

    /* Perform the search slightly delayed if requested */
    if (fRunSearch) {
      JobRunner.runInUIThread(200, getShell(), new Runnable() {
        public void run() {
          onSearch();
        }
      });
    }
  }

  /*
   * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
   */
  @Override
  protected Control createDialogArea(Composite parent) {

    /* Separator */
    new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));

    /* Title */
    setTitle(Messages.SearchNewsDialog_SEARCH_NEWS);

    /* Title Image */
    setTitleImage(OwlUI.getImage(fResources, "icons/wizban/search.gif")); //$NON-NLS-1$

    /* Title Message */
    restoreInfoMessage(false);

    /* Sashform dividing search definition from results */
    fSashForm = new SashForm(parent, SWT.VERTICAL | SWT.SMOOTH);
    fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));

    /* Top Area */
    Composite topSash = new Composite(fSashForm, SWT.NONE);
    topSash.setLayout(LayoutUtils.createGridLayout(1, 0, 0, 0, 0, false));

    Composite topSashContent = new Composite(topSash, SWT.None);
    topSashContent.setLayout(LayoutUtils.createGridLayout(2, 0, 0, 0, 0, false));
    topSashContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

    /* Create Condition Controls */
    createConditionControls(topSashContent);

    /* Separator */
    new Label(topSashContent, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.END, true, false));

    /* Create Center Sash */
    Composite centerSash = new Composite(fSashForm, SWT.NONE);
    centerSash.setLayout(LayoutUtils.createGridLayout(1, 0, 0, 0, 0, false));
    centerSash.addControlListener(new ControlAdapter() {

      @Override
      public void controlResized(ControlEvent e) {
        fCachedWeights = fSashForm.getWeights();
      }
    });

    Composite centerSashContent = new Composite(centerSash, SWT.None);
    centerSashContent.setLayout(LayoutUtils.createGridLayout(1, 0, 0, 0, 0, false));
    centerSashContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

    /* Create Viewer for Results */
    createResultViewer(centerSashContent);

    /* Create Bottom Sash */
    fBottomSash = new Composite(fSashForm, SWT.NONE);
    fBottomSash.setLayout(LayoutUtils.createGridLayout(1, 0, 0, 0, 0, false));
    fBottomSash.setVisible(fIsPreviewVisible);

    Composite bottomSashContent = new Composite(fBottomSash, SWT.None);
    bottomSashContent.setLayout(LayoutUtils.createGridLayout(1, 0, 0, 0, 0, false));
    bottomSashContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    bottomSashContent.setBackground(bottomSashContent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

    /* Separator */
    new Label(bottomSashContent, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.END, true, false));

    /* Create Viewer for News Item */
    createBrowserViewer(bottomSashContent);

    /* Set weight to SashForm */
    if (fCachedWeights != null)
      fSashForm.setWeights(fCachedWeights);
    else
      fSashForm.setWeights(fIsPreviewVisible ? THREE_SASH_WEIGHTS : TWO_SASH_WEIGHTS);

    /* Separator */
    new Label(fBottomSash, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.END, true, false));

    applyDialogFont(fSashForm);

    return fSashForm;
  }

  private void createBrowserViewer(Composite bottomSashContent) {
    fBrowserViewer = new NewsBrowserViewer(bottomSashContent, SWT.NONE) {
      @Override
      protected Collection<String> getHighlightedWords() {
        return CoreUtils.extractWords(fCurrentSearchConditions, true);
      }
    };
    fBrowserViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

    /* Create Content Provider */
    fBrowserViewer.setContentProvider(new IStructuredContentProvider() {
      public Object[] getElements(Object inputElement) {
        if (inputElement instanceof Object[] && ((Object[]) inputElement).length > 0)
          inputElement = ((Object[]) inputElement)[0];

        if (inputElement instanceof NewsReference)
          return new Object[] { ((NewsReference) inputElement).resolve() };

        return new Object[] { inputElement };
      }

      public void dispose() {}

      public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
    });

    /* Create LabelProvider */
    NewsBrowserLabelProvider labelProvider = new NewsBrowserLabelProvider(fBrowserViewer);
    labelProvider.setForceShowFeedInformation(true);
    fBrowserViewer.setLabelProvider(labelProvider);

    /* Set input when selection in result viewer changes */
    fResultViewer.addSelectionChangedListener(new ISelectionChangedListener() {
      public void selectionChanged(SelectionChangedEvent event) {
        IStructuredSelection selection = (IStructuredSelection) event.getSelection();
        if (!selection.isEmpty() && fIsPreviewVisible) {
          fBrowserViewer.setInput(selection.getFirstElement());
          hideBrowser(false);
        }
      }
    });
  }

  private void createConditionControls(Composite container) {
    Composite topControlsContainer = new Composite(container, SWT.None);
    topControlsContainer.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
    topControlsContainer.setLayout(LayoutUtils.createGridLayout(5, 10, 3));

    /* Radio to select Condition Matching */
    fMatchAllRadio = new Button(topControlsContainer, SWT.RADIO);
    fMatchAllRadio.setText(Messages.SearchNewsDialog_MATCH_ALL);
    fMatchAllRadio.setSelection(fMatchAllConditions);

    fMatchAnyRadio = new Button(topControlsContainer, SWT.RADIO);
    fMatchAnyRadio.setText(Messages.SearchNewsDialog_MATCH_ANY);
    fMatchAnyRadio.setSelection(!fMatchAllConditions);

    /* Separator */
    Label sep = new Label(topControlsContainer, SWT.SEPARATOR | SWT.VERTICAL);
    sep.setLayoutData(new GridData(SWT.DEFAULT, 20));

    /* Scope */
    Composite scopeContainer = new Composite(topControlsContainer, SWT.None);
    scopeContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
    scopeContainer.setLayout(LayoutUtils.createGridLayout(2, 0, 0, 0, 5, false));

    Label locationLabel = new Label(scopeContainer, SWT.NONE);
    locationLabel.setText(Messages.SearchNewsDialog_SEARCH_IN);

    fLocationControl = new LocationControl(scopeContainer, SWT.WRAP) {
      @Override
      protected String getDefaultLabel() {
        return Messages.SearchNewsDialog_ALL_NEWS;
      }
    };
    fLocationControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
    ((GridData) fLocationControl.getLayoutData()).widthHint = 100;
    fLocationControl.setLayout(LayoutUtils.createGridLayout(1, 0, 0, 0, 0, false));

    /* ToolBar to add and select existing saved searches */
    final ToolBarManager dialogToolBar = new ToolBarManager(SWT.RIGHT | SWT.FLAT);

    /* Columns */
    IAction columnDropdown = new Action(Messages.SearchNewsDialog_VISIBLE_COLUMNS, IAction.AS_DROP_DOWN_MENU) {
      @Override
      public void run() {
        OwlUI.positionDropDownMenu(this, dialogToolBar);
      }

      @Override
      public ImageDescriptor getImageDescriptor() {
        return OwlUI.COLUMNS;
      }

      @Override
      public String getId() {
        return COLUMNS_ACTION;
      }
    };

    columnDropdown.setMenuCreator(new ContextMenuCreator() {

      @Override
      public Menu createMenu(Control parent) {
        Menu menu = new Menu(parent);

        MenuItem restoreDefaults = new MenuItem(menu, SWT.None);
        restoreDefaults.setText(Messages.SearchNewsDialog_RESTORE_DEFAULT_COLUMNS);
        restoreDefaults.addSelectionListener(new SelectionAdapter() {
          @Override
          public void widgetSelected(SelectionEvent e) {
            NewsColumnViewModel defaultModel = NewsColumnViewModel.createDefault(true);
            if (!defaultModel.equals(fColumnModel))
              showColumns(defaultModel, true);
          }
        });

        new MenuItem(menu, SWT.SEPARATOR);

        NewsColumn[] columns = NewsColumn.values();
        for (final NewsColumn column : columns) {
          if (column.isSelectable()) {
            MenuItem item = new MenuItem(menu, SWT.CHECK);
            item.setText(column.getName());
            if (fColumnModel.contains(column))
              item.setSelection(true);

            item.addSelectionListener(new SelectionAdapter() {
              @Override
              public void widgetSelected(SelectionEvent e) {
                if (fColumnModel.contains(column))
                  fColumnModel.removeColumn(column);
                else
                  fColumnModel.addColumn(column);

                showColumns(fColumnModel, true);
              }
            });
          }
        }

        return menu;
      }
    });

    dialogToolBar.add(columnDropdown);

    /* Separator */
    dialogToolBar.add(new Separator());

    /* Toggle Preview */
    final String previewActionId = "org.rssowl.ui.internal.dialogs.search.PreviewAction"; //$NON-NLS-1$
    IAction previewAction = new Action(Messages.SearchNewsDialog_PREVIEW_RESULTS, IAction.AS_CHECK_BOX) {
      @Override
      public void run() {
        fIsPreviewVisible = !fIsPreviewVisible;
        fSashForm.setWeights(fIsPreviewVisible ? THREE_SASH_WEIGHTS : TWO_SASH_WEIGHTS);
        fBottomSash.setVisible(fIsPreviewVisible);
        fSashForm.layout();
        dialogToolBar.find(previewActionId).update(IAction.TOOL_TIP_TEXT);

        /* Select and Show News if required */
        if (fIsPreviewVisible && fResultViewer.getTable().getItemCount() > 0) {

          /* Select first News if required */
          if (fResultViewer.getSelection().isEmpty())
            fResultViewer.getTable().select(0);

          /* Set input and Focus */
          fBrowserViewer.setInput(((IStructuredSelection) fResultViewer.getSelection()).getFirstElement());
          hideBrowser(false);
          fResultViewer.getTable().setFocus();

          /* Make sure to show the selection */
          fResultViewer.getTable().showSelection();
        }
      }

      @Override
      public ImageDescriptor getImageDescriptor() {
        return OwlUI.getImageDescriptor("icons/etool16/browsermaximized.gif"); //$NON-NLS-1$
      }

      @Override
      public String getToolTipText() {
        if (fIsPreviewVisible)
          return Messages.SearchNewsDialog_HIDE_PREVIEW;

        return Messages.SearchNewsDialog_SHOW_PREVIEW;
      }
    };
    previewAction.setId(previewActionId);
    previewAction.setChecked(fIsPreviewVisible);
    dialogToolBar.add(previewAction);

    /* Separator */
    dialogToolBar.add(new Separator());

    /* Existing Saved Searches */
    IAction savedSearches = new Action(Messages.SearchNewsDialog_SHOW_SAVED_SEARCH, IAction.AS_DROP_DOWN_MENU) {
      @Override
      public void run() {
        OwlUI.positionDropDownMenu(this, dialogToolBar);
      }

      @Override
      public ImageDescriptor getImageDescriptor() {
        return OwlUI.SEARCHMARK;
      }

      @Override
      public String getId() {
        return SEARCHES_ACTION;
      }
    };

    savedSearches.setMenuCreator(new ContextMenuCreator() {

      @Override
      public Menu createMenu(Control parent) {
        Collection<ISearchMark> searchMarks = CoreUtils.loadSortedSearchMarks();
        Menu menu = new Menu(parent);

        /* Create new Saved Search */
        MenuItem newSavedSearch = new MenuItem(menu, SWT.NONE);
        newSavedSearch.setText(Messages.SearchNewsDialog_NEW_SAVED_SEARCH);
        newSavedSearch.setImage(OwlUI.getImage(fResources, "icons/etool16/add.gif")); //$NON-NLS-1$
        newSavedSearch.addSelectionListener(new SelectionAdapter() {
          @Override
          public void widgetSelected(SelectionEvent e) {
            onSave();
          }
        });

        /* Separator */
        if (searchMarks.size() > 0)
          new MenuItem(menu, SWT.SEPARATOR);

        /* Show Existing Saved Searches */
        for (final ISearchMark searchMark : searchMarks) {
          MenuItem item = new MenuItem(menu, SWT.None);
          item.setText(searchMark.getName());
          item.setImage(OwlUI.getImage(fResources, OwlUI.SEARCHMARK));
          item.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
              show(searchMark);
            }
          });
        }

        return menu;
      }
    });

    dialogToolBar.add(savedSearches);
    dialogToolBar.createControl(topControlsContainer);
    dialogToolBar.getControl().setLayoutData(new GridData(SWT.END, SWT.CENTER, true, true));

    /* Container for Conditions */
    final Composite conditionsContainer = new Composite(container, SWT.NONE);
    conditionsContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
    conditionsContainer.setLayout(LayoutUtils.createGridLayout(2, 5, 10));
    conditionsContainer.setBackground(container.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
    conditionsContainer.setBackgroundMode(SWT.INHERIT_FORCE);
    conditionsContainer.addPaintListener(new PaintListener() {
      public void paintControl(PaintEvent e) {
        GC gc = e.gc;
        Rectangle clArea = conditionsContainer.getClientArea();
        gc.setForeground(conditionsContainer.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
        gc.drawLine(clArea.x, clArea.y, clArea.x + clArea.width, clArea.y);
      }
    });

    /* Search Conditions List */
    fSearchConditionList = new SearchConditionList(conditionsContainer, SWT.None, getDefaultConditions());
    fSearchConditionList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
    fSearchConditionList.setVisibleItemCount(3);

    /* Show Initial Scope if present */
    if (fInitialScope != null && fInitialScope.getValue() instanceof Long[][])
      fLocationControl.select((Long[][]) fInitialScope.getValue());

    /* Show Initial Conditions if present */
    if (fInitialConditions != null)
      fSearchConditionList.showConditions(fInitialConditions);

    /* Focus Input */
    int index = 0;
    if (fInitialConditions != null && fInitialConditions.size() == 2)
      index = 1;

    fSearchConditionList.focusInput(index);
  }

  /* Show conditions of the given searchmark */
  private void show(ISearchMark sm) {

    /* Match Conditions */
    fMatchAllRadio.setSelection(sm.matchAllConditions());
    fMatchAnyRadio.setSelection(!sm.matchAllConditions());

    Pair<ISearchCondition, List<ISearchCondition>> conditions = CoreUtils.splitScope(sm.getSearchConditions());

    /* Show Scope */
    Long[][] scope = null;
    if (conditions.getFirst() != null && conditions.getFirst().getValue() instanceof Long[][])
      scope = (Long[][]) conditions.getFirst().getValue();
    fLocationControl.select(scope);

    /* Show Conditions */
    fSearchConditionList.showConditions(conditions.getSecond());

    /* Unset Warning/Error Message */
    restoreInfoMessage(true);

    /* Layout */
    fLocationControl.getParent().getParent().getParent().layout(true, true);
  }

  private void restoreInfoMessage(boolean clearError) {
    if (clearError)
      setErrorMessage(null);
    setMessage(Messages.SearchNewsDialog_SEARCH_HELP, IMessageProvider.INFORMATION);
  }

  /*
   * @see org.eclipse.jface.dialogs.TrayDialog#createButtonBar(org.eclipse.swt.widgets.Composite)
   */
  @Override
  protected Control createButtonBar(Composite parent) {
    GridLayout layout = new GridLayout(1, false);
    layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
    layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
    layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
    layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);

    Composite buttonBar = new Composite(parent, SWT.NONE);
    buttonBar.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
    buttonBar.setLayout(layout);

    /* Status Label */
    fStatusLabel = new Link(buttonBar, SWT.NONE);
    fStatusLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
    fStatusLabel.setText(""); //$NON-NLS-1$
    applyDialogFont(fStatusLabel);
    fStatusLabel.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        onSave();
      }
    });

    /* Search */
    Button searchButton = createButton(buttonBar, BUTTON_SEARCH, Messages.SearchNewsDialog_SEARCH, true);
    ((GridData) searchButton.getLayoutData()).horizontalAlignment = SWT.END;
    ((GridData) searchButton.getLayoutData()).grabExcessHorizontalSpace = false;

    /* Clear */
    createButton(buttonBar, BUTTON_CLEAR, Messages.SearchNewsDialog_CLEAR, false);

    /* Close */
    Button closeButton = createButton(buttonBar, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, false);
    closeButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        close();
      }
    });

    return buttonBar;
  }

  /*
   * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
   */
  @Override
  protected void buttonPressed(int buttonId) {
    switch (buttonId) {
      case BUTTON_SEARCH:
        onSearch();
        break;

      case BUTTON_CLEAR:
        onClear();
        break;
    }
  }

  private void onSearch() {

    /* Make sure Conditions are provided */
    if (fSearchConditionList.isEmpty()) {
      setErrorMessage(Messages.SearchNewsDialog_SEARCH_DESCRIPTION);
      fSearchConditionList.focusInput();
      return;
    }

    /* Create Conditions */
    fCurrentSearchConditions = fSearchConditionList.createConditions();
    ISearchCondition locationCondition = fLocationControl.toScopeCondition();
    if (locationCondition != null)
      fCurrentSearchConditions.add(locationCondition);

    /* Make sure there is no Location Conflict */
    if (CoreUtils.isLocationConflict(fCurrentSearchConditions)) {
      setErrorMessage(null);
      setMessage(Messages.SearchNewsDialog_LOCATION_WARNING, IMessageProvider.WARNING);
    }

    /* Unset Warning/Error Message */
    else
      restoreInfoMessage(true);

    final boolean matchAllConditions = fMatchAllRadio.getSelection();

    /* Disable Buttons and update Cursor */
    getButton(BUTTON_SEARCH).setEnabled(false);
    getShell().setCursor(getShell().getDisplay().getSystemCursor(SWT.CURSOR_APPSTARTING));

    JobRunner.runUIUpdater(new UIBackgroundJob(getShell()) {
      private List<ScoredNews> fResult = null;
      private Exception fException = null;

      @Override
      protected void runInBackground(IProgressMonitor monitor) {

        /* Perform Search in the Background */
        try {
          List<SearchHit<NewsReference>> searchHits = fModelSearch.searchNews(fCurrentSearchConditions, matchAllConditions);
          fResult = new ArrayList<ScoredNews>(searchHits.size());

          /* Retrieve maximum raw relevance */
          Float maxRelevanceScore = 0f;
          for (SearchHit<NewsReference> searchHit : searchHits) {
            Float relevanceRaw = searchHit.getRelevance();
            maxRelevanceScore = Math.max(maxRelevanceScore, relevanceRaw);
          }

          /* Calculate Thresholds */
          Float mediumRelThreshold = maxRelevanceScore / 3f * 1f;
          Float highRelThreshold = maxRelevanceScore / 3f * 2f;

          Set<State> visibleStates = State.getVisible();

          /* Fill Results with Relevance */
          for (SearchHit<NewsReference> searchHit : searchHits) {

            /* Only add visible News for now */
            INews.State state = (State) searchHit.getData(INews.STATE);
            if (!visibleStates.contains(state))
              continue;

            /* TODO Have to test if Entity really exists (bug 337) */
            if (!fNewsDao.exists(searchHit.getResult().getId()))
              continue;

            Float relevanceRaw = searchHit.getRelevance();
            Relevance relevance = Relevance.LOW;
            if (relevanceRaw > highRelThreshold)
              relevance = Relevance.HIGH;
            else if (relevanceRaw > mediumRelThreshold)
              relevance = Relevance.MEDIUM;

            /* Add to result */
            fResult.add(new ScoredNews(searchHit.getResult(), state, relevanceRaw, relevance));
          }

          /* Preload some results that are known to be shown initially */
          preload(fResult);
        } catch (PersistenceException e) {
          fException = e;
        }
      }

      @Override
      protected void runInUI(IProgressMonitor monitor) {

        /* Check for error first */
        if (fException != null) {
          setErrorMessage(fException.getMessage());
          fResult = Collections.emptyList();
        }

        /* Set Input (sorted) to Viewer */
        fResultViewer.setInput(fResult);

        /* Update Status Label */
        String text;
        int size = fResult.size() - fLowScoreNewsFilteredCount.get();
        if (fLowScoreNewsFilteredCount.get() != 0) {
          if (size == 0)
            text = NLS.bind(Messages.SearchNewsDialog_SEARCH_RESULT_1_FILTERED, fLowScoreNewsFilteredCount.get());
          else if (size == 1)
            text = NLS.bind(Messages.SearchNewsDialog_SEARCH_RESULT_2_FILTERED, size, fLowScoreNewsFilteredCount.get());
          else
            text = NLS.bind(Messages.SearchNewsDialog_SEARCH_RESULT_3_FILTERED, size, fLowScoreNewsFilteredCount.get());
        } else {
          if (size == 0)
            text = Messages.SearchNewsDialog_SEARCH_RESULT_1;
          else if (size == 1)
            text = NLS.bind(Messages.SearchNewsDialog_SEARCH_RESULT_2, fResult.size());
          else
            text = NLS.bind(Messages.SearchNewsDialog_SEARCH_RESULT_3, fResult.size());
        }

        fStatusLabel.setText(text);

        /* Enable Buttons and update Cursor */
        getButton(BUTTON_SEARCH).setEnabled(true);
        getShell().setCursor(null);
        getShell().setDefaultButton(getButton(BUTTON_SEARCH));
        getButton(BUTTON_SEARCH).setFocus();

        /* Move Focus back to last Search Condition Element */
        fSearchConditionList.focusInput();

        /* Select First Result if Preview is visible */
        if (fIsPreviewVisible && size > 0) {
          fResultViewer.getTable().select(0);
          fResultViewer.getTable().showSelection();

          /* Set input and Focus */
          Object selection = ((IStructuredSelection) fResultViewer.getSelection()).getFirstElement();
          boolean refresh = selection.equals(fBrowserViewer.getInput());

          fBrowserViewer.setInput(selection);
          hideBrowser(false);
          fResultViewer.getTable().setFocus();

          if (refresh)
            fBrowserViewer.refresh();
        }

        /* Clear Browser Viewer otherwise */
        else if (fIsPreviewVisible)
          hideBrowser(true);
      }
    });
  }

  private void preload(List<ScoredNews> list) {
    for (int i = 0; i < list.size() && i < NUM_PRELOADED; i++) {
      list.get(i).getNews();
    }
  }

  private void hideBrowser(boolean hide) {
    if (hide) {
      fBrowserViewer.setInput(URIUtils.ABOUT_BLANK);
      fBrowserViewer.getControl().setVisible(false);
    } else
      fBrowserViewer.getControl().setVisible(true);
  }

  private void onClear() {

    /* Reset Conditions */
    fSearchConditionList.reset();
    fMatchAllRadio.setSelection(true);
    fMatchAnyRadio.setSelection(false);
    fResultViewer.setInput(Collections.emptyList());
    hideBrowser(true);

    /* Unset Warning/Error Message */
    restoreInfoMessage(true);

    /* Unset Status Message */
    fStatusLabel.setText(""); //$NON-NLS-1$
  }

  private void onSave() {
    List<ISearchCondition> conditions = fSearchConditionList.createConditions();

    /* Add default if empty */
    if (conditions.isEmpty())
      conditions.addAll(getDefaultConditions());

    ISearchCondition locationCondition = fLocationControl.toScopeCondition();
    if (locationCondition != null)
      conditions.add(locationCondition);

    SearchMarkDialog dialog = new SearchMarkDialog((Shell) getShell().getParent(), OwlUI.getBookMarkExplorerSelection(), null, conditions, fMatchAllRadio.getSelection());
    dialog.open();
  }

  private void createResultViewer(Composite centerSashContent) {

    /* Container for Table */
    Composite tableContainer = new Composite(centerSashContent, SWT.NONE);
    tableContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    tableContainer.setLayout(LayoutUtils.createGridLayout(1, 0, 0));

    /* Custom Table */
    int style = SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL;
    fCustomTable = new CTable(tableContainer, style);

    /* Viewer */
    fResultViewer = new TableViewer(fCustomTable.getControl()) {
      @Override
      public ISelection getSelection() {
        StructuredSelection selection = (StructuredSelection) super.getSelection();
        return convertToNews(selection);
      }
    };
    fResultViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    fResultViewer.setUseHashlookup(true);
    fResultViewer.getControl().setData(ApplicationWorkbenchWindowAdvisor.FOCUSLESS_SCROLL_HOOK, new Object());
    fResultViewer.getTable().setHeaderVisible(true);

    /* Custom Tooltips for Feed Column */
    FeedColumnToolTipSupport.enableFor(fResultViewer);

    /* Separator */
    new Label(centerSashContent, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));

    /* Apply ContentProvider */
    fResultViewer.setContentProvider(getContentProvider());

    /* Create LabelProvider */
    NewsColumnViewModel model = NewsColumnViewModel.loadFrom(fPreferences, true);
    fNewsTableLabelProvider = new ScoredNewsLabelProvider(model);
    if (!OwlUI.isHighContrast()) {
      fResultViewer.getControl().addListener(SWT.EraseItem, new Listener() {
        public void handleEvent(Event event) {
          Object element = event.item.getData();
          fNewsTableLabelProvider.erase(event, element);
        }
      });
    }

    /* Create Sorter */
    fNewsSorter = new ScoredNewsComparator();
    fResultViewer.setComparator(fNewsSorter);

    /* Create Filter (if necessary) */
    if (fUseLowScoreFilter)
      fResultViewer.addFilter(new FirstTimeLowScoreFilter());

    /* Create the Columns */
    showColumns(model, false);

    /* Hook Contextual Menu */
    hookContextualMenu();

    /* Drag and Drop */
    initDragAndDrop();

    /* Register Listeners */
    registerListeners();
  }

  private void showColumns(NewsColumnViewModel model, boolean update) {
    fResultViewer.getTable().setRedraw(false);
    try {

      /* Dispose Old */
      fCustomTable.clear();

      /* Keep as current */
      fColumnModel = model;

      /* Create New */
      List<NewsColumn> cols = model.getColumns();
      for (NewsColumn col : cols) {
        TableViewerColumn viewerColumn = new TableViewerColumn(fResultViewer, SWT.LEFT);
        fCustomTable.manageColumn(viewerColumn.getColumn(), model.getLayoutData(col), col.showName() ? col.getName() : null, col.showTooltip() ? col.getName() : null, null, col.isMoveable(), col.isResizable());
        viewerColumn.getColumn().setData(NewsColumnViewModel.COL_ID, col);

        if (model.getSortColumn() == col && col.showSortIndicator()) {
          fCustomTable.getControl().setSortColumn(viewerColumn.getColumn());
          fCustomTable.getControl().setSortDirection(model.isAscending() ? SWT.UP : SWT.DOWN);
        }
      }

      /* Enable Sorting adding listeners to Columns */
      TableColumn[] columns = fResultViewer.getTable().getColumns();
      for (final TableColumn column : columns) {
        column.addSelectionListener(new SelectionAdapter() {
          @SuppressWarnings("unchecked")
          @Override
          public void widgetSelected(SelectionEvent e) {
            NewsColumn oldSortBy = fNewsSorter.getSortBy();
            NewsColumn newSortBy = (NewsColumn) column.getData(NewsColumnViewModel.COL_ID);
            boolean defaultAscending = newSortBy.prefersAscending();
            boolean ascending = (oldSortBy != newSortBy) ? defaultAscending : !fNewsSorter.isAscending();

            fNewsSorter.setSortBy(newSortBy);
            fNewsSorter.setAscending(ascending);

            fColumnModel.setSortColumn(newSortBy);
            fColumnModel.setAscending(ascending);

            /* Indicate Sort-Column in UI for Columns that have a certain width */
            if (newSortBy.showSortIndicator()) {
              fResultViewer.getTable().setSortColumn(column);
              fResultViewer.getTable().setSortDirection(ascending ? SWT.UP : SWT.DOWN);
            } else {
              fResultViewer.getTable().setSortColumn(null);
            }

            /* Since Virtual Style is set, we have to sort the model manually */
            if (fResultViewer.getInput() != null) {
              Collections.sort(((List<ScoredNews>) fResultViewer.getInput()), fNewsSorter);
              fResultViewer.refresh(false);
            }
          }
        });
      }

      /* Update Table */
      if (update)
        fCustomTable.update();

      /* Update Sorter */
      fNewsSorter.setAscending(model.isAscending());
      fNewsSorter.setSortBy(model.getSortColumn());

      /* Set Label Provider */
      fNewsTableLabelProvider.init(model);
      fResultViewer.setLabelProvider(fNewsTableLabelProvider);

      /* Refresh if necessary */
      if (update)
        fResultViewer.refresh(true);
    } finally {
      fResultViewer.getTable().setRedraw(true);
    }
  }

  private void initDragAndDrop() {
    int ops = DND.DROP_COPY | DND.DROP_MOVE;
    Transfer[] transfers = new Transfer[] { LocalSelectionTransfer.getTransfer(), TextTransfer.getInstance(), URLTransfer.getInstance() };

    /* Drag Support */
    fResultViewer.addDragSupport(ops, transfers, new DragSourceListener() {
      public void dragStart(final DragSourceEvent event) {
        SafeRunner.run(new LoggingSafeRunnable() {
          public void run() throws Exception {
            LocalSelectionTransfer.getTransfer().setSelection(fResultViewer.getSelection());
            LocalSelectionTransfer.getTransfer().setSelectionSetTime(event.time & 0xFFFFFFFFL);
            event.doit = true;
          }
        });
      }

      public void dragSetData(final DragSourceEvent event) {
        SafeRunner.run(new LoggingSafeRunnable() {
          public void run() throws Exception {

            /* Set Selection using LocalSelectionTransfer */
            if (LocalSelectionTransfer.getTransfer().isSupportedType(event.dataType))
              event.data = LocalSelectionTransfer.getTransfer().getSelection();

            /* Set Text using Text- or URLTransfer */
            else if (TextTransfer.getInstance().isSupportedType(event.dataType) || URLTransfer.getInstance().isSupportedType(event.dataType))
              setTextData(event);
          }
        });
      }

      public void dragFinished(DragSourceEvent event) {
        SafeRunner.run(new LoggingSafeRunnable() {
          public void run() throws Exception {
            LocalSelectionTransfer.getTransfer().setSelection(null);
            LocalSelectionTransfer.getTransfer().setSelectionSetTime(0);
          }
        });
      }
    });
  }

  private void setTextData(DragSourceEvent event) {
    IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection();
    Collection<INews> news = ModelUtils.normalize(selection.toList());

    if (!news.isEmpty()) {
      StringBuilder strB = new StringBuilder();

      for (INews item : news) {
        String link = CoreUtils.getLink(item);
        if (StringUtils.isSet(link)) {
          strB.append(link);

          if (news.size() > 1)
            strB.append("\n"); //$NON-NLS-1$
        }
      }

      if (strB.length() > 0)
        event.data = strB.toString();
    }
  }

  /* Convert Selection to INews */
  private ISelection convertToNews(StructuredSelection selection) {
    List<?> selectedElements = selection.toList();
    List<INews> selectedNews = new ArrayList<INews>();
    for (Object selectedElement : selectedElements) {
      ScoredNews scoredNews = (ScoredNews) selectedElement;
      selectedNews.add(scoredNews.getNews());
    }

    return new StructuredSelection(selectedNews);
  }

  private void registerListeners() {

    /* Open selected News Links in Browser on doubleclick */
    fResultViewer.addDoubleClickListener(new IDoubleClickListener() {
      public void doubleClick(DoubleClickEvent event) {
        onMouseDoubleClick(event);
      }
    });

    /* Perform Action on Mouse-Down */
    fResultViewer.getControl().addListener(SWT.MouseDown, new Listener() {
      public void handleEvent(Event event) {
        onMouseDown(event);
      }
    });

    /* Update Cursor on Mouse-Move */
    fResultViewer.getControl().addListener(SWT.MouseMove, new Listener() {
      public void handleEvent(Event event) {
        onMouseMove(event);
      }
    });

    /* Listen to News-Events */
    fNewsListener = new NewsListener() {
      public void entitiesAdded(Set<NewsEvent> events) {
      /* Ignore */
      }

      public void entitiesUpdated(Set<NewsEvent> events) {
        onNewsEvent(events);
      }

      public void entitiesDeleted(Set<NewsEvent> events) {
      /* Ignore */
      }
    };
    DynamicDAO.addEntityListener(INews.class, fNewsListener);

    /* Redraw on Label update */
    fLabelListener = new LabelAdapter() {
      @Override
      public void entitiesUpdated(Set<LabelEvent> events) {
        JobRunner.runInUIThread(fResultViewer.getTable(), new Runnable() {
          public void run() {
            fResultViewer.refresh(true);
          }
        });
      }
    };
    DynamicDAO.addEntityListener(ILabel.class, fLabelListener);
  }

  private void onNewsEvent(final Set<NewsEvent> events) {

    /* No Result set yet */
    if (fResultViewer.getInput() == null)
      return;

    /* Check for Update / Deleted News */
    JobRunner.runUIUpdater(new UIBackgroundJob(getShell()) {
      private List<ScoredNews> fDeletedScoredNews;
      private List<ScoredNews> fUpdatedScoredNews;
      private Set<NewsEvent> fUpdatedNewsEvents;

      @Override
      protected void runInBackground(IProgressMonitor monitor) {
        List<?> input = (List<?>) fResultViewer.getInput();
        for (NewsEvent event : events) {
          for (Object object : input) {
            ScoredNews scoredNews = ((ScoredNews) object);
            NewsReference newsRef = scoredNews.getNewsReference();

            /* Return on Cancellation or Shutdown */
            if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
              return;

            /* News is part of the list */
            if (newsRef.references(event.getEntity())) {
              INews news = event.getEntity();

              /* News got Deleted */
              if (!news.isVisible()) {
                if (fDeletedScoredNews == null)
                  fDeletedScoredNews = new ArrayList<ScoredNews>();
                fDeletedScoredNews.add(scoredNews);
              }

              /* News got Updated */
              else {
                if (fUpdatedScoredNews == null)
                  fUpdatedScoredNews = new ArrayList<ScoredNews>();
                fUpdatedScoredNews.add(scoredNews);

                if (fUpdatedNewsEvents == null)
                  fUpdatedNewsEvents = new HashSet<NewsEvent>();
                fUpdatedNewsEvents.add(event);
              }
            }
          }
        }
      }

      @Override
      protected void runInUI(IProgressMonitor monitor) {

        /* Return on Cancellation or Shutdown */
        if (monitor.isCanceled() || Controller.getDefault().isShuttingDown())
          return;

        /* News got Deleted */
        if (fDeletedScoredNews != null) {

          /* Temporary Fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=295980 */
          if (Application.isWindows7()) {
            Object input = fResultViewer.getInput();
            if (input instanceof List<?>) {
              ((List<?>) input).removeAll(fDeletedScoredNews);
              fResultViewer.refresh();
            }
          } else
            fResultViewer.remove(fDeletedScoredNews.toArray());
        }

        /* News got Updated */
        if (fUpdatedScoredNews != null)
          fResultViewer.update(fUpdatedScoredNews.toArray(), null);

        /* Update Browser Viewer if visible */
        if (fBrowserViewer.getControl().isVisible()) {
          Object input = fBrowserViewer.getInput();

          if (fUpdatedNewsEvents != null) {
            for (NewsEvent event : fUpdatedNewsEvents) {
              if (event.getEntity().equals(input)) {
                fBrowserViewer.update(Collections.singleton(event));
                break; // Viewer only shows 1 News at maximum
              }
            }
          }

          if (fDeletedScoredNews != null) {
            for (ScoredNews news : fDeletedScoredNews) {
              if (news.getNews().equals(input)) {
                fBrowserViewer.remove(news.getNews());
                break; // Viewer only shows 1 News at maximum
              }
            }
          }
        }
      }
    });
  }

  private void onMouseDown(Event event) {
    Point p = new Point(event.x, event.y);
    TableItem item = fResultViewer.getTable().getItem(p);

    /* Problem - return */
    if (item == null || item.isDisposed())
      return;

    /* Mouse-Up over Read-State-Column */
    if (event.button == 1 && isInImageBounds(item, NewsColumn.TITLE, p)) {
      Object data = item.getData();

      /* Toggle State between Read / Unread */
      if (data instanceof ScoredNews) {
        INews news = ((ScoredNews) data).getNews();
        INews.State newState = (news.getState() == INews.State.READ) ? INews.State.UNREAD : INews.State.READ;
        setNewsState(new ArrayList<INews>(Arrays.asList(new INews[] { news })), newState);
        fLastColumnActionInvokedMillies = System.currentTimeMillis();
      }
    }

    /* Mouse-Up over Sticky-State-Column */
    else if (event.button == 1 && isInImageBounds(item, NewsColumn.STICKY, p)) {
      Object data = item.getData();

      /* Toggle State between Sticky / Not Sticky */
      if (data instanceof ScoredNews) {
        new MakeNewsStickyAction(new StructuredSelection(((ScoredNews) data).getNews())).run();
        fLastColumnActionInvokedMillies = System.currentTimeMillis();
      }
    }

    /* Mouse-Up over Attachments-Column */
    else if (event.button == 1 && isInImageBounds(item, NewsColumn.ATTACHMENTS, p)) {
      Object data = item.getData();

      if (data instanceof ScoredNews) {
        MenuManager contextMenu = new MenuManager();
        ApplicationActionBarAdvisor.fillAttachmentsMenu(contextMenu, new StructuredSelection(((ScoredNews) data).getNews()), this, true);

        if (fAttachmentsMenu != null)
          fAttachmentsMenu.dispose();

        fAttachmentsMenu = contextMenu.createContextMenu(fResultViewer.getControl());

        Point cursorLocation = item.getDisplay().getCursorLocation();
        cursorLocation.y = cursorLocation.y + 16;
        fAttachmentsMenu.setLocation(cursorLocation);
        fAttachmentsMenu.setVisible(true);

        fLastColumnActionInvokedMillies = System.currentTimeMillis();
      }
    }
  }

  private void onMouseMove(Event event) {
    Point p = new Point(event.x, event.y);
    TableItem item = fResultViewer.getTable().getItem(p);

    /* Problem / Group hovered - reset */
    if (item == null || item.isDisposed() || item.getData() instanceof EntityGroup) {
      if (fShowsHandCursor && !fResultViewer.getControl().isDisposed()) {
        fResultViewer.getControl().setCursor(null);
        fShowsHandCursor = false;
      }
      return;
    }

    /* Show Hand-Cursor if action can be performed */
    boolean changeToHandCursor = isInImageBounds(item, NewsColumn.TITLE, p) || isInImageBounds(item, NewsColumn.STICKY, p) || isInImageBounds(item, NewsColumn.ATTACHMENTS, p);
    if (!fShowsHandCursor && changeToHandCursor) {
      fResultViewer.getControl().setCursor(fHandCursor);
      fShowsHandCursor = true;
    } else if (fShowsHandCursor && !changeToHandCursor) {
      fResultViewer.getControl().setCursor(null);
      fShowsHandCursor = false;
    }
  }

  private void unregisterListeners() {
    DynamicDAO.removeEntityListener(INews.class, fNewsListener);
    DynamicDAO.removeEntityListener(ILabel.class, fLabelListener);
  }

  private void onMouseDoubleClick(DoubleClickEvent event) {
    IStructuredSelection selection = (IStructuredSelection) event.getSelection();
    if (selection.isEmpty())
      return;

    /* Do nothing if the user recently invokved a column action */
    if (System.currentTimeMillis() - fLastColumnActionInvokedMillies > 200) {

      /* Convert Selection to INews */
      List<?> selectedElements = selection.toList();
      List<INews> selectedNews = new ArrayList<INews>();
      for (Object selectedElement : selectedElements) {
        ScoredNews scoredNews = (ScoredNews) selectedElement;
        selectedNews.add(scoredNews.getNews());
      }

      /* Open News */
      new OpenNewsAction(new StructuredSelection(selectedNews), getShell()).run();
    }
  }

  private void hookContextualMenu() {
    MenuManager manager = new MenuManager();
    manager.setRemoveAllWhenShown(true);
    manager.addMenuListener(new IMenuListener() {
      public void menuAboutToShow(IMenuManager manager) {
        final IStructuredSelection selection = (IStructuredSelection) fResultViewer.getSelection();

        /* Open */
        {

          /* Open in FeedView */
          manager.add(new Separator("internalopen")); //$NON-NLS-1$
          if (!selection.isEmpty())
            manager.appendToGroup("internalopen", new OpenNewsAction(selection, getShell())); //$NON-NLS-1$

          manager.add(new GroupMarker("open")); //$NON-NLS-1$

          /* Show only when internal browser is used */
          if (!selection.isEmpty() && !fPreferences.getBoolean(DefaultPreferences.USE_CUSTOM_EXTERNAL_BROWSER) && !fPreferences.getBoolean(DefaultPreferences.USE_DEFAULT_EXTERNAL_BROWSER))
            manager.add(new OpenInExternalBrowserAction(selection));
        }

        /* Attachments */
        {
          ApplicationActionBarAdvisor.fillAttachmentsMenu(manager, selection, new SameShellProvider(getShell()), false);
        }

        /* Mark / Label */
        if (!selection.isEmpty()) {
          manager.add(new Separator("mark")); //$NON-NLS-1$

          /* Mark */
          MenuManager markMenu = new MenuManager(Messages.SearchNewsDialog_MARK, "mark"); //$NON-NLS-1$
          manager.add(markMenu);

          /* Mark as Read */
          IAction action = new ToggleReadStateAction(selection);
          action.setEnabled(!selection.isEmpty());
          markMenu.add(action);

          /* Sticky */
          markMenu.add(new Separator());
          action = new MakeNewsStickyAction(selection);
          action.setEnabled(!selection.isEmpty());
          markMenu.add(action);

          /* Label */
          ApplicationActionBarAdvisor.fillLabelMenu(manager, selection, new SameShellProvider(getShell()), false);
        }

        /* Move To / Copy To */
        if (!selection.isEmpty()) {
          manager.add(new Separator("movecopy")); //$NON-NLS-1$

          /* Load all news bins and sort by name */
          List<INewsBin> newsbins = new ArrayList<INewsBin>(DynamicDAO.loadAll(INewsBin.class));

          Comparator<INewsBin> comparator = new Comparator<INewsBin>() {
            public int compare(INewsBin o1, INewsBin o2) {
              return o1.getName().compareTo(o2.getName());
            };
          };

          Collections.sort(newsbins, comparator);

          /* Move To */
          MenuManager moveMenu = new MenuManager(Messages.SearchNewsDialog_MOVE, "moveto"); //$NON-NLS-1$
          manager.add(moveMenu);

          for (INewsBin bin : newsbins) {
            moveMenu.add(new MoveCopyNewsToBinAction(selection, bin, true));
          }

          moveMenu.add(new MoveCopyNewsToBinAction(selection, null, true));
          moveMenu.add(new Separator());
          moveMenu.add(new AutomateFilterAction(PresetAction.MOVE, selection));

          /* Copy To */
          MenuManager copyMenu = new MenuManager(Messages.SearchNewsDialog_COPY, "copyto"); //$NON-NLS-1$
          manager.add(copyMenu);

          for (INewsBin bin : newsbins) {
            copyMenu.add(new MoveCopyNewsToBinAction(selection, bin, false));
          }

          copyMenu.add(new MoveCopyNewsToBinAction(selection, null, false));
          copyMenu.add(new Separator());
          copyMenu.add(new AutomateFilterAction(PresetAction.COPY, selection));
        }

        /* Share */
        {
          ApplicationActionBarAdvisor.fillShareMenu(manager, selection, new SameShellProvider(getShell()), false);
        }

        manager.add(new Separator("filter")); //$NON-NLS-1$
        manager.add(new Separator("copy")); //$NON-NLS-1$
        manager.add(new GroupMarker("edit")); //$NON-NLS-1$
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
      }
    });

    /* Create and Register with Workbench */
    Menu menu = manager.createContextMenu(fResultViewer.getControl());
    fResultViewer.getControl().setMenu(menu);

    /* Register with Part Site */
    IWorkbenchWindow window = OwlUI.getWindow();
    if (window != null) {
      IWorkbenchPart activePart = window.getPartService().getActivePart();
      if (activePart != null && activePart.getSite() != null)
        activePart.getSite().registerContextMenu(manager, fResultViewer);
    }
  }

  private IStructuredContentProvider getContentProvider() {
    return new IStructuredContentProvider() {
      public Object[] getElements(Object inputElement) {
        if (inputElement instanceof List<?>)
          return getVisibleNews((List<?>) inputElement);

        return new Object[0];
      }

      public void dispose() {}

      public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
    };
  }

  private Object[] getVisibleNews(List<?> elements) {
    List<ScoredNews> news = new ArrayList<ScoredNews>();
    Set<INews.State> visibleStates = INews.State.getVisible();
    for (Object element : elements) {
      if (element instanceof ScoredNews) {
        ScoredNews scoredNews = (ScoredNews) element;
        if (visibleStates.contains(scoredNews.getState()))
          news.add((ScoredNews) element);
      }
    }

    return news.toArray();
  }

  private List<ISearchCondition> getDefaultConditions() {
    List<ISearchCondition> conditions = new ArrayList<ISearchCondition>(1);
    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$

    conditions.add(condition);

    return conditions;
  }

  /*
   * @see org.eclipse.jface.window.Window#getShellStyle()
   */
  @Override
  protected int getShellStyle() {
    int style = SWT.TITLE | SWT.BORDER | SWT.MIN | SWT.MAX | SWT.RESIZE | SWT.CLOSE | getDefaultOrientation();

    return style;
  }

  /*
   * @see org.eclipse.jface.dialogs.Dialog#getDialogBoundsSettings()
   */
  @Override
  protected IDialogSettings getDialogBoundsSettings() {
    IDialogSettings section = fDialogSettings.getSection(SETTINGS_SECTION);
    if (section != null)
      return section;

    return fDialogSettings.addNewSection(SETTINGS_SECTION);
  }

  /*
   * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
   */
  @Override
  protected void initializeBounds() {
    super.initializeBounds();

    /* No dialog settings stored */
    if (fFirstTimeOpen) {

      /* Minimum Size */
      int minWidth = convertHorizontalDLUsToPixels(DIALOG_MIN_WIDTH);

      Point bestSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
      getShell().setSize(minWidth, bestSize.y);
      LayoutUtils.positionShell(getShell());
    }

    /* Move a bit to bottom right if multiple dialogs are open at the same time */
    if (fgOpenDialogCount > 1) {
      Point location = getShell().getLocation();
      location.x += 20 * (fgOpenDialogCount - 1);
      location.y += 20 * (fgOpenDialogCount - 1);
      getShell().setLocation(location);
    }
  }

  private void setNewsState(List<INews> news, INews.State state) {

    /* Add to UndoStack */
    UndoStack.getInstance().addOperation(new NewsStateOperation(news, state, true));

    /* Perform Operation */
    Owl.getPersistenceService().getDAOService().getNewsDAO().setState(news, state, true, false);
  }

  private int indexOf(NewsColumn column) {
    Table table = fCustomTable.getControl();
    if (table.isDisposed())
      return -1;

    TableColumn[] columns = table.getColumns();
    for (int i = 0; i < columns.length; i++) {
      if (column == columns[i].getData(NewsColumnViewModel.COL_ID))
        return i;
    }

    return -1;
  }

  private boolean isInImageBounds(TableItem item, NewsColumn column, Point p) {
    int index = indexOf(column);
    if (index == -1)
      return false;

    return item.getImageBounds(index).contains(p);
  }
}
TOP

Related Classes of org.rssowl.ui.internal.dialogs.SearchNewsDialog$FeedColumnToolTipSupport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.