diff --git a/src/core.cpp b/src/core.cpp index 29265c27c..27d0afc61 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -40,11 +40,12 @@ #include const QString Core::CONFIG_FILE_NAME = "data"; +const QString Core::TOX_EXT = ".tox"; QList Core::fileSendQueue; QList Core::fileRecvQueue; -Core::Core(Camera* cam, QThread *coreThread) : - tox(nullptr), camera(cam) +Core::Core(Camera* cam, QThread *coreThread, QString loadPath) : + tox(nullptr), camera(cam), loadPath(loadPath) { videobuf = new uint8_t[videobufsize]; videoBusyness=0; @@ -124,12 +125,11 @@ Core* Core::getInstance() return Widget::getInstance()->getCore(); } -void Core::start() +void Core::make_tox() { // IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be disabled in options. bool enableIPv6 = Settings::getInstance().getEnableIPv6(); bool forceTCP = Settings::getInstance().getForceTCP(); - bool useProxy = Settings::getInstance().getUseProxy(); if (enableIPv6) @@ -188,7 +188,7 @@ void Core::start() emit failedToStart(); } return; - } + } else qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery may not work properly."; } @@ -212,15 +212,24 @@ void Core::start() emit failedToStart(); return; } +} + +void Core::start() +{ + make_tox(); qsrand(time(nullptr)); - if (!loadConfiguration()) + if (loadPath != "") { - emit failedToStart(); - tox_kill(tox); - tox = nullptr; - return; + if (!loadConfiguration(loadPath)) // loadPath is meaningless after this + { + emit failedToStart(); + tox_kill(tox); + tox = nullptr; + return; + } + loadPath = ""; } tox_callback_friend_request(tox, onFriendRequest, this); @@ -280,9 +289,9 @@ void Core::start() * 5 disconnected; 4 were DCd for less than 20 ticks, while the 5th was ~50 ticks. * So I set the tolerance here at 25, and initial DCs should be very rare now. * This should be able to go to 50 or 100 without affecting legitimate disconnects' - * downtime, but lets be conservative for now. Edit: now 40. + * downtime, but lets be conservative for now. Edit: now ~~40~~ 30. */ -#define CORE_DISCONNECT_TOLERANCE 40 +#define CORE_DISCONNECT_TOLERANCE 30 void Core::process() { @@ -634,8 +643,8 @@ void Core::onAvatarInfoCallback(Tox*, int32_t friendnumber, uint8_t format, { qDebug() << "Core: Got null avatar info from" << core->getFriendUsername(friendnumber); emit core->friendAvatarRemoved(friendnumber); - QFile::remove(QDir(Settings::getInstance().getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".png")); - QFile::remove(QDir(Settings::getInstance().getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".hash")); + QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".png")); + QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".hash")); } else { @@ -925,6 +934,8 @@ void Core::acceptFileRecvRequest(int friendId, int fileNum, QString path) void Core::removeFriend(int friendId) { + if (!tox) + return; if (tox_del_friend(tox, friendId) == -1) { emit failedToRemoveFriend(friendId); } else { @@ -935,6 +946,8 @@ void Core::removeFriend(int friendId) void Core::removeGroup(int groupId) { + if (!tox) + return; tox_del_groupchat(tox, groupId); } @@ -956,8 +969,8 @@ void Core::setUsername(const QString& username) if (tox_set_name(tox, cUsername.data(), cUsername.size()) == -1) { emit failedToSetUsername(username); } else { - saveConfiguration(); emit usernameSet(username); + saveConfiguration(); } } @@ -988,6 +1001,13 @@ ToxID Core::getSelfId() return ToxID::fromString(CFriendAddress::toString(friendAddress)); } +QString Core::getIDString() +{ + return getSelfId().toString().left(12); + // 12 is the smallest multiple of four such that + // 16^n > 10^10 (which is roughly the planet's population) +} + QString Core::getStatusMessage() { int size = tox_get_self_status_message_size(tox); @@ -1045,11 +1065,25 @@ void Core::onFileTransferFinished(ToxFile file) emit fileDownloadFinished(file.filePath); } -bool Core::loadConfiguration() +QString Core::sanitize(QString name) { - QString path = QDir(Settings::getSettingsDirPath()).filePath(CONFIG_FILE_NAME); + // these are pretty much Windows banned filename characters + QList banned = {'/', '\\', ':', '<', '>', '"', '|', '?', '*'}; + for (QChar c : banned) + name.replace(c, '_'); + // also remove leading and trailing periods + if (name[0] == '.') + name[0] = '_'; + if (name.endsWith('.')) + name[name.length()-1] = '_'; + return name; +} +bool Core::loadConfiguration(QString path) +{ + // setting the profile is now the responsibility of the caller QFile configurationFile(path); + qDebug() << "Core::loadConfiguration: reading from " << path; if (!configurationFile.exists()) { qWarning() << "The Tox configuration file was not found"; @@ -1111,31 +1145,51 @@ bool Core::loadConfiguration() void Core::saveConfiguration() { - Settings::getInstance().save(); + QString dir = Settings::getSettingsDirPath(); + QDir directory(dir); + if (!directory.exists() && !directory.mkpath(directory.absolutePath())) { + qCritical() << "Error while creating directory " << dir; + return; + } + QString profile = Settings::getInstance().getCurrentProfile(); + //qDebug() << "saveConf read profile: " << profile; + if (profile == "") + { // no profile active; this should only happen on startup, if at all + profile = sanitize(getUsername()); + if (profile == "") // happens on creation of a new Tox ID + profile = getIDString(); + //qDebug() << "saveConf: read sanitized user as " << profile; + Settings::getInstance().setCurrentProfile(profile); + } + + QString path = dir + QDir::separator() + profile + TOX_EXT; + QFileInfo info(path); +// if (!info.exists()) // fall back to old school 'data' +// { //path = dir + QDir::separator() + CONFIG_FILE_NAME; +// qDebug() << "Core:" << path << " does not exist"; +// } + + saveConfiguration(path); +} + +void Core::saveConfiguration(const QString& path) +{ if (!tox) { qWarning() << "Core::saveConfiguration: Tox not started, aborting!"; return; } - QString path = Settings::getSettingsDirPath(); + Settings::getInstance().save(); - QDir directory(path); - - if (!directory.exists() && !directory.mkpath(directory.absolutePath())) { - qCritical() << "Error while creating directory " << path; - return; - } - - path = directory.filePath(CONFIG_FILE_NAME); QSaveFile configurationFile(path); if (!configurationFile.open(QIODevice::WriteOnly)) { qCritical() << "File " << path << " cannot be opened"; return; } - qDebug() << "Core: Saving"; + qDebug() << "Core: writing tox_save to " << path; uint32_t fileSize; if (Settings::getInstance().getEncryptTox()) @@ -1164,6 +1218,27 @@ void Core::saveConfiguration() } } +void Core::switchConfiguration(QString profile) +{ + saveConfiguration(); + + toxTimer->stop(); + + if (tox) { + toxav_kill(toxav); + toxav = nullptr; + tox_kill(tox); + tox = nullptr; + } + emit selfAvatarChanged(QPixmap(":/img/contact_dark.png")); + Widget::getInstance()->clearContactsList(); // we need this to block, so no signals for us + + loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT); + Settings::getInstance().setCurrentProfile(profile); + + start(); +} + void Core::loadFriends() { const uint32_t friendCount = tox_count_friendlist(tox); diff --git a/src/core.h b/src/core.h index 41a95d626..2e928c9a4 100644 --- a/src/core.h +++ b/src/core.h @@ -34,9 +34,13 @@ class Core : public QObject { Q_OBJECT public: - explicit Core(Camera* cam, QThread* coreThread); + explicit Core(Camera* cam, QThread* coreThread, QString initialLoadPath); static Core* getInstance(); ///< Returns the global widget's Core instance ~Core(); + + static const QString TOX_EXT; + static const QString CONFIG_FILE_NAME; + static QString sanitize(QString name); int getGroupNumberPeers(int groupId) const; QString getGroupPeerName(int groupId, int peerId) const; @@ -48,6 +52,10 @@ public: void dispatchVideoFrame(vpx_image img) const; void saveConfiguration(); + void saveConfiguration(const QString& path); + void switchConfiguration(QString profile); + + QString getIDString(); QString getUsername(); QString getStatusMessage(); @@ -216,7 +224,8 @@ private: bool checkConnection(); - bool loadConfiguration(); // Returns false for a critical error, true otherwise + bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise + void make_tox(); void loadFriends(); static void sendAllFileData(Core* core, ToxFile* file); @@ -232,15 +241,15 @@ private slots: private: Tox* tox; ToxAv* toxav; - QTimer *toxTimer, *fileTimer, *bootstrapTimer; //, *saveTimer; + QTimer *toxTimer, *fileTimer; //, *saveTimer; Camera* camera; + QString loadPath; // meaningless after start() is called QList dhtServerList; int dhtServerId; static QList fileSendQueue, fileRecvQueue; static ToxCall calls[]; uint8_t* pwhash = nullptr; // use the pw's hash as the "pw" - static const QString CONFIG_FILE_NAME; static const int videobufsize; static uint8_t* videobuf; static int videoBusyness; // Used to know when to drop frames diff --git a/src/filetransferinstance.cpp b/src/filetransferinstance.cpp index f3301e58a..158ddd65c 100644 --- a/src/filetransferinstance.cpp +++ b/src/filetransferinstance.cpp @@ -24,7 +24,7 @@ #include #include -#define CONTENT_WIDTH 250 +#define MAX_CONTENT_WIDTH 250 #define MAX_PREVIEW_SIZE 25*1024*1024 uint FileTransferInstance::Idconter = 0; @@ -43,9 +43,10 @@ FileTransferInstance::FileTransferInstance(ToxFile File) // update this whenever you change the font in innerStyle.css QFontMetrics fm(Style::getFont(Style::Small)); - filenameElided = fm.elidedText(filename, Qt::ElideRight, CONTENT_WIDTH); - + filenameElided = fm.elidedText(filename, Qt::ElideRight, MAX_CONTENT_WIDTH); size = getHumanReadableSize(File.filesize); + contentPrefWidth = std::max(fm.width(filenameElided), fm.width(size)); + speed = "0B/s"; eta = "00:00"; @@ -57,7 +58,7 @@ FileTransferInstance::FileTransferInstance(ToxFile File) File.file->seek(0); if (preview.loadFromData(File.file->readAll())) { - pic = preview.scaledToHeight(50); + pic = preview.scaled(100, 50, Qt::KeepAspectRatio, Qt::SmoothTransformation); } } File.file->seek(0); @@ -127,7 +128,7 @@ void FileTransferInstance::onFileTransferFinished(ToxFile File) { if (preview.loadFromData(previewFile.readAll())) { - pic = preview.scaledToHeight(50); + pic = preview.scaled(100, 50, Qt::KeepAspectRatio, Qt::SmoothTransformation); } previewFile.close(); } @@ -377,7 +378,8 @@ QString FileTransferInstance::draw2ButtonsForm(const QString &type, const QImage QString imgBstr = ""; QString content; - QString progrBar = ""; + QString progrBar = ""; content = "

