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

Initial commit

This commit is contained in:
Tux3 / Mlkj / !Lev.uXFMLA 2014-06-24 22:11:11 +02:00
commit 18cee41cfb
107 changed files with 3980 additions and 0 deletions

45
addfriendform.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "addfriendform.h"
#include "ui_widget.h"
#include <QFont>
AddFriendForm::AddFriendForm()
{
main = new QWidget(), head = new QWidget();
QFont bold;
bold.setBold(true);
headLabel.setText("Add Friends");
headLabel.setFont(bold);
toxIdLabel.setText("Tox ID");
messageLabel.setText("Message");
sendButton.setText("Send friend request");
main->setLayout(&layout);
layout.addWidget(&toxIdLabel);
layout.addWidget(&toxId);
layout.addWidget(&messageLabel);
layout.addWidget(&message);
layout.addWidget(&sendButton);
head->setLayout(&headLayout);
headLayout.addWidget(&headLabel);
connect(&sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
}
void AddFriendForm::show(Ui::Widget &ui)
{
ui.mainContent->layout()->addWidget(main);
ui.mainHead->layout()->addWidget(head);
main->show();
head->show();
}
void AddFriendForm::onSendTriggered()
{
QString id = toxId.text(), msg = message.toPlainText();
if (id.isEmpty())
return;
emit friendRequested(id, msg);
}

35
addfriendform.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef ADDFRIENDFORM_H
#define ADDFRIENDFORM_H
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QTextEdit>
#include <QPushButton>
#include "ui_widget.h"
class AddFriendForm : public QObject
{
Q_OBJECT
public:
AddFriendForm();
void show(Ui::Widget& ui);
signals:
void friendRequested(const QString& friendAddress, const QString& message);
private slots:
void onSendTriggered();
private:
QLabel headLabel, toxIdLabel, messageLabel;
QPushButton sendButton;
QLineEdit toxId;
QTextEdit message;
QVBoxLayout layout, headLayout;
QWidget *head, *main;
};
#endif // ADDFRIENDFORM_H

153
chatform.cpp Normal file
View File

@ -0,0 +1,153 @@
#include "chatform.h"
#include "friend.h"
#include "friendwidget.h"
#include "widget.h"
#include <QFont>
#include <QTime>
#include <QScrollBar>
ChatForm::ChatForm(Friend* chatFriend)
: f(chatFriend), curRow{0}, lockSliderToBottom{true}
{
main = new QWidget(), head = new QWidget(), chatAreaWidget = new QWidget();
name = new QLabel(), avatar = new QLabel(), statusMessage = new QLabel();
headLayout = new QHBoxLayout(), mainFootLayout = new QHBoxLayout();
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout();
mainChatLayout = new QGridLayout();
msgEdit = new ChatTextEdit();
sendButton = new QPushButton();
chatArea = new QScrollArea();
QFont bold;
bold.setBold(true);
name->setText(chatFriend->widget->name.text());
name->setFont(bold);
statusMessage->setText(chatFriend->widget->statusMessage.text());
avatar->setPixmap(*chatFriend->widget->avatar.pixmap());
chatAreaWidget->setLayout(mainChatLayout);
chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
chatArea->setWidgetResizable(true);
mainChatLayout->setColumnStretch(1,1);
mainChatLayout->setSpacing(10);
sendButton->setIcon(QIcon("img/button icons/sendmessage_2x.png"));
sendButton->setFlat(true);
QPalette pal;
pal.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
sendButton->setPalette(pal);
sendButton->setAutoFillBackground(true);
msgEdit->setFixedHeight(50);
sendButton->setFixedSize(50, 50);
main->setLayout(mainLayout);
mainLayout->addWidget(chatArea);
mainLayout->addLayout(mainFootLayout);
mainLayout->setMargin(0);
mainFootLayout->addWidget(msgEdit);
mainFootLayout->addWidget(sendButton);
head->setLayout(headLayout);
headLayout->addWidget(avatar);
headLayout->addLayout(headTextLayout);
headLayout->addStretch();
headTextLayout->addStretch();
headTextLayout->addWidget(name);
headTextLayout->addWidget(statusMessage);
headTextLayout->addStretch();
chatArea->setWidget(chatAreaWidget);
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
}
ChatForm::~ChatForm()
{
delete main;
delete head;
}
void ChatForm::show(Ui::Widget &ui)
{
ui.mainContent->layout()->addWidget(main);
ui.mainHead->layout()->addWidget(head);
main->show();
head->show();
}
void ChatForm::setName(QString newName)
{
name->setText(newName);
}
void ChatForm::setStatusMessage(QString newMessage)
{
statusMessage->setText(newMessage);
}
void ChatForm::onSendTriggered()
{
QString msg = msgEdit->toPlainText();
if (msg.isEmpty())
return;
QString name = Widget::getInstance()->getUsername();
msgEdit->clear();
addMessage(name, msg);
emit sendMessage(f->friendId, msg);
}
void ChatForm::addFriendMessage(QString message)
{
QLabel *msgAuthor = new QLabel(name->text());
QLabel *msgText = new QLabel(message);
QLabel *msgDate = new QLabel(QTime::currentTime().toString("hh:mm"));
addMessage(msgAuthor, msgText, msgDate);
}
void ChatForm::addMessage(QString author, QString message, QString date)
{
addMessage(new QLabel(author), new QLabel(message), new QLabel(date));
}
void ChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date)
{
QScrollBar* scroll = chatArea->verticalScrollBar();
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
author->setAlignment(Qt::AlignTop | Qt::AlignRight);
date->setAlignment(Qt::AlignTop);
if (author->text() == Widget::getInstance()->getUsername())
{
QPalette pal;
pal.setColor(QPalette::WindowText, Qt::gray);
author->setPalette(pal);
message->setPalette(pal);
}
if (previousName.isEmpty() || previousName != author->text())
{
if (curRow)
{
mainChatLayout->setRowStretch(curRow, 0);
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
curRow++;
}
mainChatLayout->addWidget(author, curRow, 0);
}
previousName = author->text();
mainChatLayout->addWidget(message, curRow, 1);
mainChatLayout->addWidget(date, curRow, 3);
mainChatLayout->setRowStretch(curRow+1, 1);
mainChatLayout->setRowStretch(curRow, 0);
curRow++;
}
void ChatForm::onSliderRangeChanged()
{
QScrollBar* scroll = chatArea->verticalScrollBar();
if (lockSliderToBottom)
scroll->setValue(scroll->maximum());
}

57
chatform.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef CHATFORM_H
#define CHATFORM_H
#include <QLabel>
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QTextEdit>
#include <QScrollArea>
#include <QTime>
#include "chattextedit.h"
#include "ui_widget.h"
// Spacing in px inserted when the author of the last message changes
#define AUTHOR_CHANGE_SPACING 5
class Friend;
class ChatForm : public QObject
{
Q_OBJECT
public:
ChatForm(Friend* chatFriend);
~ChatForm();
void show(Ui::Widget& ui);
void setName(QString newName);
void setStatusMessage(QString newMessage);
void addFriendMessage(QString message);
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
void addMessage(QLabel* author, QLabel* message, QLabel* date);
signals:
void sendMessage(int, QString);
private slots:
void onSendTriggered();
void onSliderRangeChanged();
private:
Friend* f;
QHBoxLayout *headLayout, *mainFootLayout;
QVBoxLayout *headTextLayout, *mainLayout;
QGridLayout *mainChatLayout;
QLabel *avatar, *name, *statusMessage;
ChatTextEdit *msgEdit;
QPushButton *sendButton;
QScrollArea *chatArea;
QWidget *main, *head, *chatAreaWidget;
QString previousName;
int curRow;
bool lockSliderToBottom;
};
#endif // CHATFORM_H

19
chattextedit.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "chattextedit.h"
#include <QKeyEvent>
ChatTextEdit::ChatTextEdit(QWidget *parent) :
QTextEdit(parent)
{
}
void ChatTextEdit::keyPressEvent(QKeyEvent * event)
{
int key = event->key();
if ((key == Qt::Key_Enter || key == Qt::Key_Return)
&& !(event->modifiers() && Qt::ShiftModifier))
{
emit enterPressed();
return;
}
QTextEdit::keyPressEvent(event);
}

20
chattextedit.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef CHATTEXTEDIT_H
#define CHATTEXTEDIT_H
#include <QTextEdit>
class ChatTextEdit : public QTextEdit
{
Q_OBJECT
public:
explicit ChatTextEdit(QWidget *parent = 0);
virtual void keyPressEvent(QKeyEvent * event) override;
signals:
void enterPressed();
public slots:
};
#endif // CHATTEXTEDIT_H

47
copyableelidelabel.cpp Normal file
View File

@ -0,0 +1,47 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 "copyableelidelabel.h"
#include <QApplication>
#include <QMenu>
#include <QClipboard>
CopyableElideLabel::CopyableElideLabel(QWidget* parent) :
ElideLabel(parent)
{
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &CopyableElideLabel::customContextMenuRequested, this, &CopyableElideLabel::showContextMenu);
actionCopy = new QAction(tr("Copy"), this);
connect(actionCopy, &QAction::triggered, [this]() {
QApplication::clipboard()->setText(text());
});
}
void CopyableElideLabel::showContextMenu(const QPoint& pos)
{
if (text().length() == 0) {
return;
}
QPoint globalPos = mapToGlobal(pos);
QMenu contextMenu;
contextMenu.addAction(actionCopy);
contextMenu.exec(globalPos);
}

35
copyableelidelabel.h Normal file
View File

@ -0,0 +1,35 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 COPYABLEELIDELABEL_HPP
#define COPYABLEELIDELABEL_HPP
#include "elidelabel.h"
class CopyableElideLabel : public ElideLabel
{
public:
explicit CopyableElideLabel(QWidget* parent = 0);
private:
QAction* actionCopy;
private slots:
void showContextMenu(const QPoint& pos);
};
#endif // COPYABLEELIDELABEL_HPP

568
core.cpp Normal file
View File

