File indexing completed on 2024-11-24 04:53:27
0001 /* Copyright (C) 2006 - 2016 Jan Kundrát <jkt@kde.org> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include "Common/StashingReverseIterator.h" 0024 #include "UiUtils/QaimDfsIterator.h" 0025 0026 namespace UiUtils { 0027 0028 QaimDfsIterator::QaimDfsIterator(const QModelIndex &index) 0029 : m_current(index) 0030 , m_model(index.model()) 0031 { 0032 Q_ASSERT(m_model); 0033 } 0034 0035 QaimDfsIterator::QaimDfsIterator(const QModelIndex &index, const QAbstractItemModel *model) 0036 : m_current(index) 0037 , m_model(model) 0038 { 0039 Q_ASSERT(m_model); 0040 Q_ASSERT(!index.isValid() || m_model == index.model()); 0041 } 0042 0043 QaimDfsIterator::QaimDfsIterator() 0044 : m_model(nullptr) 0045 { 0046 } 0047 0048 QaimDfsIterator &QaimDfsIterator::operator++() 0049 { 0050 bool wentUp = false; 0051 while (m_current.isValid()) { 0052 // if there are (unvisited) children, descent into them 0053 auto firstChild = m_model->index(0, 0, m_current); 0054 if (!wentUp && firstChild.isValid()) { 0055 m_current = firstChild; 0056 return *this; 0057 } 0058 0059 // if there are siblings, go there; we surely haven't been there yet 0060 auto nextSibling = m_current.sibling(m_current.row() + 1, 0); 0061 if (nextSibling.isValid()) { 0062 m_current = nextSibling; 0063 return *this; 0064 } 0065 0066 // else, check our parent 0067 m_current = m_current.parent(); 0068 wentUp = true; 0069 } 0070 Q_ASSERT(!m_current.isValid()); 0071 return *this; 0072 } 0073 0074 QaimDfsIterator &QaimDfsIterator::operator--() 0075 { 0076 auto deepestChild = [](const QAbstractItemModel *model, QModelIndex where) -> QModelIndex { 0077 while (model->hasChildren(where)) { 0078 where = model->index(model->rowCount(where) - 1, 0, where); 0079 } 0080 return where; 0081 }; 0082 0083 // special case: do not wrap around 0084 if (!m_model) { 0085 return *this; 0086 } 0087 0088 // special case: if we're at the end, find "the last child" 0089 if (!m_current.isValid()) { 0090 m_current = deepestChild(m_model, QModelIndex()); 0091 return *this; 0092 } 0093 0094 // check if we can visit our previous sibling 0095 auto previousSibling = m_current.sibling(m_current.row() - 1, 0); 0096 if (previousSibling.isValid()) { 0097 // actually, its children, if there are any 0098 m_current = deepestChild(previousSibling.model(), previousSibling); 0099 return *this; 0100 } 0101 0102 // OK, we're a first child of something, apparently 0103 Q_ASSERT(m_current.row() == 0); 0104 if (!m_current.parent().isValid()) { 0105 // a special case: prevent circling back to the very end 0106 m_model = nullptr; 0107 } 0108 m_current = m_current.parent(); 0109 return *this; 0110 } 0111 0112 const QModelIndex &QaimDfsIterator::operator*() const 0113 { 0114 return m_current; 0115 } 0116 0117 const QModelIndex *QaimDfsIterator::operator->() const 0118 { 0119 return &m_current; 0120 } 0121 0122 bool QaimDfsIterator::operator==(const QaimDfsIterator &other) 0123 { 0124 if (m_model) { 0125 return m_current == other.m_current; 0126 } else { 0127 return !other.m_model || !other.m_current.isValid(); 0128 } 0129 } 0130 0131 bool QaimDfsIterator::operator!=(const QaimDfsIterator &other) 0132 { 0133 return m_current != other.m_current; 0134 // yes, this ignores m_model, which means that an iterator which got decremented too much 0135 // won't be considered different from one which is at the end 0136 } 0137 0138 /** @short Search between start1 and end1, wrap around to start2 and continue to end2 */ 0139 template <typename Iterator> 0140 static void wrappedFind(Iterator start1, Iterator end1, Iterator start2, Iterator end2, 0141 std::function<bool(typename std::iterator_traits<Iterator>::reference)> itValidityChecker, 0142 std::function<bool(typename std::iterator_traits<Iterator>::reference)> matcher, 0143 std::function<void(typename std::iterator_traits<Iterator>::reference)> onSuccess, 0144 std::function<void()> onFailure) 0145 { 0146 auto it = start1; 0147 auto rejectedResult = end1; 0148 if (itValidityChecker(*it)) { 0149 it = std::find_if(it, end1, matcher); 0150 } 0151 0152 if (!itValidityChecker(*it)) { 0153 it = std::find_if(start2, end2, matcher); 0154 rejectedResult = end2; 0155 } 0156 0157 if (itValidityChecker(*it) && it != rejectedResult) { 0158 onSuccess(*it); 0159 } else { 0160 onFailure(); 0161 } 0162 } 0163 0164 void gotoNext(const QAbstractItemModel *model, const QModelIndex ¤tIndex, 0165 std::function<bool(const QModelIndex &)> matcher, 0166 std::function<void(const QModelIndex &)> onSuccess, 0167 std::function<void()> onFailure) 0168 { 0169 auto it = UiUtils::QaimDfsIterator(currentIndex, model); 0170 ++it; 0171 // explicitly specify the template to help GCC 4.8.5 realize that our lambda is copmatible with that std::function 0172 UiUtils::wrappedFind<decltype(it)>( 0173 it, 0174 QaimDfsIterator(QModelIndex(), model), 0175 QaimDfsIterator(model->index(0, 0, QModelIndex()), model), 0176 QaimDfsIterator(currentIndex, model), 0177 [](const QModelIndex &idx) { return idx.isValid(); }, 0178 matcher, 0179 onSuccess, 0180 onFailure); 0181 } 0182 0183 void gotoPrevious(const QAbstractItemModel *model, const QModelIndex ¤tIndex, 0184 std::function<bool(const QModelIndex &)> matcher, 0185 std::function<void(const QModelIndex &)> onSuccess, 0186 std::function<void()> onFailure) 0187 { 0188 auto it = Common::make_stashing_reverse_iterator(QaimDfsIterator(currentIndex, model)); 0189 --it; 0190 0191 UiUtils::wrappedFind<decltype(it)>( 0192 Common::make_stashing_reverse_iterator(QaimDfsIterator(currentIndex, model)), 0193 Common::make_stashing_reverse_iterator(QaimDfsIterator(model->index(0, 0, QModelIndex()), model)), 0194 Common::make_stashing_reverse_iterator(QaimDfsIterator(QModelIndex(), model)), 0195 it, 0196 [](const QModelIndex &idx) { return idx.isValid(); }, 0197 matcher, 0198 onSuccess, 0199 onFailure); 0200 } 0201 0202 }