From 3c5d7378340309ab5d5d41a2c5feb7527d8e53c4 Mon Sep 17 00:00:00 2001
From: Eike Ziller <eike.ziller@digia.com>
Date: Wed, 17 Apr 2013 13:16:16 +0200
Subject: [PATCH] Fix searching in search results (and other tree views).

Task-number: QTCREATORBUG-9066

Change-Id: I76b7916b4ce64c400c175e72edc2b0a3ef015156
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Aurindam Jana <aurindam.jana@digia.com>
---
 src/plugins/find/treeviewfind.cpp            |  74 ++++-----
 tests/auto/auto.pro                          |   1 +
 tests/auto/treeviewfind/treeviewfind.pro     |   8 +
 tests/auto/treeviewfind/tst_treeviewfind.cpp | 156 +++++++++++++++++++
 4 files changed, 203 insertions(+), 36 deletions(-)
 create mode 100644 tests/auto/treeviewfind/treeviewfind.pro
 create mode 100644 tests/auto/treeviewfind/tst_treeviewfind.cpp

diff --git a/src/plugins/find/treeviewfind.cpp b/src/plugins/find/treeviewfind.cpp
index 292e075e359..4d3a3ccc112 100644
--- a/src/plugins/find/treeviewfind.cpp
+++ b/src/plugins/find/treeviewfind.cpp
@@ -166,7 +166,7 @@ IFindSupport::Result TreeViewFind::find(const QString &searchTxt,
                                                           Qt::CaseInsensitive));
                 if (searchExpr.indexIn(text) != -1
                         && d->m_view->model()->flags(index) & Qt::ItemIsSelectable
-                        && currentRow != index.row())
+                        && (index.row() != currentRow || index.parent() != currentIndex.parent()))
                     resultIndex = index;
             } else {
                 QTextDocument doc(text);
@@ -174,7 +174,7 @@ IFindSupport::Result TreeViewFind::find(const QString &searchTxt,
                               flags & (Find::FindCaseSensitively |
                                        Find::FindWholeWords)).isNull()
                         && d->m_view->model()->flags(index) & Qt::ItemIsSelectable
-                        && currentRow != index.row())
+                        && (index.row() != currentRow || index.parent() != currentIndex.parent()))
                     resultIndex = index;
             }
         }
@@ -202,33 +202,35 @@ QModelIndex TreeViewFind::nextIndex(const QModelIndex &idx, bool *wrapped) const
     if (!idx.isValid())
         return model->index(0, 0);
 
+    // same parent has more columns, go to next column
+    if (idx.column() + 1 < model->columnCount(idx.parent()))
+        return model->index(idx.row(), idx.column() + 1, idx.parent());
 
