1 /******************************************************************************* 2 * Copyright (c) 2000, 2020 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.help.ui.internal.views; 15 16 import java.net.URL; 17 import java.util.ArrayList; 18 import java.util.Collections; 19 20 import org.eclipse.core.runtime.IStatus; 21 import org.eclipse.core.runtime.Platform; 22 import org.eclipse.help.IHelpResource; 23 import org.eclipse.help.internal.base.BaseHelpSystem; 24 import org.eclipse.help.internal.base.HelpBasePlugin; 25 import org.eclipse.help.internal.search.SearchHit; 26 import org.eclipse.help.search.ISearchEngineResult; 27 import org.eclipse.help.search.ISearchEngineResult2; 28 import org.eclipse.help.ui.internal.HelpUIResources; 29 import org.eclipse.help.ui.internal.IHelpUIConstants; 30 import org.eclipse.help.ui.internal.Messages; 31 import org.eclipse.help.ui.internal.util.EscapeUtils; 32 import org.eclipse.osgi.util.NLS; 33 import org.eclipse.swt.SWT; 34 import org.eclipse.swt.custom.BusyIndicator; 35 import org.eclipse.swt.graphics.Image; 36 import org.eclipse.swt.graphics.ImageData; 37 import org.eclipse.swt.layout.GridData; 38 import org.eclipse.swt.layout.GridLayout; 39 import org.eclipse.swt.widgets.Composite; 40 import org.eclipse.swt.widgets.Control; 41 import org.eclipse.swt.widgets.Label; 42 import org.eclipse.swt.widgets.Menu; 43 import org.eclipse.ui.ISharedImages; 44 import org.eclipse.ui.IWorkbenchPage; 45 import org.eclipse.ui.IWorkbenchWindow; 46 import org.eclipse.ui.PartInitException; 47 import org.eclipse.ui.PlatformUI; 48 import org.eclipse.ui.forms.IFormColors; 49 import org.eclipse.ui.forms.events.ExpansionAdapter; 50 import org.eclipse.ui.forms.events.ExpansionEvent; 51 import org.eclipse.ui.forms.events.HyperlinkAdapter; 52 import org.eclipse.ui.forms.events.HyperlinkEvent; 53 import org.eclipse.ui.forms.events.IHyperlinkListener; 54 import org.eclipse.ui.forms.widgets.FormText; 55 import org.eclipse.ui.forms.widgets.FormToolkit; 56 import org.eclipse.ui.forms.widgets.ImageHyperlink; 57 import org.eclipse.ui.forms.widgets.Section; 58 import org.eclipse.ui.forms.widgets.TableWrapData; 59 import org.eclipse.ui.forms.widgets.TableWrapLayout; 60 import org.osgi.framework.Bundle; 61 62 public class EngineResultSection { 63 64 private static final String KEY_PREFIX_GRAYED = "grayed:"; //$NON-NLS-1$ 65 66 private static final String CAT_HEADING_PREFIX = "catheading:"; //$NON-NLS-1$ 67 68 private SearchResultsPart part; 69 70 private EngineDescriptor desc; 71 72 private IStatus errorStatus; 73 74 private ArrayList<ISearchEngineResult> hits; 75 76 private Section section; 77 78 private Composite container; 79 80 private FormText searchResults; 81 82 private ImageHyperlink prevLink; 83 84 private ImageHyperlink nextLink; 85 86 private boolean needsUpdating; 87 88 private FederatedSearchSorter sorter; 89 90 private int HITS_PER_PAGE = 10; 91 92 private static final String HREF_PROGRESS = "__progress__"; //$NON-NLS-1$ 93 94 private static final String PROGRESS_VIEW = "org.eclipse.ui.views.ProgressView"; //$NON-NLS-1$ 95 96 private int resultOffset = 0; 97 EngineResultSection(SearchResultsPart part, EngineDescriptor desc)98 public EngineResultSection(SearchResultsPart part, EngineDescriptor desc) { 99 this.part = part; 100 this.desc = desc; 101 hits = new ArrayList<>(); 102 sorter = new FederatedSearchSorter(); 103 } 104 hasControl(Control control)105 public boolean hasControl(Control control) { 106 return searchResults.equals(control); 107 } 108 matches(EngineDescriptor desc)109 public boolean matches(EngineDescriptor desc) { 110 return this.desc == desc; 111 } 112 createControl(Composite parent, final FormToolkit toolkit)113 public Control createControl(Composite parent, final FormToolkit toolkit) { 114 section = toolkit.createSection(parent, Section.SHORT_TITLE_BAR | Section.COMPACT | Section.TWISTIE 115 | Section.EXPANDED | Section.LEFT_TEXT_CLIENT_ALIGNMENT); 116 // section.marginHeight = 10; 117 container = toolkit.createComposite(section); 118 TableWrapLayout layout = new TableWrapLayout(); 119 layout.topMargin = 0; 120 layout.bottomMargin = 0; 121 layout.leftMargin = 0; 122 layout.rightMargin = 0; 123 layout.verticalSpacing = 0; 124 container.setLayout(layout); 125 createFormText(container, toolkit); 126 searchResults.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); 127 searchResults.setColor("summary", parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW)); //$NON-NLS-1$ 128 section.setClient(container); 129 updateSectionTitle(0); 130 section.addExpansionListener(new ExpansionAdapter() { 131 132 @Override 133 public void expansionStateChanging(ExpansionEvent e) { 134 if (needsUpdating) 135 asyncUpdateResults(true, false); 136 } 137 }); 138 return section; 139 } 140 createFormText(Composite parent, FormToolkit toolkit)141 private void createFormText(Composite parent, FormToolkit toolkit) { 142 searchResults = toolkit.createFormText(parent, false); 143 searchResults.setColor(IFormColors.TITLE, toolkit.getColors().getColor(IFormColors.TITLE)); 144 searchResults.marginHeight = 5; 145 String topicKey = IHelpUIConstants.IMAGE_FILE_F1TOPIC; 146 String searchKey = IHelpUIConstants.IMAGE_HELP_SEARCH; 147 searchResults.setImage(topicKey, HelpUIResources.getImage(topicKey)); 148 searchResults.setImage(searchKey, HelpUIResources.getImage(searchKey)); 149 searchResults.setColor("summary", parent.getDisplay().getSystemColor( //$NON-NLS-1$ 150 SWT.COLOR_WIDGET_DARK_SHADOW)); 151 searchResults.setImage(ISharedImages.IMG_TOOL_FORWARD, PlatformUI.getWorkbench().getSharedImages() 152 .getImage(ISharedImages.IMG_TOOL_FORWARD)); 153 searchResults.setImage(ISharedImages.IMG_TOOL_BACK, PlatformUI.getWorkbench().getSharedImages() 154 .getImage(ISharedImages.IMG_TOOL_BACK)); 155 searchResults.setImage(ISharedImages.IMG_OBJS_ERROR_TSK, PlatformUI.getWorkbench().getSharedImages() 156 .getImage(ISharedImages.IMG_OBJS_ERROR_TSK)); 157 searchResults.setImage(desc.getId(), desc.getIconImage()); 158 searchResults.setImage(KEY_PREFIX_GRAYED + desc.getId(), getGrayedImage(desc.getIconImage())); 159 searchResults.addHyperlinkListener(new IHyperlinkListener() { 160 161 @Override 162 public void linkActivated(HyperlinkEvent e) { 163 Object href = e.getHref(); 164 String shref = (String) href; 165 if (HREF_PROGRESS.equals(href)) { 166 showProgressView(); 167 } else if (shref.startsWith("bmk:")) { //$NON-NLS-1$ 168 doBookmark(e.getLabel(), shref); 169 } else if (shref.startsWith(CAT_HEADING_PREFIX)) { 170 part.doCategoryLink(shref.substring(CAT_HEADING_PREFIX.length())); 171 } else 172 part.doOpenLink(e.getHref()); 173 } 174 175 @Override 176 public void linkEntered(HyperlinkEvent e) { 177 part.parent.handleLinkEntered(e); 178 } 179 180 @Override 181 public void linkExited(HyperlinkEvent e) { 182 part.parent.handleLinkExited(e); 183 } 184 }); 185 initializeText(); 186 part.parent.hookFormText(searchResults); 187 needsUpdating = true; 188 } 189 initializeText()190 private void initializeText() { 191 Bundle bundle = Platform.getBundle("org.eclipse.ui.views"); //$NON-NLS-1$ 192 if (bundle != null) { 193 StringBuilder buff = new StringBuilder(); 194 buff.append("<form>"); //$NON-NLS-1$ 195 buff.append("<p><a href=\""); //$NON-NLS-1$ 196 buff.append(HREF_PROGRESS); 197 buff.append("\""); //$NON-NLS-1$ 198 if (!Platform.getWS().equals(Platform.WS_GTK)) { 199 buff.append(" alt=\""); //$NON-NLS-1$ 200 buff.append(Messages.EngineResultSection_progressTooltip); 201 buff.append("\""); //$NON-NLS-1$ 202 } 203 buff.append(">"); //$NON-NLS-1$ 204 buff.append(Messages.EngineResultSection_searchInProgress); 205 buff.append("</a></p></form>"); //$NON-NLS-1$ 206 searchResults.setText(buff.toString(), true, false); 207 } else { 208 searchResults.setText(Messages.EngineResultSection_progress2, false, false); 209 } 210 } 211 showProgressView()212 private void showProgressView() { 213 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); 214 if (window != null) { 215 IWorkbenchPage page = window.getActivePage(); 216 if (page != null) { 217 try { 218 page.showView(PROGRESS_VIEW); 219 } catch (PartInitException e) { 220 Platform.getLog(getClass()).error(Messages.EngineResultSection_progressError, e); 221 } 222 } 223 } 224 } 225 add(ISearchEngineResult match)226 public synchronized void add(ISearchEngineResult match) { 227 hits.add(match); 228 asyncUpdateResults(false, false); 229 } 230 231 /* 232 * (non-Javadoc) 233 * 234 * @see org.eclipse.help.internal.search.federated.ISearchEngineResultCollector#add(org.eclipse.help.internal.search.federated.ISearchEngineResult[]) 235 */ add(ISearchEngineResult[] matches)236 public synchronized void add(ISearchEngineResult[] matches) { 237 Collections.addAll(hits, matches); 238 asyncUpdateResults(false, false); 239 } 240 error(IStatus status)241 public synchronized void error(IStatus status) { 242 errorStatus = status; 243 asyncUpdateResults(false, false); 244 } 245 completed()246 public synchronized void completed() { 247 if (hits.isEmpty() && !searchResults.isDisposed()) 248 asyncUpdateResults(false, false); 249 } 250 canceling()251 public synchronized void canceling() { 252 if (hits.isEmpty() && !searchResults.isDisposed()) { 253 StringBuilder buff = new StringBuilder(); 254 buff.append("<form>"); //$NON-NLS-1$ 255 buff.append("<p><span color=\"summary\">");//$NON-NLS-1$ 256 buff.append(Messages.EngineResultSection_canceling); 257 buff.append("</span></p>"); //$NON-NLS-1$ 258 buff.append("</form>"); //$NON-NLS-1$ 259 searchResults.setText(buff.toString(), true, false); 260 } 261 } 262 asyncUpdateResults(boolean now, final boolean scrollToBeginning)263 private void asyncUpdateResults(boolean now, final boolean scrollToBeginning) { 264 Runnable runnable = () -> BusyIndicator.showWhile(PlatformUI.getWorkbench().getDisplay(), () -> { 265 if (section.isDisposed()) { 266 return; 267 } 268 updateResults(true); 269 if (scrollToBeginning) { 270 searchResults.setFocus(); 271 FormToolkit.setControlVisible(section, true); 272 part.updateSeparatorVisibility(); 273 } 274 }); 275 if (section.isDisposed()) { 276 return; 277 } 278 if (now) { 279 PlatformUI.getWorkbench().getDisplay().syncExec(runnable); 280 } else { 281 PlatformUI.getWorkbench().getDisplay().asyncExec(runnable); 282 } 283 } 284 getResults()285 private ISearchEngineResult[] getResults() { 286 ArrayList<ISearchEngineResult> list = hits; 287 if (desc.getEngineTypeId().equals(IHelpUIConstants.INTERNAL_HELP_ID)) { 288 if (part.parent.isFilteredByRoles()) { 289 list = new ArrayList<>(); 290 for (int i = 0; i < hits.size(); i++) { 291 ISearchEngineResult hit = hits.get(i); 292 if (HelpBasePlugin.getActivitySupport().isEnabled(hit.getHref())) 293 list.add(hit); 294 } 295 } 296 } 297 ISearchEngineResult[] results = list.toArray(new ISearchEngineResult[list.size()]); 298 if (part.getShowCategories()) 299 sorter.sort(null, results); 300 return results; 301 } 302 303 /** 304 * Returns a copy of the given image but grayed and half transparent. 305 * This gives the icon a grayed/disabled look. 306 * 307 * @param image the image to gray 308 * @return the grayed image 309 */ getGrayedImage(Image image)310 private Image getGrayedImage(Image image) { 311 // first gray the image 312 Image temp = new Image(image.getDevice(), image, SWT.IMAGE_GRAY); 313 // then add alpha to blend it 50/50 with the background 314 ImageData data = temp.getImageData(); 315 ImageData maskData = data.getTransparencyMask(); 316 if (maskData != null) { 317 for (int y=0;y<maskData.height;++y) { 318 for (int x=0;x<maskData.width;++x) { 319 if (maskData.getPixel(x, y) == 0) { 320 // masked; set to transparent 321 data.setAlpha(x, y, 0); 322 } 323 else { 324 // not masked; set to translucent 325 data.setAlpha(x, y, 128); 326 } 327 } 328 } 329 data.maskData = null; 330 } 331 Image grayed = new Image(image.getDevice(), data); 332 temp.dispose(); 333 return grayed; 334 } 335 updateResults(boolean reflow)336 void updateResults(boolean reflow) { 337 ISearchEngineResult[] results = getResults(); 338 updateSectionTitle(results.length); 339 StringBuilder buff = new StringBuilder(); 340 buff.append("<form>"); //$NON-NLS-1$ 341 IHelpResource oldCat = null; 342 343 for (int i = resultOffset; i < results.length; i++) { 344 if (i - resultOffset == HITS_PER_PAGE) { 345 break; 346 } 347 ISearchEngineResult hit = results[i]; 348 IHelpResource cat = hit.getCategory(); 349 if (part.getShowCategories() && cat != null 350 && (oldCat == null || !oldCat.getLabel().equals(cat.getLabel()))) { 351 buff.append("<p>"); //$NON-NLS-1$ 352 if (cat.getHref() != null) { 353 buff.append("<a bold=\"true\" href=\""); //$NON-NLS-1$ 354 String absoluteHref = ""; //$NON-NLS-1$ 355 if (cat.getHref().endsWith(".xml")) { //$NON-NLS-1$ 356 absoluteHref = absoluteHref + CAT_HEADING_PREFIX; 357 } 358 absoluteHref = absoluteHref + hit.toAbsoluteHref(cat.getHref(), true); 359 buff.append(EscapeUtils.escapeSpecialChars(absoluteHref)); 360 buff.append("\">"); //$NON-NLS-1$ 361 buff.append(cat.getLabel()); 362 buff.append("</a>"); //$NON-NLS-1$ 363 } else { 364 buff.append("<b>"); //$NON-NLS-1$ 365 buff.append(cat.getLabel()); 366 buff.append("</b>"); //$NON-NLS-1$ 367 } 368 buff.append("</p>"); //$NON-NLS-1$ 369 oldCat = cat; 370 } 371 int indent = part.getShowCategories() && cat != null ? 26 : 21; 372 int bindent = part.getShowCategories() && cat != null ? 5 : 0; 373 buff.append("<li indent=\"" + indent + "\" bindent=\"" + bindent + "\" style=\"image\" value=\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 374 String imageId = desc.getId(); 375 boolean isPotentialHit = (hit instanceof SearchHit && ((SearchHit)hit).isPotentialHit()); 376 if (hit instanceof ISearchEngineResult2) { 377 URL iconURL = ((ISearchEngineResult2) hit).getIconURL(); 378 if (iconURL != null) { 379 String id = null; 380 if (isPotentialHit) { 381 id = registerGrayedHitIcon(iconURL); 382 } 383 else { 384 id = registerHitIcon(iconURL); 385 } 386 if (id != null) 387 imageId = id; 388 } 389 } 390 391 if (isPotentialHit) { 392 imageId = KEY_PREFIX_GRAYED + imageId; 393 } 394 395 buff.append(imageId); 396 buff.append("\">"); //$NON-NLS-1$ 397 buff.append("<a href=\""); //$NON-NLS-1$ 398 String href=null; 399 if (hit instanceof ISearchEngineResult2) { 400 ISearchEngineResult2 hit2 = (ISearchEngineResult2)hit; 401 if (((ISearchEngineResult2)hit).canOpen()) { 402 href = "open:"+desc.getId()+"?id="+hit2.getId(); //$NON-NLS-1$ //$NON-NLS-2$ 403 } 404 } 405 if (href==null) { 406 if (hit.getForceExternalWindow()) 407 href = "nw:";//$NON-NLS-1$ 408 href = EscapeUtils.escapeSpecialChars(hit.toAbsoluteHref(hit.getHref(), false)); 409 } 410 buff.append(href); 411 buff.append("\""); //$NON-NLS-1$ 412 if (hit.getCategory() != null && Platform.getWS() != Platform.WS_GTK) { 413 buff.append(" alt=\""); //$NON-NLS-1$ 414 buff.append(hit.getCategory().getLabel()); 415 buff.append("\""); //$NON-NLS-1$ 416 } 417 buff.append(">"); //$NON-NLS-1$ 418 String elabel = null; 419 if (isPotentialHit) { 420 // add "(potential hit)" 421 elabel = Messages.bind(Messages.SearchPart_potential_hit, hit.getLabel()); 422 } 423 else { 424 elabel = hit.getLabel(); 425 } 426 427 elabel = EscapeUtils.escapeSpecialChars(elabel); 428 buff.append(elabel); 429 buff.append("</a>"); //$NON-NLS-1$ 430 if (part.getShowDescription()) { 431 String edesc = hit.getDescription(); 432 if (edesc != null) { 433 edesc = EscapeUtils.escapeSpecialChars(edesc); 434 buff.append("<br/>"); //$NON-NLS-1$ 435 buff.append(edesc); 436 } 437 } 438 buff.append("</li>"); //$NON-NLS-1$ 439 } 440 if (errorStatus != null) 441 updateErrorStatus(buff); 442 updateNavigation(results.length); 443 buff.append("</form>"); //$NON-NLS-1$ 444 searchResults.setText(buff.toString(), true, false); 445 section.layout(); 446 if (reflow) 447 part.reflow(); 448 } 449 450 /** 451 * Registers the given icon URL for use with this section. Icons 452 * must be registered before use and referenced by the returned 453 * ID. 454 * 455 * @param iconURL the URL to the icon 456 * @return the ID to use for referencing the icon 457 */ registerHitIcon(URL iconURL)458 private String registerHitIcon(URL iconURL) { 459 Image image = HelpUIResources.getImage(iconURL); 460 if (image != null) { 461 searchResults.setImage(iconURL.toString(), image); 462 return iconURL.toString(); 463 } 464 return null; 465 } 466 467 /** 468 * Same as registerHitIcon() but to register a grayed icon. You 469 * can provide the same URL for both the regular and grayed icons, 470 * but two different IDs will be returned. 471 * 472 * @param iconURL the URL to the icon 473 * @return the ID to use for referencing the icon 474 */ registerGrayedHitIcon(URL iconURL)475 private String registerGrayedHitIcon(URL iconURL) { 476 Image image = HelpUIResources.getImage(iconURL); 477 if (image != null) { 478 searchResults.setImage(iconURL.toString(), image); 479 return KEY_PREFIX_GRAYED + iconURL.toString(); 480 } 481 return null; 482 } 483 updateErrorStatus(StringBuilder buff)484 private void updateErrorStatus(StringBuilder buff) { 485 int indent = 21; 486 buff.append("<li indent=\"" + indent + "\" style=\"image\" value=\""); //$NON-NLS-1$ //$NON-NLS-2$ 487 buff.append(ISharedImages.IMG_OBJS_ERROR_TSK); 488 buff.append("\">"); //$NON-NLS-1$ 489 buff.append("<b>"); //$NON-NLS-1$ 490 buff.append(EscapeUtils.escapeSpecialChars(errorStatus.getMessage())); 491 buff.append("</b>"); //$NON-NLS-1$ 492 buff.append("<br/>"); //$NON-NLS-1$ 493 Throwable t = errorStatus.getException(); 494 if (t != null && t.getMessage() != null) 495 buff.append(EscapeUtils.escapeSpecialChars(t.getMessage())); 496 buff.append("</li>"); //$NON-NLS-1$ 497 } 498 updateNavigation(int size)499 private void updateNavigation(int size) { 500 if (size > HITS_PER_PAGE) { 501 if (prevLink == null) { 502 FormToolkit toolkit = part.getToolkit(); 503 Composite navContainer = toolkit.createComposite(container); 504 TableWrapData td = new TableWrapData(TableWrapData.FILL_GRAB); 505 navContainer.setLayoutData(td); 506 GridLayout glayout = new GridLayout(); 507 glayout.numColumns = 2; 508 navContainer.setLayout(glayout); 509 GridData gd; 510 /* 511 * Label sep = toolkit.createLabel(navContainer, null, SWT.SEPARATOR | 512 * SWT.HORIZONTAL); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); 513 * gd.horizontalSpan = 2; gd.widthHint = 2; sep.setLayoutData(gd); 514 */ 515 prevLink = toolkit.createImageHyperlink(navContainer, SWT.NULL); 516 517 prevLink.setText(NLS.bind(Messages.EngineResultSection_previous, "" + HITS_PER_PAGE)); //$NON-NLS-1$ 518 prevLink.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( 519 ISharedImages.IMG_TOOL_BACK)); 520 prevLink.addHyperlinkListener(new HyperlinkAdapter() { 521 522 @Override 523 public void linkActivated(HyperlinkEvent e) { 524 resultOffset -= HITS_PER_PAGE; 525 asyncUpdateResults(false, true); 526 } 527 }); 528 nextLink = toolkit.createImageHyperlink(navContainer, SWT.RIGHT); 529 530 nextLink.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( 531 ISharedImages.IMG_TOOL_FORWARD)); 532 gd = new GridData(GridData.HORIZONTAL_ALIGN_END); 533 gd.grabExcessHorizontalSpace = true; 534 nextLink.setLayoutData(gd); 535 nextLink.addHyperlinkListener(new HyperlinkAdapter() { 536 537 @Override 538 public void linkActivated(HyperlinkEvent e) { 539 resultOffset += HITS_PER_PAGE; 540 asyncUpdateResults(false, true); 541 } 542 }); 543 } 544 prevLink.setVisible(resultOffset > 0); 545 546 int nextOffset = resultOffset + HITS_PER_PAGE; 547 int remainder = hits.size() - nextOffset; 548 remainder = Math.min(remainder, HITS_PER_PAGE); 549 550 nextLink.setText(NLS.bind(Messages.EngineResultSection_next, "" + remainder)); //$NON-NLS-1$ 551 nextLink.setVisible(hits.size() > resultOffset + HITS_PER_PAGE); 552 } else { 553 if (prevLink != null) { 554 prevLink.getParent().setMenu(null); 555 prevLink.getParent().dispose(); 556 prevLink = null; 557 nextLink = null; 558 } 559 } 560 } 561 updateSectionTitle(int size)562 private void updateSectionTitle(int size) { 563 if (errorStatus != null) { 564 Label label = part.getToolkit().createLabel(section, null); 565 label.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( 566 ISharedImages.IMG_OBJS_ERROR_TSK)); 567 section.setTextClient(label); 568 section.setText(Messages.EngineResultSection_sectionTitle_error); 569 } else { 570 section.setTextClient(null); 571 } 572 if (size == 1) 573 section.setText(NLS.bind(Messages.EngineResultSection_sectionTitle_hit, desc.getLabel(), "" //$NON-NLS-1$ 574 + hits.size())); 575 else if (size <= HITS_PER_PAGE) 576 section.setText(NLS.bind(Messages.EngineResultSection_sectionTitle_hits, desc.getLabel(), 577 "" + hits.size())); //$NON-NLS-1$ 578 else { 579 int from = (resultOffset + 1); 580 int to = (resultOffset + HITS_PER_PAGE); 581 to = Math.min(to, size); 582 section.setText(NLS.bind(Messages.EngineResultSection_sectionTitle_hitsRange, new String[] { 583 desc.getLabel(), "" + from, "" + to, "" + size })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 584 } 585 } 586 doBookmark(final String label, String href)587 private void doBookmark(final String label, String href) { 588 final String fhref = href.substring(4); 589 BusyIndicator.showWhile(container.getDisplay(), 590 () -> BaseHelpSystem.getBookmarkManager().addBookmark(fhref, label)); 591 } 592 dispose()593 public void dispose() { 594 part.parent.unhookFormText(searchResults); 595 if (!section.isDisposed()) { 596 recursiveSetMenu(section, null); 597 section.dispose(); 598 } 599 } 600 recursiveSetMenu(Control control, Menu menu)601 private void recursiveSetMenu(Control control, Menu menu) { 602 control.setMenu(menu); 603 if (control instanceof Composite) { 604 Composite parent = (Composite) control; 605 Control[] children = parent.getChildren(); 606 for (int i = 0; i < children.length; i++) { 607 recursiveSetMenu(children[i], menu); 608 } 609 } 610 } 611 } 612