Browse Source

Initial commit

pull/2/head
Tux3 / Mlkj / !Lev.uXFMLA 11 years ago
commit
18cee41cfb
  1. 45
      addfriendform.cpp
  2. 35
      addfriendform.h
  3. 153
      chatform.cpp
  4. 57
      chatform.h
  5. 19
      chattextedit.cpp
  6. 20
      chattextedit.h
  7. 47
      copyableelidelabel.cpp
  8. 35
      copyableelidelabel.h
  9. 568
      core.cpp
  10. 216
      core.h
  11. 110
      editablelabelwidget.cpp
  12. 66
      editablelabelwidget.h
  13. 82
      elidelabel.cpp
  14. 50
      elidelabel.h
  15. 34
      esclineedit.cpp
  16. 36
      esclineedit.h
  17. 33
      friend.cpp
  18. 25
      friend.h
  19. 32
      friendlist.cpp
  20. 21
      friendlist.h
  21. 61
      friendrequestdialog.cpp
  22. 29
      friendrequestdialog.h
  23. 62
      friendwidget.cpp
  24. 28
      friendwidget.h
  25. 98
      group.cpp
  26. 37
      group.h
  27. 175
      groupchatform.cpp
  28. 56
      groupchatform.h
  29. 31
      grouplist.cpp
  30. 22
      grouplist.h
  31. 75
      groupwidget.cpp
  32. 29
      groupwidget.h
  33. BIN
      img/button icons/arrow_medgrey.png
  34. 7
      img/button icons/arrow_medgrey.svg
  35. BIN
      img/button icons/arrow_medgrey_2x.png
  36. BIN
      img/button icons/arrow_white.png
  37. 7
      img/button icons/arrow_white.svg
  38. BIN
      img/button icons/arrow_white_2x.png
  39. BIN
      img/button icons/attach.png
  40. 38
      img/button icons/attach.svg
  41. BIN
      img/button icons/attach_2x.png
  42. BIN
      img/button icons/call.png
  43. 10
      img/button icons/call.svg
  44. BIN
      img/button icons/call_2x.png
  45. 7
      img/button icons/check.svg
  46. BIN
      img/button icons/emoticon.png
  47. 10
      img/button icons/emoticon.svg
  48. BIN
      img/button icons/emoticon_2x.png
  49. BIN
      img/button icons/no.png
  50. 8
      img/button icons/no.svg
  51. BIN
      img/button icons/no_2x.png
  52. BIN
      img/button icons/pause.png
  53. 10
      img/button icons/pause.svg
  54. BIN
      img/button icons/pause_2x.png
  55. BIN
      img/button icons/sendmessage.png
  56. 10
      img/button icons/sendmessage.svg
  57. BIN
      img/button icons/sendmessage_2x.png
  58. BIN
      img/button icons/video.png
  59. 10
      img/button icons/video.svg
  60. BIN
      img/button icons/video_2x.png
  61. BIN
      img/button icons/yes.png
  62. BIN
      img/button icons/yes_2x.png
  63. BIN
      img/contact list icons/add.png
  64. 8
      img/contact list icons/add.svg
  65. BIN
      img/contact list icons/add_2x.png
  66. BIN
      img/contact list icons/contact.png
  67. BIN
      img/contact list icons/group.png
  68. 20
      img/contact list icons/group.svg
  69. BIN
      img/contact list icons/group_2x.png
  70. BIN
      img/contact list icons/settings.png
  71. 19
      img/contact list icons/settings.svg
  72. BIN
      img/contact list icons/settings_2x.png
  73. BIN
      img/contact list icons/transfer.png
  74. 18
      img/contact list icons/transfer.svg
  75. BIN
      img/contact list icons/transfer_2x.png
  76. BIN
      img/icon.ico
  77. BIN
      img/icon.png
  78. BIN
      img/status/dot_away.png
  79. 36
      img/status/dot_away.svg
  80. BIN
      img/status/dot_away_2x.png
  81. BIN
      img/status/dot_away_notification.png
  82. 40
      img/status/dot_away_notification.svg
  83. BIN
      img/status/dot_away_notification_2x.png
  84. BIN
      img/status/dot_idle.png
  85. 8
      img/status/dot_idle.svg
  86. BIN
      img/status/dot_idle_2x.png
  87. BIN
      img/status/dot_idle_notification.png
  88. 14
      img/status/dot_idle_notification.svg
  89. BIN
      img/status/dot_idle_notification_2x.png
  90. BIN
      img/status/dot_online.png
  91. 7
      img/status/dot_online.svg
  92. BIN
      img/status/dot_online_2x.png
  93. BIN
      img/status/dot_online_notification.png
  94. 14
      img/status/dot_online_notification.svg
  95. BIN
      img/status/dot_online_notification_2x.png
  96. 17
      main.cpp
  97. 5
      res.qrc
  98. 55
      res/settings.ini
  99. 343
      settings.cpp
  100. 163
      settings.h
  101. Some files were not shown because too many files have changed in this diff Show More

45
addfriendform.cpp

@ -0,0 +1,45 @@ @@ -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

@ -0,0 +1,35 @@ @@ -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

@ -0,0 +1,153 @@ @@ -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