@ -0,0 +1,568 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 "core.h"
#include <cstdint>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QSaveFile>
#include <QStandardPaths>
#include <QtEndian>
#include <QThread>
#include "settings.h"
const QString Core::CONFIG_FILE_NAME = "tox_save";
Core::Core() :
tox(nullptr)
{
toxTimer = new QTimer(this);
toxTimer->setSingleShot(true);
saveTimer = new QTimer(this);
saveTimer->start(TOX_SAVE_INTERVAL);
connect(toxTimer, &QTimer::timeout, this, &Core::process);
connect(saveTimer, &QTimer::timeout, this, &Core::saveConfiguration);
connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::bootstrapDht);
}
Core::~Core()
{
if (tox) {
saveConfiguration();
tox_kill(tox);
}
}
void Core::onFriendRequest(Tox*/* tox*/, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core)
{
emit static_cast<Core*>(core)->friendRequestReceived(CUserId::toString(cUserId), CString::toString(cMessage, cMessageSize));
}
void Core::onFriendMessage(Tox*/* tox*/, int friendId, uint8_t* cMessage, uint16_t cMessageSize, void* core)
{
emit static_cast<Core*>(core)->friendMessageReceived(friendId, CString::toString(cMessage, cMessageSize));
}
void Core::onFriendNameChange(Tox*/* tox*/, int friendId, uint8_t* cName, uint16_t cNameSize, void* core)
{
emit static_cast<Core*>(core)->friendUsernameChanged(friendId, CString::toString(cName, cNameSize));
}
void Core::onFriendTypingChange(Tox*/* tox*/, int friendId, uint8_t isTyping, void *core)
{
emit static_cast<Core*>(core)->friendTypingChanged(friendId, isTyping ? true : false);
}
void Core::onStatusMessageChanged(Tox*/* tox*/, int friendId, uint8_t* cMessage, uint16_t cMessageSize, void* core)
{
emit static_cast<Core*>(core)->friendStatusMessageChanged(friendId, CString::toString(cMessage, cMessageSize));
}
void Core::onUserStatusChanged(Tox*/* tox*/, int friendId, uint8_t userstatus, void* core)
{
Status status;
switch (userstatus) {
case TOX_USERSTATUS_NONE:
status = Status::Online;
break;
case TOX_USERSTATUS_AWAY:
status = Status::Away;
break;
case TOX_USERSTATUS_BUSY:
status = Status::Busy;
break;
default:
status = Status::Online;
break;
}
emit static_cast<Core*>(core)->friendStatusChanged(friendId, status);
}
void Core::onConnectionStatusChanged(Tox*/* tox*/, int friendId, uint8_t status, void* core)
{
Status friendStatus = status ? Status::Online : Status::Offline;
emit static_cast<Core*>(core)->friendStatusChanged(friendId, friendStatus);
if (friendStatus == Status::Offline) {
static_cast<Core*>(core)->checkLastOnline(friendId);
}
}
void Core::onAction(Tox*/* tox*/, int friendId, uint8_t *cMessage, uint16_t cMessageSize, void *core)
{
emit static_cast<Core*>(core)->actionReceived(friendId, CString::toString(cMessage, cMessageSize));
}
void Core::onGroupInvite(Tox*, int friendnumber, uint8_t *group_public_key, void *core)
{
qDebug() << QString("Core: Group invite by %1").arg(friendnumber);
emit static_cast<Core*>(core)->groupInviteReceived(friendnumber, group_public_key);
}
void Core::onGroupMessage(Tox*, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *core)
{
emit static_cast<Core*>(core)->groupMessageReceived(groupnumber, friendgroupnumber, CString::toString(message, length));
}
void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t change, void *core)
{
qDebug() << QString("Core: Group namelist change %1:%2 %3").arg(groupnumber).arg(peernumber).arg(change);
emit static_cast<Core*>(core)->groupNamelistChanged(groupnumber, peernumber, change);
}
void Core::acceptFriendRequest(const QString& userId)
{
int friendId = tox_add_friend_norequest(tox, CUserId(userId).data());
if (friendId == -1) {
emit failedToAddFriend(userId);
} else {
emit friendAdded(friendId, userId);
}
}
void Core::requestFriendship(const QString& friendAddress, const QString& message)
{
CString cMessage(message);
int friendId = tox_add_friend(tox, CFriendAddress(friendAddress).data(), cMessage.data(), cMessage.size());
const QString userId = friendAddress.mid(0, TOX_CLIENT_ID_SIZE * 2);
// TODO: better error handling
if (friendId < 0) {
emit failedToAddFriend(userId);
} else {
emit friendAdded(friendId, userId);
}
}
void Core::sendMessage(int friendId, const QString& message)
{
CString cMessage(message);
int messageId = tox_send_message(tox, friendId, cMessage.data(), cMessage.size());
emit messageSentResult(friendId, message, messageId);
}
void Core::sendAction(int friendId, const QString &action)
{
CString cMessage(action);
int ret = tox_send_action(tox, friendId, cMessage.data(), cMessage.size());
emit actionSentResult(friendId, action, ret);
}
void Core::sendTyping(int friendId, bool typing)
{
int ret = tox_set_user_is_typing(tox, friendId, typing);
if (ret == -1)
emit failedToSetTyping(typing);
}
void Core::sendGroupMessage(int groupId, const QString& message)
{
CString cMessage(message);
tox_group_message_send(tox, groupId, cMessage.data(), cMessage.size());
}
void Core::removeFriend(int friendId)
{
if (tox_del_friend(tox, friendId) == -1) {
emit failedToRemoveFriend(friendId);
} else {
emit friendRemoved(friendId);
}
}
void Core::removeGroup(int groupId)
{
tox_del_groupchat(tox, groupId);
}
void Core::setUsername(const QString& username)
{
CString cUsername(username);
if (tox_set_name(tox, cUsername.data(), cUsername.size()) == -1) {
emit failedToSetUsername(username);
} else {
emit usernameSet(username);
}
}
void Core::setStatusMessage(const QString& message)
{
CString cMessage(message);
if (tox_set_status_message(tox, cMessage.data(), cMessage.size()) == -1) {
emit failedToSetStatusMessage(message);
} else {
emit statusMessageSet(message);
}
}
void Core::setStatus(Status status)
{
TOX_USERSTATUS userstatus;
switch (status) {
case Status::Online:
userstatus = TOX_USERSTATUS_NONE;
break;
case Status::Away:
userstatus = TOX_USERSTATUS_AWAY;
break;
case Status::Busy:
userstatus = TOX_USERSTATUS_BUSY;
break;
default:
userstatus = TOX_USERSTATUS_INVALID;
break;
}
if (tox_set_user_status(tox, userstatus) == 0) {
emit statusSet(status);
} else {
emit failedToSetStatus(status);
}
}
void Core::bootstrapDht()
{
const Settings& s = Settings::getInstance();
QList<Settings::DhtServer> dhtServerList = s.getDhtServerList();
for (const Settings::DhtServer& dhtServer : dhtServerList) {
tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(), 0, qToBigEndian(dhtServer.port), CUserId(dhtServer.userId).data());
}
}
void Core::process()
{
tox_do(tox);
#ifdef DEBUG
//we want to see the debug messages immediately
fflush(stdout);
#endif
checkConnection();
if (!tox_isconnected(tox))
bootstrapDht();
toxTimer->start(tox_do_interval(tox));
}
void Core::checkConnection()
{
static bool isConnected = false;
if (tox_isconnected(tox) && !isConnected) {
emit connected();
isConnected = true;
} else if (!tox_isconnected(tox) && isConnected) {
emit disconnected();
isConnected = false;
}
}
void Core::loadConfiguration()
{
QString path = Settings::getSettingsDirPath() + '/' + CONFIG_FILE_NAME;
QFile configurationFile(path);
if (!configurationFile.exists()) {
qWarning() << "The Tox configuration file was not found";
return;
}
if (!configurationFile.open(QIODevice::ReadOnly)) {
qCritical() << "File " << path << " cannot be opened";
return;
}
qint64 fileSize = configurationFile.size();
if (fileSize > 0) {
QByteArray data = configurationFile.readAll();
tox_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size());
}
configurationFile.close();
loadFriends();
}
void Core::saveConfiguration()
{
QString path = Settings::getSettingsDirPath();
QDir directory(path);
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
qCritical() << "Error while creating directory " << path;
return;
}
path += '/' + CONFIG_FILE_NAME;
QSaveFile configurationFile(path);
if (!configurationFile.open(QIODevice::WriteOnly)) {
qCritical() << "File " << path << " cannot be opened";
return;
}
qDebug() << "Core: writing tox_save";
uint32_t fileSize = tox_size(tox);
if (fileSize > 0 && fileSize <= INT32_MAX) {
uint8_t *data = new uint8_t[fileSize];
tox_save(tox, data);
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
configurationFile.commit();
delete[] data;
}
}
void Core::loadFriends()
{
const uint32_t friendCount = tox_count_friendlist(tox);
if (friendCount > 0) {
// assuming there are not that many friends to fill up the whole stack
int32_t *ids = new int32_t[friendCount];
tox_get_friendlist(tox, ids, friendCount);
uint8_t clientId[TOX_CLIENT_ID_SIZE];
for (int32_t i = 0; i < static_cast<int32_t>(friendCount); ++i) {
if (tox_get_client_id(tox, ids[i], clientId) == 0) {
emit friendAdded(ids[i], CUserId::toString(clientId));
const int nameSize = tox_get_name_size(tox, ids[i]);
if (nameSize > 0) {
uint8_t *name = new uint8_t[nameSize];
if (tox_get_name(tox, ids[i], name) == nameSize) {
emit friendUsernameLoaded(ids[i], CString::toString(name, nameSize));
}
delete[] name;
}
const int statusMessageSize = tox_get_status_message_size(tox, ids[i]);
if (statusMessageSize > 0) {
uint8_t *statusMessage = new uint8_t[statusMessageSize];
if (tox_get_status_message(tox, ids[i], statusMessage, statusMessageSize) == statusMessageSize) {
emit friendStatusMessageLoaded(ids[i], CString::toString(statusMessage, statusMessageSize));
}
delete[] statusMessage;
}
checkLastOnline(ids[i]);
}
}
delete[] ids;
}
}
void Core::checkLastOnline(int friendId) {
const uint64_t lastOnline = tox_get_last_online(tox, friendId);
if (lastOnline > 0) {
emit friendLastSeenChanged(friendId, QDateTime::fromTime_t(lastOnline));
}
}
void Core::start()
{
tox = tox_new(0);
if (tox == nullptr) {
qCritical() << "Core failed to start";
emit failedToStart();
return;
}
loadConfiguration();
tox_callback_friend_request(tox, onFriendRequest, this);
tox_callback_friend_message(tox, onFriendMessage, this);
tox_callback_friend_action(tox, onAction, this);
tox_callback_name_change(tox, onFriendNameChange, this);
tox_callback_typing_change(tox, onFriendTypingChange, this);
tox_callback_status_message(tox, onStatusMessageChanged, this);
tox_callback_user_status(tox, onUserStatusChanged, this);
tox_callback_connection_status(tox, onConnectionStatusChanged, this);
tox_callback_group_invite(tox, onGroupInvite, this);
tox_callback_group_message(tox, onGroupMessage, this);
tox_callback_group_namelist_change(tox, onGroupNamelistChange, this);
uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox, friendAddress);
emit friendAddressGenerated(CFriendAddress::toString(friendAddress));
CString cUsername(Settings::getInstance().getUsername());
tox_set_name(tox, cUsername.data(), cUsername.size());
CString cStatusMessage(Settings::getInstance().getStatusMessage());
tox_set_status_message(tox, cStatusMessage.data(), cStatusMessage.size());
bootstrapDht();
toxTimer->start(tox_do_interval(tox));
}
int Core::getGroupNumberPeers(int groupId) const
{
return tox_group_number_peers(tox, groupId);
}
QString Core::getGroupPeerName(int groupId, int peerId) const
{
QString name;
uint8_t nameArray[TOX_MAX_NAME_LENGTH];
int length = tox_group_peername(tox, groupId, peerId, nameArray);
if (length == -1)
{
qWarning() << "Core::getGroupPeerName: Unknown error";
return name;
}
name = CString::toString(nameArray, length);
return name;
}
QList<QString> Core::getGroupPeerNames(int groupId) const
{
QList<QString> names;
int nPeers = getGroupNumberPeers(groupId);
if (nPeers == -1)
{
qWarning() << "Core::getGroupPeerNames: Unable to get number of peers";
return names;
}
uint8_t namesArray[nPeers][TOX_MAX_NAME_LENGTH];
uint16_t* lengths = new uint16_t[nPeers];
int result = tox_group_get_names(tox, groupId, namesArray, lengths, nPeers);
if (result != nPeers)
{
qWarning() << "Core::getGroupPeerNames: Unexpected result";
return names;
}
for (int i=0; i<nPeers; i++)
names.push_back(CString::toString(namesArray[i], lengths[i]));
return names;
}
int Core::joinGroupchat(int32_t friendnumber, uint8_t* friend_group_public_key) const
{
return tox_join_groupchat(tox, friendnumber, friend_group_public_key);
}
// CData
Core::CData::CData(const QString &data, uint16_t byteSize)
{
cData = new uint8_t[byteSize];
cDataSize = fromString(data, cData);
}
Core::CData::~CData()
{
delete[] cData;
}
uint8_t* Core::CData::data()
{
return cData;
}
uint16_t Core::CData::size()
{
return cDataSize;
}
QString Core::CData::toString(const uint8_t *cData, const uint16_t cDataSize)
{
return QString(QByteArray(reinterpret_cast<const char*>(cData), cDataSize).toHex()).toUpper();
}
uint16_t Core::CData::fromString(const QString& data, uint8_t* cData)
{
QByteArray arr = QByteArray::fromHex(data.toLower().toLatin1());
memcpy(cData, reinterpret_cast<uint8_t*>(arr.data()), arr.size());
return arr.size();
}
// CUserId
Core::CUserId::CUserId(const QString &userId) :
CData(userId, SIZE)
{
// intentionally left empty
}
QString Core::CUserId::toString(const uint8_t* cUserId)
{
return CData::toString(cUserId, SIZE);
}
// CFriendAddress
Core::CFriendAddress::CFriendAddress(const QString &friendAddress) :
CData(friendAddress, SIZE)
{
// intentionally left empty
}
QString Core::CFriendAddress::toString(const uint8_t *cFriendAddress)
{
return CData::toString(cFriendAddress, SIZE);
}
// CString
Core::CString::CString(const QString& string)
{
cString = new uint8_t[string.length() * MAX_SIZE_OF_UTF8_ENCODED_CHARACTER]();
cStringSize = fromString(string, cString);
}
Core::CString::~CString()
{
delete[] cString;
}
uint8_t* Core::CString::data()
{
return cString;
}
uint16_t Core::CString::size()
{
return cStringSize;
}
QString Core::CString::toString(const uint8_t* cString, uint16_t cStringSize)
{
return QString::fromUtf8(reinterpret_cast<const char*>(cString), cStringSize);
}
uint16_t Core::CString::fromString(const QString& string, uint8_t* cString)
{
QByteArray byteArray = QByteArray(string.toUtf8());
memcpy(cString, reinterpret_cast<uint8_t*>(byteArray.data()), byteArray.size());
return byteArray.size();
}
void Core::quitGroupChat(int groupId) const
{
tox_del_groupchat(tox, groupId);
}

216
core.h Normal file
View File

