1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 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.io.IOException;
17 import java.io.InputStream;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Observable;
21 import java.util.Observer;
22 
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
25 import org.eclipse.core.runtime.jobs.IJobChangeListener;
26 import org.eclipse.core.runtime.jobs.Job;
27 import org.eclipse.help.HelpSystem;
28 import org.eclipse.help.internal.base.BaseHelpSystem;
29 import org.eclipse.help.internal.search.federated.FederatedSearchEntry;
30 import org.eclipse.help.internal.search.federated.FederatedSearchJob;
31 import org.eclipse.help.internal.search.federated.LocalHelp;
32 import org.eclipse.help.search.ISearchEngineResult;
33 import org.eclipse.help.search.ISearchEngineResultCollector;
34 import org.eclipse.help.search.ISearchScope;
35 import org.eclipse.help.ui.internal.HelpUIResources;
36 import org.eclipse.help.ui.internal.IHelpUIConstants;
37 import org.eclipse.help.ui.internal.Messages;
38 import org.eclipse.jface.action.IAction;
39 import org.eclipse.jface.action.IMenuManager;
40 import org.eclipse.jface.preference.PreferenceDialog;
41 import org.eclipse.jface.preference.PreferenceManager;
42 import org.eclipse.osgi.util.NLS;
43 import org.eclipse.swt.SWT;
44 import org.eclipse.swt.events.KeyAdapter;
45 import org.eclipse.swt.events.KeyEvent;
46 import org.eclipse.swt.events.SelectionAdapter;
47 import org.eclipse.swt.events.SelectionEvent;
48 import org.eclipse.swt.widgets.Button;
49 import org.eclipse.swt.widgets.Composite;
50 import org.eclipse.swt.widgets.Control;
51 import org.eclipse.swt.widgets.Label;
52 import org.eclipse.swt.widgets.Menu;
53 import org.eclipse.swt.widgets.Shell;
54 import org.eclipse.ui.IMemento;
55 import org.eclipse.ui.actions.ActionFactory;
56 import org.eclipse.ui.forms.AbstractFormPart;
57 import org.eclipse.ui.forms.IFormColors;
58 import org.eclipse.ui.forms.events.HyperlinkAdapter;
59 import org.eclipse.ui.forms.events.HyperlinkEvent;
60 import org.eclipse.ui.forms.widgets.FormText;
61 import org.eclipse.ui.forms.widgets.FormToolkit;
62 import org.eclipse.ui.forms.widgets.Hyperlink;
63 import org.eclipse.ui.forms.widgets.Section;
64 import org.eclipse.ui.forms.widgets.TableWrapData;
65 import org.eclipse.ui.forms.widgets.TableWrapLayout;
66 
67 public class SearchPart extends AbstractFormPart implements IHelpPart, IHelpUIConstants {
68 
69 	public class SearchScopeObserver implements Observer {
70 
71 		@Override
update(Observable arg0, Object arg1)72 		public void update(Observable arg0, Object arg1) {
73 			ScopeSet set = scopeSetManager.getActiveSet();
74 			scopeSetLink.setText(set.getName());
75 			scopeSetManager.setActiveSet(set);
76 			scopeSection.layout();
77 			if (parent != null)
78 				parent.reflow();
79 		}
80 
81 	}
82 
83 	private ReusableHelpPart parent;
84 
85 	@SuppressWarnings("rawtypes")
86 	protected static java.util.List previousSearchQueryData = new java.util.ArrayList(20);
87 
88 	private static final String HREF_SEARCH_HELP = "/org.eclipse.platform.doc.user/tasks/help_search.htm"; //$NON-NLS-1$
89 
90 	private static boolean SEARCH_HELP_AVAILABLE = false;
91 
92 	static {
93 		InputStream is = HelpSystem.getHelpContent(HREF_SEARCH_HELP);
94 		if (is != null) {
95 			// don't leak the input stream
96 			try {
is.close()97 				is.close();
98 				SEARCH_HELP_AVAILABLE = true;
99 			} catch (IOException e) {
100 				// ignore
101 			}
102 		}
103 	}
104 
105 	private String id;
106 
107 	private Composite container;
108 
109 	private Composite filteringGroup;
110 	private FormText searchWordText;
111 
112 	private ComboPart searchWordCombo;
113 
114 	private Section scopeSection;
115 
116 	private Button goButton;
117 
118 	private Button shellDefaultButton;
119 
120 	private Hyperlink scopeSetLink;
121 
122 	private Hyperlink advancedLink;
123 
124 	private Observer engineObserver;
125 
126 	private ScopeSetManager scopeSetManager;
127 
128 	private static final int COMBO_HISTORY_SIZE = 10;
129 
130 	private JobListener jobListener;
131 
132 	private boolean searchPending;
133 
134 	private SearchScopeObserver scopeObserver;
135 
136 	private Section alternateQuerySection;
137 
138 	private FormToolkit toolkit;
139 
140 	private Composite alternateQueryComposite;
141 
142 	private class JobListener implements IJobChangeListener, Runnable {
143 
144 		private boolean searchInProgress = false;
145 
146 		@Override
aboutToRun(IJobChangeEvent event)147 		public void aboutToRun(IJobChangeEvent event) {
148 		}
149 
150 		@Override
awake(IJobChangeEvent event)151 		public void awake(IJobChangeEvent event) {
152 		}
153 
154 		@Override
done(IJobChangeEvent event)155 		public void done(IJobChangeEvent event) {
156 			if (event.getJob().belongsTo(FederatedSearchJob.FAMILY)) {
157 				Job[] searchJobs = Job.getJobManager().find(FederatedSearchJob.FAMILY);
158 				if (searchJobs.length == 0) {
159 					// search finished
160 					searchInProgress = false;
161 					if (container.isDisposed())
162 						return;
163 					container.getDisplay().asyncExec(this);
164 					SearchResultsPart results = (SearchResultsPart) parent
165 							.findPart(IHelpUIConstants.HV_FSEARCH_RESULT);
166 					results.completed();
167 				}
168 			}
169 		}
170 
171 		@Override
running(IJobChangeEvent event)172 		public void running(IJobChangeEvent event) {
173 		}
174 
175 		@Override
scheduled(IJobChangeEvent event)176 		public void scheduled(IJobChangeEvent event) {
177 			if (!searchInProgress && event.getJob().belongsTo(FederatedSearchJob.FAMILY)) {
178 				searchInProgress = true;
179 				container.getDisplay().asyncExec(this);
180 			}
181 		}
182 
183 		@Override
sleeping(IJobChangeEvent event)184 		public void sleeping(IJobChangeEvent event) {
185 		}
186 
187 		@Override
run()188 		public void run() {
189 			searchWordCombo.getControl().setEnabled(!searchInProgress);
190 			if (!searchInProgress)
191 				goButton.setEnabled(true);
192 			if (searchInProgress)
193 				goButton.setText(Messages.SearchPart_stop);
194 			else
195 				goButton.setText(Messages.SearchPart_go);
196 			parent.getForm().getForm().setBusy(searchInProgress);
197 			goButton.getParent().layout();
198 		}
199 	}
200 
201 	/**
202 	 * @param parent
203 	 * @param toolkit
204 	 * @param style
205 	 */
SearchPart(final Composite parent, FormToolkit toolkit)206 	public SearchPart(final Composite parent, FormToolkit toolkit) {
207 		this.toolkit = toolkit;
208 		container = toolkit.createComposite(parent);
209 		scopeSetManager = ScopeState.getInstance().getScopeSetManager();
210 		TableWrapLayout layout = new TableWrapLayout();
211 		layout.numColumns = 2;
212 		container.setLayout(layout);
213 		TableWrapData td;
214 		//createSearchExpressionDescription(parent, toolkit);
215 		createSearchExpressionSection(toolkit);
216 		// Pattern combo
217 		searchWordCombo = new ComboPart(container, toolkit, toolkit.getBorderStyle());
218 		updateSearchCombo(null);
219 		td = new TableWrapData(TableWrapData.FILL_GRAB);
220 		td.maxWidth = 100;
221 		td.valign = TableWrapData.MIDDLE;
222 		searchWordCombo.getControl().setLayoutData(td);
223 		goButton = toolkit.createButton(container, Messages.SearchPart_go, SWT.PUSH);
224 		goButton.addSelectionListener(new SelectionAdapter() {
225 
226 			@Override
227 			public void widgetSelected(SelectionEvent e) {
228 				handleButtonPressed();
229 			}
230 		});
231 		goButton.setEnabled(false);
232 		searchWordCombo.addModifyListener(e -> goButton.setEnabled(searchWordCombo.getText().length() > 0));
233 		searchWordCombo.addKeyListener(new KeyAdapter() {
234 
235 			@Override
236 			public void keyPressed(KeyEvent e) {
237 				if (e.character == '\r') {
238 					if (goButton.isEnabled())  {
239 						doSearch(searchWordCombo.getText());
240 					}
241 				}
242 			}
243 		});
244 		searchWordCombo.getControl().addListener(SWT.FocusIn, event -> {
245 			shellDefaultButton = null;
246 			Shell shell = searchWordCombo.getControl().getShell();
247 			Button button = shell.getDefaultButton();
248 			if (button != null) {
249 				shellDefaultButton = button;
250 				shell.setDefaultButton(goButton);
251 			}
252 		});
253 		searchWordCombo.getControl().addListener(SWT.FocusOut, event -> {
254 			if (shellDefaultButton != null) {
255 				Shell shell = searchWordCombo.getControl().getShell();
256 				shell.setDefaultButton(shellDefaultButton);
257 				shellDefaultButton = null;
258 			}
259 		});
260 
261 		createScopeSection(toolkit);
262 
263 //		createAlternateQueriesSection(toolkit);
264 
265 		toolkit.paintBordersFor(container);
266 		jobListener = new JobListener();
267 		Job.getJobManager().addJobChangeListener(jobListener);
268 	}
269 
createScopeSection(FormToolkit toolkit)270 	private void createScopeSection(FormToolkit toolkit) {
271 		TableWrapData td;
272 		scopeSection = toolkit.createSection(container, Section.TWISTIE | Section.COMPACT
273 				| Section.LEFT_TEXT_CLIENT_ALIGNMENT);
274 		scopeSection.setText(Messages.limit_to);
275 		td = new TableWrapData();
276 		td.colspan = 2;
277 		td.align = TableWrapData.FILL;
278 		scopeSection.setLayoutData(td);
279 		filteringGroup = toolkit.createComposite(scopeSection);
280 		scopeSection.setClient(filteringGroup);
281 		TableWrapLayout flayout = new TableWrapLayout();
282 		flayout.numColumns = 2;
283 		filteringGroup.setLayout(flayout);
284 		createScopeSet(scopeSection, toolkit);
285 		toolkit.paintBordersFor(filteringGroup);
286 		scopeObserver = new SearchScopeObserver();
287 		scopeSetManager.addObserver(scopeObserver);
288 	}
289 
createSearchExpressionSection(FormToolkit toolkit)290 	private void createSearchExpressionSection(FormToolkit toolkit) {
291 		TableWrapData td;
292 		Section searchExpressionSection = toolkit.createSection(container, Section.TWISTIE | Section.COMPACT
293 				| Section.LEFT_TEXT_CLIENT_ALIGNMENT);
294 		searchExpressionSection.setText(Messages.expression);
295 		td = new TableWrapData();
296 		td.colspan = 2;
297 		td.align = TableWrapData.FILL;
298 		searchExpressionSection.setLayoutData(td);
299 		Composite detailGroup = toolkit.createComposite(searchExpressionSection);
300 		searchExpressionSection.setClient(detailGroup);
301 		TableWrapLayout dgLayout = new TableWrapLayout();
302 		detailGroup.setLayout(dgLayout);
303 		//Label syntaxLabel = toolkit.createLabel(detailGroup, Messages.expression_label, SWT.WRAP);
304 		searchWordText = toolkit.createFormText(detailGroup, false);
305 		searchWordText.setImage(IHelpUIConstants.IMAGE_HELP, HelpUIResources
306 				.getImage(IHelpUIConstants.IMAGE_HELP));
307 		searchWordText.addHyperlinkListener(new HyperlinkAdapter() {
308 
309 			@Override
310 			public void linkActivated(HyperlinkEvent e) {
311 				SearchPart.this.parent.showURL(HREF_SEARCH_HELP, true);
312 			}
313 		});
314 		updateSearchWordText();
315 		toolkit.paintBordersFor(detailGroup);
316 	}
317 
createAlternateQueriesSection(FormToolkit toolkit)318 	private void createAlternateQueriesSection(FormToolkit toolkit){
319 
320 		TableWrapData td = new TableWrapData();
321 		td.colspan = 2;
322 		td.align = TableWrapData.FILL;
323 
324 		container.setMenu(new Menu(container));
325 
326 		alternateQuerySection = toolkit.createSection(container, Section.TWISTIE | Section.COMPACT
327 				| Section.LEFT_TEXT_CLIENT_ALIGNMENT);
328 		alternateQuerySection.setLayoutData(td);
329 		alternateQuerySection.setText(Messages.AlternateQueries);
330 
331 		alternateQueryComposite = toolkit.createComposite(alternateQuerySection);
332 		alternateQuerySection.setClient(alternateQueryComposite);
333 		TableWrapLayout flayout = new TableWrapLayout();
334 		flayout.numColumns = 1;
335 		alternateQueryComposite.setLayout(flayout);
336 		alternateQuerySection.setExpanded(true);
337 
338 //		alternateQuerySection.setVisible(false);
339 	}
340 
createAdvancedLink(Composite parent, FormToolkit toolkit)341 	private void createAdvancedLink(Composite parent, FormToolkit toolkit) {
342 		advancedLink = toolkit.createHyperlink(parent, Messages.FederatedSearchPart_advanced, SWT.NULL);
343 		advancedLink.addHyperlinkListener(new HyperlinkAdapter() {
344 
345 			@Override
346 			public void linkActivated(HyperlinkEvent e) {
347 				doAdvanced();
348 			}
349 		});
350 		TableWrapData td = new TableWrapData();
351 		td.colspan = 2;
352 		advancedLink.setLayoutData(td);
353 	}
354 
createScopeSet(Section section, FormToolkit toolkit)355 	private void createScopeSet(Section section, FormToolkit toolkit) {
356 		scopeSetLink = toolkit.createHyperlink(section, null, SWT.WRAP);
357 		scopeSetLink.addHyperlinkListener(new HyperlinkAdapter() {
358 
359 			@Override
360 			public void linkActivated(HyperlinkEvent e) {
361 				doChangeScopeSet();
362 			}
363 		});
364 		scopeSetLink.setToolTipText(Messages.FederatedSearchPart_changeScopeSet);
365 		section.setTextClient(scopeSetLink);
366 		ScopeSet active = scopeSetManager.getActiveSet();
367 		setActiveScopeSet(active);
368 	}
369 
updateSearchWordText()370 	private void updateSearchWordText() {
371 		StringBuilder buff = new StringBuilder();
372 		buff.append("<form>"); //$NON-NLS-1$
373 		buff.append("<p>"); //$NON-NLS-1$
374 		buff.append(Messages.expression_label);
375 			// Only add the link if available
376 			if (SEARCH_HELP_AVAILABLE) {
377 				buff.append("</p><p>"); //$NON-NLS-1$
378 				buff.append("<img href=\""); //$NON-NLS-1$
379 				buff.append(IHelpUIConstants.IMAGE_HELP);
380 				buff.append("\"/> "); //$NON-NLS-1$
381 				buff.append("<a href=\""); //$NON-NLS-1$
382 				buff.append(HREF_SEARCH_HELP);
383 				buff.append("\">"); //$NON-NLS-1$
384 				buff.append(Messages.SearchPart_learnMore);
385 				buff.append("</a>"); //$NON-NLS-1$
386 			}
387 
388 		buff.append("</p>"); //$NON-NLS-1$
389 		buff.append("</form>"); //$NON-NLS-1$
390 		searchWordText.setText(buff.toString(), true, false);
391 	}
392 
setActiveScopeSet(ScopeSet set)393 	private void setActiveScopeSet(ScopeSet set) {
394 		scopeSetLink.setText(set.getName());
395 		scopeSetManager.setActiveSet(set);
396 		updateMasters(set);
397 		scopeSection.layout();
398 		if (parent != null)
399 			parent.reflow();
400 	}
401 
updateMasters(ScopeSet set)402 	private void updateMasters(ScopeSet set) {
403 		Control[] children = ((Composite) scopeSection.getClient()).getChildren();
404 		for (int i = 0; i < children.length; i++) {
405 			Control child = children[i];
406 			if (child instanceof Button) {
407 				Button master = (Button) child;
408 				Object data = master.getData();
409 				if (data != null && data instanceof EngineDescriptor) {
410 					EngineDescriptor ed = (EngineDescriptor) data;
411 					master.setSelection(set.getEngineEnabled(ed));
412 				}
413 			}
414 		}
415 	}
416 
loadEngines(final Composite container, final FormToolkit toolkit)417 	private void loadEngines(final Composite container, final FormToolkit toolkit) {
418 		EngineDescriptorManager descManager = parent.getEngineManager();
419 		EngineDescriptor[] descriptors = descManager.getDescriptors();
420 		for (int i = 0; i < descriptors.length; i++) {
421 			EngineDescriptor desc = descriptors[i];
422 			loadEngine(desc, container, toolkit);
423 		}
424 		engineObserver = (o, arg) -> {
425 			EngineDescriptorManager.DescriptorEvent event = (EngineDescriptorManager.DescriptorEvent) arg;
426 			int kind = event.getKind();
427 			EngineDescriptor desc = event.getDescriptor();
428 			if (kind == IHelpUIConstants.ADD) {
429 				advancedLink.dispose();
430 				loadEngine(desc, container, toolkit);
431 				createAdvancedLink(container, toolkit);
432 				parent.reflow();
433 			} else if (kind == IHelpUIConstants.REMOVE) {
434 				removeEngine(desc);
435 			} else {
436 				updateEngine(desc);
437 			}
438 		};
439 
440 		descManager.addObserver(engineObserver);
441 		updateMasters(scopeSetManager.getActiveSet());
442 	}
443 
loadEngine(final EngineDescriptor edesc, Composite container, FormToolkit toolkit)444 	private EngineDescriptor loadEngine(final EngineDescriptor edesc, Composite container, FormToolkit toolkit) {
445 		Label ilabel = toolkit.createLabel(container, null);
446 		ilabel.setImage(edesc.getIconImage());
447 		ilabel.setData(edesc);
448 		final Button master = toolkit.createButton(container, edesc.getLabel(), SWT.CHECK);
449 		master.setData(edesc);
450 		master.addSelectionListener(new SelectionAdapter() {
451 
452 			@Override
453 			public void widgetSelected(SelectionEvent e) {
454 				scopeSetManager.getActiveSet().setEngineEnabled(edesc, master.getSelection());
455 			}
456 		});
457 		String desc = edesc.getDescription();
458 		if (desc != null) {
459 			Label spacer = toolkit.createLabel(container, null);
460 			spacer.setData(edesc);
461 			Label dlabel = toolkit.createLabel(container, desc, SWT.WRAP);
462 			dlabel.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
463 			dlabel.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
464 			dlabel.setMenu(container.getMenu());
465 			dlabel.setData(edesc);
466 		}
467 		return edesc;
468 	}
469 
removeEngine(EngineDescriptor desc)470 	private void removeEngine(EngineDescriptor desc) {
471 		boolean reflowNeeded = false;
472 		Control[] children = ((Composite) scopeSection.getClient()).getChildren();
473 		for (int i = 0; i < children.length; i++) {
474 			Control child = children[i];
475 			EngineDescriptor ed = (EngineDescriptor) child.getData();
476 			if (ed == desc) {
477 				child.setMenu(null);
478 				child.dispose();
479 				reflowNeeded = true;
480 			}
481 		}
482 		if (reflowNeeded)
483 			parent.reflow();
484 	}
485 
updateEngine(EngineDescriptor desc)486 	private void updateEngine(EngineDescriptor desc) {
487 		Control[] children = ((Composite) scopeSection.getClient()).getChildren();
488 		boolean reflowNeeded = false;
489 		for (int i = 0; i < children.length; i++) {
490 			Control child = children[i];
491 			EngineDescriptor ed = (EngineDescriptor) child.getData();
492 			if (ed == desc) {
493 				Button b = (Button) children[i + 1];
494 				b.setText(desc.getLabel());
495 				Label d = (Label) children[i + 3];
496 				d.setText(desc.getDescription());
497 				d.getParent().layout();
498 				reflowNeeded = true;
499 				break;
500 			}
501 		}
502 		if (reflowNeeded)
503 			parent.reflow();
504 	}
505 
startSearch(String text)506 	public void startSearch(String text) {
507 		searchWordCombo.setText(text);
508 		doSearch(text);
509 	}
510 
storeSearchHistory(String expression)511 	private void storeSearchHistory(String expression) {
512 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=95479
513 		HistoryScopeSet sset = scopeSetManager.findSearchSet(expression);
514 		if (sset == null) {
515 			sset = new HistoryScopeSet(expression);
516 			scopeSetManager.add(sset);
517 		}
518 		ScopeSet activeSet = scopeSetManager.getActiveSet();
519 		sset.copyFrom(activeSet);
520 		sset.save();
521 		updateSearchCombo(sset);
522 		searchWordCombo.setText(expression);
523 	}
524 
updateSearchCombo(HistoryScopeSet current)525 	private void updateSearchCombo(HistoryScopeSet current) {
526 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=95479
527 		ScopeSet[] sets = scopeSetManager.getScopeSets(true);
528 		ArrayList<String> items = new ArrayList<>();
529 		ArrayList<HistoryScopeSet> toDelete = new ArrayList<>();
530 		// if (current!=null)
531 		// items.add(current.getExpression());
532 		for (int i = sets.length - 1; i >= 0; i--) {
533 			HistoryScopeSet sset = (HistoryScopeSet) sets[i];
534 			if (current != null && sset == current)
535 				continue;
536 			if (sets.length - i > COMBO_HISTORY_SIZE)
537 				toDelete.add(sset);
538 			items.add(sset.getExpression());
539 		}
540 		for (int i = 0; i < toDelete.size(); i++) {
541 			HistoryScopeSet sset = toDelete.get(i);
542 			scopeSetManager.remove(sset);
543 		}
544 		if (items.size() > 0)
545 			searchWordCombo.setItems(items.toArray(new String[items.size()]));
546 	}
547 
handleButtonPressed()548 	private void handleButtonPressed() {
549 		if (searchWordCombo.getControl().isEnabled())
550 			doSearch(searchWordCombo.getText());
551 		else {
552 			goButton.setEnabled(false);
553 			stop();
554 		}
555 	}
556 
doSearch(String text)557 	private void doSearch(String text) {
558 		doSearch(text, false);
559 	}
560 
doSearch(String text, boolean fromHistory)561 	private void doSearch(String text, boolean fromHistory) {
562 		ScopeSet set = scopeSetManager.getActiveSet();
563 		if (!fromHistory && set instanceof HistoryScopeSet) {
564 			String setExpression = ((HistoryScopeSet) set).getExpression();
565 			if (setExpression.equals(text))
566 				fromHistory = true;
567 		}
568 		if (!fromHistory) {
569 			storeSearchHistory(text);
570 			boolean switchedSet = scopeSetManager.restoreLastExplicitSet();
571 			set = scopeSetManager.getActiveSet();
572 			if (switchedSet)
573 				setActiveScopeSet(set);
574 		}
575 		ArrayList<FederatedSearchEntry> entries = new ArrayList<>();
576 		final SearchResultsPart results = (SearchResultsPart) parent
577 				.findPart(IHelpUIConstants.HV_FSEARCH_RESULT);
578 		ArrayList<EngineDescriptor> eds = new ArrayList<>();
579 		EngineDescriptor[] engineDescriptors = parent.getEngineManager().getDescriptors();
580 		for (int i = 0; i < engineDescriptors.length; i++) {
581 			final EngineDescriptor ed = engineDescriptors[i];
582 			if (set.getEngineEnabled(ed) && ed.getEngine() != null) {
583 				ISearchScope scope = ed.createSearchScope(set.getPreferenceStore());
584 				FederatedSearchEntry entry = new FederatedSearchEntry(ed.getId(), ed.getLabel(), scope, ed
585 						.getEngine(), new ISearchEngineResultCollector() {
586 
587 							@Override
588 					public void accept(ISearchEngineResult searchResult) {
589 						results.add(ed, searchResult);
590 					}
591 
592 							@Override
593 					public void accept(ISearchEngineResult[] searchResults) {
594 						results.add(ed, searchResults);
595 						if (ed.getEngine() instanceof LocalHelp && !container.isDisposed())
596 						{
597 							container.getDisplay().asyncExec(new Thread() {
598 
599 								@Override
600 								public void run(){
601 									if (alternateQuerySection!=null)
602 									{
603 										alternateQuerySection.dispose();
604 										alternateQuerySection = null;
605 									}
606 									List<String> alts = ((LocalHelp) ed.getEngine()).getAlternates();
607 									if (!alts.isEmpty())
608 									{
609 										createAlternateQueriesSection(toolkit);
610 										for (int b=0;b<alts.size();b++)
611 										{
612 											Hyperlink link = toolkit.createHyperlink(
613 													alternateQueryComposite, alts.get(b), SWT.NONE);
614 											link.addHyperlinkListener(new HyperlinkAdapter(){
615 												@Override
616 												public void linkActivated(HyperlinkEvent e) {
617 													if (!searchWordCombo.getControl().isDisposed()) {
618 														searchWordCombo.setText(((Hyperlink)e.getSource()).getText());
619 														doSearch(((Hyperlink)e.getSource()).getText());
620 													}
621 												}
622 											});
623 										}
624 									}
625 								}
626 							});
627 						}
628 					}
629 
630 							@Override
631 					public void error(IStatus status) {
632 						results.error(ed, status);
633 					}
634 				});
635 				entries.add(entry);
636 				eds.add(ed);
637 			}
638 		}
639 		if (entries.isEmpty())
640 			return;
641 		FederatedSearchEntry[] array = entries.toArray(new FederatedSearchEntry[entries.size()]);
642 		if (scopeSection.isExpanded()) {
643 			scopeSection.setExpanded(false);
644 			parent.reflow();
645 		}
646 		results.clearResults();
647 		results.startNewSearch(text, eds);
648 		BaseHelpSystem.getSearchManager().search(text, array);
649 	}
650 
doAdvanced()651 	private void doAdvanced() {
652 		ScopeSet set = scopeSetManager.getActiveSet();
653 		PreferenceManager manager = new ScopePreferenceManager(parent.getEngineManager(), set);
654 		PreferenceDialog dialog = new ScopePreferenceDialog(container.getShell(), manager, parent
655 				.getEngineManager(), set.isEditable());
656 		dialog.setPreferenceStore(set.getPreferenceStore());
657 		dialog.create();
658 		dialog.getShell().setText(NLS.bind(Messages.ScopePreferenceDialog_wtitle, set.getName()));
659 		dialog.open();
660 		updateMasters(set);
661 	}
662 
doChangeScopeSet()663 	private void doChangeScopeSet() {
664 		ScopeSetDialog dialog = new ScopeSetDialog(container.getShell(), scopeSetManager, parent
665 				.getEngineManager(), false);
666 		dialog.setInput(scopeSetManager);
667 		dialog.create();
668 		dialog.getShell().setText(Messages.ScopeSetDialog_wtitle);
669 		if (dialog.open() == ScopeSetDialog.OK) {
670 			ScopeSet set = dialog.getActiveSet();
671 			if (set != null) {
672 				setActiveScopeSet(set);
673 			}
674 			scopeSetManager.save();
675 			scopeSetManager.notifyObservers();
676 		}
677 	}
678 
679 	@Override
dispose()680 	public void dispose() {
681 		ScopeSet activeSet = scopeSetManager.getActiveSet();
682 		if (activeSet != null)
683 			activeSet.save();
684 		if (engineObserver != null) {
685 			parent.getEngineManager().deleteObserver(engineObserver);
686 			engineObserver = null;
687 		}
688 
689 		if (scopeObserver != null) {
690 			ScopeState.getInstance().getScopeSetManager().deleteObserver(scopeObserver);
691 		}
692 
693 		Job.getJobManager().removeJobChangeListener(jobListener);
694 		stop();
695 		super.dispose();
696 	}
697 
698 	@Override
getControl()699 	public Control getControl() {
700 		return container;
701 	}
702 
703 	@Override
init(ReusableHelpPart parent, String id, IMemento memento)704 	public void init(ReusableHelpPart parent, String id, IMemento memento) {
705 		this.parent = parent;
706 		this.id = id;
707 		loadEngines(filteringGroup, parent.getForm().getToolkit());
708 		createAdvancedLink(filteringGroup, parent.getForm().getToolkit());
709 		parent.hookFormText(searchWordText);
710 		if (memento != null)
711 			restorePart(memento);
712 	}
713 
restorePart(IMemento memento)714 	private void restorePart(IMemento memento) {
715 		String setName = memento.getString("activeSet"); //$NON-NLS-1$
716 		if (setName != null) {
717 			ScopeSet sset = scopeSetManager.findSet(setName);
718 			if (sset != null)
719 				scopeSetManager.setActiveSet(sset);
720 		}
721 		String expression = memento.getString("expression"); //$NON-NLS-1$
722 		if (expression != null && expression.length() > 0) {
723 			searchWordCombo.setText(expression);
724 			searchPending = true;
725 			markStale();
726 		}
727 	}
728 
729 	@Override
refresh()730 	public void refresh() {
731 		super.refresh();
732 		if (searchPending) {
733 			searchPending = false;
734 			doSearch(searchWordCombo.getText());
735 		}
736 
737 	}
738 
739 	@Override
getId()740 	public String getId() {
741 		return id;
742 	}
743 
744 	@Override
setVisible(boolean visible)745 	public void setVisible(boolean visible) {
746 		getControl().setVisible(visible);
747 	}
748 
749 	@Override
fillContextMenu(IMenuManager manager)750 	public boolean fillContextMenu(IMenuManager manager) {
751 		return parent.fillFormContextMenu(searchWordText, manager);
752 	}
753 
754 	@Override
hasFocusControl(Control control)755 	public boolean hasFocusControl(Control control) {
756 		return control == searchWordText || control == searchWordCombo.getControl()
757 				|| scopeSection.getClient() == control;
758 	}
759 
760 	@Override
setFocus()761 	public void setFocus() {
762 		searchWordCombo.getControl().setFocus();
763 	}
764 
765 	@Override
getGlobalAction(String id)766 	public IAction getGlobalAction(String id) {
767 		if (id.equals(ActionFactory.COPY.getId()))
768 			return parent.getCopyAction();
769 		return null;
770 	}
771 
772 	@Override
stop()773 	public void stop() {
774 		SearchResultsPart results = (SearchResultsPart) parent.findPart(IHelpUIConstants.HV_FSEARCH_RESULT);
775 		if (results != null) {
776 			results.canceling();
777 		}
778 		Job.getJobManager().cancel(FederatedSearchJob.FAMILY);
779 	}
780 
781 	@Override
toggleRoleFilter()782 	public void toggleRoleFilter() {
783 	}
784 
785 	@Override
refilter()786 	public void refilter() {
787 	}
788 
789 	@Override
saveState(IMemento memento)790 	public void saveState(IMemento memento) {
791 		ScopeSet sset = scopeSetManager.getActiveSet();
792 		if (sset != null)
793 			memento.putString("activeSet", sset.getName()); //$NON-NLS-1$
794 		memento.putString("expression", searchWordCombo.getText()); //$NON-NLS-1$
795 	}
796 }
797