@ -0,0 +1,57 @@ @@ -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

@ -0,0 +1,19 @@ @@ -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

@ -0,0 +1,20 @@ @@ -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

@ -0,0 +1,47 @@ @@ -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

@ -0,0 +1,35 @@ @@ -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

@ -0,0 +1,568 @@ @@ -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

@ -0,0 +1,216 @@ @@ -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

@ -0,0 +1,110 @@ @@ -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

@ -0,0 +1,66 @@ @@ -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

@ -0,0 +1,82 @@ @@ -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

@ -0,0 +1,50 @@ @@ -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

@ -0,0 +1,34 @@ @@ -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

@ -0,0 +1,36 @@ @@ -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

@ -0,0 +1,33 @@ @@ -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

@ -0,0 +1,25 @@ @@ -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

@ -0,0 +1,32 @@ @@ -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

@ -0,0 +1,21 @@ @@ -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

@ -0,0 +1,61 @@ @@ -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

@ -0,0 +1,29 @@ @@ -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

@ -0,0 +1,62 @@ @@ -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

@ -0,0 +1,28 @@ @@ -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

@ -0,0 +1,98 @@ @@ -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

@ -0,0 +1,37 @@ @@ -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

@ -0,0 +1,175 @@ @@ -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

@ -0,0 +1,56 @@ @@ -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

@ -0,0 +1,31 @@ @@ -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

@ -0,0 +1,22 @@ @@ -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

@ -0,0 +1,75 @@ @@ -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

@ -0,0 +1,29 @@ @@ -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

BIN
img/button icons/arrow_medgrey.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

7
img/button icons/arrow_medgrey.svg

@ -0,0 +1,7 @@ @@ -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

BIN
img/button icons/arrow_medgrey_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

BIN
img/button icons/arrow_white.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

7
img/button icons/arrow_white.svg

@ -0,0 +1,7 @@ @@ -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

BIN
img/button icons/arrow_white_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

BIN
img/button icons/attach.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

38
img/button icons/attach.svg

@ -0,0 +1,38 @@ @@ -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

BIN
img/button icons/attach_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

BIN
img/button icons/call.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

10
img/button icons/call.svg

@ -0,0 +1,10 @@ @@ -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

BIN
img/button icons/call_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

7
img/button icons/check.svg

@ -0,0 +1,7 @@ @@ -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

BIN
img/button icons/emoticon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

10
img/button icons/emoticon.svg

@ -0,0 +1,10 @@ @@ -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

BIN
img/button icons/emoticon_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

BIN
img/button icons/no.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

8
img/button icons/no.svg

@ -0,0 +1,8 @@ @@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

BIN
img/button icons/pause.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

10
img/button icons/pause.svg

@ -0,0 +1,10 @@ @@ -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

BIN
img/button icons/pause_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

BIN
img/button icons/sendmessage.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

10
img/button icons/sendmessage.svg

@ -0,0 +1,10 @@ @@ -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

BIN
img/button icons/sendmessage_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

BIN
img/button icons/video.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

10
img/button icons/video.svg

@ -0,0 +1,10 @@ @@ -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

BIN
img/button icons/video_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

BIN
img/button icons/yes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
img/button icons/yes_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

BIN
img/contact list icons/add.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

8
img/contact list icons/add.svg

@ -0,0 +1,8 @@ @@ -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

BIN
img/contact list icons/add_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

BIN
img/contact list icons/contact.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

BIN
img/contact list icons/group.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

20
img/contact list icons/group.svg

@ -0,0 +1,20 @@ @@ -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

BIN
img/contact list icons/group_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

BIN
img/contact list icons/settings.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

19
img/contact list icons/settings.svg

@ -0,0 +1,19 @@ @@ -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

BIN
img/contact list icons/settings_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

BIN
img/contact list icons/transfer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

18
img/contact list icons/transfer.svg

@ -0,0 +1,18 @@ @@ -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

BIN
img/contact list icons/transfer_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

BIN
img/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

BIN
img/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
img/status/dot_away.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

36
img/status/dot_away.svg

@ -0,0 +1,36 @@ @@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

BIN
img/status/dot_away_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

40
img/status/dot_away_notification.svg

@ -0,0 +1,40 @@ @@ -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

BIN
img/status/dot_away_notification_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

BIN
img/status/dot_idle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

8
img/status/dot_idle.svg

@ -0,0 +1,8 @@ @@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

BIN
img/status/dot_idle_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

14
img/status/dot_idle_notification.svg

@ -0,0 +1,14 @@ @@ -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

BIN
img/status/dot_idle_notification_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

BIN
img/status/dot_online.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

7
img/status/dot_online.svg

@ -0,0 +1,7 @@ @@ -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

BIN
img/status/dot_online_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

BIN
img/status/dot_online_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

14
img/status/dot_online_notification.svg

@ -0,0 +1,14 @@ @@ -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

BIN
img/status/dot_online_notification_2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

17
main.cpp

@ -0,0 +1,17 @@ @@ -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

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

55
res/settings.ini

@ -0,0 +1,55 @@ @@ -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

@ -0,0 +1,343 @@ @@ -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

@ -0,0 +1,163 @@ @@ -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

Loading…
Cancel
Save