@ -0,0 +1,216 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 CORE_HPP
#define CORE_HPP
#include "status.h"
#include <tox/tox.h>
#include <QDateTime>
#include <QObject>
#include <QTimer>
#include <QString>
#include <QList>
#define GROUPCHAT_MAX_SIZE 32
#define TOX_SAVE_INTERVAL 10*1000
struct DhtServer
{
QString name;
QString userId;
QString address;
int port;
};
class Core : public QObject
{
Q_OBJECT
public:
explicit Core();
~Core();
private:
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onFriendMessage(Tox* tox, int friendId, uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onFriendNameChange(Tox* tox, int friendId, uint8_t* cName, uint16_t cNameSize, void* core);
static void onFriendTypingChange(Tox* tox, int friendId, uint8_t isTyping, void* core);
static void onStatusMessageChanged(Tox* tox, int friendId, uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onUserStatusChanged(Tox* tox, int friendId, uint8_t userstatus, void* core);
static void onConnectionStatusChanged(Tox* tox, int friendId, uint8_t status, void* core);
static void onAction(Tox* tox, int friendId, uint8_t* cMessage, uint16_t cMessageSize, void* core);
static void onGroupInvite(Tox *tox, int friendnumber, uint8_t *group_public_key, void *userdata);
static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata);
static void onGroupNamelistChange(Tox *tox, int groupnumber, int peernumber, uint8_t change, void *userdata);
void checkConnection();
void loadConfiguration();
void saveConfiguration();
void loadFriends();
void checkLastOnline(int friendId);
Tox* tox;
QTimer *toxTimer, *saveTimer;
QList<DhtServer> dhtServerList;
int dhtServerId;
static const QString CONFIG_FILE_NAME;
class CData
{
public:
uint8_t* data();
uint16_t size();
protected:
explicit CData(const QString& data, uint16_t byteSize);
virtual ~CData();
static QString toString(const uint8_t* cData, const uint16_t cDataSize);
private:
uint8_t* cData;
uint16_t cDataSize;
static uint16_t fromString(const QString& userId, uint8_t* cData);
};
class CUserId : public CData
{
public:
explicit CUserId(const QString& userId);
static QString toString(const uint8_t *cUserId);
private:
static const uint16_t SIZE = TOX_CLIENT_ID_SIZE;
};
class CFriendAddress : public CData
{
public:
explicit CFriendAddress(const QString& friendAddress);
static QString toString(const uint8_t* cFriendAddress);
private:
static const uint16_t SIZE = TOX_FRIEND_ADDRESS_SIZE;
};
class CString
{
public:
explicit CString(const QString& string);
~CString();
uint8_t* data();
uint16_t size();
static QString toString(const uint8_t* cMessage, const uint16_t cMessageSize);
private:
const static int MAX_SIZE_OF_UTF8_ENCODED_CHARACTER = 4;
uint8_t* cString;
uint16_t cStringSize;
static uint16_t fromString(const QString& message, uint8_t* cMessage);
};
public:
int getGroupNumberPeers(int groupId) const;
QString getGroupPeerName(int groupId, int peerId) const;
QList<QString> getGroupPeerNames(int groupId) const;
int joinGroupchat(int32_t friendnumber, uint8_t* friend_group_public_key) const;
void quitGroupChat(int groupId) const;
public slots:
void start();
void acceptFriendRequest(const QString& userId);
void requestFriendship(const QString& friendAddress, const QString& message);
void removeFriend(int friendId);
void removeGroup(int groupId);
void sendMessage(int friendId, const QString& message);
void sendAction(int friendId, const QString& action);
void sendTyping(int friendId, bool typing);
void sendGroupMessage(int groupId, const QString& message);
void setUsername(const QString& username);
void setStatusMessage(const QString& message);
void setStatus(Status status);
void process();
void bootstrapDht();
signals:
void connected();
void disconnected();
void friendRequestReceived(const QString& userId, const QString& message);
void friendMessageReceived(int friendId, const QString& message);
void friendAdded(int friendId, const QString& userId);
void friendStatusChanged(int friendId, Status status);
void friendStatusMessageChanged(int friendId, const QString& message);
void friendUsernameChanged(int friendId, const QString& username);
void friendTypingChanged(int friendId, bool isTyping);
void friendStatusMessageLoaded(int friendId, const QString& message);
void friendUsernameLoaded(int friendId, const QString& username);
void friendAddressGenerated(const QString& friendAddress);
void friendRemoved(int friendId);
void friendLastSeenChanged(int friendId, const QDateTime& dateTime);
void groupInviteReceived(int friendnumber, uint8_t *group_public_key);
void groupMessageReceived(int groupnumber, int friendgroupnumber, const QString& message);
void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
void usernameSet(const QString& username);
void statusMessageSet(const QString& message);
void statusSet(Status status);
void messageSentResult(int friendId, const QString& message, int messageId);
void actionSentResult(int friendId, const QString& action, int success);
void failedToAddFriend(const QString& userId);
void failedToRemoveFriend(int friendId);
void failedToSetUsername(const QString& username);
void failedToSetStatusMessage(const QString& message);
void failedToSetStatus(Status status);
void failedToSetTyping(bool typing);
void actionReceived(int friendId, const QString& acionMessage);
void failedToStart();
};
#endif // CORE_HPP

110
editablelabelwidget.cpp Normal file
View File

@ -0,0 +1,110 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 "editablelabelwidget.h"
#include <QApplication>
#include <QEvent>
#include <QFontMetrics>
#include <QMouseEvent>
#include <QVBoxLayout>
ClickableCopyableElideLabel::ClickableCopyableElideLabel(QWidget* parent) :
CopyableElideLabel(parent)
{
}
bool ClickableCopyableElideLabel::event(QEvent* event)
{
if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::LeftButton) {
emit clicked();
}
} else if (event->type() == QEvent::Enter) {
QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor));
} else if (event->type() == QEvent::Leave) {
QApplication::restoreOverrideCursor();
}
return CopyableElideLabel::event(event);
}
EditableLabelWidget::EditableLabelWidget(QWidget* parent) :
QStackedWidget(parent), isSubmitting(false)
{
label = new ClickableCopyableElideLabel(this);
connect(label, &ClickableCopyableElideLabel::clicked, this, &EditableLabelWidget::onLabelClicked);
lineEdit = new EscLineEdit(this);
lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
lineEdit->setMinimumHeight(label->fontMetrics().lineSpacing() + LINE_SPACING_OFFSET);
connect(lineEdit, &EscLineEdit::editingFinished, this, &EditableLabelWidget::onLabelChangeSubmited);
connect(lineEdit, &EscLineEdit::escPressed, this, &EditableLabelWidget::onLabelChangeCancelled);
addWidget(label);
addWidget(lineEdit);
setCurrentWidget(label);
}
void EditableLabelWidget::setText(const QString& text)
{
label->setText(text);
lineEdit->setText(text);
}
QString EditableLabelWidget::text()
{
return label->text();
}
void EditableLabelWidget::onLabelChangeSubmited()
{
if (isSubmitting) {
return;
}
isSubmitting = true;
QString oldText = label->text();
QString newText = lineEdit->text();
// `lineEdit->clearFocus()` triggers `onLabelChangeSubmited()`, we use `isSubmitting` as a workaround
lineEdit->clearFocus();
setCurrentWidget(label);
if (oldText != newText) {
label->setText(newText);
emit textChanged(newText, oldText);
}
isSubmitting = false;
}
void EditableLabelWidget::onLabelChangeCancelled()
{
// order of calls matters, since clearFocus() triggers EditableLabelWidget::onLabelChangeSubmited()
lineEdit->setText(label->text());
lineEdit->clearFocus();
setCurrentWidget(label);
}
void EditableLabelWidget::onLabelClicked()
{
setCurrentWidget(lineEdit);
lineEdit->setFocus();
}

66
editablelabelwidget.h Normal file
View File

@ -0,0 +1,66 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 EDITABLELABELWIDGET_HPP
#define EDITABLELABELWIDGET_HPP
#include "copyableelidelabel.h"
#include "esclineedit.h"
#include <QLineEdit>
#include <QStackedWidget>
class ClickableCopyableElideLabel : public CopyableElideLabel
{
Q_OBJECT
public:
explicit ClickableCopyableElideLabel(QWidget* parent = 0);
protected:
bool event(QEvent* event) Q_DECL_OVERRIDE;
signals:
void clicked();
};
class EditableLabelWidget : public QStackedWidget
{
Q_OBJECT
public:
explicit EditableLabelWidget(QWidget* parent = 0);
ClickableCopyableElideLabel* label;
EscLineEdit* lineEdit;
void setText(const QString& text);
QString text();
private:
static const int LINE_SPACING_OFFSET = 2;
bool isSubmitting;
private slots:
void onLabelChangeSubmited();
void onLabelChangeCancelled();
void onLabelClicked();
signals:
void textChanged(QString newText, QString oldText);
};
#endif // EDITABLELABELWIDGET_HPP

82
elidelabel.cpp Normal file
View File

@ -0,0 +1,82 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 "elidelabel.h"
#include <QPainter>
#include <QEvent>
ElideLabel::ElideLabel(QWidget *parent) :
QLabel(parent), _textElide(false), _textElideMode(Qt::ElideNone), _showToolTipOnElide(false)
{
}
void ElideLabel::paintEvent(QPaintEvent *event)
{
QFrame::paintEvent(event);
QPainter p(this);
QFontMetrics metrics(font());
if ((metrics.width(text()) > contentsRect().width()) && textElide()) {
QString elidedText = fontMetrics().elidedText(text(), textElideMode(), rect().width());
p.drawText(rect(), alignment(), elidedText);
} else {
QLabel::paintEvent(event);
}
}
bool ElideLabel::event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QFontMetrics metrics(font());
if ((metrics.width(text()) > contentsRect().width()) && textElide() && showToolTipOnElide()) {
setToolTip(text());
} else {
setToolTip("");
}
}
return QLabel::event(event);
}
void ElideLabel::setTextElide(bool set)
{
_textElide = set;
}
bool ElideLabel::textElide() const
{
return _textElide;
}
void ElideLabel::setTextElideMode(Qt::TextElideMode mode)
{
_textElideMode = mode;
}
Qt::TextElideMode ElideLabel::textElideMode() const
{
return _textElideMode;
}
void ElideLabel::setShowToolTipOnElide(bool show)
{
_showToolTipOnElide = show;
}
bool ElideLabel::showToolTipOnElide()
{
return _showToolTipOnElide;
}

50
elidelabel.h Normal file
View File

@ -0,0 +1,50 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 ELIDELABEL_HPP
#define ELIDELABEL_HPP
#include <QLabel>
class ElideLabel : public QLabel
{
Q_OBJECT
public:
explicit ElideLabel(QWidget *parent = 0);
void setTextElide(bool set);
bool textElide() const;
void setTextElideMode(Qt::TextElideMode mode);
Qt::TextElideMode textElideMode() const;
void setShowToolTipOnElide(bool show);
bool showToolTipOnElide();
protected:
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
bool event(QEvent *e) Q_DECL_OVERRIDE;
private:
bool _textElide;
Qt::TextElideMode _textElideMode;
bool _showToolTipOnElide;
};
#endif // ELIDELABEL_HPP

34
esclineedit.cpp Normal file
View File

@ -0,0 +1,34 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 "esclineedit.h"
#include <QKeyEvent>
#include <QMouseEvent>
EscLineEdit::EscLineEdit(QWidget* parent) :
QLineEdit(parent)
{
}
void EscLineEdit::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Escape && event->modifiers() == Qt::NoModifier) {
emit escPressed();
} else {
QLineEdit::keyPressEvent(event);
}
}

36
esclineedit.h Normal file
View File

@ -0,0 +1,36 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 ESCLINEEDIT_HPP
#define ESCLINEEDIT_HPP
#include <QLineEdit>
class EscLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit EscLineEdit(QWidget* parent);
protected:
void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE;
signals:
void escPressed();
};
#endif // ESCLINEEDIT_HPP

33
friend.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "friend.h"
#include "friendlist.h"
#include "friendwidget.h"
Friend::Friend(int FriendId, QString UserId)
: friendId(FriendId), userId(UserId)
{
widget = new FriendWidget(friendId, userId);
chatForm = new ChatForm(this);
}
Friend::~Friend()
{
delete chatForm;
delete widget;
}
void Friend::setName(QString name)
{
widget->name.setText(name);
chatForm->setName(name);
}
void Friend::setStatusMessage(QString message)
{
widget->statusMessage.setText(message);
chatForm->setStatusMessage(message);
}
QString Friend::getName()
{
return widget->name.text();
}

25
friend.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef FRIEND_H
#define FRIEND_H
#include <QString>
#include "chatform.h"
class FriendWidget;
struct Friend
{
public:
Friend(int FriendId, QString UserId);
~Friend();
void setName(QString name);
void setStatusMessage(QString message);
QString getName();
public:
FriendWidget* widget;
int friendId;
QString userId;
ChatForm* chatForm;
};
#endif // FRIEND_H

32
friendlist.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "friend.h"
#include "friendlist.h"
#include <QMenu>
QList<Friend*> FriendList::friendList;
Friend* FriendList::addFriend(int friendId, QString userId)
{
Friend* newfriend = new Friend(friendId, userId);
friendList.append(newfriend);
return newfriend;
}
Friend* FriendList::findFriend(int friendId)
{
for (Friend* f : friendList)
if (f->friendId == friendId)
return f;
return nullptr;
}
void FriendList::removeFriend(int friendId)
{
for (int i=0; i<friendList.size(); i++)
{
if (friendList[i]->friendId == friendId)
{
friendList.removeAt(i);
return;
}
}
}

