qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
friendwidget.cpp
Go to the documentation of this file.
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 "friendwidget.h"
21 
22 #include "circlewidget.h"
23 #include "friendlistwidget.h"
24 #include "groupwidget.h"
25 #include "maskablepixmapwidget.h"
26 
27 #include "src/core/core.h"
28 #include "src/friendlist.h"
31 #include "src/model/friend.h"
32 #include "src/model/group.h"
33 #include "src/model/status.h"
37 #include "src/widget/style.h"
39 #include "src/widget/widget.h"
40 
41 #include <QApplication>
42 #include <QBitmap>
43 #include <QContextMenuEvent>
44 #include <QDrag>
45 #include <QFileDialog>
46 #include <QInputDialog>
47 #include <QMenu>
48 #include <QMimeData>
49 
50 #include <cassert>
51 
59 FriendWidget::FriendWidget(std::shared_ptr<FriendChatroom> chatroom, bool compact)
60  : GenericChatroomWidget(compact)
61  , chatroom{chatroom}
62  , isDefaultAvatar{true}
63 {
64  avatar->setPixmap(QPixmap(":/img/contact.svg"));
65  statusPic.setPixmap(QPixmap(Status::getIconPath(Status::Status::Offline)));
66  statusPic.setMargin(3);
67 
68  auto frnd = chatroom->getFriend();
69  nameLabel->setText(frnd->getDisplayedName());
70  // update alias when edited
71  connect(nameLabel, &CroppingLabel::editFinished, frnd, &Friend::setAlias);
72  // update on changes of the displayed name
73  connect(frnd, &Friend::displayedNameChanged, nameLabel, &CroppingLabel::setText);
74  connect(chatroom.get(), &FriendChatroom::activeChanged, this, &FriendWidget::setActive);
75  statusMessageLabel->setTextFormat(Qt::PlainText);
76 }
77 
85 void FriendWidget::contextMenuEvent(QContextMenuEvent* event)
86 {
87  emit contextMenuCalled(event);
88 }
89 
96 void FriendWidget::onContextMenuCalled(QContextMenuEvent* event)
97 {
98  if (!active) {
99  setBackgroundRole(QPalette::Highlight);
100  }
101 
102  installEventFilter(this); // Disable leave event.
103 
104  QMenu menu;
105 
106  if (chatroom->possibleToOpenInNewWindow()) {
107  const auto openChatWindow = menu.addAction(tr("Open chat in new window"));
108  connect(openChatWindow, &QAction::triggered, [=]() { emit newWindowOpened(this); });
109  }
110 
111  if (chatroom->canBeRemovedFromWindow()) {
112  const auto removeChatWindow = menu.addAction(tr("Remove chat from this window"));
113  connect(removeChatWindow, &QAction::triggered, this, &FriendWidget::removeChatWindow);
114  }
115 
116  menu.addSeparator();
117  QMenu* inviteMenu =
118  menu.addMenu(tr("Invite to group", "Menu to invite a friend to a groupchat"));
119  inviteMenu->setEnabled(chatroom->canBeInvited());
120  const auto newGroupAction = inviteMenu->addAction(tr("To new group"));
121  connect(newGroupAction, &QAction::triggered, chatroom.get(), &FriendChatroom::inviteToNewGroup);
122  inviteMenu->addSeparator();
123 
124  for (const auto& group : chatroom->getGroups()) {
125  const auto groupAction = inviteMenu->addAction(tr("Invite to group '%1'").arg(group.name));
126  connect(groupAction, &QAction::triggered, [=]() { chatroom->inviteFriend(group.group); });
127  }
128 
129  const auto circleId = chatroom->getCircleId();
130  auto circleMenu =
131  menu.addMenu(tr("Move to circle...", "Menu to move a friend into a different circle"));
132 
133  const auto newCircleAction = circleMenu->addAction(tr("To new circle"));
134  connect(newCircleAction, &QAction::triggered, this, &FriendWidget::moveToNewCircle);
135 
136  if (circleId != -1) {
137  const auto circleName = chatroom->getCircleName();
138  const auto removeCircleAction =
139  circleMenu->addAction(tr("Remove from circle '%1'").arg(circleName));
140  connect(removeCircleAction, &QAction::triggered, this, &FriendWidget::removeFromCircle);
141  }
142 
143  circleMenu->addSeparator();
144 
145  for (const auto& circle : chatroom->getOtherCircles()) {
146  QAction* action = new QAction(tr("Move to circle \"%1\"").arg(circle.name), circleMenu);
147  connect(action, &QAction::triggered, [=]() { moveToCircle(circle.circleId); });
148  circleMenu->addAction(action);
149  }
150 
151  const auto setAlias = menu.addAction(tr("Set alias..."));
152  connect(setAlias, &QAction::triggered, nameLabel, &CroppingLabel::editBegin);
153 
154  menu.addSeparator();
155  auto autoAccept =
156  menu.addAction(tr("Auto accept files from this friend", "context menu entry"));
157  autoAccept->setCheckable(true);
158  autoAccept->setChecked(!chatroom->autoAcceptEnabled());
159  connect(autoAccept, &QAction::triggered, this, &FriendWidget::changeAutoAccept);
160  menu.addSeparator();
161 
162  if (chatroom->friendCanBeRemoved()) {
163  const auto friendPk = chatroom->getFriend()->getPublicKey();
164  const auto removeAction =
165  menu.addAction(tr("Remove friend", "Menu to remove the friend from the friend list"));
166  connect(removeAction, &QAction::triggered, this, [=]() { emit removeFriend(friendPk); },
167  Qt::QueuedConnection);
168  }
169 
170  menu.addSeparator();
171  const auto aboutWindow = menu.addAction(tr("Show details"));
172  connect(aboutWindow, &QAction::triggered, this, &FriendWidget::showDetails);
173 
174  const auto pos = event->globalPos();
175  menu.exec(pos);
176 
177  removeEventFilter(this);
178 
179  if (!active) {
180  setBackgroundRole(QPalette::Window);
181  }
182 }
183 
185 {
186  chatroom->removeFriendFromDialogs();
187 }
188 
189 namespace {
190 
191 std::tuple<CircleWidget*, FriendListWidget*> getCircleAndFriendList(const Friend* frnd, FriendWidget* fw)
192 {
193  const auto pk = frnd->getPublicKey();
194  const auto circleId = Settings::getInstance().getFriendCircleID(pk);
195  auto circleWidget = CircleWidget::getFromID(circleId);
196  auto w = circleWidget ? static_cast<QWidget*>(circleWidget) : static_cast<QWidget*>(fw);
197  auto friendList = qobject_cast<FriendListWidget*>(w->parentWidget());
198  return std::make_tuple(circleWidget, friendList);
199 }
200 
201 } // namespace
202 
204 {
205  const auto frnd = chatroom->getFriend();
206  CircleWidget* circleWidget;
207  FriendListWidget* friendList;
208  std::tie(circleWidget, friendList) = getCircleAndFriendList(frnd, this);
209 
210  if (circleWidget != nullptr) {
211  circleWidget->updateStatus();
212  }
213 
214  if (friendList != nullptr) {
215  friendList->addCircleWidget(this);
216  } else {
217  const auto pk = frnd->getPublicKey();
218  auto& s = Settings::getInstance();
219  auto circleId = s.addCircle();
220  s.setFriendCircleID(pk, circleId);
221  }
222 }
223 
225 {
226  const auto frnd = chatroom->getFriend();
227  CircleWidget* circleWidget;
228  FriendListWidget* friendList;
229  std::tie(circleWidget, friendList) = getCircleAndFriendList(frnd, this);
230 
231  if (friendList != nullptr) {
232  friendList->moveWidget(this, frnd->getStatus(), true);
233  } else {
234  const auto pk = frnd->getPublicKey();
235  auto& s = Settings::getInstance();
236  s.setFriendCircleID(pk, -1);
237  }
238 
239  if (circleWidget != nullptr) {
240  circleWidget->updateStatus();
241  }
242 }
243 
244 void FriendWidget::moveToCircle(int newCircleId)
245 {
246  const auto frnd = chatroom->getFriend();
247  const auto pk = frnd->getPublicKey();
248  const auto oldCircleId = Settings::getInstance().getFriendCircleID(pk);
249  auto& s = Settings::getInstance();
250  auto oldCircleWidget = CircleWidget::getFromID(oldCircleId);
251  auto newCircleWidget = CircleWidget::getFromID(newCircleId);
252 
253  if (newCircleWidget) {
254  newCircleWidget->addFriendWidget(this, frnd->getStatus());
255  newCircleWidget->setExpanded(true);
256  s.savePersonal();
257  } else {
258  s.setFriendCircleID(pk, newCircleId);
259  }
260 
261  if (oldCircleWidget) {
262  oldCircleWidget->updateStatus();
263  }
264 }
265 
267 {
268  if (enable) {
269  const auto oldDir = chatroom->getAutoAcceptDir();
270  const auto newDir =
271  QFileDialog::getExistingDirectory(Q_NULLPTR,
272  tr("Choose an auto accept directory", "popup title"),
273  oldDir);
274  chatroom->setAutoAcceptDir(newDir);
275  } else {
276  chatroom->disableAutoAccept();
277  }
278 }
280 {
281  const auto frnd = chatroom->getFriend();
282  const auto iabout = new AboutFriend(frnd, &Settings::getInstance());
283  std::unique_ptr<IAboutFriend> about = std::unique_ptr<IAboutFriend>(iabout);
284  const auto aboutUser = new AboutFriendForm(std::move(about), this);
286  aboutUser->show();
287 }
288 
290 {
291  setActive(true);
292 }
293 
295 {
296  setActive(false);
297 }
298 
299 void FriendWidget::setActive(bool active)
300 {
302  if (isDefaultAvatar) {
303  const auto uri =
304  active ? QStringLiteral(":img/contact_dark.svg") : QStringLiteral(":img/contact.svg");
305  avatar->setPixmap(QPixmap{uri});
306  }
307 }
308 
310 {
311  const auto frnd = chatroom->getFriend();
312  const bool event = frnd->getEventFlag();
313  statusPic.setPixmap(QPixmap(Status::getIconPath(frnd->getStatus(), event)));
314 
315  if (event) {
316  const Settings& s = Settings::getInstance();
317  const uint32_t circleId = s.getFriendCircleID(frnd->getPublicKey());
318  CircleWidget* circleWidget = CircleWidget::getFromID(circleId);
319  if (circleWidget) {
320  circleWidget->setExpanded(true);
321  }
322 
323  emit updateFriendActivity(*frnd);
324  }
325 
326  statusPic.setMargin(event ? 1 : 3);
327 }
328 
330 {
331  const auto frnd = chatroom->getFriend();
332  const int status = static_cast<int>(frnd->getStatus());
333  const bool event = frnd->getEventFlag();
334 
335  static const QVector<QString> names = {
336  tr("Online"),
337  tr("Away"),
338  tr("Busy"),
339  tr("Offline"),
340  tr("Blocked"),
341  tr("Negotiating")
342  };
343 
344  return event ? tr("New message") : names.value(status);
345 }
346 
348 {
349  return chatroom->getFriend();
350 }
351 
353 {
354  return getFriend();
355 }
356 
358 {
359  return true;
360 }
361 
363 {
364  return false;
365 }
366 
368 {
369  const auto frnd = getFriend();
370  return Status::isOnline(frnd->getStatus());
371 }
372 
374 {
375  return isVisible();
376 }
377 
379 {
380  return nameLabel->fullText();
381 }
382 
384 {
385  const auto frnd = chatroom->getFriend();
387 }
388 
390 {
391  return this;
392 }
393 
395 {
396  setVisible(visible);
397 }
398 
400 {
401  return chatroom->getCircleId();
402 }
403 
405 {
406  chatroom->resetEventFlags();
407 }
408 
409 void FriendWidget::onAvatarSet(const ToxPk& friendPk, const QPixmap& pic)
410 {
411  const auto frnd = chatroom->getFriend();
412  if (friendPk != frnd->getPublicKey()) {
413  return;
414  }
415 
416  isDefaultAvatar = false;
417  avatar->setPixmap(pic);
418 }
419 
421 {
422  const auto frnd = chatroom->getFriend();
423  if (friendPk != frnd->getPublicKey()) {
424  return;
425  }
426 
427  isDefaultAvatar = true;
428 
429  const QString path = QString(":/img/contact%1.svg").arg(isActive() ? "_dark" : "");
430  avatar->setPixmap(QPixmap(path));
431 }
432 
433 void FriendWidget::mousePressEvent(QMouseEvent* ev)
434 {
435  if (ev->button() == Qt::LeftButton) {
436  dragStartPos = ev->pos();
437  }
438 
439  GenericChatroomWidget::mousePressEvent(ev);
440 }
441 
442 void FriendWidget::mouseMoveEvent(QMouseEvent* ev)
443 {
444  if (!(ev->buttons() & Qt::LeftButton)) {
445  return;
446  }
447 
448  const int distance = (dragStartPos - ev->pos()).manhattanLength();
449  if (distance > QApplication::startDragDistance()) {
450  QMimeData* mdata = new QMimeData;
451  const Friend* frnd = getFriend();
452  mdata->setText(frnd->getDisplayedName());
453  mdata->setData("toxPk", frnd->getPublicKey().getByteArray());
454 
455  QDrag* drag = new QDrag(this);
456  drag->setMimeData(mdata);
457  drag->setPixmap(avatar->getPixmap());
458  drag->exec(Qt::CopyAction | Qt::MoveAction);
459  }
460 }
FriendWidget::getWidget
QWidget * getWidget() final
Definition: friendwidget.cpp:389
GenericChatroomWidget::avatar
MaskablePixmapWidget * avatar
Definition: genericchatroomwidget.h:83
FriendWidget::onAvatarSet
void onAvatarSet(const ToxPk &friendPk, const QPixmap &pic)
Definition: friendwidget.cpp:409
FriendListWidget::addCircleWidget
void addCircleWidget(int id)
Definition: friendlistwidget.cpp:382
Settings
Definition: settings.h:51
FriendWidget::widgetIsVisible
bool widgetIsVisible() const final
Definition: friendwidget.cpp:373
style.h
FriendListWidget::moveWidget
void moveWidget(FriendWidget *w, Status::Status s, bool add=false)
Definition: friendlistwidget.cpp:563
FriendWidget::friendHistoryRemoved
void friendHistoryRemoved()
FriendWidget::getLastActivity
QDateTime getLastActivity() const final
Definition: friendwidget.cpp:383
friend.h
FriendWidget::resetEventFlags
void resetEventFlags() final
Definition: friendwidget.cpp:404
friendlist.h
Status::Status::Offline
@ Offline
settings.h
GenericChatroomWidget::isActive
bool isActive()
Definition: genericchatroomwidget.cpp:119
FriendWidget::moveToCircle
void moveToCircle(int circleId)
Definition: friendwidget.cpp:244
MaskablePixmapWidget::getPixmap
QPixmap getPixmap() const
Definition: maskablepixmapwidget.cpp:68
CroppingLabel::editFinished
void editFinished(const QString &newText)
FriendWidget::isGroup
bool isGroup() const final
Definition: friendwidget.cpp:362
FriendWidget::moveToNewCircle
void moveToNewCircle()
Definition: friendwidget.cpp:203
FriendWidget::setAsInactiveChatroom
void setAsInactiveChatroom() final
Definition: friendwidget.cpp:294
GenericChatroomWidget::setActive
void setActive(bool active)
Definition: genericchatroomwidget.cpp:124
FriendChatroom::activeChanged
void activeChanged(bool activated)
Friend::getEventFlag
bool getEventFlag() const override
Definition: friend.cpp:151
FriendWidget::setActive
void setActive(bool active)
Definition: friendwidget.cpp:299
group.h
FriendWidget::onContextMenuCalled
void onContextMenuCalled(QContextMenuEvent *event)
FriendWidget::onContextMenuCalled.
Definition: friendwidget.cpp:96
aboutfriend.h
FriendWidget::onAvatarRemoved
void onAvatarRemoved(const ToxPk &friendPk)
Definition: friendwidget.cpp:420
CroppingLabel::setText
void setText(const QString &text)
Definition: croppinglabel.cpp:81
FriendChatroom::inviteToNewGroup
void inviteToNewGroup()
Definition: friendchatroom.cpp:86
MaskablePixmapWidget::setPixmap
void setPixmap(const QPixmap &pmap)
Definition: maskablepixmapwidget.cpp:54
FriendWidget::changeAutoAccept
void changeAutoAccept(bool enable)
Definition: friendwidget.cpp:266
Status::getIconPath
QString getIconPath(Status status, bool event)
Definition: status.cpp:72
FriendWidget::removeChatWindow
void removeChatWindow()
Definition: friendwidget.cpp:184
circlewidget.h
FriendWidget::getContact
const Contact * getContact() const final
Definition: friendwidget.cpp:352
FriendWidget::isFriend
bool isFriend() const final
Definition: friendwidget.cpp:357
CroppingLabel::editBegin
void editBegin()
Definition: croppinglabel.cpp:60
croppinglabel.h
Settings::getFriendCircleID
int getFriendCircleID(const ToxPk &id) const override
Definition: settings.cpp:1894
FriendWidget::mousePressEvent
void mousePressEvent(QMouseEvent *ev) override
Definition: friendwidget.cpp:433
chatform.h
AboutFriend
Definition: aboutfriend.h:31
Friend::getStatus
Status::Status getStatus() const
Definition: friend.cpp:190
FriendWidget::updateFriendActivity
void updateFriendActivity(Friend &frnd)
GenericChatItemWidget::statusPic
QLabel statusPic
Definition: genericchatitemwidget.h:54
FriendWidget::removeFriend
void removeFriend(const ToxPk &friendPk)
FriendWidget::getCircleId
int getCircleId() const final
Definition: friendwidget.cpp:399
ToxPk
This class represents a Tox Public Key, which is a part of Tox ID.
Definition: toxpk.h:26
Contact
Definition: contact.h:26
GenericChatItemWidget::nameLabel
CroppingLabel * nameLabel
Definition: genericchatitemwidget.h:53
Friend
Definition: friend.h:31
widget.h
FriendWidget::mouseMoveEvent
void mouseMoveEvent(QMouseEvent *ev) override
Definition: friendwidget.cpp:442
FriendWidget::getFriend
const Friend * getFriend() const final
Definition: friendwidget.cpp:347
FriendWidget::getStatusString
QString getStatusString() const final
Definition: friendwidget.cpp:329
GenericChatroomWidget::newWindowOpened
void newWindowOpened(GenericChatroomWidget *widget)
FriendWidget::isDefaultAvatar
bool isDefaultAvatar
Definition: friendwidget.h:87
maskablepixmapwidget.h
friendlistwidget.h
Settings::getFriendActivity
QDateTime getFriendActivity(const ToxPk &id) const override
Definition: settings.cpp:1911
GenericChatroomWidget::dragStartPos
QPoint dragStartPos
Definition: genericchatroomwidget.h:79
FriendWidget::showDetails
void showDetails()
Definition: friendwidget.cpp:279
FriendListWidget
Definition: friendlistwidget.h:42
Friend::getDisplayedName
QString getDisplayedName() const override
Friend::getDisplayedName Gets the name that should be displayed for a user.
Definition: friend.cpp:112
Contact::displayedNameChanged
void displayedNameChanged(const QString &newName)
FriendWidget::setAsActiveChatroom
void setAsActiveChatroom() final
Definition: friendwidget.cpp:289
Settings::getInstance
static Settings & getInstance()
Returns the singleton instance.
Definition: settings.cpp:88
CircleWidget::getFromID
static CircleWidget * getFromID(int id)
Definition: circlewidget.cpp:76
FriendWidget::updateStatusLight
void updateStatusLight() final
Definition: friendwidget.cpp:309
FriendWidget::setWidgetVisible
void setWidgetVisible(bool visible) final
Definition: friendwidget.cpp:394
Friend::getPublicKey
const ToxPk & getPublicKey() const
Definition: friend.cpp:131
FriendWidget::contextMenuCalled
void contextMenuCalled(QContextMenuEvent *event)
FriendWidget::contextMenuEvent
void contextMenuEvent(QContextMenuEvent *event) final
FriendWidget::contextMenuEvent.
Definition: friendwidget.cpp:85
AboutFriendForm::histroyRemoved
void histroyRemoved()
friendchatroom.h
AboutFriendForm
Definition: aboutfriendform.h:33
CategoryWidget::setExpanded
void setExpanded(bool isExpanded, bool save=true)
Definition: categorywidget.cpp:83
core.h
CircleWidget
Definition: circlewidget.h:27
GenericChatroomWidget::active
bool active
Definition: genericchatroomwidget.h:85
CroppingLabel::fullText
QString fullText()
Get original full text.
Definition: croppinglabel.cpp:168
aboutfriendform.h
CategoryWidget::updateStatus
void updateStatus()
Definition: categorywidget.cpp:144
FriendWidget::FriendWidget
FriendWidget(std::shared_ptr< FriendChatroom > chatform, bool compact)
Definition: friendwidget.cpp:59
FriendWidget::removeFromCircle
void removeFromCircle()
Definition: friendwidget.cpp:224
GenericChatroomWidget
Definition: genericchatroomwidget.h:32
Status::isOnline
bool isOnline(Status status)
Definition: status.cpp:83
FriendWidget::getNameItem
QString getNameItem() const final
Definition: friendwidget.cpp:378
FriendWidget::isOnline
bool isOnline() const final
Definition: friendwidget.cpp:367
FriendWidget::chatroom
std::shared_ptr< FriendChatroom > chatroom
Definition: friendwidget.h:86
groupwidget.h
friendwidget.h
status.h
FriendWidget
Definition: friendwidget.h:33
Friend::setAlias
void setAlias(const QString &name)
Friend::setAlias sets the alias for the friend.
Definition: friend.cpp:76
ContactId::getByteArray
QByteArray getByteArray() const
Get a copy of the id.
Definition: contactid.cpp:101