-    if (model->rowCount(idx) > 0) {
-        // node with children
-        return idx.child(0, 0);
+    // tree views have their children attached to first column
+    // make sure we are at first column
+    QModelIndex current = model->index(idx.row(), 0, idx.parent());
+
+    // check for children
+    if (model->rowCount(current) > 0) {
+        return current.child(0, 0);
     }
-    // leaf node
+
+    // no more children, go up and look for parent with more children
     QModelIndex nextIndex;
-    QModelIndex current = idx;
     while (!nextIndex.isValid()) {
         int row = current.row();
-        int column = current.column();
         current = current.parent();
 
-        if (column + 1 < model->columnCount(current)) {
-            nextIndex = model->index(row, column + 1, current);
+        if (row + 1 < model->rowCount(current)) {
+            // Same parent has another child
+            nextIndex = model->index(row + 1, 0, current);
         } else {
-            if (row + 1 < model->rowCount(current)) {
-                // Same parent has another child
-                nextIndex = model->index(row + 1, 0, current);
-            } else {
-                // go up one parent
-                if (!current.isValid()) {
-                    // we start from the beginning
-                    if (wrapped)
-                        *wrapped = true;
-                    nextIndex = model->index(0, 0);
-                }
+            // go up one parent
+            if (!current.isValid()) {
+                // we start from the beginning
+                if (wrapped)
+                    *wrapped = true;
+                nextIndex = model->index(0, 0);
             }
         }
     }
@@ -239,34 +241,34 @@ QModelIndex TreeViewFind::prevIndex(const QModelIndex &idx, bool *wrapped) const
 {
     if (wrapped)
         *wrapped = false;
+    QAbstractItemModel *model = d->m_view->model();
+    // if same parent has earlier columns, just move there
+    if (idx.column() > 0)
+        return model->index(idx.row(), idx.column() - 1, idx.parent());
+
     QModelIndex current = idx;
     bool checkForChildren = true;
-    QAbstractItemModel *model = d->m_view->model();
     if (current.isValid()) {
         int row = current.row();
-        int column = current.column();
-        if (column > 0) {
-            current = model->index(row, column - 1, current.parent());
+        if (row > 0) {
+            current = model->index(row - 1, 0, current.parent());
         } else {
-            if (row > 0) {
-                current = model->index(row - 1, model->columnCount(current.parent()) - 1,
-                                       current.parent());
-            } else {
-                current = current.parent();
-                checkForChildren = !current.isValid();
-                if (checkForChildren && wrapped) {
-                    // we start from the end
-                    *wrapped = true;
-                }
+            current = current.parent();
+            checkForChildren = !current.isValid();
+            if (checkForChildren && wrapped) {
+                // we start from the end
+                *wrapped = true;
             }
         }
     }
     if (checkForChildren) {
         // traverse down the hierarchy
         while (int rc = model->rowCount(current)) {
-            current = model->index(rc - 1, model->columnCount(current) - 1, current);
+            current = model->index(rc - 1, 0, current);
         }
     }
+    // set to last column
+    current = model->index(current.row(), model->columnCount(current.parent()) - 1, current.parent());
     return current;
 }
 
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 449876b36e5..2c2bc7ba7ab 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -10,6 +10,7 @@ SUBDIRS += \
     fakevim \
     generichighlighter \
     profilewriter \
+    treeviewfind \
     ioutils \
     qtcprocess \
     utils \
diff --git a/tests/auto/treeviewfind/treeviewfind.pro b/tests/auto/treeviewfind/treeviewfind.pro
new file mode 100644
index 00000000000..00aa98ae5d2
--- /dev/null
+++ b/tests/auto/treeviewfind/treeviewfind.pro
@@ -0,0 +1,8 @@
+include(../qttest.pri)
+
+include($$IDE_SOURCE_TREE/src/plugins/find/find.pri)
+
+LIBS *= -L$$IDE_LIBRARY_PATH/QtProject
+
+SOURCES += \
+    tst_treeviewfind.cpp
diff --git a/tests/auto/treeviewfind/tst_treeviewfind.cpp b/tests/auto/treeviewfind/tst_treeviewfind.cpp
new file mode 100644
index 00000000000..0e288ca0b0f
--- /dev/null
+++ b/tests/auto/treeviewfind/tst_treeviewfind.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include <find/treeviewfind.h>
+
+#include <QtTest>
+
+#include <QTreeWidget>
+
+class tst_treeviewfind : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void wrapping();
+    void columns();
+};
+
+
+void tst_treeviewfind::wrapping()
+{
+    // set up tree
+    //   search for FOO in
+    //   * HEADER1
+    //     * FOO1
+    //   * HEADER2
+    //     * A
+    //   * HEADER3
+    //     * FOO2
+    QTreeWidget *tree = new QTreeWidget;
+    tree->setColumnCount(1);
+    QList<QTreeWidgetItem *> toplevelitems;
+    QTreeWidgetItem *item;
+    item = new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("HEADER1"));
+    item->addChild(new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("FOO1")));
+    toplevelitems << item;
+    item = new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("HEADER2"));
+    item->addChild(new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("A")));
+    toplevelitems << item;
+    item = new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("HEADER3"));
+    item->addChild(new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("FOO2")));
+    toplevelitems << item;
+    tree->addTopLevelItems(toplevelitems);
+
+    // set up
+    Find::TreeViewFind *findSupport = new Find::TreeViewFind(tree);
+    tree->setCurrentItem(toplevelitems.at(2)->child(0));
+    QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("FOO2"));
+
+    // forward
+    findSupport->findStep(QLatin1String("FOO"), 0);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0));
+
+    // backward
+    tree->setCurrentItem(toplevelitems.at(0)->child(0));
+    QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("FOO1"));
+    findSupport->findStep(QLatin1String("FOO"), Find::FindBackward);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(2)->child(0));
+
+    // clean up
+    delete findSupport;
+    delete tree;
+}
+
+void tst_treeviewfind::columns()
+{
+    // set up tree
+    //   search for FOO in
+    //   * HEADER1 | HEADER1
+    //     * FOO1  | A
+    //   * HEADER2 | FOOHEADER2
+    //     * FOO2  | FOO3
+    //   * HEADER3 | HEADER2
+    //     * A     | FOO4
+    QTreeWidget *tree = new QTreeWidget;
+    tree->setColumnCount(2);
+    QList<QTreeWidgetItem *> toplevelitems;
+    QTreeWidgetItem *item;
+    item = new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("HEADER1") << QLatin1String("HEADER1"));
+    item->addChild(new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("FOO1") << QLatin1String("A")));
+    toplevelitems << item;
+    item = new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("HEADER2") << QLatin1String("FOOHEADER2"));
+    item->addChild(new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("FOO2") << QLatin1String("FOO3")));
+    toplevelitems << item;
+    item = new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("HEADER3") << QLatin1String("HEADER3"));
+    item->addChild(new QTreeWidgetItem((QTreeWidget *)0, QStringList() << QLatin1String("A") << QLatin1String("FOO4")));
+    toplevelitems << item;
+    tree->addTopLevelItems(toplevelitems);
+
+    // set up
+    Find::TreeViewFind *findSupport = new Find::TreeViewFind(tree);
+    tree->setCurrentItem(toplevelitems.at(0));
+    QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("HEADER1"));
+
+    // find in first column
+    findSupport->findStep(QLatin1String("FOO"), 0);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0));
+    // find in second column of node with children
+    findSupport->findStep(QLatin1String("FOO"), 0);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(1));
+    // again find in first column
+    findSupport->findStep(QLatin1String("FOO"), 0);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(1)->child(0));
+    // don't stay in item if multiple columns match, and find in second column
+    findSupport->findStep(QLatin1String("FOO"), 0);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(2)->child(0));
+    // wrap
+    findSupport->findStep(QLatin1String("FOO"), 0);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0));
+
+    // backwards
+    tree->setCurrentItem(toplevelitems.at(2)->child(0));
+    QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("A"));
+    findSupport->findStep(QLatin1String("FOO"), Find::FindBackward);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(1)->child(0));
+    findSupport->findStep(QLatin1String("FOO"), Find::FindBackward);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(1));
+    findSupport->findStep(QLatin1String("FOO"), Find::FindBackward);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0));
+    findSupport->findStep(QLatin1String("FOO"), Find::FindBackward);
+    QCOMPARE(tree->currentItem(), toplevelitems.at(2)->child(0));
+
+    // clean up
+    delete findSupport;
+    delete tree;
+}
+
+QTEST_MAIN(tst_treeviewfind)
+
+#include "tst_treeviewfind.moc"
-- 
GitLab