diff --git a/qtox.pro b/qtox.pro index 7b895882a..e53035e15 100644 --- a/qtox.pro +++ b/qtox.pro @@ -496,7 +496,8 @@ SOURCES += \ src/widget/genericchatitemlayout.cpp \ src/widget/categorywidget.cpp \ src/widget/tool/removefrienddialog.cpp \ - src/widget/contentlayout.cpp + src/widget/contentlayout.cpp \ + src/widget/contentdialog.cpp HEADERS += \ src/audio/audio.h \ @@ -541,4 +542,5 @@ HEADERS += \ src/widget/genericchatitemlayout.h \ src/widget/categorywidget.h \ src/widget/contentlayout.h \ + src/widget/contentdialog.h \ src/widget/tool/removefrienddialog.h diff --git a/src/persistence/settings.cpp b/src/persistence/settings.cpp index 81651b17b..2fe9fe5eb 100644 --- a/src/persistence/settings.cpp +++ b/src/persistence/settings.cpp @@ -175,6 +175,7 @@ void Settings::loadGlobal() QStandardPaths::locate(QStandardPaths::HomeLocation, QString(), QStandardPaths::LocateDirectory) ).toString(); separateWindow = s.value("separateWindow", false).toBool(); + dontGroupWindows = s.value("dontGroupWindows", false).toBool(); groupchatPosition = s.value("groupchatPosition", true).toBool(); s.endGroup(); @@ -217,6 +218,8 @@ void Settings::loadGlobal() windowGeometry = s.value("windowGeometry", QByteArray()).toByteArray(); windowState = s.value("windowState", QByteArray()).toByteArray(); splitterState = s.value("splitterState", QByteArray()).toByteArray(); + dialogGeometry = s.value("dialogGeometry", QByteArray()).toByteArray(); + dialogSplitterState = s.value("dialogSplitterState", QByteArray()).toByteArray(); s.endGroup(); s.beginGroup("Audio"); @@ -377,6 +380,7 @@ void Settings::saveGlobal() s.setValue("groupAlwaysNotify", groupAlwaysNotify); s.setValue("fauxOfflineMessaging", fauxOfflineMessaging); s.setValue("separateWindow", separateWindow); + s.setValue("dontGroupWindows", dontGroupWindows); s.setValue("groupchatPosition", groupchatPosition); s.setValue("autoSaveEnabled", autoSaveEnabled); s.setValue("globalAutoAcceptDir", globalAutoAcceptDir); @@ -1092,6 +1096,30 @@ void Settings::setSplitterState(const QByteArray &value) splitterState = value; } +QByteArray Settings::getDialogGeometry() const +{ + QMutexLocker locker{&bigLock}; + return dialogGeometry; +} + +void Settings::setDialogGeometry(const QByteArray &value) +{ + QMutexLocker locker{&bigLock}; + dialogGeometry = value; +} + +QByteArray Settings::getDialogSplitterState() const +{ + QMutexLocker locker{&bigLock}; + return dialogSplitterState; +} + +void Settings::setDialogSplitterState(const QByteArray &value) +{ + QMutexLocker locker{&bigLock}; + dialogSplitterState = value; +} + bool Settings::isMinimizeOnCloseEnabled() const { QMutexLocker locker{&bigLock}; @@ -1338,6 +1366,18 @@ void Settings::setSeparateWindow(bool value) separateWindow = value; } +bool Settings::getDontGroupWindows() const +{ + QMutexLocker locker{&bigLock}; + return dontGroupWindows; +} + +void Settings::setDontGroupWindows(bool value) +{ + QMutexLocker locker{&bigLock}; + dontGroupWindows = value; +} + bool Settings::getGroupchatPosition() const { QMutexLocker locker{&bigLock}; diff --git a/src/persistence/settings.h b/src/persistence/settings.h index 88e6f18e0..3b6fdf11f 100644 --- a/src/persistence/settings.h +++ b/src/persistence/settings.h @@ -217,6 +217,12 @@ public: QByteArray getSplitterState() const; void setSplitterState(const QByteArray &value); + QByteArray getDialogGeometry() const; + void setDialogGeometry(const QByteArray& value); + + QByteArray getDialogSplitterState() const; + void setDialogSplitterState(const QByteArray &value); + QString getFriendAdress(const QString &publicKey) const; void updateFriendAdress(const QString &newAddr); @@ -240,6 +246,9 @@ public: bool getSeparateWindow() const; void setSeparateWindow(bool value); + bool getDontGroupWindows() const; + void setDontGroupWindows(bool value); + bool getGroupchatPosition() const; void setGroupchatPosition(bool value); @@ -299,6 +308,7 @@ private: bool compactLayout; bool groupchatPosition; bool separateWindow; + bool dontGroupWindows; bool enableIPv6; QString translation; bool makeToxPortable; @@ -338,6 +348,8 @@ private: QByteArray windowGeometry; QByteArray windowState; QByteArray splitterState; + QByteArray dialogGeometry; + QByteArray dialogSplitterState; QString style; bool showSystemTray; diff --git a/src/widget/contentdialog.cpp b/src/widget/contentdialog.cpp new file mode 100644 index 000000000..4ff894870 --- /dev/null +++ b/src/widget/contentdialog.cpp @@ -0,0 +1,174 @@ +/* + 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 . +*/ + +#include "contentdialog.h" +#include "contentlayout.h" +#include "friendwidget.h" +#include "style.h" +#include "tool/adjustingscrollarea.h" +#include "src/persistence/settings.h" +#include "src/friend.h" +#include "src/friendlist.h" +#include +#include + +ContentDialog* ContentDialog::currentDialog = nullptr; +QHash> ContentDialog::friendList; + +ContentDialog::ContentDialog(QWidget* parent) + : QDialog(parent, Qt::Window) + , activeChatroomWidget(nullptr) +{ + QVBoxLayout* boxLayout = new QVBoxLayout(this); + boxLayout->setMargin(0); + boxLayout->setSpacing(0); + + splitter = new QSplitter(this); + setStyleSheet("QSplitter{color: rgb(255, 255, 255);background-color: rgb(255, 255, 255);alternate-background-color: rgb(255, 255, 255);border-color: rgb(255, 255, 255);gridline-color: rgb(255, 255, 255);selection-color: rgb(255, 255, 255);selection-background-color: rgb(255, 255, 255);}QSplitter:handle{color: rgb(255, 255, 255);background-color: rgb(255, 255, 255);}"); + splitter->setHandleWidth(6); + + QWidget *friendWidget = new QWidget(); + friendWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + friendWidget->setAutoFillBackground(true); + + friendLayout = new QVBoxLayout(friendWidget); + friendLayout->setMargin(0); + friendLayout->setSpacing(0); + friendLayout->addStretch(); + + QScrollArea *friendScroll = new QScrollArea(this); + friendScroll->setMinimumWidth(220); + friendScroll->setFrameStyle(QFrame::NoFrame); + friendScroll->setLayoutDirection(Qt::RightToLeft); + friendScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + friendScroll->setStyleSheet(Style::getStylesheet(":/ui/friendList/friendList.css")); + friendScroll->setWidgetResizable(true); + friendScroll->setWidget(friendWidget); + + QWidget* contentWidget = new QWidget(this); + contentWidget->setAutoFillBackground(true); + contentLayout = new ContentLayout(contentWidget); + contentLayout->setMargin(0); + contentLayout->setSpacing(0); + + splitter->addWidget(friendScroll); + splitter->addWidget(contentWidget); + splitter->setStretchFactor(1, 1); + splitter->setCollapsible(1, false); + boxLayout->addWidget(splitter); + + setMinimumSize(775, 420); + setAttribute(Qt::WA_DeleteOnClose); + + //restore window state + restoreGeometry(Settings::getInstance().getDialogGeometry()); + splitter->restoreState(Settings::getInstance().getDialogSplitterState()); + + currentDialog = this; +} + +ContentDialog::~ContentDialog() +{ + if (currentDialog == this) + currentDialog = nullptr; + + auto friendIt = friendList.begin(); + + while (friendIt != friendList.end()) + { + if (std::get<0>(friendIt.value()) == this) + { + friendIt = friendList.erase(friendIt); + continue; + } + ++friendIt; + } +} + +void ContentDialog::addFriend(int friendId, QString id) +{ + FriendWidget* friendWidget = new FriendWidget(friendId, id); + friendLayout->insertWidget(friendLayout->count() - 1, friendWidget); + + onChatroomWidgetClicked(friendWidget); + + connect(friendWidget, &FriendWidget::chatroomWidgetClicked, this, &ContentDialog::onChatroomWidgetClicked); + + friendList.insert(friendId, std::make_tuple(this, friendWidget)); +} + +ContentDialog* ContentDialog::current() +{ + return currentDialog; +} + +bool ContentDialog::showChatroomWidget(int friendId) +{ + auto widgetIt = friendList.find(friendId); + if (widgetIt == friendList.end()) + return false; + + std::get<0>(widgetIt.value())->activateWindow(); + std::get<0>(widgetIt.value())->onChatroomWidgetClicked(std::get<1>(widgetIt.value())); + + return true; +} + +void ContentDialog::resizeEvent(QResizeEvent* event) +{ + Q_UNUSED(event); + saveDialogGeometry(); +} + +void ContentDialog::closeEvent(QCloseEvent* event) +{ + saveDialogGeometry(); + saveSplitterState(); + QWidget::closeEvent(event); +} + +void ContentDialog::onChatroomWidgetClicked(GenericChatroomWidget *widget) +{ + contentLayout->clear(); + + if (activeChatroomWidget != nullptr) + activeChatroomWidget->setAsInactiveChatroom(); + + activeChatroomWidget = widget; + + widget->setChatForm(contentLayout); + setWindowTitle(widget->getName()); + widget->setAsActiveChatroom(); + widget->resetEventFlags(); + widget->updateStatusLight(); + QString windowTitle = widget->getName(); + if (!widget->getStatusString().isNull()) + windowTitle += " (" + widget->getStatusString() + ")"; + setWindowTitle(windowTitle); +} + +void ContentDialog::saveDialogGeometry() +{ + Settings::getInstance().setDialogGeometry(saveGeometry()); +} + +void ContentDialog::saveSplitterState() +{ + Settings::getInstance().setDialogSplitterState(splitter->saveState()); +} diff --git a/src/widget/contentdialog.h b/src/widget/contentdialog.h new file mode 100644 index 000000000..44244d692 --- /dev/null +++ b/src/widget/contentdialog.h @@ -0,0 +1,62 @@ +/* + 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 . +*/ + +#ifndef CONTENTDIALOG_H +#define CONTENTDIALOG_H + +#include +#include + +template class QHash; + +class QSplitter; +class QVBoxLayout; +class ContentLayout; +class GenericChatroomWidget; + +class ContentDialog : public QDialog +{ + Q_OBJECT +public: + ContentDialog(QWidget* parent = 0); + ~ContentDialog(); + void addFriend(int friendId, QString id); + static ContentDialog* current(); + static bool showChatroomWidget(int friendId); + +protected: + void resizeEvent(QResizeEvent* event) override; + void closeEvent(QCloseEvent* event) override; + +private slots: + void onChatroomWidgetClicked(GenericChatroomWidget* widget); + +private: + void saveDialogGeometry(); + void saveSplitterState(); + + QSplitter* splitter; + QVBoxLayout* friendLayout; + ContentLayout* contentLayout; + GenericChatroomWidget* activeChatroomWidget; + static ContentDialog* currentDialog; + static QHash> friendList; +}; + +#endif // CONTENTDIALOG_H diff --git a/src/widget/form/settings/generalform.cpp b/src/widget/form/settings/generalform.cpp index d919f8555..7e552fd85 100644 --- a/src/widget/form/settings/generalform.cpp +++ b/src/widget/form/settings/generalform.cpp @@ -88,6 +88,8 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : bodyUI->cbFauxOfflineMessaging->setChecked(Settings::getInstance().getFauxOfflineMessaging()); bodyUI->cbCompactLayout->setChecked(Settings::getInstance().getCompactLayout()); bodyUI->cbSeparateWindow->setChecked(Settings::getInstance().getSeparateWindow()); + bodyUI->cbDontGroupWindows->setChecked(Settings::getInstance().getDontGroupWindows()); + bodyUI->cbDontGroupWindows->setEnabled(bodyUI->cbSeparateWindow->isChecked()); bodyUI->cbGroupchatPosition->setChecked(Settings::getInstance().getGroupchatPosition()); for (auto entry : SmileyPack::listSmileyPacks()) @@ -181,6 +183,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : connect(bodyUI->cbFauxOfflineMessaging, &QCheckBox::stateChanged, this, &GeneralForm::onFauxOfflineMessaging); connect(bodyUI->cbCompactLayout, &QCheckBox::stateChanged, this, &GeneralForm::onCompactLayout); connect(bodyUI->cbSeparateWindow, &QCheckBox::stateChanged, this, &GeneralForm::onSeparateWindowChanged); + connect(bodyUI->cbDontGroupWindows, &QCheckBox::stateChanged, this, &GeneralForm::onDontGroupWindowsChanged); connect(bodyUI->cbGroupchatPosition, &QCheckBox::stateChanged, this, &GeneralForm::onGroupchatPositionChanged); // prevent stealing mouse whell scroll @@ -434,10 +437,16 @@ void GeneralForm::onCompactLayout() void GeneralForm::onSeparateWindowChanged() { + bodyUI->cbDontGroupWindows->setEnabled(bodyUI->cbSeparateWindow->isChecked()); Settings::getInstance().setSeparateWindow(bodyUI->cbSeparateWindow->isChecked()); emit parent->separateWindowToggled(bodyUI->cbSeparateWindow->isChecked()); } +void GeneralForm::onDontGroupWindowsChanged() +{ + Settings::getInstance().setDontGroupWindows(bodyUI->cbDontGroupWindows->isChecked()); +} + void GeneralForm::onGroupchatPositionChanged() { Settings::getInstance().setGroupchatPosition(bodyUI->cbGroupchatPosition->isChecked()); diff --git a/src/widget/form/settings/generalform.h b/src/widget/form/settings/generalform.h index 31d24b8de..74fc7e19f 100644 --- a/src/widget/form/settings/generalform.h +++ b/src/widget/form/settings/generalform.h @@ -68,6 +68,7 @@ private slots: void onFauxOfflineMessaging(); void onCompactLayout(); void onSeparateWindowChanged(); + void onDontGroupWindowsChanged(); void onGroupchatPositionChanged(); void onThemeColorChanged(int); diff --git a/src/widget/form/settings/generalsettings.ui b/src/widget/form/settings/generalsettings.ui index 54446a19b..d99510599 100644 --- a/src/widget/form/settings/generalsettings.ui +++ b/src/widget/form/settings/generalsettings.ui @@ -377,7 +377,17 @@ instead of system taskbar. - Use separate windows for friend list. + Open chats in separate window. + + + + + + + false + + + Don't group chat wndows. diff --git a/src/widget/friendwidget.cpp b/src/widget/friendwidget.cpp index 0f10e96ab..fd298c72e 100644 --- a/src/widget/friendwidget.cpp +++ b/src/widget/friendwidget.cpp @@ -26,6 +26,7 @@ #include "src/core/core.h" #include "form/chatform.h" #include "maskablepixmapwidget.h" +#include "contentdialog.h" #include "src/widget/tool/croppinglabel.h" #include "src/widget/style.h" #include "src/persistence/settings.h" @@ -67,6 +68,9 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event) ToxId id = FriendList::findFriend(friendId)->getToxId(); QString dir = Settings::getInstance().getAutoAcceptDir(id); QMenu menu; + menu.addAction(tr("Open Chat")); + menu.addAction(tr("Open Chat in New Window")); + menu.addSeparator(); QMenu* inviteMenu = menu.addMenu(tr("Invite to group","Menu to invite a friend to a groupchat")); QMap groupActions; @@ -296,6 +300,11 @@ QString FriendWidget::getStatusString() return QString::null; } +Friend* FriendWidget::getFriend() const +{ + return FriendList::findFriend(friendId); +} + void FriendWidget::search(const QString &searchString, bool hide) { searchName(searchString, hide); @@ -307,7 +316,7 @@ void FriendWidget::search(const QString &searchString, bool hide) bool FriendWidget::chatFormIsSet() const { Friend* f = FriendList::findFriend(friendId); - return f->getChatForm()->isVisible(); + return f->getChatForm()->isVisible() || ContentDialog::showChatroomWidget(friendId); } void FriendWidget::setChatForm(ContentLayout* contentLayout) diff --git a/src/widget/friendwidget.h b/src/widget/friendwidget.h index e319a9ebf..2ef2b26d2 100644 --- a/src/widget/friendwidget.h +++ b/src/widget/friendwidget.h @@ -36,6 +36,7 @@ public: virtual void setChatForm(ContentLayout* contentLayout) override; virtual void resetEventFlags() override; virtual QString getStatusString() override; + virtual Friend* getFriend() const override; void search(const QString &searchString, bool hide = false); signals: diff --git a/src/widget/genericchatroomwidget.h b/src/widget/genericchatroomwidget.h index b74a0d9ac..73866c2c9 100644 --- a/src/widget/genericchatroomwidget.h +++ b/src/widget/genericchatroomwidget.h @@ -27,6 +27,7 @@ class MaskablePixmapWidget; class QVBoxLayout; class QHBoxLayout; class ContentLayout; +class Friend; class GenericChatroomWidget : public GenericChatItemWidget { @@ -41,6 +42,7 @@ public: virtual void setChatForm(ContentLayout* contentLayout) = 0; virtual void resetEventFlags() = 0; virtual QString getStatusString() = 0; + virtual Friend* getFriend() const{return nullptr;} virtual bool eventFilter(QObject *, QEvent *) final override; diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 4cf5a8de5..7320ae17f 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -22,6 +22,7 @@ #include "ui_mainwindow.h" #include "src/core/core.h" #include "src/persistence/settings.h" +#include "contentdialog.h" #include "src/friend.h" #include "src/friendlist.h" #include "tool/friendrequestdialog.h" @@ -534,6 +535,7 @@ void Widget::onSeparateWindowChanged(bool separate, bool clicked) if (!separate) { QWindowList windowList = QGuiApplication::topLevelWindows(); + for (QWindow* window : windowList) { qDebug() << window->objectName(); @@ -542,7 +544,6 @@ void Widget::onSeparateWindowChanged(bool separate, bool clicked) } QWidget* contentWidget = new QWidget(this); - contentWidget->setObjectName("yolo"); contentLayout = new ContentLayout(contentWidget); ui->mainSplitter->addWidget(contentWidget); @@ -553,6 +554,8 @@ void Widget::onSeparateWindowChanged(bool separate, bool clicked) { if (contentLayout != nullptr) { + contentLayout->clear(); + contentLayout->parentWidget()->setParent(0); // Remove from splitter. contentLayout->parentWidget()->hide(); contentLayout->parentWidget()->deleteLater(); contentLayout->deleteLater(); @@ -563,6 +566,8 @@ void Widget::onSeparateWindowChanged(bool separate, bool clicked) if (clicked) resize(ui->statusPanel->width(), height()); + + setWindowTitle(QString()); } if (clicked) @@ -571,9 +576,16 @@ void Widget::onSeparateWindowChanged(bool separate, bool clicked) void Widget::setWindowTitle(const QString& title) { - QString tmp = title; - /// <[^>]*> Regexp to remove HTML tags, in case someone used them in title - QMainWindow::setWindowTitle("qTox - " + tmp.remove(QRegExp("<[^>]*>"))); + if (title.isEmpty()) + { + QMainWindow::setWindowTitle(QApplication::applicationName()); + } + else + { + QString tmp = title; + /// <[^>]*> Regexp to remove HTML tags, in case someone used them in title + QMainWindow::setWindowTitle(QApplication::applicationName() + QStringLiteral(" - ") + tmp.remove(QRegExp("<[^>]*>"))); + } } void Widget::forceShow() @@ -941,12 +953,27 @@ void Widget::onChatroomWidgetClicked(GenericChatroomWidget *widget) if (Settings::getInstance().getSeparateWindow()) { if (!widget->chatFormIsSet()) - widget->setChatForm(createContentDialog(widget->getName())); + { + ContentDialog* dialog = nullptr; + + if (!Settings::getInstance().getDontGroupWindows()) + dialog = ContentDialog::current(); + + if (dialog == nullptr) + dialog = new ContentDialog(); + + dialog->show(); + Friend* frnd = widget->getFriend(); + if (frnd != nullptr) + { + dialog->addFriend(frnd->getFriendID(), frnd->getDisplayedName()); + } + } } else { + hideMainForms(widget); widget->setChatForm(contentLayout); - setWindowTitle(widget->getName()); widget->setAsActiveChatroom(); widget->resetEventFlags(); widget->updateStatusLight(); @@ -1799,7 +1826,7 @@ void Widget::retranslateUi() statusOnline->setText(tr("Online", "Button to set your status to 'Online'")); statusAway->setText(tr("Away", "Button to set your status to 'Away'")); statusBusy->setText(tr("Busy", "Button to set your status to 'Busy'")); - setWindowTitle(tr("Settings")); + //setWindowTitle(tr("Settings")); } #ifdef Q_OS_MAC