1 /*
2 Copyright © 2015-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 "categorywidget.h"
21 #include "friendlistlayout.h"
22 #include "friendlistwidget.h"
23 #include "friendwidget.h"
24 #include "src/model/status.h"
25 #include "src/widget/style.h"
26 #include "tool/croppinglabel.h"
27 #include <QBoxLayout>
28 #include <QMouseEvent>
29
30 #include <QApplication>
31
emitChatroomWidget(QLayout * layout,int index)32 void CategoryWidget::emitChatroomWidget(QLayout* layout, int index)
33 {
34 QWidget* widget = layout->itemAt(index)->widget();
35 GenericChatroomWidget* chatWidget = qobject_cast<GenericChatroomWidget*>(widget);
36 if (chatWidget != nullptr) {
37 emit chatWidget->chatroomWidgetClicked(chatWidget);
38 }
39 }
40
CategoryWidget(bool compact,QWidget * parent)41 CategoryWidget::CategoryWidget(bool compact, QWidget* parent)
42 : GenericChatItemWidget(compact, parent)
43 {
44 container = new QWidget(this);
45 container->setObjectName("circleWidgetContainer");
46 container->setLayoutDirection(Qt::LeftToRight);
47
48 statusLabel = new QLabel(this);
49 statusLabel->setObjectName("status");
50 statusLabel->setTextFormat(Qt::PlainText);
51
52 statusPic.setPixmap(QPixmap(Style::getImagePath("chatArea/scrollBarRightArrow.svg")));
53
54 fullLayout = new QVBoxLayout(this);
55 fullLayout->setSpacing(0);
56 fullLayout->setMargin(0);
57 fullLayout->addWidget(container);
58
59 lineFrame = new QFrame(container);
60 lineFrame->setObjectName("line");
61 lineFrame->setFrameShape(QFrame::HLine);
62 lineFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
63 lineFrame->resize(0, 0);
64
65 listLayout = new FriendListLayout();
66 listWidget = new QWidget(this);
67 listWidget->setLayout(listLayout);
68 fullLayout->addWidget(listWidget);
69
70 setAcceptDrops(true);
71
72 onCompactChanged(isCompact());
73
74 setExpanded(true, false);
75 updateStatus();
76 }
77
isExpanded() const78 bool CategoryWidget::isExpanded() const
79 {
80 return expanded;
81 }
82
setExpanded(bool isExpanded,bool save)83 void CategoryWidget::setExpanded(bool isExpanded, bool save)
84 {
85 if (expanded == isExpanded) {
86 return;
87 }
88 expanded = isExpanded;
89 setMouseTracking(true);
90 listWidget->setVisible(isExpanded);
91
92 QString pixmapPath;
93 if (isExpanded)
94 pixmapPath = Style::getImagePath("chatArea/scrollBarDownArrow.svg");
95 else
96 pixmapPath = Style::getImagePath("chatArea/scrollBarRightArrow.svg");
97 statusPic.setPixmap(QPixmap(pixmapPath));
98 // The listWidget will recieve a enterEvent for some reason if now visible.
99 // Using the following, we prevent that.
100 QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers);
101 container->hide();
102 container->show();
103
104 if (save)
105 onExpand();
106 }
107
leaveEvent(QEvent * event)108 void CategoryWidget::leaveEvent(QEvent* event)
109 {
110 event->ignore();
111 }
112
setName(const QString & name,bool save)113 void CategoryWidget::setName(const QString& name, bool save)
114 {
115 nameLabel->setText(name);
116
117 if (isCompact())
118 nameLabel->minimizeMaximumWidth();
119
120 if (save)
121 onSetName();
122 }
123
editName()124 void CategoryWidget::editName()
125 {
126 nameLabel->editBegin();
127 nameLabel->setMaximumWidth(QWIDGETSIZE_MAX);
128 }
129
addFriendWidget(FriendWidget * w,Status::Status s)130 void CategoryWidget::addFriendWidget(FriendWidget* w, Status::Status s)
131 {
132 listLayout->addFriendWidget(w, s);
133 updateStatus();
134 onAddFriendWidget(w);
135 w->reloadTheme(); // Otherwise theme will change when moving to another circle.
136 }
137
removeFriendWidget(FriendWidget * w,Status::Status s)138 void CategoryWidget::removeFriendWidget(FriendWidget* w, Status::Status s)
139 {
140 listLayout->removeFriendWidget(w, s);
141 updateStatus();
142 }
143
updateStatus()144 void CategoryWidget::updateStatus()
145 {
146 QString online = QString::number(listLayout->friendOnlineCount());
147 QString offline = QString::number(listLayout->friendTotalCount());
148 QString text = online + QStringLiteral(" / ") + offline;
149 statusLabel->setText(text);
150 }
151
hasChatrooms() const152 bool CategoryWidget::hasChatrooms() const
153 {
154 return listLayout->hasChatrooms();
155 }
156
search(const QString & searchString,bool updateAll,bool hideOnline,bool hideOffline)157 void CategoryWidget::search(const QString& searchString, bool updateAll, bool hideOnline,
158 bool hideOffline)
159 {
160 if (updateAll) {
161 listLayout->searchChatrooms(searchString, hideOnline, hideOffline);
162 }
163 bool inCategory = searchString.isEmpty() && !(hideOnline && hideOffline);
164 setVisible(inCategory || listLayout->hasChatrooms());
165 }
166
cycleContacts(bool forward)167 bool CategoryWidget::cycleContacts(bool forward)
168 {
169 if (listLayout->friendTotalCount() == 0) {
170 return false;
171 }
172 if (forward) {
173 if (!listLayout->getLayoutOnline()->isEmpty()) {
174 setExpanded(true);
175 emitChatroomWidget(listLayout->getLayoutOnline(), 0);
176 return true;
177 } else if (!listLayout->getLayoutOffline()->isEmpty()) {
178 setExpanded(true);
179 emitChatroomWidget(listLayout->getLayoutOffline(), 0);
180 return true;
181 }
182 } else {
183 if (!listLayout->getLayoutOffline()->isEmpty()) {
184 setExpanded(true);
185 emitChatroomWidget(listLayout->getLayoutOffline(),
186 listLayout->getLayoutOffline()->count() - 1);
187 return true;
188 } else if (!listLayout->getLayoutOnline()->isEmpty()) {
189 setExpanded(true);
190 emitChatroomWidget(listLayout->getLayoutOnline(),
191 listLayout->getLayoutOnline()->count() - 1);
192 return true;
193 }
194 }
195 return false;
196 }
197
cycleContacts(FriendWidget * activeChatroomWidget,bool forward)198 bool CategoryWidget::cycleContacts(FriendWidget* activeChatroomWidget, bool forward)
199 {
200 int index = -1;
201 QLayout* currentLayout = nullptr;
202
203 FriendWidget* friendWidget = qobject_cast<FriendWidget*>(activeChatroomWidget);
204 if (friendWidget == nullptr)
205 return false;
206
207 currentLayout = listLayout->getLayoutOnline();
208 index = listLayout->indexOfFriendWidget(friendWidget, true);
209 if (index == -1) {
210 currentLayout = listLayout->getLayoutOffline();
211 index = listLayout->indexOfFriendWidget(friendWidget, false);
212 }
213
214 index += forward ? 1 : -1;
215 for (;;) {
216 // Bounds checking.
217 if (index < 0) {
218 if (currentLayout == listLayout->getLayoutOffline())
219 currentLayout = listLayout->getLayoutOnline();
220 else
221 return false;
222
223 index = currentLayout->count() - 1;
224 continue;
225 } else if (index >= currentLayout->count()) {
226 if (currentLayout == listLayout->getLayoutOnline())
227 currentLayout = listLayout->getLayoutOffline();
228 else
229 return false;
230
231 index = 0;
232 continue;
233 }
234
235 GenericChatroomWidget* chatWidget =
236 qobject_cast<GenericChatroomWidget*>(currentLayout->itemAt(index)->widget());
237 if (chatWidget != nullptr)
238 emit chatWidget->chatroomWidgetClicked(chatWidget);
239 return true;
240 }
241
242 return false;
243 }
244
onCompactChanged(bool _compact)245 void CategoryWidget::onCompactChanged(bool _compact)
246 {
247 delete topLayout;
248 delete mainLayout;
249
250 topLayout = new QHBoxLayout;
251 topLayout->setSpacing(0);
252 topLayout->setMargin(0);
253
254 Q_UNUSED(_compact);
255 setCompact(true);
256
257 nameLabel->minimizeMaximumWidth();
258
259 mainLayout = nullptr;
260
261 container->setFixedHeight(25);
262 container->setLayout(topLayout);
263
264 topLayout->addSpacing(18);
265 topLayout->addWidget(&statusPic);
266 topLayout->addSpacing(5);
267 topLayout->addWidget(nameLabel, 100);
268 topLayout->addWidget(lineFrame, 1);
269 topLayout->addSpacing(5);
270 topLayout->addWidget(statusLabel);
271 topLayout->addSpacing(5);
272 topLayout->activate();
273
274 Style::repolish(this);
275 }
276
mouseReleaseEvent(QMouseEvent * event)277 void CategoryWidget::mouseReleaseEvent(QMouseEvent* event)
278 {
279 if (event->button() == Qt::LeftButton)
280 setExpanded(!expanded);
281 }
282
setContainerAttribute(Qt::WidgetAttribute attribute,bool enabled)283 void CategoryWidget::setContainerAttribute(Qt::WidgetAttribute attribute, bool enabled)
284 {
285 container->setAttribute(attribute, enabled);
286 Style::repolish(container);
287 }
288
friendOfflineLayout() const289 QLayout* CategoryWidget::friendOfflineLayout() const
290 {
291 return listLayout->getLayoutOffline();
292 }
293
friendOnlineLayout() const294 QLayout* CategoryWidget::friendOnlineLayout() const
295 {
296 return listLayout->getLayoutOnline();
297 }
298
moveFriendWidgets(FriendListWidget * friendList)299 void CategoryWidget::moveFriendWidgets(FriendListWidget* friendList)
300 {
301 listLayout->moveFriendWidgets(friendList);
302 }
303