" + filenameElided + "

"; content += ""; @@ -421,11 +423,14 @@ QString FileTransferInstance::wrapIntoForm(const QString& content, const QString res += "
" + imgLeftA + "
" + imgLeftB + "
\n"; res += "\n"; res += insertMiniature(type); - res += "\n"; + res += "\n"; res += "\n"; diff --git a/src/filetransferinstance.h b/src/filetransferinstance.h index f32c10f92..a4f6524ee 100644 --- a/src/filetransferinstance.h +++ b/src/filetransferinstance.h @@ -78,6 +78,7 @@ private: long long lastBytesSent, totalBytes; int fileNum; int friendId; + int contentPrefWidth; QString savePath; ToxFile::FileDirection direction; QString stopFileButtonStylesheet, pauseFileButtonStylesheet, acceptFileButtonStylesheet; diff --git a/src/misc/settings.cpp b/src/misc/settings.cpp index 5407565ba..84c4a2003 100644 --- a/src/misc/settings.cpp +++ b/src/misc/settings.cpp @@ -115,6 +115,7 @@ void Settings::load() useProxy = s.value("useProxy", false).toBool(); proxyAddr = s.value("proxyAddr", "").toString(); proxyPort = s.value("proxyPort", 0).toInt(); + currentProfile = s.value("currentProfile", "").toString(); s.endGroup(); s.beginGroup("Widgets"); @@ -219,6 +220,7 @@ void Settings::save(QString path) s.setValue("forceTCP", forceTCP); s.setValue("proxyAddr", proxyAddr); s.setValue("proxyPort", proxyPort); + s.setValue("currentProfile", currentProfile); s.endGroup(); s.beginGroup("Widgets"); @@ -410,6 +412,16 @@ void Settings::setProxyPort(int newValue) proxyPort = newValue; } +QString Settings::getCurrentProfile() const +{ + return currentProfile; +} + +void Settings::setCurrentProfile(QString profile) +{ + currentProfile = profile; +} + bool Settings::getEnableLogging() const { return enableLogging; diff --git a/src/misc/settings.h b/src/misc/settings.h index 0c45153c5..1552f62ba 100644 --- a/src/misc/settings.h +++ b/src/misc/settings.h @@ -52,6 +52,9 @@ public: bool getAutostartInTray() const; void setAutostartInTray(bool newValue); + QString getCurrentProfile() const; + void setCurrentProfile(QString profile); + bool getUseTranslations() const; void setUseTranslations(bool newValue); @@ -179,6 +182,8 @@ private: QString proxyAddr; int proxyPort; + QString currentProfile; + bool enableLogging; bool encryptLogs; bool encryptTox; diff --git a/src/widget/form/settings/identityform.cpp b/src/widget/form/settings/identityform.cpp index b8369945e..c0853f958 100644 --- a/src/widget/form/settings/identityform.cpp +++ b/src/widget/form/settings/identityform.cpp @@ -18,11 +18,16 @@ #include "ui_identitysettings.h" #include "identityform.h" #include "src/widget/form/settingswidget.h" +#include "src/misc/settings.h" #include "src/widget/croppinglabel.h" +#include "src/widget/widget.h" #include #include #include #include +#include +#include +#include IdentityForm::IdentityForm() : GenericForm(tr("Your identity"), QPixmap(":/img/settings/identity.png")) @@ -38,7 +43,7 @@ IdentityForm::IdentityForm() : // toxId->setTextInteractionFlags(Qt::TextSelectableByMouse); toxId->setReadOnly(true); -// toxId->setFrameStyle(QFrame::NoFrame); + toxId->setFrame(false); // toxId->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // toxId->setFixedHeight(toxId->document()->size().height()*2); toxId->setFont(small); @@ -49,6 +54,11 @@ IdentityForm::IdentityForm() : connect(toxId, SIGNAL(clicked()), this, SLOT(copyIdClicked())); connect(bodyUI->userName, SIGNAL(editingFinished()), this, SLOT(onUserNameEdited())); connect(bodyUI->statusMessage, SIGNAL(editingFinished()), this, SLOT(onStatusMessageEdited())); + connect(bodyUI->loadButton, &QPushButton::clicked, this, &IdentityForm::onLoadClicked); + connect(bodyUI->renameButton, &QPushButton::clicked, this, &IdentityForm::onRenameClicked); + connect(bodyUI->exportButton, &QPushButton::clicked, this, &IdentityForm::onExportClicked); + connect(bodyUI->deleteButton, &QPushButton::clicked, this, &IdentityForm::onDeleteClicked); + connect(bodyUI->importButton, &QPushButton::clicked, this, &IdentityForm::onImportClicked); } IdentityForm::~IdentityForm() @@ -61,6 +71,7 @@ void IdentityForm::copyIdClicked() QString txt = toxId->text(); txt.replace('\n',""); QApplication::clipboard()->setText(txt); + toxId->setCursorPosition(0); } void IdentityForm::onUserNameEdited() @@ -76,6 +87,13 @@ void IdentityForm::onStatusMessageEdited() void IdentityForm::present() { toxId->setText(Core::getInstance()->getSelfId().toString()); + toxId->setCursorPosition(0); + bodyUI->profiles->clear(); + for (QString profile : Widget::searchProfiles()) + bodyUI->profiles->addItem(profile); + QString current = Settings::getInstance().getCurrentProfile(); + if (current != "") + bodyUI->profiles->setCurrentText(current); } void IdentityForm::setUserName(const QString &name) @@ -87,3 +105,62 @@ void IdentityForm::setStatusMessage(const QString &msg) { bodyUI->statusMessage->setText(msg); } + +void IdentityForm::onLoadClicked() +{ + Core::getInstance()->switchConfiguration(bodyUI->profiles->currentText()); +} + +void IdentityForm::onRenameClicked() +{ + QString cur = bodyUI->profiles->currentText(); + QString title = tr("Rename \"%1\"", "renaming a profile").arg(cur); + QString name = QInputDialog::getText(this, title, title+":"); + if (name != "") + { + name = Core::sanitize(name); + QDir dir(Settings::getSettingsDirPath()); + QFile::rename(dir.filePath(cur+Core::TOX_EXT), dir.filePath(name+Core::TOX_EXT)); + bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name); + Settings::getInstance().setCurrentProfile(name); + } +} + +void IdentityForm::onExportClicked() +{ + QString current = bodyUI->profiles->currentText() + Core::TOX_EXT; + QString path = QFileDialog::getSaveFileName(this, tr("Export profile", "save dialog title"), + QDir::home().filePath(current), + tr("Tox save file (*.tox)", "save dialog filter")); + QFile::copy(QDir(Settings::getSettingsDirPath()).filePath(current), path); +} + +void IdentityForm::onDeleteClicked() +{ + if (Settings::getInstance().getCurrentProfile() == bodyUI->profiles->currentText()) + { + QMessageBox::warning(this, tr("Profile currently loaded","current profile deletion warning title"), tr("This profile is currently in use. Please load a different profile before deleting this one.","current profile deletion warning text")); + } + else + { + QMessageBox::StandardButton resp = QMessageBox::question(this, + tr("Deletion imminent!","deletion confirmation title"), tr("Are you sure you want to delete this profile?","deletion confirmation text"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (resp == QMessageBox::Yes) + { + QFile::remove(QDir(Settings::getSettingsDirPath()).filePath(bodyUI->profiles->currentText()+Core::TOX_EXT)); + bodyUI->profiles->removeItem(bodyUI->profiles->currentIndex()); + bodyUI->profiles->setCurrentText(Settings::getInstance().getCurrentProfile()); + } + } +} + +void IdentityForm::onImportClicked() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Import profile", "import dialog title"), QDir::homePath(), tr("Tox save file (*.tox)", "import dialog filter")); + QFileInfo info(path); + QString profile = info.completeBaseName(); + QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT); + QFile::copy(path, profilePath); + bodyUI->profiles->addItem(profile); + Core::getInstance()->switchConfiguration(profile); +} diff --git a/src/widget/form/settings/identityform.h b/src/widget/form/settings/identityform.h index abbbf3a2c..fa3759903 100644 --- a/src/widget/form/settings/identityform.h +++ b/src/widget/form/settings/identityform.h @@ -60,6 +60,11 @@ private slots: void copyIdClicked(); void onUserNameEdited(); void onStatusMessageEdited(); + void onLoadClicked(); + void onRenameClicked(); + void onExportClicked(); + void onDeleteClicked(); + void onImportClicked(); private: Ui::IdentitySettings* bodyUI; diff --git a/src/widget/form/settings/identitysettings.ui b/src/widget/form/settings/identitysettings.ui index 67ee1a1ac..437495795 100644 --- a/src/widget/form/settings/identitysettings.ui +++ b/src/widget/form/settings/identitysettings.ui @@ -59,6 +59,71 @@ + + + + Profiles + + + + + + + + Available profiles: + + + + + + + + + + + + + + Load + + + + + + + Rename + + + + + + + Export + + + + + + + Delete + + + This is useful to remain safe on public computers + + + + + + + + + Import a profile + + + + + + diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 471f98321..b310627c0 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include Widget *Widget::instance{nullptr}; @@ -116,8 +117,9 @@ Widget::Widget(QWidget *parent) qRegisterMetaType("ToxFile"); qRegisterMetaType("ToxFile::FileDirection"); + QString profilePath = detectProfile(); coreThread = new QThread(this); - core = new Core(Camera::getInstance(), coreThread); + core = new Core(Camera::getInstance(), coreThread, profilePath); core->moveToThread(coreThread); connect(coreThread, &QThread::started, core, &Core::start); @@ -211,6 +213,69 @@ void Widget::closeEvent(QCloseEvent *event) QWidget::closeEvent(event); } +QString Widget::detectProfile() +{ + QDir dir(Settings::getSettingsDirPath()); + QString path, profile = Settings::getInstance().getCurrentProfile(); + path = dir.filePath(profile + Core::TOX_EXT); + QFile file(path); + if (profile == "" || !file.exists()) + { + Settings::getInstance().setCurrentProfile(""); +#if 1 // deprecation attempt + // if the last profile doesn't exist, fall back to old "data" + path = dir.filePath(Core::CONFIG_FILE_NAME); + QFile file(path); + if (file.exists()) + return path; + else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data + return path; + else +#endif + { + profile = askProfiles(); + if (profile != "") + return dir.filePath(profile + Core::TOX_EXT); + else + return ""; + } + } + else + return path; +} + +QList Widget::searchProfiles() +{ + QList out; + QDir dir(Settings::getSettingsDirPath()); + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + dir.setNameFilters(QStringList("*.tox")); + for(QFileInfo file : dir.entryInfoList()) + out += file.completeBaseName(); + return out; +} + +QString Widget::askProfiles() +{ // TODO: allow user to create new Tox ID, even if a profile already exists + QList profiles = searchProfiles(); + if (profiles.empty()) return ""; + bool ok; + QString profile = QInputDialog::getItem(this, + tr("Choose a profile"), + tr("Please choose which identity to use"), + profiles, + 0, // which slot to start on + false, // if the user can enter their own input + &ok); + if (!ok) // user cancelled + { + qApp->quit(); + return ""; + } + else + return profile; +} + QString Widget::getUsername() { return core->getUsername(); @@ -565,19 +630,31 @@ void Widget::onFriendRequestReceived(const QString& userId, const QString& messa emit friendRequestAccepted(userId); } -void Widget::removeFriend(int friendId) +void Widget::removeFriend(Friend* f) { - Friend* f = FriendList::findFriend(friendId); f->widget->setAsInactiveChatroom(); if (static_cast(f->widget) == activeChatroomWidget) activeChatroomWidget = nullptr; - FriendList::removeFriend(friendId); - core->removeFriend(friendId); + FriendList::removeFriend(f->friendId); + core->removeFriend(f->friendId); delete f; if (ui->mainHead->layout()->isEmpty()) onAddClicked(); } +void Widget::removeFriend(int friendId) +{ + removeFriend(FriendList::findFriend(friendId)); +} + +void Widget::clearContactsList() +{ + for (Friend* f : FriendList::friendList) + removeFriend(f); + for (Group* g : GroupList::groupList) + removeGroup(g); +} + void Widget::copyFriendIdToClipboard(int friendId) { Friend* f = FriendList::findFriend(friendId); @@ -641,19 +718,23 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha g->updatePeer(peernumber,core->getGroupPeerName(groupnumber, peernumber)); } -void Widget::removeGroup(int groupId) +void Widget::removeGroup(Group* g) { - Group* g = GroupList::findGroup(groupId); g->widget->setAsInactiveChatroom(); if (static_cast(g->widget) == activeChatroomWidget) activeChatroomWidget = nullptr; - GroupList::removeGroup(groupId); - core->removeGroup(groupId); + GroupList::removeGroup(g->groupId); + core->removeGroup(g->groupId); delete g; if (ui->mainHead->layout()->isEmpty()) onAddClicked(); } +void Widget::removeGroup(int groupId) +{ + removeGroup(GroupList::findGroup(groupId)); +} + Core *Widget::getCore() { return core; diff --git a/src/widget/widget.h b/src/widget/widget.h index 45953a206..923d077df 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -56,6 +56,8 @@ public: void newMessageAlert(); bool isFriendWidgetCurActiveWidget(Friend* f); bool getIsWindowMinimized(); + static QList searchProfiles(); + void clearContactsList(); ~Widget(); virtual void closeEvent(QCloseEvent *event); @@ -111,8 +113,11 @@ private: void hideMainForms(); virtual bool event(QEvent * e); Group* createGroup(int groupId); + void removeFriend(Friend* f); + void removeGroup(Group* g); + QString askProfiles(); + QString detectProfile(); -private: Ui::MainWindow *ui; QSplitter *centralLayout; QPoint dragPosition; diff --git a/ui/chatArea/innerStyle.css b/ui/chatArea/innerStyle.css index 823c8dfbe..b5c537362 100644 --- a/ui/chatArea/innerStyle.css +++ b/ui/chatArea/innerStyle.css @@ -41,7 +41,7 @@ div.green { margin-top: 12px; margin-bottom: 12px; margin-left: 0px; - margin-right: 12px; + margin-right: 0px; color: @white; background-color: @green; font: @small; @@ -51,7 +51,7 @@ div.silver { margin-top: 12px; margin-bottom: 12px; margin-left: 0px; - margin-right: 12px; + margin-right: 0px; color: @black; background-color: @lightGrey; font: @small; @@ -61,7 +61,7 @@ div.red { margin-top: 12px; margin-bottom: 12px; margin-left: 0px; - margin-right: 12px; + margin-right: 0px; color: @white; background-color: @red; font: @small;
\n"; + res += "\n"; res += "
"; res += content; res += "
\n"; res += "
\n"; + res += "
\n"; + res += "
\n"; res += "
" + imgAstr + "
" + imgBstr + "
\n"; res += "