21
friendlist.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef FRIENDLIST_H
#define FRIENDLIST_H
#include <QString>
#include <QList>
class Friend;
class FriendList
{
public:
FriendList();
static Friend* addFriend(int friendId, QString userId);
static Friend* findFriend(int friendId);
static void removeFriend(int friendId);
public:
static QList<Friend*> friendList;
};
#endif // FRIENDLIST_H

61
friendrequestdialog.cpp Normal file
View File

@ -0,0 +1,61 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 "friendrequestdialog.h"
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QPushButton>
FriendRequestDialog::FriendRequestDialog(QWidget *parent, const QString &userId, const QString &message) :
QDialog(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setWindowTitle("Friend request");
QLabel *friendsLabel = new QLabel("Someone wants to make friends with you.", this);
QLabel *userIdLabel = new QLabel("User ID:", this);
QLineEdit *userIdEdit = new QLineEdit(userId, this);
userIdEdit->setCursorPosition(0);
userIdEdit->setReadOnly(true);
QLabel *messageLabel = new QLabel("Friend request message:", this);
QPlainTextEdit *messageEdit = new QPlainTextEdit(message, this);
messageEdit->setReadOnly(true);
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this);
buttonBox->addButton("Accept", QDialogButtonBox::AcceptRole);
buttonBox->addButton("Reject", QDialogButtonBox::RejectRole);
connect(buttonBox, &QDialogButtonBox::accepted, this, &FriendRequestDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &FriendRequestDialog::reject);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(friendsLabel);
layout->addSpacing(12);
layout->addWidget(userIdLabel);
layout->addWidget(userIdEdit);
layout->addWidget(messageLabel);
layout->addWidget(messageEdit);
layout->addWidget(buttonBox);
resize(300, 200);
}

29
friendrequestdialog.h Normal file
View File

@ -0,0 +1,29 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 FRIENDREQUESTDIALOG_HPP
#define FRIENDREQUESTDIALOG_HPP
#include <QDialog>
class FriendRequestDialog : public QDialog
{
Q_OBJECT
public:
explicit FriendRequestDialog(QWidget *parent, const QString &userId, const QString &message);
};
#endif // FRIENDREQUESTDIALOG_HPP

62
friendwidget.cpp Normal file
View File

@ -0,0 +1,62 @@
#include "friendwidget.h"
#include <QContextMenuEvent>
#include <QMenu>
FriendWidget::FriendWidget(int FriendId, QString id)
: friendId(FriendId)
{
this->setLayout(&layout);
this->setFixedWidth(225);
this->setFixedHeight(55);
layout.setSpacing(0);
layout.setMargin(0);
textLayout.setSpacing(0);
textLayout.setMargin(0);
avatar.setPixmap(QPixmap("img/contact list icons/contact.png"));
name.setText(id);
statusPic.setPixmap(QPixmap("img/status/dot_away.png"));
QFont small;
small.setPixelSize(10);
statusMessage.setFont(small);
QPalette pal;
pal.setColor(QPalette::WindowText,Qt::gray);
statusMessage.setPalette(pal);
textLayout.addStretch();
textLayout.addWidget(&name);
textLayout.addWidget(&statusMessage);
textLayout.addStretch();
layout.addSpacing(20);
layout.addWidget(&avatar);
layout.addSpacing(5);
layout.addLayout(&textLayout);
layout.addStretch();
layout.addSpacing(5);
layout.addWidget(&statusPic);
layout.addSpacing(5);
}
void FriendWidget::mouseReleaseEvent (QMouseEvent*)
{
emit friendWidgetClicked(this);
}
void FriendWidget::contextMenuEvent(QContextMenuEvent * event)
{
QPoint pos = event->globalPos();
QMenu menu;
menu.addAction("Remove friend");
QAction* selectedItem = menu.exec(pos);
if (selectedItem)
{
if (selectedItem->text() == "Remove friend")
{
hide();
emit removeFriend(friendId);
return;
}
}
}

28
friendwidget.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef FRIENDWIDGET_H
#define FRIENDWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
struct FriendWidget : public QWidget
{
Q_OBJECT
public:
FriendWidget(int FriendId, QString id);
void mouseReleaseEvent (QMouseEvent* event);
void contextMenuEvent(QContextMenuEvent * event);
signals:
void friendWidgetClicked(FriendWidget* widget);
void removeFriend(int friendId);
public:
int friendId;
QLabel avatar, name, statusMessage, statusPic;
QHBoxLayout layout;
QVBoxLayout textLayout;
};
#endif // FRIENDWIDGET_H

98
group.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "group.h"
#include "groupwidget.h"
#include "groupchatform.h"
#include "friendlist.h"
#include "friend.h"
#include "widget.h"
#include "core.h"
#include <QDebug>
Group::Group(int GroupId, QString Name)
: groupId(GroupId), nPeers{0}, hasPeerInfo{false}
{
widget = new GroupWidget(groupId, Name);
chatForm = new GroupChatForm(this);
connect(&peerInfoTimer, SIGNAL(timeout()), this, SLOT(queryPeerInfo()));
peerInfoTimer.setInterval(500);
peerInfoTimer.setSingleShot(false);
//peerInfoTimer.start();
}
Group::~Group()
{
delete chatForm;
delete widget;
}
void Group::queryPeerInfo()
{
const Core* core = Widget::getInstance()->getCore();
int nPeersResult = core->getGroupNumberPeers(groupId);
if (nPeersResult == -1)
{
qDebug() << "Group::queryPeerInfo: Can't get number of peers";
return;
}
nPeers = nPeersResult;
widget->onUserListChanged();
chatForm->onUserListChanged();
if (nPeersResult == 0)
return;
bool namesOk = true;
QList<QString> names = core->getGroupPeerNames(groupId);
if (names.isEmpty())
{
qDebug() << "Group::queryPeerInfo: Can't get names of peers";
return;
}
for (int i=0; i<names.size(); i++)
{
QString name = names[i];
if (name.isEmpty())
{
name = "<Unknown>";
namesOk = false;
}
peers[i] = name;
}
nPeers = names.size();
widget->onUserListChanged();
chatForm->onUserListChanged();
if (namesOk)
{
qDebug() << "Group::queryPeerInfo: Successfully loaded names";
hasPeerInfo = true;
peerInfoTimer.stop();
}
}
void Group::addPeer(int peerId, QString name)
{
if (peers.contains(peerId))
qWarning() << "Group::addPeer: peerId already used, overwriting anyway";
if (name.isEmpty())
peers[peerId] = "<Unknown>";
else
peers[peerId] = name;
nPeers++;
widget->onUserListChanged();
chatForm->onUserListChanged();
}
void Group::removePeer(int peerId)
{
peers.remove(peerId);
widget->onUserListChanged();
chatForm->onUserListChanged();
}
void Group::updatePeer(int peerId, QString name)
{
peers[peerId] = name;
widget->onUserListChanged();
chatForm->onUserListChanged();
}

37
group.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef GROUP_H
#define GROUP_H
#include <QList>
#include <QMap>
#include <QTimer>
#define RETRY_PEER_INFO_INTERVAL 500
class Friend;
class GroupWidget;
class GroupChatForm;
class Group : public QObject
{
Q_OBJECT
public:
Group(int GroupId, QString Name);
~Group();
void addPeer(int peerId, QString name);
void removePeer(int peerId);
void updatePeer(int peerId, QString newName);
private slots:
void queryPeerInfo();
public:
int groupId;
QMap<int,QString> peers;
int nPeers;
GroupWidget* widget;
GroupChatForm* chatForm;
bool hasPeerInfo;
QTimer peerInfoTimer;
};
#endif // GROUP_H

175
groupchatform.cpp Normal file
View File

@ -0,0 +1,175 @@
#include "groupchatform.h"
#include "group.h"
#include "groupwidget.h"
#include "widget.h"
#include "friend.h"
#include "friendlist.h"
#include <QFont>
#include <QTime>
#include <QScrollBar>
GroupChatForm::GroupChatForm(Group* chatGroup)
: group(chatGroup), curRow{0}, lockSliderToBottom{true}
{
main = new QWidget(), head = new QWidget(), chatAreaWidget = new QWidget();
headLayout = new QHBoxLayout(), mainFootLayout = new QHBoxLayout();
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout();
mainChatLayout = new QGridLayout();
avatar = new QLabel(), name = new QLabel(), nusers = new QLabel(), namesList = new QLabel();
msgEdit = new ChatTextEdit();
sendButton = new QPushButton();
chatArea = new QScrollArea();
QFont bold;
bold.setBold(true);
QFont small;
small.setPixelSize(10);
name->setText(group->widget->name.text());
name->setFont(bold);
nusers->setFont(small);
nusers->setText(QString("%1 users in chat").arg(group->peers.size()));
avatar->setPixmap(QPixmap("img/contact list icons/group.png"));
QString names;
for (QString& s : group->peers)
names.append(s+", ");
names.chop(2);
namesList->setText(names);
namesList->setFont(small);
chatAreaWidget->setLayout(mainChatLayout);
chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
chatArea->setWidgetResizable(true);
mainChatLayout->setColumnStretch(1,1);
mainChatLayout->setHorizontalSpacing(10);
sendButton->setIcon(QIcon("img/button icons/sendmessage_2x.png"));
sendButton->setFlat(true);
QPalette pal;
pal.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
sendButton->setPalette(pal);
sendButton->setAutoFillBackground(true);
msgEdit->setFixedHeight(50);
sendButton->setFixedSize(50, 50);
main->setLayout(mainLayout);
mainLayout->addWidget(chatArea);
mainLayout->addLayout(mainFootLayout);
mainLayout->setMargin(0);
mainFootLayout->addWidget(msgEdit);
mainFootLayout->addWidget(sendButton);
head->setLayout(headLayout);
headLayout->addWidget(avatar);
headLayout->addLayout(headTextLayout);
headLayout->addStretch();
headLayout->setMargin(0);
headTextLayout->addStretch();
headTextLayout->addWidget(name);
headTextLayout->addWidget(nusers);
headTextLayout->addWidget(namesList);
headTextLayout->setMargin(0);
headTextLayout->setSpacing(0);
headTextLayout->addStretch();
chatArea->setWidget(chatAreaWidget);
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
}
GroupChatForm::~GroupChatForm()
{
delete head;
delete main;
}
void GroupChatForm::show(Ui::Widget &ui)
{
ui.mainContent->layout()->addWidget(main);
ui.mainHead->layout()->addWidget(head);
main->show();
head->show();
}
void GroupChatForm::setName(QString newName)
{
name->setText(newName);
}
void GroupChatForm::onSendTriggered()
{
QString msg = msgEdit->toPlainText();
if (msg.isEmpty())
return;
msgEdit->clear();
emit sendMessage(group->groupId, msg);
}
void GroupChatForm::addGroupMessage(QString message, int peerId)
{
QLabel *msgAuthor;
if (group->peers.contains(peerId))
msgAuthor = new QLabel(group->peers[peerId]);
else
msgAuthor = new QLabel("<Unknown>");
QLabel *msgText = new QLabel(message);
QLabel *msgDate = new QLabel(QTime::currentTime().toString("hh:mm"));
addMessage(msgAuthor, msgText, msgDate);
}
void GroupChatForm::addMessage(QString author, QString message, QString date)
{
addMessage(new QLabel(author), new QLabel(message), new QLabel(date));
}
void GroupChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date)
{
QScrollBar* scroll = chatArea->verticalScrollBar();
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
author->setAlignment(Qt::AlignTop | Qt::AlignLeft);
date->setAlignment(Qt::AlignTop);
if (author->text() == Widget::getInstance()->getUsername())
{
QPalette pal;
pal.setColor(QPalette::WindowText, Qt::gray);
author->setPalette(pal);
message->setPalette(pal);
}
if (previousName.isEmpty() || previousName != author->text())
{
if (curRow)
{
mainChatLayout->setRowStretch(curRow, 0);
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
curRow++;
}
mainChatLayout->addWidget(author, curRow, 0);
}
previousName = author->text();
mainChatLayout->addWidget(message, curRow, 1);
mainChatLayout->addWidget(date, curRow, 3);
mainChatLayout->setRowStretch(curRow+1, 1);
mainChatLayout->setRowStretch(curRow, 0);
curRow++;
}
void GroupChatForm::onSliderRangeChanged()
{
QScrollBar* scroll = chatArea->verticalScrollBar();
if (lockSliderToBottom)
scroll->setValue(scroll->maximum());
}
void GroupChatForm::onUserListChanged()
{
nusers->setText(QString("%1 users in chat").arg(group->nPeers));
QString names;
for (QString& s : group->peers)
names.append(s+", ");
names.chop(2);
namesList->setText(names);
}

