1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge pull request #414 from dubslow/profiles

Profiles -- they actually work!!!!!!!
This commit is contained in:
Tux3 / Mlkj / !Lev.uXFMLA 2014-10-13 19:54:16 +02:00
commit 259a1d042b
9 changed files with 377 additions and 44 deletions

View File

@ -39,11 +39,12 @@
#include <QMessageBox>
const QString Core::CONFIG_FILE_NAME = "data";
const QString Core::TOX_EXT = ".tox";
QList<ToxFile> Core::fileSendQueue;
QList<ToxFile> 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;
@ -117,12 +118,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)
@ -181,7 +181,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.";
}
@ -205,15 +205,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);
@ -273,9 +282,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()
{
@ -627,8 +636,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
{
@ -918,6 +927,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 {
@ -928,6 +939,8 @@ void Core::removeFriend(int friendId)
void Core::removeGroup(int groupId)
{
if (!tox)
return;
tox_del_groupchat(tox, groupId);
}
@ -949,8 +962,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();
}
}
@ -981,6 +994,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);
@ -1038,11 +1058,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<QChar> 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";
@ -1093,32 +1127,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 = tox_size(tox);
if (fileSize > 0 && fileSize <= INT32_MAX) {
uint8_t *data = new uint8_t[fileSize];
@ -1129,6 +1182,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);

View File

@ -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();
@ -209,7 +217,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);
@ -225,14 +234,14 @@ 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<DhtServer> dhtServerList;
int dhtServerId;
static QList<ToxFile> fileSendQueue, fileRecvQueue;
static ToxCall calls[];
static const QString CONFIG_FILE_NAME;
static const int videobufsize;
static uint8_t* videobuf;
static int videoBusyness; // Used to know when to drop frames

View File

@ -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;

View File

@ -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);
@ -176,6 +179,8 @@ private:
QString proxyAddr;
int proxyPort;
QString currentProfile;
bool enableLogging;
bool encryptLogs;

View File

@ -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 <QLabel>
#include <QLineEdit>
#include <QApplication>
#include <QClipboard>
#include <QInputDialog>
#include <QFileDialog>
#include <QMessageBox>
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);
}

View File

@ -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;

View File

@ -59,6 +59,71 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="profilesGroup">
<property name="title">
<string>Profiles</string>
</property>
<layout class="QVBoxLayout" name="profilesVLayout">
<item>
<layout class="QHBoxLayout" name="profilesComboLayout">
<item>
<widget class="QLabel" name="profilesLabel">
<property name="text">
<string>Available profiles:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="profiles" />
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="profilesButtonsLayout">
<item>
<widget class="QPushButton" name="loadButton">
<property name="text">
<string comment="load profile button">Load</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="renameButton">
<property name="text">
<string comment="rename profile button">Rename</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="exportButton">
<property name="text">
<string comment="export profile button">Export</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string comment="delete profile button">Delete</string>
</property>
<property name="toolTip">
<string comment="delete profile button tooltip">This is useful to remain safe on public computers</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="importButton">
<property name="text">
<string comment="import profile button">Import a profile</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View File

@ -41,6 +41,7 @@
#include <QClipboard>
#include <QThread>
#include <QFileDialog>
#include <QInputDialog>
#include <tox/tox.h>
Widget *Widget::instance{nullptr};
@ -116,8 +117,9 @@ Widget::Widget(QWidget *parent)
qRegisterMetaType<ToxFile>("ToxFile");
qRegisterMetaType<ToxFile::FileDirection>("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<QString> Widget::searchProfiles()
{
QList<QString> 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<QString> 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<GenericChatroomWidget*>(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<GenericChatroomWidget*>(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;

View File

@ -56,6 +56,8 @@ public:
void newMessageAlert();
bool isFriendWidgetCurActiveWidget(Friend* f);
bool getIsWindowMinimized();
static QList<QString> 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;