{"thread":{"id":"12919","subject":"[EGIT PATCH 2/4] FindToolbar port to the new history page.","startedAt":"2008-03-30T15:18:45Z","lastAt":"2008-04-01T04:02:41Z","messageCount":4,"participants":["Roger C. Soares","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"73356","messageId":"1206890325-3732-1-git-send-email-rogersoares@intelinet.com.br","threadId":"12919","inReplyTo":null,"subject":"[EGIT PATCH 2/4] FindToolbar port to the new history page.","fromName":"Roger C. Soares","fromEmail":"rogersoares@intelinet.com.br","sentAt":"2008-03-30T15:18:45Z","receivedAt":"2008-03-30T15:18:45Z","isPatch":true,"sender":{"key":"rogersoares@intelinet.com.br","avatar":null},"body":"The find toolbar already generates a list of table rows that need\nhighlighting. The hightlight flag + RevFilter solution currently\ndoesn't support all the features that the find toolbar has, so for\nthis port the toolbar is replacing the highlight flag.\n\nSigned-off-by: Roger C. Soares <rogersoares@intelinet.com.br>\n---\n .../egit/ui/internal/history/CommitGraphTable.java |   27 +-\n .../egit/ui/internal/history/FindResults.java      |  184 ++++++++\n .../egit/ui/internal/history/FindToolbar.java      |  457 ++++++++++++++++++++\n .../ui/internal/history/FindToolbarThread.java     |  237 ++++++++++\n .../egit/ui/internal/history/GitHistoryPage.java   |   65 +++-\n 5 files changed, 948 insertions(+), 22 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindResults.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbar.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbarThread.java\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/CommitGraphTable.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/CommitGraphTable.java\nindex 6559d64..d20db77 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/CommitGraphTable.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/CommitGraphTable.java\n@@ -49,7 +49,6 @@ import org.spearce.egit.ui.UIPreferences;\n import org.spearce.egit.ui.UIText;\n import org.spearce.jgit.revplot.PlotCommit;\n import org.spearce.jgit.revwalk.RevCommit;\n-import org.spearce.jgit.revwalk.RevFlag;\n \n class CommitGraphTable {\n \tprivate static Font highlightFont() {\n@@ -71,6 +70,8 @@ class CommitGraphTable {\n \n \tprivate final TableViewer table;\n \n+\tprivate final Table rawTable;\n+\n \tprivate Clipboard clipboard;\n \n \tprivate final SWTPlotRenderer renderer;\n@@ -81,14 +82,14 @@ class CommitGraphTable {\n \n \tprivate SWTCommitList allCommits;\n \n-\tprivate RevFlag highlight;\n+\tprivate FindResults findResults;\n \n \tCommitGraphTable(final Composite parent) {\n \t\tnFont = Activator.getFont(UIPreferences.THEME_CommitGraphNormalFont);\n \t\thFont = highlightFont();\n \n-\t\tfinal Table rawTable = new Table(parent, SWT.MULTI | SWT.H_SCROLL\n-\t\t\t\t| SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL);\n+\t\trawTable = new Table(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL\n+\t\t\t\t| SWT.BORDER | SWT.FULL_SELECTION | SWT.VIRTUAL);\n \t\trawTable.setHeaderVisible(true);\n \t\trawTable.setLinesVisible(false);\n \t\trawTable.setFont(nFont);\n@@ -159,10 +160,10 @@ class CommitGraphTable {\n \t\t\t\tnew Transfer[] { TextTransfer.getInstance() }, DND.CLIPBOARD);\n \t}\n \n-\tvoid setInput(final RevFlag hFlag, final SWTCommitList list,\n+\tvoid setInput(final FindResults fResults, final SWTCommitList list,\n \t\t\tfinal SWTCommit[] asArray) {\n \t\tfinal SWTCommitList oldList = allCommits;\n-\t\thighlight = hFlag;\n+\t\tfindResults = fResults;\n \t\tallCommits = list;\n \t\ttable.setInput(asArray);\n \t\tif (asArray != null && asArray.length > 0) {\n@@ -211,8 +212,9 @@ class CommitGraphTable {\n \t}\n \n \tvoid doPaint(final Event event) {\n-\t\tfinal RevCommit c = (RevCommit) ((TableItem) event.item).getData();\n-\t\tif (highlight != null && c.has(highlight))\n+\t\tTableItem ti = (TableItem) event.item;\n+\t\tfinal RevCommit c = (RevCommit) ti.getData();\n+\t\tif (findResults != null && findResults.isFoundAt(rawTable.indexOf(ti)))\n \t\t\tevent.gc.setFont(hFont);\n \t\telse\n \t\t\tevent.gc.setFont(nFont);\n@@ -232,4 +234,13 @@ class CommitGraphTable {\n \t\tfinal int texty = (event.height - textsz.y) / 2;\n \t\tevent.gc.drawString(txt, event.x, event.y + texty);\n \t}\n+\n+\t/**\n+\t * Returns the SWT Table that backs this TableViewer.\n+\t *\n+\t * @return Table the SWT Table\n+\t */\n+\tpublic Table getRawTable() {\n+\t\treturn rawTable;\n+\t}\n }\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindResults.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindResults.java\nnew file mode 100644\nindex 0000000..0202a70\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindResults.java\n@@ -0,0 +1,184 @@\n+/*\n+ *  Copyright (C) 2008  Roger C. Soares\n+ *\n+ *  This library is free software; you can redistribute it and/or\n+ *  modify it under the terms of the GNU Lesser General Public\n+ *  License, version 2.1, as published by the Free Software Foundation.\n+ *\n+ *  This library is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ *  Lesser General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU Lesser General Public\n+ *  License along with this library; if not, write to the Free Software\n+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\n+ */\n+package org.spearce.egit.ui.internal.history;\n+\n+import java.util.Arrays;\n+import java.util.Iterator;\n+import java.util.LinkedHashMap;\n+import java.util.Map;\n+\n+/**\n+ * Results for the find toolbar. This object stores the rows in the history\n+ * table that contain a match to a given pattern.\n+ *\n+ * @see FindToolbar\n+ * @see FindToolbarThread\n+ */\n+public class FindResults {\n+\tprivate Map<Integer, Integer> matchesMap = new LinkedHashMap<Integer, Integer>();\n+\n+\tInteger[] keysArray;\n+\n+\tprivate int matchesCount;\n+\n+\t/**\n+\t * Returns if the index in the history table matches the find pattern.\n+\t *\n+\t * @param index\n+\t *            history table item index.\n+\t * @return boolean <code>true</code> if the history table\n+\t *         <code>index</code> contains a match to the find pattern,\n+\t *         <code>false</code> otherwise\n+\t */\n+\tpublic synchronized boolean isFoundAt(int index) {\n+\t\treturn matchesMap.containsKey(new Integer(index));\n+\t}\n+\n+\t/**\n+\t * Returns the first table item index after <code>index</code> that\n+\t * contains a match to the find pattern.\n+\t *\n+\t * @param index\n+\t *            the history table item index\n+\t * @return the index after <code>index</code> that contains a match.\n+\t *         Returns -1 if there isn't a match after <code>index</code>\n+\t */\n+\tpublic synchronized int getIndexAfter(int index) {\n+\t\tInteger[] matches = getkeysArray();\n+\t\tint sres = Arrays.binarySearch(matches, new Integer(index));\n+\t\tif (sres >= 0 && sres != matches.length - 1) {\n+\t\t\treturn matches[sres + 1].intValue();\n+\t\t} else if (sres < 0) {\n+\t\t\tsres = -sres - 1;\n+\t\t\tif (sres < matches.length) {\n+\t\t\t\treturn matches[sres].intValue();\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn -1;\n+\t}\n+\n+\t/**\n+\t * Returns the first table item index before <code>index</code> that\n+\t * contains a match to the find pattern.\n+\t *\n+\t * @param index\n+\t *            the history table item index\n+\t * @return the index before <code>index</code> that contains a match.\n+\t *         Returns -1 if there isn't a match before <code>index</code>\n+\t */\n+\tpublic synchronized int getIndexBefore(int index) {\n+\t\tInteger[] matches = getkeysArray();\n+\t\tint sres = Arrays.binarySearch(matches, new Integer(index));\n+\t\tif (sres >= 0 && sres != 0) {\n+\t\t\treturn matches[sres - 1].intValue();\n+\t\t} else if (sres < -1) {\n+\t\t\tsres = -sres;\n+\t\t\treturn matches[sres - 2].intValue();\n+\t\t}\n+\n+\t\treturn -1;\n+\t}\n+\n+\t/**\n+\t * Returns the first table item index that contains a match to the find\n+\t * pattern.\n+\t *\n+\t * @return the first index that contains a match. Returns -1 if there isn't\n+\t *         any match\n+\t */\n+\tpublic synchronized int getFirstIndex() {\n+\t\tIterator iter = matchesMap.keySet().iterator();\n+\t\tif (iter.hasNext()) {\n+\t\t\treturn ((Integer) iter.next()).intValue();\n+\t\t}\n+\n+\t\treturn -1;\n+\t}\n+\n+\t/**\n+\t * Returns the last table item index that contains a match to the find\n+\t * pattern.\n+\t *\n+\t * @return the last index that contains a match. Returns -1 if there isn't\n+\t *         any match\n+\t */\n+\tpublic synchronized int getLastIndex() {\n+\t\tInteger[] matches = getkeysArray();\n+\t\tif (matches.length > 0) {\n+\t\t\treturn matches[matches.length - 1].intValue();\n+\t\t}\n+\n+\t\treturn -1;\n+\t}\n+\n+\t/**\n+\t * Returns the index in the matches list for the history table item\n+\t * <code>index</code>.\n+\t *\n+\t * @param index\n+\t *            the history table item index\n+\t * @return the position of the <code>index</code> in the total matches\n+\t *         list. Returns -1 if <code>index</code> doesn't contain a match\n+\t */\n+\tpublic synchronized int getMatchNumberFor(int index) {\n+\t\tInteger ix = matchesMap.get(new Integer(index));\n+\t\tif (ix != null) {\n+\t\t\treturn ix.intValue();\n+\t\t}\n+\n+\t\treturn -1;\n+\t}\n+\n+\t/**\n+\t * @return int\n+\t */\n+\tpublic int size() {\n+\t\treturn matchesCount;\n+\t}\n+\n+\t/**\n+\t * Cleans the find results. All match item indexes are removed.\n+\t */\n+\tpublic synchronized void clear() {\n+\t\tmatchesMap.clear();\n+\t\tkeysArray = null;\n+\t\tmatchesCount = 0;\n+\t}\n+\n+\t/**\n+\t * Adds a history table item index (<code>matchIx</code>) to the find\n+\t * results matches list.\n+\t *\n+\t * @param matchIx\n+\t *            the history table item index that matches a find pattern.\n+\t */\n+\tpublic synchronized void add(int matchIx) {\n+\t\tmatchesMap.put(new Integer(matchIx), new Integer(++matchesCount));\n+\t\tkeysArray = null;\n+\t}\n+\n+\tprivate Integer[] getkeysArray() {\n+\t\tif (keysArray == null) {\n+\t\t\tkeysArray = matchesMap.keySet().toArray(\n+\t\t\t\t\tnew Integer[matchesMap.size()]);\n+\t\t}\n+\n+\t\treturn keysArray;\n+\t}\n+\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbar.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbar.java\nnew file mode 100644\nindex 0000000..eae0cc4\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbar.java\n@@ -0,0 +1,457 @@\n+/*\n+ *  Copyright (C) 2008  Roger C. Soares\n+ *\n+ *  This library is free software; you can redistribute it and/or\n+ *  modify it under the terms of the GNU Lesser General Public\n+ *  License, version 2.1, as published by the Free Software Foundation.\n+ *\n+ *  This library is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ *  Lesser General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU Lesser General Public\n+ *  License along with this library; if not, write to the Free Software\n+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\n+ */\n+package org.spearce.egit.ui.internal.history;\n+\n+import java.util.ArrayList;\n+import java.util.List;\n+\n+import org.eclipse.core.runtime.Preferences;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.events.KeyAdapter;\n+import org.eclipse.swt.events.KeyEvent;\n+import org.eclipse.swt.events.ModifyEvent;\n+import org.eclipse.swt.events.ModifyListener;\n+import org.eclipse.swt.events.SelectionAdapter;\n+import org.eclipse.swt.events.SelectionEvent;\n+import org.eclipse.swt.graphics.Color;\n+import org.eclipse.swt.graphics.Image;\n+import org.eclipse.swt.graphics.Point;\n+import org.eclipse.swt.graphics.RGB;\n+import org.eclipse.swt.graphics.Rectangle;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n+import org.eclipse.swt.widgets.Button;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Display;\n+import org.eclipse.swt.widgets.Event;\n+import org.eclipse.swt.widgets.Label;\n+import org.eclipse.swt.widgets.Listener;\n+import org.eclipse.swt.widgets.Menu;\n+import org.eclipse.swt.widgets.MenuItem;\n+import org.eclipse.swt.widgets.ProgressBar;\n+import org.eclipse.swt.widgets.Table;\n+import org.eclipse.swt.widgets.Text;\n+import org.eclipse.swt.widgets.ToolBar;\n+import org.eclipse.swt.widgets.ToolItem;\n+import org.eclipse.swt.widgets.Widget;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIIcons;\n+import org.spearce.egit.ui.UIPreferences;\n+\n+/**\n+ * A toolbar for the history page.\n+ *\n+ * @see FindToolbarThread\n+ * @see FindResults\n+ * @see GitHistoryPage\n+ */\n+public class FindToolbar extends Composite {\n+\tprivate Color errorBackgroundColor;\n+\n+\t/**\n+\t * The results (matches) of the current find operation.\n+\t */\n+\tpublic final FindResults findResults = new FindResults();\n+\n+\tprivate Preferences prefs = Activator.getDefault().getPluginPreferences();\n+\n+\tprivate List<Listener> eventList = new ArrayList<Listener>();\n+\n+\tprivate Image nextIcon;\n+\n+\tprivate Image previousIcon;\n+\n+\tprivate Table historyTable;\n+\n+\tprivate SWTCommit[] fileRevisions;\n+\n+\tprivate Text patternField;\n+\n+\tprivate Button nextButton;\n+\n+\tprivate Button previousButton;\n+\n+\tprivate Label currentPositionLabel;\n+\n+\tprivate ProgressBar progressBar;\n+\n+\tprivate String lastErrorPattern;\n+\n+\t/**\n+\t * Creates the toolbar.\n+\t *\n+\t * @param parent\n+\t *            the parent widget\n+\t */\n+\tpublic FindToolbar(Composite parent) {\n+\t\tsuper(parent, SWT.NULL);\n+\t\tcreateToolbar();\n+\t}\n+\n+\tprivate void createToolbar() {\n+\t\terrorBackgroundColor = new Color(getDisplay(), new RGB(255, 150, 150));\n+\t\tnextIcon = UIIcons.ELCL16_NEXT.createImage();\n+\t\tpreviousIcon = UIIcons.ELCL16_PREVIOUS.createImage();\n+\n+\t\tGridLayout findLayout = new GridLayout();\n+\t\tfindLayout.marginHeight = 2;\n+\t\tfindLayout.marginWidth = 2;\n+\t\tfindLayout.numColumns = 8;\n+\t\tsetLayout(findLayout);\n+\t\tsetLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));\n+\n+\t\tLabel findLabel = new Label(this, SWT.NULL);\n+\t\tfindLabel.setText(\"Find:\");\n+\n+\t\tpatternField = new Text(this, SWT.SEARCH);\n+\t\tGridData findTextData = new GridData(SWT.FILL, SWT.FILL, true, false);\n+\t\tfindTextData.minimumWidth = 50;\n+\t\tpatternField.setLayoutData(findTextData);\n+\t\tpatternField.setText(\"\");\n+\t\tpatternField.setTextLimit(100);\n+\n+\t\tnextButton = new Button(this, SWT.HORIZONTAL);\n+\t\tnextButton.setImage(nextIcon);\n+\t\tnextButton.setText(\"next\");\n+\n+\t\tpreviousButton = new Button(this, SWT.HORIZONTAL);\n+\t\tpreviousButton.setImage(previousIcon);\n+\t\tpreviousButton.setText(\"previous\");\n+\n+\t\tfinal ToolBar toolBar = new ToolBar(this, SWT.FLAT);\n+\t\tnew ToolItem(toolBar, SWT.SEPARATOR);\n+\n+\t\tfinal ToolItem prefsItem = new ToolItem(toolBar, SWT.DROP_DOWN);\n+\t\tfinal Menu prefsMenu = new Menu(this.getShell(), SWT.POP_UP);\n+\t\tfinal MenuItem caseItem = new MenuItem(prefsMenu, SWT.CHECK);\n+\t\tcaseItem.setText(\"Ignore case\");\n+\t\tnew MenuItem(prefsMenu, SWT.SEPARATOR);\n+\t\tfinal MenuItem commitIdItem = new MenuItem(prefsMenu, SWT.CHECK);\n+\t\tcommitIdItem.setText(\"Commit\");\n+\t\tfinal MenuItem commentsItem = new MenuItem(prefsMenu, SWT.CHECK);\n+\t\tcommentsItem.setText(\"Comments\");\n+\t\tfinal MenuItem authorItem = new MenuItem(prefsMenu, SWT.CHECK);\n+\t\tauthorItem.setText(\"Author\");\n+\t\tfinal MenuItem committerItem = new MenuItem(prefsMenu, SWT.CHECK);\n+\t\tcommitterItem.setText(\"Committer\");\n+\n+\t\tprefsItem.addListener(SWT.Selection, new Listener() {\n+\t\t\tpublic void handleEvent(Event event) {\n+\t\t\t\tif (event.detail == SWT.ARROW) {\n+\t\t\t\t\tRectangle itemBounds = prefsItem.getBounds();\n+\t\t\t\t\tPoint point = toolBar.toDisplay(itemBounds.x, itemBounds.y\n+\t\t\t\t\t\t\t+ itemBounds.height);\n+\t\t\t\t\tprefsMenu.setLocation(point);\n+\t\t\t\t\tprefsMenu.setVisible(true);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t});\n+\n+\t\tcurrentPositionLabel = new Label(this, SWT.NULL);\n+\t\tGridData totalLabelData = new GridData();\n+\t\ttotalLabelData.horizontalAlignment = SWT.FILL;\n+\t\ttotalLabelData.grabExcessHorizontalSpace = true;\n+\t\tcurrentPositionLabel.setLayoutData(totalLabelData);\n+\t\tcurrentPositionLabel.setAlignment(SWT.RIGHT);\n+\t\tcurrentPositionLabel.setText(\"\");\n+\n+\t\tprogressBar = new ProgressBar(this, SWT.HORIZONTAL);\n+\t\tGridData findProgressBarData = new GridData();\n+\t\tfindProgressBarData.heightHint = 12;\n+\t\tfindProgressBarData.widthHint = 35;\n+\t\tprogressBar.setLayoutData(findProgressBarData);\n+\t\tprogressBar.setMinimum(0);\n+\t\tprogressBar.setMaximum(100);\n+\n+\t\tfinal FindToolbar thisToolbar = this;\n+\t\tpatternField.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(ModifyEvent e) {\n+\t\t\t\tfinal FindToolbarThread finder = new FindToolbarThread();\n+\t\t\t\tfinder.pattern = ((Text) e.getSource()).getText();\n+\t\t\t\tfinder.fileRevisions = fileRevisions;\n+\t\t\t\tfinder.toolbar = thisToolbar;\n+\t\t\t\tfinder.ignoreCase = caseItem.getSelection();\n+\t\t\t\tfinder.findInCommitId = commitIdItem.getSelection();\n+\t\t\t\tfinder.findInComments = commentsItem.getSelection();\n+\t\t\t\tfinder.findInAuthor = authorItem.getSelection();\n+\t\t\t\tfinder.findInCommitter = committerItem.getSelection();\n+\t\t\t\tDisplay.getDefault().timerExec(200, new Runnable() {\n+\t\t\t\t\tpublic void run() {\n+\t\t\t\t\t\tfinder.start();\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\t\t}\n+\t\t});\n+\n+\t\tfinal Listener findButtonsListener = new Listener() {\n+\t\t\tpublic void handleEvent(Event event) {\n+\t\t\t\tif (patternField.getText().length() > 0\n+\t\t\t\t\t\t&& findResults.size() == 0) {\n+\t\t\t\t\t// If the toolbar was cleared and has a pattern typed,\n+\t\t\t\t\t// then we redo the find with the new table data.\n+\t\t\t\t\tfinal FindToolbarThread finder = new FindToolbarThread();\n+\t\t\t\t\tfinder.pattern = patternField.getText();\n+\t\t\t\t\tfinder.fileRevisions = fileRevisions;\n+\t\t\t\t\tfinder.toolbar = thisToolbar;\n+\t\t\t\t\tfinder.ignoreCase = caseItem.getSelection();\n+\t\t\t\t\tfinder.findInCommitId = commitIdItem.getSelection();\n+\t\t\t\t\tfinder.findInComments = commentsItem.getSelection();\n+\t\t\t\t\tfinder.findInAuthor = authorItem.getSelection();\n+\t\t\t\t\tfinder.findInCommitter = committerItem.getSelection();\n+\t\t\t\t\tfinder.start();\n+\t\t\t\t\tpatternField.setSelection(0, 0);\n+\t\t\t\t} else {\n+\t\t\t\t\tint currentIx = historyTable.getSelectionIndex();\n+\t\t\t\t\tint newIx = -1;\n+\t\t\t\t\tif (event.widget == nextButton) {\n+\t\t\t\t\t\tnewIx = findResults.getIndexAfter(currentIx);\n+\t\t\t\t\t\tif (newIx == -1) {\n+\t\t\t\t\t\t\tnewIx = findResults.getFirstIndex();\n+\t\t\t\t\t\t}\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tnewIx = findResults.getIndexBefore(currentIx);\n+\t\t\t\t\t\tif (newIx == -1) {\n+\t\t\t\t\t\t\tnewIx = findResults.getLastIndex();\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t\tsendEvent(event.widget, newIx);\n+\n+\t\t\t\t\tString current = null;\n+\t\t\t\t\tint currentValue = findResults.getMatchNumberFor(newIx);\n+\t\t\t\t\tif (currentValue == -1) {\n+\t\t\t\t\t\tcurrent = \"-\";\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tcurrent = String.valueOf(currentValue);\n+\t\t\t\t\t}\n+\t\t\t\t\tcurrentPositionLabel.setText(current + \"/\"\n+\t\t\t\t\t\t\t+ findResults.size());\n+\t\t\t\t}\n+\t\t\t}\n+\t\t};\n+\t\tnextButton.addListener(SWT.Selection, findButtonsListener);\n+\t\tpreviousButton.addListener(SWT.Selection, findButtonsListener);\n+\n+\t\tpatternField.addKeyListener(new KeyAdapter() {\n+\t\t\tprivate Event event = new Event();\n+\n+\t\t\t@Override\n+\t\t\tpublic void keyPressed(KeyEvent e) {\n+\t\t\t\tif (e.keyCode == SWT.ARROW_DOWN) {\n+\t\t\t\t\tif (nextButton.isEnabled()) {\n+\t\t\t\t\t\tevent.widget = nextButton;\n+\t\t\t\t\t\tfindButtonsListener.handleEvent(event);\n+\t\t\t\t\t}\n+\t\t\t\t} else if (e.keyCode == SWT.ARROW_UP) {\n+\t\t\t\t\tif (previousButton.isEnabled()) {\n+\t\t\t\t\t\tevent.widget = previousButton;\n+\t\t\t\t\t\tfindButtonsListener.handleEvent(event);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t});\n+\n+\t\tcaseItem.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tprefs.setValue(UIPreferences.FINDTOOLBAR_IGNORE_CASE, caseItem\n+\t\t\t\t\t\t.getSelection());\n+\t\t\t\tActivator.getDefault().savePluginPreferences();\n+\t\t\t\tclear();\n+\t\t\t}\n+\t\t});\n+\t\tcaseItem.setSelection(prefs\n+\t\t\t\t.getBoolean(UIPreferences.FINDTOOLBAR_IGNORE_CASE));\n+\n+\t\tcommitIdItem.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tprefs.setValue(UIPreferences.FINDTOOLBAR_COMMIT_ID,\n+\t\t\t\t\t\tcommitIdItem.getSelection());\n+\t\t\t\tActivator.getDefault().savePluginPreferences();\n+\t\t\t\tclear();\n+\t\t\t}\n+\t\t});\n+\t\tcommitIdItem.setSelection(prefs\n+\t\t\t\t.getBoolean(UIPreferences.FINDTOOLBAR_COMMIT_ID));\n+\n+\t\tcommentsItem.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tprefs.setValue(UIPreferences.FINDTOOLBAR_COMMENTS, commentsItem\n+\t\t\t\t\t\t.getSelection());\n+\t\t\t\tActivator.getDefault().savePluginPreferences();\n+\t\t\t\tclear();\n+\t\t\t}\n+\t\t});\n+\t\tcommentsItem.setSelection(prefs\n+\t\t\t\t.getBoolean(UIPreferences.FINDTOOLBAR_COMMENTS));\n+\n+\t\tauthorItem.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tprefs.setValue(UIPreferences.FINDTOOLBAR_AUTHOR, authorItem\n+\t\t\t\t\t\t.getSelection());\n+\t\t\t\tActivator.getDefault().savePluginPreferences();\n+\t\t\t\tclear();\n+\t\t\t}\n+\t\t});\n+\t\tauthorItem.setSelection(prefs\n+\t\t\t\t.getBoolean(UIPreferences.FINDTOOLBAR_AUTHOR));\n+\n+\t\tcommitterItem.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tprefs.setValue(UIPreferences.FINDTOOLBAR_COMMITTER,\n+\t\t\t\t\t\tcommitterItem.getSelection());\n+\t\t\t\tActivator.getDefault().savePluginPreferences();\n+\t\t\t\tclear();\n+\t\t\t}\n+\t\t});\n+\t\tcommitterItem.setSelection(prefs\n+\t\t\t\t.getBoolean(UIPreferences.FINDTOOLBAR_COMMITTER));\n+\t}\n+\n+\t@Override\n+\tpublic void dispose() {\n+\t\terrorBackgroundColor.dispose();\n+\t\tnextIcon.dispose();\n+\t\tpreviousIcon.dispose();\n+\t\tsuper.dispose();\n+\t}\n+\n+\t/**\n+\t * Sets the table that will have its selected items changed by this toolbar.\n+\t * Sets the list to be searched.\n+\t *\n+\t * @param historyTable\n+\t * @param commitArray\n+\t * \n+\t */\n+\tpublic void setInput(final Table historyTable, final SWTCommit[] commitArray) {\n+\t\tthis.fileRevisions = commitArray;\n+\t\tthis.historyTable = historyTable;\n+\t}\n+\n+\tvoid progressUpdate(int percent) {\n+\t\tint total = findResults.size();\n+\t\tcurrentPositionLabel.setText(\"-/\" + total);\n+\t\tcurrentPositionLabel.setForeground(null);\n+\t\tif (total > 0) {\n+\t\t\tnextButton.setEnabled(true);\n+\t\t\tpreviousButton.setEnabled(true);\n+\t\t\tpatternField.setBackground(null);\n+\t\t} else {\n+\t\t\tnextButton.setEnabled(false);\n+\t\t\tpreviousButton.setEnabled(false);\n+\t\t}\n+\t\tprogressBar.setSelection(percent);\n+\t\thistoryTable.clearAll();\n+\t}\n+\n+\tvoid findCompletionUpdate(String pattern, boolean overflow) {\n+\t\tint total = findResults.size();\n+\t\tif (total > 0) {\n+\t\t\tif (overflow) {\n+\t\t\t\tcurrentPositionLabel.setText(\"Results limit exceed 1/\" + total);\n+\t\t\t} else {\n+\t\t\t\tcurrentPositionLabel.setText(\"1/\" + total);\n+\t\t\t}\n+\t\t\tint ix = findResults.getFirstIndex();\n+\t\t\tsendEvent(null, ix);\n+\n+\t\t\tpatternField.setBackground(null);\n+\t\t\tnextButton.setEnabled(true);\n+\t\t\tpreviousButton.setEnabled(true);\n+\t\t\tlastErrorPattern = null;\n+\t\t} else {\n+\t\t\tif (pattern.length() > 0) {\n+\t\t\t\tpatternField.setBackground(errorBackgroundColor);\n+\t\t\t\tcurrentPositionLabel.setText(\"String not found\");\n+\t\t\t\t// Don't keep beeping every time if the user is deleting\n+\t\t\t\t// a long not found pattern\n+\t\t\t\tif (lastErrorPattern == null\n+\t\t\t\t\t\t|| (lastErrorPattern != null && !lastErrorPattern\n+\t\t\t\t\t\t\t\t.startsWith(pattern))) {\n+\t\t\t\t\tDisplay.getDefault().beep();\n+\t\t\t\t\tnextButton.setEnabled(false);\n+\t\t\t\t\tpreviousButton.setEnabled(false);\n+\t\t\t\t}\n+\t\t\t\tlastErrorPattern = pattern;\n+\t\t\t} else {\n+\t\t\t\tpatternField.setBackground(null);\n+\t\t\t\tcurrentPositionLabel.setText(\"\");\n+\t\t\t\tnextButton.setEnabled(false);\n+\t\t\t\tpreviousButton.setEnabled(false);\n+\t\t\t\tlastErrorPattern = null;\n+\t\t\t}\n+\t\t}\n+\t\tprogressBar.setSelection(0);\n+\t\thistoryTable.clearAll();\n+\n+\t\tif (overflow) {\n+\t\t\tDisplay display = Display.getCurrent();\n+\t\t\tcurrentPositionLabel.setForeground(display\n+\t\t\t\t\t.getSystemColor(SWT.COLOR_RED));\n+\t\t\tdisplay.beep();\n+\t\t} else {\n+\t\t\tcurrentPositionLabel.setForeground(null);\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Clears the toolbar.\n+\t */\n+\tpublic void clear() {\n+\t\tpatternField.setBackground(null);\n+\t\tif (patternField.getText().length() > 0) {\n+\t\t\tpatternField.selectAll();\n+\t\t\tnextButton.setEnabled(true);\n+\t\t\tpreviousButton.setEnabled(true);\n+\t\t} else {\n+\t\t\tnextButton.setEnabled(false);\n+\t\t\tpreviousButton.setEnabled(false);\n+\t\t}\n+\t\tcurrentPositionLabel.setText(\"\");\n+\t\tprogressBar.setSelection(0);\n+\t\tlastErrorPattern = null;\n+\n+\t\tfindResults.clear();\n+\t\tif (historyTable != null) {\n+\t\t\thistoryTable.clearAll();\n+\t\t}\n+\n+\t\tFindToolbarThread.updateGlobalThreadIx();\n+\t}\n+\n+\tprivate void sendEvent(Widget widget, int index) {\n+\t\tEvent event = new Event();\n+\t\tevent.type = SWT.Selection;\n+\t\tevent.index = index;\n+\t\tevent.widget = widget;\n+\t\tevent.data = fileRevisions[index];\n+\t\tfor (Listener listener : eventList) {\n+\t\t\tlistener.handleEvent(event);\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Adds a selection event listener. The toolbar generates events when it\n+\t * selects an item in the history table\n+\t *\n+\t * @param listener\n+\t *            the listener that will receive the event\n+\t */\n+\tpublic void addSelectionListener(Listener listener) {\n+\t\teventList.add(listener);\n+\t}\n+\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbarThread.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbarThread.java\nnew file mode 100644\nindex 0000000..931f82b\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbarThread.java\n@@ -0,0 +1,237 @@\n+/*\n+ *  Copyright (C) 2008  Roger C. Soares\n+ *\n+ *  This library is free software; you can redistribute it and/or\n+ *  modify it under the terms of the GNU Lesser General Public\n+ *  License, version 2.1, as published by the Free Software Foundation.\n+ *\n+ *  This library is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ *  Lesser General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU Lesser General Public\n+ *  License along with this library; if not, write to the Free Software\n+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\n+ */\n+package org.spearce.egit.ui.internal.history;\n+\n+import org.eclipse.swt.widgets.Display;\n+\n+/**\n+ * This class executes the search function for the find toolbar. Only one thread\n+ * is executed at a time.\n+ * <p>\n+ * This class maintains a <code>globalThreadIx</code> internal variable that\n+ * is incremented for each new thread started and the current running thread\n+ * constantly checks this variable. If the current thread has the same value as\n+ * <code>globalThreadIx</code> it continues executing, if it has a lower value\n+ * it means that a more recent search needs to be done and the current isn't\n+ * necessary any more, so the current thread returns.\n+ * </p>\n+ * <p>\n+ * To avoid consuming all the memory in the system, this class limits the\n+ * maximum results it stores.\n+ * </p>\n+ *\n+ * @see FindToolbar\n+ * @see FindResults\n+ */\n+public class FindToolbarThread extends Thread {\n+\n+\tprivate static final int MAX_RESULTS = 20000;\n+\n+\tString pattern;\n+\n+\tSWTCommit[] fileRevisions;\n+\n+\tFindToolbar toolbar;\n+\n+\tboolean ignoreCase;\n+\n+\tboolean findInCommitId;\n+\n+\tboolean findInComments;\n+\n+\tboolean findInAuthor;\n+\n+\tboolean findInCommitter;\n+\n+\tprivate static Display display = Display.getDefault();\n+\n+\tprivate static int globalThreadIx = 0;\n+\n+\tprivate int currentThreadIx;\n+\n+\t/**\n+\t * Creates a new object and increments the internal\n+\t * <code>globalThreadIx</code> variable causing any earlier running thread\n+\t * to return.\n+\t */\n+\tpublic FindToolbarThread() {\n+\t\tsuper(\"history_find_thread\" + ++globalThreadIx);\n+\t\tcurrentThreadIx = globalThreadIx;\n+\t}\n+\n+\tpublic void run() {\n+\t\texecFind(currentThreadIx, fileRevisions, pattern, toolbar, ignoreCase,\n+\t\t\t\tfindInCommitId, findInComments, findInAuthor, findInCommitter);\n+\t}\n+\n+\tprivate synchronized static void execFind(int threadIx,\n+\t\t\tSWTCommit[] fileRevisions, final String pattern,\n+\t\t\tfinal FindToolbar toolbar, boolean ignoreCase,\n+\t\t\tboolean findInCommitId, boolean findInComments,\n+\t\t\tboolean findInAuthor, boolean findInCommitter) {\n+\t\t// If it isn't the last event, just ignore it.\n+\t\tif (threadIx < globalThreadIx) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tFindResults findResults = toolbar.findResults;\n+\t\tfindResults.clear();\n+\n+\t\tboolean maxResultsOverflow = false;\n+\t\tif (pattern.length() > 0 && fileRevisions != null) {\n+\t\t\tString findPattern = pattern;\n+\t\t\tif (ignoreCase) {\n+\t\t\t\tfindPattern = pattern.toLowerCase();\n+\t\t\t}\n+\n+\t\t\tlong lastUIUpdate = System.currentTimeMillis();\n+\n+\t\t\tint totalRevisions = fileRevisions.length;\n+\t\t\tint totalMatches = 0;\n+\t\t\tboolean notFound = true;\n+\t\t\tfor (int i = 0; i < totalRevisions; i++) {\n+\t\t\t\t// If a new find event was generated, ends the current thread.\n+\t\t\t\tif (display.isDisposed() || threadIx < globalThreadIx) {\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\t// Updates the toolbar with in process info.\n+\t\t\t\tif (System.currentTimeMillis() - lastUIUpdate > 500) {\n+\t\t\t\t\tfinal int percentage = (int) (((i + 1F) / totalRevisions) * 100);\n+\t\t\t\t\tdisplay.asyncExec(new Runnable() {\n+\t\t\t\t\t\tpublic void run() {\n+\t\t\t\t\t\t\tif (toolbar.isDisposed()) {\n+\t\t\t\t\t\t\t\treturn;\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\ttoolbar.progressUpdate(percentage);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t});\n+\t\t\t\t\tlastUIUpdate = System.currentTimeMillis();\n+\t\t\t\t}\n+\n+\t\t\t\t// Finds for the pattern in the revision history.\n+\t\t\t\tnotFound = true;\n+\t\t\t\tSWTCommit revision = fileRevisions[i];\n+\n+\t\t\t\tif (findInCommitId) {\n+\t\t\t\t\tString contentId = revision.getId().toString();\n+\t\t\t\t\tif (contentId != null) {\n+\t\t\t\t\t\tif (ignoreCase) {\n+\t\t\t\t\t\t\tcontentId = contentId.toLowerCase();\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (contentId.indexOf(findPattern) != -1) {\n+\t\t\t\t\t\t\ttotalMatches++;\n+\t\t\t\t\t\t\tfindResults.add(i);\n+\t\t\t\t\t\t\tnotFound = false;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tif (findInComments && notFound) {\n+\t\t\t\t\tString comment = revision.getFullMessage();\n+\t\t\t\t\tif (comment != null) {\n+\t\t\t\t\t\tif (ignoreCase) {\n+\t\t\t\t\t\t\tcomment = comment.toLowerCase();\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (comment.indexOf(findPattern) != -1) {\n+\t\t\t\t\t\t\ttotalMatches++;\n+\t\t\t\t\t\t\tfindResults.add(i);\n+\t\t\t\t\t\t\tnotFound = false;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tif (findInAuthor && notFound) {\n+\t\t\t\t\tString author = revision.getAuthorIdent().getName();\n+\t\t\t\t\tif (author != null) {\n+\t\t\t\t\t\tif (ignoreCase) {\n+\t\t\t\t\t\t\tauthor = author.toLowerCase();\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (author.indexOf(findPattern) != -1) {\n+\t\t\t\t\t\t\ttotalMatches++;\n+\t\t\t\t\t\t\tfindResults.add(i);\n+\t\t\t\t\t\t\tnotFound = false;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t\tif (notFound) {\n+\t\t\t\t\t\tString email = revision.getAuthorIdent()\n+\t\t\t\t\t\t\t\t.getEmailAddress();\n+\t\t\t\t\t\tif (email != null) {\n+\t\t\t\t\t\t\tif (ignoreCase) {\n+\t\t\t\t\t\t\t\temail = email.toLowerCase();\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\tif (email.indexOf(findPattern) != -1) {\n+\t\t\t\t\t\t\t\ttotalMatches++;\n+\t\t\t\t\t\t\t\tfindResults.add(i);\n+\t\t\t\t\t\t\t\tnotFound = false;\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tif (findInCommitter && notFound) {\n+\t\t\t\t\tString committer = revision.getCommitterIdent().getName();\n+\t\t\t\t\tif (committer != null) {\n+\t\t\t\t\t\tif (ignoreCase) {\n+\t\t\t\t\t\t\tcommitter = committer.toLowerCase();\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (committer.indexOf(findPattern) != -1) {\n+\t\t\t\t\t\t\ttotalMatches++;\n+\t\t\t\t\t\t\tfindResults.add(i);\n+\t\t\t\t\t\t\tnotFound = false;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t\tif (notFound) {\n+\t\t\t\t\t\tString email = revision.getCommitterIdent()\n+\t\t\t\t\t\t\t\t.getEmailAddress();\n+\t\t\t\t\t\tif (email != null) {\n+\t\t\t\t\t\t\tif (ignoreCase) {\n+\t\t\t\t\t\t\t\temail = email.toLowerCase();\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\tif (email.indexOf(findPattern) != -1) {\n+\t\t\t\t\t\t\t\ttotalMatches++;\n+\t\t\t\t\t\t\t\tfindResults.add(i);\n+\t\t\t\t\t\t\t\tnotFound = false;\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tif (totalMatches == MAX_RESULTS) {\n+\t\t\t\t\tmaxResultsOverflow = true;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t}\n+\n+\t\t// Updates the toolbar with the result find info.\n+\t\tfinal boolean overflow = maxResultsOverflow;\n+\t\tdisplay.syncExec(new Runnable() {\n+\t\t\tpublic void run() {\n+\t\t\t\tif (toolbar.isDisposed()) {\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\t\t\t\ttoolbar.findCompletionUpdate(pattern, overflow);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tstatic void updateGlobalThreadIx() {\n+\t\t++globalThreadIx;\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java\nindex 3b6d1c9..09b4d68 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java\n@@ -29,6 +29,7 @@ import org.eclipse.jface.action.Action;\n import org.eclipse.jface.action.IAction;\n import org.eclipse.jface.action.IContributionItem;\n import org.eclipse.jface.action.IMenuManager;\n+import org.eclipse.jface.action.IToolBarManager;\n import org.eclipse.jface.action.MenuManager;\n import org.eclipse.jface.action.Separator;\n import org.eclipse.jface.text.ITextOperationTarget;\n@@ -46,6 +47,8 @@ import org.eclipse.swt.layout.GridData;\n import org.eclipse.swt.layout.GridLayout;\n import org.eclipse.swt.widgets.Composite;\n import org.eclipse.swt.widgets.Control;\n+import org.eclipse.swt.widgets.Event;\n+import org.eclipse.swt.widgets.Listener;\n import org.eclipse.team.ui.history.HistoryPage;\n import org.eclipse.ui.IActionBars;\n import org.eclipse.ui.IPartListener;\n@@ -59,13 +62,13 @@ import org.eclipse.ui.progress.IWorkbenchSiteProgressService;\n import org.spearce.egit.core.ResourceList;\n import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIPreferences;\n import org.spearce.egit.ui.UIText;\n import org.spearce.jgit.lib.AnyObjectId;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.revplot.PlotCommit;\n import org.spearce.jgit.revwalk.RevCommit;\n-import org.spearce.jgit.revwalk.RevFlag;\n import org.spearce.jgit.revwalk.RevSort;\n import org.spearce.jgit.revwalk.filter.RevFilter;\n import org.spearce.jgit.treewalk.TreeWalk;\n@@ -85,6 +88,8 @@ public class GitHistoryPage extends HistoryPage {\n \n \tprivate static final String SPLIT_INFO = UIPreferences.RESOURCEHISTORY_REV_SPLIT;\n \n+\tprivate static final String SHOW_FIND_TOOLBAR = UIPreferences.RESOURCEHISTORY_SHOW_FINDTOOLBAR;\n+\n \tprivate static final String POPUP_ID = \"org.spearce.egit.ui.historyPageContributions\";\n \n \t/**\n@@ -147,6 +152,9 @@ public class GitHistoryPage extends HistoryPage {\n \t/** Viewer displaying file difference implied by {@link #graph}'s commit. */\n \tprivate CommitFileDiffViewer fileViewer;\n \n+\t/** Toolbar to find commits in the history view. */\n+\tprivate FindToolbar findToolbar;\n+\n \t/** Our context menu manager for the entire page. */\n \tprivate MenuManager popupMgr;\n \n@@ -157,14 +165,6 @@ public class GitHistoryPage extends HistoryPage {\n \tprivate SWTWalk currentWalk;\n \n \t/**\n-\t * Highlight flag that can be applied to commits to make them stand out.\n-\t * <p>\n-\t * Allocated at the same time as {@link #currentWalk}. If the walk\n-\t * rebuilds, so must this flag.\n-\t */\n-\tprivate RevFlag highlightFlag;\n-\n-\t/**\n \t * List of paths we used to limit {@link #currentWalk}; null if no paths.\n \t * <p>\n \t * Note that a change in this list requires that {@link #currentWalk} and\n@@ -197,12 +197,14 @@ public class GitHistoryPage extends HistoryPage {\n \t\trevInfoSplit = new SashForm(graphDetailSplit, SWT.HORIZONTAL);\n \t\tcommentViewer = new CommitMessageViewer(revInfoSplit);\n \t\tfileViewer = new CommitFileDiffViewer(revInfoSplit);\n+\t\tfindToolbar = new FindToolbar(ourControl);\n \n \t\tlayoutSashForm(graphDetailSplit, SPLIT_GRAPH);\n \t\tlayoutSashForm(revInfoSplit, SPLIT_INFO);\n \n \t\tpopupMgr = new MenuManager(POPUP_ID);\n \t\tattachCommitSelectionChanged();\n+\t\tcreateLocalToolbarActions();\n \t\tcreateStandardActions();\n \t\tcreateViewMenu();\n \n@@ -248,6 +250,7 @@ public class GitHistoryPage extends HistoryPage {\n \tprivate void layout() {\n \t\tfinal boolean showComment = prefs.getBoolean(SHOW_COMMENT);\n \t\tfinal boolean showFiles = prefs.getBoolean(SHOW_FILES);\n+\t\tfinal boolean showFindToolbar = prefs.getBoolean(SHOW_FIND_TOOLBAR);\n \n \t\tif (showComment && showFiles) {\n \t\t\tgraphDetailSplit.setMaximizedControl(null);\n@@ -261,6 +264,13 @@ public class GitHistoryPage extends HistoryPage {\n \t\t} else if (!showComment && !showFiles) {\n \t\t\tgraphDetailSplit.setMaximizedControl(graph.getControl());\n \t\t}\n+\t\tif (showFindToolbar) {\n+\t\t\t((GridData) findToolbar.getLayoutData()).heightHint = SWT.DEFAULT;\n+\t\t} else {\n+\t\t\t((GridData) findToolbar.getLayoutData()).heightHint = 0;\n+\t\t\tfindToolbar.clear();\n+\t\t}\n+\t\tourControl.layout();\n \t}\n \n \tprivate void attachCommitSelectionChanged() {\n@@ -288,6 +298,32 @@ public class GitHistoryPage extends HistoryPage {\n \t\t\t\t\t\tgraph.selectCommit(c);\n \t\t\t\t\t}\n \t\t\t\t});\n+\t\tfindToolbar.addSelectionListener(new Listener() {\n+\t\t\tpublic void handleEvent(Event event) {\n+\t\t\t\tgraph.selectCommit((RevCommit) event.data);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void createLocalToolbarActions() {\n+\t\tfinal IToolBarManager barManager = getSite().getActionBars()\n+\t\t\t\t.getToolBarManager();\n+\t\tIAction a;\n+\n+\t\ta = createFindToolbarAction();\n+\t\tbarManager.add(a);\n+\t}\n+\n+\tprivate IAction createFindToolbarAction() {\n+\t\tfinal IAction r = new Action(\"Fi\", UIIcons.ELCL16_FIND) {\n+\t\t\tpublic void run() {\n+\t\t\t\tprefs.setValue(SHOW_FIND_TOOLBAR, isChecked());\n+\t\t\t\tlayout();\n+\t\t\t}\n+\t\t};\n+\t\tr.setChecked(prefs.getBoolean(SHOW_FIND_TOOLBAR));\n+\t\tr.setToolTipText(\"Find\");\n+\t\treturn r;\n \t}\n \n \tprivate void createViewMenu() {\n@@ -470,7 +506,6 @@ public class GitHistoryPage extends HistoryPage {\n \t\t\t\t|| pathChange(pathFilters, paths)) {\n \t\t\tcurrentWalk = new SWTWalk(db);\n \t\t\tcurrentWalk.sort(RevSort.COMMIT_TIME_DESC, true);\n-\t\t\thighlightFlag = currentWalk.newFlag(\"highlight\");\n \t\t} else {\n \t\t\tcurrentWalk.reset();\n \t\t}\n@@ -500,7 +535,8 @@ public class GitHistoryPage extends HistoryPage {\n \t\t\tfileWalker.setFilter(TreeFilter.ANY_DIFF);\n \t\t}\n \t\tfileViewer.setTreeWalk(fileWalker);\n-\t\tgraph.setInput(highlightFlag, null, null);\n+\t\tfindToolbar.clear();\n+\t\tgraph.setInput(findToolbar.findResults, null, null);\n \n \t\tfinal SWTCommitList list;\n \t\tlist = new SWTCommitList(graph.getControl().getDisplay());\n@@ -539,7 +575,6 @@ public class GitHistoryPage extends HistoryPage {\n \t\t\t//\n \t\t\tjob = null;\n \t\t\tcurrentWalk = null;\n-\t\t\thighlightFlag = null;\n \t\t\tpathFilters = null;\n \t\t}\n \t}\n@@ -571,8 +606,10 @@ public class GitHistoryPage extends HistoryPage {\n \n \t\tgraph.getControl().getDisplay().asyncExec(new Runnable() {\n \t\t\tpublic void run() {\n-\t\t\t\tif (!graph.getControl().isDisposed() && job == j)\n-\t\t\t\t\tgraph.setInput(highlightFlag, list, asArray);\n+\t\t\t\tif (!graph.getControl().isDisposed() && job == j) {\n+\t\t\t\t\tgraph.setInput(findToolbar.findResults, list, asArray);\n+\t\t\t\t\tfindToolbar.setInput(graph.getRawTable(), asArray);\n+\t\t\t\t}\n \t\t\t}\n \t\t});\n \t}\n-- \n1.5.4.1\n"},{"id":"73411","messageId":"20080331061914.GK10274@spearce.org","threadId":"12919","inReplyTo":"1206890325-3732-1-git-send-email-rogersoares@intelinet.com.br","subject":"Re: [EGIT PATCH 2/4] FindToolbar port to the new history page.","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-03-31T06:19:14Z","receivedAt":"2008-03-31T06:19:14Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"\"Roger C. Soares\" <rogersoares@intelinet.com.br> wrote:\n> The find toolbar already generates a list of table rows that need\n> highlighting. The hightlight flag + RevFilter solution currently\n> doesn't support all the features that the find toolbar has, so for\n> this port the toolbar is replacing the highlight flag.\n\nHmm.  So what functionality did the highlight flag + RevFilter\nnot get you?  It supports both regex as well as non-regex matches,\nis quick, and can be joined together with other filters.  A lot of\nthe code in the FindToolbarThread should drop out.\n \n> @@ -71,6 +70,8 @@ class CommitGraphTable {\n>  \n>  \tprivate final TableViewer table;\n>  \n> +\tprivate final Table rawTable;\n> +\n\nThis field isn't necessary.  \"table.getTable()\" will get you the\nsame widget.\n\n> diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindResults.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindResults.java\n> new file mode 100644\n> index 0000000..0202a70\n> --- /dev/null\n> +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindResults.java\n> @@ -0,0 +1,184 @@\n...\n> +public class FindResults {\n> +\tprivate Map<Integer, Integer> matchesMap = new LinkedHashMap<Integer, Integer>();\n> +\n> +\tInteger[] keysArray;\n> +\n> +\tprivate int matchesCount;\n> +\n...\n> +\tpublic synchronized boolean isFoundAt(int index) {\n> +\t\treturn matchesMap.containsKey(new Integer(index));\n\nSo this is doing basically the same thing as the highlight RevFlag\n(give a boolean about match status for a given RevCommit) but needs\nto consult a HashMap by creating a temporary boxed Integer, and this\nis deep down inside of the painting code for the table.  Urrgh.\n\n> diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbar.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbar.java\n> new file mode 100644\n> index 0000000..eae0cc4\n> --- /dev/null\n> +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbar.java\n> @@ -0,0 +1,457 @@\n...\n> +public class FindToolbar extends Composite {\n...\n> +\tprivate void createToolbar() {\n...\n> +\t\tfinal ToolItem prefsItem = new ToolItem(toolBar, SWT.DROP_DOWN);\n> +\t\tfinal Menu prefsMenu = new Menu(this.getShell(), SWT.POP_UP);\n> +\t\tfinal MenuItem caseItem = new MenuItem(prefsMenu, SWT.CHECK);\n> +\t\tcaseItem.setText(\"Ignore case\");\n\nThese strings should be in UIText, and UIText.properties, so they\ncan be translated strings.\n\n> +\t\tcommitterItem.addSelectionListener(new SelectionAdapter() {\n> +\t\t\tpublic void widgetSelected(SelectionEvent e) {\n> +\t\t\t\tprefs.setValue(UIPreferences.FINDTOOLBAR_COMMITTER,\n> +\t\t\t\t\t\tcommitterItem.getSelection());\n> +\t\t\t\tActivator.getDefault().savePluginPreferences();\n> +\t\t\t\tclear();\n> +\t\t\t}\n> +\t\t});\n> +\t\tcommitterItem.setSelection(prefs\n> +\t\t\t\t.getBoolean(UIPreferences.FINDTOOLBAR_COMMITTER));\n\nWould it make sense to abstract out and reuse the BooleanPrefAction\nclass I added to GitHistoryPage in ea3f1e7a7684b8?  \n\n> diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbarThread.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbarThread.java\n> new file mode 100644\n> index 0000000..931f82b\n> --- /dev/null\n> +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/FindToolbarThread.java\n> @@ -0,0 +1,237 @@\n...\n> +public class FindToolbarThread extends Thread {\n\nShouldn't this maybe be a Job instead, and scheduled on the History\nsite so the History title bar goes italics and the user can see\nthe progress meter in the status bar of the workspace and in their\nProgress view?\n\n> +\tprivate static Display display = Display.getDefault();\n\nI'm not an SWT expert, but I think hanging onto a Display reference\nfrom a class static isn't a good idea.\n\n> +\tprivate static int globalThreadIx = 0;\n\nShouldn't this be a volatile, or an AtomicInteger, or protected by\na synchronized block?\n\n> +\tpublic void run() {\n> +\t\texecFind(currentThreadIx, fileRevisions, pattern, toolbar, ignoreCase,\n> +\t\t\t\tfindInCommitId, findInComments, findInAuthor, findInCommitter);\n> +\t}\n> +\n> +\tprivate synchronized static void execFind(int threadIx,\n> +\t\t\tSWTCommit[] fileRevisions, final String pattern,\n> +\t\t\tfinal FindToolbar toolbar, boolean ignoreCase,\n> +\t\t\tboolean findInCommitId, boolean findInComments,\n> +\t\t\tboolean findInAuthor, boolean findInCommitter) {\n\nWow.  That's black magic.  You are blocking the threads from\ndoing multiple searches at once by synchonizing on the static\nmethod, but since you are in an instance method you had to pass\neverything through as arguments.  :-|\n\nIt would be a lot easier to follow if this was an instance member,\nand thus had access to the instance fields, and if you used an\nexplicit lock, e.g.:\n\n\tprivate static final Object EXEC_LOCK = new Object();\n\n\tpublic void run() {\n\t\tsynchronized (EXEC_LOCK) {\n\t\t\texecFind();\n\t\t}\n\t}\n\n\tprivate void execFind() { ... }\n\n\n-- \nShawn.\n"},{"id":"73472","messageId":"47F1AF86.3030503@intelinet.com.br","threadId":"12919","inReplyTo":"20080331061914.GK10274@spearce.org","subject":"Re: [EGIT PATCH 2/4] FindToolbar port to the new history page.","fromName":"Roger C. Soares","fromEmail":"rogersoares@intelinet.com.br","sentAt":"2008-04-01T03:44:06Z","receivedAt":"2008-04-01T03:44:06Z","isPatch":true,"sender":{"key":"rogersoares@intelinet.com.br","avatar":null},"body":"\nShawn O. Pearce escreveu:\n> Hmm.  So what functionality did the highlight flag + RevFilter\n> not get you?  It supports both regex as well as non-regex matches,\n> is quick, and can be joined together with other filters.  A lot of\n> the code in the FindToolbarThread should drop out.\n>   \nOk, so, my motivation first. I don't have too much time to work on egit \nbut I'm interested in using your work in my build. So, I'm pushing the \nfeatures I need. This patch was intended as a simple port of the \nexisting FindToolbar to the new history page so I can use it.\n\nI tried the applyFlags you described but it doesn't have a monitor \napproach to give feedback to the toolbar so it knows when to refresh the \ntable and to select the first match, or to go red when nothing was \nfound. I also couldn't find from the highlight flag + RevFilter solution \nhow to get the total rows encountered and the index of a match so the \ntoolbar can show that the selected match is number 2 from 10.\n\nSo, this patch was intented as a port. I'm not sure everything related \nto search should go inside jgit, but I agree that RevFilters should be \nreused. I was thinking about it as an improvement after the port, it's \nnot my priority right now but someone else can do it? ;)\n\n\n> This field isn't necessary.  \"table.getTable()\" will get you the\n> same widget.\n>   \nOk.\n\n\n> So this is doing basically the same thing as the highlight RevFlag\n> (give a boolean about match status for a given RevCommit) but needs\n> to consult a HashMap by creating a temporary boxed Integer, and this\n> is deep down inside of the painting code for the table.  Urrgh.\n>   \nThe map is used to give the x from total information. When using a \nVIRTUAL table it doesn't have a noticable performance impact because \nonly a small set is required at a time.\n\nRobin sent a patch some time ago to change those new Integer() to \nInteger.valueOf(), I guess he didn't push it yet.\n\n\n> These strings should be in UIText, and UIText.properties, so they\n> can be translated strings.\n>   \nOk.\n\n\n> Would it make sense to abstract out and reuse the BooleanPrefAction\n> class I added to GitHistoryPage in ea3f1e7a7684b8?  \n>   \nProbably, I'll get a look on it.\n\n\n> Shouldn't this maybe be a Job instead, and scheduled on the History\n> site so the History title bar goes italics and the user can see\n> the progress meter in the status bar of the workspace and in their\n> Progress view?\n>   \nMaybe. When I wrote this I wasn't aware about the existence of the Job \ninterface. Then when I met it I thought about replacing it, but havent't \ndone it yet.\n\n\n> I'm not an SWT expert, but I think hanging onto a Display reference\n> from a class static isn't a good idea.\n>   \nYou're right.\n\n\n> Shouldn't this be a volatile, or an AtomicInteger, or protected by\n> a synchronized block?\n>   \nYep.\n\n\n> Wow.  That's black magic.  You are blocking the threads from\n> doing multiple searches at once by synchonizing on the static\n> method, but since you are in an instance method you had to pass\n> everything through as arguments.  :-|\n>\n> It would be a lot easier to follow if this was an instance member,\n> and thus had access to the instance fields, and if you used an\n> explicit lock, e.g.:\n>\n> \tprivate static final Object EXEC_LOCK = new Object();\n>\n> \tpublic void run() {\n> \t\tsynchronized (EXEC_LOCK) {\n> \t\t\texecFind();\n> \t\t}\n> \t}\n>\n> \tprivate void execFind() { ... }\n>   \nYep, looks better.\n\n[]s,\nRoger.\n"},{"id":"73474","messageId":"20080401040241.GQ10274@spearce.org","threadId":"12919","inReplyTo":"47F1AF86.3030503@intelinet.com.br","subject":"Re: [EGIT PATCH 2/4] FindToolbar port to the new history page.","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-04-01T04:02:41Z","receivedAt":"2008-04-01T04:02:41Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"\"Roger C. Soares\" <rogersoares@intelinet.com.br> wrote:\n> Shawn O. Pearce escreveu:\n> >Hmm.  So what functionality did the highlight flag + RevFilter\n> >not get you?  It supports both regex as well as non-regex matches,\n> >is quick, and can be joined together with other filters.  A lot of\n> >the code in the FindToolbarThread should drop out.\n> \n> Ok, so, my motivation first. I don't have too much time to work on egit \n> but I'm interested in using your work in my build. So, I'm pushing the \n> features I need. This patch was intended as a simple port of the \n> existing FindToolbar to the new history page so I can use it.\n\nOK, that makes sense.  Under that basis I'm willing to take your port\nin, especially if the other items I mentioned that you said \"Ok\" to\nwere cleaned up.\n\n> I tried the applyFlags you described but it doesn't have a monitor \n> approach to give feedback to the toolbar so it knows when to refresh the \n> table and to select the first match, or to go red when nothing was \n> found. I also couldn't find from the highlight flag + RevFilter solution \n> how to get the total rows encountered and the index of a match so the \n> toolbar can show that the selected match is number 2 from 10.\n\nOK.  Major gaps in the jgit API.  I now understand better what you\nwere needing here.  I'll probably go another around on that API\nsoon and see if I can't update your port once I have these things\ndown at the jgit level.\n \n> So, this patch was intented as a port. I'm not sure everything related \n> to search should go inside jgit, but I agree that RevFilters should be \n> reused. I was thinking about it as an improvement after the port, it's \n> not my priority right now but someone else can do it? ;)\n\nRight.  :)\n\n> >So this is doing basically the same thing as the highlight RevFlag\n> >(give a boolean about match status for a given RevCommit) but needs\n> >to consult a HashMap by creating a temporary boxed Integer, and this\n> >is deep down inside of the painting code for the table.  Urrgh.\n> \n> The map is used to give the x from total information. When using a \n> VIRTUAL table it doesn't have a noticable performance impact because \n> only a small set is required at a time.\n\nHmm.  Not really.  We're still beating on that paint listener every\ntime the screen needs to draw.  I don't think SWT is double buffering\nthe table either, so every redraw event is coming through this code.\nBe nice if we didn't have to suffer through a HashMap hit every time.\n\n> >Would it make sense to abstract out and reuse the BooleanPrefAction\n> >class I added to GitHistoryPage in ea3f1e7a7684b8?  \n> >  \n> Probably, I'll get a look on it.\n\nThis is maybe something to clean up later, after the port is\ninitially in my tree.\n\n-- \nShawn.\n"}]}