@ -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); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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()); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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); |
||||||
|
} |
||||||
@ -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
|
||||||
|
|
||||||
@ -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(); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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; |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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(); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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(); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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); |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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
|
||||||
@ -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"); |
||||||
|
} |
||||||
@ -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
|
||||||
|
After Width: | Height: | Size: 172 B |
|
After Width: | Height: | Size: 249 B |
|
After Width: | Height: | Size: 152 B |
|
After Width: | Height: | Size: 214 B |
|
After Width: | Height: | Size: 274 B |
|
After Width: | Height: | Size: 432 B |
|
After Width: | Height: | Size: 307 B |
|
After Width: | Height: | Size: 510 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 323 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 265 B |
|
After Width: | Height: | Size: 142 B |
|
After Width: | Height: | Size: 129 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 533 B |
|
After Width: | Height: | Size: 248 B |
|
After Width: | Height: | Size: 382 B |
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 267 B |
|
After Width: | Height: | Size: 164 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 790 B |
|
After Width: | Height: | Size: 314 B |
|
After Width: | Height: | Size: 512 B |
|
After Width: | Height: | Size: 291 B |
|
After Width: | Height: | Size: 474 B |
|
After Width: | Height: | Size: 288 B |
|
After Width: | Height: | Size: 433 B |
|
After Width: | Height: | Size: 361 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 411 B |
|
After Width: | Height: | Size: 472 B |
|
After Width: | Height: | Size: 852 B |
|
After Width: | Height: | Size: 240 B |
|
After Width: | Height: | Size: 376 B |
|
After Width: | Height: | Size: 458 B |
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 272 B |
|
After Width: | Height: | Size: 282 B |
|
After Width: | Height: | Size: 424 B |
|
After Width: | Height: | Size: 766 B |
@ -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; |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
<RCC> |
||||||
|
<qresource prefix="/conf"> |
||||||
|
<file alias="settings.ini">res/settings.ini</file> |
||||||
|
</qresource> |
||||||
|
</RCC> |
||||||
@ -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 |
||||||
|
|
||||||
@ -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; |
||||||
|
} |
||||||
@ -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
|
||||||