/* ********************************************************************** **
** 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.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
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.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.actions.ActionFactory;
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.IModelFactory;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.INewsBin;
import org.rssowl.core.persist.INewsMark;
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.dao.DynamicDAO;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.util.ITask;
import org.rssowl.core.util.StringUtils;
import org.rssowl.core.util.TaskAdapter;
import org.rssowl.ui.internal.ContextMenuCreator;
import org.rssowl.ui.internal.Controller;
import org.rssowl.ui.internal.FolderNewsMark;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.OwlUI.Layout;
import org.rssowl.ui.internal.dialogs.SearchMarkDialog;
import org.rssowl.ui.internal.editors.feed.NewsFilter.SearchTarget;
import org.rssowl.ui.internal.editors.feed.NewsFilter.Type;
import org.rssowl.ui.internal.util.EditorUtils;
import org.rssowl.ui.internal.util.JobRunner;
import org.rssowl.ui.internal.util.JobTracker;
import org.rssowl.ui.internal.util.LayoutUtils;
import org.rssowl.ui.internal.util.ModelUtils;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* The FilterBar is the central control to filter News that are showing in the
* FeedView. It supports filtering, grouping and a quick-search.
*
* @author bpasero
*/
public class FilterBar {
/* Action to Filter News */
private static final String FILTER_ACTION = "org.rssowl.ui.internal.editors.feed.FilterAction"; //$NON-NLS-1$
/* Action to Group News */
private static final String GROUP_ACTION = "org.rssowl.ui.internal.editors.feed.GroupAction"; //$NON-NLS-1$
/* Action to Layout News */
private static final String LAYOUT_ACTION = "org.rssowl.ui.internal.editors.feed.LayoutAction"; //$NON-NLS-1$
/* Action to Quicksearch */
private static final String QUICKSEARCH_ACTION = "org.rssowl.ui.internal.editors.feed.QuickSearchAction"; //$NON-NLS-1$
private Composite fParent;
private Composite fContainer;
private ToolBarManager fFilterGroupingLayoutToolBarManager;
private ToolBarManager fClearQuicksearchToolBar;
private ToolBarManager fHighlightToolBarManager;
private IAction fHighlightSearchAction;
private FeedView fFeedView;
private JobTracker fQuickSearchTracker;
private Text fSearchInput;
private IPreferenceScope fGlobalPreferences;
private boolean fBlockRefresh;
private NewsFilter.Type fLastFilterType;
private NewsGrouping.Type fLastGroupType;
private boolean fSearchSelectAllOnce = true;
/**
* @param feedView
* @param parent
*/
public FilterBar(FeedView feedView, Composite parent) {
fFeedView = feedView;
fParent = parent;
fQuickSearchTracker = new JobTracker(500, false, true, ITask.Priority.SHORT);
fGlobalPreferences = Owl.getPreferenceService().getGlobalScope();
createControl();
}
boolean isVisible() {
IPreferenceScope globalScope = Owl.getPreferenceService().getGlobalScope();
boolean hideFilterBar = globalScope.getBoolean(DefaultPreferences.FV_FEED_TOOLBAR_HIDDEN);
return !hideFilterBar;
}
private boolean isListLayout() {
return (getLayout() == Layout.LIST);
}
private Layout getLayout() {
FeedViewInput input = ((FeedViewInput) fFeedView.getEditorInput());
if (input != null)
return OwlUI.getLayout(Owl.getPreferenceService().getEntityScope(input.getMark()));
return OwlUI.getLayout(fGlobalPreferences);
}
private boolean isSearchMark() {
FeedViewInput input = ((FeedViewInput) fFeedView.getEditorInput());
if (input != null && input.getMark() instanceof ISearchMark)
return true;
return false;
}
/**
* Clear the Quick-Search
*
* @param refresh
*/
public void clearQuickSearch(boolean refresh) {
setSearchControlsVisible(false);
if (fSearchInput.getText().length() != 0) {
fBlockRefresh = !refresh;
try {
fSearchInput.setText(""); //$NON-NLS-1$
} finally {
fBlockRefresh = false;
}
}
}
/** Give Focus to the Quicksearch Input */
public void focusQuickSearch() {
fSearchInput.setFocus();
}
private void createControl() {
fContainer = new Composite(fParent, SWT.NONE);
fContainer.setLayout(LayoutUtils.createGridLayout(5, 3, 0, 0, 0, false));
fContainer.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
updateVisibility();
/* Left Toolbar with Filter, Grouping and Layout */
fFilterGroupingLayoutToolBarManager = new ToolBarManager(SWT.FLAT | SWT.RIGHT);
createFilterBar();
createGrouperBar();
createLayoutBar();
fFilterGroupingLayoutToolBarManager.createControl(fContainer);
fFilterGroupingLayoutToolBarManager.getControl().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
/* Quick Search */
createQuickSearch(fContainer);
}
void updateVisibility() {
boolean isVisible = isVisible();
((GridData) fContainer.getLayoutData()).exclude = !isVisible;
fContainer.setVisible(isVisible);
}
private boolean setHighlight(boolean enabled) {
/* Highlighting is unsupported when headlines layout is used */
if (isListLayout())
return false;
/* Return if already in same state */
boolean isHighlightEnabled = fGlobalPreferences.getBoolean(DefaultPreferences.FV_HIGHLIGHT_SEARCH_RESULTS);
if (enabled == isHighlightEnabled)
return false;
fGlobalPreferences.putBoolean(DefaultPreferences.FV_HIGHLIGHT_SEARCH_RESULTS, !isHighlightEnabled);
fHighlightSearchAction.setChecked(!isHighlightEnabled);
return true;
}
/* Quick Search */
private void createQuickSearch(Composite parent) {
Composite searchContainer = new Composite(parent, SWT.NONE);
searchContainer.setLayout(LayoutUtils.createGridLayout(OwlUI.needsCancelControl() ? 4 : 3, 0, 0, 0, 0, false));
((GridLayout) searchContainer.getLayout()).marginTop = 1;
searchContainer.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, true));
((GridData) searchContainer.getLayoutData()).widthHint = 280;
final ToolBarManager manager = new ToolBarManager(SWT.FLAT);
final NewsFilter filter = fFeedView.getFilter();
IAction quickSearch = new Action(Messages.FilterBar_QUICK_SEARCH, IAction.AS_DROP_DOWN_MENU) {
@Override
public void run() {
OwlUI.positionDropDownMenu(this, manager);
}
@Override
public String getId() {
return QUICKSEARCH_ACTION;
}
};
quickSearch.setImageDescriptor(OwlUI.getImageDescriptor("icons/etool16/find.gif")); //$NON-NLS-1$
quickSearch.setMenuCreator(new ContextMenuCreator() {
@Override
public Menu createMenu(Control parent) {
Menu menu = new Menu(parent);
/* Search on: Subject */
final MenuItem searchHeadline = new MenuItem(menu, SWT.RADIO);
searchHeadline.setText(NewsFilter.SearchTarget.HEADLINE.getName());
searchHeadline.setSelection(NewsFilter.SearchTarget.HEADLINE == filter.getSearchTarget());
searchHeadline.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (searchHeadline.getSelection() && filter.getSearchTarget() != NewsFilter.SearchTarget.HEADLINE)
doSearch(NewsFilter.SearchTarget.HEADLINE);
}
});
/* Search on: Entire News */
final MenuItem searchEntireNews = new MenuItem(menu, SWT.RADIO);
searchEntireNews.setText(NewsFilter.SearchTarget.ALL.getName());
searchEntireNews.setSelection(NewsFilter.SearchTarget.ALL == filter.getSearchTarget());
searchEntireNews.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (searchEntireNews.getSelection() && filter.getSearchTarget() != NewsFilter.SearchTarget.ALL)
doSearch(NewsFilter.SearchTarget.ALL);
}
});
new MenuItem(menu, SWT.SEPARATOR);
/* Search on: Author */
final MenuItem searchAuthor = new MenuItem(menu, SWT.RADIO);
searchAuthor.setText(NewsFilter.SearchTarget.AUTHOR.getName());
searchAuthor.setSelection(NewsFilter.SearchTarget.AUTHOR == filter.getSearchTarget());
searchAuthor.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (searchAuthor.getSelection() && filter.getSearchTarget() != NewsFilter.SearchTarget.AUTHOR)
doSearch(NewsFilter.SearchTarget.AUTHOR);
}
});
/* Search on: Category */
final MenuItem searchCategory = new MenuItem(menu, SWT.RADIO);
searchCategory.setText(NewsFilter.SearchTarget.CATEGORY.getName());
searchCategory.setSelection(NewsFilter.SearchTarget.CATEGORY == filter.getSearchTarget());
searchCategory.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (searchCategory.getSelection() && filter.getSearchTarget() != NewsFilter.SearchTarget.CATEGORY)
doSearch(NewsFilter.SearchTarget.CATEGORY);
}
});
/* Search on: Source */
final MenuItem searchSource = new MenuItem(menu, SWT.RADIO);
searchSource.setText(NewsFilter.SearchTarget.SOURCE.getName());
searchSource.setSelection(NewsFilter.SearchTarget.SOURCE == filter.getSearchTarget());
searchSource.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (searchSource.getSelection() && filter.getSearchTarget() != NewsFilter.SearchTarget.SOURCE)
doSearch(NewsFilter.SearchTarget.SOURCE);
}
});
/* Search on: Attachments */
final MenuItem searchAttachments = new MenuItem(menu, SWT.RADIO);
searchAttachments.setText(NewsFilter.SearchTarget.ATTACHMENTS.getName());
searchAttachments.setSelection(NewsFilter.SearchTarget.ATTACHMENTS == filter.getSearchTarget());
searchAttachments.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (searchAttachments.getSelection() && filter.getSearchTarget() != NewsFilter.SearchTarget.ATTACHMENTS)
doSearch(NewsFilter.SearchTarget.ATTACHMENTS);
}
});
/* Search on: Labels */
final MenuItem searchLabels = new MenuItem(menu, SWT.RADIO);
searchLabels.setText(NewsFilter.SearchTarget.LABELS.getName());
searchLabels.setSelection(NewsFilter.SearchTarget.LABELS == filter.getSearchTarget());
searchLabels.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (searchLabels.getSelection() && filter.getSearchTarget() != NewsFilter.SearchTarget.LABELS)
doSearch(NewsFilter.SearchTarget.LABELS);
}
});
/* Offer to Save as Search */
INewsMark inputMark = ((FeedViewInput) fFeedView.getEditorInput()).getMark();
if (inputMark instanceof IBookMark || inputMark instanceof INewsBin || inputMark instanceof FolderNewsMark) {
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Convert Filter to Saved Search */
final MenuItem createSavedSearch = new MenuItem(menu, SWT.RADIO);
createSavedSearch.setText(Messages.FilterBar_SAVE_SEARCH);
createSavedSearch.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onCreateSavedSearch(true);
}
});
}
return menu;
}
});
manager.add(quickSearch);
manager.createControl(searchContainer);
/* Input for the Search */
fSearchInput = new Text(searchContainer, SWT.BORDER | SWT.SINGLE | SWT.SEARCH | SWT.CANCEL);
fSearchInput.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
fSearchInput.setMessage(fFeedView.getFilter().getSearchTarget().getName());
/* Register this Input Field to Context Service */
Controller.getDefault().getContextService().registerInputField(fSearchInput);
/* Reset any Filter if set on ESC */
fSearchInput.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ESC) {
clearQuickSearch(true);
fFeedView.handleQuicksearchTraversalEvent(SWT.TRAVERSE_RETURN, true);
}
}
});
/* Handle the CR Key Pressed */
fSearchInput.addTraverseListener(new TraverseListener() {
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_RETURN || e.detail == SWT.TRAVERSE_PAGE_NEXT || e.detail == SWT.TRAVERSE_PAGE_PREVIOUS) {
e.doit = false;
fFeedView.handleQuicksearchTraversalEvent(e.detail, false);
}
}
});
/* Run search when text is entered */
fSearchInput.addModifyListener(new ModifyListener() {
private boolean highlightChanged = false;
public void modifyText(ModifyEvent e) {
/* Clear Search immediately */
if (fSearchInput.getText().length() == 0 && fFeedView.getFilter().isPatternSet()) {
fFeedView.getFilter().setPattern(fSearchInput.getText());
if (!fBlockRefresh) {
BusyIndicator.showWhile(Display.getDefault(), new Runnable() {
public void run() {
if (highlightChanged) {
setHighlight(false);
highlightChanged = false;
}
if (needsCacheRevalidationFromSearch())
fFeedView.revalidateCaches();
fFeedView.refresh(true, false);
}
});
}
setSearchControlsVisible(false);
}
/* Run Search in JobTracker */
else if (fSearchInput.getText().length() > 0) {
fQuickSearchTracker.run(new TaskAdapter() {
public IStatus run(IProgressMonitor monitor) {
BusyIndicator.showWhile(Display.getDefault(), new Runnable() {
public void run() {
if (setHighlight(true))
highlightChanged = true;
fFeedView.getFilter().setPattern(fSearchInput.getText());
if (needsCacheRevalidationFromSearch())
fFeedView.revalidateCaches();
fFeedView.refresh(true, false);
updateBrowserSelection();
}
});
setSearchControlsVisible(true);
return Status.OK_STATUS;
}
});
}
}
});
fSearchInput.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
fFeedView.getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.CUT.getId()).setEnabled(true);
fFeedView.getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.COPY.getId()).setEnabled(true);
fFeedView.getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.PASTE.getId()).setEnabled(true);
}
public void focusLost(FocusEvent e) {
fFeedView.getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.CUT.getId()).setEnabled(false);
fFeedView.getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.COPY.getId()).setEnabled(false);
fFeedView.getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.PASTE.getId()).setEnabled(false);
fSearchSelectAllOnce = true;
}
});
/* Select All on Mouse Up */
fSearchInput.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
if (fSearchSelectAllOnce && fSearchInput.getSelectionCount() == 0)
fSearchInput.selectAll();
fSearchSelectAllOnce = false;
}
});
/* Clear Button */
if (OwlUI.needsCancelControl()) {
ToolBar toolBar = new ToolBar(searchContainer, SWT.FLAT | SWT.HORIZONTAL);
fClearQuicksearchToolBar = new ToolBarManager(toolBar);
toolBar.setBackground(parent.getBackground());
toolBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, true));
/* Initially Hide */
((GridData) toolBar.getLayoutData()).exclude = true;
toolBar.setVisible(false);
IAction clearTextAction = new Action("", IAction.AS_PUSH_BUTTON) {//$NON-NLS-1$
@Override
public void run() {
clearQuickSearch(true);
fFeedView.handleQuicksearchTraversalEvent(SWT.TRAVERSE_RETURN, true);
}
};
clearTextAction.setToolTipText(Messages.FilterBar_CLEAR);
clearTextAction.setImageDescriptor(OwlUI.getImageDescriptor("icons/etool16/clear.gif")); //$NON-NLS-1$
fClearQuicksearchToolBar.add(clearTextAction);
fClearQuicksearchToolBar.update(false);
}
/* Highlight Button */
ToolBar toolBar = new ToolBar(searchContainer, SWT.FLAT | SWT.HORIZONTAL);
fHighlightToolBarManager = new ToolBarManager(toolBar);
toolBar.setBackground(parent.getBackground());
toolBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, true));
((GridData) toolBar.getLayoutData()).horizontalIndent = 2;
/* Set Initial State based on Input and Layout */
boolean show = isSearchMark() && !isListLayout();
((GridData) toolBar.getLayoutData()).exclude = !show;
toolBar.setVisible(show);
fHighlightSearchAction = new Action(Messages.FilterBar_HIGHLIGHT, IAction.AS_CHECK_BOX) {
@Override
public void run() {
fGlobalPreferences.putBoolean(DefaultPreferences.FV_HIGHLIGHT_SEARCH_RESULTS, isChecked());
if (fFeedView.isBrowserViewerVisible())
fFeedView.getNewsBrowserControl().getViewer().refresh();
}
};
fHighlightSearchAction.setImageDescriptor(OwlUI.getImageDescriptor("icons/etool16/highlight.gif")); //$NON-NLS-1$
fHighlightSearchAction.setToolTipText(Messages.FilterBar_HIGHLIGHT);
fHighlightSearchAction.setChecked(fGlobalPreferences.getBoolean(DefaultPreferences.FV_HIGHLIGHT_SEARCH_RESULTS));
fHighlightToolBarManager.add(fHighlightSearchAction);
fHighlightToolBarManager.update(false);
}
private void updateBrowserSelection() {
if (fFeedView.isTableViewerVisible() && fFeedView.isBrowserViewerVisible() && fFeedView.isBrowserShowingNews()) {
NewsTableControl newsTable = fFeedView.getNewsTableControl();
if (newsTable.getViewer().getSelection().isEmpty())
fFeedView.getNewsBrowserControl().setPartInput(null);
}
}
void setSearchControlsVisible(boolean visible) {
if (!isVisible())
return;
boolean layout = false;
/* Clear */
if (fClearQuicksearchToolBar != null && !fClearQuicksearchToolBar.getControl().isDisposed() && ((GridData) fClearQuicksearchToolBar.getControl().getLayoutData()).exclude == visible) {
((GridData) fClearQuicksearchToolBar.getControl().getLayoutData()).exclude = !visible;
fClearQuicksearchToolBar.getControl().setVisible(visible);
layout = true;
}
/* Highlight */
if (isListLayout())
visible = false; //Never show highlight bar for headlines layout
else if (isSearchMark())
visible = true; //Always show highlight bar for saved searches
if (fHighlightToolBarManager != null && !fHighlightToolBarManager.getControl().isDisposed() && ((GridData) fHighlightToolBarManager.getControl().getLayoutData()).exclude == visible) {
((GridData) fHighlightToolBarManager.getControl().getLayoutData()).exclude = !visible;
fHighlightToolBarManager.getControl().setVisible(visible);
layout = true;
}
/* Layout as necessary */
if (layout)
fSearchInput.getParent().layout();
}
/* News Filter */
private void createFilterBar() {
final NewsFilter filter = fFeedView.getFilter();
IAction newsFilterAction = new Action(Messages.FilterBar_FILTER_NEWS, IAction.AS_DROP_DOWN_MENU) {
@Override
public void run() {
/* Toggle Show All */
if (filter.getType() != NewsFilter.Type.SHOW_ALL)
onFilter(NewsFilter.Type.SHOW_ALL);
/* Toggle back to previous filter */
else if (fLastFilterType != null)
onFilter(fLastFilterType);
/* Show Menu */
else
OwlUI.positionDropDownMenu(this, fFilterGroupingLayoutToolBarManager);
}
@Override
public ImageDescriptor getImageDescriptor() {
if (filter.getType() == NewsFilter.Type.SHOW_ALL)
return OwlUI.FILTER;
return OwlUI.getImageDescriptor("icons/etool16/filter_active.gif"); //$NON-NLS-1$
}
@Override
public String getText() {
return filter.getType().getDisplayName();
}
};
newsFilterAction.setId(FILTER_ACTION);
ActionContributionItem item = new ActionContributionItem(newsFilterAction);
item.setMode(ActionContributionItem.MODE_FORCE_TEXT);
fFilterGroupingLayoutToolBarManager.add(item);
newsFilterAction.setMenuCreator(new ContextMenuCreator() {
@Override
public Menu createMenu(Control parent) {
Menu menu = new Menu(parent);
/* Filter: None */
final MenuItem showAll = new MenuItem(menu, SWT.RADIO);
showAll.setText(NewsFilter.Type.SHOW_ALL.getName());
showAll.setSelection(NewsFilter.Type.SHOW_ALL == filter.getType());
showAll.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (showAll.getSelection() && filter.getType() != NewsFilter.Type.SHOW_ALL)
onFilter(NewsFilter.Type.SHOW_ALL);
}
});
menu.setDefaultItem(showAll);
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Filter: New */
final MenuItem showNew = new MenuItem(menu, SWT.RADIO);
showNew.setText(NewsFilter.Type.SHOW_NEW.getName());
showNew.setSelection(NewsFilter.Type.SHOW_NEW == filter.getType());
showNew.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (showNew.getSelection() && filter.getType() != NewsFilter.Type.SHOW_NEW)
onFilter(NewsFilter.Type.SHOW_NEW);
}
});
/* Filter: Unread */
final MenuItem showUnread = new MenuItem(menu, SWT.RADIO);
showUnread.setText(NewsFilter.Type.SHOW_UNREAD.getName());
showUnread.setSelection(NewsFilter.Type.SHOW_UNREAD == filter.getType());
showUnread.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (showUnread.getSelection() && filter.getType() != NewsFilter.Type.SHOW_UNREAD)
onFilter(NewsFilter.Type.SHOW_UNREAD);
}
});
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Filter: Sticky */
final MenuItem showSticky = new MenuItem(menu, SWT.RADIO);
showSticky.setText(NewsFilter.Type.SHOW_STICKY.getName());
showSticky.setSelection(NewsFilter.Type.SHOW_STICKY == filter.getType());
showSticky.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (showSticky.getSelection() && filter.getType() != NewsFilter.Type.SHOW_STICKY)
onFilter(NewsFilter.Type.SHOW_STICKY);
}
});
/* Filter: Labeled */
final MenuItem showLabeled = new MenuItem(menu, SWT.RADIO);
showLabeled.setText(NewsFilter.Type.SHOW_LABELED.getName());
showLabeled.setSelection(NewsFilter.Type.SHOW_LABELED == filter.getType());
showLabeled.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (showLabeled.getSelection() && filter.getType() != NewsFilter.Type.SHOW_LABELED)
onFilter(NewsFilter.Type.SHOW_LABELED);
}
});
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Filter: Recent News */
final MenuItem showRecent = new MenuItem(menu, SWT.RADIO);
showRecent.setText(NewsFilter.Type.SHOW_RECENT.getName());
showRecent.setSelection(NewsFilter.Type.SHOW_RECENT == filter.getType());
showRecent.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (showRecent.getSelection() && filter.getType() != NewsFilter.Type.SHOW_RECENT)
onFilter(NewsFilter.Type.SHOW_RECENT);
}
});
/* Filter: Last 5 Days */
final MenuItem showLastFiveDays = new MenuItem(menu, SWT.RADIO);
showLastFiveDays.setText(NewsFilter.Type.SHOW_LAST_5_DAYS.getName());
showLastFiveDays.setSelection(NewsFilter.Type.SHOW_LAST_5_DAYS == filter.getType());
showLastFiveDays.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (showLastFiveDays.getSelection() && filter.getType() != NewsFilter.Type.SHOW_LAST_5_DAYS)
onFilter(NewsFilter.Type.SHOW_LAST_5_DAYS);
}
});
/* Offer to Save as Search */
INewsMark inputMark = ((FeedViewInput) fFeedView.getEditorInput()).getMark();
if (inputMark instanceof IBookMark || inputMark instanceof INewsBin || inputMark instanceof FolderNewsMark) {
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Convert Filter to Saved Search */
final MenuItem createSavedSearch = new MenuItem(menu, SWT.RADIO);
createSavedSearch.setText(Messages.FilterBar_SAVE_SEARCH);
createSavedSearch.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onCreateSavedSearch(false);
}
});
}
return menu;
}
});
}
private void onCreateSavedSearch(boolean withQuickSearch) {
IModelFactory factory = Owl.getModelFactory();
List<ISearchCondition> conditions = new ArrayList<ISearchCondition>(2);
/* Create Condition from Location */
List<IFolderChild> searchScope = new ArrayList<IFolderChild>(1);
searchScope.add(((FeedViewInput) fFeedView.getEditorInput()).getMark());
ISearchField field = factory.createSearchField(INews.LOCATION, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.SCOPE, ModelUtils.toPrimitive(searchScope)));
/* Create Condition from Filter */
Type filterType = fFeedView.getFilter().getType();
switch (filterType) {
case SHOW_ALL:
if (!withQuickSearch) {
field = factory.createSearchField(IEntity.ALL_FIELDS, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.CONTAINS_ALL, "")); //$NON-NLS-1$
}
break;
case SHOW_NEW:
field = factory.createSearchField(INews.STATE, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS, EnumSet.of(INews.State.NEW)));
break;
case SHOW_RECENT:
field = factory.createSearchField(INews.AGE_IN_DAYS, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS_LESS_THAN, 2));
break;
case SHOW_LAST_5_DAYS:
field = factory.createSearchField(INews.AGE_IN_DAYS, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS_LESS_THAN, 6));
break;
case SHOW_STICKY:
field = factory.createSearchField(INews.IS_FLAGGED, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS, true));
break;
case SHOW_LABELED:
field = factory.createSearchField(INews.LABEL, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS, "*")); //$NON-NLS-1$
break;
case SHOW_UNREAD:
field = factory.createSearchField(INews.STATE, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS, EnumSet.of(INews.State.NEW, INews.State.UNREAD, INews.State.UPDATED)));
break;
}
/* Also add Quick Search if required */
if (withQuickSearch) {
SearchTarget target = fFeedView.getFilter().getSearchTarget();
String text = fSearchInput.getText();
/* Convert to Wildcard Query */
if (StringUtils.supportsTrailingWildcards(text))
text = text + "*"; //$NON-NLS-1$
switch (target) {
case ALL:
field = factory.createSearchField(IEntity.ALL_FIELDS, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.CONTAINS_ALL, text));
break;
case HEADLINE:
field = factory.createSearchField(INews.TITLE, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.CONTAINS_ALL, text));
break;
case ATTACHMENTS:
field = factory.createSearchField(INews.ATTACHMENTS_CONTENT, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.CONTAINS_ALL, text));
break;
case AUTHOR:
field = factory.createSearchField(INews.AUTHOR, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.CONTAINS_ALL, text));
break;
case CATEGORY:
field = factory.createSearchField(INews.CATEGORIES, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS, text));
break;
case LABELS:
field = factory.createSearchField(INews.LABEL, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS, text));
break;
case SOURCE:
field = factory.createSearchField(INews.SOURCE, INews.class.getName());
conditions.add(factory.createSearchCondition(field, SearchSpecifier.IS, text));
break;
}
}
/* Create and Show SM Dialog */
SearchMarkDialog dialog = new SearchMarkDialog(fParent.getShell(), OwlUI.getBookMarkExplorerSelection(), null, conditions, true);
dialog.open();
}
private void onFilter(NewsFilter.Type type) {
doFilter(type, true, true);
EditorUtils.updateFilterAndGrouping(fFeedView);
}
void doFilter(final NewsFilter.Type type, boolean refresh, boolean saveSettings) {
doFilter(type, refresh, saveSettings, null);
}
void doFilter(final NewsFilter.Type type, boolean refresh, boolean saveSettings, final Runnable browserRefreshRunnable) {
Type oldType = fFeedView.getFilter().getType();
boolean noChange = (oldType == type);
/* Remember last filter type */
if (type != Type.SHOW_ALL)
fLastFilterType = type;
else if (fFeedView.getFilter().getType() != Type.SHOW_ALL)
fLastFilterType = oldType;
/* No need to refresh or save settings if nothing changed */
if (noChange)
return;
/* Apply Type */
fFeedView.getFilter().setType(type);
fFilterGroupingLayoutToolBarManager.find(FILTER_ACTION).update();
/* Refresh if set */
if (refresh) {
final Runnable uiRunnable = new Runnable() {
public void run() {
if (browserRefreshRunnable != null) //If runnable is passed in, it will take care of refreshing
fFeedView.getNewsBrowserControl().getViewer().setBlockRefresh(true);
try {
/* Only Refresh Table as Browser shows single News */
NewsTableControl newsTable = fFeedView.getNewsTableControl();
boolean isNewsTableVisible = fFeedView.isTableViewerVisible();
if (newsTable != null && isNewsTableVisible)
fFeedView.refreshTableViewer(true, false);
/* Refresh All */
else
fFeedView.refresh(true, false);
/* Update Selection */
updateBrowserSelection();
/* Execute passed in code if provided */
if (browserRefreshRunnable != null)
browserRefreshRunnable.run();
} finally {
if (browserRefreshRunnable != null)
fFeedView.getNewsBrowserControl().getViewer().setBlockRefresh(false);
}
}
};
/* Filter has changed - ask Feedview to revalidate caches in Background Thread */
if (oldType != type) {
JobRunner.runInBackgroundWithBusyIndicator(new Runnable() {
public void run() {
/* Potential Long-op running in Background */
fFeedView.revalidateCaches();
/* Execute UI Code in UI Thread again */
JobRunner.runInUIThread(fParent, uiRunnable);
}
});
}
/* No Filter Change, directly run UI Code */
else
uiRunnable.run();
}
/* Update Settings */
if (saveSettings)
saveIntegerValue(DefaultPreferences.BM_NEWS_FILTERING, type.ordinal());
}
private boolean needsCacheRevalidationFromSearch() {
INewsMark mark = ((FeedViewInput) fFeedView.getEditorInput()).getMark();
return (mark instanceof FolderNewsMark && mark.getNewsCount(INews.State.getVisible()) > NewsContentProvider.MAX_FOLDER_ELEMENTS);
}
private void doSearch(final NewsFilter.SearchTarget target) {
fFeedView.getFilter().setSearchTarget(target);
fSearchInput.setMessage(fFeedView.getFilter().getSearchTarget().getName());
fSearchInput.setFocus();
if (fSearchInput.getText().length() > 0) {
if (needsCacheRevalidationFromSearch())
fFeedView.revalidateCaches();
fFeedView.refresh(true, false);
updateBrowserSelection();
}
/* Update Settings */
JobRunner.runInBackgroundThread(new Runnable() {
public void run() {
fGlobalPreferences.putInteger(DefaultPreferences.FV_SEARCH_TARGET, target.ordinal());
}
});
}
/* News Group */
private void createGrouperBar() {
final NewsGrouping grouping = fFeedView.getGrouper();
final IAction newsGroup = new Action(Messages.FilterBar_GROUP_NEWS, IAction.AS_DROP_DOWN_MENU) {
@Override
public void run() {
/* Toggle Ungrouped */
if (fFeedView.getGrouper().getType() != NewsGrouping.Type.NO_GROUPING)
onGrouping(NewsGrouping.Type.NO_GROUPING);
/* Toggle back to previous grouping */
else if (fLastGroupType != null)
onGrouping(fLastGroupType);
/* Show Menu */
else
OwlUI.positionDropDownMenu(this, fFilterGroupingLayoutToolBarManager);
}
@Override
public ImageDescriptor getImageDescriptor() {
if (grouping.getType() == NewsGrouping.Type.NO_GROUPING)
return OwlUI.getImageDescriptor("icons/etool16/group.gif"); //$NON-NLS-1$
return OwlUI.getImageDescriptor("icons/etool16/group_active.gif"); //$NON-NLS-1$
}
@Override
public String getText() {
return grouping.getType().getDisplayName();
}
};
newsGroup.setId(GROUP_ACTION);
newsGroup.setMenuCreator(new ContextMenuCreator() {
@Override
public Menu createMenu(Control parent) {
Menu menu = new Menu(parent);
/* Group: None */
final MenuItem noGrouping = new MenuItem(menu, SWT.RADIO);
noGrouping.setText(NewsGrouping.Type.NO_GROUPING.getName());
noGrouping.setSelection(grouping.getType() == NewsGrouping.Type.NO_GROUPING);
noGrouping.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (noGrouping.getSelection() && grouping.getType() != NewsGrouping.Type.NO_GROUPING)
onGrouping(NewsGrouping.Type.NO_GROUPING);
}
});
menu.setDefaultItem(noGrouping);
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Group: By Date */
final MenuItem groupByDate = new MenuItem(menu, SWT.RADIO);
groupByDate.setText(NewsGrouping.Type.GROUP_BY_DATE.getName());
groupByDate.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_DATE);
groupByDate.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByDate.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_DATE)
onGrouping(NewsGrouping.Type.GROUP_BY_DATE);
}
});
/* Group: By Author */
final MenuItem groupByAuthor = new MenuItem(menu, SWT.RADIO);
groupByAuthor.setText(NewsGrouping.Type.GROUP_BY_AUTHOR.getName());
groupByAuthor.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_AUTHOR);
groupByAuthor.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByAuthor.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_AUTHOR)
onGrouping(NewsGrouping.Type.GROUP_BY_AUTHOR);
}
});
/* Group: By Category */
final MenuItem groupByCategory = new MenuItem(menu, SWT.RADIO);
groupByCategory.setText(NewsGrouping.Type.GROUP_BY_CATEGORY.getName());
groupByCategory.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_CATEGORY);
groupByCategory.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByCategory.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_CATEGORY)
onGrouping(NewsGrouping.Type.GROUP_BY_CATEGORY);
}
});
/* Group: By Topic */
final MenuItem groupByTopic = new MenuItem(menu, SWT.RADIO);
groupByTopic.setText(NewsGrouping.Type.GROUP_BY_TOPIC.getName());
groupByTopic.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_TOPIC);
groupByTopic.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByTopic.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_TOPIC)
onGrouping(NewsGrouping.Type.GROUP_BY_TOPIC);
}
});
/* Group: By Feed */
final MenuItem groupByFeed = new MenuItem(menu, SWT.RADIO);
groupByFeed.setText(NewsGrouping.Type.GROUP_BY_FEED.getName());
groupByFeed.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_FEED);
groupByFeed.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByFeed.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_FEED)
onGrouping(NewsGrouping.Type.GROUP_BY_FEED);
}
});
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Group: By State */
final MenuItem groupByState = new MenuItem(menu, SWT.RADIO);
groupByState.setText(NewsGrouping.Type.GROUP_BY_STATE.getName());
groupByState.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_STATE);
groupByState.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByState.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_STATE)
onGrouping(NewsGrouping.Type.GROUP_BY_STATE);
}
});
/* Group: By Stickyness */
final MenuItem groupByStickyness = new MenuItem(menu, SWT.RADIO);
groupByStickyness.setText(NewsGrouping.Type.GROUP_BY_STICKY.getName());
groupByStickyness.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_STICKY);
groupByStickyness.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByStickyness.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_STICKY)
onGrouping(NewsGrouping.Type.GROUP_BY_STICKY);
}
});
/* Separator */
new MenuItem(menu, SWT.SEPARATOR);
/* Group: By Label */
final MenuItem groupByLabel = new MenuItem(menu, SWT.RADIO);
groupByLabel.setText(NewsGrouping.Type.GROUP_BY_LABEL.getName());
groupByLabel.setSelection(grouping.getType() == NewsGrouping.Type.GROUP_BY_LABEL);
groupByLabel.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (groupByLabel.getSelection() && grouping.getType() != NewsGrouping.Type.GROUP_BY_LABEL)
onGrouping(NewsGrouping.Type.GROUP_BY_LABEL);
}
});
return menu;
}
});
ActionContributionItem item = new ActionContributionItem(newsGroup);
item.setMode(ActionContributionItem.MODE_FORCE_TEXT);
fFilterGroupingLayoutToolBarManager.add(item);
}
/* Layout */
private void createLayoutBar() {
final IAction newsLayout = new Action("", IAction.AS_DROP_DOWN_MENU) { //$NON-NLS-1$
@Override
public void run() {
OwlUI.positionDropDownMenu(this, fFilterGroupingLayoutToolBarManager);
}
@Override
public ImageDescriptor getImageDescriptor() {
Layout currentLayout = getLayout();
switch (currentLayout) {
case CLASSIC:
return OwlUI.getImageDescriptor("icons/obj16/classic_layout.gif"); //$NON-NLS-1$
case VERTICAL:
return OwlUI.getImageDescriptor("icons/obj16/vertical_layout.gif"); //$NON-NLS-1$
case LIST:
return OwlUI.getImageDescriptor("icons/obj16/list_layout.gif"); //$NON-NLS-1$
case NEWSPAPER:
return OwlUI.getImageDescriptor("icons/obj16/newspaper_layout.gif"); //$NON-NLS-1$
case HEADLINES:
return OwlUI.getImageDescriptor("icons/obj16/headlines_layout.gif"); //$NON-NLS-1$
}
return OwlUI.getImageDescriptor("icons/obj16/classic_layout.gif"); //$NON-NLS-1$
}
@Override
public String getText() {
Layout currentLayout = getLayout();
return currentLayout.getName();
}
};
newsLayout.setId(LAYOUT_ACTION);
newsLayout.setMenuCreator(new ContextMenuCreator() {
@Override
public Menu createMenu(Control parent) {
Layout currentLayout = getLayout();
Menu menu = new Menu(parent);
Layout[] layouts = new Layout[] { Layout.CLASSIC, Layout.VERTICAL, Layout.LIST, Layout.NEWSPAPER, Layout.HEADLINES };
for (final Layout layout : layouts) {
final MenuItem layoutMenuItem = new MenuItem(menu, SWT.RADIO);
layoutMenuItem.setText(layout.getName());
layoutMenuItem.setSelection(layout == currentLayout);
layoutMenuItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (layoutMenuItem.getSelection())
onLayout(layout);
}
});
}
return menu;
}
});
ActionContributionItem item = new ActionContributionItem(newsLayout);
item.setMode(ActionContributionItem.MODE_FORCE_TEXT);
fFilterGroupingLayoutToolBarManager.add(item);
}
private void onGrouping(NewsGrouping.Type type) {
doGrouping(type, true, true);
EditorUtils.updateFilterAndGrouping(fFeedView);
}
private void onLayout(Layout layout) {
doLayout(layout, true);
EditorUtils.updateLayout();
}
void doLayout(final Layout layout, boolean saveSettings) {
/* Update Settings */
if (saveSettings)
saveIntegerValue(DefaultPreferences.FV_LAYOUT, layout.ordinal());
/* Update Toolbar */
fFilterGroupingLayoutToolBarManager.find(LAYOUT_ACTION).update();
}
void doGrouping(final NewsGrouping.Type type, boolean refresh, boolean saveSettings) {
boolean noChange = fFeedView.getGrouper().getType() == type;
if (type != NewsGrouping.Type.NO_GROUPING)
fLastGroupType = type;
else if (fFeedView.getGrouper().getType() != NewsGrouping.Type.NO_GROUPING)
fLastGroupType = fFeedView.getGrouper().getType();
fFeedView.getGrouper().setType(type);
fFilterGroupingLayoutToolBarManager.find(GROUP_ACTION).update();
/* No need to refresh or save settings if nothing changed */
if (noChange)
return;
/* Refresh if set */
if (refresh) {
NewsTableControl newsTable = fFeedView.getNewsTableControl();
boolean isNewsTableVisible = fFeedView.isTableViewerVisible();
try {
/* Only Refresh Table as Browser shows single News */
if (newsTable != null && isNewsTableVisible) {
newsTable.setBlockNewsStateTracker(true);
fFeedView.refreshTableViewer(true, false);
}
/* Refresh All */
else
fFeedView.refresh(true, false);
} finally {
if (newsTable != null && isNewsTableVisible)
newsTable.setBlockNewsStateTracker(false);
}
}
/* Update Settings */
if (saveSettings)
saveIntegerValue(DefaultPreferences.BM_NEWS_GROUPING, type.ordinal());
}
/*
* This Method stores an Integer value to either the entity scope or global scope,
* depending on if the current feed view input has the given setting stored in the
* entity or not.
*/
private void saveIntegerValue(String key, int value) {
FeedViewInput input = ((FeedViewInput) fFeedView.getEditorInput());
/* Save only into Entity if the Entity was configured with the given Settings before */
IPreferenceScope entityPrefs = Owl.getPreferenceService().getEntityScope(input.getMark());
if (entityPrefs.hasKey(key)) {
entityPrefs.putInteger(key, value);
if (input.getMark() instanceof FolderNewsMark)
DynamicDAO.save(((FolderNewsMark) input.getMark()).getFolder());
else
DynamicDAO.save(input.getMark());
}
/* Save Globally */
else
fGlobalPreferences.putInteger(key, value);
}
}