1 /*
2 Copyright © 2019 by The qTox Project Contributors
3
4 This file is part of qTox, a Qt-based graphical interface for Tox.
5
6 qTox is libre software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 qTox is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "genericchatitemlayout.h"
21 #include "genericchatitemwidget.h"
22 #include <QBoxLayout>
23 #include <QCollator>
24 #include <cassert>
25
26 // As this layout sorts widget, extra care must be taken when inserting widgets.
27 // Prefer using the build in add and remove functions for modifying widgets.
28 // Inserting widgets other ways would cause this layout to be unable to sort.
29 // As such, they are protected using asserts.
30
GenericChatItemLayout()31 GenericChatItemLayout::GenericChatItemLayout()
32 : layout(new QVBoxLayout())
33 {
34 }
35
~GenericChatItemLayout()36 GenericChatItemLayout::~GenericChatItemLayout()
37 {
38 delete layout;
39 }
40
addSortedWidget(GenericChatItemWidget * widget,int stretch,Qt::Alignment alignment)41 void GenericChatItemLayout::addSortedWidget(GenericChatItemWidget* widget, int stretch,
42 Qt::Alignment alignment)
43 {
44 int closest = indexOfClosestSortedWidget(widget);
45 layout->insertWidget(closest, widget, stretch, alignment);
46 }
47
indexOfSortedWidget(GenericChatItemWidget * widget) const48 int GenericChatItemLayout::indexOfSortedWidget(GenericChatItemWidget* widget) const
49 {
50 if (layout->isEmpty())
51 return -1;
52
53 int index = indexOfClosestSortedWidget(widget);
54
55 if (index >= layout->count())
56 return -1;
57
58 GenericChatItemWidget* atMid =
59 qobject_cast<GenericChatItemWidget*>(layout->itemAt(index)->widget());
60 assert(atMid != nullptr);
61
62 if (atMid == widget)
63 return index;
64
65 return -1;
66 }
67
existsSortedWidget(GenericChatItemWidget * widget) const68 bool GenericChatItemLayout::existsSortedWidget(GenericChatItemWidget* widget) const
69 {
70 return indexOfSortedWidget(widget) != -1;
71 }
72
removeSortedWidget(GenericChatItemWidget * widget)73 void GenericChatItemLayout::removeSortedWidget(GenericChatItemWidget* widget)
74 {
75 if (layout->isEmpty())
76 return;
77
78 int index = indexOfClosestSortedWidget(widget);
79
80 if (layout->itemAt(index) == nullptr)
81 return;
82
83 GenericChatItemWidget* atMid =
84 qobject_cast<GenericChatItemWidget*>(layout->itemAt(index)->widget());
85 assert(atMid != nullptr);
86
87 if (atMid == widget)
88 layout->removeWidget(widget);
89 }
90
search(const QString & searchString,bool hideAll)91 void GenericChatItemLayout::search(const QString& searchString, bool hideAll)
92 {
93 for (int index = 0; index < layout->count(); ++index) {
94 GenericChatItemWidget* widgetAt =
95 qobject_cast<GenericChatItemWidget*>(layout->itemAt(index)->widget());
96 assert(widgetAt != nullptr);
97
98 widgetAt->searchName(searchString, hideAll);
99 }
100 }
101
getLayout() const102 QLayout* GenericChatItemLayout::getLayout() const
103 {
104 return layout;
105 }
106
indexOfClosestSortedWidget(GenericChatItemWidget * widget) const107 int GenericChatItemLayout::indexOfClosestSortedWidget(GenericChatItemWidget* widget) const
108 {
109 // Binary search: Deferred test of equality.
110 int min = 0, max = layout->count();
111 while (min < max) {
112 int mid = (max - min) / 2 + min;
113 GenericChatItemWidget* atMid =
114 qobject_cast<GenericChatItemWidget*>(layout->itemAt(mid)->widget());
115 assert(atMid != nullptr);
116
117 bool lessThan = false;
118
119 QCollator collator;
120 collator.setNumericMode(true);
121
122 int compareValue = collator.compare(atMid->getName(), widget->getName());
123
124 if (compareValue < 0)
125 lessThan = true;
126 else if (compareValue == 0)
127 lessThan = atMid < widget; // Consistent ordering.
128
129 if (lessThan)
130 min = mid + 1;
131 else
132 max = mid;
133 }
134 return min;
135 }
136