56
groupchatform.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef GROUPCHATFORM_H
#define GROUPCHATFORM_H
#include <QLabel>
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QTextEdit>
#include <QScrollArea>
#include <QTime>
#include "chattextedit.h"
#include "ui_widget.h"
// Spacing in px inserted when the author of the last message changes
#define AUTHOR_CHANGE_SPACING 5
class Group;
class GroupChatForm : public QObject
{
Q_OBJECT
public:
GroupChatForm(Group* chatGroup);
~GroupChatForm();
void show(Ui::Widget& ui);
void setName(QString newName);
void addGroupMessage(QString message, int peerId);
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
void addMessage(QLabel* author, QLabel* message, QLabel* date);
void onUserListChanged();
signals:
void sendMessage(int, QString);
private slots:
void onSendTriggered();
void onSliderRangeChanged();
private:
Group* group;
QHBoxLayout *headLayout, *mainFootLayout;
QVBoxLayout *headTextLayout, *mainLayout;
QGridLayout *mainChatLayout;
QLabel *avatar, *name, *nusers, *namesList;
ChatTextEdit *msgEdit;
QPushButton *sendButton;
QScrollArea *chatArea;
QWidget *main, *head, *chatAreaWidget;
QString previousName;
int curRow;
bool lockSliderToBottom;
};
#endif // GROUPCHATFORM_H

31
grouplist.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "grouplist.h"
#include "group.h"
QList<Group*> GroupList::groupList;
Group* GroupList::addGroup(int groupId, QString name)
{
Group* newGroup = new Group(groupId, name);
groupList.append(newGroup);
return newGroup;
}
Group* GroupList::findGroup(int groupId)
{
for (Group* g : groupList)
if (g->groupId == groupId)
return g;
return nullptr;
}
void GroupList::removeGroup(int groupId)
{
for (int i=0; i<groupList.size(); i++)
{
if (groupList[i]->groupId == groupId)
{
groupList.removeAt(i);
return;
}
}
}

22
grouplist.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef GROUPLIST_H
#define GROUPLIST_H
#include <QString>
#include <QList>
#include <QMap>
class Group;
class GroupList
{
public:
GroupList();
static Group* addGroup(int groupId, QString name);
static Group* findGroup(int groupId);
static void removeGroup(int groupId);
public:
static QList<Group*> groupList;
};
#endif // GROUPLIST_H

75
groupwidget.cpp Normal file
View File

@ -0,0 +1,75 @@
#include "groupwidget.h"
#include "grouplist.h"
#include "group.h"
#include <QPalette>
#include <QMenu>
#include <QContextMenuEvent>
GroupWidget::GroupWidget(int GroupId, QString Name)
: groupId{GroupId}
{
this->setLayout(&layout);
this->setFixedWidth(225);
this->setFixedHeight(55);
layout.setSpacing(0);
layout.setMargin(0);
textLayout.setSpacing(0);
textLayout.setMargin(0);
avatar.setPixmap(QPixmap("img/contact list icons/group_2x.png"));
name.setText(Name);
QFont small;
small.setPixelSize(10);
nusers.setFont(small);
QPalette pal;
pal.setColor(QPalette::WindowText,Qt::gray);
nusers.setPalette(pal);
Group* g = GroupList::findGroup(groupId);
if (g)
nusers.setText(QString("%1 users in chat").arg(g->peers.size()));
else
nusers.setText("0 users in chat");
textLayout.addStretch();
textLayout.addWidget(&name);
textLayout.addWidget(&nusers);
textLayout.addStretch();
layout.addSpacing(20);
layout.addWidget(&avatar);
layout.addSpacing(5);
layout.addLayout(&textLayout);
layout.addStretch();
}
void GroupWidget::mouseReleaseEvent (QMouseEvent*)
{
emit groupWidgetClicked(this);
}
void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
{
QPoint pos = event->globalPos();
QMenu menu;
menu.addAction("Quit group");
QAction* selectedItem = menu.exec(pos);
if (selectedItem)
{
if (selectedItem->text() == "Quit group")
{
hide();
emit removeGroup(groupId);
return;
}
}
}
void GroupWidget::onUserListChanged()
{
Group* g = GroupList::findGroup(groupId);
if (g)
nusers.setText(QString("%1 users in chat").arg(g->nPeers));
else
nusers.setText("0 users in chat");
}

29
groupwidget.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef GROUPWIDGET_H
#define GROUPWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
class GroupWidget : public QWidget
{
Q_OBJECT
public:
GroupWidget(int GroupId, QString Name);
void onUserListChanged();
void mouseReleaseEvent (QMouseEvent* event);
void contextMenuEvent(QContextMenuEvent * event);
signals:
void groupWidgetClicked(GroupWidget* widget);
void removeGroup(int groupId);
public:
int groupId;
QLabel avatar, name, nusers;
QHBoxLayout layout;
QVBoxLayout textLayout;
};
#endif // GROUPWIDGET_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="7.145px" height="6.187px" viewBox="0 0 7.145 6.187" enable-background="new 0 0 7.145 6.187" xml:space="preserve">
<polygon fill="#636365" points="7.145,0 3.572,6.187 0,0 "/>
</svg>

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="7.145px" height="6.187px" viewBox="0 0 7.145 6.187" enable-background="new 0 0 7.145 6.187" xml:space="preserve">
<polygon fill="#FFFFFF" points="7.145,0 3.572,6.187 0,0 "/>
</svg>

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

BIN
img/button icons/attach.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="13.736px" height="13.708px" viewBox="0 0 13.736 13.708" enable-background="new 0 0 13.736 13.708" xml:space="preserve">
<g>
<path fill="#FFFFFF" d="M13.629,2.874c-0.062-0.255-0.149-0.494-0.256-0.716c-0.188-0.388-0.438-0.719-0.718-0.992
c-0.21-0.206-0.438-0.382-0.672-0.527c-0.353-0.221-0.72-0.377-1.078-0.481c-0.179-0.053-0.355-0.091-0.528-0.118
C10.204,0.014,10.036,0,9.872,0C9.446,0,9.068,0.061,8.744,0.156C8.501,0.228,8.29,0.317,8.107,0.413
C7.835,0.558,7.632,0.713,7.49,0.84C7.396,0.926,7.33,0.996,7.291,1.043L1.279,6.974L1.278,6.977
C1.12,7.134,0.962,7.315,0.812,7.523C0.587,7.834,0.383,8.206,0.235,8.636C0.089,9.066,0,9.556,0,10.099
c0.001,0.216,0.028,0.432,0.079,0.645c0.088,0.371,0.246,0.734,0.461,1.075c0.161,0.256,0.354,0.5,0.578,0.724
c0.336,0.336,0.743,0.626,1.211,0.834c0.233,0.104,0.482,0.187,0.743,0.244c0.262,0.057,0.536,0.088,0.82,0.088
c0.27,0,0.529-0.042,0.774-0.108c0.214-0.06,0.418-0.138,0.609-0.229c0.338-0.16,0.642-0.357,0.914-0.562
c0.406-0.306,0.738-0.629,0.976-0.878c0.235-0.25,0.374-0.426,0.384-0.439L6.44,10.622l0,0l0.51,0.399l-0.51-0.4l0.453,0.356
l-0.452-0.356H6.44l0,0v0.001l0,0c-0.002,0.004-0.093,0.116-0.246,0.282c-0.116,0.126-0.269,0.281-0.447,0.441
c-0.133,0.121-0.28,0.245-0.437,0.358c-0.233,0.175-0.485,0.328-0.73,0.433c-0.123,0.054-0.243,0.094-0.359,0.121
c-0.115,0.027-0.225,0.04-0.328,0.04c-0.185,0-0.36-0.021-0.527-0.058c-0.293-0.063-0.561-0.182-0.803-0.336
c-0.181-0.115-0.346-0.253-0.49-0.403c-0.217-0.227-0.387-0.481-0.499-0.73c-0.056-0.125-0.098-0.246-0.124-0.36
S1.41,10.19,1.41,10.099c0-0.26,0.028-0.493,0.075-0.705C1.557,9.077,1.671,8.806,1.81,8.571c0.138-0.234,0.302-0.433,0.466-0.597
l6.065-5.986l0.023-0.03c0.009-0.01,0.042-0.048,0.104-0.1c0.096-0.08,0.252-0.191,0.48-0.282C9.063,1.53,9.195,1.489,9.348,1.459
s0.326-0.048,0.524-0.048c0.1,0,0.219,0.01,0.348,0.032c0.226,0.038,0.48,0.116,0.727,0.232c0.186,0.089,0.366,0.198,0.53,0.329
c0.124,0.097,0.238,0.207,0.34,0.327c0.152,0.182,0.277,0.388,0.366,0.629c0.09,0.241,0.143,0.518,0.144,0.848
c0,0.151-0.02,0.297-0.053,0.436c-0.053,0.21-0.139,0.407-0.248,0.59s-0.241,0.348-0.376,0.486l-0.001,0.002L8.609,8.496
C8.604,8.5,8.586,8.519,8.553,8.542C8.501,8.579,8.418,8.63,8.306,8.671C8.194,8.712,8.052,8.744,7.869,8.744
c-0.051,0-0.098-0.006-0.141-0.016c-0.075-0.017-0.14-0.047-0.202-0.09c-0.046-0.03-0.089-0.071-0.13-0.118
C7.336,8.449,7.284,8.359,7.248,8.257C7.213,8.155,7.192,8.043,7.192,7.926c0-0.111,0.021-0.216,0.055-0.314
c0.024-0.075,0.058-0.145,0.094-0.207C7.396,7.31,7.455,7.231,7.498,7.183C7.52,7.156,7.537,7.14,7.546,7.13
C7.55,7.127,7.553,7.125,7.554,7.123l0.008-0.008l2.355-2.326L8.925,3.786L6.593,6.09c-0.025,0.024-0.072,0.07-0.13,0.134
c-0.115,0.128-0.278,0.334-0.42,0.618C5.973,6.983,5.907,7.146,5.86,7.328C5.813,7.51,5.781,7.711,5.781,7.926
c0.001,0.281,0.05,0.557,0.143,0.814c0.069,0.193,0.164,0.376,0.282,0.545c0.09,0.126,0.193,0.243,0.31,0.349
c0.176,0.158,0.38,0.289,0.609,0.381c0.229,0.091,0.482,0.141,0.744,0.141c0.273,0,0.521-0.04,0.735-0.1
c0.161-0.045,0.304-0.103,0.427-0.162C9.217,9.803,9.359,9.704,9.46,9.623c0.09-0.07,0.146-0.128,0.174-0.156l3.03-3.166
c0.268-0.277,0.53-0.623,0.732-1.041c0.101-0.209,0.186-0.436,0.245-0.679c0.061-0.242,0.095-0.503,0.095-0.772
C13.736,3.479,13.699,3.167,13.629,2.874z M8.362,1.96L8.362,1.96L8.362,1.96L8.362,1.96z"/>
<polygon fill="#FFFFFF" points="12.666,6.299 12.664,6.301 12.664,6.301 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

BIN
img/button icons/call.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

10
img/button icons/call.svg Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="21.222px" height="19.66px" viewBox="0 0 21.222 19.66" enable-background="new 0 0 21.222 19.66" xml:space="preserve">
<path fill="#FFFFFF" d="M16.656,0.009c0,0-0.501-0.112-0.723,0.473l-1.782,4.204c0,0-0.334,0.611,0.167,1.196l1.28,1.67
c0,0,0.473,0.446,0.111,0.975c-0.362,0.528-2.255,4.036-7.154,6.235c0,0-0.611,0.417-1.056-0.195
c-0.446-0.612-1.811-2.839-2.674-2.365l-4.509,2.115c0,0-0.39,0.027-0.306,0.835C0.095,15.958-0.017,19.66,5.3,19.66
c6.348,0,15.922-9.061,15.922-14.809c0,0,0-4.843-2.979-4.843H16.656z"/>
</svg>

After

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="12.608px" height="10.029px" viewBox="0 0 12.608 10.029" enable-background="new 0 0 12.608 10.029" xml:space="preserve">
<polygon fill="#FFFFFF" points="4.24,6.707 1.661,4.127 0,5.789 4.24,10.029 4.241,10.029 5.902,8.369 12.608,1.662 10.946,0 "/>
</svg>

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="10.664px" height="11.333px" viewBox="0 0 10.664 11.333" enable-background="new 0 0 10.664 11.333" xml:space="preserve">
<path fill="#FFFFFF" d="M5.332,11.333C2.387,11.332,0.002,8.947,0,6.001h1.582C1.582,7.041,2,7.972,2.68,8.654
c0.682,0.68,1.613,1.098,2.652,1.098s1.973-0.418,2.654-1.098c0.68-0.682,1.098-1.613,1.098-2.652h1.58
C10.664,8.947,8.277,11.332,5.332,11.333z M3.75,1.5c0,0.826-0.672,1.5-1.5,1.5s-1.5-0.674-1.5-1.5c0-0.83,0.672-1.5,1.5-1.5
S3.75,0.669,3.75,1.5z M9.75,1.5c0,0.826-0.672,1.5-1.5,1.5s-1.5-0.674-1.5-1.5c0-0.83,0.672-1.5,1.5-1.5S9.75,0.669,9.75,1.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
img/button icons/no.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

8
img/button icons/no.svg Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="10.086px" height="10.087px" viewBox="0 0 10.086 10.087" enable-background="new 0 0 10.086 10.087" xml:space="preserve">
<polygon fill="#FFFFFF" points="10.086,1.692 8.395,0 5.043,3.351 1.693,0.001 0,1.693 3.35,5.043 0,8.394 1.693,10.086
5.043,6.736 8.395,10.087 10.086,8.394 6.734,5.043 "/>
</svg>

