blob: eec77dd4e9fa2feb2ff9fb8e5534a08f577a7cb2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.help.ui.internal.views;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.help.IContext;
import org.eclipse.help.IHelpResource;
import org.eclipse.help.internal.base.BaseHelpSystem;
import org.eclipse.help.internal.base.HelpBasePlugin;
import org.eclipse.help.internal.search.SearchHit;
import org.eclipse.help.internal.search.SearchQuery;
import org.eclipse.help.internal.search.SearchResults;
import org.eclipse.help.internal.search.federated.IndexerJob;
import org.eclipse.help.ui.internal.HelpUIResources;
import org.eclipse.help.ui.internal.IHelpUIConstants;
import org.eclipse.help.ui.internal.Messages;
import org.eclipse.help.ui.internal.util.EscapeUtils;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
public class DynamicHelpPart extends SectionPart implements IHelpPart {
private static final String CANCEL_HREF = "__cancel__"; //$NON-NLS-1$
private static final String MORE_HREF = "__more__"; //$NON-NLS-1$
private ReusableHelpPart parent;
private FormText searchResults;
private SorterByScore resultSorter;
private String id;
private String phrase;
private Job runningJob;
private IContext context;
private JobListener jobListener;
public static final int SHORT_COUNT = 8;
class JobListener extends JobChangeAdapter {
@Override
public void done(IJobChangeEvent event) {
if (event.getJob() == runningJob) {
runningJob = null;
}
}
}
/**
* @param parent
* @param toolkit
* @param style
*/
public DynamicHelpPart(Composite parent, FormToolkit toolkit) {
super(parent, toolkit, Section.EXPANDED | Section.TWISTIE
| Section.TITLE_BAR);
// configure section
Section section = getSection();
section.setText(Messages.SearchPart_title);
section.marginWidth = 5;
section.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
if (e.getState()) {
refilter();
}
}
});
// create 'clear' hyperlink on the section tool bar
//ImageHyperlink clearLink = new ImageHyperlink(section, SWT.NULL);
//toolkit.adapt(clearLink, true, true);
/*
clearLink.setToolTipText(HelpUIResources
.getString("SearchPart.clearResults")); //$NON-NLS-1$
clearLink.setImage(HelpUIResources
.getImage(IHelpUIConstants.IMAGE_CLEAR));
clearLink.setBackground(section.getTitleBarGradientBackground());
clearLink.addHyperlinkListener(new HyperlinkAdapter() {
public void linkActivated(HyperlinkEvent e) {
clearResults();
}
});
section.setTextClient(clearLink);
*/
resultSorter = new SorterByScore();
searchResults = toolkit.createFormText(section, false);
section.setClient(searchResults);
searchResults.setColor(IFormColors.TITLE, toolkit.getColors().getColor(
IFormColors.TITLE));
String topicKey = IHelpUIConstants.IMAGE_FILE_F1TOPIC;
String nwKey = IHelpUIConstants.IMAGE_NW;
String searchKey = IHelpUIConstants.IMAGE_HELP_SEARCH;
searchResults.setImage(topicKey, HelpUIResources.getImage(topicKey));
searchResults.setImage(nwKey, HelpUIResources.getImage(nwKey));
searchResults.setImage(searchKey, HelpUIResources.getImage(searchKey));
searchResults.addHyperlinkListener(new IHyperlinkListener() {
@Override
public void linkActivated(HyperlinkEvent e) {
Object href = e.getHref();
if (href.equals(CANCEL_HREF)) {
if (runningJob != null) {
runningJob.cancel();
runningJob = null;
}
clearResults();
} else if (href.equals(MORE_HREF)) {
doMore();
} else
doOpenLink(e.getHref());
}
@Override
public void linkEntered(HyperlinkEvent e) {
DynamicHelpPart.this.parent.handleLinkEntered(e);
}
@Override
public void linkExited(HyperlinkEvent e) {
DynamicHelpPart.this.parent.handleLinkExited(e);
}
});
searchResults.setText("", false, false); //$NON-NLS-1$
jobListener = new JobListener();
Job.getJobManager().addJobChangeListener(jobListener);
}
@Override
public void dispose() {
Job.getJobManager().removeJobChangeListener(jobListener);
stop();
super.dispose();
}
@Override
public void setFocus() {
if (searchResults!=null)
searchResults.setFocus();
}
@Override
public void stop () {
if (runningJob!=null) {
runningJob.cancel();
runningJob=null;
}
}
@Override
public Control getControl() {
return getSection();
}
@Override
public void init(ReusableHelpPart parent, String id, IMemento memento) {
this.parent = parent;
this.id = id;
parent.hookFormText(searchResults);
}
@Override
public String getId() {
return id;
}
@Override
public void setVisible(boolean visible) {
getSection().setVisible(visible);
}
void clearResults() {
if (runningJob != null) {
runningJob.cancel();
runningJob = null;
}
searchResults.setText("", false, false); //$NON-NLS-1$
getManagedForm().reflow(true);
}
public void startSearch(String newPhrase, IContext excludeContext) {
if (phrase!=null && phrase.equals(newPhrase))
return;
this.phrase = newPhrase;
this.context = excludeContext;
if (getSection().isExpanded())
startInPlaceSearch(phrase, excludeContext);
}
private void startInPlaceSearch(final String phrase,
final IContext excludeContext) {
Job job = new Job(Messages.SearchPart_dynamicJob) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
try {
Job.getJobManager().join(IndexerJob.FAMILY,
monitor);
} catch (InterruptedException e) {
// TODO should we do someting here?
}
performSearch(phrase, excludeContext, monitor);
return Status.OK_STATUS;
} catch (OperationCanceledException e) {
// it is ok to cancel the search
return Status.OK_STATUS;
}
}
};
job.setSystem(true);
scheduleSearch(job);
}
private void performSearch(String phrase, IContext excludeContext,
IProgressMonitor monitor) {
SearchQuery searchQuery = new SearchQuery();
searchQuery.setSearchWord(phrase);
SearchResults localResults = new SearchResults(null,
DynamicHelpPart.SHORT_COUNT * 2, Platform.getNL()) {
@Override
public void addHits(List<SearchHit> hits, String highlightTerms) {
// don't highlight any terms for dynamic help part
super.addHits(hits, ""); //$NON-NLS-1$
}
};
BaseHelpSystem.getSearchManager().search(searchQuery, localResults,
monitor);
SearchHit[] hits = localResults.getSearchHits();
updateResults(phrase, excludeContext, new StringBuffer(), hits);
}
void scheduleSearch(Job job) {
if (runningJob != null) {
runningJob.cancel();
}
StringBuffer buff = new StringBuffer();
buff.append("<form>"); //$NON-NLS-1$
buff.append("<p><span color=\""); //$NON-NLS-1$
buff.append(IFormColors.TITLE);
buff.append("\">"); //$NON-NLS-1$
buff.append(Messages.SearchResultsPart_progress);
buff.append("</span>"); //$NON-NLS-1$
buff.append("<a href=\""); //$NON-NLS-1$
buff.append(CANCEL_HREF);
buff.append("\">"); //$NON-NLS-1$
buff.append(Messages.SearchResultsPart_cancel);
buff.append("</a></p>"); //$NON-NLS-1$
buff.append("</form>"); //$NON-NLS-1$
searchResults.setText(buff.toString(), true, false);
getManagedForm().reflow(true);
runningJob = job;
job.schedule();
}
private void updateResults(final String phrase,
final IContext excludeContext, final StringBuffer buffer,
final SearchHit[] hits) {
if (getSection().isDisposed())
return;
getSection().getDisplay().asyncExec(() -> doUpdateResults(phrase, excludeContext, buffer, hits));
}
private void doUpdateResults(String phrase, IContext excludeContext, StringBuffer buff, SearchHit[] hits) {
if (runningJob != null) {
runningJob.cancel();
}
this.phrase = phrase;
buff.delete(0, buff.length());
if (hits.length > 0) {
buff.append("<form>"); //$NON-NLS-1$
buff.append("<p><span color=\""); //$NON-NLS-1$
buff.append(IFormColors.TITLE);
buff.append("\">"); //$NON-NLS-1$
buff.append(NLS.bind(Messages.SearchResultsPart_label, phrase));
buff.append("</span></p>"); //$NON-NLS-1$
resultSorter.sort(null, hits);
IHelpResource [] excludedTopics = excludeContext!=null?excludeContext.getRelatedTopics():null;
for (int i = 0; i < hits.length; i++) {
SearchHit hit = hits[i];
if (hit.canOpen()) // Do not list Welcome/Cheatsheets etc.
continue;
if (isExcluded(hit.getHref(), excludedTopics))
continue;
if (i==SHORT_COUNT)
break;
buff.append("<li indent=\"21\" style=\"image\" value=\""); //$NON-NLS-1$
buff.append(IHelpUIConstants.IMAGE_FILE_F1TOPIC);
buff.append("\">"); //$NON-NLS-1$
buff.append("<a href=\""); //$NON-NLS-1$
String href = hit.getHref();
buff.append(href);
buff.append("\""); //$NON-NLS-1$
if (hit.getToc()!=null && !Platform.getWS().equals(Platform.WS_GTK)) {
buff.append(" alt=\""); //$NON-NLS-1$
buff.append(EscapeUtils.escapeSpecialChars(hit.getToc().getLabel()));
buff.append("\""); //$NON-NLS-1$
}
buff.append(">"); //$NON-NLS-1$
buff.append(EscapeUtils.escapeSpecialChars(hit.getLabel()));
buff.append("</a>"); //$NON-NLS-1$
buff.append("</li>"); //$NON-NLS-1$
}
if (hits.length > 0) {
buff.append("<p><img href=\""); //$NON-NLS-1$
buff.append(IHelpUIConstants.IMAGE_HELP_SEARCH);
buff.append("\"/>"); //$NON-NLS-1$
buff.append(" <a href=\""); //$NON-NLS-1$
buff.append(MORE_HREF);
buff.append("\">"); //$NON-NLS-1$
buff.append(Messages.SearchResultsPart_moreResults);
buff.append("</a></p>"); //$NON-NLS-1$
}
buff.append("</form>"); //$NON-NLS-1$
if (!searchResults.isDisposed())
searchResults.setText(buff.toString(), true, false);
} else
if (!searchResults.isDisposed())
searchResults.setText(NLS.bind(Messages.SearchResultsPart_noHits, phrase) , false, false);
if (!searchResults.isDisposed())
getManagedForm().reflow(true);
}
private boolean isExcluded(String href, IHelpResource [] excludedTopics) {
if (excludedTopics==null) return false;
for (int i=0; i<excludedTopics.length; i++) {
if (href.startsWith(excludedTopics[i].getHref()))
return true;
if (parent.isFilteredByRoles()) {
if (!HelpBasePlugin.getActivitySupport().isEnabled(href))
return true;
}
}
return false;
}
private void doMore() {
parent.startSearch(phrase);
}
private void doOpenLink(Object href) {
String url = (String) href;
if (url.startsWith("nw:")) { //$NON-NLS-1$
PlatformUI.getWorkbench().getHelpSystem().displayHelpResource(url.substring(3));
} else
parent.showURL(url);
}
@Override
public boolean fillContextMenu(IMenuManager manager) {
return parent.fillFormContextMenu(searchResults, manager);
}
@Override
public boolean hasFocusControl(Control control) {
return searchResults.equals(control);
}
@Override
public IAction getGlobalAction(String id) {
if (id.equals(ActionFactory.COPY.getId()))
return parent.getCopyAction();
return null;
}
@Override
public void toggleRoleFilter() {
refilter();
}
@Override
public void refilter() {
if (phrase!=null && phrase.length() > 0)
startInPlaceSearch(phrase, context);
}
@Override
public void saveState(IMemento memento) {
}
}