NEW - bug 134165: provide find functionality for task editor
https://bugs.eclipse.org/bugs/show_bug.cgi?id=134165
diff --git a/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleBugzillaTaskEditorPage.java b/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleBugzillaTaskEditorPage.java
index b10d497..41392a7 100644
--- a/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleBugzillaTaskEditorPage.java
+++ b/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleBugzillaTaskEditorPage.java
@@ -8,17 +8,46 @@
 
 package org.eclipse.mylyn.internal.sandbox.ui.editors;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ControlContribution;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.FindReplaceDocumentAdapter;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.TextViewer;
 import org.eclipse.mylyn.internal.bugzilla.ui.editor.BugzillaTaskEditorPage;
+import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages;
+import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil;
 import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
 import org.eclipse.mylyn.tasks.ui.editors.AbstractAttributeEditor;
+import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
 import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPart;
 import org.eclipse.mylyn.tasks.ui.editors.AttributeEditorFactory;
 import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
 import org.eclipse.mylyn.tasks.ui.editors.TaskEditorPartDescriptor;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.actions.ActionFactory;
 import org.eclipse.ui.contexts.IContextService;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.IFormPage;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
 
 /**
  * A bugzilla task editor page that has wiki facilities.
@@ -27,10 +56,82 @@
  */
 public class ExtensibleBugzillaTaskEditorPage extends BugzillaTaskEditorPage {
 
+	private Action toggleFindAction;
+
+	private static final Color HIGHLIGHTER_YELLOW = new Color(Display.getDefault(), 255, 238, 99);
+
+	private static final StyleRange HIGHLIGHT_STYLE_RANGE = new StyleRange(0, 0, null, HIGHLIGHTER_YELLOW);
+
 	public ExtensibleBugzillaTaskEditorPage(TaskEditor editor) {
 		super(editor);
 	}
 
+	private void addFindAction(IToolBarManager toolBarManager) {
+		toolBarManager.add(new Separator("find"));
+
+		if (toggleFindAction != null && toggleFindAction.isChecked()) {
+			ControlContribution findTextboxControl = new ControlContribution("Find") {
+
+				@Override
+				protected Control createControl(Composite parent) {
+					FormToolkit toolkit = getTaskEditor().getHeaderForm().getToolkit();
+					Composite findComposite = toolkit.createComposite(parent);
+					findComposite.setLayout(new RowLayout());
+					findComposite.setBackground(null);
+
+					final Text findTextbox = toolkit.createText(findComposite, "", SWT.FLAT);
+					findTextbox.setLayoutData(new RowData(100, SWT.DEFAULT));
+					findTextbox.setFocus();
+					toolkit.adapt(findTextbox, false, false);
+					findTextbox.addModifyListener(new ModifyListener() {
+						public void modifyText(ModifyEvent e) {
+							try {
+								setReflow(false);
+								findAndHighlight(ExtensibleBugzillaTaskEditorPage.this, findTextbox.getText());
+								// always toggle attachment part close after every search, since all ExpandableComposites are open
+								AbstractTaskEditorPart attachmentsPart = getPart(AbstractTaskEditorPage.ID_PART_ATTACHMENTS);
+								EditorUtil.toggleExpandableComposite(false,
+										(ExpandableComposite) attachmentsPart.getControl());
+							} finally {
+								setReflow(true);
+							}
+							reflow();
+						}
+					});
+
+					return findComposite;
+				}
+
+			};
+			toolBarManager.add(findTextboxControl);
+		}
+
+		if (toggleFindAction == null) {
+			toggleFindAction = new Action("", SWT.TOGGLE) {
+				@Override
+				public void run() {
+					getTaskEditor().updateHeaderToolBar();
+				}
+
+			};
+			toggleFindAction.setImageDescriptor(CommonImages.FIND);
+			toggleFindAction.setToolTipText("Find");
+			//getManagedForm().getForm().setData(TaskEditorFindHandler.KEY_FIND_ACTION, toggleFindAction);
+		}
+
+		toolBarManager.add(toggleFindAction);
+
+	}
+
+	@Override
+	public boolean canPerformAction(String actionId) {
+		if (actionId.equals(ActionFactory.FIND.getId())) {
+			return true;
+		}
+
+		return super.canPerformAction(actionId);
+	}
+
 	@Override
 	protected AttributeEditorFactory createAttributeEditorFactory() {
 		final AttributeEditorFactory bugzillaFactory = super.createAttributeEditorFactory();
@@ -72,4 +173,102 @@
 
 		return descriptors;
 	}
+
+	@Override
+	public void doAction(String actionId) {
+		if (actionId.equals(ActionFactory.FIND.getId())) {
+			if (toggleFindAction != null) {
+				toggleFindAction.setChecked(!toggleFindAction.isChecked());
+				toggleFindAction.run();
+			}
+		}
+		super.doAction(actionId);
+	}
+
+	@Override
+	public void fillToolBar(IToolBarManager toolBarManager) {
+		super.fillToolBar(toolBarManager);
+
+		addFindAction(toolBarManager);
+	}
+
+	private static void findTextViewerControl(Composite composite, List<TextViewer> found) {
+		if (!composite.isDisposed()) {
+			for (Control child : composite.getChildren()) {
+				TextViewer viewer = EditorUtil.getTextViewer(child);
+				if (viewer != null && viewer.getDocument().get().length() > 0) {
+					found.add(viewer);
+				}
+
+				// have to do this since TaskEditorCommentPart.expendComment(..) will dispose the TextViewer when the ExpandableComposite is close
+				if (child instanceof ExpandableComposite) {
+					EditorUtil.toggleExpandableComposite(true, (ExpandableComposite) child);
+				}
+
+				if (child instanceof Composite) {
+					findTextViewerControl((Composite) child, found);
+				}
+			}
+		}
+	}
+
+	private static boolean findAndHighlightTextViewer(TextViewer viewer, FindReplaceDocumentAdapter adapter,
+			String findString, int startOffset) throws BadLocationException {
+		IRegion matchRegion = adapter.find(startOffset, findString, true, false, false, false);
+
+		if (matchRegion != null) {
+			int widgetOffset = matchRegion.getOffset();
+			int length = matchRegion.getLength();
+			HIGHLIGHT_STYLE_RANGE.start = widgetOffset;
+			HIGHLIGHT_STYLE_RANGE.length = length;
+			viewer.getTextWidget().setStyleRange(HIGHLIGHT_STYLE_RANGE);
+
+			findAndHighlightTextViewer(viewer, adapter, findString, widgetOffset + length);
+
+			return true;
+		}
+
+		return false;
+	}
+
+	public static void findAndHighlight(IFormPage page, String findString) {
+		IManagedForm form = page.getManagedForm();
+		if (form == null) {
+			return;
+		}
+		ScrolledForm scrolledForm = form.getForm();
+		if (scrolledForm == null) {
+			return;
+		}
+
+		List<TextViewer> found = new ArrayList<TextViewer>();
+		findTextViewerControl(scrolledForm.getBody(), found);
+
+		for (TextViewer viewer : found) {
+			try {
+				// Wiping previous highlighted element
+				viewer.setRedraw(false);
+				viewer.refresh();
+				viewer.setRedraw(true);
+
+				FindReplaceDocumentAdapter adapter = new FindReplaceDocumentAdapter(viewer.getDocument());
+
+				if (!findAndHighlightTextViewer(viewer, adapter, findString, -1)) {
+					// toggle close if can't match the keyword
+					Composite comp = viewer.getControl().getParent();
+					while (comp != null) {
+						if (comp instanceof ExpandableComposite) {
+							ExpandableComposite ex = (ExpandableComposite) comp;
+							EditorUtil.toggleExpandableComposite(false, ex);
+							break;
+						}
+						comp = comp.getParent();
+					}
+				}
+			} catch (BadLocationException e) {
+				//ignore
+			}
+		}
+	}
+
 }