/* ********************************************************************** **
** 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.editors.feed;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.LocationAdapter;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.browser.StatusTextEvent;
import org.eclipse.swt.browser.StatusTextListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.persist.pref.DefaultPreferences;
import org.rssowl.core.persist.IEntity;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INewsMark;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.persist.reference.NewsReference;
import org.rssowl.core.util.CoreUtils;
import org.rssowl.core.util.Pair;
import org.rssowl.core.util.StringUtils;
import org.rssowl.core.util.URIUtils;
import org.rssowl.ui.internal.ApplicationServer;
import org.rssowl.ui.internal.Controller;
import org.rssowl.ui.internal.EntityGroup;
import org.rssowl.ui.internal.ILinkHandler;
import org.rssowl.ui.internal.LinkTransformer;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.OwlUI.Layout;
import org.rssowl.ui.internal.util.LayoutUtils;
/**
* Part of the FeedView to display News in a BrowserViewer.
*
* @author bpasero
*/
public class NewsBrowserControl implements IFeedViewPart {
private IFeedViewSite fFeedViewSite;
private NewsBrowserViewer fViewer;
private ISelectionListener fSelectionListener;
private Object fInitialInput;
private boolean fInputSet;
private IPreferenceScope fInputPreferences;
private IPropertyChangeListener fPropertyChangeListener;
private boolean fStripImagesFromNews;
private boolean fStripMediaFromNews;
private boolean fHeadlinesOnly;
private NewsComparator fNewsSorter;
private FeedViewInput fEditorInput;
private Composite fInfoBar;
private Label fInfoBarSeparator;
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#init(org.rssowl.ui.internal.editors.feed.IFeedViewSite)
*/
public void init(IFeedViewSite feedViewSite) {
fFeedViewSite = feedViewSite;
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#onInputChanged(org.rssowl.ui.internal.editors.feed.FeedViewInput)
*/
public void onInputChanged(FeedViewInput input) {
fEditorInput = input;
fInputPreferences = Owl.getPreferenceService().getEntityScope(input.getMark());
fStripImagesFromNews = !fInputPreferences.getBoolean(DefaultPreferences.ENABLE_IMAGES);
fStripMediaFromNews = !fInputPreferences.getBoolean(DefaultPreferences.ENABLE_MEDIA);
fHeadlinesOnly = (OwlUI.getLayout(fInputPreferences) == Layout.HEADLINES);
if (fViewer != null && fViewer.getLabelProvider() != null) {
((NewsBrowserLabelProvider) fViewer.getLabelProvider()).setStripMediaFromNews(fStripImagesFromNews, fStripMediaFromNews);
((NewsBrowserLabelProvider) fViewer.getLabelProvider()).setHeadlinesOnly(fHeadlinesOnly);
}
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#onLayoutChanged(org.rssowl.ui.internal.OwlUI.Layout)
*/
public void onLayoutChanged(Layout newLayout) {
fHeadlinesOnly = (newLayout == Layout.HEADLINES);
if (fViewer != null) {
/* Indicate Headlines Mode */
if (fViewer.getLabelProvider() != null)
((NewsBrowserLabelProvider) fViewer.getLabelProvider()).setHeadlinesOnly(fHeadlinesOnly);
/* Pass on to Viewer */
fViewer.onLayoutChanged(newLayout);
}
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#createPart(org.eclipse.swt.widgets.Composite)
*/
public void createPart(Composite parent) {
Composite container = new Composite(parent, SWT.None);
container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
container.setLayout(LayoutUtils.createGridLayout(1, 0, 0, 0, 0, false));
container.setBackground(parent.getBackground());
/* Info Bar to indicate new incoming news when in news paper view */
createInfoBar(container);
setInfoBarVisible(false, false);
/* Browser Viewer for News */
fViewer = new NewsBrowserViewer(container, SWT.NONE, fFeedViewSite) {
@Override
protected void onRefresh() {
setInfoBarVisible(false);
}
};
fViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
}
private void createInfoBar(final Composite parent) {
fInfoBar = new Composite(parent, SWT.None);
fInfoBar.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
fInfoBar.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
fInfoBar.setLayout(LayoutUtils.createGridLayout(3, 3, 3));
fInfoBar.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
fInfoBar.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
onInfoBarClicked();
}
});
Label imgLabel = new Label(fInfoBar, SWT.None);
imgLabel.setImage(OwlUI.getImage(imgLabel, "icons/obj16/info.gif")); //$NON-NLS-1$
imgLabel.setBackground(fInfoBar.getBackground());
imgLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, true));
Link textLink = new Link(fInfoBar, SWT.NONE);
textLink.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
textLink.setBackground(fInfoBar.getBackground());
textLink.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
textLink.setText(Messages.NewsBrowserControl_ADDITIONAL_NEWS_INFO);
textLink.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onInfoBarClicked();
}
});
textLink.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
onInfoBarClicked();
}
});
ToolBar bar = new ToolBar(fInfoBar, SWT.FLAT);
bar.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, true));
bar.setBackground(fInfoBar.getBackground());
ToolItem closeItem = new ToolItem(bar, SWT.PUSH);
closeItem.setToolTipText(Messages.NewsBrowserControl_CLOSE);
closeItem.setImage(OwlUI.getImage(bar, "icons/etool16/close_normal.png")); //$NON-NLS-1$
closeItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
setInfoBarVisible(false);
}
});
/* Separator */
fInfoBarSeparator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
fInfoBarSeparator.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
}
private void onInfoBarClicked() {
boolean moveToTop = OwlUI.getPageSize(fInputPreferences).getPageSize() == 0; //Only move to top when paging is disabled
fViewer.refresh(true, moveToTop); //Refresh will take care of closing the info bar
}
/**
* @param visible <code>true</code> to show the info bar and
* <code>false</code> otherwise.
*/
public void setInfoBarVisible(boolean visible) {
setInfoBarVisible(visible, true);
}
private void setInfoBarVisible(boolean visible, boolean layout) {
/* Return early if disposed */
if (fInfoBar == null || fInfoBar.isDisposed())
return;
/* Return early if visibility already identical to request */
if (((GridData) fInfoBar.getLayoutData()).exclude != visible)
return;
/* Update Layout and Visibility */
((GridData) fInfoBarSeparator.getLayoutData()).exclude = !visible;
((GridData) fInfoBar.getLayoutData()).exclude = !visible;
fInfoBarSeparator.setVisible(visible);
fInfoBar.setVisible(visible);
if (layout)
fInfoBar.getParent().layout(true, true);
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#getViewer()
*/
public NewsBrowserViewer getViewer() {
return fViewer;
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#initViewer(org.eclipse.jface.viewers.IStructuredContentProvider,
* org.eclipse.jface.viewers.ViewerFilter)
*/
public void initViewer(IStructuredContentProvider contentProvider, ViewerFilter filter) {
/* Apply ContentProvider */
fViewer.setContentProvider(contentProvider);
/* Create LabelProvider */
NewsBrowserLabelProvider labelProvider = new NewsBrowserLabelProvider(fViewer);
labelProvider.setStripMediaFromNews(fStripImagesFromNews, fStripMediaFromNews);
labelProvider.setHeadlinesOnly(fHeadlinesOnly);
fViewer.setLabelProvider(labelProvider);
/* Create Sorter */
fNewsSorter = new NewsComparator();
fViewer.setComparator(fNewsSorter);
updateSorting(fEditorInput.getMark(), false);
/* Add ViewerFilter */
fViewer.addFilter(filter);
/* Register Listeners */
registerListener();
}
void updateSorting(Object input, boolean refreshIfChanged) {
if (fViewer.getControl().isDisposed())
return;
IPreferenceScope preferences;
if (input instanceof IEntity)
preferences = Owl.getPreferenceService().getEntityScope((IEntity) input);
else
preferences = Owl.getPreferenceService().getGlobalScope();
NewsColumn sortColumn = NewsColumn.values()[preferences.getInteger(DefaultPreferences.BM_NEWS_SORT_COLUMN)];
boolean ascending = preferences.getBoolean(DefaultPreferences.BM_NEWS_SORT_ASCENDING);
NewsColumn oldSortColumn = fNewsSorter.getSortBy();
boolean oldAscending = fNewsSorter.isAscending();
fNewsSorter.setSortBy(sortColumn);
fNewsSorter.setAscending(ascending);
if (refreshIfChanged && ((oldSortColumn != sortColumn) || (oldAscending != ascending)))
fViewer.refresh();
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#setInput(java.lang.Object)
*/
public void setPartInput(Object input) {
/* Update Columns for Input */
if (input instanceof EntityGroup || input instanceof INewsMark)
updateSorting(input, false);
/* Set input to Viewer */
Pair<?, Boolean> computedInput = getInput(input);
fViewer.setInput(computedInput.getFirst(), computedInput.getSecond());
/* Remember as initial Input */
fInitialInput = fViewer.getInput();
fInputSet = true;
}
private Pair<? /* Input from News */, Boolean /* Block External Navigation */> getInput(Object obj) {
/* Return Reference */
if (obj instanceof INewsMark)
return Pair.create(((INewsMark) obj).toReference(), false);
/* News: Handle special dependant on settings */
else if (obj instanceof INews)
return getInput((INews) obj);
/* NewsReference: Resolve and special handle */
else if (obj instanceof NewsReference) {
INews resolvedNews = null;
if (fViewer.getContentProvider() instanceof NewsContentProvider)
resolvedNews = ((NewsContentProvider) fViewer.getContentProvider()).obtainFromCache(((NewsReference) obj).getId());
if (resolvedNews == null)
resolvedNews = ((NewsReference) obj).resolve();
return getInput(resolvedNews);
}
return Pair.create(obj, false);
}
private Pair<? /* Input from News */, Boolean /* Block External Navigation */> getInput(INews news) {
/* Check if user configured to open link of news or not */
if (!fInputPreferences.getBoolean(DefaultPreferences.BM_OPEN_SITE_FOR_NEWS))
return Pair.create(news, false);
/* Check if user configured to only link if news is empty or not */
if (fInputPreferences.getBoolean(DefaultPreferences.BM_OPEN_SITE_FOR_EMPTY_NEWS) && !CoreUtils.isEmpty(news))
return Pair.create(news, false);
/* Check if a news link is provided at all */
String newsLink = CoreUtils.getLink(news);
if (!StringUtils.isSet(newsLink))
return Pair.create(news, false);
/* Check if user configured to use a link transformer */
if (fInputPreferences.getBoolean(DefaultPreferences.BM_USE_TRANSFORMER)) {
String transformerId = fInputPreferences.getString(DefaultPreferences.BM_TRANSFORMER_ID);
LinkTransformer transformer = Controller.getDefault().getLinkTransformer(transformerId);
if (transformer != null)
return Pair.create(transformer.toTransformedUrl(newsLink), true);
}
/* User wants to open the link of the news */
return Pair.create(newsLink, false);
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#dispose()
*/
public void dispose() {
unregisterListeners();
fEditorInput = null;
}
private void registerListener() {
/* Listen on selection-changes */
fSelectionListener = new ISelectionListener() {
public void selectionChanged(IWorkbenchPart part, ISelection sel) {
/* Only Track selections from the HeadlineControl */
if (!part.equals(fFeedViewSite.getEditorSite().getPart()))
return;
/* Return early if browser is not even visible */
if (!fFeedViewSite.isBrowserViewerVisible())
return;
IStructuredSelection selection = (IStructuredSelection) sel;
/* Restore Initial Input (if set) if selection is empty */
if (selection.isEmpty() && fInputSet) {
fViewer.setInput(fInitialInput);
}
/* Set Elements as Input if 1 Item is selected */
else if (selection.size() == 1)
setPartInput(selection.getFirstElement());
}
};
fFeedViewSite.getEditorSite().getPage().addSelectionListener(fSelectionListener);
/* Send Browser-Status to Workbench-Status */
((Browser) fViewer.getControl()).addStatusTextListener(new StatusTextListener() {
public void changed(StatusTextEvent event) {
/* Don't show Status for the Handler Protocol */
if (event.text != null && !event.text.contains(ILinkHandler.HANDLER_PROTOCOL) && !event.text.contains(ApplicationServer.DEFAULT_LOCALHOST)) {
/* Do not post to status line if browser is hidden (e.g. hidden tab) */
if (!fViewer.getControl().isDisposed() && fViewer.getControl().isVisible()) {
String statusText = event.text;
statusText = URIUtils.fastDecode(statusText);
statusText = statusText.replaceAll("&", "&&"); //$NON-NLS-1$//$NON-NLS-2$
if (URIUtils.isManaged(statusText))
statusText = URIUtils.toUnManaged(statusText);
fFeedViewSite.getEditorSite().getActionBars().getStatusLineManager().setMessage(statusText);
}
}
}
});
/* Control Browser's visibility based on the location */
((Browser) fViewer.getControl()).addLocationListener(new LocationAdapter() {
@Override
public void changing(LocationEvent event) {
if (event.doit) {
String loc = event.location;
boolean visible = fViewer.getControl().getVisible();
/* Make Browser visible now */
if (!visible && StringUtils.isSet(loc) && !URIUtils.ABOUT_BLANK.equals(loc))
fViewer.getControl().setVisible(true);
}
}
});
/* Refresh Browser when Font Changes */
fPropertyChangeListener = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (fViewer.getControl().isDisposed())
return;
String property = event.getProperty();
if (OwlUI.NEWS_TEXT_FONT_ID.equals(property) || OwlUI.STICKY_BG_COLOR_ID.equals(property) || OwlUI.LINK_FG_COLOR_ID.equals(property) || OwlUI.NEWS_LIST_BG_COLOR_ID.equals(property))
fViewer.getBrowser().refresh();
}
};
PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(fPropertyChangeListener);
}
private void unregisterListeners() {
fFeedViewSite.getEditorSite().getPage().removeSelectionListener(fSelectionListener);
PlatformUI.getWorkbench().getThemeManager().removePropertyChangeListener(fPropertyChangeListener);
}
/*
* @see org.rssowl.ui.internal.editors.feed.IFeedViewPart#setFocus()
*/
public void setFocus() {
fViewer.getControl().setFocus();
}
}