After

Width:  |  Height:  |  Size: 668 B

BIN
img/button icons/no_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

BIN
img/button icons/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="8.4px" height="9.4px" viewBox="0 0 8.4 9.4" enable-background="new 0 0 8.4 9.4" xml:space="preserve">
<g>
<rect y="0.002" fill="#FFFFFF" width="2.395" height="9.398"/>
<rect x="6.007" fill="#FFFFFF" width="2.395" height="9.4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="19.509px" height="23.089px" viewBox="0 0 19.509 23.089" enable-background="new 0 0 19.509 23.089" xml:space="preserve">
<path fill="#FFFFFF" d="M3.663,2.025C2.146,3.105,0.95,4.664,0.417,6.458c-0.68,2.182-0.537,4.631,0.494,6.682
c1.055,2.143,3.014,3.775,5.248,4.58c1.865,0.723,3.939,0.826,5.881,0.365c-0.232,1.795-1.008,3.461-1.922,5.004
c2.865-1.863,5.223-4.416,7.145-7.227c1.414-2.119,2.354-4.643,2.236-7.219c-0.104-2.264-1.211-4.419-2.852-5.96
c-1.777-1.665-4.189-2.613-6.619-2.677C7.757-0.078,5.485,0.677,3.663,2.025z"/>
</svg>

After

Width:  |  Height:  |  Size: 902 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

BIN
img/button icons/video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="22.625px" height="14.396px" viewBox="0 0 22.625 14.396" enable-background="new 0 0 22.625 14.396" xml:space="preserve">
<g>
<path fill="#FFFFFF" d="M8.845,2c0-1.1,0.9-2,2-2h9.78c1.1,0,2,0.9,2,2v10.396c0,1.1-0.9,2-2,2h-9.78c-1.1,0-2-0.9-2-2V2z"/>
<polygon fill="#FFFFFF" points="0,12.916 7.509,8.581 7.509,5.814 0,1.48 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

BIN
img/button icons/yes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
img/button icons/yes_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="15.884px" height="15.883px" viewBox="0 0 15.884 15.883" enable-background="new 0 0 15.884 15.883" xml:space="preserve">
<polygon fill="#FFFFFF" points="9.563,6.319 9.563,0 6.321,0 6.321,6.321 0,6.321 0,9.563 6.321,9.563 6.321,15.883 9.563,15.883
9.563,9.563 15.884,9.563 15.884,6.319 "/>
</svg>

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="15.402px" height="15.918px" viewBox="0 0 15.402 15.918" enable-background="new 0 0 15.402 15.918" xml:space="preserve">
<g>
<path fill="#FFFFFF" d="M9.429,11.411c4.71,1.681,3.195,4.507,3.195,4.507H2.666c0,0-0.773-3.161,3.465-4.507
C6.131,11.411,7.813,12.823,9.429,11.411z"/>
<path fill="#FFFFFF" d="M4.92,6.682c0,0,0.27,3.938,2.793,3.938s2.826-3.668,2.826-3.668s0.101-3.194-2.826-3.194
S4.92,6.682,4.92,6.682z"/>
<path fill="#FFFFFF" d="M3.359,7.276c-0.005-0.092-0.016-0.52,0.093-1.082c-0.521-0.1-0.841-0.336-0.841-0.336
c-3.15,1.045-2.569,3.23-2.569,3.23h3.651C3.428,8.178,3.368,7.413,3.361,7.313L3.359,7.276z"/>
<path fill="#FFFFFF" d="M12.1,7.522v0.041l-0.004,0.04c-0.035,0.427-0.127,0.944-0.29,1.485h3.554c0,0,0.583-2.186-2.567-3.23
c0,0-0.319,0.234-0.835,0.335C12.111,6.877,12.104,7.414,12.1,7.522z"/>
<path fill="#FFFFFF" d="M5.904,2.525C5.923,2.403,5.93,2.328,5.93,2.328S6.002,0,3.88,0S1.855,2.131,1.855,2.131
s0.18,2.606,1.781,2.85c0.176-0.513,0.461-1.063,0.922-1.545C4.859,3.121,5.297,2.774,5.904,2.525z"/>
<path fill="#FFFFFF" d="M11.758,4.99c1.664-0.17,1.869-2.662,1.869-2.662S13.699,0,11.576,0C9.455,0,9.553,2.131,9.553,2.131
s0.013,0.183,0.061,0.454c0.519,0.238,0.905,0.548,1.18,0.832C11.262,3.901,11.564,4.459,11.758,4.99z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="15.615px" height="15.616px" viewBox="0 0 15.615 15.616" enable-background="new 0 0 15.615 15.616" xml:space="preserve">
<path fill="#FFFFFF" d="M15.615,7.034c0-0.197-0.163-0.358-0.358-0.358h-0.66c-0.199,0-0.407-0.154-0.462-0.345l-0.896-2.092
c-0.095-0.171-0.064-0.425,0.075-0.567l0.446-0.445c0.14-0.14,0.14-0.367,0-0.507l-1.073-1.074c-0.139-0.14-0.367-0.14-0.51,0
L11.694,2.13c-0.142,0.142-0.399,0.18-0.574,0.09L9.3,1.49c-0.19-0.053-0.348-0.259-0.348-0.458V0.357C8.952,0.162,8.791,0,8.596,0
h-1.52C6.879,0,6.717,0.162,6.717,0.357v0.675c0,0.199-0.156,0.406-0.344,0.465L4.263,2.4c-0.17,0.099-0.422,0.066-0.563-0.072
L3.225,1.854c-0.138-0.139-0.367-0.139-0.506,0L1.645,2.929c-0.141,0.14-0.141,0.367,0,0.507l0.515,0.519
c0.143,0.14,0.181,0.396,0.089,0.57L1.531,6.33C1.478,6.521,1.272,6.676,1.073,6.676H0.357C0.16,6.676,0,6.837,0,7.034v1.519
c0,0.198,0.16,0.36,0.357,0.36h0.716c0.199,0,0.407,0.154,0.464,0.344l0.882,2.076c0.101,0.171,0.065,0.425-0.072,0.564L1.853,12.39
c-0.139,0.14-0.139,0.367,0,0.507l1.076,1.074c0.14,0.14,0.368,0.14,0.505,0l0.528-0.525c0.141-0.14,0.394-0.177,0.57-0.083
l1.841,0.734c0.188,0.055,0.344,0.262,0.344,0.458v0.702c0,0.198,0.162,0.359,0.359,0.359h1.52c0.195,0,0.356-0.161,0.356-0.359
v-0.702c0-0.196,0.156-0.403,0.348-0.461l2.059-0.874c0.172-0.097,0.422-0.063,0.564,0.075l0.465,0.468
c0.139,0.139,0.368,0.139,0.507,0l1.076-1.076c0.139-0.139,0.139-0.368,0-0.506l-0.497-0.495c-0.137-0.14-0.176-0.396-0.082-0.57
l0.746-1.857c0.052-0.19,0.26-0.345,0.459-0.347h0.66c0.195,0,0.358-0.161,0.358-0.358V7.034z M7.808,10.36
c-1.411,0-2.552-1.143-2.552-2.553s1.141-2.552,2.552-2.552c1.409,0,2.554,1.142,2.554,2.552S9.217,10.36,7.808,10.36z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="15.604px" height="15.604px" viewBox="0 0 15.604 15.604" enable-background="new 0 0 15.604 15.604" xml:space="preserve">
<g>
<path fill="#FFFFFF" d="M6.303,15.588c-0.104-0.018-0.24-0.057-0.317-0.15s-0.149-0.301-0.119-0.604L6.482,8.78
c0.023-0.23,0.115-0.492,0.297-0.461c0.133,0.023,0.34,0.164,0.552,0.375l1.4,1.4l4.144-4.145c0.076-0.076,0.191-0.105,0.321-0.084
c0.131,0.023,0.263,0.096,0.37,0.203l1.828,1.828c0.223,0.223,0.277,0.533,0.117,0.691l-4.143,4.143l1.4,1.4
c0.293,0.293,0.387,0.508,0.377,0.602s-0.138,0.215-0.463,0.248l-6.055,0.615C6.513,15.608,6.402,15.606,6.303,15.588L6.303,15.588
z"/>
<path fill="#FFFFFF" d="M9.303,0.015c0.102,0.018,0.24,0.057,0.316,0.15C9.696,0.259,9.77,0.467,9.738,0.77L9.122,6.825
C9.1,7.055,9.006,7.315,8.825,7.284C8.691,7.26,8.484,7.122,8.273,6.911l-1.4-1.4L2.729,9.655C2.652,9.731,2.539,9.76,2.408,9.737
C2.277,9.715,2.146,9.645,2.037,9.536L0.209,7.708c-0.223-0.223-0.275-0.533-0.117-0.693l4.143-4.143l-1.4-1.4
c-0.292-0.291-0.385-0.505-0.375-0.6c0.01-0.094,0.137-0.215,0.462-0.247l6.056-0.617C9.092-0.005,9.201-0.002,9.303,0.015
L9.303,0.015z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

BIN
img/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

BIN
img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
img/status/dot_away.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

