/* ********************************************************************** **
** 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.actions;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.core.runtime.IExecutableExtensionFactory;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.PartInitException;
import org.rssowl.core.persist.IFolder;
import org.rssowl.core.persist.IFolderChild;
import org.rssowl.core.persist.IMark;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INewsMark;
import org.rssowl.core.util.CoreUtils;
import org.rssowl.core.util.ITreeNode;
import org.rssowl.core.util.ModelTreeNode;
import org.rssowl.core.util.TreeTraversal;
import org.rssowl.ui.internal.Activator;
import org.rssowl.ui.internal.FolderNewsMark;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.editors.feed.FeedView;
import org.rssowl.ui.internal.editors.feed.FeedViewInput;
import org.rssowl.ui.internal.editors.feed.PerformAfterInputSet;
import org.rssowl.ui.internal.views.explorer.BookMarkExplorer;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* The <code>NavigationActionFactory</code> is providing a list of common
* Actions to navigate to News-Items or Feeds.
*
* @author bpasero
*/
public class NavigationActionFactory implements IExecutableExtensionFactory, IExecutableExtension {
private String fId;
/** Actual Action for the Navigation */
public static class NavigationAction implements IWorkbenchWindowActionDelegate {
private final NavigationActionType fType;
/**
* @param type the type of Navigation.
*/
public NavigationAction(NavigationActionType type) {
fType = type;
}
public void dispose() {}
public void init(IWorkbenchWindow window) {}
public void run(IAction action) {
/* Tab Navigation */
if (fType == NavigationActionType.NEXT_TAB || fType == NavigationActionType.PREVIOUS_TAB) {
navigateInTabs();
}
/* News/Feed Navigation */
else {
/* 1.) Navigate in opened FeedView */
if (fType.isNewsScoped() && navigateOnActiveFeedView())
return;
/* 2.) Navigate in opened Explorer */
if (navigateOnOpenExplorer())
return;
/* 3.) Navigate on entire Model */
if (navigateOnModel())
return;
}
}
private void navigateInTabs() {
/* Current Active Editor */
IEditorPart activeEditor = OwlUI.getActiveEditor();
if (activeEditor == null)
return;
List<IEditorReference> editors = OwlUI.getEditorReferences();
int index = -1;
for (int i = 0; i < editors.size(); i++) {
try {
if (activeEditor.getEditorInput().equals(editors.get(i).getEditorInput())) {
index = i;
break;
}
} catch (PartInitException e) {
Activator.getDefault().logError(e.getMessage(), e);
}
}
if (index < 0)
return;
IEditorPart tab = null;
/* Next Tab */
if (fType == NavigationActionType.NEXT_TAB)
tab = editors.get(index + 1 < editors.size() ? index + 1 : 0).getEditor(true);
/* Previous Tab */
else if (fType == NavigationActionType.PREVIOUS_TAB)
tab = editors.get(index - 1 >= 0 ? index - 1 : editors.size() - 1).getEditor(true);
/* Activate */
if (tab != null) {
IWorkbenchPage page = tab.getSite().getPage();
page.activate(tab.getSite().getPart());
page.activate(tab);
}
}
private boolean navigateOnActiveFeedView() {
/* Get active FeedView if any */
FeedView activeFeedView = OwlUI.getActiveFeedView();
/* Run on active FeedView if any */
if (activeFeedView != null) {
boolean success = activeFeedView.navigate(true, false, fType.isNext(), fType.isUnread());
/* For unread & next news, consider all news of the active feed */
if (!success && fType.isNewsScoped() && fType.isUnread() && fType.isNext())
success = activeFeedView.navigate(false, false, fType.isNext(), fType.isUnread());
if (success) {
IWorkbenchPage page = activeFeedView.getSite().getPage();
page.activate(activeFeedView.getSite().getPart());
page.activate(activeFeedView);
return true;
}
}
return false;
}
private boolean navigateOnOpenExplorer() {
/* Try finding the open Explorer for BookMarks */
BookMarkExplorer bookmarkExplorer = OwlUI.getOpenedBookMarkExplorer();
if (bookmarkExplorer == null)
return false;
/* Navigate on Explorer */
bookmarkExplorer.navigate(fType.isNext(), fType.isUnread(), fType.performOnFeedView());
return true; //Avoid navigation on Model if Explorer is Opened
}
private boolean navigateOnModel() {
List<IFolderChild> startingNodes = new ArrayList<IFolderChild>();
/* Check Current Active FeedView */
FeedView activeFeedView = OwlUI.getActiveFeedView();
if (activeFeedView != null) {
IEditorInput input = activeFeedView.getEditorInput();
if (input != null && input instanceof FeedViewInput) {
INewsMark mark = ((FeedViewInput) input).getMark();
if (mark != null)
startingNodes.add(mark instanceof FolderNewsMark ? ((FolderNewsMark) mark).getFolder() : mark);
}
}
/* Add all Root Folders */
startingNodes.addAll(CoreUtils.loadRootFolders());
/* Select from all available Starting Nodes */
ITreeNode targetNode = null;
for (IFolderChild startingNode : startingNodes) {
TreeTraversal traversal = new TreeTraversal(startingNode instanceof IFolder ? new ModelTreeNode((IFolder) startingNode) : new ModelTreeNode((IMark) startingNode)) {
@Override
public boolean select(ITreeNode node) {
Object data = node.getData();
/* Check for Unread news if required */
if (data instanceof INewsMark) {
INewsMark newsmark = (INewsMark) data;
if (fType.isUnread() && newsmark.getNewsCount(EnumSet.of(INews.State.NEW, INews.State.UNREAD, INews.State.UPDATED)) == 0)
return false;
}
/* Folders are no valid navigation nodes */
else if (data instanceof IFolder)
return false;
return true;
}
};
targetNode = fType.isNext() ? traversal.nextNode() : traversal.previousNode();
if (targetNode != null)
break;
}
/* Open Node if present */
if (targetNode != null) {
INewsMark mark = (INewsMark) targetNode.getData();
/* Open in FeedView */
PerformAfterInputSet perform = null;
if (fType.isNewsScoped() && fType.isUnread())
perform = PerformAfterInputSet.SELECT_UNREAD_NEWS;
else if (fType.isNewsScoped())
perform = PerformAfterInputSet.SELECT_FIRST_NEWS;
IWorkbenchPage page = OwlUI.getPage();
if (page != null)
OwlUI.openInFeedView(page, new StructuredSelection(mark), true, false, perform);
}
return targetNode != null;
}
public void selectionChanged(IAction action, ISelection selection) {}
}
/** Enumeration with all possible types of NavigationAction */
public enum NavigationActionType {
/** Action: Go to the next News */
NEXT_NEWS("nextNews", "org.rssowl.ui.NextNews", Messages.NavigationActionFactory_NEXT_NEWS, true, true, false), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to the next unread News */
NEXT_UNREAD_NEWS("nextUnreadNews", "org.rssowl.ui.NextUnreadNews", Messages.NavigationActionFactory_NEXT_UNREAD_NEWS, true, true, true), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to the next Feed */
NEXT_FEED("nextFeed", "org.rssowl.ui.NextFeed", Messages.NavigationActionFactory_NEXT_FEED, false, true, false), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to the next unread Feed */
NEXT_UNREAD_FEED("nextUnreadFeed", "org.rssowl.ui.NextUnreadFeed", Messages.NavigationActionFactory_NEXT_UNREAD_FEED, false, true, true), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to the previous News */
PREVIOUS_NEWS("previousNews", "org.rssowl.ui.PreviousNews", Messages.NavigationActionFactory_PREVIOUS_NEWS, true, false, false), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to the previous unread News */
PREVIOUS_UNREAD_NEWS("previousUnreadNews", "org.rssowl.ui.PreviousUnreadNews", Messages.NavigationActionFactory_PREVIOUS_UNREAD_NEWS, true, false, true), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to the previous Feed */
PREVIOUS_FEED("previousFeed", "org.rssowl.ui.PreviousFeed", Messages.NavigationActionFactory_PREVIOUS_FEED, false, false, false), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to the previous unread Feed */
PREVIOUS_UNREAD_FEED("previousUnreadFeed", "org.rssowl.ui.PreviousUnreadFeed", Messages.NavigationActionFactory_PREVIOUS_UNREAD_FEED, false, false, true), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to next Tab */
NEXT_TAB("nextTab", "org.rssowl.ui.NextTab", Messages.NavigationActionFactory_NEXT_TAB, false, false, false), //$NON-NLS-1$ //$NON-NLS-2$
/** Action: Go to previous Tab */
PREVIOUS_TAB("previousTab", "org.rssowl.ui.PreviousTab", Messages.NavigationActionFactory_PREVIOUS_TAB, false, false, false), //$NON-NLS-1$ //$NON-NLS-2$
/** Special Combined Actions only executed from Browser Viewer */
NEXT_FEED_NEXT_NEWS("nextFeedNextNews", null, null, false, true, false, true), //$NON-NLS-1$
NEXT_UNREAD_FEED_NEXT_UNREAD_NEWS("nextUnreadFeedNextUnreadNews", null, null, false, true, true, true), //$NON-NLS-1$
PREVIOUS_FEED_PREVIOUS_NEWS("previousFeedPreviousNews", null, null, false, false, false, true), //$NON-NLS-1$
PREVIOUS_UNREAD_FEED_PREVIOUS_UNREAD_NEWS("previousUnreadFeedPreviousUnreadNews", null, null, false, false, true, true); //$NON-NLS-1$
String fId;
String fCommandId;
private String fName;
boolean fIsNewsScoped;
boolean fIsNext;
boolean fIsUnread;
boolean fPerformOnFeedView;
NavigationActionType(String id, String commandId, String name, boolean isNewsScoped, boolean isNext, boolean isUnread) {
this(id, commandId, name, isNewsScoped, isNext, isUnread, isNewsScoped);
}
NavigationActionType(String id, String commandId, String name, boolean isNewsScoped, boolean isNext, boolean isUnread, boolean performOnFeedView) {
fId = id;
fCommandId = commandId;
fName = name;
fIsNewsScoped = isNewsScoped;
fIsNext = isNext;
fIsUnread = isUnread;
fPerformOnFeedView = performOnFeedView;
}
/**
* @return the id of this navigation action.
*/
public String getId() {
return fId;
}
/**
* @return the id of the command for this navigation action.
*/
public String getCommandId() {
return fCommandId;
}
/**
* @return the human readable name of this navigation action.
*/
public String getName() {
return fName;
}
/**
* @return <code>true</code> if the navigation is around news and
* <code>false</code> in the case of feeds.
*/
boolean isNewsScoped() {
return fIsNewsScoped;
}
/**
* @return <code>true</code> if navigation should only consider unread items
* and <code>false</code> otherwise.
*/
boolean isUnread() {
return fIsUnread;
}
/**
* @return <code>true</code> if the navigation goes to the next item and
* <code>false</code> otherwise.
*/
boolean isNext() {
return fIsNext;
}
/**
* @return <code>true</code> if the navigational action should be performed
* on the feedview and <code>false</code> otherwise.
*/
public boolean performOnFeedView() {
return fPerformOnFeedView;
}
};
/** Keep for reflection */
public NavigationActionFactory() {}
/*
* @see org.eclipse.core.runtime.IExecutableExtensionFactory#create()
*/
public Object create() {
if (NavigationActionType.NEXT_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.NEXT_NEWS);
if (NavigationActionType.NEXT_UNREAD_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.NEXT_UNREAD_NEWS);
if (NavigationActionType.NEXT_FEED.getId().equals(fId))
return new NavigationAction(NavigationActionType.NEXT_FEED);
if (NavigationActionType.NEXT_UNREAD_FEED.getId().equals(fId))
return new NavigationAction(NavigationActionType.NEXT_UNREAD_FEED);
if (NavigationActionType.NEXT_FEED_NEXT_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.NEXT_FEED_NEXT_NEWS);
if (NavigationActionType.NEXT_UNREAD_FEED_NEXT_UNREAD_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.NEXT_UNREAD_FEED_NEXT_UNREAD_NEWS);
if (NavigationActionType.PREVIOUS_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.PREVIOUS_NEWS);
if (NavigationActionType.PREVIOUS_UNREAD_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.PREVIOUS_UNREAD_NEWS);
if (NavigationActionType.PREVIOUS_FEED.getId().equals(fId))
return new NavigationAction(NavigationActionType.PREVIOUS_FEED);
if (NavigationActionType.PREVIOUS_UNREAD_FEED.getId().equals(fId))
return new NavigationAction(NavigationActionType.PREVIOUS_UNREAD_FEED);
if (NavigationActionType.PREVIOUS_FEED_PREVIOUS_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.PREVIOUS_FEED_PREVIOUS_NEWS);
if (NavigationActionType.PREVIOUS_UNREAD_FEED_PREVIOUS_UNREAD_NEWS.getId().equals(fId))
return new NavigationAction(NavigationActionType.PREVIOUS_UNREAD_FEED_PREVIOUS_UNREAD_NEWS);
if (NavigationActionType.NEXT_TAB.getId().equals(fId))
return new NavigationAction(NavigationActionType.NEXT_TAB);
if (NavigationActionType.PREVIOUS_TAB.getId().equals(fId))
return new NavigationAction(NavigationActionType.PREVIOUS_TAB);
return null;
}
/*
* @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
* java.lang.String, java.lang.Object)
*/
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
if (data instanceof String)
fId = (String) data;
else
throw new CoreException(Activator.getDefault().createErrorStatus("Data argument must be a String for " + getClass(), null)); //$NON-NLS-1$
}
}