| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.help.ui.internal.views; |
| |
| import java.net.URL; |
| import java.util.ArrayList; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Platform; |
| 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.search.ISearchEngineResult; |
| import org.eclipse.help.search.ISearchEngineResult2; |
| import org.eclipse.help.ui.internal.HelpUIPlugin; |
| 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.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.BusyIndicator; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.ImageData; |
| 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.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.forms.IFormColors; |
| import org.eclipse.ui.forms.events.ExpansionAdapter; |
| import org.eclipse.ui.forms.events.ExpansionEvent; |
| import org.eclipse.ui.forms.events.HyperlinkAdapter; |
| 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.ImageHyperlink; |
| import org.eclipse.ui.forms.widgets.Section; |
| import org.eclipse.ui.forms.widgets.TableWrapData; |
| import org.eclipse.ui.forms.widgets.TableWrapLayout; |
| import org.osgi.framework.Bundle; |
| |
| public class EngineResultSection { |
| |
| private static final String KEY_PREFIX_GRAYED = "grayed:"; //$NON-NLS-1$ |
| |
| private static final String CAT_HEADING_PREFIX = "catheading:"; //$NON-NLS-1$ |
| |
| private SearchResultsPart part; |
| |
| private EngineDescriptor desc; |
| |
| private IStatus errorStatus; |
| |
| private ArrayList<ISearchEngineResult> hits; |
| |
| private Section section; |
| |
| private Composite container; |
| |
| private FormText searchResults; |
| |
| private ImageHyperlink prevLink; |
| |
| private ImageHyperlink nextLink; |
| |
| private boolean needsUpdating; |
| |
| private FederatedSearchSorter sorter; |
| |
| private int HITS_PER_PAGE = 10; |
| |
| private static final String HREF_PROGRESS = "__progress__"; //$NON-NLS-1$ |
| |
| private static final String PROGRESS_VIEW = "org.eclipse.ui.views.ProgressView"; //$NON-NLS-1$ |
| |
| private int resultOffset = 0; |
| |
| public EngineResultSection(SearchResultsPart part, EngineDescriptor desc) { |
| this.part = part; |
| this.desc = desc; |
| hits = new ArrayList<>(); |
| sorter = new FederatedSearchSorter(); |
| } |
| |
| public boolean hasControl(Control control) { |
| return searchResults.equals(control); |
| } |
| |
| public boolean matches(EngineDescriptor desc) { |
| return this.desc == desc; |
| } |
| |
| public Control createControl(Composite parent, final FormToolkit toolkit) { |
| section = toolkit.createSection(parent, Section.SHORT_TITLE_BAR | Section.COMPACT | Section.TWISTIE |
| | Section.EXPANDED | Section.LEFT_TEXT_CLIENT_ALIGNMENT); |
| // section.marginHeight = 10; |
| container = toolkit.createComposite(section); |
| TableWrapLayout layout = new TableWrapLayout(); |
| layout.topMargin = 0; |
| layout.bottomMargin = 0; |
| layout.leftMargin = 0; |
| layout.rightMargin = 0; |
| layout.verticalSpacing = 0; |
| container.setLayout(layout); |
| createFormText(container, toolkit); |
| searchResults.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); |
| searchResults.setColor("summary", parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW)); //$NON-NLS-1$ |
| section.setClient(container); |
| updateSectionTitle(0); |
| section.addExpansionListener(new ExpansionAdapter() { |
| |
| @Override |
| public void expansionStateChanging(ExpansionEvent e) { |
| if (needsUpdating) |
| asyncUpdateResults(true, false); |
| } |
| }); |
| return section; |
| } |
| |
| private void createFormText(Composite parent, FormToolkit toolkit) { |
| searchResults = toolkit.createFormText(parent, false); |
| searchResults.setColor(IFormColors.TITLE, toolkit.getColors().getColor(IFormColors.TITLE)); |
| searchResults.marginHeight = 5; |
| String topicKey = IHelpUIConstants.IMAGE_FILE_F1TOPIC; |
| String searchKey = IHelpUIConstants.IMAGE_HELP_SEARCH; |
| searchResults.setImage(topicKey, HelpUIResources.getImage(topicKey)); |
| searchResults.setImage(searchKey, HelpUIResources.getImage(searchKey)); |
| searchResults.setColor("summary", parent.getDisplay().getSystemColor( //$NON-NLS-1$ |
| SWT.COLOR_WIDGET_DARK_SHADOW)); |
| searchResults.setImage(ISharedImages.IMG_TOOL_FORWARD, PlatformUI.getWorkbench().getSharedImages() |
| .getImage(ISharedImages.IMG_TOOL_FORWARD)); |
| searchResults.setImage(ISharedImages.IMG_TOOL_BACK, PlatformUI.getWorkbench().getSharedImages() |
| .getImage(ISharedImages.IMG_TOOL_BACK)); |
| searchResults.setImage(ISharedImages.IMG_OBJS_ERROR_TSK, PlatformUI.getWorkbench().getSharedImages() |
| .getImage(ISharedImages.IMG_OBJS_ERROR_TSK)); |
| searchResults.setImage(desc.getId(), desc.getIconImage()); |
| searchResults.setImage(KEY_PREFIX_GRAYED + desc.getId(), getGrayedImage(desc.getIconImage())); |
| searchResults.addHyperlinkListener(new IHyperlinkListener() { |
| |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| Object href = e.getHref(); |
| String shref = (String) href; |
| if (HREF_PROGRESS.equals(href)) { |
| showProgressView(); |
| } else if (shref.startsWith("bmk:")) { //$NON-NLS-1$ |
| doBookmark(e.getLabel(), shref); |
| } else if (shref.startsWith(CAT_HEADING_PREFIX)) { |
| part.doCategoryLink(shref.substring(CAT_HEADING_PREFIX.length())); |
| } else |
| part.doOpenLink(e.getHref()); |
| } |
| |
| @Override |
| public void linkEntered(HyperlinkEvent e) { |
| part.parent.handleLinkEntered(e); |
| } |
| |
| @Override |
| public void linkExited(HyperlinkEvent e) { |
| part.parent.handleLinkExited(e); |
| } |
| }); |
| initializeText(); |
| part.parent.hookFormText(searchResults); |
| needsUpdating = true; |
| } |
| |
| private void initializeText() { |
| Bundle bundle = Platform.getBundle("org.eclipse.ui.views"); //$NON-NLS-1$ |
| if (bundle != null) { |
| StringBuilder buff = new StringBuilder(); |
| buff.append("<form>"); //$NON-NLS-1$ |
| buff.append("<p><a href=\""); //$NON-NLS-1$ |
| buff.append(HREF_PROGRESS); |
| buff.append("\""); //$NON-NLS-1$ |
| if (!Platform.getWS().equals(Platform.WS_GTK)) { |
| buff.append(" alt=\""); //$NON-NLS-1$ |
| buff.append(Messages.EngineResultSection_progressTooltip); |
| buff.append("\""); //$NON-NLS-1$ |
| } |
| buff.append(">"); //$NON-NLS-1$ |
| buff.append(Messages.EngineResultSection_searchInProgress); |
| buff.append("</a></p></form>"); //$NON-NLS-1$ |
| searchResults.setText(buff.toString(), true, false); |
| } else { |
| searchResults.setText(Messages.EngineResultSection_progress2, false, false); |
| } |
| } |
| |
| private void showProgressView() { |
| IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); |
| if (window != null) { |
| IWorkbenchPage page = window.getActivePage(); |
| if (page != null) { |
| try { |
| page.showView(PROGRESS_VIEW); |
| } catch (PartInitException e) { |
| HelpUIPlugin.logError(Messages.EngineResultSection_progressError, e); |
| } |
| } |
| } |
| } |
| |
| public synchronized void add(ISearchEngineResult match) { |
| hits.add(match); |
| asyncUpdateResults(false, false); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.help.internal.search.federated.ISearchEngineResultCollector#add(org.eclipse.help.internal.search.federated.ISearchEngineResult[]) |
| */ |
| public synchronized void add(ISearchEngineResult[] matches) { |
| for (int i = 0; i < matches.length; i++) |
| hits.add(matches[i]); |
| asyncUpdateResults(false, false); |
| } |
| |
| public synchronized void error(IStatus status) { |
| errorStatus = status; |
| asyncUpdateResults(false, false); |
| } |
| |
| public synchronized void completed() { |
| if (hits.size() == 0 && !searchResults.isDisposed()) |
| asyncUpdateResults(false, false); |
| } |
| |
| public synchronized void canceling() { |
| if (hits.size() == 0 && !searchResults.isDisposed()) { |
| StringBuilder buff = new StringBuilder(); |
| buff.append("<form>"); //$NON-NLS-1$ |
| buff.append("<p><span color=\"summary\">");//$NON-NLS-1$ |
| buff.append(Messages.EngineResultSection_canceling); |
| buff.append("</span></p>"); //$NON-NLS-1$ |
| buff.append("</form>"); //$NON-NLS-1$ |
| searchResults.setText(buff.toString(), true, false); |
| } |
| } |
| |
| private void asyncUpdateResults(boolean now, final boolean scrollToBeginning) { |
| Runnable runnable = () -> BusyIndicator.showWhile(PlatformUI.getWorkbench().getDisplay(), () -> { |
| if (section.isDisposed()) { |
| return; |
| } |
| updateResults(true); |
| if (scrollToBeginning) { |
| searchResults.setFocus(); |
| FormToolkit.setControlVisible(section, true); |
| part.updateSeparatorVisibility(); |
| } |
| }); |
| if (section.isDisposed()) { |
| return; |
| } |
| if (now) { |
| PlatformUI.getWorkbench().getDisplay().syncExec(runnable); |
| } else { |
| PlatformUI.getWorkbench().getDisplay().asyncExec(runnable); |
| } |
| } |
| |
| private ISearchEngineResult[] getResults() { |
| ArrayList<ISearchEngineResult> list = hits; |
| if (desc.getEngineTypeId().equals(IHelpUIConstants.INTERNAL_HELP_ID)) { |
| if (part.parent.isFilteredByRoles()) { |
| list = new ArrayList<>(); |
| for (int i = 0; i < hits.size(); i++) { |
| ISearchEngineResult hit = hits.get(i); |
| if (HelpBasePlugin.getActivitySupport().isEnabled(hit.getHref())) |
| list.add(hit); |
| } |
| } |
| } |
| ISearchEngineResult[] results = list.toArray(new ISearchEngineResult[list.size()]); |
| if (part.getShowCategories()) |
| sorter.sort(null, results); |
| return results; |
| } |
| |
| /** |
| * Returns a copy of the given image but grayed and half transparent. |
| * This gives the icon a grayed/disabled look. |
| * |
| * @param image the image to gray |
| * @return the grayed image |
| */ |
| private Image getGrayedImage(Image image) { |
| // first gray the image |
| Image temp = new Image(image.getDevice(), image, SWT.IMAGE_GRAY); |
| // then add alpha to blend it 50/50 with the background |
| ImageData data = temp.getImageData(); |
| ImageData maskData = data.getTransparencyMask(); |
| if (maskData != null) { |
| for (int y=0;y<maskData.height;++y) { |
| for (int x=0;x<maskData.width;++x) { |
| if (maskData.getPixel(x, y) == 0) { |
| // masked; set to transparent |
| data.setAlpha(x, y, 0); |
| } |
| else { |
| // not masked; set to translucent |
| data.setAlpha(x, y, 128); |
| } |
| } |
| } |
| data.maskData = null; |
| } |
| Image grayed = new Image(image.getDevice(), data); |
| temp.dispose(); |
| return grayed; |
| } |
| |
| void updateResults(boolean reflow) { |
| ISearchEngineResult[] results = getResults(); |
| updateSectionTitle(results.length); |
| StringBuilder buff = new StringBuilder(); |
| buff.append("<form>"); //$NON-NLS-1$ |
| IHelpResource oldCat = null; |
| |
| for (int i = resultOffset; i < results.length; i++) { |
| if (i - resultOffset == HITS_PER_PAGE) { |
| break; |
| } |
| ISearchEngineResult hit = results[i]; |
| IHelpResource cat = hit.getCategory(); |
| if (part.getShowCategories() && cat != null |
| && (oldCat == null || !oldCat.getLabel().equals(cat.getLabel()))) { |
| buff.append("<p>"); //$NON-NLS-1$ |
| if (cat.getHref() != null) { |
| buff.append("<a bold=\"true\" href=\""); //$NON-NLS-1$ |
| String absoluteHref = ""; //$NON-NLS-1$ |
| if (cat.getHref().endsWith(".xml")) { //$NON-NLS-1$ |
| absoluteHref = absoluteHref + CAT_HEADING_PREFIX; |
| } |
| absoluteHref = absoluteHref + hit.toAbsoluteHref(cat.getHref(), true); |
| buff.append(EscapeUtils.escapeSpecialChars(absoluteHref)); |
| buff.append("\">"); //$NON-NLS-1$ |
| buff.append(cat.getLabel()); |
| buff.append("</a>"); //$NON-NLS-1$ |
| } else { |
| buff.append("<b>"); //$NON-NLS-1$ |
| buff.append(cat.getLabel()); |
| buff.append("</b>"); //$NON-NLS-1$ |
| } |
| buff.append("</p>"); //$NON-NLS-1$ |
| oldCat = cat; |
| } |
| int indent = part.getShowCategories() && cat != null ? 26 : 21; |
| int bindent = part.getShowCategories() && cat != null ? 5 : 0; |
| buff.append("<li indent=\"" + indent + "\" bindent=\"" + bindent + "\" style=\"image\" value=\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| String imageId = desc.getId(); |
| boolean isPotentialHit = (hit instanceof SearchHit && ((SearchHit)hit).isPotentialHit()); |
| if (hit instanceof ISearchEngineResult2) { |
| URL iconURL = ((ISearchEngineResult2) hit).getIconURL(); |
| if (iconURL != null) { |
| String id = null; |
| if (isPotentialHit) { |
| id = registerGrayedHitIcon(iconURL); |
| } |
| else { |
| id = registerHitIcon(iconURL); |
| } |
| if (id != null) |
| imageId = id; |
| } |
| } |
| |
| if (isPotentialHit) { |
| imageId = KEY_PREFIX_GRAYED + imageId; |
| } |
| |
| buff.append(imageId); |
| buff.append("\">"); //$NON-NLS-1$ |
| buff.append("<a href=\""); //$NON-NLS-1$ |
| String href=null; |
| if (hit instanceof ISearchEngineResult2) { |
| ISearchEngineResult2 hit2 = (ISearchEngineResult2)hit; |
| if (((ISearchEngineResult2)hit).canOpen()) { |
| href = "open:"+desc.getId()+"?id="+hit2.getId(); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| if (href==null) { |
| if (hit.getForceExternalWindow()) |
| href = "nw:";//$NON-NLS-1$ |
| href = EscapeUtils.escapeSpecialChars(hit.toAbsoluteHref(hit.getHref(), false)); |
| } |
| buff.append(href); |
| buff.append("\""); //$NON-NLS-1$ |
| if (hit.getCategory() != null && Platform.getWS() != Platform.WS_GTK) { |
| buff.append(" alt=\""); //$NON-NLS-1$ |
| buff.append(hit.getCategory().getLabel()); |
| buff.append("\""); //$NON-NLS-1$ |
| } |
| buff.append(">"); //$NON-NLS-1$ |
| String elabel = null; |
| if (isPotentialHit) { |
| // add "(potential hit)" |
| elabel = Messages.bind(Messages.SearchPart_potential_hit, hit.getLabel()); |
| } |
| else { |
| elabel = hit.getLabel(); |
| } |
| |
| elabel = EscapeUtils.escapeSpecialChars(elabel); |
| buff.append(elabel); |
| buff.append("</a>"); //$NON-NLS-1$ |
| if (part.getShowDescription()) { |
| String edesc = hit.getDescription(); |
| if (edesc != null) { |
| edesc = EscapeUtils.escapeSpecialChars(edesc); |
| buff.append("<br/>"); //$NON-NLS-1$ |
| buff.append(edesc); |
| } |
| } |
| buff.append("</li>"); //$NON-NLS-1$ |
| } |
| if (errorStatus != null) |
| updateErrorStatus(buff); |
| updateNavigation(results.length); |
| buff.append("</form>"); //$NON-NLS-1$ |
| searchResults.setText(buff.toString(), true, false); |
| section.layout(); |
| if (reflow) |
| part.reflow(); |
| } |
| |
| /** |
| * Registers the given icon URL for use with this section. Icons |
| * must be registered before use and referenced by the returned |
| * ID. |
| * |
| * @param iconURL the URL to the icon |
| * @return the ID to use for referencing the icon |
| */ |
| private String registerHitIcon(URL iconURL) { |
| Image image = HelpUIResources.getImage(iconURL); |
| if (image != null) { |
| searchResults.setImage(iconURL.toString(), image); |
| return iconURL.toString(); |
| } |
| return null; |
| } |
| |
| /** |
| * Same as registerHitIcon() but to register a grayed icon. You |
| * can provide the same URL for both the regular and grayed icons, |
| * but two different IDs will be returned. |
| * |
| * @param iconURL the URL to the icon |
| * @return the ID to use for referencing the icon |
| */ |
| private String registerGrayedHitIcon(URL iconURL) { |
| Image image = HelpUIResources.getImage(iconURL); |
| if (image != null) { |
| searchResults.setImage(iconURL.toString(), image); |
| return KEY_PREFIX_GRAYED + iconURL.toString(); |
| } |
| return null; |
| } |
| |
| private void updateErrorStatus(StringBuilder buff) { |
| int indent = 21; |
| buff.append("<li indent=\"" + indent + "\" style=\"image\" value=\""); //$NON-NLS-1$ //$NON-NLS-2$ |
| buff.append(ISharedImages.IMG_OBJS_ERROR_TSK); |
| buff.append("\">"); //$NON-NLS-1$ |
| buff.append("<b>"); //$NON-NLS-1$ |
| buff.append(EscapeUtils.escapeSpecialChars(errorStatus.getMessage())); |
| buff.append("</b>"); //$NON-NLS-1$ |
| buff.append("<br/>"); //$NON-NLS-1$ |
| Throwable t = errorStatus.getException(); |
| if (t != null && t.getMessage() != null) |
| buff.append(EscapeUtils.escapeSpecialChars(t.getMessage())); |
| buff.append("</li>"); //$NON-NLS-1$ |
| } |
| |
| private void updateNavigation(int size) { |
| if (size > HITS_PER_PAGE) { |
| if (prevLink == null) { |
| FormToolkit toolkit = part.getToolkit(); |
| Composite navContainer = toolkit.createComposite(container); |
| TableWrapData td = new TableWrapData(TableWrapData.FILL_GRAB); |
| navContainer.setLayoutData(td); |
| GridLayout glayout = new GridLayout(); |
| glayout.numColumns = 2; |
| navContainer.setLayout(glayout); |
| GridData gd; |
| /* |
| * Label sep = toolkit.createLabel(navContainer, null, SWT.SEPARATOR | |
| * SWT.HORIZONTAL); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); |
| * gd.horizontalSpan = 2; gd.widthHint = 2; sep.setLayoutData(gd); |
| */ |
| prevLink = toolkit.createImageHyperlink(navContainer, SWT.NULL); |
| |
| prevLink.setText(NLS.bind(Messages.EngineResultSection_previous, "" + HITS_PER_PAGE)); //$NON-NLS-1$ |
| prevLink.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( |
| ISharedImages.IMG_TOOL_BACK)); |
| prevLink.addHyperlinkListener(new HyperlinkAdapter() { |
| |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| resultOffset -= HITS_PER_PAGE; |
| asyncUpdateResults(false, true); |
| } |
| }); |
| nextLink = toolkit.createImageHyperlink(navContainer, SWT.RIGHT); |
| |
| nextLink.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( |
| ISharedImages.IMG_TOOL_FORWARD)); |
| gd = new GridData(GridData.HORIZONTAL_ALIGN_END); |
| gd.grabExcessHorizontalSpace = true; |
| nextLink.setLayoutData(gd); |
| nextLink.addHyperlinkListener(new HyperlinkAdapter() { |
| |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| resultOffset += HITS_PER_PAGE; |
| asyncUpdateResults(false, true); |
| } |
| }); |
| } |
| prevLink.setVisible(resultOffset > 0); |
| |
| int nextOffset = resultOffset + HITS_PER_PAGE; |
| int remainder = hits.size() - nextOffset; |
| remainder = Math.min(remainder, HITS_PER_PAGE); |
| |
| nextLink.setText(NLS.bind(Messages.EngineResultSection_next, "" + remainder)); //$NON-NLS-1$ |
| nextLink.setVisible(hits.size() > resultOffset + HITS_PER_PAGE); |
| } else { |
| if (prevLink != null) { |
| prevLink.getParent().setMenu(null); |
| prevLink.getParent().dispose(); |
| prevLink = null; |
| nextLink = null; |
| } |
| } |
| } |
| |
| private void updateSectionTitle(int size) { |
| if (errorStatus != null) { |
| Label label = part.getToolkit().createLabel(section, null); |
| label.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( |
| ISharedImages.IMG_OBJS_ERROR_TSK)); |
| section.setTextClient(label); |
| section.setText(Messages.EngineResultSection_sectionTitle_error); |
| } else { |
| section.setTextClient(null); |
| } |
| if (size == 1) |
| section.setText(NLS.bind(Messages.EngineResultSection_sectionTitle_hit, desc.getLabel(), "" //$NON-NLS-1$ |
| + hits.size())); |
| else if (size <= HITS_PER_PAGE) |
| section.setText(NLS.bind(Messages.EngineResultSection_sectionTitle_hits, desc.getLabel(), |
| "" + hits.size())); //$NON-NLS-1$ |
| else { |
| int from = (resultOffset + 1); |
| int to = (resultOffset + HITS_PER_PAGE); |
| to = Math.min(to, size); |
| section.setText(NLS.bind(Messages.EngineResultSection_sectionTitle_hitsRange, new String[] { |
| desc.getLabel(), "" + from, "" + to, "" + size })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| } |
| |
| private void doBookmark(final String label, String href) { |
| final String fhref = href.substring(4); |
| BusyIndicator.showWhile(container.getDisplay(), |
| () -> BaseHelpSystem.getBookmarkManager().addBookmark(fhref, label)); |
| } |
| |
| public void dispose() { |
| part.parent.unhookFormText(searchResults); |
| if (!section.isDisposed()) { |
| recursiveSetMenu(section, null); |
| section.dispose(); |
| } |
| } |
| |
| private void recursiveSetMenu(Control control, Menu menu) { |
| control.setMenu(menu); |
| if (control instanceof Composite) { |
| Composite parent = (Composite) control; |
| Control[] children = parent.getChildren(); |
| for (int i = 0; i < children.length; i++) { |
| recursiveSetMenu(children[i], menu); |
| } |
| } |
| } |
| } |