36
img/status/dot_away.svg Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="10.057px" height="10.055px" viewBox="0 0 10.057 10.055" enable-background="new 0 0 10.057 10.055" xml:space="preserve">
<path fill="#C94F4F" d="M5.029,0C2.252,0,0,2.25,0,5.027s2.252,5.027,5.029,5.027c2.775,0,5.027-2.25,5.027-5.027S7.805,0,5.029,0z
M5.029,8.641c-1.996,0-3.613-1.617-3.613-3.613c0-1.033,0.434-1.967,1.131-2.625C2.549,2.4,2.553,2.396,2.555,2.395
c0.033-0.031,0.066-0.061,0.098-0.09c0.008-0.004,0.014-0.01,0.02-0.016C2.705,2.262,2.736,2.234,2.77,2.207
c0.006-0.004,0.014-0.01,0.02-0.014C2.93,2.082,3.08,1.98,3.238,1.891C3.25,1.883,3.264,1.875,3.277,1.867
C3.307,1.85,3.336,1.834,3.367,1.82c0.018-0.012,0.037-0.02,0.055-0.029C3.449,1.777,3.479,1.762,3.508,1.75
C3.529,1.74,3.549,1.73,3.57,1.721c0.027-0.012,0.057-0.023,0.084-0.035c0.023-0.01,0.047-0.02,0.07-0.027
c0.027-0.012,0.055-0.021,0.08-0.031c0.029-0.01,0.057-0.02,0.086-0.029c0.035-0.012,0.07-0.023,0.105-0.033
c0.039-0.012,0.078-0.023,0.115-0.033c0.023-0.006,0.047-0.01,0.068-0.016C4.213,1.508,4.244,1.5,4.275,1.494
c0.023-0.006,0.049-0.012,0.072-0.016c0.031-0.006,0.064-0.012,0.096-0.016c0.023-0.004,0.049-0.01,0.072-0.012
c0.033-0.004,0.066-0.01,0.1-0.014c0.023-0.002,0.047-0.004,0.068-0.006c0.037-0.006,0.076-0.008,0.113-0.01
C4.816,1.42,4.836,1.42,4.855,1.418c0.057-0.002,0.113-0.004,0.17-0.004c0,0,0.002,0,0.004,0l0,0l0,0
c0.059,0,0.115,0.002,0.174,0.004C5.221,1.42,5.238,1.42,5.256,1.422c0.041,0.002,0.08,0.004,0.121,0.01
c0.02,0.002,0.039,0.004,0.061,0.006c0.037,0.004,0.074,0.01,0.109,0.014c0.021,0.002,0.043,0.008,0.063,0.01
c0.037,0.006,0.074,0.014,0.109,0.02c0.02,0.004,0.039,0.008,0.059,0.012C5.818,1.5,5.857,1.51,5.896,1.52
c0.016,0.004,0.029,0.008,0.045,0.01c0.055,0.016,0.107,0.029,0.162,0.047c0,0,0,0.002,0.002,0.002
C6.158,1.594,6.209,1.611,6.26,1.629c0.016,0.008,0.031,0.014,0.047,0.018C6.344,1.66,6.379,1.676,6.414,1.689
c0.018,0.008,0.037,0.016,0.055,0.023c0.033,0.016,0.064,0.029,0.096,0.045c0.02,0.008,0.039,0.016,0.059,0.027
c0.029,0.014,0.061,0.029,0.092,0.047C6.732,1.84,6.75,1.85,6.77,1.859c0.031,0.018,0.063,0.037,0.096,0.057
C6.881,1.924,6.895,1.934,6.91,1.941C6.957,1.971,7,1.998,7.045,2.027c0.014,0.01,0.027,0.02,0.039,0.029
C7.115,2.078,7.146,2.1,7.176,2.123c0.02,0.014,0.039,0.027,0.057,0.043c0.023,0.018,0.047,0.035,0.07,0.053
c0.021,0.018,0.041,0.035,0.061,0.053s0.041,0.033,0.063,0.051c0.02,0.02,0.039,0.037,0.061,0.057
c0.018,0.018,0.037,0.035,0.055,0.053c0.021,0.02,0.041,0.039,0.061,0.061C7.619,2.51,7.635,2.527,7.652,2.543
c0.02,0.023,0.041,0.043,0.061,0.066c0.004,0.004,0.006,0.006,0.01,0.01c0.053,0.059,0.102,0.119,0.152,0.184
c0.01,0.014,0.021,0.027,0.033,0.041c0.02,0.027,0.039,0.055,0.059,0.082C7.979,2.941,7.99,2.957,8,2.973
C8.02,3,8.039,3.029,8.057,3.057c0.01,0.016,0.021,0.031,0.031,0.049c0.018,0.027,0.037,0.057,0.055,0.088
C8.15,3.207,8.158,3.223,8.166,3.236c0.02,0.033,0.037,0.064,0.055,0.098c0.006,0.01,0.01,0.02,0.016,0.029
C8.297,3.48,8.352,3.602,8.4,3.727c0.004,0.008,0.006,0.018,0.01,0.025c0.014,0.037,0.027,0.074,0.039,0.111
c0.006,0.014,0.01,0.029,0.016,0.043c0.01,0.035,0.021,0.072,0.031,0.107c0.006,0.016,0.01,0.031,0.014,0.047
c0.01,0.037,0.02,0.072,0.029,0.109c0.002,0.004,0.002,0.01,0.004,0.016l0,0c0.064,0.27,0.1,0.551,0.1,0.842
C8.643,7.023,7.023,8.641,5.029,8.641z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
img/status/dot_away_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16.814px" height="16.816px" viewBox="0 0 16.814 16.816" enable-background="new 0 0 16.814 16.816" xml:space="preserve">
<path fill="#C94F4F" d="M8.408,3.381c-2.777,0-5.029,2.25-5.029,5.027s2.252,5.027,5.029,5.027c2.775,0,5.027-2.25,5.027-5.027
S11.184,3.381,8.408,3.381z M8.408,12.021c-1.996,0-3.613-1.617-3.613-3.613c0-1.033,0.434-1.967,1.131-2.625
c0.002-0.002,0.006-0.006,0.008-0.008C5.967,5.744,6,5.715,6.031,5.686c0.008-0.004,0.014-0.01,0.02-0.016
c0.033-0.027,0.064-0.055,0.098-0.082c0.006-0.004,0.014-0.01,0.02-0.014c0.141-0.111,0.291-0.213,0.449-0.303
c0.012-0.008,0.025-0.016,0.039-0.023c0.029-0.018,0.059-0.033,0.09-0.047c0.018-0.012,0.037-0.02,0.055-0.029
c0.027-0.014,0.057-0.029,0.086-0.041c0.021-0.01,0.041-0.02,0.063-0.029C6.977,5.09,7.006,5.078,7.033,5.066
c0.023-0.01,0.047-0.02,0.07-0.027c0.027-0.012,0.055-0.021,0.08-0.031c0.029-0.01,0.057-0.02,0.086-0.029
c0.035-0.012,0.07-0.023,0.105-0.033C7.414,4.934,7.453,4.922,7.49,4.912c0.023-0.006,0.047-0.01,0.068-0.016
c0.033-0.008,0.064-0.016,0.096-0.021c0.023-0.006,0.049-0.012,0.072-0.016c0.031-0.006,0.064-0.012,0.096-0.016
c0.023-0.004,0.049-0.01,0.072-0.012c0.033-0.004,0.066-0.01,0.1-0.014c0.023-0.002,0.047-0.004,0.068-0.006
C8.1,4.807,8.139,4.805,8.176,4.803c0.02-0.002,0.039-0.002,0.059-0.004c0.057-0.002,0.113-0.004,0.17-0.004c0,0,0.002,0,0.004,0
l0,0l0,0c0.059,0,0.115,0.002,0.174,0.004C8.6,4.801,8.617,4.801,8.635,4.803c0.041,0.002,0.08,0.004,0.121,0.01
c0.02,0.002,0.039,0.004,0.061,0.006c0.037,0.004,0.074,0.01,0.109,0.014c0.021,0.002,0.043,0.008,0.063,0.01
c0.037,0.006,0.074,0.014,0.109,0.02c0.02,0.004,0.039,0.008,0.059,0.012C9.197,4.881,9.236,4.891,9.275,4.9
C9.291,4.904,9.305,4.908,9.32,4.91c0.055,0.016,0.107,0.029,0.162,0.047c0,0,0,0.002,0.002,0.002
C9.537,4.975,9.588,4.992,9.639,5.01C9.654,5.018,9.67,5.023,9.686,5.027C9.723,5.041,9.758,5.057,9.793,5.07
C9.811,5.078,9.83,5.086,9.848,5.094c0.033,0.016,0.064,0.029,0.096,0.045c0.02,0.008,0.039,0.016,0.059,0.027
c0.029,0.014,0.061,0.029,0.092,0.047c0.018,0.008,0.035,0.018,0.055,0.027c0.031,0.018,0.063,0.037,0.096,0.057
c0.016,0.008,0.029,0.018,0.045,0.025c0.047,0.029,0.09,0.057,0.135,0.086c0.014,0.01,0.027,0.02,0.039,0.029
c0.031,0.021,0.063,0.043,0.092,0.066c0.02,0.014,0.039,0.027,0.057,0.043c0.023,0.018,0.047,0.035,0.07,0.053
c0.021,0.018,0.041,0.035,0.061,0.053s0.041,0.033,0.063,0.051c0.02,0.02,0.039,0.037,0.061,0.057
c0.018,0.018,0.037,0.035,0.055,0.053c0.021,0.02,0.041,0.039,0.061,0.061c0.018,0.018,0.033,0.035,0.051,0.051
c0.02,0.023,0.041,0.043,0.061,0.066c0.004,0.004,0.006,0.006,0.01,0.01c0.053,0.059,0.102,0.119,0.152,0.184
c0.01,0.014,0.021,0.027,0.033,0.041c0.02,0.027,0.039,0.055,0.059,0.082c0.012,0.016,0.023,0.031,0.033,0.047
c0.02,0.027,0.039,0.057,0.057,0.084c0.01,0.016,0.021,0.031,0.031,0.049c0.018,0.027,0.037,0.057,0.055,0.088
c0.008,0.014,0.016,0.029,0.023,0.043c0.02,0.033,0.037,0.064,0.055,0.098c0.006,0.01,0.01,0.02,0.016,0.029
c0.061,0.117,0.115,0.238,0.164,0.363c0.004,0.008,0.006,0.018,0.01,0.025c0.014,0.037,0.027,0.074,0.039,0.111
c0.006,0.014,0.01,0.029,0.016,0.043c0.01,0.035,0.021,0.072,0.031,0.107c0.006,0.016,0.01,0.031,0.014,0.047
c0.01,0.037,0.02,0.072,0.029,0.109c0.002,0.004,0.002,0.01,0.004,0.016l0,0c0.064,0.27,0.1,0.551,0.1,0.842
C12.021,10.404,10.402,12.021,8.408,12.021z"/>
<path fill="#C94F4F" d="M8.408,0C3.765,0.001,0,3.766,0,8.408c0,4.644,3.765,8.407,8.408,8.408c4.644-0.001,8.405-3.766,8.406-8.408
C16.813,3.766,13.052,0.001,8.408,0z M13.292,13.293c-1.253,1.251-2.975,2.023-4.884,2.023s-3.632-0.772-4.885-2.023
C2.272,12.04,1.5,10.317,1.5,8.408c0-1.909,0.772-3.632,2.023-4.884C4.776,2.272,6.499,1.501,8.408,1.5
c1.909,0.001,3.631,0.772,4.884,2.023c1.251,1.253,2.022,2.976,2.022,4.885S14.543,12.04,13.292,13.293z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

BIN
img/status/dot_idle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

8
img/status/dot_idle.svg Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="10.059px" height="10.055px" viewBox="0 0 10.059 10.055" enable-background="new 0 0 10.059 10.055" xml:space="preserve">
<path fill="#CEBF43" d="M5.029,0C2.252,0,0,2.25,0,5.027s2.252,5.027,5.029,5.027s5.029-2.25,5.029-5.027S7.807,0,5.029,0z
M1.416,5.027c0-1.996,1.617-3.613,3.613-3.613s3.613,1.617,3.613,3.613H1.416z"/>
</svg>

After

Width:  |  Height:  |  Size: 696 B

BIN
img/status/dot_idle_2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16.814px" height="16.817px" viewBox="0 0 16.814 16.817" enable-background="new 0 0 16.814 16.817" xml:space="preserve">
<g>
<path fill="#CEBF43" d="M8.441,3.475c-2.777,0-5.029,2.25-5.029,5.028c0,2.777,2.252,5.027,5.029,5.027s5.029-2.25,5.029-5.027
C13.47,5.725,11.218,3.475,8.441,3.475z M4.828,8.502c0-1.997,1.617-3.614,3.613-3.614s3.613,1.617,3.613,3.614H4.828z"/>
<path fill="#CEBF43" d="M15.314,8.409c0,1.909-0.771,3.632-2.022,4.885c-1.253,1.251-2.975,2.023-4.884,2.023
s-3.632-0.772-4.884-2.023C2.272,12.041,1.5,10.318,1.5,8.409c0-1.91,0.772-3.632,2.024-4.885C4.776,2.272,6.499,1.5,8.408,1.5
c1.909,0,3.631,0.772,4.884,2.024C14.542,4.776,15.314,6.499,15.314,8.409h1.5C16.813,3.765,13.051,0,8.408,0
C3.764,0,0,3.765,0,8.409c0,4.644,3.764,8.407,8.408,8.408c4.644-0.001,8.405-3.766,8.406-8.408H15.314z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

BIN
img/status/dot_online.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="10px" height="10px" viewBox="0 0 10 10" enable-background="new 0 0 10 10" xml:space="preserve">
<path fill="#6FC062" d="M10,5c0,2.761-2.24,5-5,5c-2.762,0-5-2.239-5-5s2.238-5,5-5C7.759,0,10,2.239,10,5"/>
</svg>

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16.814px" height="16.816px" viewBox="0 0 16.814 16.816" enable-background="new 0 0 16.814 16.816" xml:space="preserve">
<g>
<path fill="#6FC062" d="M13.408,8.408c0,2.761-2.24,5-5,5c-2.762,0-5-2.239-5-5c0-2.761,2.238-5,5-5
C11.168,3.408,13.408,5.646,13.408,8.408"/>
<path fill="#6FC062" d="M15.314,8.408c0,1.909-0.771,3.632-2.022,4.885c-1.253,1.251-2.975,2.023-4.884,2.023
s-3.632-0.772-4.884-2.023C2.272,12.04,1.5,10.317,1.5,8.408c0-1.91,0.772-3.632,2.023-4.884C4.776,2.272,6.499,1.5,8.408,1.5
c1.909,0,3.631,0.772,4.884,2.023C14.543,4.776,15.314,6.498,15.314,8.408h1.5C16.813,3.764,13.052,0,8.408,0
C3.765,0,0,3.764,0,8.408c0,4.644,3.765,8.407,8.408,8.408c4.644-0.001,8.405-3.766,8.406-8.408H15.314z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

17
main.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setApplicationName("Toxgui");
a.setOrganizationName("Tox");
Widget* w = Widget::getInstance();
w->show();
int errorcode = a.exec();
delete w;
return errorcode;
}

5
res.qrc Normal file
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/conf">
<file alias="settings.ini">res/settings.ini</file>
</qresource>
</RCC>

55
res/settings.ini Normal file
View File

