| /******************************************************************************* |
| * 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) { |
| } |
| } |