/**
* Copyright (c) 2010, 2014 Darmstadt University of Technology.
* 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.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marcel Bruch - initial API and implementation.
* Olav Lenz - change list viewer to tree viewer
*/
package org.eclipse.recommenders.internal.snipmatch.rcp;
import static com.google.common.base.Optional.*;
import static java.text.MessageFormat.format;
import static org.eclipse.recommenders.internal.snipmatch.rcp.SnipmatchRcpModule.REPOSITORY_CONFIGURATION_FILE;
import static org.eclipse.recommenders.rcp.SharedImages.Images.*;
import static org.eclipse.recommenders.utils.Checks.*;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.inject.Inject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.databinding.viewers.IViewerObservableList;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ILazyTreeContentProvider;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.recommenders.internal.snipmatch.rcp.Repositories.SnippetRepositoryConfigurationChangedEvent;
import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.LogMessages;
import org.eclipse.recommenders.internal.snipmatch.rcp.l10n.Messages;
import org.eclipse.recommenders.rcp.IRcpService;
import org.eclipse.recommenders.rcp.SharedImages;
import org.eclipse.recommenders.rcp.SharedImages.ImageResource;
import org.eclipse.recommenders.rcp.SharedImages.Images;
import org.eclipse.recommenders.rcp.utils.Jobs;
import org.eclipse.recommenders.snipmatch.ISnippet;
import org.eclipse.recommenders.snipmatch.ISnippetRepository;
import org.eclipse.recommenders.snipmatch.SearchContext;
import org.eclipse.recommenders.snipmatch.Snippet;
import org.eclipse.recommenders.snipmatch.model.SnippetRepositoryConfiguration;
import org.eclipse.recommenders.snipmatch.rcp.ISnippetRepositoryWizard;
import org.eclipse.recommenders.snipmatch.rcp.SnippetEditor;
import org.eclipse.recommenders.snipmatch.rcp.SnippetEditorInput;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryClosedEvent;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryContentChangedEvent;
import org.eclipse.recommenders.snipmatch.rcp.SnippetRepositoryOpenedEvent;
import org.eclipse.recommenders.snipmatch.rcp.model.SnippetRepositoryConfigurations;
import org.eclipse.recommenders.utils.Logs;
import org.eclipse.recommenders.utils.Nonnull;
import org.eclipse.recommenders.utils.Nullable;
import org.eclipse.recommenders.utils.Recommendation;
import org.eclipse.swt.SWT;
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.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.UIJob;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.inject.name.Named;
public class SnippetsView extends ViewPart implements IRcpService {
public static final String SEARCH_FIELD = "org.eclipse.recommenders.snipmatch.rcp.snippetsview.searchfield"; //$NON-NLS-1$
public static final String TREE = "org.eclipse.recommenders.snipmatch.rcp.snippetsview.tree"; //$NON-NLS-1$
public static final String SWT_ID = "org.eclipse.swtbot.widget.key"; //$NON-NLS-1$
private Text txtSearch;
private TreeViewer treeViewer;
private Tree tree;
private IViewerObservableList selection;
private SharedImages images;
private final Repositories repos;
private final SnippetRepositoryConfigurations configs;
private final File repositoryConfigurationFile;
private final EventBus bus;
private final SnipmatchRcpPreferences prefs;
private Action addRepositoryAction;
private Action removeRepositoryAction;
private Action editRepositoryAction;
private Action enableRepositoryAction;
private Action disableRepositoryAction;
private Action refreshAction;
private Action addSnippetAction;
private Action removeSnippetAction;
private Action editSnippetAction;
private Action shareSnippetAction;
private volatile boolean initializeTableData = true;
private volatile List<SnippetRepositoryConfiguration> availableRepositories = Lists.newArrayList();
private volatile ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> snippetsGroupedByRepoName = LinkedListMultimap
.create();
private volatile ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> filteredSnippetsGroupedByRepoName = LinkedListMultimap
.create();
private final Function<KnownSnippet, String> toStringRepresentation = new Function<KnownSnippet, String>() {
@Override
public String apply(KnownSnippet input) {
return SnippetProposals.createDisplayString(input.snippet);
}
};
@Inject
public SnippetsView(Repositories repos, SharedImages images, SnippetRepositoryConfigurations configs, EventBus bus,
@Named(REPOSITORY_CONFIGURATION_FILE) File repositoryConfigurationFile, SnipmatchRcpPreferences prefs) {
this.repos = repos;
this.configs = configs;
this.images = images;
this.bus = bus;
this.repositoryConfigurationFile = repositoryConfigurationFile;
this.prefs = prefs;
}
@Override
public void createPartControl(final Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridLayoutFactory.swtDefaults().applyTo(composite);
txtSearch = new Text(composite, SWT.BORDER | SWT.ICON_SEARCH | SWT.SEARCH | SWT.CANCEL);
txtSearch.setMessage(Messages.SEARCH_PLACEHOLDER_SEARCH_TEXT);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(1, 1).applyTo(txtSearch);
txtSearch.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
refreshUI();
}
});
txtSearch.setData(SWT_ID, SEARCH_FIELD);
txtSearch.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ARROW_DOWN && tree.getItemCount() != 0) {
tree.setFocus();
tree.setSelection(tree.getTopItem());
}
}
});
Composite treeComposite = new Composite(composite, SWT.NONE);
TreeColumnLayout treeLayout = new TreeColumnLayout();
treeComposite.setLayout(treeLayout);
treeComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
treeViewer = new TreeViewer(treeComposite, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.VIRTUAL);
ColumnViewerToolTipSupport.enableFor(treeViewer);
tree = treeViewer.getTree();
tree.setData(SWT_ID, TREE);
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
TreeViewerColumn snippetViewerColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
TreeColumn snippetColumn = snippetViewerColumn.getColumn();
treeLayout.setColumnData(snippetColumn, new ColumnWeightData(1, ColumnWeightData.MINIMUM_WIDTH, true));
snippetColumn.setText(Messages.TABLE_COLUMN_TITLE_SNIPPETS);
snippetViewerColumn.setLabelProvider(new StyledCellLabelProvider() {
@Override
public void update(ViewerCell cell) {
Object element = cell.getElement();
StyledString text = new StyledString();
if (element instanceof SnippetRepositoryConfiguration) {
SnippetRepositoryConfiguration config = (SnippetRepositoryConfiguration) element;
text.append(config.getName());
text.append(" "); //$NON-NLS-1$
if (prefs.isRepositoryEnabled(config)) {
text.append(format(Messages.TABLE_CELL_SUFFIX_SNIPPETS, fetchNumberOfSnippets(config)),
StyledString.COUNTER_STYLER);
} else {
text.append(Messages.TABLE_REPOSITORY_DISABLED, StyledString.COUNTER_STYLER);
}
cell.setImage(images.getImage(Images.OBJ_REPOSITORY));
} else if (element instanceof KnownSnippet) {
KnownSnippet knownSnippet = (KnownSnippet) element;
text.append(toStringRepresentation.apply(knownSnippet));
}
cell.setText(text.toString());
cell.setStyleRanges(text.getStyleRanges());
super.update(cell);
}
});
treeViewer.addOpenListener(new IOpenListener() {
@Override
public void open(OpenEvent event) {
if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
editSnippets();
}
}
});
treeViewer.setUseHashlookup(true);
treeViewer.setContentProvider(new ILazyTreeContentProvider() {
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
@Override
public void dispose() {
}
@Override
public void updateElement(Object parent, int index) {
if (parent instanceof IViewSite) {
SnippetRepositoryConfiguration config = availableRepositories.get(index);
treeViewer.replace(parent, index, config);
treeViewer.setChildCount(config, getChildren(config).length);
} else if (parent instanceof SnippetRepositoryConfiguration) {
treeViewer.replace(parent, index, getChildren(parent)[index]);
}
}
private Object[] getChildren(Object element) {
if (element instanceof SnippetRepositoryConfiguration) {
return filteredSnippetsGroupedByRepoName.get((SnippetRepositoryConfiguration) element).toArray();
}
return new Object[0];
}
@Override
public void updateChildCount(Object element, int currentChildCount) {
int count = 0;
if (element instanceof IViewSite) {
count = availableRepositories.size();
}
if (availableRepositories.contains(element)) {
count = getChildren(element).length;
}
if (count != currentChildCount) {
treeViewer.setChildCount(element, count);
}
}
@Override
public Object getParent(Object element) {
if (element instanceof KnownSnippet) {
KnownSnippet knownSnippet = (KnownSnippet) element;
return knownSnippet.config;
}
return null;
}
});
selection = ViewersObservables.observeMultiSelection(treeViewer);
createActions(parent);
addToolBar(parent);
addContextMenu();
refreshUI();
}
private int fetchNumberOfSnippets(SnippetRepositoryConfiguration config) {
return snippetsGroupedByRepoName.get(config).size();
}
private void createActions(final Composite parent) {
addRepositoryAction = new Action() {
@Override
public void run() {
addRepo();
}
};
removeRepositoryAction = new Action() {
@Override
public void run() {
removeRepos();
}
};
editRepositoryAction = new Action() {
@Override
public void run() {
editRepos();
}
};
enableRepositoryAction = new Action() {
@Override
public void run() {
changeEnableStateRepos(true);
}
};
disableRepositoryAction = new Action() {
@Override
public void run() {
changeEnableStateRepos(false);
}
};
refreshAction = new Action() {
@Override
public void run() {
reload();
}
};
addSnippetAction = new Action() {
@Override
public void run() {
addSnippet();
}
};
removeSnippetAction = new Action() {
@Override
public void run() {
removeSnippets();
}
};
editSnippetAction = new Action() {
@Override
public void run() {
editSnippets();
}
};
shareSnippetAction = new Action() {
@Override
public void run() {
shareSnippets();
};
};
treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
updateActionsStatus();
}
});
updateActionsStatus();
}
private void updateActionsStatus() {
boolean removeRepoEnabled = false;
boolean editRepoEnabled = false;
boolean enableRepoEnabled = false;
boolean disableRepoEnabled = false;
boolean removeSnippetEnabled = false;
boolean editSnippetEnabled = false;
boolean shareSnippetEnabled = false;
if (selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class)) {
SnippetRepositoryConfiguration selectedConfig = (SnippetRepositoryConfiguration) selection.get(0);
editRepoEnabled = WizardDescriptors.isWizardAvailable(selectedConfig)
&& !selectionContainsDefaultConfigurations();
}
if (selectionConsistsOnlyElementsOf(SnippetRepositoryConfiguration.class)
&& !selectionContainsDefaultConfigurations()) {
removeRepoEnabled = true;
}
if (selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class)) {
SnippetRepositoryConfiguration config = (SnippetRepositoryConfiguration) selection.get(0);
boolean repositoryEnabled = prefs.isRepositoryEnabled(config);
enableRepoEnabled = !repositoryEnabled;
disableRepoEnabled = repositoryEnabled;
}
if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
removeSnippetEnabled = true;
editSnippetEnabled = true;
}
if (selectionIsShareable()) {
shareSnippetEnabled = true;
}
removeRepositoryAction.setEnabled(removeRepoEnabled);
editRepositoryAction.setEnabled(editRepoEnabled);
enableRepositoryAction.setEnabled(enableRepoEnabled);
disableRepositoryAction.setEnabled(disableRepoEnabled);
removeSnippetAction.setEnabled(removeSnippetEnabled);
editSnippetAction.setEnabled(editSnippetEnabled);
shareSnippetAction.setEnabled(shareSnippetEnabled);
}
private boolean selectionContainsDefaultConfigurations() {
List<SnippetRepositoryConfiguration> configs = castSelection();
for (SnippetRepositoryConfiguration config : configs) {
if (config.isDefaultConfiguration()) {
return true;
}
}
return false;
}
private void addRepo() {
List<WizardDescriptor> availableWizards = WizardDescriptors.loadAvailableWizards();
if (!availableWizards.isEmpty()) {
SnippetRepositoryTypeSelectionWizard newWizard = new SnippetRepositoryTypeSelectionWizard();
WizardDialog dialog = new WizardDialog(tree.getShell(), newWizard);
if (dialog.open() == Window.OK) {
SnippetRepositoryConfiguration newConfiguration = newWizard.getConfiguration();
newConfiguration.setId(UUID.randomUUID().toString());
configs.getRepos().add(newConfiguration);
RepositoryConfigurations.storeConfigurations(configs, repositoryConfigurationFile);
bus.post(new Repositories.SnippetRepositoryConfigurationChangedEvent());
refreshUI();
}
}
}
private void removeRepos() {
ensureIsTrue(selectionConsistsOnlyElementsOf(SnippetRepositoryConfiguration.class));
List<SnippetRepositoryConfiguration> configurations = castSelection();
for (SnippetRepositoryConfiguration config : configurations) {
MessageDialogWithToggle confirmDialog = MessageDialogWithToggle.openOkCancelConfirm(tree.getShell(),
Messages.CONFIRM_DIALOG_DELETE_REPOSITORY_TITLE,
format(Messages.CONFIRM_DIALOG_DELETE_REPOSITORY_MESSAGE, config.getName()),
Messages.CONFIRM_DIALOG_DELETE_REPOSITORY_TOGGLE_MESSAGE, true, null, null);
boolean confirmed = confirmDialog.getReturnCode() == Status.OK;
if (!confirmed) {
return;
}
boolean delete = confirmDialog.getToggleState();
if (delete) {
ISnippetRepository repo = repos.getRepository(config.getId()).orNull();
if (repo != null) {
repo.delete();
}
}
configs.getRepos().remove(config);
}
RepositoryConfigurations.storeConfigurations(configs, repositoryConfigurationFile);
bus.post(new Repositories.SnippetRepositoryConfigurationChangedEvent());
refreshUI();
}
private void editRepos() {
ensureIsTrue(selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class));
SnippetRepositoryConfiguration oldConfiguration = cast(selection.get(0));
List<WizardDescriptor> suitableWizardDescriptors = WizardDescriptors
.filterApplicableWizardDescriptors(WizardDescriptors.loadAvailableWizards(), oldConfiguration);
if (!suitableWizardDescriptors.isEmpty()) {
ISnippetRepositoryWizard wizard;
if (suitableWizardDescriptors.size() == 1) {
wizard = Iterables.getOnlyElement(suitableWizardDescriptors).getWizard();
wizard.setConfiguration(oldConfiguration);
} else {
wizard = new SnippetRepositoryTypeSelectionWizard(oldConfiguration);
}
WizardDialog dialog = new WizardDialog(tree.getShell(), wizard);
if (dialog.open() == Window.OK) {
List<SnippetRepositoryConfiguration> configurations = configs.getRepos();
configurations.add(configurations.indexOf(oldConfiguration), wizard.getConfiguration());
configurations.remove(oldConfiguration);
RepositoryConfigurations.storeConfigurations(configs, repositoryConfigurationFile);
bus.post(new SnippetRepositoryConfigurationChangedEvent());
refreshUI();
}
}
}
private void changeEnableStateRepos(boolean enabled) {
ensureIsTrue(selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class));
SnippetRepositoryConfiguration config = cast(selection.get(0));
prefs.setRepositoryEnabled(config, enabled);
updateActionsStatus();
}
private void reload() {
Job reconnectJob = new Job(Messages.JOB_NAME_RECONNECTING_SNIPPET_REPOSITORY) {
@Override
protected IStatus run(IProgressMonitor monitor) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
refreshAction.setEnabled(false);
}
});
try {
repos.reload();
} catch (Exception e) {
// Snipmatch's default repositories cannot throw an
// IOException here
Logs.log(LogMessages.ERROR_FAILED_TO_RELOAD_REPOSITORIES, e);
}
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
refreshAction.setEnabled(true);
}
});
return Status.OK_STATUS;
}
};
reconnectJob.schedule();
}
private void addSnippet(ISnippetRepository repo) {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
try {
ISnippet snippet = new Snippet();
final SnippetEditorInput input = new SnippetEditorInput(snippet, repo);
SnippetEditor editor = cast(
page.openEditor(input, "org.eclipse.recommenders.snipmatch.rcp.editors.snippet")); //$NON-NLS-1$
// mark the editor dirty when opening a newly created snippet
editor.markDirtyUponSnippetCreation();
} catch (Exception e) {
Throwables.propagate(e);
}
}
private void addSnippet() {
addSnippet(null);
}
private void removeSnippets() {
ensureIsTrue(selectionConsistsOnlyElementsOf(KnownSnippet.class));
boolean confirmed = MessageDialog.openConfirm(tree.getShell(), Messages.CONFIRM_DIALOG_DELETE_SNIPPET_TITLE,
Messages.CONFIRM_DIALOG_DELETE_SNIPPET_MESSAGE);
if (!confirmed) {
return;
}
List<KnownSnippet> selectedSnippets = castSelection();
for (KnownSnippet knownSnippet : selectedSnippets) {
try {
for (ISnippetRepository repo : repos.getRepositories()) {
repo.delete(knownSnippet.snippet.getUuid());
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
}
private void editSnippets() {
ensureIsTrue(selectionConsistsOnlyElementsOf(KnownSnippet.class));
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
List<KnownSnippet> selectedSnippets = castSelection();
for (KnownSnippet knownSnippet : selectedSnippets) {
try {
ISnippet snippet = knownSnippet.snippet;
ISnippetRepository repository = findRepoForOriginalSnippet(snippet);
final SnippetEditorInput input = new SnippetEditorInput(snippet, repository);
page.openEditor(input, "org.eclipse.recommenders.snipmatch.rcp.editors.snippet"); //$NON-NLS-1$
} catch (Exception e) {
Throwables.propagate(e);
}
}
}
private void shareSnippets() {
ensureIsTrue(selectionIsShareable());
List<KnownSnippet> selectedSnippets = castSelection();
Optional<ISnippetRepository> repository = repos.getRepository(selectedSnippets.get(0).config.getId());
if (!repository.isPresent()) {
return;
}
Collection<UUID> uuids = Collections2.transform(selectedSnippets, new Function<KnownSnippet, UUID>() {
@Override
public UUID apply(KnownSnippet input) {
return input.snippet.getUuid();
}
});
repository.get().share(uuids);
}
private void addToolBar(final Composite parent) {
IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_SNIPPET, ELCL_ADD_SNIPPET, toolBarManager, addSnippetAction);
addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_REPOSITORY, ELCL_ADD_REPOSITORY, toolBarManager,
addRepositoryAction);
toolBarManager.add(new Separator());
addAction(Messages.TOOLBAR_TOOLTIP_EXPAND_ALL, ELCL_EXPAND_ALL, toolBarManager, new Action() {
@Override
public void run() {
for (int i = 0; i < treeViewer.getTree().getItemCount(); i++) {
treeViewer.getTree().getItem(i).setExpanded(true);
}
}
});
addAction(Messages.TOOLBAR_TOOLTIP_COLLAPSE_ALL, ELCL_COLLAPSE_ALL, toolBarManager, new Action() {
@Override
public void run() {
for (int i = 0; i < treeViewer.getTree().getItemCount(); i++) {
treeViewer.getTree().getItem(i).setExpanded(false);
}
}
});
toolBarManager.add(new Separator());
addAction(Messages.SNIPPETS_VIEW_MENUITEM_REFRESH, ELCL_REFRESH, toolBarManager, refreshAction);
}
private void addContextMenu() {
final MenuManager menuManager = new MenuManager();
Menu contextMenu = menuManager.createContextMenu(tree);
menuManager.setRemoveAllWhenShown(true);
tree.setMenu(contextMenu);
menuManager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager manager) {
boolean addedAddSnippetToRepoAction = false;
SnippetRepositoryConfiguration guessedConfiguration = guessConfigurationForNewSnippet().orNull();
if (guessedConfiguration != null) {
final ISnippetRepository repo = repos.getRepository(guessedConfiguration.getId()).orNull();
if (repo != null) {
addAction(format(Messages.SNIPPETS_VIEW_MENUITEM_ADD_SNIPPET_TO_REPOSITORY,
guessedConfiguration.getName()), ELCL_ADD_SNIPPET, manager, new Action() {
@Override
public void run() {
addSnippet(repo);
}
});
addedAddSnippetToRepoAction = true;
}
}
if (!addedAddSnippetToRepoAction) {
addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_SNIPPET, ELCL_ADD_SNIPPET, manager, addSnippetAction);
}
if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
addAction(Messages.SNIPPETS_VIEW_MENUITEM_EDIT_SNIPPET, ELCL_EDIT_SNIPPET, manager,
editSnippetAction);
addAction(Messages.SNIPPETS_VIEW_MENUITEM_REMOVE_SNIPPET, ELCL_REMOVE_SNIPPET, manager,
removeSnippetAction);
addAction(Messages.SNIPPETS_VIEW_MENUITEM_SHARE_SNIPPET, ELCL_SHARE_SNIPPET, manager,
shareSnippetAction);
}
manager.add(new Separator());
addAction(Messages.SNIPPETS_VIEW_MENUITEM_ADD_REPOSITORY, ELCL_ADD_REPOSITORY, manager,
addRepositoryAction);
if (selectionConsistsOnlyElementsOf(SnippetRepositoryConfiguration.class)) {
addAction(Messages.SNIPPETS_VIEW_MENUITEM_EDIT_REPOSITORY, ELCL_EDIT_REPOSITORY, manager,
editRepositoryAction);
addAction(Messages.SNIPPETS_VIEW_MENUITEM_REMOVE_REPOSITORY, ELCL_REMOVE_REPOSITORY,
ELCL_REMOVE_REPOSITORY_DISABLED, manager, removeRepositoryAction);
if (disableRepositoryAction.isEnabled()) {
addAction(Messages.SNIPPETS_VIEW_MENUITEM_DISABLE_REPOSITORY, ELCL_DISABLE_REPOSITORY, manager,
disableRepositoryAction);
}
if (enableRepositoryAction.isEnabled()) {
addAction(Messages.SNIPPETS_VIEW_MENUITEM_ENABLE_REPOSITORY, ELCL_ENABLE_REPOSITORY, manager,
enableRepositoryAction);
}
}
}
});
}
protected Optional<SnippetRepositoryConfiguration> guessConfigurationForNewSnippet() {
if (selection.isEmpty()) {
return absent();
}
SnippetRepositoryConfiguration selectedConfiguration = null;
if (selectionContainsOnlyOneElementOf(SnippetRepositoryConfiguration.class)) {
selectedConfiguration = cast(selection.get(0));
} else if (selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
List<KnownSnippet> selectedSnippets = castSelection();
for (KnownSnippet snippet : selectedSnippets) {
if (selectedConfiguration == null) {
selectedConfiguration = snippet.config;
} else if (!selectedConfiguration.equals(snippet.config)) {
return absent();
}
}
}
return fromNullable(selectedConfiguration);
}
private <T> boolean selectionContainsOnlyOneElementOf(Class<T> aClass) {
return selection != null && selection.size() == 1 && aClass.isAssignableFrom(selection.get(0).getClass());
}
private <T> boolean selectionConsistsOnlyElementsOf(Class<T> aClass) {
if (selection == null || selection.isEmpty()) {
return false;
}
for (Object element : selection) {
if (!aClass.isAssignableFrom(element.getClass())) {
return false;
}
}
return true;
}
private boolean selectionIsShareable() {
if (selection == null || selection.isEmpty()) {
return false;
}
if (!selectionConsistsOnlyElementsOf(KnownSnippet.class)) {
return false;
}
Set<String> configIds = Sets.newHashSet();
for (Object element : selection) {
KnownSnippet snippet = (KnownSnippet) element;
String configId = snippet.config.getId();
configIds.add(configId);
if (configIds.size() > 1) {
return false;
}
Optional<ISnippetRepository> repository = repos.getRepository(configId);
if (!repository.isPresent()) {
return false;
}
if (!repository.get().isSharingSupported()) {
return false;
}
}
return true;
}
private <T> List<T> castSelection() {
List<T> result = Lists.newArrayList();
for (Object element : selection) {
T casted = cast(element);
result.add(casted);
}
return result;
}
private void addAction(String text, ImageResource imageResource, IContributionManager contributionManager,
IAction action) {
action.setImageDescriptor(images.getDescriptor(imageResource));
action.setText(text);
action.setToolTipText(text);
contributionManager.add(action);
}
private void addAction(String text, Images imageResource, Images imageResourceDisabled,
IContributionManager contributionManager, Action action) {
action.setDisabledImageDescriptor(images.getDescriptor(imageResourceDisabled));
addAction(text, imageResource, contributionManager, action);
}
@Subscribe
public void onEvent(SnippetRepositoryOpenedEvent e) throws IOException {
refreshUI();
}
@Subscribe
public void onEvent(SnippetRepositoryClosedEvent e) throws IOException {
refreshUI();
}
@Subscribe
public void onEvent(SnippetRepositoryContentChangedEvent e) throws IOException {
refreshUI();
}
private void refreshUI() {
if (!repos.isRunning()) {
return;
}
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
Job.getJobManager().cancel(SearchJob.SEARCH_JOB_FAMILY);
Job.getJobManager().cancel(RefreshTableJob.REFRESH_TABLE_JOB_FAMILY);
final String query = txtSearch.isDisposed() ? "" : txtSearch.getText(); //$NON-NLS-1$
Job searchSnippetsJob = new SearchJob(Messages.JOB_NAME_SEARCHING_SNIPPET_REPOSITORIES, query);
Job refreshTableJob = new RefreshTableJob(Messages.JOB_NAME_REFRESHING_SNIPPETS_VIEW);
Jobs.sequential(Messages.JOB_GROUP_UPDATING_SNIPPETS_VIEW, searchSnippetsJob, refreshTableJob);
}
});
}
@Nullable
private ISnippetRepository findRepoForOriginalSnippet(ISnippet snippet) {
for (ISnippetRepository repo : repos.getRepositories()) {
if (repo.hasSnippet(snippet.getUuid())) {
return repo;
}
}
return null;
}
@Override
public void setFocus() {
treeViewer.getControl().setFocus();
}
private boolean isImportSupported() {
for (ISnippetRepository repo : repos.getRepositories()) {
if (repo.isImportSupported()) {
return true;
}
}
return false;
}
private final class SearchJob extends Job {
public static final String SEARCH_JOB_FAMILY = "SEARCH_JOB_FAMILY"; //$NON-NLS-1$
private final String query;
private SearchJob(String name, String query) {
super(name);
this.query = query;
setSystem(true);
}
@Override
public IStatus run(@Nonnull IProgressMonitor monitor) {
SubMonitor progress = SubMonitor.convert(monitor, Messages.MONITOR_SEARCH_SNIPPETS, 3);
try {
snippetsGroupedByRepoName = searchSnippets("", progress.newChild(1)); //$NON-NLS-1$
filteredSnippetsGroupedByRepoName = searchSnippets(query, progress.newChild(1));
availableRepositories = configs.getRepos();
progress.worked(1);
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
return Status.OK_STATUS;
} finally {
monitor.done();
}
}
private ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> searchSnippets(String searchTerm,
IProgressMonitor monitor) {
try {
ListMultimap<SnippetRepositoryConfiguration, KnownSnippet> snippetsGroupedByRepositoryName = LinkedListMultimap
.create();
monitor.beginTask(Messages.MONITOR_SEARCH_SNIPPETS, configs.getRepos().size());
for (SnippetRepositoryConfiguration config : configs.getRepos()) {
if (monitor.isCanceled()) {
return snippetsGroupedByRepositoryName;
}
ISnippetRepository repo = repos.getRepository(config.getId()).orNull();
if (repo == null) {
continue;
}
Set<KnownSnippet> knownSnippets = Sets.newHashSet();
for (Recommendation<ISnippet> recommendation : repo.search(new SearchContext(searchTerm.trim()))) {
if (monitor.isCanceled()) {
return snippetsGroupedByRepositoryName;
}
knownSnippets.add(new KnownSnippet(config, recommendation.getProposal()));
}
List<KnownSnippet> sorted = Ordering.from(String.CASE_INSENSITIVE_ORDER)
.onResultOf(toStringRepresentation).sortedCopy(knownSnippets);
snippetsGroupedByRepositoryName.putAll(config, sorted);
monitor.worked(1);
}
return snippetsGroupedByRepositoryName;
} finally {
monitor.done();
}
}
@Override
public boolean belongsTo(Object family) {
return SEARCH_JOB_FAMILY.equals(family);
}
}
private final class RefreshTableJob extends UIJob {
public static final String REFRESH_TABLE_JOB_FAMILY = "REFRESH_TABLE_JOB_FAMILY"; //$NON-NLS-1$
private RefreshTableJob(String name) {
super(name);
setSystem(true);
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
try {
if (!treeViewer.getControl().isDisposed()) {
if (initializeTableData) {
treeViewer.setInput(getViewSite());
initializeTableData = false;
} else {
refreshTable(monitor);
}
}
addSnippetAction.setEnabled(isImportSupported());
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
} else {
return Status.OK_STATUS;
}
} finally {
monitor.done();
}
}
private void refreshTable(IProgressMonitor monitor) {
int numberOfVisibleElements = treeViewer.getTree().getSize().y / treeViewer.getTree().getItemHeight() + 1;
try {
monitor.beginTask(Messages.MONITOR_REFRESHING_TABLE, availableRepositories.size());
treeViewer.refresh();
int replacedElementsCount = 0;
for (int i = 0; i < availableRepositories.size(); i++) {
SnippetRepositoryConfiguration config = availableRepositories.get(i);
List<KnownSnippet> elements = filteredSnippetsGroupedByRepoName.get(config);
treeViewer.setChildCount(config, elements.size());
for (int j = 0; j < elements.size() && replacedElementsCount < numberOfVisibleElements; j++) {
if (monitor.isCanceled()) {
return;
}
treeViewer.replace(config, j, elements.get(j));
replacedElementsCount++;
}
monitor.worked(1);
}
} finally {
monitor.done();
}
}
@Override
public boolean belongsTo(Object family) {
return REFRESH_TABLE_JOB_FAMILY.equals(family);
}
}
public class KnownSnippet {
public ISnippet snippet;
public SnippetRepositoryConfiguration config;
public KnownSnippet(SnippetRepositoryConfiguration config, ISnippet snippet) {
this.config = config;
this.snippet = snippet;
}
}
}