@ -0,0 +1,55 @@
[DHT%20Server]
dhtServerList\size=13
dhtServerList\1\name=benwaffle
dhtServerList\1\userId=8E6667FF967EA30B3DC3DB57A4B533152476E7AAE090158B9C2D9DF58ECC7B78
dhtServerList\1\address=192.3.30.132
dhtServerList\1\port=33445
dhtServerList\2\name=zlacki RU #1
dhtServerList\2\userId=D59F99384592DE4C8AB9D534D5197DB90F4755CC9E975ED0C565E18468A1445B
dhtServerList\2\address=31.192.105.19
dhtServerList\2\port=33445
dhtServerList\3\name=aitjcize
dhtServerList\3\userId=7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
dhtServerList\3\address=54.199.139.199
dhtServerList\3\port=33445
dhtServerList\4\name=zlacki US
dhtServerList\4\userId=9430A83211A7AD1C294711D069D587028CA0B4782FA43CB9B30008247A43C944
dhtServerList\4\address=69.42.220.58
dhtServerList\4\port=33445
dhtServerList\5\name=platos
dhtServerList\5\userId=B24E2FB924AE66D023FE1E42A2EE3B432010206F751A2FFD3E297383ACF1572E
dhtServerList\5\address=66.175.223.88
dhtServerList\5\port=33445
dhtServerList\6\name=stqism
dhtServerList\6\userId=951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
dhtServerList\6\address=192.254.75.98
dhtServerList\6\port=33445
dhtServerList\7\name=nurupo
dhtServerList\7\userId=F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
dhtServerList\7\address=192.210.149.121
dhtServerList\7\port=33445
dhtServerList\8\name=JmanGuy
dhtServerList\8\userId=20C797E098701A848B07D0384222416B0EFB60D08CECB925B860CAEAAB572067
dhtServerList\8\address=66.74.15.98
dhtServerList\8\port=33445
dhtServerList\9\name=zlacki NL
dhtServerList\9\userId=CC2B02636A2ADBC2871D6EC57C5E9589D4FD5E6F98A14743A4B949914CF26D39
dhtServerList\9\address=5.39.218.35
dhtServerList\9\port=33445
dhtServerList\10\name=zlacki RU #2
dhtServerList\10\userId=AE27E1E72ADA3DC423C60EEBACA241456174048BE76A283B41AD32D953182D49
dhtServerList\10\address=193.107.16.73
dhtServerList\10\port=33445
dhtServerList\11\name=stal
dhtServerList\11\userId=A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
dhtServerList\11\address=23.226.230.47
dhtServerList\11\port=33445
dhtServerList\12\name=sonOfRa
dhtServerList\12\userId=DDCF277B8B45B0D357D78AA4E201766932DF6CDB7179FC7D5C9F3C2E8E705326
dhtServerList\12\address=144.76.60.215
dhtServerList\12\port=33445
dhtServerList\13\name=anonymous
dhtServerList\13\userId=5CD7EB176C19A2FD840406CD56177BB8E75587BB366F7BB3004B19E3EDC04143
dhtServerList\13\address=192.184.81.118
dhtServerList\13\port=33445

343
settings.cpp Normal file
View File

@ -0,0 +1,343 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 "settings.h"
#include <QApplication>
#include <QDir>
#include <QFile>
#include <QSettings>
#include <QStandardPaths>
const QString Settings::FILENAME = "settings.ini";
Settings::Settings() :
loaded(false)
{
load();
}
Settings::~Settings()
{
save();
}
Settings& Settings::getInstance()
{
static Settings settings;
return settings;
}
void Settings::load()
{
if (loaded) {
return;
}
QString filePath = getSettingsDirPath() + '/' + FILENAME;
//if no settings file exist -- use the default one
QFile file(filePath);
if (!file.exists()) {
filePath = ":/conf/" + FILENAME;
}
QSettings s(filePath, QSettings::IniFormat);
s.beginGroup("DHT Server");
int serverListSize = s.beginReadArray("dhtServerList");
for (int i = 0; i < serverListSize; i ++) {
s.setArrayIndex(i);
DhtServer server;
server.name = s.value("name").toString();
server.userId = s.value("userId").toString();
server.address = s.value("address").toString();
server.port = s.value("port").toInt();
dhtServerList << server;
}
s.endArray();
s.endGroup();
//NOTE: uncomment when logging will be implemented
/*
s.beginGroup("Logging");
enableLogging = s.value("enableLogging", false).toBool();
encryptLogs = s.value("encryptLogs", true).toBool();
s.endGroup();
*/
s.beginGroup("General");
username = s.value("username", "My name").toString();
statusMessage = s.value("statusMessage", "My status").toString();
s.endGroup();
s.beginGroup("Widgets");
QList<QString> objectNames = s.childKeys();
for (const QString& name : objectNames) {
widgetSettings[name] = s.value(name).toByteArray();
}
s.endGroup();
s.beginGroup("GUI");
enableSmoothAnimation = s.value("smoothAnimation", true).toBool();
smileyPack = s.value("smileyPack").toByteArray();
customEmojiFont = s.value("customEmojiFont", true).toBool();
emojiFontFamily = s.value("emojiFontFamily", "DejaVu Sans").toString();
emojiFontPointSize = s.value("emojiFontPointSize", QApplication::font().pointSize()).toInt();
firstColumnHandlePos = s.value("firstColumnHandlePos", 50).toInt();
secondColumnHandlePosFromRight = s.value("secondColumnHandlePosFromRight", 50).toInt();
timestampFormat = s.value("timestampFormat", "hh:mm").toString();
minimizeOnClose = s.value("minimizeOnClose", false).toBool();
s.endGroup();
s.beginGroup("Privacy");
typingNotification = s.value("typingNotification", false).toBool();
s.endGroup();
loaded = true;
}
void Settings::save()
{
QString filePath = getSettingsDirPath() + '/' + FILENAME;
QSettings s(filePath, QSettings::IniFormat);
s.clear();
s.beginGroup("DHT Server");
s.beginWriteArray("dhtServerList", dhtServerList.size());
for (int i = 0; i < dhtServerList.size(); i ++) {
s.setArrayIndex(i);
s.setValue("name", dhtServerList[i].name);
s.setValue("userId", dhtServerList[i].userId);
s.setValue("address", dhtServerList[i].address);
s.setValue("port", dhtServerList[i].port);
}
s.endArray();
s.endGroup();
//NOTE: uncomment when logging will be implemented
/*
s.beginGroup("Logging");
s.setValue("storeLogs", enableLogging);
s.setValue("encryptLogs", encryptLogs);
s.endGroup();
*/
s.beginGroup("General");
s.setValue("username", username);
s.setValue("statusMessage", statusMessage);
s.endGroup();
s.beginGroup("Widgets");
const QList<QString> widgetNames = widgetSettings.keys();
for (const QString& name : widgetNames) {
s.setValue(name, widgetSettings.value(name));
}
s.endGroup();
s.beginGroup("GUI");
s.setValue("smoothAnimation", enableSmoothAnimation);
s.setValue("smileyPack", smileyPack);
s.setValue("customEmojiFont", customEmojiFont);
s.setValue("emojiFontFamily", emojiFontFamily);
s.setValue("emojiFontPointSize", emojiFontPointSize);
s.setValue("firstColumnHandlePos", firstColumnHandlePos);
s.setValue("secondColumnHandlePosFromRight", secondColumnHandlePosFromRight);
s.setValue("timestampFormat", timestampFormat);
s.setValue("minimizeOnClose", minimizeOnClose);
s.endGroup();
s.beginGroup("Privacy");
s.setValue("typingNotification", typingNotification);
s.endGroup();
}
QString Settings::getSettingsDirPath()
{
// workaround for https://bugreports.qt-project.org/browse/QTBUG-38845
#ifdef Q_OS_WIN
return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
#else
return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + '/' + qApp->organizationName() + '/' + qApp->applicationName();
#endif
}
const QList<Settings::DhtServer>& Settings::getDhtServerList() const
{
return dhtServerList;
}
void Settings::setDhtServerList(const QList<DhtServer>& newDhtServerList)
{
dhtServerList = newDhtServerList;
emit dhtServerListChanged();
}
QString Settings::getUsername() const
{
return username;
}
void Settings::setUsername(const QString& newUsername)
{
username = newUsername;
}
QString Settings::getStatusMessage() const
{
return statusMessage;
}
void Settings::setStatusMessage(const QString& newMessage)
{
statusMessage = newMessage;
}
bool Settings::getEnableLogging() const
{
return enableLogging;
}
void Settings::setEnableLogging(bool newValue)
{
enableLogging = newValue;
}
bool Settings::getEncryptLogs() const
{
return encryptLogs;
}
void Settings::setEncryptLogs(bool newValue)
{
encryptLogs = newValue;
}
void Settings::setWidgetData(const QString& uniqueName, const QByteArray& data)
{
widgetSettings[uniqueName] = data;
}
QByteArray Settings::getWidgetData(const QString& uniqueName) const
{
return widgetSettings.value(uniqueName);
}
bool Settings::isAnimationEnabled() const
{
return enableSmoothAnimation;
}
void Settings::setAnimationEnabled(bool newValue)
{
enableSmoothAnimation = newValue;
}
QByteArray Settings::getSmileyPack() const
{
return smileyPack;
}
void Settings::setSmileyPack(const QByteArray &value)
{
smileyPack = value;
emit smileyPackChanged();
}
bool Settings::isCurstomEmojiFont() const
{
return customEmojiFont;
}
void Settings::setCurstomEmojiFont(bool value)
{
customEmojiFont = value;
emit emojiFontChanged();
}
int Settings::getEmojiFontPointSize() const
{
return emojiFontPointSize;
}
void Settings::setEmojiFontPointSize(int value)
{
emojiFontPointSize = value;
emit emojiFontChanged();
}
int Settings::getFirstColumnHandlePos() const
{
return firstColumnHandlePos;
}
void Settings::setFirstColumnHandlePos(const int pos)
{
firstColumnHandlePos = pos;
}
int Settings::getSecondColumnHandlePosFromRight() const
{
return secondColumnHandlePosFromRight;
}
void Settings::setSecondColumnHandlePosFromRight(const int pos)
{
secondColumnHandlePosFromRight = pos;
}
const QString &Settings::getTimestampFormat() const
{
return timestampFormat;
}
void Settings::setTimestampFormat(const QString &format)
{
timestampFormat = format;
emit timestampFormatChanged();
}
QString Settings::getEmojiFontFamily() const
{
return emojiFontFamily;
}
void Settings::setEmojiFontFamily(const QString &value)
{
emojiFontFamily = value;
emit emojiFontChanged();
}
bool Settings::isMinimizeOnCloseEnabled() const
{
return minimizeOnClose;
}
void Settings::setMinimizeOnClose(bool newValue)
{
minimizeOnClose = newValue;
}
bool Settings::isTypingNotificationEnabled() const
{
return typingNotification;
}
void Settings::setTypingNotification(bool enabled)
{
typingNotification = enabled;
}

163
settings.h Normal file
View File

@ -0,0 +1,163 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program is free 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 SETTINGS_HPP
#define SETTINGS_HPP
#include <QHash>
#include <QMainWindow>
#include <QSplitter>
class Settings : public QObject
{
Q_OBJECT
public:
static Settings& getInstance();
~Settings();
void executeSettingsDialog(QWidget* parent);
static QString getSettingsDirPath();
struct DhtServer
{
QString name;
QString userId;
QString address;
int port;
};
const QList<DhtServer>& getDhtServerList() const;
void setDhtServerList(const QList<DhtServer>& newDhtServerList);
QString getUsername() const;
void setUsername(const QString& newUsername);
QString getStatusMessage() const;
void setStatusMessage(const QString& newMessage);
bool getEnableLogging() const;
void setEnableLogging(bool newValue);
bool getEncryptLogs() const;
void setEncryptLogs(bool newValue);
// 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,
// which have widget->saveX() and widget->loadX() methods.
QByteArray getWidgetData(const QString& uniqueName) const;
void setWidgetData(const QString& uniqueName, const QByteArray& data);
// Wrappers around getWidgetData() and setWidgetData()
// Assume widget has a unique objectName set
template <class T>
void restoreGeometryState(T* widget) const
{
widget->restoreGeometry(getWidgetData(widget->objectName() + "Geometry"));
widget->restoreState(getWidgetData(widget->objectName() + "State"));
}
template <class T>
void saveGeometryState(const T* widget)
{
setWidgetData(widget->objectName() + "Geometry", widget->saveGeometry());
setWidgetData(widget->objectName() + "State", widget->saveState());
}
bool isAnimationEnabled() const;
void setAnimationEnabled(bool newValue);
QByteArray getSmileyPack() const;
void setSmileyPack(const QByteArray &value);
bool isCurstomEmojiFont() const;
void setCurstomEmojiFont(bool value);
QString getEmojiFontFamily() const;
void setEmojiFontFamily(const QString &value);
int getEmojiFontPointSize() const;
void setEmojiFontPointSize(int value);
// ChatView
int getFirstColumnHandlePos() const;
void setFirstColumnHandlePos(const int pos);
int getSecondColumnHandlePosFromRight() const;
void setSecondColumnHandlePosFromRight(const int pos);
const QString &getTimestampFormat() const;
void setTimestampFormat(const QString &format);
bool isMinimizeOnCloseEnabled() const;
void setMinimizeOnClose(bool newValue);
// Privacy
bool isTypingNotificationEnabled() const;
void setTypingNotification(bool enabled);
private:
Settings();
Settings(Settings &settings) = delete;
Settings& operator=(const Settings&) = delete;
void save();
void load();
static const QString FILENAME;
bool loaded;
QList<DhtServer> dhtServerList;
int dhtServerId;
bool dontShowDhtDialog;
QString username;
QString statusMessage;
bool enableLogging;
bool encryptLogs;
QHash<QString, QByteArray> widgetSettings;
// GUI
bool enableSmoothAnimation;
QByteArray smileyPack;
bool customEmojiFont;
QString emojiFontFamily;
int emojiFontPointSize;
bool minimizeOnClose;
// ChatView
int firstColumnHandlePos;
int secondColumnHandlePosFromRight;
QString timestampFormat;
// Privacy
bool typingNotification;
signals:
//void dataChanged();
void dhtServerListChanged();
void logStorageOptsChanged();
void smileyPackChanged();
void emojiFontChanged();
void timestampFormatChanged();
};
#endif // SETTINGS_HPP

Some files were not shown because too many files have changed in this diff Show More