mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge branch 'friendlist' from PR #1879
Closes #1879 by rebasing and merging it
This commit is contained in:
commit
47c4fab08b
152
qtox.pro
152
qtox.pro
|
@ -38,7 +38,7 @@ FORMS += \
|
|||
|
||||
CONFIG += c++11
|
||||
|
||||
QMAKE_CXXFLAGS += -fno-exceptions -fno-rtti
|
||||
QMAKE_CXXFLAGS += -fno-exceptions
|
||||
|
||||
# Rules for creating/updating {ts|qm}-files
|
||||
include(translations/i18n.pri)
|
||||
|
@ -108,9 +108,9 @@ contains(HIGH_DPI, YES) {
|
|||
}
|
||||
|
||||
contains(JENKINS,YES) {
|
||||
INCLUDEPATH += ./libs/include/
|
||||
INCLUDEPATH += ./libs/include/
|
||||
} else {
|
||||
INCLUDEPATH += libs/include
|
||||
INCLUDEPATH += libs/include
|
||||
}
|
||||
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) {
|
||||
|
@ -133,8 +133,8 @@ contains(DEFINES, QTOX_PLATFORM_EXT) {
|
|||
# Rules for Windows, Mac OSX, and Linux
|
||||
win32 {
|
||||
RC_FILE = windows/qtox.rc
|
||||
LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -lavformat -lavdevice -lavcodec -lavutil -lswscale -lOpenAL32 -lopus
|
||||
LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -lavformat -lavdevice -lavcodec -lavutil -lswscale -lOpenAL32 -lopus
|
||||
LIBS += -lopengl32 -lole32 -loleaut32 -lvfw32 -lws2_32 -liphlpapi -lz -luuid
|
||||
LIBS += -lqrencode
|
||||
LIBS += -lstrmiids # For DirectShow
|
||||
|
@ -200,31 +200,31 @@ win32 {
|
|||
# The systray Unity backend implements the system tray icon on Unity (Ubuntu) and GNOME desktops.
|
||||
unix:!macx:!android {
|
||||
contains(ENABLE_SYSTRAY_UNITY_BACKEND, YES) {
|
||||
DEFINES += ENABLE_SYSTRAY_UNITY_BACKEND
|
||||
DEFINES += ENABLE_SYSTRAY_UNITY_BACKEND
|
||||
|
||||
INCLUDEPATH += "/usr/include/glib-2.0"
|
||||
INCLUDEPATH += "/usr/include/gtk-2.0"
|
||||
INCLUDEPATH += "/usr/include/atk-1.0"
|
||||
INCLUDEPATH += "/usr/include/cairo"
|
||||
INCLUDEPATH += "/usr/include/ffmpeg"
|
||||
INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0"
|
||||
INCLUDEPATH += "/usr/include/libappindicator-0.1"
|
||||
INCLUDEPATH += "/usr/include/libdbusmenu-glib-0.4"
|
||||
INCLUDEPATH += "/usr/include/pango-1.0"
|
||||
equals(QT_ARCH, x86_64) {
|
||||
INCLUDEPATH += "/usr/lib64/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib64/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
else {
|
||||
INCLUDEPATH += "/usr/lib/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
INCLUDEPATH += "/usr/include/glib-2.0"
|
||||
INCLUDEPATH += "/usr/include/gtk-2.0"
|
||||
INCLUDEPATH += "/usr/include/atk-1.0"
|
||||
INCLUDEPATH += "/usr/include/cairo"
|
||||
INCLUDEPATH += "/usr/include/ffmpeg"
|
||||
INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0"
|
||||
INCLUDEPATH += "/usr/include/libappindicator-0.1"
|
||||
INCLUDEPATH += "/usr/include/libdbusmenu-glib-0.4"
|
||||
INCLUDEPATH += "/usr/include/pango-1.0"
|
||||
equals(QT_ARCH, x86_64) {
|
||||
INCLUDEPATH += "/usr/lib64/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib64/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
else {
|
||||
INCLUDEPATH += "/usr/lib/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
|
||||
LIBS += -lgobject-2.0 -lappindicator -lgtk-x11-2.0
|
||||
LIBS += -lgobject-2.0 -lappindicator -lgtk-x11-2.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,30 +232,30 @@ contains(ENABLE_SYSTRAY_UNITY_BACKEND, YES) {
|
|||
unix:!macx:!android {
|
||||
contains(ENABLE_SYSTRAY_STATUSNOTIFIER_BACKEND, NO) {
|
||||
} else {
|
||||
DEFINES += ENABLE_SYSTRAY_STATUSNOTIFIER_BACKEND
|
||||
DEFINES += ENABLE_SYSTRAY_STATUSNOTIFIER_BACKEND
|
||||
|
||||
INCLUDEPATH += "/usr/include/glib-2.0"
|
||||
INCLUDEPATH += "/usr/include/gtk-2.0"
|
||||
INCLUDEPATH += "/usr/include/atk-1.0"
|
||||
INCLUDEPATH += "/usr/include/cairo"
|
||||
INCLUDEPATH += "/usr/include/ffmpeg"
|
||||
INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0"
|
||||
INCLUDEPATH += "/usr/include/pango-1.0"
|
||||
equals(QT_ARCH, x86_64) {
|
||||
INCLUDEPATH += "/usr/lib64/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib64/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
else {
|
||||
INCLUDEPATH += "/usr/lib/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
INCLUDEPATH += "/usr/include/glib-2.0"
|
||||
INCLUDEPATH += "/usr/include/gtk-2.0"
|
||||
INCLUDEPATH += "/usr/include/atk-1.0"
|
||||
INCLUDEPATH += "/usr/include/cairo"
|
||||
INCLUDEPATH += "/usr/include/ffmpeg"
|
||||
INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0"
|
||||
INCLUDEPATH += "/usr/include/pango-1.0"
|
||||
equals(QT_ARCH, x86_64) {
|
||||
INCLUDEPATH += "/usr/lib64/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib64/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
else {
|
||||
INCLUDEPATH += "/usr/lib/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
|
||||
|
||||
LIBS += -lglib-2.0 -lgdk_pixbuf-2.0 -lgio-2.0 -lcairo -lgtk-x11-2.0 -lgdk-x11-2.0 -lgobject-2.0
|
||||
LIBS += -lglib-2.0 -lgdk_pixbuf-2.0 -lgio-2.0 -lcairo -lgtk-x11-2.0 -lgdk-x11-2.0 -lgobject-2.0
|
||||
|
||||
SOURCES += src/platform/statusnotifier/closures.c \
|
||||
src/platform/statusnotifier/enums.c \
|
||||
|
@ -272,29 +272,29 @@ contains(ENABLE_SYSTRAY_STATUSNOTIFIER_BACKEND, NO) {
|
|||
unix:!macx:!android {
|
||||
contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
|
||||
} else {
|
||||
DEFINES += ENABLE_SYSTRAY_GTK_BACKEND
|
||||
DEFINES += ENABLE_SYSTRAY_GTK_BACKEND
|
||||
|
||||
INCLUDEPATH += "/usr/include/glib-2.0"
|
||||
INCLUDEPATH += "/usr/include/gtk-2.0"
|
||||
INCLUDEPATH += "/usr/include/atk-1.0"
|
||||
INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0"
|
||||
INCLUDEPATH += "/usr/include/cairo"
|
||||
INCLUDEPATH += "/usr/include/pango-1.0"
|
||||
equals(QT_ARCH, x86_64) {
|
||||
INCLUDEPATH += "/usr/lib64/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib64/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
else {
|
||||
INCLUDEPATH += "/usr/lib/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
INCLUDEPATH += "/usr/include/glib-2.0"
|
||||
INCLUDEPATH += "/usr/include/gtk-2.0"
|
||||
INCLUDEPATH += "/usr/include/atk-1.0"
|
||||
INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0"
|
||||
INCLUDEPATH += "/usr/include/cairo"
|
||||
INCLUDEPATH += "/usr/include/pango-1.0"
|
||||
equals(QT_ARCH, x86_64) {
|
||||
INCLUDEPATH += "/usr/lib64/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib64/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
else {
|
||||
INCLUDEPATH += "/usr/lib/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/glib-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/gtk-2.0/include"
|
||||
INCLUDEPATH += "/usr/lib/i386-linux-gnu/gtk-2.0/include"
|
||||
}
|
||||
|
||||
|
||||
LIBS += -lglib-2.0 -lgdk_pixbuf-2.0 -lgio-2.0 -lcairo -lgtk-x11-2.0 -lgdk-x11-2.0 -lgobject-2.0
|
||||
LIBS += -lglib-2.0 -lgdk_pixbuf-2.0 -lgio-2.0 -lcairo -lgtk-x11-2.0 -lgdk-x11-2.0 -lgobject-2.0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -488,7 +488,12 @@ SOURCES += \
|
|||
src/widget/translator.cpp \
|
||||
src/persistence/settingsserializer.cpp \
|
||||
src/widget/notificationscrollarea.cpp \
|
||||
src/widget/notificationedgewidget.cpp
|
||||
src/widget/notificationedgewidget.cpp \
|
||||
src/widget/circlewidget.cpp \
|
||||
src/widget/genericchatitemwidget.cpp \
|
||||
src/widget/friendlistlayout.cpp \
|
||||
src/widget/genericchatitemlayout.cpp \
|
||||
src/widget/categorywidget.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/audio/audio.h \
|
||||
|
@ -526,4 +531,9 @@ HEADERS += \
|
|||
src/widget/translator.h \
|
||||
src/persistence/settingsserializer.h \
|
||||
src/widget/notificationscrollarea.h \
|
||||
src/widget/notificationedgewidget.h
|
||||
src/widget/notificationedgewidget.h \
|
||||
src/widget/circlewidget.h \
|
||||
src/widget/genericchatitemwidget.h \
|
||||
src/widget/friendlistlayout.h \
|
||||
src/widget/genericchatitemlayout.h \
|
||||
src/widget/categorywidget.h
|
||||
|
|
|
@ -533,6 +533,15 @@ QVector<ChatLine::Ptr> ChatLog::getLines()
|
|||
return lines;
|
||||
}
|
||||
|
||||
ChatLine::Ptr ChatLog::getLatestLine() const
|
||||
{
|
||||
if (!lines.empty())
|
||||
{
|
||||
return lines.last();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ChatLog::clear()
|
||||
{
|
||||
clearSelection();
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
|
||||
ChatLine::Ptr getTypingNotification() const;
|
||||
QVector<ChatLine::Ptr> getLines();
|
||||
ChatLine::Ptr getLatestLine() const;
|
||||
// repetition interval sender name (sec)
|
||||
const uint repNameAfter = 5*60;
|
||||
|
||||
|
|
|
@ -602,6 +602,7 @@ void Core::acceptFriendRequest(const QString& userId)
|
|||
{
|
||||
profile.saveToxSave();
|
||||
emit friendAdded(friendId, userId);
|
||||
emit friendshipChanged(friendId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,6 +644,7 @@ void Core::requestFriendship(const QString& friendAddress, const QString& messag
|
|||
|
||||
HistoryKeeper::getInstance()->addChatEntry(userId, inviteStr, getSelfId().publicKey, QDateTime::currentDateTime(), true);
|
||||
emit friendAdded(friendId, userId);
|
||||
emit friendshipChanged(friendId);
|
||||
}
|
||||
}
|
||||
profile.saveToxSave();
|
||||
|
|
|
@ -161,6 +161,7 @@ signals:
|
|||
void friendMessageReceived(uint32_t friendId, const QString& message, bool isAction);
|
||||
|
||||
void friendAdded(uint32_t friendId, const QString& userId);
|
||||
void friendshipChanged(uint32_t friendId);
|
||||
|
||||
void friendStatusChanged(uint32_t friendId, Status status);
|
||||
void friendStatusMessageChanged(uint32_t friendId, const QString& message);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "src/core/corestructs.h"
|
||||
#include "core/toxid.h"
|
||||
|
||||
struct FriendWidget;
|
||||
class FriendWidget;
|
||||
class ChatForm;
|
||||
|
||||
class Friend : public QObject
|
||||
|
|
|
@ -43,7 +43,7 @@ Group::Group(int GroupId, QString Name, bool IsAvGroupchat)
|
|||
Group::~Group()
|
||||
{
|
||||
delete chatForm;
|
||||
delete widget;
|
||||
widget->deleteLater();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -88,7 +88,6 @@ void Group::updatePeer(int peerId, QString name)
|
|||
|
||||
void Group::setName(const QString& name)
|
||||
{
|
||||
widget->setName(name);
|
||||
chatForm->setName(name);
|
||||
|
||||
if (widget->isActive())
|
||||
|
|
|
@ -1022,7 +1022,7 @@ QSplitter:handle{
|
|||
<item>
|
||||
<widget class="QLineEdit" name="searchContactText">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
|
@ -1030,13 +1030,25 @@ QSplitter:handle{
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="searchContactFilterCBox">
|
||||
<widget class="QToolButton" name="searchContactFilterBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::NoArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -1072,7 +1084,7 @@ QSplitter:handle{
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>292</width>
|
||||
<width>284</width>
|
||||
<height>353</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
|
|
@ -163,6 +163,7 @@ void Nexus::showMainGUI()
|
|||
connect(core, &Core::statusMessageSet, widget, &Widget::setStatusMessage);
|
||||
connect(core, &Core::selfAvatarChanged, widget, &Widget::onSelfAvatarLoaded);
|
||||
connect(core, &Core::friendAdded, widget, &Widget::addFriend);
|
||||
connect(core, &Core::friendshipChanged, widget, &Widget::onFriendshipChanged);
|
||||
connect(core, &Core::failedToAddFriend, widget, &Widget::addFriendFailed);
|
||||
connect(core, &Core::friendUsernameChanged, widget, &Widget::onFriendUsernameChanged);
|
||||
connect(core, &Core::friendStatusChanged, widget, &Widget::onFriendStatusChanged);
|
||||
|
|
|
@ -392,6 +392,26 @@ void HistoryKeeper::markAsSent(int m_id)
|
|||
db->exec(QString("UPDATE sent_status SET status = 1 WHERE id = %1;").arg(m_id));
|
||||
}
|
||||
|
||||
QDate HistoryKeeper::getLatestDate(const QString &chat)
|
||||
{
|
||||
int chat_id = getChatID(chat, ctSingle).first;
|
||||
|
||||
QSqlQuery dbAnswer;
|
||||
dbAnswer = db->exec(QString("SELECT MAX(timestamp) FROM history LEFT JOIN sent_status ON history.id = sent_status.id ") +
|
||||
QString("INNER JOIN aliases ON history.sender = aliases.id AND chat_id = %3;")
|
||||
.arg(chat_id));
|
||||
|
||||
if (dbAnswer.first())
|
||||
{
|
||||
qint64 timeInt = dbAnswer.value(0).toLongLong();
|
||||
|
||||
if (timeInt != 0)
|
||||
return QDateTime::fromMSecsSinceEpoch(timeInt).date();
|
||||
}
|
||||
|
||||
return QDate();
|
||||
}
|
||||
|
||||
void HistoryKeeper::setSyncType(Db::syncType sType)
|
||||
{
|
||||
QString syncCmd;
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
qint64 addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt);
|
||||
QList<HistMessage> getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to);
|
||||
void markAsSent(int m_id);
|
||||
QDate getLatestDate(const QString& chat);
|
||||
|
||||
QList<HistMessage> exportMessages();
|
||||
void importMessages(const QList<HistoryKeeper::HistMessage> &lst);
|
||||
|
|
|
@ -287,6 +287,7 @@ void Settings::loadPersonnal(Profile* profile)
|
|||
friendLst.clear();
|
||||
ps.beginGroup("Friends");
|
||||
int size = ps.beginReadArray("Friend");
|
||||
friendLst.reserve(size);
|
||||
for (int i = 0; i < size; i ++)
|
||||
{
|
||||
ps.setArrayIndex(i);
|
||||
|
@ -294,6 +295,11 @@ void Settings::loadPersonnal(Profile* profile)
|
|||
fp.addr = ps.value("addr").toString();
|
||||
fp.alias = ps.value("alias").toString();
|
||||
fp.autoAcceptDir = ps.value("autoAcceptDir").toString();
|
||||
fp.circleID = ps.value("circle", -1).toInt();
|
||||
|
||||
if (getEnableLogging())
|
||||
fp.activity = ps.value("activity", QDate()).toDate();
|
||||
|
||||
friendLst[ToxId(fp.addr).publicKey] = fp;
|
||||
}
|
||||
ps.endArray();
|
||||
|
@ -303,6 +309,20 @@ void Settings::loadPersonnal(Profile* profile)
|
|||
compactLayout = ps.value("compactLayout", false).toBool();
|
||||
ps.endGroup();
|
||||
|
||||
ps.beginGroup("Circles");
|
||||
size = ps.beginReadArray("Circle");
|
||||
circleLst.reserve(size);
|
||||
for (int i = 0; i < size; i ++)
|
||||
{
|
||||
ps.setArrayIndex(i);
|
||||
circleProp cp;
|
||||
cp.name = ps.value("name").toString();
|
||||
cp.expanded = ps.value("expanded", true).toBool();
|
||||
circleLst.push_back(cp);
|
||||
}
|
||||
ps.endArray();
|
||||
ps.endGroup();
|
||||
|
||||
ps.beginGroup("Privacy");
|
||||
typingNotification = ps.value("typingNotification", false).toBool();
|
||||
enableLogging = ps.value("enableLogging", false).toBool();
|
||||
|
@ -450,6 +470,11 @@ void Settings::savePersonal(QString profileName, QString password)
|
|||
ps.setValue("addr", frnd.addr);
|
||||
ps.setValue("alias", frnd.alias);
|
||||
ps.setValue("autoAcceptDir", frnd.autoAcceptDir);
|
||||
ps.setValue("circle", frnd.circleID);
|
||||
|
||||
if (getEnableLogging())
|
||||
ps.setValue("activity", frnd.activity);
|
||||
|
||||
index++;
|
||||
}
|
||||
ps.endArray();
|
||||
|
@ -459,6 +484,19 @@ void Settings::savePersonal(QString profileName, QString password)
|
|||
ps.setValue("compactLayout", compactLayout);
|
||||
ps.endGroup();
|
||||
|
||||
ps.beginGroup("Circles");
|
||||
ps.beginWriteArray("Circle", circleLst.size());
|
||||
index = 0;
|
||||
for (auto& circle : circleLst)
|
||||
{
|
||||
ps.setArrayIndex(index);
|
||||
ps.setValue("name", circle.name);
|
||||
ps.setValue("expanded", circle.expanded);
|
||||
index++;
|
||||
}
|
||||
ps.endArray();
|
||||
ps.endGroup();
|
||||
|
||||
ps.beginGroup("Privacy");
|
||||
ps.setValue("typingNotification", typingNotification);
|
||||
ps.setValue("enableLogging", enableLogging);
|
||||
|
@ -1255,6 +1293,66 @@ void Settings::setFriendAlias(const ToxId &id, const QString &alias)
|
|||
}
|
||||
}
|
||||
|
||||
int Settings::getFriendCircleID(const ToxId &id) const
|
||||
{
|
||||
QString key = id.publicKey;
|
||||
auto it = friendLst.find(key);
|
||||
if (it != friendLst.end())
|
||||
return it->circleID;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Settings::setFriendCircleID(const ToxId &id, int circleID)
|
||||
{
|
||||
QString key = id.publicKey;
|
||||
auto it = friendLst.find(key);
|
||||
if (it != friendLst.end())
|
||||
{
|
||||
it->circleID = circleID;
|
||||
}
|
||||
else
|
||||
{
|
||||
friendProp fp;
|
||||
fp.addr = key;
|
||||
fp.alias = "";
|
||||
fp.autoAcceptDir = "";
|
||||
fp.circleID = circleID;
|
||||
friendLst[key] = fp;
|
||||
}
|
||||
}
|
||||
|
||||
QDate Settings::getFriendActivity(const ToxId &id) const
|
||||
{
|
||||
QString key = id.publicKey;
|
||||
auto it = friendLst.find(key);
|
||||
if (it != friendLst.end())
|
||||
return it->activity;
|
||||
|
||||
return QDate();
|
||||
}
|
||||
|
||||
void Settings::setFriendActivity(const ToxId &id, const QDate &activity)
|
||||
{
|
||||
QString key = id.publicKey;
|
||||
auto it = friendLst.find(key);
|
||||
if (it != friendLst.end())
|
||||
{
|
||||
it->activity = activity;
|
||||
}
|
||||
else
|
||||
{
|
||||
friendProp fp;
|
||||
fp.addr = key;
|
||||
fp.alias = "";
|
||||
fp.autoAcceptDir = "";
|
||||
fp.circleID = -1;
|
||||
fp.activity = activity;
|
||||
friendLst[key] = fp;
|
||||
}
|
||||
savePersonal();
|
||||
}
|
||||
|
||||
void Settings::removeFriendSettings(const ToxId &id)
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
|
@ -1298,6 +1396,57 @@ void Settings::setGroupchatPosition(bool value)
|
|||
groupchatPosition = value;
|
||||
}
|
||||
|
||||
int Settings::getCircleCount() const
|
||||
{
|
||||
return circleLst.size();
|
||||
}
|
||||
|
||||
QString Settings::getCircleName(int id) const
|
||||
{
|
||||
return circleLst[id].name;
|
||||
}
|
||||
|
||||
void Settings::setCircleName(int id, const QString &name)
|
||||
{
|
||||
circleLst[id].name = name;
|
||||
savePersonal();
|
||||
}
|
||||
|
||||
int Settings::addCircle(const QString &name)
|
||||
{
|
||||
circleProp cp;
|
||||
cp.expanded = false;
|
||||
|
||||
if (name.isEmpty())
|
||||
cp.name = tr("Circle #%1").arg(circleLst.count() + 1);
|
||||
else
|
||||
cp.name = name;
|
||||
|
||||
circleLst.append(cp);
|
||||
savePersonal();
|
||||
return circleLst.count() - 1;
|
||||
}
|
||||
|
||||
bool Settings::getCircleExpanded(int id) const
|
||||
{
|
||||
return circleLst[id].expanded;
|
||||
}
|
||||
|
||||
void Settings::setCircleExpanded(int id, bool expanded)
|
||||
{
|
||||
circleLst[id].expanded = expanded;
|
||||
}
|
||||
|
||||
int Settings::removeCircle(int id)
|
||||
{
|
||||
// Replace index with last one and remove last one instead.
|
||||
// This gives you contiguous ids all the time.
|
||||
circleLst[id] = circleLst.last();
|
||||
circleLst.pop_back();
|
||||
savePersonal();
|
||||
return circleLst.count();
|
||||
}
|
||||
|
||||
int Settings::getThemeColor() const
|
||||
{
|
||||
QMutexLocker locker{&bigLock};
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
#include <QMutex>
|
||||
#include <QDate>
|
||||
#include "src/core/corestructs.h"
|
||||
|
||||
class ToxId;
|
||||
|
@ -228,6 +229,12 @@ public:
|
|||
QString getFriendAlias(const ToxId &id) const;
|
||||
void setFriendAlias(const ToxId &id, const QString &alias);
|
||||
|
||||
int getFriendCircleID(const ToxId &id) const;
|
||||
void setFriendCircleID(const ToxId &id, int circleID);
|
||||
|
||||
QDate getFriendActivity(const ToxId &id) const;
|
||||
void setFriendActivity(const ToxId &id, const QDate &date);
|
||||
|
||||
void removeFriendSettings(const ToxId &id);
|
||||
|
||||
bool getFauxOfflineMessaging() const;
|
||||
|
@ -242,6 +249,14 @@ public:
|
|||
bool getAutoLogin() const;
|
||||
void setAutoLogin(bool state);
|
||||
|
||||
int getCircleCount() const;
|
||||
int addCircle(const QString &name = QString());
|
||||
int removeCircle(int id);
|
||||
QString getCircleName(int id) const;
|
||||
void setCircleName(int id, const QString &name);
|
||||
bool getCircleExpanded(int id) const;
|
||||
void setCircleExpanded(int id, bool expanded);
|
||||
|
||||
// Assume all widgets have unique names
|
||||
// Don't use it to save every single thing you want to save, use it
|
||||
// for some general purpose widgets, such as MainWindows or Splitters,
|
||||
|
@ -357,10 +372,20 @@ private:
|
|||
QString alias;
|
||||
QString addr;
|
||||
QString autoAcceptDir;
|
||||
int circleID = -1;
|
||||
QDate activity = QDate();
|
||||
};
|
||||
|
||||
struct circleProp
|
||||
{
|
||||
QString name;
|
||||
bool expanded;
|
||||
};
|
||||
|
||||
QHash<QString, friendProp> friendLst;
|
||||
|
||||
QVector<circleProp> circleLst;
|
||||
|
||||
int themeColor;
|
||||
|
||||
static QMutex bigLock;
|
||||
|
|
333
src/widget/categorywidget.cpp
Normal file
333
src/widget/categorywidget.cpp
Normal file
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "categorywidget.h"
|
||||
#include "friendlistlayout.h"
|
||||
#include "friendlistwidget.h"
|
||||
#include "friendwidget.h"
|
||||
#include "src/widget/style.h"
|
||||
#include "tool/croppinglabel.h"
|
||||
#include <QBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
void emitChatroomWidget(QLayout* layout, int index)
|
||||
{
|
||||
GenericChatroomWidget* chatWidget = dynamic_cast<GenericChatroomWidget*>(layout->itemAt(index)->widget());
|
||||
if (chatWidget != nullptr)
|
||||
emit chatWidget->chatroomWidgetClicked(chatWidget);
|
||||
}
|
||||
|
||||
CategoryWidget::CategoryWidget(QWidget* parent)
|
||||
: GenericChatItemWidget(parent)
|
||||
{
|
||||
container = new QWidget(this);
|
||||
container->setObjectName("circleWidgetContainer");
|
||||
container->setLayoutDirection(Qt::LeftToRight);
|
||||
|
||||
statusLabel = new QLabel(this);
|
||||
statusLabel->setObjectName("status");
|
||||
statusLabel->setTextFormat(Qt::PlainText);
|
||||
|
||||
statusPic.setPixmap(QPixmap(":/ui/chatArea/scrollBarRightArrow.svg"));
|
||||
|
||||
fullLayout = new QVBoxLayout(this);
|
||||
fullLayout->setSpacing(0);
|
||||
fullLayout->setMargin(0);
|
||||
fullLayout->addWidget(container);
|
||||
|
||||
lineFrame = new QFrame(container);
|
||||
lineFrame->setObjectName("line");
|
||||
lineFrame->setFrameShape(QFrame::HLine);
|
||||
lineFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
||||
lineFrame->resize(0, 0);
|
||||
|
||||
listLayout = new FriendListLayout();
|
||||
listWidget = new QWidget(this);
|
||||
listWidget->setLayout(listLayout);
|
||||
fullLayout->addWidget(listWidget);
|
||||
|
||||
setAcceptDrops(true);
|
||||
|
||||
onCompactChanged(isCompact());
|
||||
|
||||
setExpanded(true, false);
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
bool CategoryWidget::isExpanded() const
|
||||
{
|
||||
return expanded;
|
||||
}
|
||||
|
||||
void CategoryWidget::setExpanded(bool isExpanded, bool save)
|
||||
{
|
||||
expanded = isExpanded;
|
||||
setMouseTracking(true);
|
||||
listWidget->setVisible(isExpanded);
|
||||
|
||||
if (isExpanded)
|
||||
statusPic.setPixmap(QPixmap(":/ui/chatArea/scrollBarDownArrow.svg"));
|
||||
else
|
||||
statusPic.setPixmap(QPixmap(":/ui/chatArea/scrollBarRightArrow.svg"));
|
||||
|
||||
// The listWidget will recieve a enterEvent for some reason if now visible.
|
||||
// Using the following, we prevent that.
|
||||
QApplication::processEvents(QEventLoop::ExcludeSocketNotifiers);
|
||||
container->hide();
|
||||
container->show();
|
||||
|
||||
if (save)
|
||||
onExpand();
|
||||
}
|
||||
|
||||
void CategoryWidget::leaveEvent(QEvent *event)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void CategoryWidget::setName(const QString &name, bool save)
|
||||
{
|
||||
nameLabel->setText(name);
|
||||
|
||||
if (isCompact())
|
||||
nameLabel->minimizeMaximumWidth();
|
||||
|
||||
if (save)
|
||||
onSetName();
|
||||
}
|
||||
|
||||
void CategoryWidget::editName()
|
||||
{
|
||||
nameLabel->editBegin();
|
||||
nameLabel->setMaximumWidth(QWIDGETSIZE_MAX);
|
||||
}
|
||||
|
||||
void CategoryWidget::addFriendWidget(FriendWidget* w, Status s)
|
||||
{
|
||||
listLayout->addFriendWidget(w, s);
|
||||
updateStatus();
|
||||
onAddFriendWidget(w);
|
||||
w->reloadTheme(); // Otherwise theme will change when moving to another circle.
|
||||
}
|
||||
|
||||
void CategoryWidget::removeFriendWidget(FriendWidget* w, Status s)
|
||||
{
|
||||
listLayout->removeFriendWidget(w, s);
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void CategoryWidget::updateStatus()
|
||||
{
|
||||
statusLabel->setText(QString::number(listLayout->friendOnlineCount()) + QStringLiteral(" / ") + QString::number(listLayout->friendTotalCount()));
|
||||
}
|
||||
|
||||
bool CategoryWidget::hasChatrooms() const
|
||||
{
|
||||
return listLayout->hasChatrooms();
|
||||
}
|
||||
|
||||
void CategoryWidget::search(const QString &searchString, bool updateAll, bool hideOnline, bool hideOffline)
|
||||
{
|
||||
if (updateAll)
|
||||
{
|
||||
listLayout->searchChatrooms(searchString, hideOnline, hideOffline);
|
||||
}
|
||||
bool inCategory = searchString.isEmpty() && !(hideOnline && hideOffline);
|
||||
setVisible(inCategory || listLayout->hasChatrooms());
|
||||
}
|
||||
|
||||
bool CategoryWidget::cycleContacts(bool forward)
|
||||
{
|
||||
if (listLayout->friendTotalCount() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (forward)
|
||||
{
|
||||
if (listLayout->getLayoutOnline()->count() != 0)
|
||||
{
|
||||
setExpanded(true);
|
||||
emitChatroomWidget(listLayout->getLayoutOnline(), 0);
|
||||
return true;
|
||||
}
|
||||
else if (listLayout->getLayoutOffline()->count() != 0)
|
||||
{
|
||||
setExpanded(true);
|
||||
emitChatroomWidget(listLayout->getLayoutOffline(), 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (listLayout->getLayoutOffline()->count() != 0)
|
||||
{
|
||||
setExpanded(true);
|
||||
emitChatroomWidget(listLayout->getLayoutOffline(), listLayout->getLayoutOffline()->count() - 1);
|
||||
return true;
|
||||
}
|
||||
else if (listLayout->getLayoutOnline()->count() != 0)
|
||||
{
|
||||
setExpanded(true);
|
||||
emitChatroomWidget(listLayout->getLayoutOnline(), listLayout->getLayoutOnline()->count() - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CategoryWidget::cycleContacts(FriendWidget* activeChatroomWidget, bool forward)
|
||||
{
|
||||
int index = -1;
|
||||
QLayout* currentLayout = nullptr;
|
||||
|
||||
FriendWidget* friendWidget = dynamic_cast<FriendWidget*>(activeChatroomWidget);
|
||||
if (friendWidget != nullptr)
|
||||
{
|
||||
currentLayout = listLayout->getLayoutOnline();
|
||||
index = listLayout->indexOfFriendWidget(friendWidget, true);
|
||||
if (index == -1)
|
||||
{
|
||||
currentLayout = listLayout->getLayoutOffline();
|
||||
index = listLayout->indexOfFriendWidget(friendWidget, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
index += forward ? 1 : -1;
|
||||
for (;;)
|
||||
{
|
||||
// Bounds checking.
|
||||
if (index < 0)
|
||||
{
|
||||
if (currentLayout == listLayout->getLayoutOffline())
|
||||
currentLayout = listLayout->getLayoutOnline();
|
||||
else
|
||||
return false;
|
||||
|
||||
index = currentLayout->count() - 1;
|
||||
continue;
|
||||
}
|
||||
else if (index >= currentLayout->count())
|
||||
{
|
||||
if (currentLayout == listLayout->getLayoutOnline())
|
||||
currentLayout = listLayout->getLayoutOffline();
|
||||
else
|
||||
return false;
|
||||
|
||||
index = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
GenericChatroomWidget* chatWidget = dynamic_cast<GenericChatroomWidget*>(currentLayout->itemAt(index)->widget());
|
||||
if (chatWidget != nullptr)
|
||||
emit chatWidget->chatroomWidgetClicked(chatWidget);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CategoryWidget::onCompactChanged(bool _compact)
|
||||
{
|
||||
delete topLayout;
|
||||
delete mainLayout;
|
||||
|
||||
topLayout = new QHBoxLayout;
|
||||
topLayout->setSpacing(0);
|
||||
topLayout->setMargin(0);
|
||||
|
||||
(void)_compact;
|
||||
setCompact(true);
|
||||
|
||||
if (true)
|
||||
{
|
||||
nameLabel->minimizeMaximumWidth();
|
||||
|
||||
mainLayout = nullptr;
|
||||
|
||||
container->setFixedHeight(25);
|
||||
container->setLayout(topLayout);
|
||||
|
||||
topLayout->addSpacing(18);
|
||||
topLayout->addWidget(&statusPic);
|
||||
topLayout->addSpacing(5);
|
||||
topLayout->addWidget(nameLabel, 100);
|
||||
topLayout->addWidget(lineFrame, 1);
|
||||
topLayout->addSpacing(5);
|
||||
topLayout->addWidget(statusLabel);
|
||||
topLayout->addSpacing(5);
|
||||
topLayout->activate();
|
||||
}
|
||||
/*else
|
||||
{
|
||||
nameLabel->setMaximumWidth(QWIDGETSIZE_MAX);
|
||||
|
||||
mainLayout = new QVBoxLayout();
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->setContentsMargins(20, 0, 20, 0);
|
||||
|
||||
container->setFixedHeight(25);
|
||||
container->setLayout(mainLayout);
|
||||
|
||||
topLayout->addWidget(&statusPic);
|
||||
topLayout->addSpacing(10);
|
||||
topLayout->addWidget(nameLabel, 1);
|
||||
topLayout->addSpacing(5);
|
||||
topLayout->addWidget(statusLabel);
|
||||
topLayout->activate();
|
||||
|
||||
mainLayout->addStretch();
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(lineFrame);
|
||||
mainLayout->addStretch();
|
||||
mainLayout->activate();
|
||||
}*/
|
||||
|
||||
Style::repolish(this);
|
||||
}
|
||||
|
||||
void CategoryWidget::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
setExpanded(!expanded);
|
||||
}
|
||||
|
||||
void CategoryWidget::setContainerAttribute(Qt::WidgetAttribute attribute, bool enabled)
|
||||
{
|
||||
container->setAttribute(attribute, enabled);
|
||||
Style::repolish(container);
|
||||
}
|
||||
|
||||
QLayout* CategoryWidget::friendOfflineLayout() const
|
||||
{
|
||||
return listLayout->getLayoutOffline();
|
||||
}
|
||||
|
||||
QLayout* CategoryWidget::friendOnlineLayout() const
|
||||
{
|
||||
return listLayout->getLayoutOnline();
|
||||
}
|
||||
|
||||
void CategoryWidget::moveFriendWidgets(FriendListWidget* friendList)
|
||||
{
|
||||
listLayout->moveFriendWidgets(friendList);
|
||||
}
|
80
src/widget/categorywidget.h
Normal file
80
src/widget/categorywidget.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CATEGORYWIDGET_H
|
||||
#define CATEGORYWIDGET_H
|
||||
|
||||
#include "genericchatitemwidget.h"
|
||||
#include "src/core/corestructs.h"
|
||||
|
||||
class FriendListLayout;
|
||||
class FriendListWidget;
|
||||
class FriendWidget;
|
||||
class QVBoxLayout;
|
||||
class QHBoxLayout;
|
||||
|
||||
class CategoryWidget : public GenericChatItemWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CategoryWidget(QWidget* parent = 0);
|
||||
|
||||
bool isExpanded() const;
|
||||
void setExpanded(bool isExpanded, bool save = true);
|
||||
void setName(const QString &name, bool save = true);
|
||||
|
||||
void addFriendWidget(FriendWidget* w, Status s);
|
||||
void removeFriendWidget(FriendWidget* w, Status s);
|
||||
void updateStatus();
|
||||
|
||||
bool hasChatrooms() const;
|
||||
bool cycleContacts(bool forward);
|
||||
bool cycleContacts(FriendWidget* activeChatroomWidget, bool forward);
|
||||
void search(const QString &searchString, bool updateAll = false, bool hideOnline = false, bool hideOffline = false);
|
||||
|
||||
public slots:
|
||||
void onCompactChanged(bool compact);
|
||||
|
||||
protected:
|
||||
virtual void leaveEvent(QEvent* event) final override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event) final override;
|
||||
|
||||
void editName();
|
||||
void setContainerAttribute(Qt::WidgetAttribute attribute, bool enabled);
|
||||
QLayout* friendOnlineLayout() const;
|
||||
QLayout* friendOfflineLayout() const;
|
||||
void moveFriendWidgets(FriendListWidget* friendList);
|
||||
|
||||
private:
|
||||
virtual void onSetName() {}
|
||||
virtual void onExpand() {}
|
||||
virtual void onAddFriendWidget(FriendWidget*) {}
|
||||
|
||||
QWidget* listWidget;
|
||||
FriendListLayout* listLayout;
|
||||
QVBoxLayout* fullLayout;
|
||||
QVBoxLayout* mainLayout = nullptr;
|
||||
QHBoxLayout* topLayout = nullptr;
|
||||
QLabel* statusLabel;
|
||||
QWidget* container;
|
||||
QFrame* lineFrame;
|
||||
bool expanded = false;
|
||||
};
|
||||
|
||||
#endif // CATEGORYWIDGET_H
|
196
src/widget/circlewidget.cpp
Normal file
196
src/widget/circlewidget.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "circlewidget.h"
|
||||
#include "friendwidget.h"
|
||||
#include "friendlistwidget.h"
|
||||
#include "tool/croppinglabel.h"
|
||||
#include "src/persistence/settings.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "src/friend.h"
|
||||
#include "widget.h"
|
||||
#include <QVariant>
|
||||
#include <QBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QMimeData>
|
||||
#include <QMenu>
|
||||
#include <cassert>
|
||||
|
||||
QHash<int, CircleWidget*> CircleWidget::circleList;
|
||||
|
||||
CircleWidget::CircleWidget(FriendListWidget* parent, int id)
|
||||
: CategoryWidget(parent)
|
||||
, id(id)
|
||||
{
|
||||
setName(Settings::getInstance().getCircleName(id), false);
|
||||
circleList[id] = this;
|
||||
|
||||
connect(nameLabel, &CroppingLabel::editFinished, [this](const QString &newName)
|
||||
{
|
||||
if (!newName.isEmpty())
|
||||
emit renameRequested(this, newName);
|
||||
});
|
||||
|
||||
connect(nameLabel, &CroppingLabel::editRemoved, [this]()
|
||||
{
|
||||
if (isCompact())
|
||||
nameLabel->minimizeMaximumWidth();
|
||||
});
|
||||
|
||||
setExpanded(Settings::getInstance().getCircleExpanded(id), false);
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
CircleWidget::~CircleWidget()
|
||||
{
|
||||
if (circleList[id] == this)
|
||||
circleList.remove(id);
|
||||
}
|
||||
|
||||
void CircleWidget::editName()
|
||||
{
|
||||
CategoryWidget::editName();
|
||||
}
|
||||
|
||||
CircleWidget* CircleWidget::getFromID(int id)
|
||||
{
|
||||
auto circleIt = circleList.find(id);
|
||||
|
||||
if (circleIt != circleList.end())
|
||||
return circleIt.value();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CircleWidget::contextMenuEvent(QContextMenuEvent* event)
|
||||
{
|
||||
QMenu menu;
|
||||
QAction* renameAction = menu.addAction(tr("Rename circle", "Menu for renaming a circle"));
|
||||
QAction* removeAction = menu.addAction(tr("Remove circle", "Menu for removing a circle"));
|
||||
|
||||
QAction* selectedItem = menu.exec(mapToGlobal(event->pos()));
|
||||
if (selectedItem == renameAction)
|
||||
{
|
||||
editName();
|
||||
}
|
||||
else if (selectedItem == removeAction)
|
||||
{
|
||||
FriendListWidget* friendList = static_cast<FriendListWidget*>(parentWidget());
|
||||
moveFriendWidgets(friendList);
|
||||
|
||||
friendList->removeCircleWidget(this);
|
||||
|
||||
int replacedCircle = Settings::getInstance().removeCircle(id);
|
||||
|
||||
auto circleReplace = circleList.find(replacedCircle);
|
||||
if (circleReplace != circleList.end())
|
||||
circleReplace.value()->updateID(id);
|
||||
else
|
||||
assert(true); // This should never happen.
|
||||
|
||||
circleList.remove(replacedCircle);
|
||||
}
|
||||
setContainerAttribute(Qt::WA_UnderMouse, false);
|
||||
}
|
||||
|
||||
void CircleWidget::dragEnterEvent(QDragEnterEvent* event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat("friend"))
|
||||
event->acceptProposedAction();
|
||||
|
||||
setContainerAttribute(Qt::WA_UnderMouse, true); // Simulate hover.
|
||||
}
|
||||
|
||||
void CircleWidget::dragLeaveEvent(QDragLeaveEvent* )
|
||||
{
|
||||
setContainerAttribute(Qt::WA_UnderMouse, false);
|
||||
}
|
||||
|
||||
void CircleWidget::dropEvent(QDropEvent* event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat("friend"))
|
||||
{
|
||||
setExpanded(true, false);
|
||||
|
||||
int friendId = event->mimeData()->data("friend").toInt();
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
assert(f != nullptr);
|
||||
|
||||
FriendWidget* widget = f->getFriendWidget();
|
||||
assert(widget != nullptr);
|
||||
|
||||
// Update old circle after moved.
|
||||
CircleWidget* circleWidget = getFromID(Settings::getInstance().getFriendCircleID(f->getToxId()));
|
||||
|
||||
addFriendWidget(widget, f->getStatus());
|
||||
Settings::getInstance().savePersonal();
|
||||
|
||||
if (circleWidget != nullptr)
|
||||
{
|
||||
circleWidget->updateStatus();
|
||||
Widget::getInstance()->searchCircle(circleWidget);
|
||||
}
|
||||
|
||||
setContainerAttribute(Qt::WA_UnderMouse, false);
|
||||
}
|
||||
}
|
||||
|
||||
void CircleWidget::onSetName()
|
||||
{
|
||||
Settings::getInstance().setCircleName(id, getName());
|
||||
}
|
||||
|
||||
void CircleWidget::onExpand()
|
||||
{
|
||||
Settings::getInstance().setCircleExpanded(id, isExpanded());
|
||||
Settings::getInstance().savePersonal();
|
||||
}
|
||||
|
||||
void CircleWidget::onAddFriendWidget(FriendWidget* w)
|
||||
{
|
||||
Settings::getInstance().setFriendCircleID(FriendList::findFriend(w->friendId)->getToxId(), id);
|
||||
}
|
||||
|
||||
void CircleWidget::updateID(int index)
|
||||
{
|
||||
// For when a circle gets destroyed, another takes its id.
|
||||
// This function updates all friends widgets for this new id.
|
||||
|
||||
if (id == index)
|
||||
return;
|
||||
|
||||
id = index;
|
||||
circleList[id] = this;
|
||||
|
||||
for (int i = 0; i < friendOnlineLayout()->count(); ++i)
|
||||
{
|
||||
FriendWidget* friendWidget = dynamic_cast<FriendWidget*>(friendOnlineLayout()->itemAt(i));
|
||||
|
||||
if (friendWidget != nullptr)
|
||||
Settings::getInstance().setFriendCircleID(FriendList::findFriend(friendWidget->friendId)->getToxId(), id);
|
||||
}
|
||||
for (int i = 0; i < friendOfflineLayout()->count(); ++i)
|
||||
{
|
||||
FriendWidget* friendWidget = dynamic_cast<FriendWidget*>(friendOfflineLayout()->itemAt(i));
|
||||
|
||||
if (friendWidget != nullptr)
|
||||
Settings::getInstance().setFriendCircleID(FriendList::findFriend(friendWidget->friendId)->getToxId(), id);
|
||||
}
|
||||
}
|
54
src/widget/circlewidget.h
Normal file
54
src/widget/circlewidget.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CIRCLEWIDGET_H
|
||||
#define CIRCLEWIDGET_H
|
||||
|
||||
#include "categorywidget.h"
|
||||
|
||||
class CircleWidget final : public CategoryWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CircleWidget(FriendListWidget* parent, int id);
|
||||
~CircleWidget();
|
||||
|
||||
void editName();
|
||||
static CircleWidget* getFromID(int id);
|
||||
|
||||
signals:
|
||||
void renameRequested(CircleWidget* circleWidget, const QString &newName);
|
||||
|
||||
protected:
|
||||
virtual void contextMenuEvent(QContextMenuEvent* event) final override;
|
||||
virtual void dragEnterEvent(QDragEnterEvent* event) final override;
|
||||
virtual void dragLeaveEvent(QDragLeaveEvent* event) final override;
|
||||
virtual void dropEvent(QDropEvent* event) final override;
|
||||
|
||||
private:
|
||||
virtual void onSetName() final override;
|
||||
virtual void onExpand() final override;
|
||||
virtual void onAddFriendWidget(FriendWidget* w) final override;
|
||||
void updateID(int index);
|
||||
|
||||
static QHash<int, CircleWidget*> circleList;
|
||||
int id;
|
||||
};
|
||||
|
||||
#endif // CIRCLEWIDGET_H
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QBoxLayout>
|
||||
#include <QScrollBar>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
@ -103,8 +104,10 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
Core::getInstance()->sendTyping(f->getFriendID(), false);
|
||||
isTyping = false;
|
||||
} );
|
||||
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig) {
|
||||
if (text != orig) emit aliasChanged(text);
|
||||
connect(nameLabel, &CroppingLabel::editFinished, this, [=](const QString& newName)
|
||||
{
|
||||
nameLabel->setText(newName);
|
||||
emit aliasChanged(newName);
|
||||
} );
|
||||
|
||||
setAcceptDrops(true);
|
||||
|
@ -199,6 +202,8 @@ void ChatForm::startFileSend(ToxFile file)
|
|||
}
|
||||
|
||||
insertChatMessage(ChatMessage::createFileTransferMessage(name, file, true, QDateTime::currentDateTime()));
|
||||
|
||||
Widget::getInstance()->updateFriendActivity(f);
|
||||
}
|
||||
|
||||
void ChatForm::onFileRecvRequest(ToxFile file)
|
||||
|
@ -233,6 +238,8 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
|||
FileTransferWidget* tfWidget = static_cast<FileTransferWidget*>(proxy->getWidget());
|
||||
tfWidget->autoAcceptTransfer(Settings::getInstance().getAutoAcceptDir(f->getToxId()));
|
||||
}
|
||||
|
||||
Widget::getInstance()->updateFriendActivity(f);
|
||||
}
|
||||
|
||||
void ChatForm::onAvInvite(uint32_t FriendId, int CallId, bool video)
|
||||
|
@ -407,6 +414,8 @@ void ChatForm::onAvRinging(uint32_t FriendId, int CallId, bool video)
|
|||
}
|
||||
|
||||
addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime());
|
||||
|
||||
Widget::getInstance()->updateFriendActivity(f);
|
||||
}
|
||||
|
||||
void ChatForm::onAvStarting(uint32_t FriendId, int CallId, bool video)
|
||||
|
@ -1045,6 +1054,8 @@ void ChatForm::SendMessageStr(QString msg)
|
|||
getOfflineMsgEngine()->registerReceipt(rec, id, ma);
|
||||
|
||||
msgEdit->setLastMessage(msg); //set last message only when sending it
|
||||
|
||||
Widget::getInstance()->updateFriendActivity(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -232,6 +232,23 @@ ChatLog *GenericChatForm::getChatLog() const
|
|||
return chatWidget;
|
||||
}
|
||||
|
||||
QDate GenericChatForm::getLatestDate() const
|
||||
{
|
||||
ChatLine::Ptr chatLine = chatWidget->getLatestLine();
|
||||
|
||||
if (chatLine)
|
||||
{
|
||||
Timestamp* timestamp = dynamic_cast<Timestamp*>(chatLine->getContent(2));
|
||||
|
||||
if (timestamp)
|
||||
return timestamp->getTime().date();
|
||||
else
|
||||
return QDate::currentDate();
|
||||
}
|
||||
|
||||
return QDate();
|
||||
}
|
||||
|
||||
void GenericChatForm::setName(const QString &newName)
|
||||
{
|
||||
nameLabel->setText(newName);
|
||||
|
@ -360,7 +377,11 @@ void GenericChatForm::onSaveLogClicked()
|
|||
auto lines = chatWidget->getLines();
|
||||
for (ChatLine::Ptr l : lines)
|
||||
{
|
||||
Timestamp* rightCol = static_cast<Timestamp*>(l->getContent(2));
|
||||
Timestamp* rightCol = dynamic_cast<Timestamp*>(l->getContent(2));
|
||||
|
||||
if (!rightCol)
|
||||
return;
|
||||
|
||||
ChatLineContent* middleCol = l->getContent(1);
|
||||
ChatLineContent* leftCol = l->getContent(0);
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
bool isEmpty();
|
||||
|
||||
ChatLog* getChatLog() const;
|
||||
QDate getLatestDate() const;
|
||||
|
||||
signals:
|
||||
void sendMessage(uint32_t, QString);
|
||||
|
|
|
@ -96,8 +96,14 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
|
|||
connect(callButton, &QPushButton::clicked, this, &GroupChatForm::onCallClicked);
|
||||
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
||||
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
|
||||
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig)
|
||||
{if (text != orig) emit groupTitleChanged(group->getGroupId(), text.left(128));} );
|
||||
connect(nameLabel, &CroppingLabel::editFinished, this, [=](const QString& newName)
|
||||
{
|
||||
if (!newName.isEmpty())
|
||||
{
|
||||
nameLabel->setText(newName);
|
||||
emit groupTitleChanged(group->getGroupId(), newName.left(128));
|
||||
}
|
||||
});
|
||||
|
||||
setAcceptDrops(true);
|
||||
Translator::registerHandler(std::bind(&GroupChatForm::retranslateUi, this), this);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include <QFileDialog>
|
||||
#include <QBuffer>
|
||||
#include <QMessageBox>
|
||||
#include <QComboBox>
|
||||
|
||||
ProfileForm::ProfileForm(QWidget *parent) :
|
||||
QWidget{parent}, qr{nullptr}
|
||||
|
|
124
src/widget/friendlistlayout.cpp
Normal file
124
src/widget/friendlistlayout.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "friendlistlayout.h"
|
||||
#include "src/friend.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "friendwidget.h"
|
||||
#include "friendlistwidget.h"
|
||||
#include <cassert>
|
||||
|
||||
FriendListLayout::FriendListLayout()
|
||||
{
|
||||
setSpacing(0);
|
||||
setMargin(0);
|
||||
|
||||
friendOnlineLayout.getLayout()->setSpacing(0);
|
||||
friendOnlineLayout.getLayout()->setMargin(0);
|
||||
|
||||
friendOfflineLayout.getLayout()->setSpacing(0);
|
||||
friendOfflineLayout.getLayout()->setMargin(0);
|
||||
|
||||
addLayout(friendOnlineLayout.getLayout());
|
||||
addLayout(friendOfflineLayout.getLayout());
|
||||
}
|
||||
|
||||
FriendListLayout::FriendListLayout(QWidget* parent)
|
||||
: QVBoxLayout(parent)
|
||||
{
|
||||
FriendListLayout();
|
||||
}
|
||||
|
||||
void FriendListLayout::addFriendWidget(FriendWidget* w, Status s)
|
||||
{
|
||||
friendOfflineLayout.removeSortedWidget(w);
|
||||
friendOnlineLayout.removeSortedWidget(w);
|
||||
|
||||
if (s == Status::Offline)
|
||||
{
|
||||
friendOfflineLayout.addSortedWidget(w);
|
||||
return;
|
||||
}
|
||||
|
||||
friendOnlineLayout.addSortedWidget(w);
|
||||
}
|
||||
|
||||
void FriendListLayout::removeFriendWidget(FriendWidget *widget, Status s)
|
||||
{
|
||||
if (s == Status::Offline)
|
||||
friendOfflineLayout.removeSortedWidget(widget);
|
||||
else
|
||||
friendOnlineLayout.removeSortedWidget(widget);
|
||||
}
|
||||
|
||||
int FriendListLayout::indexOfFriendWidget(FriendWidget* widget, bool online) const
|
||||
{
|
||||
if (online)
|
||||
return friendOnlineLayout.indexOfSortedWidget(widget);
|
||||
return friendOfflineLayout.indexOfSortedWidget(widget);
|
||||
}
|
||||
|
||||
void FriendListLayout::moveFriendWidgets(FriendListWidget* listWidget)
|
||||
{
|
||||
while (friendOnlineLayout.getLayout()->count() != 0)
|
||||
{
|
||||
QWidget* getWidget = friendOnlineLayout.getLayout()->takeAt(0)->widget();
|
||||
|
||||
FriendWidget* friendWidget = dynamic_cast<FriendWidget*>(getWidget);
|
||||
listWidget->moveWidget(friendWidget, FriendList::findFriend(friendWidget->friendId)->getStatus(), true);
|
||||
}
|
||||
while (friendOfflineLayout.getLayout()->count() != 0)
|
||||
{
|
||||
QWidget* getWidget = friendOfflineLayout.getLayout()->takeAt(0)->widget();
|
||||
|
||||
FriendWidget* friendWidget = dynamic_cast<FriendWidget*>(getWidget);
|
||||
listWidget->moveWidget(friendWidget, FriendList::findFriend(friendWidget->friendId)->getStatus(), true);
|
||||
}
|
||||
}
|
||||
|
||||
int FriendListLayout::friendOnlineCount() const
|
||||
{
|
||||
return friendOnlineLayout.getLayout()->count();
|
||||
}
|
||||
|
||||
int FriendListLayout::friendTotalCount() const
|
||||
{
|
||||
return friendOfflineLayout.getLayout()->count() + friendOnlineCount();
|
||||
}
|
||||
|
||||
bool FriendListLayout::hasChatrooms() const
|
||||
{
|
||||
return !(friendOfflineLayout.getLayout()->isEmpty() && friendOnlineLayout.getLayout()->isEmpty());
|
||||
}
|
||||
|
||||
void FriendListLayout::searchChatrooms(const QString& searchString, bool hideOnline, bool hideOffline)
|
||||
{
|
||||
friendOnlineLayout.search(searchString, hideOnline);
|
||||
friendOfflineLayout.search(searchString, hideOffline);
|
||||
}
|
||||
|
||||
QLayout* FriendListLayout::getLayoutOnline() const
|
||||
{
|
||||
return friendOnlineLayout.getLayout();
|
||||
}
|
||||
|
||||
QLayout* FriendListLayout::getLayoutOffline() const
|
||||
{
|
||||
return friendOfflineLayout.getLayout();
|
||||
}
|
||||
|
||||
QLayout* FriendListLayout::getFriendLayout(Status s) const
|
||||
{
|
||||
return s == Status::Offline ? friendOfflineLayout.getLayout() : friendOnlineLayout.getLayout();
|
||||
}
|
52
src/widget/friendlistlayout.h
Normal file
52
src/widget/friendlistlayout.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef FRIENDLISTLAYOUT_H
|
||||
#define FRIENDLISTLAYOUT_H
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include "src/core/corestructs.h"
|
||||
#include "genericchatitemlayout.h"
|
||||
|
||||
class FriendWidget;
|
||||
class FriendListWidget;
|
||||
|
||||
class FriendListLayout : public QVBoxLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FriendListLayout();
|
||||
explicit FriendListLayout(QWidget* parent);
|
||||
|
||||
void addFriendWidget(FriendWidget* widget, Status s);
|
||||
void removeFriendWidget(FriendWidget* widget, Status s);
|
||||
int indexOfFriendWidget(FriendWidget* widget, bool online) const;
|
||||
void moveFriendWidgets(FriendListWidget* listWidget);
|
||||
int friendOnlineCount() const;
|
||||
int friendTotalCount() const;
|
||||
|
||||
bool hasChatrooms() const;
|
||||
void searchChatrooms(const QString& searchString, bool hideOnline = false, bool hideOffline = false);
|
||||
|
||||
QLayout* getLayoutOnline() const;
|
||||
QLayout* getLayoutOffline() const;
|
||||
|
||||
private:
|
||||
QLayout* getFriendLayout(Status s) const;
|
||||
|
||||
GenericChatItemLayout friendOnlineLayout;
|
||||
GenericChatItemLayout friendOfflineLayout;
|
||||
};
|
||||
|
||||
#endif // FRIENDLISTLAYOUT_H
|
|
@ -16,122 +16,635 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "friendlistwidget.h"
|
||||
#include <QDebug>
|
||||
#include <QGridLayout>
|
||||
#include "friendlistlayout.h"
|
||||
#include "src/friend.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "src/widget/friendwidget.h"
|
||||
#include "src/persistence/settings.h"
|
||||
#include "friendwidget.h"
|
||||
#include "groupwidget.h"
|
||||
#include "circlewidget.h"
|
||||
#include "widget.h"
|
||||
#include "src/persistence/historykeeper.h"
|
||||
#include <QGridLayout>
|
||||
#include <QMimeData>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDragLeaveEvent>
|
||||
#include <cassert>
|
||||
|
||||
FriendListWidget::FriendListWidget(QWidget *parent, bool groupchatPosition) :
|
||||
QWidget(parent)
|
||||
enum Time : int
|
||||
{
|
||||
mainLayout = new QGridLayout();
|
||||
setLayout(mainLayout);
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
layout()->setSpacing(0);
|
||||
layout()->setMargin(0);
|
||||
Today = 0,
|
||||
Yesterday = 1,
|
||||
ThisWeek = 2,
|
||||
ThisMonth = 3,
|
||||
Month1Ago = 4,
|
||||
Month2Ago = 5,
|
||||
Month3Ago = 6,
|
||||
Month4Ago = 7,
|
||||
Month5Ago = 8,
|
||||
LongAgo = 9,
|
||||
Never = 10
|
||||
};
|
||||
|
||||
groupLayout = new QVBoxLayout();
|
||||
groupLayout->setSpacing(0);
|
||||
groupLayout->setMargin(0);
|
||||
bool last7DaysWasLastMonth()
|
||||
{
|
||||
return QDate::currentDate().addDays(-7).month() == QDate::currentDate().month();
|
||||
}
|
||||
|
||||
for (Status s : {Status::Online, Status::Offline})
|
||||
Time getTime(const QDate date)
|
||||
{
|
||||
if (date == QDate())
|
||||
return Never;
|
||||
|
||||
QDate today = QDate::currentDate();
|
||||
|
||||
if (date == today)
|
||||
return Today;
|
||||
|
||||
today = today.addDays(-1);
|
||||
|
||||
if (date == today)
|
||||
return Yesterday;
|
||||
|
||||
today = today.addDays(-6);
|
||||
|
||||
if (date >= today)
|
||||
return ThisWeek;
|
||||
|
||||
today = today.addDays(-today.day() + 1); // Go to the beginning of the month.
|
||||
|
||||
if (last7DaysWasLastMonth())
|
||||
{
|
||||
QVBoxLayout *l = new QVBoxLayout();
|
||||
l->setSpacing(0);
|
||||
l->setMargin(0);
|
||||
if (date >= today)
|
||||
return ThisMonth;
|
||||
|
||||
layouts[static_cast<int>(s)] = l;
|
||||
today = today.addMonths(-1);
|
||||
}
|
||||
|
||||
if (groupchatPosition)
|
||||
if (date >= today)
|
||||
return Month1Ago;
|
||||
|
||||
today = today.addMonths(-1);
|
||||
|
||||
if (date >= today)
|
||||
return Month2Ago;
|
||||
|
||||
today = today.addMonths(-1);
|
||||
|
||||
if (date >= today)
|
||||
return Month3Ago;
|
||||
|
||||
today = today.addMonths(-1);
|
||||
|
||||
if (date >= today)
|
||||
return Month4Ago;
|
||||
|
||||
today = today.addMonths(-1);
|
||||
|
||||
if (date >= today)
|
||||
return Month5Ago;
|
||||
|
||||
return LongAgo;
|
||||
}
|
||||
|
||||
QDate getDateFriend(Friend* contact)
|
||||
{
|
||||
return Settings::getInstance().getFriendActivity(contact->getToxId());
|
||||
}
|
||||
|
||||
FriendListWidget::FriendListWidget(Widget* parent, bool groupsOnTop)
|
||||
: QWidget(parent)
|
||||
// Prevent Valgrind from complaining. We're changing this to Name here.
|
||||
// Must be Activity for change to take effect.
|
||||
, mode(Activity)
|
||||
, groupsOnTop(groupsOnTop)
|
||||
{
|
||||
listLayout = new FriendListLayout();
|
||||
setLayout(listLayout);
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
|
||||
groupLayout.getLayout()->setSpacing(0);
|
||||
groupLayout.getLayout()->setMargin(0);
|
||||
|
||||
// Prevent QLayout's add child warning before setting the mode.
|
||||
listLayout->removeItem(listLayout->getLayoutOnline());
|
||||
listLayout->removeItem(listLayout->getLayoutOffline());
|
||||
|
||||
setMode(Name);
|
||||
|
||||
onGroupchatPositionChanged(groupsOnTop);
|
||||
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
FriendListWidget::~FriendListWidget()
|
||||
{
|
||||
if (activityLayout != nullptr)
|
||||
{
|
||||
mainLayout->addLayout(groupLayout, 0, 0);
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 1, 0);
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Offline)], 2, 0);
|
||||
QLayoutItem* item;
|
||||
while ((item = activityLayout->takeAt(0)) != nullptr)
|
||||
{
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
delete activityLayout;
|
||||
}
|
||||
|
||||
if (circleLayout != nullptr)
|
||||
{
|
||||
QLayoutItem* item;
|
||||
while ((item = circleLayout->getLayout()->takeAt(0)) != nullptr)
|
||||
{
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
delete circleLayout;
|
||||
}
|
||||
}
|
||||
|
||||
void FriendListWidget::setMode(Mode mode)
|
||||
{
|
||||
if (this->mode == mode)
|
||||
return;
|
||||
|
||||
this->mode = mode;
|
||||
|
||||
if (mode == Name)
|
||||
{
|
||||
circleLayout = new GenericChatItemLayout;
|
||||
circleLayout->getLayout()->setSpacing(0);
|
||||
circleLayout->getLayout()->setMargin(0);
|
||||
|
||||
for (int i = 0; i < Settings::getInstance().getCircleCount(); ++i)
|
||||
{
|
||||
addCircleWidget(i);
|
||||
CircleWidget::getFromID(i)->setVisible(false);
|
||||
}
|
||||
|
||||
// Only display circles once all created to avoid artifacts.
|
||||
for (int i = 0; i < Settings::getInstance().getCircleCount(); ++i)
|
||||
CircleWidget::getFromID(i)->setVisible(true);
|
||||
|
||||
QList<Friend*> friendList = FriendList::getAllFriends();
|
||||
for (Friend* contact : friendList)
|
||||
{
|
||||
int circleId = Settings::getInstance().getFriendCircleID(contact->getToxId());
|
||||
addFriendWidget(contact->getFriendWidget(), contact->getStatus(), circleId);
|
||||
}
|
||||
|
||||
listLayout->addLayout(listLayout->getLayoutOnline());
|
||||
listLayout->addLayout(listLayout->getLayoutOffline());
|
||||
listLayout->addLayout(circleLayout->getLayout());
|
||||
onGroupchatPositionChanged(groupsOnTop);
|
||||
|
||||
if (activityLayout != nullptr)
|
||||
{
|
||||
QLayoutItem* item;
|
||||
while ((item = activityLayout->takeAt(0)) != nullptr)
|
||||
{
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
delete activityLayout;
|
||||
activityLayout = nullptr;
|
||||
}
|
||||
|
||||
reDraw();
|
||||
}
|
||||
else if (mode == Activity)
|
||||
{
|
||||
activityLayout = new QVBoxLayout();
|
||||
|
||||
CategoryWidget* categoryToday = new CategoryWidget(this);
|
||||
categoryToday->setObjectName("Todddd");
|
||||
categoryToday->setName(tr("Today", "Category for sorting friends by activity"));
|
||||
activityLayout->addWidget(categoryToday);
|
||||
|
||||
CategoryWidget* categoryYesterday = new CategoryWidget(this);
|
||||
categoryYesterday->setName(tr("Yesterday", "Category for sorting friends by activity"));
|
||||
activityLayout->addWidget(categoryYesterday);
|
||||
|
||||
CategoryWidget* categoryLastWeek = new CategoryWidget(this);
|
||||
categoryLastWeek->setName(tr("Last 7 days", "Category for sorting friends by activity"));
|
||||
activityLayout->addWidget(categoryLastWeek);
|
||||
|
||||
QDate currentDate = QDate::currentDate();
|
||||
if (last7DaysWasLastMonth())
|
||||
{
|
||||
CategoryWidget* categoryThisMonth = new CategoryWidget(this);
|
||||
categoryThisMonth ->setName(tr("This month", "Category for sorting friends by activity"));
|
||||
activityLayout->addWidget(categoryThisMonth);
|
||||
currentDate = currentDate.addMonths(-1);
|
||||
}
|
||||
|
||||
CategoryWidget* categoryLast1Month = new CategoryWidget(this);
|
||||
categoryLast1Month ->setName(QDate::longMonthName(currentDate.month()));
|
||||
activityLayout->addWidget(categoryLast1Month);
|
||||
|
||||
currentDate = currentDate.addMonths(-1);
|
||||
CategoryWidget* categoryLast2Month = new CategoryWidget(this);
|
||||
categoryLast2Month ->setName(QDate::longMonthName(currentDate.month()));
|
||||
activityLayout->addWidget(categoryLast2Month);
|
||||
|
||||
currentDate = currentDate.addMonths(-1);
|
||||
CategoryWidget* categoryLast3Month = new CategoryWidget(this);
|
||||
categoryLast3Month ->setName(QDate::longMonthName(currentDate.month()));
|
||||
activityLayout->addWidget(categoryLast3Month);
|
||||
|
||||
currentDate = currentDate.addMonths(-1);
|
||||
CategoryWidget* categoryLast4Month = new CategoryWidget(this);
|
||||
categoryLast4Month ->setName(QDate::longMonthName(currentDate.month()));
|
||||
activityLayout->addWidget(categoryLast4Month);
|
||||
|
||||
currentDate = currentDate.addMonths(-1);
|
||||
CategoryWidget* categoryLast5Month = new CategoryWidget(this);
|
||||
categoryLast5Month ->setName(QDate::longMonthName(currentDate.month()));
|
||||
activityLayout->addWidget(categoryLast5Month);
|
||||
|
||||
CategoryWidget* categoryOlder = new CategoryWidget(this);
|
||||
categoryOlder->setName(tr("Older than 6 Months", "Category for sorting friends by activity"));
|
||||
activityLayout->addWidget(categoryOlder);
|
||||
|
||||
CategoryWidget* categoryNever = new CategoryWidget(this);
|
||||
categoryNever->setName(tr("Unknown", "Category for sorting friends by activity"));
|
||||
activityLayout->addWidget(categoryNever);
|
||||
|
||||
QList<Friend*> friendList = FriendList::getAllFriends();
|
||||
for (Friend* contact : friendList)
|
||||
{
|
||||
QDate activityDate = getDateFriend(contact);
|
||||
Time time = getTime(activityDate);
|
||||
CategoryWidget* categoryWidget = dynamic_cast<CategoryWidget*>(activityLayout->itemAt(time)->widget());
|
||||
categoryWidget->addFriendWidget(contact->getFriendWidget(), contact->getStatus());
|
||||
}
|
||||
|
||||
for (int i = 0; i < activityLayout->count(); ++i)
|
||||
{
|
||||
CategoryWidget* categoryWidget = dynamic_cast<CategoryWidget*>(activityLayout->itemAt(i)->widget());
|
||||
categoryWidget->setVisible(categoryWidget->hasChatrooms());
|
||||
}
|
||||
|
||||
listLayout->removeItem(listLayout->getLayoutOnline());
|
||||
listLayout->removeItem(listLayout->getLayoutOffline());
|
||||
listLayout->removeItem(circleLayout->getLayout());
|
||||
listLayout->insertLayout(1, activityLayout);
|
||||
|
||||
if (circleLayout != nullptr)
|
||||
{
|
||||
QLayoutItem* item;
|
||||
while ((item = circleLayout->getLayout()->takeAt(0)) != nullptr)
|
||||
{
|
||||
delete item->widget();
|
||||
delete item;
|
||||
}
|
||||
delete circleLayout;
|
||||
circleLayout = nullptr;
|
||||
}
|
||||
|
||||
reDraw();
|
||||
}
|
||||
}
|
||||
|
||||
FriendListWidget::Mode FriendListWidget::getMode() const
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
void FriendListWidget::addGroupWidget(GroupWidget* widget)
|
||||
{
|
||||
groupLayout.addSortedWidget(widget);
|
||||
connect(widget, &GroupWidget::renameRequested, this, &FriendListWidget::renameGroupWidget);
|
||||
}
|
||||
|
||||
void FriendListWidget::addFriendWidget(FriendWidget* w, Status s, int circleIndex)
|
||||
{
|
||||
CircleWidget* circleWidget = CircleWidget::getFromID(circleIndex);
|
||||
if (circleWidget == nullptr)
|
||||
moveWidget(w, s, true);
|
||||
else
|
||||
circleWidget->addFriendWidget(w, s);
|
||||
}
|
||||
|
||||
void FriendListWidget::removeFriendWidget(FriendWidget* w)
|
||||
{
|
||||
Friend* contact = FriendList::findFriend(w->friendId);
|
||||
if (mode == Activity)
|
||||
{
|
||||
QDate activityDate = getDateFriend(contact);
|
||||
Time time = getTime(activityDate);
|
||||
CategoryWidget* categoryWidget = dynamic_cast<CategoryWidget*>(activityLayout->itemAt(time)->widget());
|
||||
categoryWidget->removeFriendWidget(w, contact->getStatus());
|
||||
categoryWidget->setVisible(categoryWidget->hasChatrooms());
|
||||
}
|
||||
else
|
||||
{
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 0, 0);
|
||||
mainLayout->addLayout(groupLayout, 1, 0);
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Offline)], 2, 0);
|
||||
int id = Settings::getInstance().getFriendCircleID(contact->getToxId());
|
||||
CircleWidget* circleWidget = CircleWidget::getFromID(id);
|
||||
if (circleWidget != nullptr)
|
||||
{
|
||||
circleWidget->removeFriendWidget(w, contact->getStatus());
|
||||
Widget::getInstance()->searchCircle(circleWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVBoxLayout* FriendListWidget::getGroupLayout()
|
||||
void FriendListWidget::addCircleWidget(int id)
|
||||
{
|
||||
return groupLayout;
|
||||
createCircleWidget(id);
|
||||
}
|
||||
|
||||
QVBoxLayout* FriendListWidget::getFriendLayout(Status s)
|
||||
void FriendListWidget::addCircleWidget(FriendWidget* friendWidget)
|
||||
{
|
||||
auto res = layouts.find(static_cast<int>(s));
|
||||
if (res != layouts.end())
|
||||
return res.value();
|
||||
CircleWidget* circleWidget = createCircleWidget();
|
||||
if (circleWidget != nullptr)
|
||||
{
|
||||
if (friendWidget != nullptr)
|
||||
{
|
||||
CircleWidget* circleOriginal = CircleWidget::getFromID(Settings::getInstance().getFriendCircleID(FriendList::findFriend(friendWidget->friendId)->getToxId()));
|
||||
|
||||
return layouts[static_cast<int>(Status::Online)];
|
||||
circleWidget->addFriendWidget(friendWidget, FriendList::findFriend(friendWidget->friendId)->getStatus());
|
||||
circleWidget->setExpanded(true);
|
||||
|
||||
if (circleOriginal != nullptr)
|
||||
Widget::getInstance()->searchCircle(circleOriginal);
|
||||
|
||||
}
|
||||
|
||||
Widget::getInstance()->searchCircle(circleWidget);
|
||||
circleWidget->editName();
|
||||
}
|
||||
}
|
||||
|
||||
void FriendListWidget::removeCircleWidget(CircleWidget* widget)
|
||||
{
|
||||
circleLayout->removeSortedWidget(widget);
|
||||
widget->deleteLater();
|
||||
}
|
||||
|
||||
void FriendListWidget::searchChatrooms(const QString &searchString, bool hideOnline, bool hideOffline, bool hideGroups)
|
||||
{
|
||||
groupLayout.search(searchString, hideGroups);
|
||||
listLayout->searchChatrooms(searchString, hideOnline, hideOffline);
|
||||
|
||||
if (circleLayout != nullptr)
|
||||
{
|
||||
for (int i = 0; i != circleLayout->getLayout()->count(); ++i)
|
||||
{
|
||||
CircleWidget* circleWidget = static_cast<CircleWidget*>(circleLayout->getLayout()->itemAt(i)->widget());
|
||||
circleWidget->search(searchString, true, hideOnline, hideOffline);
|
||||
}
|
||||
}
|
||||
else if (activityLayout != nullptr)
|
||||
{
|
||||
for (int i = 0; i != activityLayout->count(); ++i)
|
||||
{
|
||||
CategoryWidget* categoryWidget = static_cast<CategoryWidget*>(activityLayout->itemAt(i)->widget());
|
||||
categoryWidget->search(searchString, true, hideOnline, hideOffline);
|
||||
categoryWidget->setVisible(categoryWidget->hasChatrooms());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FriendListWidget::renameGroupWidget(GroupWidget* groupWidget, const QString &newName)
|
||||
{
|
||||
groupLayout.removeSortedWidget(groupWidget);
|
||||
groupWidget->setName(newName);
|
||||
groupLayout.addSortedWidget(groupWidget);
|
||||
}
|
||||
|
||||
void FriendListWidget::renameCircleWidget(CircleWidget* circleWidget, const QString &newName)
|
||||
{
|
||||
circleLayout->removeSortedWidget(circleWidget);
|
||||
circleWidget->setName(newName);
|
||||
circleLayout->addSortedWidget(circleWidget);
|
||||
}
|
||||
|
||||
void FriendListWidget::onGroupchatPositionChanged(bool top)
|
||||
{
|
||||
mainLayout->removeItem(groupLayout);
|
||||
mainLayout->removeItem(getFriendLayout(Status::Online));
|
||||
groupsOnTop = top;
|
||||
|
||||
if (mode != Name)
|
||||
return;
|
||||
|
||||
listLayout->removeItem(groupLayout.getLayout());
|
||||
|
||||
if (top)
|
||||
listLayout->insertLayout(0, groupLayout.getLayout());
|
||||
else
|
||||
listLayout->insertLayout(1, groupLayout.getLayout());
|
||||
|
||||
reDraw();
|
||||
}
|
||||
|
||||
void FriendListWidget::cycleContacts(GenericChatroomWidget* activeChatroomWidget, bool forward)
|
||||
{
|
||||
if (activeChatroomWidget == nullptr)
|
||||
return;
|
||||
|
||||
int index = -1;
|
||||
FriendWidget* friendWidget = dynamic_cast<FriendWidget*>(activeChatroomWidget);
|
||||
|
||||
if (mode == Activity)
|
||||
{
|
||||
mainLayout->addLayout(groupLayout, 0, 0);
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 1, 0);
|
||||
if (friendWidget == nullptr)
|
||||
return;
|
||||
|
||||
QDate activityDate = getDateFriend(FriendList::findFriend(friendWidget->friendId));
|
||||
index = getTime(activityDate);
|
||||
CategoryWidget* categoryWidget = dynamic_cast<CategoryWidget*>(activityLayout->itemAt(index)->widget());
|
||||
|
||||
if (categoryWidget == nullptr || categoryWidget->cycleContacts(friendWidget, forward))
|
||||
return;
|
||||
|
||||
index += forward ? 1 : -1;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// Bounds checking.
|
||||
if (index < Today)
|
||||
{
|
||||
index = Never;
|
||||
continue;
|
||||
}
|
||||
else if (index > Never)
|
||||
{
|
||||
index = Today;
|
||||
continue;
|
||||
}
|
||||
|
||||
CategoryWidget* categoryWidget = dynamic_cast<CategoryWidget*>(activityLayout->itemAt(index)->widget());
|
||||
|
||||
if (categoryWidget != nullptr)
|
||||
{
|
||||
if (!categoryWidget->cycleContacts(forward))
|
||||
{
|
||||
// Skip empty or finished categories.
|
||||
index += forward ? 1 : -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QLayout* currentLayout = nullptr;
|
||||
CircleWidget* circleWidget = nullptr;
|
||||
|
||||
if (friendWidget != nullptr)
|
||||
{
|
||||
circleWidget = CircleWidget::getFromID(Settings::getInstance().getFriendCircleID(FriendList::findFriend(friendWidget->friendId)->getToxId()));
|
||||
if (circleWidget != nullptr)
|
||||
{
|
||||
if (circleWidget->cycleContacts(friendWidget, forward))
|
||||
return;
|
||||
|
||||
index = circleLayout->indexOfSortedWidget(circleWidget);
|
||||
currentLayout = circleLayout->getLayout();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLayout = listLayout->getLayoutOnline();
|
||||
index = listLayout->indexOfFriendWidget(friendWidget, true);
|
||||
if (index == -1)
|
||||
{
|
||||
currentLayout = listLayout->getLayoutOffline();
|
||||
index = listLayout->indexOfFriendWidget(friendWidget, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mainLayout->addLayout(layouts[static_cast<int>(Status::Online)], 0, 0);
|
||||
mainLayout->addLayout(groupLayout, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
QList<GenericChatroomWidget*> FriendListWidget::getAllFriends()
|
||||
{
|
||||
QList<GenericChatroomWidget*> friends;
|
||||
|
||||
for (int i = 0; i < mainLayout->count(); ++i)
|
||||
{
|
||||
QLayout* subLayout = mainLayout->itemAt(i)->layout();
|
||||
|
||||
if(!subLayout)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < subLayout->count(); ++j)
|
||||
GroupWidget* groupWidget = dynamic_cast<GroupWidget*>(activeChatroomWidget);
|
||||
if (groupWidget != nullptr)
|
||||
{
|
||||
GenericChatroomWidget* widget =
|
||||
reinterpret_cast<GenericChatroomWidget*>(subLayout->itemAt(j)->widget());
|
||||
|
||||
if(!widget)
|
||||
continue;
|
||||
|
||||
friends.append(widget);
|
||||
currentLayout = groupLayout.getLayout();
|
||||
index = groupLayout.indexOfSortedWidget(groupWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
return friends;
|
||||
}
|
||||
index += forward ? 1 : -1;
|
||||
|
||||
void FriendListWidget::moveWidget(FriendWidget *w, Status s)
|
||||
{
|
||||
QVBoxLayout* l = getFriendLayout(s);
|
||||
l->removeWidget(w);
|
||||
Friend* g = FriendList::findFriend(w->friendId);
|
||||
for (int i = 0; i < l->count(); i++)
|
||||
for (;;)
|
||||
{
|
||||
FriendWidget* w1 = static_cast<FriendWidget*>(l->itemAt(i)->widget());
|
||||
Friend* f = FriendList::findFriend(w1->friendId);
|
||||
if (f->getDisplayedName().localeAwareCompare(g->getDisplayedName()) > 0)
|
||||
// Bounds checking.
|
||||
if (index < 0)
|
||||
{
|
||||
currentLayout = nextLayout(currentLayout, forward);
|
||||
index = currentLayout->count() - 1;
|
||||
continue;
|
||||
}
|
||||
else if (index >= currentLayout->count())
|
||||
{
|
||||
currentLayout = nextLayout(currentLayout, forward);
|
||||
index = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Go to the actual next index.
|
||||
if (currentLayout == listLayout->getLayoutOnline() || currentLayout == listLayout->getLayoutOffline() || currentLayout == groupLayout.getLayout())
|
||||
{
|
||||
GenericChatroomWidget* chatWidget = dynamic_cast<GenericChatroomWidget*>(currentLayout->itemAt(index)->widget());
|
||||
|
||||
if (chatWidget != nullptr)
|
||||
emit chatWidget->chatroomWidgetClicked(chatWidget);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (currentLayout == circleLayout->getLayout())
|
||||
{
|
||||
circleWidget = dynamic_cast<CircleWidget*>(currentLayout->itemAt(index)->widget());
|
||||
if (circleWidget != nullptr)
|
||||
{
|
||||
if (!circleWidget->cycleContacts(forward))
|
||||
{
|
||||
// Skip empty or finished circles.
|
||||
index += forward ? 1 : -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
l->insertWidget(i,w);
|
||||
return;
|
||||
}
|
||||
}
|
||||
static_assert(std::is_same<decltype(w), FriendWidget*>(), "The layout must only contain FriendWidget*");
|
||||
l->addWidget(w);
|
||||
}
|
||||
|
||||
QVector<CircleWidget*> FriendListWidget::getAllCircles()
|
||||
{
|
||||
QVector<CircleWidget*> vec;
|
||||
vec.reserve(circleLayout->getLayout()->count());
|
||||
for (int i = 0; i < circleLayout->getLayout()->count(); ++i)
|
||||
{
|
||||
vec.push_back(dynamic_cast<CircleWidget*>(circleLayout->getLayout()->itemAt(i)->widget()));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
void FriendListWidget::dragEnterEvent(QDragEnterEvent* event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat("friend"))
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
void FriendListWidget::dropEvent(QDropEvent* event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat("friend"))
|
||||
{
|
||||
int friendId = event->mimeData()->data("friend").toInt();
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
assert(f != nullptr);
|
||||
|
||||
FriendWidget* widget = f->getFriendWidget();
|
||||
assert(widget != nullptr);
|
||||
|
||||
// Update old circle after moved.
|
||||
CircleWidget* circleWidget = CircleWidget::getFromID(Settings::getInstance().getFriendCircleID(f->getToxId()));
|
||||
|
||||
moveWidget(widget, f->getStatus(), true);
|
||||
|
||||
if (circleWidget != nullptr)
|
||||
circleWidget->updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void FriendListWidget::moveWidget(FriendWidget* w, Status s, bool add)
|
||||
{
|
||||
if (mode == Name)
|
||||
{
|
||||
int circleId = Settings::getInstance().getFriendCircleID(FriendList::findFriend(w->friendId)->getToxId());
|
||||
CircleWidget* circleWidget = CircleWidget::getFromID(circleId);
|
||||
|
||||
if (circleWidget == nullptr || add)
|
||||
{
|
||||
if (circleId != -1)
|
||||
Settings::getInstance().setFriendCircleID(FriendList::findFriend(w->friendId)->getToxId(), -1);
|
||||
|
||||
listLayout->addFriendWidget(w, s);
|
||||
return;
|
||||
}
|
||||
|
||||
circleWidget->addFriendWidget(w, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
Friend* contact = FriendList::findFriend(w->friendId);
|
||||
QDate activityDate = getDateFriend(contact);
|
||||
Time time = getTime(activityDate);
|
||||
CategoryWidget* categoryWidget = dynamic_cast<CategoryWidget*>(activityLayout->itemAt(time)->widget());
|
||||
categoryWidget->addFriendWidget(contact->getFriendWidget(), contact->getStatus());
|
||||
categoryWidget->show();
|
||||
}
|
||||
}
|
||||
|
||||
// update widget after add/delete/hide/show
|
||||
|
@ -141,3 +654,83 @@ void FriendListWidget::reDraw()
|
|||
show();
|
||||
resize(QSize()); //lifehack
|
||||
}
|
||||
|
||||
CircleWidget* FriendListWidget::createCircleWidget(int id)
|
||||
{
|
||||
if (id == -1)
|
||||
id = Settings::getInstance().addCircle();
|
||||
|
||||
// Stop, after it has been created. Code after this is for displaying.
|
||||
if (mode == Activity)
|
||||
return nullptr;
|
||||
|
||||
assert(circleLayout != nullptr);
|
||||
|
||||
CircleWidget* circleWidget = new CircleWidget(this, id);
|
||||
circleLayout->addSortedWidget(circleWidget);
|
||||
connect(this, &FriendListWidget::onCompactChanged, circleWidget, &CircleWidget::onCompactChanged);
|
||||
connect(circleWidget, &CircleWidget::renameRequested, this, &FriendListWidget::renameCircleWidget);
|
||||
circleWidget->show(); // Avoid flickering.
|
||||
|
||||
return circleWidget;
|
||||
}
|
||||
|
||||
QLayout* FriendListWidget::nextLayout(QLayout* layout, bool forward) const
|
||||
{
|
||||
if (layout == groupLayout.getLayout())
|
||||
{
|
||||
if (forward)
|
||||
{
|
||||
if (groupsOnTop)
|
||||
return listLayout->getLayoutOnline();
|
||||
|
||||
return listLayout->getLayoutOffline();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (groupsOnTop)
|
||||
return circleLayout->getLayout();
|
||||
|
||||
return listLayout->getLayoutOnline();
|
||||
}
|
||||
}
|
||||
else if (layout == listLayout->getLayoutOnline())
|
||||
{
|
||||
if (forward)
|
||||
{
|
||||
if (groupsOnTop)
|
||||
return listLayout->getLayoutOffline();
|
||||
|
||||
return groupLayout.getLayout();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (groupsOnTop)
|
||||
return groupLayout.getLayout();
|
||||
|
||||
return circleLayout->getLayout();
|
||||
}
|
||||
}
|
||||
else if (layout == listLayout->getLayoutOffline())
|
||||
{
|
||||
if (forward)
|
||||
return circleLayout->getLayout();
|
||||
else if (groupsOnTop)
|
||||
return listLayout->getLayoutOnline();
|
||||
|
||||
return groupLayout.getLayout();
|
||||
}
|
||||
else if (layout == circleLayout->getLayout())
|
||||
{
|
||||
if (forward)
|
||||
{
|
||||
if (groupsOnTop)
|
||||
return groupLayout.getLayout();
|
||||
|
||||
return listLayout->getLayoutOnline();
|
||||
}
|
||||
else
|
||||
return listLayout->getLayoutOffline();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -21,36 +21,70 @@
|
|||
#define FRIENDLISTWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include "src/core/corestructs.h"
|
||||
#include "src/widget/genericchatroomwidget.h"
|
||||
#include "genericchatitemlayout.h"
|
||||
|
||||
class QVBoxLayout;
|
||||
class QGridLayout;
|
||||
class QPixmap;
|
||||
struct FriendWidget;
|
||||
class Widget;
|
||||
class FriendWidget;
|
||||
class GroupWidget;
|
||||
class CircleWidget;
|
||||
class FriendListLayout;
|
||||
class GenericChatroomWidget;
|
||||
|
||||
class FriendListWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FriendListWidget(QWidget *parent = 0, bool groupchatPosition = true);
|
||||
QVBoxLayout* getGroupLayout();
|
||||
QVBoxLayout* getFriendLayout(Status s);
|
||||
enum Mode : uint8_t
|
||||
{
|
||||
Name,
|
||||
Activity,
|
||||
};
|
||||
|
||||
explicit FriendListWidget(Widget* parent, bool groupsOnTop = true);
|
||||
~FriendListWidget();
|
||||
void setMode(Mode mode);
|
||||
Mode getMode() const;
|
||||
|
||||
void addGroupWidget(GroupWidget* widget);
|
||||
void addFriendWidget(FriendWidget* w, Status s, int circleIndex);
|
||||
void removeFriendWidget(FriendWidget* w);
|
||||
void addCircleWidget(int id);
|
||||
void addCircleWidget(FriendWidget* widget = nullptr);
|
||||
void removeCircleWidget(CircleWidget* widget);
|
||||
void searchChatrooms(const QString &searchString, bool hideOnline = false, bool hideOffline = false, bool hideGroups = false);
|
||||
|
||||
void cycleContacts(GenericChatroomWidget* activeChatroomWidget, bool forward);
|
||||
QVector<CircleWidget*> getAllCircles();
|
||||
|
||||
QList<GenericChatroomWidget*> getAllFriends();
|
||||
void reDraw();
|
||||
|
||||
signals:
|
||||
void onCompactChanged(bool compact);
|
||||
|
||||
public slots:
|
||||
void renameGroupWidget(GroupWidget* groupWidget, const QString& newName);
|
||||
void renameCircleWidget(CircleWidget* circleWidget, const QString& newName);
|
||||
void onGroupchatPositionChanged(bool top);
|
||||
void moveWidget(FriendWidget *w, Status s);
|
||||
void moveWidget(FriendWidget* w, Status s, bool add = false);
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
|
||||
private:
|
||||
QHash<int, QVBoxLayout*> layouts;
|
||||
QVBoxLayout *groupLayout;
|
||||
QGridLayout *mainLayout;
|
||||
CircleWidget* createCircleWidget(int id = -1);
|
||||
QLayout* nextLayout(QLayout* layout, bool forward) const;
|
||||
|
||||
Mode mode;
|
||||
bool groupsOnTop;
|
||||
FriendListLayout* listLayout;
|
||||
GenericChatItemLayout* circleLayout = nullptr;
|
||||
GenericChatItemLayout groupLayout;
|
||||
QVBoxLayout* activityLayout = nullptr;
|
||||
};
|
||||
|
||||
#endif // FRIENDLISTWIDGET_H
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "src/group.h"
|
||||
#include "src/grouplist.h"
|
||||
#include "groupwidget.h"
|
||||
#include "circlewidget.h"
|
||||
#include "friendlistwidget.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "src/friend.h"
|
||||
#include "src/core/core.h"
|
||||
|
@ -37,6 +39,8 @@
|
|||
#include <QFileDialog>
|
||||
#include <QDebug>
|
||||
#include <QInputDialog>
|
||||
#include <QCollator>
|
||||
#include <cassert>
|
||||
|
||||
FriendWidget::FriendWidget(int FriendId, QString id)
|
||||
: friendId(FriendId)
|
||||
|
@ -48,28 +52,78 @@ FriendWidget::FriendWidget(int FriendId, QString id)
|
|||
statusPic.setMargin(3);
|
||||
nameLabel->setText(id);
|
||||
nameLabel->setTextFormat(Qt::PlainText);
|
||||
connect(nameLabel, &CroppingLabel::editFinished, this, &FriendWidget::setAlias);
|
||||
statusMessageLabel->setTextFormat(Qt::PlainText);
|
||||
}
|
||||
|
||||
void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
|
||||
{
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Highlight);
|
||||
|
||||
installEventFilter(this); // Disable leave event.
|
||||
|
||||
QPoint pos = event->globalPos();
|
||||
ToxId id = FriendList::findFriend(friendId)->getToxId();
|
||||
QString dir = Settings::getInstance().getAutoAcceptDir(id);
|
||||
QMenu menu;
|
||||
QMenu* inviteMenu = menu.addMenu(tr("Invite to group","Menu to invite a friend to a groupchat"));
|
||||
QAction* copyId = menu.addAction(tr("Copy friend ID","Menu to copy the Tox ID of that friend"));
|
||||
QMap<QAction*, Group*> groupActions;
|
||||
|
||||
|
||||
for (Group* group : GroupList::getAllGroups())
|
||||
{
|
||||
QAction* groupAction = inviteMenu->addAction(group->getGroupWidget()->getName());
|
||||
groupActions[groupAction] = group;
|
||||
}
|
||||
|
||||
|
||||
if (groupActions.isEmpty())
|
||||
inviteMenu->setEnabled(false);
|
||||
|
||||
|
||||
int circleId = Settings::getInstance().getFriendCircleID(FriendList::findFriend(friendId)->getToxId());
|
||||
CircleWidget *circleWidget = CircleWidget::getFromID(circleId);
|
||||
|
||||
QMenu* circleMenu = nullptr;
|
||||
QAction* newCircleAction = nullptr;
|
||||
QAction *removeCircleAction = nullptr;
|
||||
QMap<QAction*, int> circleActions;
|
||||
|
||||
FriendListWidget *friendList;
|
||||
|
||||
if (circleWidget == nullptr)
|
||||
friendList = dynamic_cast<FriendListWidget*>(parentWidget());
|
||||
else
|
||||
friendList = dynamic_cast<FriendListWidget*>(circleWidget->parentWidget());
|
||||
|
||||
circleMenu = menu.addMenu(tr("Move to circle...", "Menu to move a friend into a different circle"));
|
||||
|
||||
newCircleAction = circleMenu->addAction(tr("To new circle"));
|
||||
|
||||
if (circleId != -1)
|
||||
removeCircleAction = circleMenu->addAction(tr("Remove from circle '%1'").arg(Settings::getInstance().getCircleName(circleId)));
|
||||
|
||||
circleMenu->addSeparator();
|
||||
|
||||
QList<QAction*> circleActionList;
|
||||
|
||||
for (int i = 0; i < Settings::getInstance().getCircleCount(); ++i)
|
||||
{
|
||||
if (i != circleId)
|
||||
{
|
||||
circleActionList.push_back(new QAction(tr("Move to circle \"%1\"").arg(Settings::getInstance().getCircleName(i)), circleMenu));
|
||||
circleActions[circleActionList.back()] = i;
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(circleActionList.begin(), circleActionList.end(), [](const QAction* lhs, const QAction* rhs) -> bool
|
||||
{
|
||||
QCollator collator;
|
||||
collator.setNumericMode(true);
|
||||
return collator.compare(lhs->text(), rhs->text()) < 0;
|
||||
});
|
||||
|
||||
circleMenu->addActions(circleActionList);
|
||||
|
||||
QAction* copyId = menu.addAction(tr("Copy friend ID","Menu to copy the Tox ID of that friend"));
|
||||
QAction* setAlias = menu.addAction(tr("Set alias..."));
|
||||
|
||||
menu.addSeparator();
|
||||
|
@ -77,10 +131,16 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
|
|||
autoAccept->setCheckable(true);
|
||||
autoAccept->setChecked(!dir.isEmpty());
|
||||
menu.addSeparator();
|
||||
|
||||
|
||||
QAction* removeFriendAction = menu.addAction(tr("Remove friend", "Menu to remove the friend from our friendlist"));
|
||||
|
||||
QAction* selectedItem = menu.exec(pos);
|
||||
|
||||
removeEventFilter(this);
|
||||
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Window);
|
||||
|
||||
if (selectedItem)
|
||||
{
|
||||
if (selectedItem == copyId)
|
||||
|
@ -89,7 +149,7 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
|
|||
return;
|
||||
} else if (selectedItem == setAlias)
|
||||
{
|
||||
setFriendAlias();
|
||||
nameLabel->editBegin();
|
||||
}
|
||||
else if (selectedItem == removeFriendAction)
|
||||
{
|
||||
|
@ -105,7 +165,7 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
|
|||
autoAccept->setChecked(false);
|
||||
Settings::getInstance().setAutoAcceptDir(id, "");
|
||||
}
|
||||
|
||||
|
||||
if (autoAccept->isChecked())
|
||||
{
|
||||
dir = QFileDialog::getExistingDirectory(0, tr("Choose an auto accept directory","popup title"), dir);
|
||||
|
@ -114,11 +174,54 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
|
|||
Settings::getInstance().setAutoAcceptDir(id, dir);
|
||||
}
|
||||
}
|
||||
else if (selectedItem == newCircleAction)
|
||||
{
|
||||
if (circleWidget != nullptr)
|
||||
circleWidget->updateStatus();
|
||||
|
||||
if (friendList != nullptr)
|
||||
friendList->addCircleWidget(this);
|
||||
else
|
||||
Settings::getInstance().setFriendCircleID(id, Settings::getInstance().addCircle());
|
||||
}
|
||||
else if (groupActions.contains(selectedItem))
|
||||
{
|
||||
Group* group = groupActions[selectedItem];
|
||||
Core::getInstance()->groupInviteFriend(friendId, group->getGroupId());
|
||||
}
|
||||
else if (removeCircleAction != nullptr && selectedItem == removeCircleAction)
|
||||
{
|
||||
if (friendList != nullptr)
|
||||
friendList->moveWidget(this, FriendList::findFriend(friendId)->getStatus(), true);
|
||||
else
|
||||
Settings::getInstance().setFriendCircleID(id, -1);
|
||||
|
||||
if (circleWidget)
|
||||
{
|
||||
circleWidget->updateStatus();
|
||||
Widget::getInstance()->searchCircle(circleWidget);
|
||||
}
|
||||
}
|
||||
else if (circleActions.contains(selectedItem))
|
||||
{
|
||||
CircleWidget* circle = CircleWidget::getFromID(circleActions[selectedItem]);
|
||||
|
||||
if (circle != nullptr)
|
||||
{
|
||||
circle->addFriendWidget(this, FriendList::findFriend(friendId)->getStatus());
|
||||
circle->setExpanded(true);
|
||||
Widget::getInstance()->searchCircle(circle);
|
||||
Settings::getInstance().savePersonal();
|
||||
}
|
||||
else
|
||||
Settings::getInstance().setFriendCircleID(id, circleActions[selectedItem]);
|
||||
|
||||
if (circleWidget != nullptr)
|
||||
{
|
||||
circleWidget->updateStatus();
|
||||
Widget::getInstance()->searchCircle(circleWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,6 +263,15 @@ void FriendWidget::updateStatusLight()
|
|||
else if (status == Status::Offline && f->getEventFlag() == 1)
|
||||
statusPic.setPixmap(QPixmap(":img/status/dot_offline_notification.svg"));
|
||||
|
||||
if (f->getEventFlag())
|
||||
{
|
||||
CircleWidget* circleWidget = CircleWidget::getFromID(Settings::getInstance().getFriendCircleID(FriendList::findFriend(friendId)->getToxId()));
|
||||
if (circleWidget != nullptr)
|
||||
circleWidget->setExpanded(true);
|
||||
|
||||
Widget::getInstance()->updateFriendActivity(FriendList::findFriend(friendId));
|
||||
}
|
||||
|
||||
if (!f->getEventFlag())
|
||||
statusPic.setMargin(3);
|
||||
else
|
||||
|
@ -184,6 +296,14 @@ QString FriendWidget::getStatusString()
|
|||
return QString::null;
|
||||
}
|
||||
|
||||
void FriendWidget::search(const QString &searchString, bool hide)
|
||||
{
|
||||
searchName(searchString, hide);
|
||||
CircleWidget* circleWidget = CircleWidget::getFromID(Settings::getInstance().getFriendCircleID(FriendList::findFriend(friendId)->getToxId()));
|
||||
if (circleWidget != nullptr)
|
||||
circleWidget->search(searchString);
|
||||
}
|
||||
|
||||
void FriendWidget::setChatForm(Ui::MainWindow &ui)
|
||||
{
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
|
@ -247,25 +367,9 @@ void FriendWidget::mouseMoveEvent(QMouseEvent *ev)
|
|||
|
||||
void FriendWidget::setAlias(const QString& _alias)
|
||||
{
|
||||
QString alias = _alias.trimmed();
|
||||
alias.remove(QRegExp("[\\t\\n\\v\\f\\r\\x0000]")); // we should really treat regular names this way as well (*ahem* zetok)
|
||||
alias = alias.left(128); // same as TOX_MAX_NAME_LENGTH
|
||||
QString alias = _alias.left(128); // same as TOX_MAX_NAME_LENGTH
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
f->setAlias(alias);
|
||||
Settings::getInstance().setFriendAlias(f->getToxId(), alias);
|
||||
Settings::getInstance().savePersonal();
|
||||
hide();
|
||||
show();
|
||||
}
|
||||
|
||||
void FriendWidget::setFriendAlias()
|
||||
{
|
||||
bool ok;
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
|
||||
QString alias = QInputDialog::getText(nullptr, tr("User alias"), tr("You can also set this by clicking the chat form name.\nAlias:"), QLineEdit::Normal,
|
||||
f->getDisplayedName(), &ok);
|
||||
|
||||
if (ok)
|
||||
setAlias(alias);
|
||||
}
|
||||
|
|
|
@ -18,14 +18,12 @@
|
|||
#ifndef FRIENDWIDGET_H
|
||||
#define FRIENDWIDGET_H
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
#include "genericchatroomwidget.h"
|
||||
|
||||
class QPixmap;
|
||||
class MaskablePixmapWidget;
|
||||
|
||||
struct FriendWidget final : public GenericChatroomWidget
|
||||
class FriendWidget : public GenericChatroomWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -37,6 +35,7 @@ public:
|
|||
virtual void setChatForm(Ui::MainWindow &) override;
|
||||
virtual void resetEventFlags() override;
|
||||
virtual QString getStatusString() override;
|
||||
void search(const QString &searchString, bool hide = false);
|
||||
|
||||
signals:
|
||||
void friendWidgetClicked(FriendWidget* widget);
|
||||
|
@ -49,8 +48,8 @@ public slots:
|
|||
void setAlias(const QString& alias);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent* ev) final override;
|
||||
virtual void mouseMoveEvent(QMouseEvent* ev) final override;
|
||||
virtual void mousePressEvent(QMouseEvent* ev) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent* ev) override;
|
||||
void setFriendAlias();
|
||||
|
||||
public:
|
||||
|
|
127
src/widget/genericchatitemlayout.cpp
Normal file
127
src/widget/genericchatitemlayout.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "genericchatitemlayout.h"
|
||||
#include "genericchatitemwidget.h"
|
||||
#include <QBoxLayout>
|
||||
#include <QCollator>
|
||||
#include <cassert>
|
||||
|
||||
// As this layout sorts widget, extra care must be taken when inserting widgets.
|
||||
// Prefer using the build in add and remove functions for modifying widgets.
|
||||
// Inserting widgets other ways would cause this layout to be unable to sort.
|
||||
// As such, they are protected using asserts.
|
||||
|
||||
GenericChatItemLayout::GenericChatItemLayout()
|
||||
: layout(new QVBoxLayout())
|
||||
{
|
||||
}
|
||||
|
||||
GenericChatItemLayout::~GenericChatItemLayout()
|
||||
{
|
||||
delete layout;
|
||||
}
|
||||
|
||||
void GenericChatItemLayout::addSortedWidget(GenericChatItemWidget* widget, int stretch, Qt::Alignment alignment)
|
||||
{
|
||||
int closest = indexOfClosestSortedWidget(widget);
|
||||
layout->insertWidget(closest, widget, stretch, alignment);
|
||||
}
|
||||
|
||||
int GenericChatItemLayout::indexOfSortedWidget(GenericChatItemWidget* widget) const
|
||||
{
|
||||
if (layout->count() == 0)
|
||||
return -1;
|
||||
|
||||
int index = indexOfClosestSortedWidget(widget);
|
||||
|
||||
if (index >= layout->count())
|
||||
return -1;
|
||||
|
||||
GenericChatItemWidget* atMid = dynamic_cast<GenericChatItemWidget*>(layout->itemAt(index)->widget());
|
||||
assert(atMid != nullptr);
|
||||
|
||||
if (atMid == widget)
|
||||
return index;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool GenericChatItemLayout::existsSortedWidget(GenericChatItemWidget* widget) const
|
||||
{
|
||||
return indexOfSortedWidget(widget) != -1;
|
||||
}
|
||||
|
||||
void GenericChatItemLayout::removeSortedWidget(GenericChatItemWidget* widget)
|
||||
{
|
||||
if (layout->isEmpty())
|
||||
return;
|
||||
|
||||
int index = indexOfClosestSortedWidget(widget);
|
||||
|
||||
if (layout->itemAt(index) == nullptr)
|
||||
return;
|
||||
|
||||
GenericChatItemWidget* atMid = dynamic_cast<GenericChatItemWidget*>(layout->itemAt(index)->widget());
|
||||
assert(atMid != nullptr);
|
||||
|
||||
if (atMid == widget)
|
||||
layout->removeWidget(widget);
|
||||
}
|
||||
|
||||
void GenericChatItemLayout::search(const QString &searchString, bool hideAll)
|
||||
{
|
||||
for (int index = 0; index < layout->count(); ++index)
|
||||
{
|
||||
GenericChatItemWidget* widgetAt = dynamic_cast<GenericChatItemWidget*>(layout->itemAt(index)->widget());
|
||||
assert(widgetAt != nullptr);
|
||||
|
||||
widgetAt->searchName(searchString, hideAll);
|
||||
}
|
||||
}
|
||||
|
||||
QLayout* GenericChatItemLayout::getLayout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
|
||||
int GenericChatItemLayout::indexOfClosestSortedWidget(GenericChatItemWidget* widget) const
|
||||
{
|
||||
// Binary search: Deferred test of equality.
|
||||
int min = 0, max = layout->count(), mid;
|
||||
while (min < max)
|
||||
{
|
||||
mid = (max - min) / 2 + min;
|
||||
GenericChatItemWidget* atMid = dynamic_cast<GenericChatItemWidget*>(layout->itemAt(mid)->widget());
|
||||
assert(atMid != nullptr);
|
||||
|
||||
bool lessThan = false;
|
||||
|
||||
QCollator collator;
|
||||
collator.setNumericMode(true);
|
||||
|
||||
int compareValue = collator.compare(atMid->getName(), widget->getName());
|
||||
|
||||
if (compareValue < 0)
|
||||
lessThan = true;
|
||||
else if (compareValue == 0)
|
||||
lessThan = atMid < widget; // Consistent ordering.
|
||||
|
||||
if (lessThan)
|
||||
min = mid + 1;
|
||||
else
|
||||
max = mid;
|
||||
}
|
||||
return min;
|
||||
}
|
43
src/widget/genericchatitemlayout.h
Normal file
43
src/widget/genericchatitemlayout.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#ifndef GENERICCHATITEMLAYOUT_H
|
||||
#define GENERICCHATITEMLAYOUT_H
|
||||
|
||||
#include <Qt>
|
||||
|
||||
class QLayout;
|
||||
class QVBoxLayout;
|
||||
class GenericChatItemWidget;
|
||||
|
||||
class GenericChatItemLayout
|
||||
{
|
||||
public:
|
||||
|
||||
GenericChatItemLayout();
|
||||
~GenericChatItemLayout();
|
||||
|
||||
void addSortedWidget(GenericChatItemWidget* widget, int stretch = 0, Qt::Alignment alignment = 0);
|
||||
int indexOfSortedWidget(GenericChatItemWidget* widget) const;
|
||||
bool existsSortedWidget(GenericChatItemWidget* widget) const;
|
||||
void removeSortedWidget(GenericChatItemWidget* widget);
|
||||
void search(const QString &searchString, bool hideAll = false);
|
||||
|
||||
QLayout* getLayout() const;
|
||||
|
||||
private:
|
||||
int indexOfClosestSortedWidget(GenericChatItemWidget* widget) const;
|
||||
QVBoxLayout* layout;
|
||||
};
|
||||
|
||||
#endif // GENERICCHATITEMLAYOUT_H
|
54
src/widget/genericchatitemwidget.cpp
Normal file
54
src/widget/genericchatitemwidget.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "genericchatitemwidget.h"
|
||||
#include "src/widget/style.h"
|
||||
#include "src/persistence/settings.h"
|
||||
#include "src/widget/tool/croppinglabel.h"
|
||||
#include <QVariant>
|
||||
|
||||
GenericChatItemWidget::GenericChatItemWidget(QWidget *parent)
|
||||
: QFrame(parent)
|
||||
{
|
||||
setProperty("compact", Settings::getInstance().getCompactLayout());
|
||||
|
||||
nameLabel = new CroppingLabel(this);
|
||||
nameLabel->setObjectName("name");
|
||||
nameLabel->setTextFormat(Qt::PlainText);
|
||||
}
|
||||
|
||||
bool GenericChatItemWidget::isCompact() const
|
||||
{
|
||||
return compact;
|
||||
}
|
||||
|
||||
void GenericChatItemWidget::setCompact(bool compact)
|
||||
{
|
||||
this->compact = compact;
|
||||
}
|
||||
|
||||
QString GenericChatItemWidget::getName() const
|
||||
{
|
||||
return nameLabel->fullText();
|
||||
}
|
||||
|
||||
void GenericChatItemWidget::searchName(const QString &searchString, bool hide)
|
||||
{
|
||||
setVisible(!hide && getName().contains(searchString, Qt::CaseInsensitive));
|
||||
}
|
58
src/widget/genericchatitemwidget.h
Normal file
58
src/widget/genericchatitemwidget.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright © 2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
qTox is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
qTox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GENERICCHATITEMWIDGET_H
|
||||
#define GENERICCHATITEMWIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
|
||||
class CroppingLabel;
|
||||
|
||||
class GenericChatItemWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ItemType
|
||||
{
|
||||
GroupItem,
|
||||
FriendOfflineItem,
|
||||
FriendOnlineItem
|
||||
};
|
||||
|
||||
GenericChatItemWidget(QWidget *parent = 0);
|
||||
|
||||
bool isCompact() const;
|
||||
void setCompact(bool compact);
|
||||
|
||||
QString getName() const;
|
||||
|
||||
void searchName(const QString &searchString, bool hideAll);
|
||||
|
||||
Q_PROPERTY(bool compact READ isCompact WRITE setCompact)
|
||||
|
||||
protected:
|
||||
CroppingLabel* nameLabel;
|
||||
QLabel statusPic;
|
||||
|
||||
private:
|
||||
bool compact;
|
||||
};
|
||||
|
||||
#endif // GENERICCHATITEMWIDGET_H
|
|
@ -22,14 +22,14 @@
|
|||
#include "src/persistence/settings.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include "src/widget/tool/croppinglabel.h"
|
||||
#include <QBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
|
||||
GenericChatroomWidget::GenericChatroomWidget(QWidget *parent)
|
||||
: QFrame(parent), compact{Settings::getInstance().getCompactLayout()},
|
||||
active{false}
|
||||
: GenericChatItemWidget(parent), active{false}
|
||||
{
|
||||
// avatar
|
||||
if (compact)
|
||||
if (isCompact())
|
||||
avatar = new MaskablePixmapWidget(this, QSize(20,20), ":/img/avatar_mask.svg");
|
||||
else
|
||||
avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.svg");
|
||||
|
@ -39,49 +39,50 @@ GenericChatroomWidget::GenericChatroomWidget(QWidget *parent)
|
|||
statusMessageLabel->setTextFormat(Qt::PlainText);
|
||||
statusMessageLabel->setForegroundRole(QPalette::WindowText);
|
||||
|
||||
// name text
|
||||
nameLabel = new CroppingLabel(this);
|
||||
nameLabel->setTextFormat(Qt::PlainText);
|
||||
nameLabel->setForegroundRole(QPalette::WindowText);
|
||||
|
||||
setAutoFillBackground(true);
|
||||
reloadTheme();
|
||||
setCompact(compact);
|
||||
|
||||
compactChange(isCompact());
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::setCompact(bool _compact)
|
||||
bool GenericChatroomWidget::eventFilter(QObject *, QEvent *)
|
||||
{
|
||||
compact = _compact;
|
||||
return true; // Disable all events.
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::compactChange(bool _compact)
|
||||
{
|
||||
setCompact(_compact);
|
||||
|
||||
delete textLayout; // has to be first, deleted by layout
|
||||
delete layout;
|
||||
delete mainLayout;
|
||||
|
||||
compact = _compact;
|
||||
|
||||
layout = new QHBoxLayout;
|
||||
mainLayout = new QHBoxLayout;
|
||||
textLayout = new QVBoxLayout;
|
||||
|
||||
setLayout(layout);
|
||||
layout->setSpacing(0);
|
||||
layout->setMargin(0);
|
||||
setLayout(mainLayout);
|
||||
mainLayout->setSpacing(0);
|
||||
mainLayout->setMargin(0);
|
||||
textLayout->setSpacing(0);
|
||||
textLayout->setMargin(0);
|
||||
setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft
|
||||
|
||||
// avatar
|
||||
if (compact)
|
||||
if (isCompact())
|
||||
{
|
||||
setFixedHeight(25);
|
||||
avatar->setSize(QSize(20,20));
|
||||
layout->addSpacing(18);
|
||||
layout->addWidget(avatar);
|
||||
layout->addSpacing(5);
|
||||
layout->addWidget(nameLabel);
|
||||
layout->addWidget(statusMessageLabel);
|
||||
layout->addSpacing(5);
|
||||
layout->addWidget(&statusPic);
|
||||
layout->addSpacing(5);
|
||||
layout->activate();
|
||||
mainLayout->addSpacing(18);
|
||||
mainLayout->addWidget(avatar);
|
||||
mainLayout->addSpacing(5);
|
||||
mainLayout->addWidget(nameLabel);
|
||||
mainLayout->addWidget(statusMessageLabel);
|
||||
mainLayout->addSpacing(5);
|
||||
mainLayout->addWidget(&statusPic);
|
||||
mainLayout->addSpacing(5);
|
||||
mainLayout->activate();
|
||||
statusMessageLabel->setFont(Style::getFont(Style::Small));
|
||||
nameLabel->setFont(Style::getFont(Style::Medium));
|
||||
}
|
||||
|
@ -93,14 +94,14 @@ void GenericChatroomWidget::setCompact(bool _compact)
|
|||
textLayout->addWidget(nameLabel);
|
||||
textLayout->addWidget(statusMessageLabel);
|
||||
textLayout->addStretch();
|
||||
layout->addSpacing(20);
|
||||
layout->addWidget(avatar);
|
||||
layout->addSpacing(10);
|
||||
layout->addLayout(textLayout);
|
||||
layout->addSpacing(10);
|
||||
layout->addWidget(&statusPic);
|
||||
layout->addSpacing(10);
|
||||
layout->activate();
|
||||
mainLayout->addSpacing(20);
|
||||
mainLayout->addWidget(avatar);
|
||||
mainLayout->addSpacing(10);
|
||||
mainLayout->addLayout(textLayout);
|
||||
mainLayout->addSpacing(10);
|
||||
mainLayout->addWidget(&statusPic);
|
||||
mainLayout->addSpacing(10);
|
||||
mainLayout->activate();
|
||||
statusMessageLabel->setFont(Style::getFont(Style::Medium));
|
||||
nameLabel->setFont(Style::getFont(Style::Big));
|
||||
}
|
||||
|
@ -138,11 +139,6 @@ void GenericChatroomWidget::setStatusMsg(const QString &status)
|
|||
statusMessageLabel->setText(status);
|
||||
}
|
||||
|
||||
QString GenericChatroomWidget::getName() const
|
||||
{
|
||||
return nameLabel->fullText();
|
||||
}
|
||||
|
||||
QString GenericChatroomWidget::getStatusMsg() const
|
||||
{
|
||||
return statusMessageLabel->text();
|
||||
|
@ -169,17 +165,6 @@ void GenericChatroomWidget::reloadTheme()
|
|||
setPalette(p);
|
||||
}
|
||||
|
||||
bool GenericChatroomWidget::isCompact() const
|
||||
{
|
||||
return compact;
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
if (!active && event->button() == Qt::RightButton)
|
||||
setBackgroundRole(QPalette::Window);
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::mouseReleaseEvent(QMouseEvent*)
|
||||
{
|
||||
emit chatroomWidgetClicked(this);
|
||||
|
@ -191,8 +176,9 @@ void GenericChatroomWidget::enterEvent(QEvent*)
|
|||
setBackgroundRole(QPalette::Highlight);
|
||||
}
|
||||
|
||||
void GenericChatroomWidget::leaveEvent(QEvent*)
|
||||
void GenericChatroomWidget::leaveEvent(QEvent* event)
|
||||
{
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Window);
|
||||
QWidget::leaveEvent(event);
|
||||
}
|
||||
|
|
|
@ -20,64 +20,59 @@
|
|||
#ifndef GENERICCHATROOMWIDGET_H
|
||||
#define GENERICCHATROOMWIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include "genericchatitemwidget.h"
|
||||
|
||||
class CroppingLabel;
|
||||
class MaskablePixmapWidget;
|
||||
class QVBoxLayout;
|
||||
class QHBoxLayout;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class GenericChatroomWidget : public QFrame
|
||||
class GenericChatroomWidget : public GenericChatItemWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GenericChatroomWidget(QWidget *parent = 0);
|
||||
|
||||
virtual void setAsActiveChatroom(){;}
|
||||
virtual void setAsInactiveChatroom(){;}
|
||||
virtual void updateStatusLight(){;}
|
||||
virtual void setChatForm(Ui::MainWindow &){;}
|
||||
virtual void resetEventFlags(){;}
|
||||
virtual QString getStatusString(){return QString::null;}
|
||||
virtual void setAsActiveChatroom() = 0;
|
||||
virtual void setAsInactiveChatroom() = 0;
|
||||
virtual void updateStatusLight() = 0;
|
||||
virtual void setChatForm(Ui::MainWindow &) = 0;
|
||||
virtual void resetEventFlags() = 0;
|
||||
virtual QString getStatusString() = 0;
|
||||
|
||||
virtual bool eventFilter(QObject *, QEvent *) final override;
|
||||
|
||||
bool isActive();
|
||||
void setActive(bool active);
|
||||
|
||||
void setName(const QString& name);
|
||||
void setStatusMsg(const QString& status);
|
||||
|
||||
QString getName() const;
|
||||
QString getStatusMsg() const;
|
||||
|
||||
void reloadTheme();
|
||||
|
||||
bool isCompact() const;
|
||||
void reloadTheme();
|
||||
|
||||
public slots:
|
||||
void setCompact(bool compact);
|
||||
void compactChange(bool compact);
|
||||
|
||||
signals:
|
||||
void chatroomWidgetClicked(GenericChatroomWidget* widget);
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent* event) override;
|
||||
virtual void mouseReleaseEvent (QMouseEvent* event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
virtual void enterEvent(QEvent* e) override;
|
||||
virtual void leaveEvent(QEvent* e) override;
|
||||
|
||||
protected:
|
||||
QColor lastColor;
|
||||
QHBoxLayout* layout = nullptr;
|
||||
QHBoxLayout* mainLayout = nullptr;
|
||||
QVBoxLayout* textLayout = nullptr;
|
||||
MaskablePixmapWidget* avatar;
|
||||
QLabel statusPic;
|
||||
CroppingLabel* nameLabel, *statusMessageLabel;
|
||||
bool compact, active;
|
||||
CroppingLabel* statusMessageLabel;
|
||||
bool active;
|
||||
};
|
||||
|
||||
#endif // GENERICCHATROOMWIDGET_H
|
||||
|
|
|
@ -18,22 +18,18 @@
|
|||
*/
|
||||
|
||||
#include "groupwidget.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include "src/grouplist.h"
|
||||
#include "src/group.h"
|
||||
#include "src/persistence/settings.h"
|
||||
#include "form/groupchatform.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include "src/widget/style.h"
|
||||
#include "src/core/core.h"
|
||||
#include "tool/croppinglabel.h"
|
||||
#include <QPalette>
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMimeData>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QMimeData>
|
||||
|
||||
GroupWidget::GroupWidget(int GroupId, QString Name)
|
||||
: groupId{GroupId}
|
||||
|
@ -43,23 +39,39 @@ GroupWidget::GroupWidget(int GroupId, QString Name)
|
|||
statusPic.setMargin(3);
|
||||
nameLabel->setText(Name);
|
||||
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
if (g)
|
||||
statusMessageLabel->setText(GroupWidget::tr("%1 users in chat").arg(g->getPeersCount()));
|
||||
else
|
||||
statusMessageLabel->setText(GroupWidget::tr("0 users in chat"));
|
||||
onUserListChanged();
|
||||
|
||||
setAcceptDrops(true);
|
||||
|
||||
connect(nameLabel, &CroppingLabel::editFinished, [this](const QString &newName)
|
||||
{
|
||||
if (!newName.isEmpty())
|
||||
{
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
emit renameRequested(this, newName);
|
||||
emit g->getChatForm()->groupTitleChanged(groupId, newName.left(128));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
|
||||
void GroupWidget::contextMenuEvent(QContextMenuEvent* event)
|
||||
{
|
||||
QPoint pos = event->globalPos();
|
||||
QMenu menu;
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Highlight);
|
||||
|
||||
installEventFilter(this); // Disable leave event.
|
||||
|
||||
QMenu menu(this);
|
||||
QAction* setTitle = menu.addAction(tr("Set title..."));
|
||||
QAction* quitGroup = menu.addAction(tr("Quit group","Menu to quit a groupchat"));
|
||||
|
||||
QAction* selectedItem = menu.exec(pos);
|
||||
QAction* selectedItem = menu.exec(event->globalPos());
|
||||
|
||||
removeEventFilter(this);
|
||||
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Window);
|
||||
|
||||
if (selectedItem)
|
||||
{
|
||||
if (selectedItem == quitGroup)
|
||||
|
@ -68,21 +80,7 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
|
|||
}
|
||||
else if (selectedItem == setTitle)
|
||||
{
|
||||
bool ok;
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
|
||||
QString alias = QInputDialog::getText(nullptr, tr("Group title"), tr("You can also set this by clicking the chat form name.\nTitle:"), QLineEdit::Normal,
|
||||
nameLabel->fullText(), &ok);
|
||||
|
||||
if (ok && alias != nameLabel->fullText())
|
||||
emit g->getChatForm()->groupTitleChanged(groupId, alias.left(128));
|
||||
/* according to agilob:
|
||||
* “Moving mouse pointer over groupwidget results in CSS effect
|
||||
* mouse-over(?). Changing group title repaints only changed
|
||||
* element - title, the rest of the widget stays in the same CSS as it
|
||||
* was on mouse over. Repainting whole widget fixes style problem.”
|
||||
*/
|
||||
this->repaint();
|
||||
editName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +132,11 @@ QString GroupWidget::getStatusString()
|
|||
return "New Message";
|
||||
}
|
||||
|
||||
void GroupWidget::editName()
|
||||
{
|
||||
nameLabel->editBegin();
|
||||
}
|
||||
|
||||
void GroupWidget::setChatForm(Ui::MainWindow &ui)
|
||||
{
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
|
@ -151,6 +154,15 @@ void GroupWidget::dragEnterEvent(QDragEnterEvent *ev)
|
|||
{
|
||||
if (ev->mimeData()->hasFormat("friend"))
|
||||
ev->acceptProposedAction();
|
||||
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Highlight);
|
||||
}
|
||||
|
||||
void GroupWidget::dragLeaveEvent(QDragLeaveEvent *)
|
||||
{
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Window);
|
||||
}
|
||||
|
||||
void GroupWidget::dropEvent(QDropEvent *ev)
|
||||
|
@ -159,6 +171,9 @@ void GroupWidget::dropEvent(QDropEvent *ev)
|
|||
{
|
||||
int friendId = ev->mimeData()->data("friend").toInt();
|
||||
Core::getInstance()->groupInviteFriend(friendId, groupId);
|
||||
|
||||
if (!active)
|
||||
setBackgroundRole(QPalette::Window);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,15 +20,13 @@
|
|||
#ifndef GROUPWIDGET_H
|
||||
#define GROUPWIDGET_H
|
||||
|
||||
#include <QLabel>
|
||||
#include "genericchatroomwidget.h"
|
||||
|
||||
class GroupWidget : public GenericChatroomWidget
|
||||
class GroupWidget final : public GenericChatroomWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GroupWidget(int GroupId, QString Name);
|
||||
virtual void contextMenuEvent(QContextMenuEvent * event) final override;
|
||||
virtual void setAsInactiveChatroom() final override;
|
||||
virtual void setAsActiveChatroom() final override;
|
||||
virtual void updateStatusLight() final override;
|
||||
|
@ -37,15 +35,18 @@ public:
|
|||
virtual QString getStatusString() final override;
|
||||
void setName(const QString& name);
|
||||
void onUserListChanged();
|
||||
void editName();
|
||||
|
||||
signals:
|
||||
void groupWidgetClicked(GroupWidget* widget);
|
||||
void renameRequested(GroupWidget* widget, const QString& newName);
|
||||
void removeGroup(int groupId);
|
||||
|
||||
protected:
|
||||
// drag & drop
|
||||
virtual void dragEnterEvent(QDragEnterEvent* ev) final override;
|
||||
virtual void dropEvent(QDropEvent* ev) final override;
|
||||
virtual void contextMenuEvent(QContextMenuEvent * event) final override;
|
||||
virtual void dragEnterEvent(QDragEnterEvent* ev) override;
|
||||
virtual void dragLeaveEvent(QDragLeaveEvent* ev);
|
||||
virtual void dropEvent(QDropEvent* ev) override;
|
||||
|
||||
public:
|
||||
int groupId;
|
||||
|
|
|
@ -35,8 +35,13 @@ CroppingLabel::CroppingLabel(QWidget* parent)
|
|||
| Qt::ImhNoPredictiveText
|
||||
| Qt::ImhPreferLatin);
|
||||
|
||||
installEventFilter(this);
|
||||
textEdit->installEventFilter(this);
|
||||
connect(textEdit, &QLineEdit::editingFinished, this, &CroppingLabel::editingFinished);
|
||||
}
|
||||
|
||||
void CroppingLabel::editBegin()
|
||||
{
|
||||
showTextEdit();
|
||||
textEdit->selectAll();
|
||||
}
|
||||
|
||||
void CroppingLabel::setEditable(bool editable)
|
||||
|
@ -88,36 +93,14 @@ void CroppingLabel::mouseReleaseEvent(QMouseEvent *e)
|
|||
QLabel::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
bool CroppingLabel::eventFilter(QObject *obj, QEvent *e)
|
||||
void CroppingLabel::paintEvent(QPaintEvent* paintEvent)
|
||||
{
|
||||
// catch paint events if needed
|
||||
if (obj == this)
|
||||
if (blockPaintEvents)
|
||||
{
|
||||
if (e->type() == QEvent::Paint && blockPaintEvents)
|
||||
return true;
|
||||
paintEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
// events fired by the QLineEdit
|
||||
if (obj == textEdit)
|
||||
{
|
||||
if (!textEdit->isVisible())
|
||||
return false;
|
||||
|
||||
if (e->type() == QEvent::KeyPress)
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
|
||||
if (keyEvent->key() == Qt::Key_Return)
|
||||
hideTextEdit(true);
|
||||
|
||||
if (keyEvent->key() == Qt::Key_Escape)
|
||||
hideTextEdit(false);
|
||||
}
|
||||
|
||||
if (e->type() == QEvent::FocusOut)
|
||||
hideTextEdit(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
QLabel::paintEvent(paintEvent);
|
||||
}
|
||||
|
||||
void CroppingLabel::setElidedText()
|
||||
|
@ -131,28 +114,35 @@ void CroppingLabel::setElidedText()
|
|||
QLabel::setText(elidedText);
|
||||
}
|
||||
|
||||
void CroppingLabel::hideTextEdit(bool acceptText)
|
||||
{
|
||||
if (acceptText)
|
||||
{
|
||||
QString oldOrigText = origText;
|
||||
setText(textEdit->text()); // set before emitting so we don't override external reactions to signal
|
||||
emit textChanged(textEdit->text(), oldOrigText);
|
||||
}
|
||||
|
||||
textEdit->hide();
|
||||
blockPaintEvents = false;
|
||||
}
|
||||
|
||||
void CroppingLabel::showTextEdit()
|
||||
{
|
||||
blockPaintEvents = true;
|
||||
textEdit->show();
|
||||
textEdit->setFocus();
|
||||
textEdit->setText(origText);
|
||||
textEdit->setFocusPolicy(Qt::ClickFocus);
|
||||
}
|
||||
|
||||
QString CroppingLabel::fullText()
|
||||
{
|
||||
return origText;
|
||||
}
|
||||
|
||||
void CroppingLabel::minimizeMaximumWidth()
|
||||
{
|
||||
// This function chooses the smallest possible maximum width.
|
||||
// Text width + padding. Without padding, we'll have elipses.
|
||||
setMaximumWidth(fontMetrics().width(origText) + fontMetrics().width("..."));
|
||||
}
|
||||
|
||||
void CroppingLabel::editingFinished()
|
||||
{
|
||||
QString newText = textEdit->text().trimmed().remove(QRegExp("[\\t\\n\\v\\f\\r\\x0000]"));
|
||||
|
||||
if (origText != newText)
|
||||
emit editFinished(textEdit->text());
|
||||
|
||||
textEdit->hide();
|
||||
blockPaintEvents = false;
|
||||
emit editRemoved();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright © 2014 by The qTox Project
|
||||
Copyright © 2014-2015 by The qTox Project
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
|
@ -24,23 +24,30 @@
|
|||
|
||||
class QLineEdit;
|
||||
|
||||
class CroppingLabel final : public QLabel
|
||||
class CroppingLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CroppingLabel(QWidget *parent = 0);
|
||||
explicit CroppingLabel(QWidget* parent = 0);
|
||||
|
||||
void editBegin();
|
||||
void setEditable(bool editable);
|
||||
void setEdlideMode(Qt::TextElideMode elide);
|
||||
|
||||
void setText(const QString& text);
|
||||
QString fullText(); ///< Returns the un-cropped text
|
||||
|
||||
|
||||
public slots:
|
||||
void minimizeMaximumWidth();
|
||||
|
||||
signals:
|
||||
void textChanged(QString newText, QString oldText);
|
||||
void editFinished(const QString& newText);
|
||||
void editRemoved();
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* paintEvent) override;
|
||||
void setElidedText();
|
||||
void hideTextEdit(bool acceptText);
|
||||
void showTextEdit();
|
||||
|
@ -48,7 +55,9 @@ protected:
|
|||
virtual QSize sizeHint() const final override;
|
||||
virtual QSize minimumSizeHint() const final override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *e) final override;
|
||||
virtual bool eventFilter(QObject *obj, QEvent *e) final override;
|
||||
|
||||
private slots:
|
||||
void editingFinished();
|
||||
|
||||
private:
|
||||
QString origText;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "src/group.h"
|
||||
#include "groupwidget.h"
|
||||
#include "form/groupchatform.h"
|
||||
#include "circlewidget.h"
|
||||
#include "src/widget/style.h"
|
||||
#include "friendlistwidget.h"
|
||||
#include "form/chatform.h"
|
||||
|
@ -133,6 +134,45 @@ void Widget::init()
|
|||
ui->myProfile->insertWidget(0, profilePicture);
|
||||
ui->myProfile->insertSpacing(1, 7);
|
||||
|
||||
filterMenu = new QMenu(this);
|
||||
filterGroup = new QActionGroup(this);
|
||||
filterDisplayGroup = new QActionGroup(this);
|
||||
|
||||
filterDisplayName = new QAction(this);
|
||||
filterDisplayName->setCheckable(true);
|
||||
filterDisplayName->setChecked(true);
|
||||
filterDisplayGroup->addAction(filterDisplayName);
|
||||
filterMenu->addAction(filterDisplayName);
|
||||
filterDisplayActivity = new QAction(this);
|
||||
filterDisplayActivity->setCheckable(true);
|
||||
filterDisplayGroup->addAction(filterDisplayActivity);
|
||||
filterMenu->addAction(filterDisplayActivity);
|
||||
filterMenu->addSeparator();
|
||||
|
||||
filterAllAction = new QAction(this);
|
||||
filterAllAction->setCheckable(true);
|
||||
filterAllAction->setChecked(true);
|
||||
filterGroup->addAction(filterAllAction);
|
||||
filterMenu->addAction(filterAllAction);
|
||||
filterOnlineAction = new QAction(this);
|
||||
filterOnlineAction->setCheckable(true);
|
||||
filterGroup->addAction(filterOnlineAction);
|
||||
filterMenu->addAction(filterOnlineAction);
|
||||
filterOfflineAction = new QAction(this);
|
||||
filterOfflineAction->setCheckable(true);
|
||||
filterGroup->addAction(filterOfflineAction);
|
||||
filterMenu->addAction(filterOfflineAction);
|
||||
filterFriendsAction = new QAction(this);
|
||||
filterFriendsAction->setCheckable(true);
|
||||
filterGroup->addAction(filterFriendsAction);
|
||||
filterMenu->addAction(filterFriendsAction);
|
||||
filterGroupsAction = new QAction(this);
|
||||
filterGroupsAction->setCheckable(true);
|
||||
filterGroup->addAction(filterGroupsAction);
|
||||
filterMenu->addAction(filterGroupsAction);
|
||||
|
||||
ui->searchContactFilterBox->setMenu(filterMenu);
|
||||
|
||||
ui->mainContent->setLayout(new QVBoxLayout());
|
||||
ui->mainHead->setLayout(new QVBoxLayout());
|
||||
ui->mainHead->layout()->setMargin(0);
|
||||
|
@ -152,9 +192,10 @@ void Widget::init()
|
|||
ui->statusPanel->setStyleSheet(Style::getStylesheet(":/ui/window/statusPanel.css"));
|
||||
#endif
|
||||
|
||||
contactListWidget = new FriendListWidget(0, Settings::getInstance().getGroupchatPosition());
|
||||
contactListWidget = new FriendListWidget(this, Settings::getInstance().getGroupchatPosition());
|
||||
ui->friendList->setWidget(contactListWidget);
|
||||
ui->friendList->setLayoutDirection(Qt::RightToLeft);
|
||||
ui->friendList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
ui->statusLabel->setEditable(true);
|
||||
|
||||
|
@ -195,7 +236,7 @@ void Widget::init()
|
|||
connect(ui->settingsButton, &QPushButton::clicked, this, &Widget::onSettingsClicked);
|
||||
connect(profilePicture, &MaskablePixmapWidget::clicked, this, &Widget::showProfile);
|
||||
connect(ui->nameLabel, &CroppingLabel::clicked, this, &Widget::showProfile);
|
||||
connect(ui->statusLabel, &CroppingLabel::textChanged, this, &Widget::onStatusMessageChanged);
|
||||
connect(ui->statusLabel, &CroppingLabel::editFinished, this, &Widget::onStatusMessageChanged);
|
||||
connect(ui->mainSplitter, &QSplitter::splitterMoved, this, &Widget::onSplitterMoved);
|
||||
connect(addFriendForm, &AddFriendForm::friendRequested, this, &Widget::friendRequested);
|
||||
connect(timer, &QTimer::timeout, this, &Widget::onUserAwayCheck);
|
||||
|
@ -203,7 +244,9 @@ void Widget::init()
|
|||
connect(timer, &QTimer::timeout, this, &Widget::onTryCreateTrayIcon);
|
||||
connect(offlineMsgTimer, &QTimer::timeout, this, &Widget::processOfflineMsgs);
|
||||
connect(ui->searchContactText, &QLineEdit::textChanged, this, &Widget::searchContacts);
|
||||
connect(ui->searchContactFilterCBox, &QComboBox::currentTextChanged, this, &Widget::searchContacts);
|
||||
connect(filterGroup, &QActionGroup::triggered, this, &Widget::searchContacts);
|
||||
connect(filterDisplayGroup, &QActionGroup::triggered, this, &Widget::changeDisplayMode);
|
||||
connect(ui->friendList, &QWidget::customContextMenuRequested, this, &Widget::friendListContextMenu);
|
||||
|
||||
// keyboard shortcuts
|
||||
new QShortcut(Qt::CTRL + Qt::Key_Q, this, SLOT(close()));
|
||||
|
@ -219,6 +262,7 @@ void Widget::init()
|
|||
ui->settingsButton->setCheckable(true);
|
||||
setActiveToolMenuButton(Widget::AddButton);
|
||||
|
||||
connect(settingsWidget, &SettingsWidget::compactToggled, contactListWidget, &FriendListWidget::onCompactChanged);
|
||||
connect(settingsWidget, &SettingsWidget::groupchatPositionToggled, contactListWidget, &FriendListWidget::onGroupchatPositionChanged);
|
||||
#if (AUTOUPDATE_ENABLED)
|
||||
if (Settings::getInstance().getCheckUpdates())
|
||||
|
@ -552,10 +596,9 @@ void Widget::setUsername(const QString& username)
|
|||
sanitizedNameMention = QRegExp("\\b" + QRegExp::escape(sanename) + "\\b", Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
void Widget::onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage)
|
||||
void Widget::onStatusMessageChanged(const QString& newStatusMessage)
|
||||
{
|
||||
ui->statusLabel->setText(oldStatusMessage); // restore old status message until Core tells us to set it
|
||||
ui->statusLabel->setToolTip(oldStatusMessage); // for overlength messsages
|
||||
// Keep old status message until Core tells us to set it.
|
||||
Nexus::getCore()->setStatusMessage(newStatusMessage);
|
||||
}
|
||||
|
||||
|
@ -575,11 +618,18 @@ void Widget::addFriend(int friendId, const QString &userId)
|
|||
{
|
||||
ToxId userToxId = ToxId(userId);
|
||||
Friend* newfriend = FriendList::addFriend(friendId, userToxId);
|
||||
contactListWidget->moveWidget(newfriend->getFriendWidget(),Status::Offline);
|
||||
|
||||
QDate activityDate = Settings::getInstance().getFriendActivity(newfriend->getToxId());
|
||||
QDate chatDate = newfriend->getChatForm()->getLatestDate();
|
||||
|
||||
if (chatDate > activityDate && chatDate.isValid())
|
||||
Settings::getInstance().setFriendActivity(newfriend->getToxId(), chatDate);
|
||||
|
||||
contactListWidget->addFriendWidget(newfriend->getFriendWidget(),Status::Offline,Settings::getInstance().getFriendCircleID(newfriend->getToxId()));
|
||||
|
||||
Core* core = Nexus::getCore();
|
||||
connect(newfriend, &Friend::displayedNameChanged, contactListWidget, &FriendListWidget::moveWidget);
|
||||
connect(settingsWidget, &SettingsWidget::compactToggled, newfriend->getFriendWidget(), &GenericChatroomWidget::setCompact);
|
||||
connect(newfriend, &Friend::displayedNameChanged, this, &Widget::onFriendDisplayChanged);
|
||||
connect(settingsWidget, &SettingsWidget::compactToggled, newfriend->getFriendWidget(), &GenericChatroomWidget::compactChange);
|
||||
connect(newfriend->getFriendWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
|
||||
connect(newfriend->getFriendWidget(), SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int)));
|
||||
connect(newfriend->getFriendWidget(), SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int)));
|
||||
|
@ -621,7 +671,9 @@ void Widget::addFriend(int friendId, const QString &userId)
|
|||
newfriend->getFriendWidget()->onAvatarChange(friendId, avatar);
|
||||
}
|
||||
|
||||
searchContacts();
|
||||
int filter = getFilterCriteria();
|
||||
newfriend->getFriendWidget()->search(ui->searchContactText->text(), filterOffline(filter));
|
||||
|
||||
}
|
||||
|
||||
void Widget::addFriendFailed(const QString&, const QString& errorInfo)
|
||||
|
@ -634,6 +686,12 @@ void Widget::addFriendFailed(const QString&, const QString& errorInfo)
|
|||
QMessageBox::critical(0,"Error",info);
|
||||
}
|
||||
|
||||
void Widget::onFriendshipChanged(int friendId)
|
||||
{
|
||||
Friend* who = FriendList::findFriend(friendId);
|
||||
updateFriendActivity(who);
|
||||
}
|
||||
|
||||
void Widget::onFriendStatusChanged(int friendId, Status status)
|
||||
{
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
|
@ -709,7 +767,20 @@ void Widget::onFriendUsernameChanged(int friendId, const QString& username)
|
|||
QString str = username; str.replace('\n', ' ');
|
||||
str.remove('\r'); str.remove(QChar((char)0)); // null terminator...
|
||||
f->setName(str);
|
||||
searchContacts();
|
||||
}
|
||||
|
||||
void Widget::onFriendDisplayChanged(FriendWidget *friendWidget, Status s)
|
||||
{
|
||||
contactListWidget->moveWidget(friendWidget, s);
|
||||
int filter = getFilterCriteria();
|
||||
switch (s)
|
||||
{
|
||||
case Status::Offline:
|
||||
friendWidget->searchName(ui->searchContactText->text(), filterOffline(filter));
|
||||
default:
|
||||
friendWidget->searchName(ui->searchContactText->text(), filterOnline(filter));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Widget::onChatroomWidgetClicked(GenericChatroomWidget *widget)
|
||||
|
@ -831,6 +902,16 @@ void Widget::onFriendRequestReceived(const QString& userId, const QString& messa
|
|||
emit friendRequestAccepted(userId);
|
||||
}
|
||||
|
||||
void Widget::updateFriendActivity(Friend *frnd)
|
||||
{
|
||||
QDate date = Settings::getInstance().getFriendActivity(frnd->getToxId());
|
||||
if (date != QDate::currentDate())
|
||||
{
|
||||
Settings::getInstance().setFriendActivity(frnd->getToxId(), QDate::currentDate());
|
||||
contactListWidget->moveWidget(frnd->getFriendWidget(), frnd->getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::removeFriend(Friend* f, bool fake)
|
||||
{
|
||||
if (!fake)
|
||||
|
@ -853,6 +934,8 @@ void Widget::removeFriend(Friend* f, bool fake)
|
|||
onAddClicked();
|
||||
}
|
||||
|
||||
contactListWidget->removeFriendWidget(f->getFriendWidget());
|
||||
|
||||
FriendList::removeFriend(f->getFriendID(), fake);
|
||||
Nexus::getCore()->removeFriend(f->getFriendID(), fake);
|
||||
|
||||
|
@ -983,10 +1066,13 @@ void Widget::onGroupTitleChanged(int groupnumber, const QString& author, const Q
|
|||
if (!g)
|
||||
return;
|
||||
|
||||
g->setName(title);
|
||||
if (!author.isEmpty())
|
||||
g->getChatForm()->addSystemInfoMessage(tr("%1 has set the title to %2").arg(author, title), ChatMessage::INFO, QDateTime::currentDateTime());
|
||||
searchContacts();
|
||||
|
||||
contactListWidget->renameGroupWidget(g->getGroupWidget(), title);
|
||||
g->setName(title);
|
||||
int filter = getFilterCriteria();
|
||||
g->getGroupWidget()->searchName(ui->searchContactText->text(), filterGroups(filter));
|
||||
}
|
||||
|
||||
void Widget::onGroupPeerAudioPlaying(int groupnumber, int peernumber)
|
||||
|
@ -1033,24 +1119,31 @@ Group *Widget::createGroup(int groupId)
|
|||
|
||||
QString groupName = QString("Groupchat #%1").arg(groupId);
|
||||
Group* newgroup = GroupList::addGroup(groupId, groupName, core->isGroupAvEnabled(groupId));
|
||||
QLayout* layout = contactListWidget->getGroupLayout();
|
||||
layout->addWidget(newgroup->getGroupWidget());
|
||||
|
||||
contactListWidget->addGroupWidget(newgroup->getGroupWidget());
|
||||
newgroup->getGroupWidget()->updateStatusLight();
|
||||
|
||||
connect(settingsWidget, &SettingsWidget::compactToggled, newgroup->getGroupWidget(), &GenericChatroomWidget::setCompact);
|
||||
connect(settingsWidget, &SettingsWidget::compactToggled, newgroup->getGroupWidget(), &GenericChatroomWidget::compactChange);
|
||||
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
|
||||
connect(newgroup->getGroupWidget(), SIGNAL(removeGroup(int)), this, SLOT(removeGroup(int)));
|
||||
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newgroup->getChatForm(), SLOT(focusInput()));
|
||||
connect(newgroup->getChatForm(), &GroupChatForm::sendMessage, core, &Core::sendGroupMessage);
|
||||
connect(newgroup->getChatForm(), &GroupChatForm::sendAction, core, &Core::sendGroupAction);
|
||||
connect(newgroup->getChatForm(), &GroupChatForm::groupTitleChanged, core, &Core::changeGroupTitle);
|
||||
searchContacts();
|
||||
|
||||
int filter = getFilterCriteria();
|
||||
newgroup->getGroupWidget()->searchName(ui->searchContactText->text(), filterGroups(filter));
|
||||
|
||||
return newgroup;
|
||||
}
|
||||
|
||||
void Widget::onEmptyGroupCreated(int groupId)
|
||||
{
|
||||
createGroup(groupId);
|
||||
Group* group = createGroup(groupId);
|
||||
|
||||
// Only rename group if groups are visible.
|
||||
if (Widget::getInstance()->groupsVisible())
|
||||
group->getGroupWidget()->editName();
|
||||
}
|
||||
|
||||
bool Widget::isFriendWidgetCurActiveWidget(const Friend* f) const
|
||||
|
@ -1254,21 +1347,45 @@ void Widget::onSplitterMoved(int pos, int index)
|
|||
saveSplitterGeometry();
|
||||
}
|
||||
|
||||
void Widget::cycleContacts(int offset)
|
||||
void Widget::cycleContacts(bool forward)
|
||||
{
|
||||
if (!activeChatroomWidget)
|
||||
return;
|
||||
contactListWidget->cycleContacts(activeChatroomWidget, forward);
|
||||
}
|
||||
|
||||
FriendListWidget* friendList = static_cast<FriendListWidget*>(ui->friendList->widget());
|
||||
QList<GenericChatroomWidget*> friends = friendList->getAllFriends();
|
||||
bool Widget::filterGroups(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case FilterCriteria::Offline:
|
||||
case FilterCriteria::Friends:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int activeIndex = friends.indexOf(activeChatroomWidget);
|
||||
int bounded = (activeIndex + offset) % friends.length();
|
||||
bool Widget::filterOffline(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case FilterCriteria::Online:
|
||||
case FilterCriteria::Groups:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(bounded < 0)
|
||||
bounded += friends.length();
|
||||
|
||||
emit friends[bounded]->chatroomWidgetClicked(friends[bounded]);
|
||||
bool Widget::filterOnline(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case FilterCriteria::Offline:
|
||||
case FilterCriteria::Groups:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::processOfflineMsgs()
|
||||
|
@ -1298,6 +1415,7 @@ void Widget::reloadTheme()
|
|||
ui->statusHead->setStyleSheet(statusPanelStyle);
|
||||
ui->friendList->setStyleSheet(Style::getStylesheet(":ui/friendList/friendList.css"));
|
||||
ui->statusButton->setStyleSheet(Style::getStylesheet(":ui/statusButton/statusButton.css"));
|
||||
contactListWidget->reDraw();
|
||||
|
||||
for (Friend* f : FriendList::getAllFriends())
|
||||
f->getFriendWidget()->reloadTheme();
|
||||
|
@ -1308,12 +1426,12 @@ void Widget::reloadTheme()
|
|||
|
||||
void Widget::nextContact()
|
||||
{
|
||||
cycleContacts(1);
|
||||
cycleContacts(true);
|
||||
}
|
||||
|
||||
void Widget::previousContact()
|
||||
{
|
||||
cycleContacts(-1);
|
||||
cycleContacts(false);
|
||||
}
|
||||
|
||||
QString Widget::getStatusIconPath(Status status)
|
||||
|
@ -1378,79 +1496,87 @@ Status Widget::getStatusFromString(QString status)
|
|||
void Widget::searchContacts()
|
||||
{
|
||||
QString searchString = ui->searchContactText->text();
|
||||
int filter = ui->searchContactFilterCBox->currentIndex();
|
||||
int filter = getFilterCriteria();
|
||||
|
||||
switch(filter)
|
||||
{
|
||||
case FilterCriteria::All:
|
||||
hideFriends(searchString, Status::Online);
|
||||
hideFriends(searchString, Status::Offline);
|
||||
contactListWidget->searchChatrooms(searchString, filterOnline(filter), filterOffline(filter), filterGroups(filter));
|
||||
|
||||
hideGroups(searchString);
|
||||
break;
|
||||
case FilterCriteria::Online:
|
||||
hideFriends(searchString, Status::Online);
|
||||
hideFriends(QString(), Status::Offline, true);
|
||||
|
||||
hideGroups(searchString);
|
||||
break;
|
||||
case FilterCriteria::Offline:
|
||||
hideFriends(QString(), Status::Online, true);
|
||||
hideFriends(searchString, Status::Offline);
|
||||
|
||||
hideGroups(QString(), true);
|
||||
break;
|
||||
case FilterCriteria::Friends:
|
||||
hideFriends(searchString, Status::Online);
|
||||
hideFriends(searchString, Status::Offline);
|
||||
|
||||
hideGroups(QString(), true);
|
||||
break;
|
||||
case FilterCriteria::Groups:
|
||||
hideFriends(QString(), Status::Online, true);
|
||||
hideFriends(QString(), Status::Offline, true);
|
||||
|
||||
hideGroups(searchString);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
updateFilterText();
|
||||
|
||||
contactListWidget->reDraw();
|
||||
}
|
||||
|
||||
void Widget::hideFriends(QString searchString, Status status, bool hideAll)
|
||||
void Widget::changeDisplayMode()
|
||||
{
|
||||
QVBoxLayout* friends = contactListWidget->getFriendLayout(status);
|
||||
int friendCount = friends->count(), index;
|
||||
filterDisplayGroup->setEnabled(false);
|
||||
|
||||
for (index = 0; index<friendCount; index++)
|
||||
{
|
||||
FriendWidget* friendWidget = static_cast<FriendWidget*>(friends->itemAt(index)->widget());
|
||||
QString friendName = friendWidget->getName();
|
||||
if (filterDisplayGroup->checkedAction() == filterDisplayActivity)
|
||||
contactListWidget->setMode(FriendListWidget::Activity);
|
||||
else if (filterDisplayGroup->checkedAction() == filterDisplayName)
|
||||
contactListWidget->setMode(FriendListWidget::Name);
|
||||
|
||||
if (!friendName.contains(searchString, Qt::CaseInsensitive) || hideAll)
|
||||
friendWidget->setVisible(false);
|
||||
else
|
||||
friendWidget->setVisible(true);
|
||||
}
|
||||
searchContacts();
|
||||
filterDisplayGroup->setEnabled(true);
|
||||
|
||||
updateFilterText();
|
||||
}
|
||||
|
||||
void Widget::hideGroups(QString searchString, bool hideAll)
|
||||
void Widget::updateFilterText()
|
||||
{
|
||||
QVBoxLayout* groups = contactListWidget->getGroupLayout();
|
||||
int groupCount = groups->count(), index;
|
||||
ui->searchContactFilterBox->setText(filterDisplayGroup->checkedAction()->text() + QStringLiteral(" | ") + filterGroup->checkedAction()->text());
|
||||
}
|
||||
|
||||
for (index = 0; index<groupCount; index++)
|
||||
int Widget::getFilterCriteria() const
|
||||
{
|
||||
QAction* checked = filterGroup->checkedAction();
|
||||
|
||||
if (checked == filterOnlineAction)
|
||||
return Online;
|
||||
else if (checked == filterOfflineAction)
|
||||
return Offline;
|
||||
else if (checked == filterFriendsAction)
|
||||
return Friends;
|
||||
else if (checked == filterGroupsAction)
|
||||
return Groups;
|
||||
|
||||
return All;
|
||||
}
|
||||
|
||||
void Widget::searchCircle(CircleWidget *circleWidget)
|
||||
{
|
||||
int filter = getFilterCriteria();
|
||||
circleWidget->search(ui->searchContactText->text(), true, filterOnline(filter), filterOffline(filter));
|
||||
}
|
||||
|
||||
void Widget::searchItem(GenericChatItemWidget *chatItem, GenericChatItemWidget::ItemType type)
|
||||
{
|
||||
bool hide;
|
||||
int filter = getFilterCriteria();
|
||||
switch (type)
|
||||
{
|
||||
GroupWidget* groupWidget = static_cast<GroupWidget*>(groups->itemAt(index)->widget());
|
||||
QString groupName = groupWidget->getName();
|
||||
|
||||
if (!groupName.contains(searchString, Qt::CaseInsensitive) || hideAll)
|
||||
groupWidget->setVisible(false);
|
||||
else
|
||||
groupWidget->setVisible(true);
|
||||
case GenericChatItemWidget::GroupItem:
|
||||
hide = filterGroups(filter);
|
||||
break;
|
||||
default:
|
||||
hide = true;
|
||||
}
|
||||
|
||||
chatItem->searchName(ui->searchContactText->text(), hide);
|
||||
}
|
||||
|
||||
bool Widget::groupsVisible() const
|
||||
{
|
||||
int filter = getFilterCriteria();
|
||||
return !filterGroups(filter);
|
||||
}
|
||||
|
||||
void Widget::friendListContextMenu(const QPoint &pos)
|
||||
{
|
||||
QMenu menu(this);
|
||||
QAction *addCircleAction = menu.addAction(tr("Add new circle..."));
|
||||
QAction *chosenAction = menu.exec(ui->friendList->mapToGlobal(pos));
|
||||
|
||||
if (chosenAction == addCircleAction)
|
||||
contactListWidget->addCircleWidget();
|
||||
}
|
||||
|
||||
void Widget::setActiveToolMenuButton(ActiveToolMenuButton newActiveButton)
|
||||
|
@ -1471,12 +1597,17 @@ void Widget::retranslateUi()
|
|||
ui->retranslateUi(this);
|
||||
ui->nameLabel->setText(name);
|
||||
ui->statusLabel->setText(status);
|
||||
ui->searchContactFilterCBox->clear();
|
||||
ui->searchContactFilterCBox->addItem(tr("All"));
|
||||
ui->searchContactFilterCBox->addItem(tr("Online"));
|
||||
ui->searchContactFilterCBox->addItem(tr("Offline"));
|
||||
ui->searchContactFilterCBox->addItem(tr("Friends"));
|
||||
ui->searchContactFilterCBox->addItem(tr("Groups"));
|
||||
|
||||
filterDisplayName->setText(tr("By Name"));
|
||||
filterDisplayActivity->setText(tr("By Activity"));
|
||||
filterAllAction->setText(tr("All"));
|
||||
filterOnlineAction->setText(tr("Online"));
|
||||
filterOfflineAction->setText(tr("Offline"));
|
||||
filterFriendsAction->setText(tr("Friends"));
|
||||
filterGroupsAction->setText(tr("Groups"));
|
||||
ui->searchContactText->setPlaceholderText(tr("Search Contacts"));
|
||||
updateFilterText();
|
||||
|
||||
ui->searchContactText->setPlaceholderText(tr("Search Contacts"));
|
||||
statusOnline->setText(tr("Online", "Button to set your status to 'Online'"));
|
||||
statusAway->setText(tr("Away", "Button to set your status to 'Away'"));
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <QSystemTrayIcon>
|
||||
#include <QFileInfo>
|
||||
#include "src/core/corestructs.h"
|
||||
#include "genericchatitemwidget.h"
|
||||
|
||||
#define PIXELS_TO_ACT 7
|
||||
|
||||
|
@ -32,6 +33,7 @@ class MainWindow;
|
|||
}
|
||||
|
||||
class GenericChatroomWidget;
|
||||
class FriendWidget;
|
||||
class Group;
|
||||
class Friend;
|
||||
class QSplitter;
|
||||
|
@ -47,6 +49,8 @@ class FilesForm;
|
|||
class ProfileForm;
|
||||
class SettingsWidget;
|
||||
class AddFriendForm;
|
||||
class CircleWidget;
|
||||
class QActionGroup;
|
||||
|
||||
class Widget final : public QMainWindow
|
||||
{
|
||||
|
@ -77,6 +81,10 @@ public:
|
|||
static QString getStatusTitle(Status status);
|
||||
static Status getStatusFromString(QString status);
|
||||
|
||||
void searchCircle(CircleWidget* circleWidget);
|
||||
void searchItem(GenericChatItemWidget* chatItem, GenericChatItemWidget::ItemType type);
|
||||
bool groupsVisible() const;
|
||||
|
||||
public slots:
|
||||
void onSettingsClicked();
|
||||
void setWindowTitle(const QString& title);
|
||||
|
@ -91,11 +99,14 @@ public slots:
|
|||
void setStatusMessage(const QString &statusMessage);
|
||||
void addFriend(int friendId, const QString& userId);
|
||||
void addFriendFailed(const QString& userId, const QString& errorInfo = QString());
|
||||
void onFriendshipChanged(int friendId);
|
||||
void onFriendStatusChanged(int friendId, Status status);
|
||||
void onFriendStatusMessageChanged(int friendId, const QString& message);
|
||||
void onFriendUsernameChanged(int friendId, const QString& username);
|
||||
void onFriendDisplayChanged(FriendWidget* friendWidget, Status s);
|
||||
void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
|
||||
void onFriendRequestReceived(const QString& userId, const QString& message);
|
||||
void updateFriendActivity(Friend* frnd);
|
||||
void onMessageSendResult(uint32_t friendId, const QString& message, int messageId);
|
||||
void onReceiptRecieved(int friendId, int receipt);
|
||||
void onEmptyGroupCreated(int groupId);
|
||||
|
@ -132,7 +143,7 @@ private slots:
|
|||
void onTransferClicked();
|
||||
void showProfile();
|
||||
void onUsernameChanged(const QString& newUsername, const QString& oldUsername);
|
||||
void onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage);
|
||||
void onStatusMessageChanged(const QString& newStatusMessage);
|
||||
void onChatroomWidgetClicked(GenericChatroomWidget *);
|
||||
void removeFriend(int friendId);
|
||||
void copyFriendIdToClipboard(int friendId);
|
||||
|
@ -147,9 +158,7 @@ private slots:
|
|||
void onSetShowSystemTray(bool newValue);
|
||||
void onSplitterMoved(int pos, int index);
|
||||
void processOfflineMsgs();
|
||||
void searchContacts();
|
||||
void hideFriends(QString searchString, Status status, bool hideAll = false);
|
||||
void hideGroups(QString searchString, bool hideAll = false);
|
||||
void friendListContextMenu(const QPoint &pos);
|
||||
|
||||
private:
|
||||
enum ActiveToolMenuButton {
|
||||
|
@ -177,16 +186,36 @@ private:
|
|||
void removeGroup(Group* g, bool fake = false);
|
||||
void saveWindowGeometry();
|
||||
void saveSplitterGeometry();
|
||||
void cycleContacts(int offset);
|
||||
void cycleContacts(bool forward);
|
||||
void searchContacts();
|
||||
void changeDisplayMode();
|
||||
void updateFilterText();
|
||||
int getFilterCriteria() const;
|
||||
static bool filterGroups(int index);
|
||||
static bool filterOnline(int index);
|
||||
static bool filterOffline(int index);
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
SystemTrayIcon *icon;
|
||||
QMenu *trayMenu;
|
||||
QAction *statusOnline,
|
||||
*statusAway,
|
||||
*statusBusy,
|
||||
*actionQuit;
|
||||
QAction *statusOnline;
|
||||
QAction *statusAway;
|
||||
QAction *statusBusy;
|
||||
QAction *actionQuit;
|
||||
|
||||
QMenu* filterMenu;
|
||||
|
||||
QActionGroup* filterGroup;
|
||||
QAction* filterAllAction;
|
||||
QAction* filterOnlineAction;
|
||||
QAction* filterOfflineAction;
|
||||
QAction* filterFriendsAction;
|
||||
QAction* filterGroupsAction;
|
||||
|
||||
QActionGroup* filterDisplayGroup;
|
||||
QAction* filterDisplayName;
|
||||
QAction* filterDisplayActivity;
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
QSplitter *centralLayout;
|
||||
|
|
|
@ -30,3 +30,35 @@ QScrollBar:sub-line:vertical {height: 0px;subcontrol-position: top;subcontrol-or
|
|||
QScrollBar:add-page:vertical, QScrollBar::sub-page:vertical {
|
||||
background: none;
|
||||
}
|
||||
|
||||
QWidget#circleWidgetContainer > QFrame#line
|
||||
{
|
||||
color: white;
|
||||
}
|
||||
|
||||
QWidget#circleWidgetContainer
|
||||
{
|
||||
background-color: @themeMedium;
|
||||
}
|
||||
|
||||
QWidget#circleWidgetContainer:hover
|
||||
{
|
||||
background-color: @themeLight;
|
||||
}
|
||||
|
||||
QWidget#circleWidgetContainer QLineEdit
|
||||
{
|
||||
background-color: @themeLight;
|
||||
}
|
||||
|
||||
QWidget#circleWidgetContainer > QLabel#status
|
||||
{
|
||||
font: @small;
|
||||
color: @lightGrey;
|
||||
}
|
||||
|
||||
QWidget#circleWidgetContainer > QLabel#name
|
||||
{
|
||||
font: @big;
|
||||
color: @white;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ QLineEdit
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QComboBox {
|
||||
QToolButton {
|
||||
background: none;
|
||||
background-color: @themeMedium;
|
||||
color: white;
|
||||
|
@ -15,14 +15,14 @@ QComboBox {
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QComboBox:on {
|
||||
QToolButton:pressed {
|
||||
background-color: @themeMediumDark;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
QComboBox:drop-down {
|
||||
border-style: none;
|
||||
border-radius: 4px;
|
||||
QToolButton::menu-indicator {
|
||||
image: none
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue
Block a user