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  *     George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 560168
14  *******************************************************************************/
15 package org.eclipse.help.ui.internal.views;
16 
17 import java.io.UnsupportedEncodingException;
18 import java.net.MalformedURLException;
19 import java.net.URL;
20 import java.net.URLDecoder;
21 import java.text.Collator;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Set;
27 
28 import org.eclipse.core.runtime.Platform;
29 import org.eclipse.core.runtime.jobs.Job;
30 import org.eclipse.help.IContext;
31 import org.eclipse.help.IContextProvider;
32 import org.eclipse.help.IHelpResource;
33 import org.eclipse.help.IIndexEntry;
34 import org.eclipse.help.IToc;
35 import org.eclipse.help.ITopic;
36 import org.eclipse.help.UAContentFilter;
37 import org.eclipse.help.internal.base.BaseHelpSystem;
38 import org.eclipse.help.internal.base.HelpBasePlugin;
39 import org.eclipse.help.internal.base.HelpEvaluationContext;
40 import org.eclipse.help.internal.base.IHelpBaseConstants;
41 import org.eclipse.help.internal.base.MissingContentManager;
42 import org.eclipse.help.internal.base.util.LinkUtil;
43 import org.eclipse.help.internal.protocols.HelpURLConnection;
44 import org.eclipse.help.internal.search.federated.IndexerJob;
45 import org.eclipse.help.internal.util.ProductPreferences;
46 import org.eclipse.help.search.ISearchEngine2;
47 import org.eclipse.help.ui.internal.DefaultHelpUI;
48 import org.eclipse.help.ui.internal.HelpUIPlugin;
49 import org.eclipse.help.ui.internal.HelpUIResources;
50 import org.eclipse.help.ui.internal.IHelpUIConstants;
51 import org.eclipse.help.ui.internal.Messages;
52 import org.eclipse.jface.action.Action;
53 import org.eclipse.jface.action.IAction;
54 import org.eclipse.jface.action.IContributionManager;
55 import org.eclipse.jface.action.IMenuListener;
56 import org.eclipse.jface.action.IMenuManager;
57 import org.eclipse.jface.action.IStatusLineManager;
58 import org.eclipse.jface.action.IToolBarManager;
59 import org.eclipse.jface.action.MenuManager;
60 import org.eclipse.jface.action.Separator;
61 import org.eclipse.jface.action.SubMenuManager;
62 import org.eclipse.jface.action.SubStatusLineManager;
63 import org.eclipse.jface.action.SubToolBarManager;
64 import org.eclipse.jface.dialogs.MessageDialogWithToggle;
65 import org.eclipse.jface.operation.IRunnableContext;
66 import org.eclipse.jface.preference.IPreferenceStore;
67 import org.eclipse.jface.viewers.ISelectionProvider;
68 import org.eclipse.jface.viewers.IStructuredSelection;
69 import org.eclipse.jface.viewers.Viewer;
70 import org.eclipse.jface.viewers.ViewerFilter;
71 import org.eclipse.osgi.util.NLS;
72 import org.eclipse.swt.SWT;
73 import org.eclipse.swt.SWTError;
74 import org.eclipse.swt.custom.BusyIndicator;
75 import org.eclipse.swt.events.FocusEvent;
76 import org.eclipse.swt.events.FocusListener;
77 import org.eclipse.swt.events.SelectionEvent;
78 import org.eclipse.swt.events.SelectionListener;
79 import org.eclipse.swt.graphics.Image;
80 import org.eclipse.swt.graphics.Point;
81 import org.eclipse.swt.graphics.Rectangle;
82 import org.eclipse.swt.widgets.Composite;
83 import org.eclipse.swt.widgets.Control;
84 import org.eclipse.swt.widgets.Layout;
85 import org.eclipse.swt.widgets.Menu;
86 import org.eclipse.swt.widgets.Shell;
87 import org.eclipse.ui.IActionBars;
88 import org.eclipse.ui.IMemento;
89 import org.eclipse.ui.ISharedImages;
90 import org.eclipse.ui.IWorkbenchPart;
91 import org.eclipse.ui.IWorkbenchWindow;
92 import org.eclipse.ui.PlatformUI;
93 import org.eclipse.ui.SubActionBars;
94 import org.eclipse.ui.actions.ActionFactory;
95 import org.eclipse.ui.activities.ActivityManagerEvent;
96 import org.eclipse.ui.activities.IActivityManagerListener;
97 import org.eclipse.ui.forms.IFormPart;
98 import org.eclipse.ui.forms.ManagedForm;
99 import org.eclipse.ui.forms.events.HyperlinkEvent;
100 import org.eclipse.ui.forms.widgets.FormText;
101 import org.eclipse.ui.forms.widgets.FormToolkit;
102 import org.eclipse.ui.forms.widgets.ILayoutExtension;
103 import org.eclipse.ui.forms.widgets.ScrolledForm;
104 
105 public class ReusableHelpPart implements IHelpUIConstants,
106 		IActivityManagerListener {
107 	public static final int ALL_TOPICS = 1 << 1;
108 
109 	public static final int CONTEXT_HELP = 1 << 2;
110 
111 	public static final int SEARCH = 1 << 3;
112 
113 	public static final int BOOKMARKS = 1 << 4;
114 
115 	public static final int INDEX = 1 << 5;
116 
117 	public static final Collator SHARED_COLLATOR = Collator.getInstance();
118 
119 	private static final String PROMPT_KEY = "askShowAll"; //$NON-NLS-1$
120 
121 	private static final int STATE_START = 1;
122 
123 	private static final int STATE_LT = 2;
124 
125 	private static final int STATE_LT_B = 3;
126 
127 	private static final int STATE_LT_BR = 4;
128 
129 	/*
130 	 * Used as a bridge from live help actions back (e.g. breadcrumb links)
131 	 * to the originating help part.
132 	 */
133 	private static ReusableHelpPart lastActiveInstance;
134 
135 	private RoleFilter roleFilter;
136 
137 	private UAFilter uaFilter;
138 
139 	private ManagedForm mform;
140 
141 	private int verticalSpacing = 15;
142 
143 	private int bmargin = 5;
144 
145 	private String defaultContextHelpText;
146 
147 	private ArrayList<IHelpPartPage> pages;
148 
149 	private Action backAction;
150 
151 	private Action nextAction;
152 
153 	private CopyAction copyAction;
154 
155 	private Action openInfoCenterAction;
156 
157 	private OpenHrefAction openAction;
158 
159 	private OpenHrefAction openInHelpAction;
160 
161 	private OpenHrefAction bookmarkAction;
162 
163 	private Action showAllAction;
164 
165 	private ReusableHelpPartHistory history;
166 
167 	private HelpPartPage currentPage;
168 
169 	private int style;
170 
171 	private IMemento memento;
172 
173 	private boolean showDocumentsInPlace = true;
174 
175 	private int numberOfInPlaceHits = 8;
176 
177 	private IRunnableContext runnableContext;
178 
179 	private IToolBarManager toolBarManager;
180 
181 	private IStatusLineManager statusLineManager;
182 
183 	private IActionBars actionBars;
184 
185 	private EngineDescriptorManager engineManager;
186 
187 	public IMenuManager menuManager;
188 
189 	private abstract class BusyRunAction extends Action {
BusyRunAction(String name)190 		public BusyRunAction(String name) {
191 			super(name);
192 		}
193 
194 		@Override
run()195 		public void run() {
196 			BusyIndicator.showWhile(getControl().getDisplay(), this::busyRun);
197 		}
198 
busyRun()199 		protected abstract void busyRun();
200 	}
201 
202 	private abstract class OpenHrefAction extends BusyRunAction {
203 		private Object target;
204 
OpenHrefAction(String name)205 		public OpenHrefAction(String name) {
206 			super(name);
207 		}
208 
setTarget(Object target)209 		public void setTarget(Object target) {
210 			this.target = target;
211 		}
212 
getTarget()213 		public Object getTarget() {
214 			return target;
215 		}
216 	}
217 
218 	private class CopyAction extends Action implements FocusListener,
219 			SelectionListener {
220 		private FormText target;
221 
CopyAction()222 		public CopyAction() {
223 			super("copy"); //$NON-NLS-1$
224 		}
225 
hook(final FormText text)226 		public void hook(final FormText text) {
227 			text.addFocusListener(this);
228 		}
229 
unhook(FormText text)230 		public void unhook(FormText text) {
231 			text.removeFocusListener(this);
232 			if (target == text)
233 				setTarget(null);
234 		}
235 
236 		@Override
focusGained(FocusEvent e)237 		public void focusGained(FocusEvent e) {
238 			FormText text = (FormText) e.widget;
239 			text.addSelectionListener(this);
240 			setTarget(text);
241 		}
242 
243 		@Override
focusLost(FocusEvent e)244 		public void focusLost(FocusEvent e) {
245 			FormText text = (FormText) e.widget;
246 			text.removeSelectionListener(this);
247 			setTarget(null);
248 		}
249 
setTarget(FormText target)250 		public void setTarget(FormText target) {
251 			this.target = target;
252 			updateState();
253 		}
254 
updateState()255 		private void updateState() {
256 			setEnabled(target != null && target.canCopy());
257 		}
258 
259 		@Override
run()260 		public void run() {
261 			if (target != null)
262 				target.copy();
263 		}
264 
265 		@Override
widgetSelected(SelectionEvent e)266 		public void widgetSelected(SelectionEvent e) {
267 			FormText text = (FormText) e.widget;
268 			if (text == target) {
269 				updateState();
270 			}
271 		}
272 
273 		@Override
widgetDefaultSelected(SelectionEvent e)274 		public void widgetDefaultSelected(SelectionEvent e) {
275 		}
276 	}
277 
278 	private static class PartRec {
279 		String id;
280 
281 		boolean flexible;
282 
283 		boolean grabVertical;
284 
285 		IHelpPart part;
286 
PartRec(String id, boolean flexible, boolean grabVertical)287 		PartRec(String id, boolean flexible, boolean grabVertical) {
288 			this.id = id;
289 			this.flexible = flexible;
290 			this.grabVertical = grabVertical;
291 		}
292 	}
293 
294 	private class HelpPartPage implements IHelpPartPage {
295 		private String id;
296 
297 		private String iconId;
298 
299 		Action pageAction;
300 
301 		private int vspacing = verticalSpacing;
302 
303 		private int horizontalMargin = 0;
304 
305 		private String text;
306 
307 		private SubActionBars bars;
308 
309 		private IToolBarManager subToolBarManager;
310 
311 		private IMenuManager subMenuManager;
312 
313 		protected ArrayList<PartRec> partRecs;
314 
315 		private int nflexible;
316 
HelpPartPage(String id, String text)317 		public HelpPartPage(String id, String text) {
318 			this.id = id;
319 			this.text = text;
320 			partRecs = new ArrayList<>();
321 			if (ReusableHelpPart.this.actionBars != null) {
322 				// Help View
323 				bars = new SubActionBars(ReusableHelpPart.this.actionBars);
324 				subToolBarManager = bars.getToolBarManager();
325 				subMenuManager = bars.getMenuManager();
326 			} else {
327 				// Help Tray
328 				subToolBarManager = new SubToolBarManager(
329 						ReusableHelpPart.this.toolBarManager);
330 				if (ReusableHelpPart.this.menuManager != null) {
331 					subMenuManager = new SubMenuManager(
332 						ReusableHelpPart.this.menuManager);
333 				} else {
334 					subMenuManager = null;
335 				}
336 			}
337 		}
338 
HelpPartPage(String id, String text, String iconId)339 		public HelpPartPage(String id, String text, String iconId) {
340 			this(id, text);
341 			this.iconId = iconId;
342 		}
343 
344 		@Override
dispose()345 		public void dispose() {
346 			if (bars != null) {
347 				bars.dispose();
348 				bars = null;
349 				subToolBarManager = null;
350 				subMenuManager = null;
351 			} else {
352 				try {
353 					((SubToolBarManager) subToolBarManager).disposeManager();
354 					if (subMenuManager != null) {
355 						((SubMenuManager)subMenuManager).disposeManager();
356 					}
357 				} catch (RuntimeException e) {
358 					// Bug 218079
359 				}
360 			}
361 			partRecs = null;
362 		}
363 
364 		@Override
setVerticalSpacing(int value)365 		public void setVerticalSpacing(int value) {
366 			this.vspacing = value;
367 		}
368 
369 		@Override
getVerticalSpacing()370 		public int getVerticalSpacing() {
371 			return vspacing;
372 		}
373 
374 		@Override
setHorizontalMargin(int value)375 		public void setHorizontalMargin(int value) {
376 			this.horizontalMargin = value;
377 		}
378 
379 		@Override
getHorizontalMargin()380 		public int getHorizontalMargin() {
381 			return horizontalMargin;
382 		}
383 
384 		@Override
getToolBarManager()385 		public IToolBarManager getToolBarManager() {
386 			return subToolBarManager;
387 		}
388 
389 		@Override
getId()390 		public String getId() {
391 			return id;
392 		}
393 
394 		@Override
getText()395 		public String getText() {
396 			return text;
397 		}
398 
399 		@Override
getIconId()400 		public String getIconId() {
401 			return iconId;
402 		}
403 
404 		@Override
addPart(String id, boolean flexible)405 		public void addPart(String id, boolean flexible) {
406 			addPart(id, flexible, false);
407 		}
408 
409 		@Override
addPart(String id, boolean flexible, boolean grabVertical)410 		public void addPart(String id, boolean flexible, boolean grabVertical) {
411 			partRecs.add(new PartRec(id, flexible, grabVertical));
412 			if (flexible)
413 				nflexible++;
414 		}
415 
getParts()416 		public PartRec[] getParts() {
417 			return partRecs.toArray(new PartRec[partRecs.size()]);
418 		}
419 
refreshPage()420 		public void refreshPage()
421 		{
422 			PartRec parts[] = getParts();
423 			if (parts==null)
424 				return;
425 
426 			for (int p=0;p<parts.length;p++)
427 				if (parts[p]!=null && parts[p].part!=null && parts[p].part.isStale())
428 					parts[p].part.refresh();
429 		}
430 
431 		@Override
getNumberOfFlexibleParts()432 		public int getNumberOfFlexibleParts() {
433 			return nflexible;
434 		}
435 
436 		@Override
canOpen()437 		public boolean canOpen() {
438 			for (int i = 0; i < partRecs.size(); i++) {
439 				PartRec rec = partRecs.get(i);
440 
441 				if (rec.id.equals(IHelpUIConstants.HV_BROWSER)) {
442 					// Try to create a browser and watch
443 					// for 'no-handle' error - it means
444 					// that the embedded browser is not
445 					// available
446 					try {
447 						createRecPart(rec);
448 						rec.part.setVisible(false);
449 					} catch (SWTError error) {
450 						// cannot create a browser
451 						return false;
452 					}
453 				}
454 			}
455 			return true;
456 		}
457 
458 		@Override
stop()459 		public void stop() {
460 			for (int i = 0; i < partRecs.size(); i++) {
461 				PartRec rec = partRecs.get(i);
462 				if (rec.part!=null)
463 					rec.part.stop();
464 			}
465 		}
466 
467 		@Override
saveState(IMemento memento)468 		public void saveState(IMemento memento) {
469 			for (int i = 0; i < partRecs.size(); i++) {
470 				PartRec rec = partRecs.get(i);
471 				if (rec.part!=null)
472 					rec.part.saveState(memento);
473 			}
474 		}
475 
476 		@Override
toggleRoleFilter()477 		public void toggleRoleFilter() {
478 			for (int i = 0; i < partRecs.size(); i++) {
479 				PartRec rec = partRecs.get(i);
480 				if (rec.part != null)
481 					rec.part.toggleRoleFilter();
482 			}
483 		}
484 
485 		@Override
refilter()486 		public void refilter() {
487 			for (int i = 0; i < partRecs.size(); i++) {
488 				PartRec rec = partRecs.get(i);
489 				if (rec.part != null)
490 					rec.part.refilter();
491 			}
492 		}
493 
494 		@Override
setVisible(boolean visible)495 		public void setVisible(boolean visible) {
496 			if (bars != null)
497 				bars.clearGlobalActionHandlers();
498 			ArrayList<Control> tabList = new ArrayList<>();
499 			for (int i = 0; i < partRecs.size(); i++) {
500 				PartRec rec = partRecs.get(i);
501 				if (visible) {
502 					createRecPart(rec);
503 					hookGlobalAction(ActionFactory.PRINT.getId(), rec.part);
504 					hookGlobalAction(ActionFactory.COPY.getId(), rec.part);
505 					hookGlobalAction(ActionFactory.DELETE.getId(), rec.part);
506 					tabList.add(rec.part.getControl());
507 				}
508 				rec.part.setVisible(visible);
509 			}
510 			Composite parent = mform.getForm().getBody();
511 			parent.setTabList(tabList.toArray(new Control[tabList.size()]));
512 
513 			if (actionBars != null) {
514 				actionBars.clearGlobalActionHandlers();
515 				if (visible) {
516 					Map<String, IAction> handlers = bars.getGlobalActionHandlers();
517 					if (handlers != null) {
518 						Set<String> keys = handlers.keySet();
519 						for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
520 							String key = iter.next();
521 							actionBars.setGlobalActionHandler(key, handlers.get(key));
522 						}
523 					}
524 				}
525 				if (pageAction != null)
526 					pageAction.setChecked(visible);
527 			}
528 
529 			if (bars != null) {
530 				if (visible)
531 					bars.activate();
532 				else
533 					bars.deactivate();
534 				bars.updateActionBars();
535 			} else {
536 				((SubToolBarManager) subToolBarManager).setVisible(visible);
537 				if (subMenuManager != null) {
538 					((SubMenuManager)subMenuManager).setVisible(visible);
539 				}
540 				ReusableHelpPart.this.toolBarManager.update(true);
541 				getControl().getParent().layout();
542 			}
543 
544 		}
545 
hookGlobalAction(String id, IHelpPart part)546 		private void hookGlobalAction(String id, IHelpPart part) {
547 			if (bars == null)
548 				return;
549 			IAction action = part.getGlobalAction(id);
550 			if (action != null)
551 				bars.setGlobalActionHandler(id, action);
552 		}
553 
createRecPart(PartRec rec)554 		private void createRecPart(PartRec rec) throws SWTError {
555 			if (rec.part == null) {
556 				rec.part = createPart(rec.id, subToolBarManager, subMenuManager);
557 			}
558 		}
559 
560 		@Override
findPart(String id)561 		public IHelpPart findPart(String id) {
562 			for (int i = 0; i < partRecs.size(); i++) {
563 				PartRec rec = partRecs.get(i);
564 				if (rec.id.equals(id))
565 					return rec.part;
566 			}
567 			return null;
568 		}
569 
570 		@Override
setFocus()571 		public void setFocus() {
572 			// Focus on the first part that is not the see also links or missing content link
573 			for (int focusPart = 0; focusPart < partRecs.size(); focusPart++) {
574 				PartRec rec = partRecs.get(focusPart);
575 				String partId = rec.part.getId();
576 				if ( partId != IHelpUIConstants.HV_SEE_ALSO && partId != IHelpUIConstants.HV_MISSING_CONTENT) {
577 					rec.part.setFocus();
578 					return;
579 				}
580 			}
581 		}
582 
583 	}
584 
585 	class HelpPartLayout extends Layout implements ILayoutExtension {
586 
587 		@Override
computeMaximumWidth(Composite parent, boolean changed)588 		public int computeMaximumWidth(Composite parent, boolean changed) {
589 			return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
590 		}
591 
592 		@Override
computeMinimumWidth(Composite parent, boolean changed)593 		public int computeMinimumWidth(Composite parent, boolean changed) {
594 			return computeSize(parent, 0, SWT.DEFAULT, changed).x;
595 		}
596 
597 		@Override
computeSize(Composite composite, int wHint, int hHint, boolean flushCache)598 		protected Point computeSize(Composite composite, int wHint, int hHint,
599 				boolean flushCache) {
600 			if (currentPage == null)
601 				return new Point(0, 0);
602 			PartRec[] parts = currentPage.getParts();
603 			int hmargin = currentPage.getHorizontalMargin();
604 			int innerWhint = wHint != SWT.DEFAULT ? wHint - 2 * hmargin : wHint;
605 			Point result = new Point(0, 0);
606 			for (int i = 0; i < parts.length; i++) {
607 				PartRec partRec = parts[i];
608 				if (!partRec.flexible) {
609 					Control c = partRec.part.getControl();
610 					Point size = c.computeSize(innerWhint, SWT.DEFAULT,
611 							flushCache);
612 					result.x = Math.max(result.x, size.x);
613 					result.y += size.y;
614 				}
615 				if (i < parts.length - 1)
616 					result.y += currentPage.getVerticalSpacing();
617 			}
618 			result.x += hmargin * 2;
619 			result.y += bmargin;
620 			return result;
621 		}
622 
623 		@Override
layout(Composite composite, boolean flushCache)624 		protected void layout(Composite composite, boolean flushCache) {
625 			if (currentPage == null)
626 				return;
627 
628 			Rectangle clientArea = composite.getClientArea();
629 
630 			PartRec[] parts = currentPage.getParts();
631 			int hmargin = currentPage.getHorizontalMargin();
632 			int nfixedParts = parts.length
633 					- currentPage.getNumberOfFlexibleParts();
634 			Point[] fixedSizes = new Point[nfixedParts];
635 			int fixedHeight = 0;
636 			int index = 0;
637 			int innerWidth = clientArea.width - hmargin * 2;
638 			for (int i = 0; i < parts.length; i++) {
639 				PartRec partRec = parts[i];
640 				if (!partRec.flexible) {
641 					Control c = partRec.part.getControl();
642 					Point size = c.computeSize(innerWidth, SWT.DEFAULT, false);
643 					fixedSizes[index++] = size;
644 					if (!partRec.grabVertical)
645 						fixedHeight += size.y;
646 				}
647 				if (i < parts.length - 1)
648 					fixedHeight += currentPage.getVerticalSpacing();
649 			}
650 			fixedHeight += bmargin;
651 			int flexHeight = clientArea.height - fixedHeight;
652 			int flexPortion = 0;
653 			if (currentPage.getNumberOfFlexibleParts() > 0)
654 				flexPortion = flexHeight
655 						/ currentPage.getNumberOfFlexibleParts();
656 
657 			int usedFlexHeight = 0;
658 			int y = 0;
659 			index = 0;
660 			int nflexParts = 0;
661 			for (int i = 0; i < parts.length; i++) {
662 				PartRec partRec = parts[i];
663 				Control c = partRec.part.getControl();
664 
665 				if (partRec.flexible) {
666 					int height;
667 					if (++nflexParts == currentPage.getNumberOfFlexibleParts())
668 						height = flexHeight - usedFlexHeight;
669 					else {
670 						height = flexPortion;
671 						usedFlexHeight += height;
672 					}
673 					c.setBounds(0, y, clientArea.width, height);
674 				} else {
675 					Point fixedSize = fixedSizes[index++];
676 					if (fixedSize.y < flexHeight && partRec.grabVertical)
677 						c.setBounds(hmargin, y, innerWidth, flexHeight);
678 					else
679 						c.setBounds(hmargin, y, innerWidth, fixedSize.y);
680 				}
681 				if (i < parts.length - 1)
682 					y += c.getSize().y + currentPage.getVerticalSpacing();
683 			}
684 		}
685 	}
686 
687 	class RoleFilter extends ViewerFilter {
688 
689 		@Override
select(Viewer viewer, Object parentElement, Object element)690 		public boolean select(Viewer viewer, Object parentElement,
691 				Object element) {
692 			IHelpResource res = (IHelpResource) element;
693 			String href = res.getHref();
694 			if (href == null)
695 				return true;
696 			return HelpBasePlugin.getActivitySupport().isEnabled(href);
697 		}
698 	}
699 
700 	class UAFilter extends ViewerFilter {
701 
702 		@Override
select(Viewer viewer, Object parentElement, Object element)703 		public boolean select(Viewer viewer, Object parentElement,
704 				Object element) {
705 			return !UAContentFilter.isFiltered(element, HelpEvaluationContext.getContext());
706 		}
707 	}
708 
ReusableHelpPart(IRunnableContext runnableContext)709 	public ReusableHelpPart(IRunnableContext runnableContext) {
710 		this(runnableContext, getDefaultStyle());
711 	}
712 
ReusableHelpPart(IRunnableContext runnableContext, int style)713 	public ReusableHelpPart(IRunnableContext runnableContext, int style) {
714 		this.runnableContext = runnableContext;
715 		history = new ReusableHelpPartHistory();
716 		this.style = style;
717 		ensureHelpIndexed();
718 		PlatformUI.getWorkbench().getActivitySupport().getActivityManager()
719 				.addActivityManagerListener(this);
720 	}
721 
722 	/*
723 	 * Used as a bridge from live help actions back (e.g. breadcrumb links)
724 	 * to the originating help part.
725 	 */
getLastActiveInstance()726 	public static ReusableHelpPart getLastActiveInstance() {
727 		return lastActiveInstance;
728 	}
729 
ensureHelpIndexed()730 	private void ensureHelpIndexed() {
731 		// make sure we have the index but
732 		// don't schedule the indexer job if one is
733 		// already running
734 		Job[] jobs = Job.getJobManager().find(IndexerJob.FAMILY);
735 		if (jobs.length == 0) {
736 			IndexerJob indexerJob = new IndexerJob();
737 			indexerJob.schedule();
738 		}
739 	}
740 
741 	/**
742 	 * Adds the given page to this part.
743 	 *
744 	 * @param page the page to add
745 	 */
addPage(IHelpPartPage page)746 	public void addPage(IHelpPartPage page) {
747 		pages.add(page);
748 	}
749 
750 	/**
751 	 * Adds the given part to this one. The part can then be used inside
752 	 * any page and referred to by id.
753 	 *
754 	 * @param id the part's unique id
755 	 * @param part the part to add
756 	 */
addPart(String id, IHelpPart part)757 	public void addPart(String id, IHelpPart part) {
758 		part.init(this, id, memento);
759 		mform.addPart(part);
760 	}
761 
762 	/**
763 	 * Creates a new page for this part.
764 	 *
765 	 * @param id the page's unique id
766 	 * @param text the page's heading, or null for none
767 	 * @param iconId the page's icon
768 	 * @return the newly created page
769 	 */
createPage(String id, String text, String iconId)770 	public IHelpPartPage createPage(String id, String text, String iconId) {
771 		return new HelpPartPage(id, text, iconId);
772 	}
773 
definePages()774 	private void definePages() {
775 		pages = new ArrayList<>();
776 		// federated search page
777 		HelpPartPage page = new HelpPartPage(HV_FSEARCH_PAGE,
778 				Messages.ReusableHelpPart_searchPage_name,
779 				IHelpUIConstants.IMAGE_HELP_SEARCH);
780 		page.setVerticalSpacing(0);
781 		page.addPart(HV_SEE_ALSO, false);
782 		page.addPart(HV_MISSING_CONTENT, false);
783 		page.addPart(HV_FSEARCH, false);
784 		page.addPart(HV_FSEARCH_RESULT, true);
785 		pages.add(page);
786 
787 		// all topics page
788 		page = new HelpPartPage(HV_ALL_TOPICS_PAGE,
789 				Messages.ReusableHelpPart_allTopicsPage_name,
790 				IHelpUIConstants.IMAGE_ALL_TOPICS);
791 		page.setVerticalSpacing(0);
792 		page.setHorizontalMargin(0);
793 		page.addPart(HV_SEE_ALSO, false);
794 		page.addPart(HV_MISSING_CONTENT, false);
795 		page.addPart(HV_SCOPE_SELECT, false);
796 		page.addPart(HV_TOPIC_TREE, true);
797 		pages.add(page);
798 
799 		// bookmarks page
800 		page = new HelpPartPage(HV_BOOKMARKS_PAGE,
801 				Messages.ReusableHelpPart_bookmarksPage_name,
802 				IHelpUIConstants.IMAGE_BOOKMARKS);
803 		page.setVerticalSpacing(0);
804 		page.setHorizontalMargin(0);
805 		page.addPart(HV_SEE_ALSO, false);
806 		page.addPart(HV_BOOKMARKS_HEADER, false);
807 		page.addPart(HV_BOOKMARKS_TREE, true);
808 		pages.add(page);
809 		// browser page
810 		page = new HelpPartPage(HV_BROWSER_PAGE, null);
811 		page.setVerticalSpacing(0);
812 		page.addPart(HV_SEE_ALSO, false);
813 		page.addPart(HV_BROWSER, true);
814 		pages.add(page);
815 
816 		// context help page
817 		page = new HelpPartPage(HV_CONTEXT_HELP_PAGE,
818 				Messages.ReusableHelpPart_contextHelpPage_name,
819 				IHelpUIConstants.IMAGE_RELATED_TOPICS);
820 		// page.addPart(HV_CONTEXT_HELP, false);
821 		// page.addPart(HV_SEARCH_RESULT, false, true);
822 		page.setVerticalSpacing(0);
823 		page.setHorizontalMargin(0);
824 		page.addPart(HV_SEE_ALSO, false);
825 		page.addPart(HV_MISSING_CONTENT, false);
826 		page.addPart(HV_RELATED_TOPICS, true);
827 		pages.add(page);
828 
829 		// index page
830 		page = new HelpPartPage(HV_INDEX_PAGE,
831 				Messages.ReusableHelpPart_indexPage_name,
832 				IHelpUIConstants.IMAGE_INDEX);
833 		page.setVerticalSpacing(0);
834 		page.addPart(HV_SEE_ALSO, false);
835 		page.addPart(HV_MISSING_CONTENT, false);
836 		page.addPart(HV_SCOPE_SELECT, false);
837 		page.addPart(HV_INDEX_TYPEIN, false);
838 		page.addPart(HV_INDEX, true);
839 		pages.add(page);
840 	}
841 
init(IActionBars bars, IToolBarManager toolBarManager, IStatusLineManager statusLineManager, IMenuManager menuManager, IMemento memento)842 	public void init(IActionBars bars, IToolBarManager toolBarManager,
843 			IStatusLineManager statusLineManager, IMenuManager menuManager, IMemento memento) {
844 		this.memento = memento;
845 		this.actionBars = bars;
846 		this.toolBarManager = toolBarManager;
847 		this.menuManager = menuManager;
848 		this.statusLineManager = statusLineManager;
849 		definePages();
850 		makeActions();
851 	}
852 
makeActions()853 	private void makeActions() {
854 		backAction = new Action("back") { //$NON-NLS-1$
855 
856 			@Override
857 			public void run() {
858 				doBack();
859 			}
860 		};
861 		backAction.setImageDescriptor(PlatformUI.getWorkbench()
862 				.getSharedImages().getImageDescriptor(
863 						ISharedImages.IMG_TOOL_BACK));
864 		backAction.setDisabledImageDescriptor(PlatformUI.getWorkbench()
865 				.getSharedImages().getImageDescriptor(
866 						ISharedImages.IMG_TOOL_BACK_DISABLED));
867 		backAction.setEnabled(false);
868 		backAction.setText(Messages.ReusableHelpPart_back_label);
869 		backAction.setToolTipText(Messages.ReusableHelpPart_back_tooltip);
870 		backAction.setId("back"); //$NON-NLS-1$
871 
872 		nextAction = new Action("next") { //$NON-NLS-1$
873 
874 			@Override
875 			public void run() {
876 				doNext();
877 			}
878 		};
879 		nextAction.setText(Messages.ReusableHelpPart_forward_label);
880 		nextAction.setImageDescriptor(PlatformUI.getWorkbench()
881 				.getSharedImages().getImageDescriptor(
882 						ISharedImages.IMG_TOOL_FORWARD));
883 		nextAction.setDisabledImageDescriptor(PlatformUI.getWorkbench()
884 				.getSharedImages().getImageDescriptor(
885 						ISharedImages.IMG_TOOL_FORWARD_DISABLED));
886 		nextAction.setEnabled(false);
887 		nextAction.setToolTipText(Messages.ReusableHelpPart_forward_tooltip);
888 		nextAction.setId("next"); //$NON-NLS-1$
889 		toolBarManager.add(backAction);
890 		toolBarManager.add(nextAction);
891 
892 		openInfoCenterAction = new BusyRunAction("openInfoCenter") { //$NON-NLS-1$
893 
894 			@Override
895 			protected void busyRun() {
896 				PlatformUI.getWorkbench().getHelpSystem().displayHelp();
897 			}
898 		};
899 		openInfoCenterAction
900 				.setText(Messages.ReusableHelpPart_openInfoCenterAction_label);
901 		openAction = new OpenHrefAction("open") { //$NON-NLS-1$
902 
903 			@Override
904 			protected void busyRun() {
905 				doOpen(getTarget(), getShowDocumentsInPlace());
906 			}
907 		};
908 		openAction.setText(Messages.ReusableHelpPart_openAction_label);
909 		openInHelpAction = new OpenHrefAction("") {//$NON-NLS-1$
910 
911 			@Override
912 			protected void busyRun() {
913 				doOpenInHelp(getTarget());
914 			}
915 		};
916 		openInHelpAction
917 				.setText(Messages.ReusableHelpPart_openInHelpContentsAction_label);
918 		copyAction = new CopyAction();
919 		copyAction.setText(Messages.ReusableHelpPart_copyAction_label);
920 		bookmarkAction = new OpenHrefAction("bookmark") { //$NON-NLS-1$
921 
922 			@Override
923 			protected void busyRun() {
924 				doBookmark(getTarget());
925 			}
926 		};
927 		bookmarkAction.setText(Messages.ReusableHelpPart_bookmarkAction_label);
928 		bookmarkAction.setImageDescriptor(HelpUIResources
929 				.getImageDescriptor(IHelpUIConstants.IMAGE_ADD_BOOKMARK));
930 		if (actionBars != null && actionBars.getMenuManager() != null)
931 			contributeToDropDownMenu(actionBars.getMenuManager());
932 
933 		roleFilter = new RoleFilter();
934 		uaFilter = new UAFilter();
935 		if (HelpBasePlugin.getActivitySupport().isUserCanToggleFiltering()) {
936 			showAllAction = new Action() {
937 
938 				@Override
939 				public void run() {
940 					BusyIndicator.showWhile(getControl().getDisplay(),
941 							() -> toggleShowAll(showAllAction.isChecked()));
942 				}
943 			};
944 			showAllAction.setImageDescriptor(HelpUIResources
945 					.getImageDescriptor(IHelpUIConstants.IMAGE_SHOW_ALL));
946 			showAllAction
947 					.setToolTipText(Messages.AllTopicsPart_showAll_tooltip);
948 			toolBarManager.insertBefore("back", showAllAction); //$NON-NLS-1$
949 			toolBarManager.insertBefore("back", new Separator()); //$NON-NLS-1$
950 			showAllAction.setChecked(!HelpBasePlugin.getActivitySupport()
951 					.isFilteringEnabled());
952 		}
953 	}
954 
getRoleFilter()955 	ViewerFilter getRoleFilter() {
956 		return roleFilter;
957 	}
958 
getUAFilter()959 	ViewerFilter getUAFilter() {
960 		return uaFilter;
961 	}
962 
963 	@Override
activityManagerChanged(ActivityManagerEvent activityManagerEvent)964 	public void activityManagerChanged(ActivityManagerEvent activityManagerEvent) {
965 		// pages is null when the activity manager listener is added, and is set to null
966 		// prior to the activity manager listener being removed, so very short timeframes in
967 		// logic where pages could equals null entering this method
968 		if (pages != null){
969 			for (int i = 0; i < pages.size(); i++) {
970 				HelpPartPage page = (HelpPartPage) pages.get(i);
971 				page.refilter();
972 			}
973 		}
974 	}
975 
isFilteredByRoles()976 	boolean isFilteredByRoles() {
977 		return HelpBasePlugin.getActivitySupport().isFilteringEnabled();
978 	}
979 
doBack()980 	private void doBack() {
981 		String id = getCurrentPageId();
982 		if (id.equals(IHelpUIConstants.HV_BROWSER_PAGE)) {
983 			// stop the browser
984 			BrowserPart part = (BrowserPart) findPart(IHelpUIConstants.HV_BROWSER);
985 			part.stop();
986 		}
987 		HistoryEntry entry = history.prev();
988 		if (entry != null)
989 			executeHistoryEntry(entry);
990 	}
991 
doNext()992 	private void doNext() {
993 		HistoryEntry entry = history.next();
994 		if (entry != null)
995 			executeHistoryEntry(entry);
996 	}
997 
executeHistoryEntry(HistoryEntry entry)998 	private void executeHistoryEntry(HistoryEntry entry) {
999 		history.setBlocked(true);
1000 		if (entry.getType() == HistoryEntry.PAGE) {
1001 			showPage(entry.getTarget(), true);
1002 			mform.setInput(entry.getData());
1003 		} else if (entry.getType() == HistoryEntry.URL) {
1004 			String relativeUrl = (String) entry.getData();
1005 			showURL(relativeUrl != null ? relativeUrl : entry.getTarget(), true);
1006 		}
1007 	}
1008 
createControl(Composite parent, FormToolkit toolkit)1009 	public void createControl(Composite parent, FormToolkit toolkit) {
1010 		ScrolledForm form = toolkit.createScrolledForm(parent);
1011 		form.getBody().setLayout(new HelpPartLayout());
1012 		mform = new ManagedForm(toolkit, form);
1013 		mform.getForm().setDelayedReflow(false);
1014 		toolkit.decorateFormHeading(mform.getForm().getForm());
1015 		MenuManager manager = new MenuManager();
1016 		IMenuListener listener = this::contextMenuAboutToShow;
1017 		manager.setRemoveAllWhenShown(true);
1018 		manager.addMenuListener(listener);
1019 		Menu contextMenu = manager.createContextMenu(form.getForm());
1020 		form.getForm().setMenu(contextMenu);
1021 		form.addListener(SWT.Activate, event -> lastActiveInstance = ReusableHelpPart.this);
1022 		//contributeToDropDownMenu(mform.getForm().getForm().getMenuManager());
1023 	}
1024 
showPage(String id)1025 	public HelpPartPage showPage(String id) {
1026 		String currentPageId = currentPage == null ? null : currentPage.getId();
1027 		if (id.equals(currentPageId))
1028 			return currentPage;
1029 		// If navigating away from the browser page clear
1030 		// its contents
1031 		if (IHelpUIConstants.HV_BROWSER_PAGE.equals(currentPageId)) {
1032 			BrowserPart part = (BrowserPart) findPart(IHelpUIConstants.HV_BROWSER);
1033 			part.clearBrowser();
1034 		}
1035 
1036 		HelpPartPage page = findPage(id);
1037 		if (page != null) {
1038 			page.refreshPage();
1039 			boolean success = flipPages(currentPage, page);
1040 			return success ? page : null;
1041 		}
1042 		return null;
1043 	}
1044 
showPage(String id, boolean setFocus)1045 	public HelpPartPage showPage(String id, boolean setFocus) {
1046 		HelpPartPage page = this.showPage(id);
1047 		if (page != null && setFocus)
1048 			page.setFocus();
1049 		return page;
1050 	}
1051 
startSearch(String phrase)1052 	public void startSearch(String phrase) {
1053 		showPage(IHelpUIConstants.HV_FSEARCH_PAGE, true);
1054 		SearchPart part = (SearchPart) findPart(IHelpUIConstants.HV_FSEARCH);
1055 		if (part != null && phrase != null)
1056 			part.startSearch(phrase);
1057 	}
1058 
showDynamicHelp(IWorkbenchPart wpart, Control c)1059 	public void showDynamicHelp(IWorkbenchPart wpart, Control c) {
1060 		showPage(IHelpUIConstants.HV_CONTEXT_HELP_PAGE, true);
1061 		RelatedTopicsPart part = (RelatedTopicsPart) findPart(IHelpUIConstants.HV_RELATED_TOPICS);
1062 		if (part != null) {
1063 			part.handleActivation(c, wpart);
1064 		}
1065 	}
1066 
flipPages(HelpPartPage oldPage, HelpPartPage newPage)1067 	private boolean flipPages(HelpPartPage oldPage, HelpPartPage newPage) {
1068 		if (newPage.canOpen() == false)
1069 			return false;
1070 		if (oldPage != null) {
1071 			oldPage.stop();
1072 			oldPage.setVisible(false);
1073 		}
1074 		mform.getForm().setText(null); //(newPage.getText());
1075 		mform.getForm().getForm().setSeparatorVisible(newPage.getText()!=null);
1076 		Image newImage=null;
1077 		//String iconId = newPage.getIconId();
1078 		//if (iconId != null)
1079 			//newImage = HelpUIResources.getImage(iconId);
1080 		mform.getForm().setImage(newImage);
1081 		newPage.setVisible(true);
1082 		toolBarManager.update(true);
1083 		currentPage = newPage;
1084 		if (mform.isStale())
1085 			mform.refresh();
1086 		mform.getForm().getBody().layout(true);
1087 		mform.reflow(true);
1088 		if (newPage.getId().equals(IHelpUIConstants.HV_BROWSER_PAGE) == false) {
1089 			if (!history.isBlocked()) {
1090 				history.addEntry(new HistoryEntry(HistoryEntry.PAGE, newPage
1091 						.getId(), null));
1092 			}
1093 			updateNavigation();
1094 		}
1095 		return true;
1096 	}
1097 
1098 	/*
1099 	 * void addPageHistoryEntry(String id, Object data) { if
1100 	 * (!history.isBlocked()) { history.addEntry(new
1101 	 * HistoryEntry(HistoryEntry.PAGE, id, data)); } updateNavigation(); }
1102 	 */
getCurrentPage()1103 	public HelpPartPage getCurrentPage() {
1104 		return currentPage;
1105 	}
1106 
getCurrentPageId()1107 	public String getCurrentPageId() {
1108 		return currentPage != null ? currentPage.getId() : null;
1109 	}
1110 
browserChanged(String url)1111 	void browserChanged(String url) {
1112 		if (!history.isBlocked()) {
1113 			try {
1114 				history.addEntry(new HistoryEntry(HistoryEntry.URL, url,
1115 						BaseHelpSystem.unresolve(new URL(url))));
1116 			} catch (MalformedURLException e) {
1117 				// Do not add to history
1118 			}
1119 		}
1120 		updateNavigation();
1121 	}
1122 
updateNavigation()1123 	private void updateNavigation() {
1124 		backAction.setEnabled(history.hasPrev());
1125 		nextAction.setEnabled(history.hasNext());
1126 		history.setBlocked(false);
1127 	}
1128 
isMonitoringContextHelp()1129 	public boolean isMonitoringContextHelp() {
1130 		return currentPage != null && (currentPage.getId().equals(HV_CONTEXT_HELP_PAGE)
1131 				|| currentPage.getId().equals(HV_BROWSER_PAGE));
1132 	}
1133 
getControl()1134 	public Control getControl() {
1135 		return mform.getForm();
1136 	}
1137 
getForm()1138 	public ManagedForm getForm() {
1139 		return mform;
1140 	}
1141 
reflow()1142 	public void reflow() {
1143 		mform.getForm().getBody().layout();
1144 		mform.reflow(true);
1145 	}
1146 
dispose()1147 	public void dispose() {
1148 		if (lastActiveInstance == this) {
1149 			lastActiveInstance = null;
1150 		}
1151 		for (int i = 0; i < pages.size(); i++) {
1152 			HelpPartPage page = (HelpPartPage) pages.get(i);
1153 			page.dispose();
1154 		}
1155 		pages = null;
1156 		if (mform != null) {
1157 			mform.dispose();
1158 			mform = null;
1159 		}
1160 		PlatformUI.getWorkbench().getActivitySupport().getActivityManager()
1161 				.removeActivityManagerListener(this);
1162 	}
1163 
1164 	/*
1165 	 * (non-Javadoc)
1166 	 *
1167 	 * @see org.eclipse.ui.internal.intro.impl.parts.IStandbyContentPart#setFocus()
1168 	 */
setFocus()1169 	public void setFocus() {
1170 		if (currentPage != null)
1171 			currentPage.setFocus();
1172 		else
1173 			mform.setFocus();
1174 	}
1175 
update(IWorkbenchPart part, Control control)1176 	public void update(IWorkbenchPart part, Control control) {
1177 		update(null, null, part, control, false);
1178 	}
1179 
1180 	/**
1181 	 * Called to update the related topics page in response to a
1182 	 * @param provider
1183 	 * @param context
1184 	 * @param part
1185 	 * @param control
1186 	 * @param isExplicitRequest is true if this is the result of a direct user request such as
1187 	 * pressing F1 and false if it is in response to a focus change listener
1188 	 */
update(IContextProvider provider, IContext context, IWorkbenchPart part, Control control, boolean isExplicitRequest)1189 	public void update(IContextProvider provider, IContext context, IWorkbenchPart part, Control control,
1190 			boolean isExplicitRequest) {
1191 		IContext realContext = context;
1192 		if (provider != null) {
1193 			realContext = provider.getContext(control);
1194 		}
1195 		if (realContext != null) {
1196 			String contextText = realContext.getText();
1197 			IHelpResource[] topics = realContext.getRelatedTopics();
1198 			if (contextText == null && topics.length == 1) {
1199 				showURL(topics[0].getHref());
1200 				return;
1201 			}
1202 		}
1203 		// Ensure that context help is currently showing
1204 		showPage(IHelpUIConstants.HV_CONTEXT_HELP_PAGE);
1205 		mform.setInput(new ContextHelpProviderInput(provider, context, control, part, isExplicitRequest));
1206 	}
1207 
createPart(String id, IToolBarManager tbm, IMenuManager menuManager)1208 	private IHelpPart createPart(String id, IToolBarManager tbm, IMenuManager menuManager) {
1209 		IHelpPart part = null;
1210 		Composite parent = mform.getForm().getBody();
1211 
1212 		part = findPart(id);
1213 		if (part != null)
1214 			return part;
1215 
1216 		if (id.equals(HV_TOPIC_TREE)) {
1217 			part = new AllTopicsPart(parent, mform.getToolkit(), tbm);
1218 		} else if (id.equals(HV_CONTEXT_HELP)) {
1219 			part = new ContextHelpPart(parent, mform.getToolkit());
1220 			((ContextHelpPart) part)
1221 					.setDefaultText(getDefaultContextHelpText());
1222 		} else if (id.equals(HV_RELATED_TOPICS)) {
1223 			part = new RelatedTopicsPart(parent, mform.getToolkit());
1224 			((RelatedTopicsPart) part)
1225 					.setDefaultText(getDefaultContextHelpText());
1226 		} else if (id.equals(HV_BROWSER)) {
1227 			part = new BrowserPart(parent, mform.getToolkit(), tbm, menuManager);
1228 		} else if (id.equals(HV_SEARCH_RESULT)) {
1229 			part = new DynamicHelpPart(parent, mform.getToolkit());
1230 		} else if (id.equals(HV_FSEARCH_RESULT)) {
1231 			part = new SearchResultsPart(parent, mform.getToolkit(), tbm);
1232 		} else if (id.equals(HV_SCOPE_SELECT)) {
1233 			part = new ScopeSelectPart(parent, mform.getToolkit());
1234 		} else if (id.equals(HV_SEE_ALSO)) {
1235 			part = new SeeAlsoPart(parent, mform.getToolkit());
1236 		} else if (id.equals(HV_FSEARCH)) {
1237 			part = new SearchPart(parent, mform.getToolkit());
1238 		} else if (id.equals(HV_BOOKMARKS_HEADER)) {
1239 			part = new BookmarkHeaderPart(parent, mform.getToolkit());
1240 		} else if (id.equals(HV_BOOKMARKS_TREE)) {
1241 			part = new BookmarksPart(parent, mform.getToolkit(), tbm);
1242 		} else if (id.equals(HV_INDEX)) {
1243 			part = new IndexPart(parent, mform.getToolkit(), tbm);
1244 		} else if (id.equals(HV_INDEX_TYPEIN)) {
1245 			part = new IndexTypeinPart(parent, mform.getToolkit(), tbm);
1246 		} else if (id.equals(HV_MISSING_CONTENT)) {
1247 			part = new MissingContentPart(parent, mform.getToolkit());
1248 		}
1249 		if (part != null) {
1250 			mform.addPart(part);
1251 			part.init(this, id, memento);
1252 		}
1253 		return part;
1254 	}
1255 
1256 	/**
1257 	 * @return Returns the runnableContext.
1258 	 */
getRunnableContext()1259 	public IRunnableContext getRunnableContext() {
1260 		return runnableContext;
1261 	}
1262 
isInWorkbenchWindow()1263 	public boolean isInWorkbenchWindow() {
1264 		return runnableContext instanceof IWorkbenchWindow;
1265 	}
1266 
1267 	/**
1268 	 * @return Returns the defaultContextHelpText.
1269 	 */
getDefaultContextHelpText()1270 	public String getDefaultContextHelpText() {
1271 		return defaultContextHelpText;
1272 	}
1273 
1274 	/**
1275 	 * @param defaultContextHelpText
1276 	 *            The defaultContextHelpText to set.
1277 	 */
setDefaultContextHelpText(String defaultContextHelpText)1278 	public void setDefaultContextHelpText(String defaultContextHelpText) {
1279 		this.defaultContextHelpText = defaultContextHelpText;
1280 	}
1281 
showURL(final String url)1282 	public void showURL(final String url) {
1283 		BusyIndicator.showWhile(getControl().getDisplay(), () -> showURL(url, getShowDocumentsInPlace()));
1284 	}
1285 
showURL(String url, boolean replace)1286 	public void showURL(String url, boolean replace) {
1287 		if (url == null)
1288 			return;
1289 		if (url.startsWith("nw:")) { //$NON-NLS-1$
1290 			replace = false;
1291 			url = url.substring(3);
1292 		}
1293 		else if (url.startsWith("open:")) { //$NON-NLS-1$
1294 			int col = url.indexOf(':');
1295 			int qloc = url.indexOf('?');
1296 			String engineId = url.substring(col+1, qloc);
1297 			EngineDescriptor desc = getEngineManager().findEngine(engineId);
1298 			if (desc==null)
1299 				return;
1300 			HashMap<String, Object> args = new HashMap<>();
1301 			HelpURLConnection.parseQuery(url.substring(qloc+1), args);
1302 			((ISearchEngine2)desc.getEngine()).open((String)args.get("id")); //$NON-NLS-1$
1303 			return;
1304 		}
1305 		if (replace) {
1306 			if (openInternalBrowser(url))
1307 				return;
1308 		}
1309 		showExternalURL(url);
1310 	}
1311 
openInternalBrowser(String url)1312 	private boolean openInternalBrowser(String url) {
1313 		String openMode = Platform.getPreferencesService().getString(HelpBasePlugin.PLUGIN_ID,
1314 				 IHelpBaseConstants.P_KEY_HELP_VIEW_OPEN_MODE, IHelpBaseConstants.P_IN_PLACE, null);
1315 		boolean openInEditor = IHelpBaseConstants.P_IN_EDITOR.equals(openMode);
1316 		boolean openInBrowser = IHelpBaseConstants.P_IN_BROWSER.equals(openMode);
1317 		Shell windowShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
1318 		Shell helpShell = mform.getForm().getShell();
1319 		boolean isDialog = (helpShell != windowShell);
1320 		if (!isDialog && openInEditor) {
1321 			return DefaultHelpUI.showInWorkbenchBrowser(url, true);
1322 		}
1323 		if (openInBrowser) {
1324 			BaseHelpSystem.getHelpDisplay().displayHelpResource(url, false);
1325 			return true;
1326 		}
1327 		showPage(IHelpUIConstants.HV_BROWSER_PAGE);
1328 		BrowserPart bpart = (BrowserPart) findPart(IHelpUIConstants.HV_BROWSER);
1329 		if (bpart != null) {
1330 			bpart.showURL(BaseHelpSystem
1331 					.resolve(url, "/help/ntopic").toString()); //$NON-NLS-1$
1332 			return true;
1333 		}
1334 		return false;
1335 	}
1336 
showExternalURL(String url)1337 	public void showExternalURL(String url) {
1338 		if (isHelpResource(url))
1339 			PlatformUI.getWorkbench().getHelpSystem().displayHelpResource(url);
1340 		else {
1341 			try {
1342 				String aurl = BaseHelpSystem.resolve(url, true).toString();
1343 				/*
1344 				/* Previous code before fix for Bug 192750
1345 				if (aurl.endsWith("&noframes=true") || aurl.endsWith("?noframes=true")) //$NON-NLS-1$ //$NON-NLS-2$
1346 					aurl = aurl.substring(0, aurl.length() - 14);
1347 				DefaultHelpUI.showInWorkbenchBrowser(aurl, false);
1348 				*/
1349 
1350 				PlatformUI.getWorkbench().getHelpSystem().displayHelpResource(aurl);
1351 
1352 			} catch (Exception e) {
1353 				Platform.getLog(getClass()).error("Error opening browser", e); //$NON-NLS-1$
1354 			}
1355 		}
1356 	}
1357 
findPart(String id)1358 	public IHelpPart findPart(String id) {
1359 		if (mform == null)
1360 			return null;
1361 		IFormPart[] parts = mform.getParts();
1362 		for (int i = 0; i < parts.length; i++) {
1363 			IHelpPart part = (IHelpPart) parts[i];
1364 			if (part.getId().equals(id))
1365 				return part;
1366 		}
1367 		return null;
1368 	}
1369 
isHelpResource(String url)1370 	public boolean isHelpResource(String url) {
1371 		if (url == null || !url.contains("://")) //$NON-NLS-1$
1372 			return true;
1373 		return false;
1374 	}
1375 
contextMenuAboutToShow(IMenuManager manager)1376 	private void contextMenuAboutToShow(IMenuManager manager) {
1377 		IFormPart[] parts = mform.getParts();
1378 		boolean hasContext = false;
1379 		Control focusControl = getControl().getDisplay().getFocusControl();
1380 		for (int i = 0; i < parts.length; i++) {
1381 			IHelpPart part = (IHelpPart) parts[i];
1382 			if (part.hasFocusControl(focusControl)) {
1383 				hasContext = part.fillContextMenu(manager);
1384 				break;
1385 			}
1386 		}
1387 		if (hasContext)
1388 			manager.add(new Separator());
1389 		manager.add(backAction);
1390 		manager.add(nextAction);
1391 		manager.add(new Separator());
1392 		manager.add(openInfoCenterAction);
1393 	}
1394 
contributeToDropDownMenu(IMenuManager manager)1395 	private void contributeToDropDownMenu(IMenuManager manager) {
1396 		addPageAction(manager, IHelpUIConstants.HV_CONTEXT_HELP_PAGE);
1397 		addPageAction(manager, IHelpUIConstants.HV_ALL_TOPICS_PAGE);
1398 		addPageAction(manager, IHelpUIConstants.HV_INDEX_PAGE);
1399 		addPageAction(manager, IHelpUIConstants.HV_FSEARCH_PAGE);
1400 		addPageAction(manager, IHelpUIConstants.HV_BOOKMARKS_PAGE);
1401 	}
1402 
addPageAction(IMenuManager manager, final String pageId)1403 	private void addPageAction(IMenuManager manager, final String pageId) {
1404 		// String cid = getCurrentPageId();
1405 		HelpPartPage page = findPage(pageId);
1406 		if (page == null)
1407 			return;
1408 		Action action = new Action(pageId, IAction.AS_CHECK_BOX) {
1409 
1410 			@Override
1411 			public void run() {
1412 				BusyIndicator.showWhile(mform.getForm().getDisplay(),
1413 						() -> showPage(pageId));
1414 			}
1415 		};
1416 		action.setText(page.getText());
1417 		String iconId = page.getIconId();
1418 		if (iconId != null)
1419 			action.setImageDescriptor(HelpUIResources
1420 					.getImageDescriptor(iconId));
1421 		manager.add(action);
1422 		page.pageAction = action;
1423 	}
1424 
findPage(String id)1425 	private HelpPartPage findPage(String id) {
1426 		for (int i = 0; i < pages.size(); i++) {
1427 			HelpPartPage page = (HelpPartPage) pages.get(i);
1428 			if (page.getId().equals(id)) {
1429 				return page;
1430 			}
1431 		}
1432 		return null;
1433 	}
1434 
fillSelectionProviderMenu(ISelectionProvider provider, IMenuManager manager, boolean addBookmarks)1435 	boolean fillSelectionProviderMenu(ISelectionProvider provider,
1436 			IMenuManager manager, boolean addBookmarks) {
1437 		boolean value = fillOpenActions(provider, manager);
1438 		if (value && addBookmarks) {
1439 			manager.add(new Separator());
1440 			bookmarkAction.setTarget(provider);
1441 			manager.add(bookmarkAction);
1442 		}
1443 		return true;
1444 	}
1445 
fillOpenActions(Object target, IMenuManager manager)1446 	private boolean fillOpenActions(Object target, IMenuManager manager) {
1447 		String href = getHref(target);
1448 		if (href != null && !href.startsWith("__")) { //$NON-NLS-1$
1449 			openAction.setTarget(target);
1450 			manager.add(openAction);
1451 			if (!href.startsWith("nw:") && !href.startsWith("open:")) { //$NON-NLS-1$ //$NON-NLS-2$
1452 				openInHelpAction.setTarget(target);
1453 				manager.add(openInHelpAction);
1454 			}
1455 			return true;
1456 		}
1457 		return false;
1458 	}
1459 
hookFormText(FormText text)1460 	void hookFormText(FormText text) {
1461 		copyAction.hook(text);
1462 	}
1463 
unhookFormText(FormText text)1464 	void unhookFormText(FormText text) {
1465 		copyAction.unhook(text);
1466 	}
1467 
fillFormContextMenu(FormText text, IMenuManager manager)1468 	boolean fillFormContextMenu(FormText text, IMenuManager manager) {
1469 		if (fillOpenActions(text, manager))
1470 			manager.add(new Separator());
1471 		manager.add(copyAction);
1472 		copyAction.setTarget(text);
1473 		if (text.getSelectedLinkHref() != null) {
1474 			manager.add(new Separator());
1475 			manager.add(bookmarkAction);
1476 			bookmarkAction.setTarget(getResource(text));
1477 		}
1478 		return true;
1479 	}
1480 
getCopyAction()1481 	IAction getCopyAction() {
1482 		return copyAction;
1483 	}
1484 
getHref(Object target)1485 	private String getHref(Object target) {
1486 		if (target instanceof ISelectionProvider) {
1487 			ISelectionProvider provider = (ISelectionProvider) target;
1488 			IStructuredSelection ssel = (IStructuredSelection) provider
1489 					.getSelection();
1490 			Object obj = ssel.getFirstElement();
1491 			if (obj instanceof IToc)
1492 				return null;
1493 			if (obj instanceof IHelpResource) {
1494 				IHelpResource res = (IHelpResource) obj;
1495 				return res.getHref();
1496 			}
1497 			if (obj instanceof IIndexEntry) {
1498 				/*
1499 				 * if index entry has single topic
1500 				 * it represents the topic by itself
1501 				 */
1502 				IHelpResource[] topics = ((IIndexEntry) obj).getTopics();
1503 				if (topics.length == 1)
1504 					return topics[0].getHref();
1505 				return null;
1506 			}
1507 		} else if (target instanceof FormText) {
1508 			FormText text = (FormText) target;
1509 			Object href = text.getSelectedLinkHref();
1510 			if (href != null)
1511 				return href.toString();
1512 		}
1513 		return null;
1514 	}
1515 
getResource(Object target)1516 	private IHelpResource getResource(Object target) {
1517 		if (target instanceof ISelectionProvider) {
1518 			ISelectionProvider provider = (ISelectionProvider) target;
1519 			IStructuredSelection ssel = (IStructuredSelection) provider
1520 					.getSelection();
1521 			Object obj = ssel.getFirstElement();
1522 			if (obj instanceof ITopic) {
1523 				return (ITopic) obj;
1524 			} else if (obj instanceof IIndexEntry) {
1525 				/*
1526 				 * if index entry has single topic
1527 				 * it represents the topic by itself
1528 				 */
1529 				IIndexEntry entry = (IIndexEntry) obj;
1530 				IHelpResource[] topics = entry.getTopics();
1531 				if (topics.length == 1) {
1532 					final String href = topics[0].getHref();
1533 					final String label = entry.getKeyword();
1534 					return new IHelpResource() {
1535 
1536 						@Override
1537 						public String getHref() {
1538 							return href;
1539 						}
1540 
1541 						@Override
1542 						public String getLabel() {
1543 							return label;
1544 						}
1545 					};
1546 				}
1547 				return null;
1548 			} else if (obj instanceof IHelpResource) {
1549 				return (IHelpResource) obj;
1550 			}
1551 		} else if (target instanceof FormText) {
1552 			FormText text = (FormText) target;
1553 			String rawHref = text.getSelectedLinkHref().toString();
1554 			final String href = rawHref.startsWith("open") ? rawHref : //$NON-NLS-1$
1555 				LinkUtil.stripParams(text.getSelectedLinkHref().toString());
1556 			final String label = text.getSelectedLinkText();
1557 			if (href != null) {
1558 				return new IHelpResource() {
1559 
1560 					@Override
1561 					public String getHref() {
1562 						return href;
1563 					}
1564 
1565 					@Override
1566 					public String getLabel() {
1567 						return label;
1568 					}
1569 				};
1570 			}
1571 		}
1572 		return null;
1573 	}
1574 
1575 	private void doBookmark(Object target) {
1576 		IHelpResource res = null;
1577 		if (target instanceof IHelpResource) {
1578 			res = (IHelpResource)target;
1579 		}
1580 		else {
1581 			res = getResource(target);
1582 		}
1583 		if (res != null) {
1584 			BaseHelpSystem.getBookmarkManager().addBookmark(res.getHref(),
1585 					res.getLabel());
1586 		}
1587 	}
1588 
1589 	/*
1590 	 * private void doOpen(Object target) { String href = getHref(target); if
1591 	 * (href != null) showURL(href, getShowDocumentsInPlace()); }
1592 	 */
1593 
1594 	private void doOpen(Object target, boolean replace) {
1595 		String href = getHref(target);
1596 		if (href != null)
1597 			showURL(href, replace);
1598 	}
1599 
1600 	private void doOpenInHelp(Object target) {
1601 		String href = getHref(target);
1602 		if (href != null)
1603 			// WorkbenchHelp.displayHelpResource(href);
1604 			showURL(href, false);
1605 	}
1606 
1607 	/**
1608 	 * @return Returns the statusLineManager.
1609 	 */
1610 	public IStatusLineManager getStatusLineManager() {
1611 		return statusLineManager;
1612 	}
1613 
1614 	/**
1615 	 * @return Returns the showDocumentsInPlace.
1616 	 */
1617 	public boolean getShowDocumentsInPlace() {
1618 		return showDocumentsInPlace;
1619 	}
1620 
1621 	/**
1622 	 * @param showDocumentsInPlace
1623 	 *            The showDocumentsInPlace to set.
1624 	 */
1625 	public void setShowDocumentsInPlace(boolean showDocumentsInPlace) {
1626 		this.showDocumentsInPlace = showDocumentsInPlace;
1627 	}
1628 
1629 	/**
1630 	 * @return Returns the style.
1631 	 */
1632 	public int getStyle() {
1633 		return style;
1634 	}
1635 
1636 	public int getNumberOfInPlaceHits() {
1637 		return numberOfInPlaceHits;
1638 	}
1639 
1640 	public void setNumberOfInPlaceHits(int numberOfInPlaceHits) {
1641 		this.numberOfInPlaceHits = numberOfInPlaceHits;
1642 	}
1643 
1644 	void handleLinkEntered(HyperlinkEvent e) {
1645 		IStatusLineManager mng = getRoot(getStatusLineManager());
1646 		if (mng != null) {
1647 			String label = e.getLabel();
1648 			String href = (String) e.getHref();
1649 			if (href != null && href.startsWith("__")) //$NON-NLS-1$
1650 				href = null;
1651 			if (href != null) {
1652 				try {
1653 					href = URLDecoder.decode(href, "UTF-8"); //$NON-NLS-1$
1654 				} catch (UnsupportedEncodingException ex) {
1655 				}
1656 				// Next line unnecessary following fix for Bug 78746
1657 				//href = href.replaceAll("&", "&&"); //$NON-NLS-1$ //$NON-NLS-2$
1658 			}
1659 			if (label != null && href != null) {
1660 				String message = NLS.bind(Messages.ReusableHelpPart_status,
1661 						label, href);
1662 				mng.setMessage(message);
1663 			} else if (label != null)
1664 				mng.setMessage(label);
1665 			else
1666 				mng.setMessage(href);
1667 		}
1668 	}
1669 
1670 	private IStatusLineManager getRoot(IStatusLineManager mng) {
1671 		while (mng != null) {
1672 			if (mng instanceof SubStatusLineManager) {
1673 				SubStatusLineManager smng = (SubStatusLineManager) mng;
1674 				IContributionManager parent = smng.getParent();
1675 				if (parent == null)
1676 					return smng;
1677 				if (!(parent instanceof IStatusLineManager))
1678 					return smng;
1679 				mng = (IStatusLineManager) parent;
1680 			} else
1681 				break;
1682 		}
1683 		return mng;
1684 	}
1685 
1686 	void handleLinkExited(HyperlinkEvent e) {
1687 		IStatusLineManager mng = getRoot(getStatusLineManager());
1688 		if (mng != null)
1689 			mng.setMessage(null);
1690 	}
1691 
1692 
1693 
1694 	private void toggleShowAll(boolean checked) {
1695 		if (checked) {
1696 			IPreferenceStore store = HelpUIPlugin.getDefault()
1697 					.getPreferenceStore();
1698 			String value = store.getString(PROMPT_KEY);
1699 			if (value.length() == 0) {
1700 				MessageDialogWithToggle dialog = MessageDialogWithToggle
1701 						.openOkCancelConfirm(null,
1702 								Messages.AskShowAll_dialogTitle,
1703 								getShowAllMessage(),
1704 								Messages.AskShowAll_toggleMessage, false,
1705 								store, PROMPT_KEY);
1706 				if (dialog.getReturnCode() != MessageDialogWithToggle.OK) {
1707 					showAllAction.setChecked(false);
1708 					return;
1709 				}
1710 			}
1711 		}
1712 		HelpBasePlugin.getActivitySupport().setFilteringEnabled(!checked);
1713 		for (int i = 0; i < pages.size(); i++) {
1714 			HelpPartPage page = (HelpPartPage) pages.get(i);
1715 			page.toggleRoleFilter();
1716 		}
1717 	}
1718 
1719 	public void saveState(IMemento memento) {
1720 		for (int i = 0; i < pages.size(); i++) {
1721 			HelpPartPage page = (HelpPartPage) pages.get(i);
1722 			page.saveState(memento);
1723 		}
1724 	}
1725 
1726 	private String getShowAllMessage() {
1727 		String message = HelpBasePlugin.getActivitySupport()
1728 				.getShowAllMessage();
1729 		if (message == null)
1730 			return Messages.AskShowAll_message;
1731 		StringBuilder buff = new StringBuilder();
1732 		int state = STATE_START;
1733 
1734 		for (int i = 0; i < message.length(); i++) {
1735 			char c = message.charAt(i);
1736 			switch (state) {
1737 			case STATE_START:
1738 				if (c == '<')
1739 					state = STATE_LT;
1740 				else
1741 					buff.append(c);
1742 				break;
1743 			case STATE_LT:
1744 				if (c == 'b' || c == 'B')
1745 					state = STATE_LT_B;
1746 				break;
1747 			case STATE_LT_B:
1748 				if (c == 'r' || c == 'R')
1749 					state = STATE_LT_BR;
1750 				break;
1751 			case STATE_LT_BR:
1752 				if (c == '>') {
1753 					buff.append('\n');
1754 				}
1755 				state = STATE_START;
1756 				break;
1757 			default:
1758 				buff.append(c);
1759 			}
1760 		}
1761 		return buff.toString();
1762 	}
1763 
1764 	EngineDescriptorManager getEngineManager() {
1765 		if (engineManager==null) {
1766 			engineManager = new EngineDescriptorManager();
1767 		}
1768 		return engineManager;
1769 	}
1770 
1771 	static public int getDefaultStyle() {
1772 		int style = ALL_TOPICS | CONTEXT_HELP | SEARCH;
1773 		if (ProductPreferences.getBoolean(HelpBasePlugin.getDefault(), "indexView")) { //$NON-NLS-1$
1774 			style |= INDEX;
1775 		}
1776 		if (ProductPreferences.getBoolean(HelpBasePlugin.getDefault(), "bookmarksView")) { //$NON-NLS-1$
1777 			style |= BOOKMARKS;
1778 		}
1779 		return style;
1780 	}
1781 
1782 	public void checkRemoteStatus() {
1783 		clearBrowser();
1784 		showURL("/org.eclipse.help.webapp/" + MissingContentManager.REMOTE_STATUS_HELP_VIEW_HREF); //$NON-NLS-1$
1785 		updateStatusLinks();
1786 	}
1787 
1788 	public void checkPlaceholderStatus() {
1789 		clearBrowser();
1790 		showURL("/org.eclipse.help.webapp/" + MissingContentManager.MISSING_BOOKS_HELP_VIEW_HREF); //$NON-NLS-1$
1791 		updateStatusLinks();
1792 
1793 	}
1794 
1795 	private void clearBrowser() {
1796 		IHelpPart part = findPart(HV_BROWSER);
1797 		if ( part == null ) {
1798 			return;
1799 		}
1800 		BrowserPart browserPart = (BrowserPart) part;
1801 		browserPart.clearBrowser();
1802 	}
1803 
1804 	private void updateStatusLinks() {
1805 		IHelpPart part = findPart(HV_MISSING_CONTENT);
1806 		if ( part == null ) {
1807 			return;
1808 		}
1809 		MissingContentPart mcPart = (MissingContentPart) part;
1810 		mcPart.updateStatus();
1811 	}
1812 }
1813 
1814