diff --git a/INSTALL.md b/INSTALL.md index b5b2d5f11..ed63bfe78 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,3 +1,10 @@ +#Install Instructions +- [Dependencies](#dependencies) +- [Windows](#windows) +- [Linux](#linux) +- [OS X](#osx) + + ##Dependencies | Name | Version | Modules | @@ -8,6 +15,7 @@ | OpenCV | >= 2.4.9 | core, highgui | | OpenAL Soft | >= 1.16.0 | | + ##Windows ###Qt @@ -68,8 +76,9 @@ make install ``` Copy the dll "OpenAL32.dll" located at "C:\qTox\libs\openal-build\install\bin" to "C:\qTox\libs\lib". Finally, copy the directory "AL" located at "C:\qTox\libs\openal-build\install\include" to "C:\qTox\libs\include". Unlike OpenCV you don't need to patch any files. Feel free to delete the directories "openal-soft-x.y.z" and "openal-build", but you don't need to. + ##Linux -Most of the dependencies should be available through your package manger. +Most of the dependencies should be available through your package manger. You may either follow the directions below, or simply run `./simple_make.sh` after cloning, which will attempt to automatically download dependencies followed by compilation. ###Cloning the Repository In order to clone the qTox repository you need Git. @@ -156,11 +165,77 @@ cd /home/user/qTox ./bootstrap.sh # use -h or --help for more information ``` -##Building packages +After all the dependencies are thus reeady to go, compiling should be as simple as +```bash +qmake +make +``` -qTox now has the experimental and probably-dodgy ability to package itself (in .deb +###Building packages + +Alternately, qTox now has the experimental and probably-dodgy ability to package itself (in .deb form natively, and .rpm form with alien). After installing the required dependencies, run `bootstrap.sh` and then run the `buildPackages.sh` script, found in the tools folder. It will automatically get the packages necessary for building .debs, so be prepared to type your password for sudo. + + +##OS X + +###OSX Easy Install + +Since https://github.com/ReDetection/homebrew-qtox you can easily install qtox with homebrew +```bash +brew install --HEAD ReDetection/qtox/qtox +``` + +###OSX Full Install Guide + +This guide is intended for people who wish to use an existing or new ProjectTox-Core installation separate to the bundled installation with qTox, if you do not wish to use a separate installation you can skip to the section titled 'Final Steps'. + +Installation on OSX, isn't quite straight forward, here is a quick guide on how to install; + +Note that qTox now requires OpenCV and OpenAL for video and audio. + +The first thing you need to do is install ProjectTox-Core with a/v support. Refer to the INSTALL guide in the PrjectTox-Core github repo. + +Next you need to download QtTools (http://qt-project.org/downloads), at the time of writing this is at version .3.0. +Make sure you deselect all the unnecessary components from the 5.3 checkbox (iOS/Android libs) otherwise you will end up with a very large download. + +Once that is installed you will most likely need to set the path for qmake. To do this, open up terminal and paste in the following; + +```bash +export PATH=/location/to/qmake/binary:$PATH +``` + +For myself, the qmake binary was located in /Users/mouseym/Qt/5.3/clang_64/bin/. + +This is not a permanent change, it will revert when you close the terminal window, to add it permanently you will need to add echo the above line to your .profile/.bash_profile. + +Once this is installed, do the following; + +```bash +git clone https://github.com/tux3/qTox +cd toxgui +qmake +``` + +Now, we need to create a symlink to /usr/local/lib/ and /usr/local/include/ +``` +mkdir -p $HOME/qTox/libs +sudo ln -s /usr/local/lib $HOME/qTox/libs/lib +sudo ln -s /usr/local/include $HOME/qTox/libs/include +``` +####Final Steps + +The final step is to run +```bash +make +``` +in the qTox directory, or if you are using the bundled tox core installation, you can use +```bash +./bootstrap.sh +make +``` +Assuming all went well you should now have a qTox.app file within the directory. Double click and it should open! diff --git a/README.md b/README.md index d6358283e..430e6fcd2 100644 --- a/README.md +++ b/README.md @@ -11,21 +11,24 @@ However, it is not a fork. - Group chats - File transfers, with previewing of images - Audio calls -- Video calls (alpha) +- Video calls - Tox DNS - Translations in various languages +- Avatars

Downloads

This client runs on Windows, Linux and Mac natively.
+You can find the latest versions of qTox
here, or from the Tox Project's servers : + Windows download
Mac download
Linux download (click "Last successful artifacts")

Screenshots

Note: The screenshots may not always be up to date, but they should give a good idea of the general look and features
-/ + ##Documentation: diff --git a/audio/ToxicIncomingCall.pcm b/audio/ToxicIncomingCall.pcm new file mode 100644 index 000000000..8239c74cf Binary files /dev/null and b/audio/ToxicIncomingCall.pcm differ diff --git a/bootstrap.sh b/bootstrap.sh index 9c67a7af9..95a11febe 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -103,7 +103,7 @@ rm -rf ${BASE_DIR}/${TOX_CORE_DIR} # afterwards install libsodium to INSTALL_DIR # skip the installation if TOX_ONLY is true if [[ $TOX_ONLY = "false" ]]; then - git clone git://github.com/jedisct1/libsodium.git ${BASE_DIR}/${SODIUM_DIR} + git clone git://github.com/jedisct1/libsodium.git ${BASE_DIR}/${SODIUM_DIR} --depth 1 pushd ${BASE_DIR}/${SODIUM_DIR} git checkout tags/$SODIUM_VER ./autogen.sh @@ -116,7 +116,7 @@ if [[ $TOX_ONLY = "false" ]]; then make -j2 check - if [[ $GLOBAL = "false" ]]; then + if [[ $GLOBAL = "false" || $EUID -eq 0 ]]; then make install else sudo make install @@ -128,7 +128,7 @@ fi # clone current master of libtoxcore # make sure to compile with libsodium we just installed to INSTALL_DIR # afterwards install libtoxcore to INSTALL_DIR -git clone https://github.com/irungentoo/toxcore.git ${BASE_DIR}/${TOX_CORE_DIR} +git clone https://github.com/irungentoo/toxcore.git ${BASE_DIR}/${TOX_CORE_DIR} --depth 1 pushd ${BASE_DIR}/${TOX_CORE_DIR} ./autogen.sh if [[ $GLOBAL = "false" ]]; then @@ -139,7 +139,7 @@ fi make -j2 -if [[ $GLOBAL = "false" ]]; then +if [[ $GLOBAL = "false" || $EUID -eq 0 ]]; then make install else sudo make install diff --git a/core.cpp b/core.cpp index 4f0fb83c3..6b899ab8b 100644 --- a/core.cpp +++ b/core.cpp @@ -15,9 +15,9 @@ */ #include "core.h" -#include "cdata.h" -#include "cstring.h" -#include "settings.h" +#include "misc/cdata.h" +#include "misc/cstring.h" +#include "misc/settings.h" #include "widget/widget.h" #include @@ -34,6 +34,9 @@ #include #include #include +#include +#include +#include const QString Core::CONFIG_FILE_NAME = "data"; const QString Core::TOX_EXT = ".tox"; @@ -48,15 +51,9 @@ Core::Core(Camera* cam, QThread *coreThread) : toxTimer = new QTimer(this); toxTimer->setSingleShot(true); - //saveTimer = new QTimer(this); - //saveTimer->start(TOX_SAVE_INTERVAL); - bootstrapTimer = new QTimer(this); - bootstrapTimer->start(TOX_BOOTSTRAP_INTERVAL); connect(toxTimer, &QTimer::timeout, this, &Core::process); - //connect(saveTimer, &QTimer::timeout, this, &Core::saveConfiguration); //Disable save timer in favor of saving on events //connect(fileTimer, &QTimer::timeout, this, &Core::fileHeartbeat); - connect(bootstrapTimer, &QTimer::timeout, this, &Core::onBootstrapTimer); - connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::bootstrapDht); + connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::process); connect(this, SIGNAL(fileTransferFinished(ToxFile)), this, SLOT(onFileTransferFinished(ToxFile))); for (int i=0; igetCore(); } -void Core::get_tox() +void Core::make_tox() { // IPv6 needed for LAN discovery, but can crash some weird routers. On by default, can be disabled in options. bool enableIPv6 = Settings::getInstance().getEnableIPv6(); + bool forceTCP = Settings::getInstance().getForceTCP(); + bool useProxy = Settings::getInstance().getUseProxy(); + if (enableIPv6) qDebug() << "Core starting with IPv6 enabled"; else @@ -132,11 +132,32 @@ void Core::get_tox() Tox_Options toxOptions; toxOptions.ipv6enabled = enableIPv6; - toxOptions.udp_disabled = 0; + toxOptions.udp_disabled = forceTCP; + + // No proxy by default toxOptions.proxy_enabled = false; toxOptions.proxy_address[0] = 0; toxOptions.proxy_port = 0; + if (useProxy) + { + QString proxyAddr = Settings::getInstance().getProxyAddr(); + int proxyPort = Settings::getInstance().getProxyPort(); + + if (proxyAddr.length() > 255) + { + qWarning() << "Core: proxy address" << proxyAddr << "is too long"; + } + else if (proxyAddr != "" && proxyPort > 0) + { + qDebug() << "Core: using proxy" << proxyAddr << ":" << proxyPort; + toxOptions.proxy_enabled = true; + uint16_t sz = CString::fromString(proxyAddr, (unsigned char*)toxOptions.proxy_address); + toxOptions.proxy_address[sz] = 0; + toxOptions.proxy_port = proxyPort; + } + } + tox = tox_new(&toxOptions); if (tox == nullptr) { @@ -146,13 +167,29 @@ void Core::get_tox() tox = tox_new(&toxOptions); if (tox == nullptr) { - qCritical() << "Tox core failed to start"; - emit failedToStart(); + if (toxOptions.proxy_enabled) + { + //QMessageBox::critical(Widget::getInstance(), tr("Proxy failure", "popup title"), + //tr("toxcore failed to start with your proxy settings. qTox cannot run; please modify your " + //"settings and restart.", "popup text")); + qCritical() << "Core: bad proxy! no toxcore!"; + emit badProxy(); + } + else + { + qCritical() << "Tox core failed to start"; + emit failedToStart(); + } return; } else qWarning() << "Core failed to start with IPv6, falling back to IPv4. LAN discovery may not work properly."; } + else if (toxOptions.proxy_enabled) + { + emit badProxy(); + return; + } else { qCritical() << "Tox core failed to start"; @@ -172,7 +209,7 @@ void Core::get_tox() void Core::start() { - get_tox(); + make_tox(); qsrand(time(nullptr)); @@ -191,7 +228,13 @@ void Core::start() } #endif } - loadConfiguration(path); + if (!loadConfiguration(path)) + { + emit failedToStart(); + tox_kill(tox); + tox = nullptr; + return; + } tox_callback_friend_request(tox, onFriendRequest, this); tox_callback_friend_message(tox, onFriendMessage, this); @@ -207,6 +250,8 @@ void Core::start() tox_callback_file_send_request(tox, onFileSendRequestCallback, this); tox_callback_file_control(tox, onFileControlCallback, this); tox_callback_file_data(tox, onFileDataCallback, this); + tox_callback_avatar_info(tox, onAvatarInfoCallback, this); + tox_callback_avatar_data(tox, onAvatarDataCallback, this); toxav_register_callstate_callback(toxav, onAvInvite, av_OnInvite, this); toxav_register_callstate_callback(toxav, onAvStart, av_OnStart, this); @@ -225,20 +270,101 @@ void Core::start() uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE]; tox_get_address(tox, friendAddress); - emit friendAddressGenerated(CFriendAddress::toString(friendAddress)); - bootstrapDht(); + QPixmap pic = Settings::getInstance().getSavedAvatar(getSelfId().toString()); + if (!pic.isNull() && !pic.size().isEmpty()) + { + QByteArray data; + QBuffer buffer(&data); + buffer.open(QIODevice::WriteOnly); + pic.save(&buffer, "PNG"); + buffer.close(); + setAvatar(TOX_AVATAR_FORMAT_PNG, data); + } + else + qDebug() << "Core: Error loading self avatar"; + + process(); // starts its own timer +} + +/* Using the now commented out statements in checkConnection(), I watched how + * many ticks disconnects-after-initial-connect lasted. Out of roughly 15 trials, + * 5 disconnected; 4 were DCd for less than 20 ticks, while the 5th was ~50 ticks. + * So I set the tolerance here at 25, and initial DCs should be very rare now. + * This should be able to go to 50 or 100 without affecting legitimate disconnects' + * downtime, but lets be conservative for now. Edit: now 40. + */ +#define CORE_DISCONNECT_TOLERANCE 40 + +void Core::process() +{ + if (!tox) + return; + + static int tolerance = CORE_DISCONNECT_TOLERANCE; + tox_do(tox); + +#ifdef DEBUG + //we want to see the debug messages immediately + fflush(stdout); +#endif + + if (checkConnection()) + tolerance = CORE_DISCONNECT_TOLERANCE; + else if (!(--tolerance)) + { + bootstrapDht(); + } toxTimer->start(tox_do_interval(tox)); } -void Core::onBootstrapTimer() +bool Core::checkConnection() { - if (!tox) - return; - if(!tox_isconnected(tox)) - bootstrapDht(); + static bool isConnected = false; + //static int count = 0; + bool toxConnected = tox_isconnected(tox); + + if (toxConnected && !isConnected) { + qDebug() << "Core: Connected to DHT"; + emit connected(); + isConnected = true; + //if (count) qDebug() << "Core: disconnect count:" << count; + //count = 0; + } else if (!toxConnected && isConnected) { + qDebug() << "Core: Disconnected to DHT"; + emit disconnected(); + isConnected = false; + //count++; + } //else if (!toxConnected) count++; + return isConnected; +} + +void Core::bootstrapDht() +{ + const Settings& s = Settings::getInstance(); + QList dhtServerList = s.getDhtServerList(); + + int listSize = dhtServerList.size(); + static int j = qrand() % listSize; + + qDebug() << "Core: Bootstraping to the DHT ..."; + + int i=0; + while (i < 2) // i think the more we bootstrap, the more we jitter because the more we overwrite nodes + { + const Settings::DhtServer& dhtServer = dhtServerList[j % listSize]; + if (tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(), + dhtServer.port, CUserId(dhtServer.userId).data()) == 1) + qDebug() << QString("Core: Bootstraping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data() + +QString(", port ")+QString().setNum(dhtServer.port); + else + qDebug() << "Core: Error bootstraping from "+dhtServer.name; + + j++; + i++; + } } void Core::onFriendRequest(Tox*/* tox*/, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core) @@ -248,7 +374,7 @@ void Core::onFriendRequest(Tox*/* tox*/, const uint8_t* cUserId, const uint8_t* void Core::onFriendMessage(Tox*/* tox*/, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core) { - emit static_cast(core)->friendMessageReceived(friendId, CString::toString(cMessage, cMessageSize)); + emit static_cast(core)->friendMessageReceived(friendId, CString::toString(cMessage, cMessageSize), false); } void Core::onFriendNameChange(Tox*/* tox*/, int friendId, const uint8_t* cName, uint16_t cNameSize, void* core) @@ -283,6 +409,10 @@ void Core::onUserStatusChanged(Tox*/* tox*/, int friendId, uint8_t userstatus, v status = Status::Online; break; } + + if (status == Status::Online || status == Status::Away) + tox_request_avatar_info(static_cast(core)->tox, friendId); + emit static_cast(core)->friendStatusChanged(friendId, status); } @@ -292,23 +422,51 @@ void Core::onConnectionStatusChanged(Tox*/* tox*/, int friendId, uint8_t status, emit static_cast(core)->friendStatusChanged(friendId, friendStatus); if (friendStatus == Status::Offline) { static_cast(core)->checkLastOnline(friendId); + + for (ToxFile& f : fileSendQueue) + { + if (f.friendId == friendId && f.status == ToxFile::TRANSMITTING) + { + f.status = ToxFile::BROKEN; + emit static_cast(core)->fileTransferBrokenUnbroken(f, true); + } + } + for (ToxFile& f : fileRecvQueue) + { + if (f.friendId == friendId && f.status == ToxFile::TRANSMITTING) + { + f.status = ToxFile::BROKEN; + emit static_cast(core)->fileTransferBrokenUnbroken(f, true); + } + } + } else { + for (ToxFile& f : fileRecvQueue) + { + if (f.friendId == friendId && f.status == ToxFile::BROKEN) + { + qDebug() << QString("Core::onConnectionStatusChanged: %1: resuming broken filetransfer from position: %2").arg(f.file->fileName()).arg(f.bytesSent); + tox_file_send_control(static_cast(core)->tox, friendId, 1, f.fileNum, TOX_FILECONTROL_RESUME_BROKEN, reinterpret_cast(&f.bytesSent), sizeof(uint64_t)); + emit static_cast(core)->fileTransferBrokenUnbroken(f, false); + } + } } } void Core::onAction(Tox*/* tox*/, int friendId, const uint8_t *cMessage, uint16_t cMessageSize, void *core) { - emit static_cast(core)->actionReceived(friendId, CString::toString(cMessage, cMessageSize)); + emit static_cast(core)->friendMessageReceived(friendId, CString::toString(cMessage, cMessageSize), true); } -void Core::onGroupInvite(Tox*, int friendnumber, const uint8_t *group_public_key, void *core) +void Core::onGroupInvite(Tox*, int friendnumber, const uint8_t *group_public_key, uint16_t length,void *core) { qDebug() << QString("Core: Group invite by %1").arg(friendnumber); - emit static_cast(core)->groupInviteReceived(friendnumber, group_public_key); + emit static_cast(core)->groupInviteReceived(friendnumber, group_public_key,length); } -void Core::onGroupMessage(Tox*, int groupnumber, int friendgroupnumber, const uint8_t * message, uint16_t length, void *core) +void Core::onGroupMessage(Tox*, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *_core) { - emit static_cast(core)->groupMessageReceived(groupnumber, friendgroupnumber, CString::toString(message, length)); + Core* core = static_cast(_core); + emit core->groupMessageReceived(groupnumber, CString::toString(message, length), core->getGroupPeerName(groupnumber, peernumber)); } void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t change, void *core) @@ -329,7 +487,7 @@ void Core::onFileSendRequestCallback(Tox*, int32_t friendnumber, uint8_t filenum emit static_cast(core)->fileReceiveRequested(fileRecvQueue.last()); } void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, - uint8_t control_type, const uint8_t*, uint16_t, void *core) + uint8_t control_type, const uint8_t* data, uint16_t length, void *core) { ToxFile* file{nullptr}; if (receive_send == 1) @@ -416,12 +574,39 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive } else if (receive_send == 0 && control_type == TOX_FILECONTROL_ACCEPT) { + if (file->status == ToxFile::BROKEN) + { + emit static_cast(core)->fileTransferBrokenUnbroken(*file, false); + file->status = ToxFile::TRANSMITTING; + } emit static_cast(core)->fileTransferRemotePausedUnpaused(*file, false); } else if ((receive_send == 0 || receive_send == 1) && control_type == TOX_FILECONTROL_PAUSE) { emit static_cast(core)->fileTransferRemotePausedUnpaused(*file, true); } + else if (receive_send == 1 && control_type == TOX_FILECONTROL_RESUME_BROKEN) + { + if (length != sizeof(uint64_t)) + return; + + qDebug() << "Core::onFileControlCallback: TOX_FILECONTROL_RESUME_BROKEN"; + + uint64_t resumePos = *reinterpret_cast(data); + + if (resumePos >= file->filesize) + { + qWarning() << "Core::onFileControlCallback: invalid resume position"; + tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0); // don't sure about it + return; + } + + file->status = ToxFile::TRANSMITTING; + emit static_cast(core)->fileTransferBrokenUnbroken(*file, false); + + file->bytesSent = resumePos; + tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0); + } else { qDebug() << QString("Core: File control callback, receive_send=%1, control_type=%2") @@ -453,6 +638,46 @@ void Core::onFileDataCallback(Tox*, int32_t friendnumber, uint8_t filenumber, co file->filesize, file->bytesSent, ToxFile::RECEIVING); } +void Core::onAvatarInfoCallback(Tox*, int32_t friendnumber, uint8_t format, + uint8_t* hash, void* _core) +{ + Core* core = static_cast(_core); + + if (format == TOX_AVATAR_FORMAT_NONE) + { + qDebug() << "Core: Got null avatar info from" << core->getFriendUsername(friendnumber); + emit core->friendAvatarRemoved(friendnumber); + QFile::remove(QDir(Settings::getInstance().getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".png")); + QFile::remove(QDir(Settings::getInstance().getSettingsDirPath()).filePath("avatars/"+core->getFriendAddress(friendnumber).left(64)+".hash")); + } + else + { + QByteArray oldHash = Settings::getInstance().getAvatarHash(core->getFriendAddress(friendnumber)); + if (QByteArray((char*)hash, TOX_HASH_LENGTH) != oldHash) + // comparison failed miserably if I didn't convert hash to QByteArray + { + qDebug() << "Core: Got new avatar info from" << core->getFriendUsername(friendnumber); + tox_request_avatar_data(core->tox, friendnumber); + } + else + qDebug() << "Core: Got same avatar info from" << core->getFriendUsername(friendnumber); + } +} + +void Core::onAvatarDataCallback(Tox*, int32_t friendnumber, uint8_t, + uint8_t *hash, uint8_t *data, uint32_t datalen, void *core) +{ + QPixmap pic; + pic.loadFromData((uchar*)data, datalen); + if (!pic.isNull()) + { + qDebug() << "Core: Got avatar data from" << static_cast(core)->getFriendUsername(friendnumber); + Settings::getInstance().saveAvatar(pic, static_cast(core)->getFriendAddress(friendnumber)); + Settings::getInstance().saveAvatarHash(QByteArray((char*)hash, TOX_HASH_LENGTH), static_cast(core)->getFriendAddress(friendnumber)); + emit static_cast(core)->friendAvatarChanged(friendnumber, pic); + } +} + void Core::acceptFriendRequest(const QString& userId) { int friendId = tox_add_friend_norequest(tox, CUserId(userId).data()); @@ -494,10 +719,14 @@ void Core::requestFriendship(const QString& friendAddress, const QString& messag void Core::sendMessage(int friendId, const QString& message) { - CString cMessage(message); + QList cMessages = splitMessage(message); - int messageId = tox_send_message(tox, friendId, cMessage.data(), cMessage.size()); - emit messageSentResult(friendId, message, messageId); + for (auto &cMsg :cMessages) + { + int messageId = tox_send_message(tox, friendId, cMsg.data(), cMsg.size()); + if (messageId == 0) + emit messageSentResult(friendId, message, messageId); + } } void Core::sendAction(int friendId, const QString &action) @@ -516,9 +745,14 @@ void Core::sendTyping(int friendId, bool typing) void Core::sendGroupMessage(int groupId, const QString& message) { - CString cMessage(message); + QList cMessages = splitMessage(message); - tox_group_message_send(tox, groupId, cMessage.data(), cMessage.size()); + for (auto &cMsg :cMessages) + { + int ret = tox_group_message_send(tox, groupId, cMsg.data(), cMsg.size()); + if (ret == -1) + emit groupSentResult(groupId, message, ret); + } } void Core::sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize) @@ -528,6 +762,7 @@ void Core::sendFile(int32_t friendId, QString Filename, QString FilePath, long l if (fileNum == -1) { qWarning() << "Core::sendFile: Can't create the Tox file sender"; + emit fileSendFailed(friendId, Filename); return; } qDebug() << QString("Core::sendFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId); @@ -604,7 +839,7 @@ void Core::pauseResumeFileRecv(int friendId, int fileNum) tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0); } else - qWarning() << "Core::pauseResumeFileRecv: File is stopped"; + qWarning() << "Core::pauseResumeFileRecv: File is stopped or broken"; } void Core::cancelFileSend(int friendId, int fileNum) @@ -741,17 +976,36 @@ void Core::setUsername(const QString& username) } } -QString Core::getSelfId() +void Core::setAvatar(uint8_t format, const QByteArray& data) +{ + if (tox_set_avatar(tox, format, (uint8_t*)data.constData(), data.size()) != 0) + { + qWarning() << "Core: Failed to set self avatar"; + return; + } + + QPixmap pic; + pic.loadFromData(data); + Settings::getInstance().saveAvatar(pic, getSelfId().toString()); + emit selfAvatarChanged(pic); + + // Broadcast our new avatar! + // according to tox.h, we need not broadcast this ourselves, but initial testing indicated elsewise + const uint32_t friendCount = tox_count_friendlist(tox);; + for (unsigned i=0; i 10^10 (which is roughly the planet's population) } @@ -813,79 +1067,6 @@ void Core::onFileTransferFinished(ToxFile file) emit fileDownloadFinished(file.filePath); } -void Core::bootstrapDht(bool reset) -{ - const Settings& s = Settings::getInstance(); - QList dhtServerList = s.getDhtServerList(); - - int listSize = dhtServerList.size(); - static int j = qrand() % listSize, n=0; - - if (reset) - { - n = 0; - bootstrapTimer->setInterval(TOX_BOOTSTRAP_INTERVAL); - return; - } - - // We couldn't connect after trying 6 different nodes, let's try something else - if (n>3) - { - qDebug() << "Core: We're having trouble connecting to the DHT, slowing down"; - bootstrapTimer->setInterval(TOX_BOOTSTRAP_INTERVAL*(n-1)); - } - else - qDebug() << "Core: Connecting to the DHT ..."; - - int i=0; - while (i < (2 - (n>3))) - { - const Settings::DhtServer& dhtServer = dhtServerList[j % listSize]; - if (tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(), - dhtServer.port, CUserId(dhtServer.userId).data()) == 1) - qDebug() << QString("Core: Bootstraping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data() - +QString(", port ")+QString().setNum(dhtServer.port); - else - qDebug() << "Core: Error bootstraping from "+dhtServer.name; - - tox_do(tox); - j++; - i++; - n++; - } -} - -void Core::process() -{ - if (tox) - { - tox_do(tox); -#ifdef DEBUG - //we want to see the debug messages immediately - fflush(stdout); -#endif - checkConnection(); - //int toxInterval = tox_do_interval(tox); - //qDebug() << QString("Tox interval %1").arg(toxInterval); - } - toxTimer->start(50); -} - -void Core::checkConnection() -{ - static bool isConnected = false; - - if (tox_isconnected(tox) && !isConnected) { - qDebug() << "Core: Connected to DHT"; - emit connected(); - isConnected = true; - } else if (!tox_isconnected(tox) && isConnected) { - qDebug() << "Core: Disconnected to DHT"; - emit disconnected(); - isConnected = false; - } -} - QString Core::sanitize(QString name) { // these are pretty much Windows banned filename characters @@ -900,29 +1081,45 @@ QString Core::sanitize(QString name) return name; } -void Core::loadConfiguration(QString path) +bool Core::loadConfiguration(QString path) { // setting the profile is now the responsibility of the caller - QFile conf(path); + QFile configurationFile(path); qDebug() << "Core::loadConfiguration: reading from " << path; - if (!conf.exists()) { + if (!configurationFile.exists()) { qWarning() << "The Tox configuration file was not found"; - return; + return true; } - if (!conf.open(QIODevice::ReadOnly)) { + if (!configurationFile.open(QIODevice::ReadOnly)) { qCritical() << "File " << path << " cannot be opened"; - return; + return true; } - qint64 fileSize = conf.size(); + qint64 fileSize = configurationFile.size(); if (fileSize > 0) { - QByteArray data = conf.readAll(); - tox_load(tox, reinterpret_cast(data.data()), data.size()); + QByteArray data = configurationFile.readAll(); + int error = tox_load(tox, reinterpret_cast(data.data()), data.size()); + if (error < 0) + { + qWarning() << "Core: tox_load failed with error "<stop(); + if (tox) { toxav_kill(toxav); toxav = nullptr; @@ -1005,11 +1205,20 @@ void Core::switchConfiguration(QString profile) } emit clearFriends(); - get_tox(); - bootstrapDht(true); // reset this func + make_tox(); Settings::getInstance().setCurrentProfile(profile); - loadConfiguration(Settings::getSettingsDirPath() + QDir::separator() + profile + TOX_EXT); + if (!loadConfiguration(Settings::getSettingsDirPath() + QDir::separator() + profile + TOX_EXT)) + { + emit failedToStart(); + toxav_kill(toxav); + toxav = nullptr; + tox_kill(tox); + tox = nullptr; + return; + } + + process(); // restarts toxTimer } void Core::loadFriends() @@ -1028,7 +1237,7 @@ void Core::loadFriends() 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)); + emit friendUsernameChanged(ids[i], CString::toString(name, nameSize)); } delete[] name; } @@ -1037,7 +1246,7 @@ void Core::loadFriends() 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)); + emit friendStatusMessageChanged(ids[i], CString::toString(statusMessage, statusMessageSize)); } delete[] statusMessage; } @@ -1098,10 +1307,10 @@ QList Core::getGroupPeerNames(int groupId) const return names; } -int Core::joinGroupchat(int32_t friendnumber, const uint8_t* friend_group_public_key) const +int Core::joinGroupchat(int32_t friendnumber, const uint8_t* friend_group_public_key,uint16_t length) const { qDebug() << QString("Trying to join groupchat invite by friend %1").arg(friendnumber); - return tox_join_groupchat(tox, friendnumber, friend_group_public_key); + return tox_join_groupchat(tox, friendnumber, friend_group_public_key,length); } void Core::quitGroupChat(int groupId) const @@ -1252,3 +1461,39 @@ QString Core::getFriendAddress(int friendNumber) const return id; } + +QString Core::getFriendUsername(int friendnumber) const +{ + uint8_t name[TOX_MAX_NAME_LENGTH]; + tox_get_name(tox, friendnumber, name); + return CString::toString(name, tox_get_name_size(tox, friendnumber)); +} + +QList Core::splitMessage(const QString &message) +{ + QList splittedMsgs; + QByteArray ba_message(message.toUtf8()); + + while (ba_message.size() > TOX_MAX_MESSAGE_LENGTH) + { + int splitPos = ba_message.lastIndexOf(' ', TOX_MAX_MESSAGE_LENGTH - 1); + if (splitPos <= 0) + { + splitPos = TOX_MAX_MESSAGE_LENGTH; + if (ba_message[splitPos] & 0x80) + { + do { + splitPos--; + } while (!(ba_message[splitPos] & 0x40)); + } + splitPos--; + } + + splittedMsgs.push_back(CString(ba_message.left(splitPos + 1))); + ba_message = ba_message.mid(splitPos + 1); + } + + splittedMsgs.push_back(CString(ba_message)); + + return splittedMsgs; +} diff --git a/core.h b/core.h index 16d3be50b..292c82494 100644 --- a/core.h +++ b/core.h @@ -28,6 +28,7 @@ template class QList; class Camera; class QTimer; class QString; +class CString; class Core : public QObject { @@ -43,7 +44,8 @@ public: QString getGroupPeerName(int groupId, int peerId) const; QList getGroupPeerNames(int groupId) const; QString getFriendAddress(int friendNumber) const; - int joinGroupchat(int32_t friendnumber, const uint8_t* friend_group_public_key) const; + QString getFriendUsername(int friendNumber) const; + int joinGroupchat(int32_t friendnumber, const uint8_t* friend_group_public_key,uint16_t length) const; void quitGroupChat(int groupId) const; void dispatchVideoFrame(vpx_image img) const; @@ -55,7 +57,7 @@ public: QString getUsername(); QString getStatusMessage(); - QString getSelfId(); + ToxID getSelfId(); void increaseVideoBusyness(); void decreaseVideoBusyness(); @@ -63,7 +65,7 @@ public: public slots: void start(); void process(); - void bootstrapDht(bool reset = false); + void bootstrapDht(); void acceptFriendRequest(const QString& userId); void requestFriendship(const QString& friendAddress, const QString& message); @@ -76,6 +78,7 @@ public slots: void setStatus(Status status); void setUsername(const QString& username); void setStatusMessage(const QString& message); + void setAvatar(uint8_t format, const QByteArray& data); void sendMessage(int friendId, const QString& message); void sendGroupMessage(int groupId, const QString& message); @@ -102,7 +105,7 @@ signals: void disconnected(); void friendRequestReceived(const QString& userId, const QString& message); - void friendMessageReceived(int friendId, const QString& message); + void friendMessageReceived(int friendId, const QString& message, bool isAction); void friendAdded(int friendId, const QString& userId); void clearFriends(); @@ -111,9 +114,8 @@ signals: 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 friendAvatarChanged(int friendId, const QPixmap& pic); + void friendAvatarRemoved(int friendId); void friendAddressGenerated(const QString& friendAddress); @@ -122,15 +124,17 @@ signals: void friendLastSeenChanged(int friendId, const QDateTime& dateTime); void emptyGroupCreated(int groupnumber); - void groupInviteReceived(int friendnumber, const uint8_t *group_public_key); - void groupMessageReceived(int groupnumber, int friendgroupnumber, const QString& message); + void groupInviteReceived(int friendnumber, const uint8_t *group_public_key,uint16_t length); + void groupMessageReceived(int groupnumber, const QString& message, const QString& author); void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change); void usernameSet(const QString& username); void statusMessageSet(const QString& message); void statusSet(Status status); + void selfAvatarChanged(const QPixmap& pic); void messageSentResult(int friendId, const QString& message, int messageId); + void groupSentResult(int groupId, const QString& message, int result); void actionSentResult(int friendId, const QString& action, int success); void failedToAddFriend(const QString& userId); @@ -140,9 +144,8 @@ signals: void failedToSetStatus(Status status); void failedToSetTyping(bool typing); - void actionReceived(int friendId, const QString& acionMessage); - void failedToStart(); + void badProxy(); void fileSendStarted(ToxFile file); void fileReceiveRequested(ToxFile file); @@ -154,6 +157,9 @@ signals: void fileTransferPaused(int FriendId, int FileNum, ToxFile::FileDirection direction); void fileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection direction); void fileTransferRemotePausedUnpaused(ToxFile file, bool paused); + void fileTransferBrokenUnbroken(ToxFile file, bool broken); + + void fileSendFailed(int FriendId, const QString& fname); void avInvite(int friendId, int callIndex, bool video); void avStart(int friendId, int callIndex, bool video); @@ -177,7 +183,7 @@ private: 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, const uint8_t* cMessage, uint16_t cMessageSize, void* core); - static void onGroupInvite(Tox *tox, int friendnumber, const uint8_t *group_public_key, void *userdata); + static void onGroupInvite(Tox *tox, int friendnumber, const uint8_t *group_public_key, uint16_t length,void *userdata); static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber, const uint8_t * message, uint16_t length, void *userdata); static void onGroupNamelistChange(Tox *tox, int groupnumber, int peernumber, uint8_t change, void *userdata); static void onFileSendRequestCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, @@ -185,6 +191,8 @@ private: static void onFileControlCallback(Tox *tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, const uint8_t *data, uint16_t length, void *core); static void onFileDataCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata); + static void onAvatarInfoCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata); + static void onAvatarDataCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata); static void onAvInvite(void* toxav, int32_t call_index, void* core); static void onAvStart(void* toxav, int32_t call_index, void* core); @@ -206,14 +214,11 @@ private: static void playCallVideo(ToxAv* toxav, int32_t callId, vpx_image_t* img, void *user_data); void sendCallVideo(int callId); - void checkConnection(); - void onBootstrapTimer(); - - void loadConfiguration(QString path); - static QString sanitize(QString name); - - void get_tox(); + bool checkConnection(); + bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise + static QString sanitize(QString name); + void make_tox(); void loadFriends(); static void sendAllFileData(Core* core, ToxFile* file); @@ -221,6 +226,8 @@ private: void checkLastOnline(int friendId); + QList splitMessage(const QString &message); + private slots: void onFileTransferFinished(ToxFile file); diff --git a/coreav.cpp b/coreav.cpp index 9244fe433..fa4ce5cd8 100644 --- a/coreav.cpp +++ b/coreav.cpp @@ -273,7 +273,9 @@ void Core::decreaseVideoBusyness() void Core::micMuteToggle(int callId) { - calls[callId].muteMic = !calls[callId].muteMic; + if (calls[callId].active) { + calls[callId].muteMic = !calls[callId].muteMic; + } } void Core::onAvCancel(void* _toxav, int32_t callId, void* core) diff --git a/coredefines.h b/coredefines.h index cb6fcffed..e2c71b8d4 100644 --- a/coredefines.h +++ b/coredefines.h @@ -3,9 +3,7 @@ #define TOXAV_MAX_CALLS 16 #define GROUPCHAT_MAX_SIZE 32 -#define TOX_SAVE_INTERVAL 30*1000 #define TOX_FILE_INTERVAL 0 -#define TOX_BOOTSTRAP_INTERVAL 5*1000 #define TOXAV_RINGING_TIME 15 // TODO: Put that in the settings diff --git a/corestructs.h b/corestructs.h index ebaab4534..00da8a6a5 100644 --- a/corestructs.h +++ b/corestructs.h @@ -10,6 +10,32 @@ class QTimer; enum class Status : int {Online = 0, Away, Busy, Offline}; +#define TOX_ID_PUBLIC_KEY_LENGTH 64 +#define TOX_ID_NO_SPAM_LENGTH 8 +#define TOX_ID_CHECKSUM_LENGTH 4 + +struct ToxID +{ + QString publicKey; + QString noSpam; + QString checkSum; + + QString toString() const + { + return publicKey + noSpam + checkSum; + } + + ToxID static fromString(QString id) + { + ToxID toxID; + toxID.publicKey = id.left(TOX_ID_PUBLIC_KEY_LENGTH); + toxID.noSpam = id.mid(TOX_ID_PUBLIC_KEY_LENGTH, TOX_ID_NO_SPAM_LENGTH); + toxID.checkSum = id.right(TOX_ID_CHECKSUM_LENGTH); + return toxID; + } + +}; + struct DhtServer { QString name; @@ -24,7 +50,8 @@ struct ToxFile { STOPPED, PAUSED, - TRANSMITTING + TRANSMITTING, + BROKEN }; enum FileDirection : bool diff --git a/debian/control b/debian/control index ef73726b7..af90ab856 100644 --- a/debian/control +++ b/debian/control @@ -3,11 +3,11 @@ Maintainer: John Smith Section: misc Priority: optional Standards-Version: 3.9.5 -Build-Depends: debhelper (>= 9), cdbs, qt5-qmake, qt5-default, libopenal-dev, libopencv-dev, libopus-dev +Build-Depends: debhelper (>= 9), cdbs, qt5-qmake, libopenal-dev (>= 1:1.15.1), libopencv-dev (>= 2.4.8), libopus-dev (>= 1.0), qtbase5-dev (>= 5.2), sudo, autoconf, libtool, pkg-config, libvpx-dev Package: qtox Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} +Depends: libc6 (>= 2.17), libgcc1 (>= 1:4.1.1), libgl1-mesa-glx | libgl1, libopenal1 (>= 1.14), libopencv-core2.4, libopencv-highgui2.4, libopus0 (>= 1.0), libqt5core5a (>= 5.2), libqt5gui5 (>= 5.2), libqt5network5 (>= 5.0), libqt5widgets5 (>= 5.2), libqt5xml5 (>= 5.0), libstdc++6 (>= 4.9), libvpx1 (>= 1.0.0) Description: Tox client qTox is a powerful Tox client that follows the Tox design guidelines. Tox is a decentralized and encrypted replacement for Skype, supporting diff --git a/debian/rules b/debian/rules index 6815966a4..5d17ec7e7 100644 --- a/debian/rules +++ b/debian/rules @@ -3,4 +3,4 @@ include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/qmake.mk -QMAKE=qmake STATICPKG=YES +QMAKE=qmake -qt=5 STATICPKG=YES diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 000000000..2c6d70e0d --- /dev/null +++ b/debian/source/options @@ -0,0 +1 @@ +--diff-ignore=libs diff --git a/filetransferinstance.cpp b/filetransferinstance.cpp index 9bcc7b6e1..f3301e58a 100644 --- a/filetransferinstance.cpp +++ b/filetransferinstance.cpp @@ -16,33 +16,49 @@ #include "filetransferinstance.h" #include "core.h" +#include "misc/style.h" #include #include #include #include #include +#include + +#define CONTENT_WIDTH 250 +#define MAX_PREVIEW_SIZE 25*1024*1024 uint FileTransferInstance::Idconter = 0; FileTransferInstance::FileTransferInstance(ToxFile File) - : lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0}, + : lastBytesSent{0}, fileNum{File.fileNum}, friendId{File.friendId}, direction{File.direction} { id = Idconter++; state = tsPending; remotePaused = false; + lastUpdateTime = QDateTime::currentDateTime(); filename = File.fileName; + + // update this whenever you change the font in innerStyle.css + QFontMetrics fm(Style::getFont(Style::Small)); + + filenameElided = fm.elidedText(filename, Qt::ElideRight, CONTENT_WIDTH); + size = getHumanReadableSize(File.filesize); speed = "0B/s"; eta = "00:00"; + if (File.direction == ToxFile::SENDING) { - QImage preview; - File.file->seek(0); - if (preview.loadFromData(File.file->readAll())) + if (File.file->size() <= MAX_PREVIEW_SIZE) { - pic = preview.scaledToHeight(50); + QImage preview; + File.file->seek(0); + if (preview.loadFromData(File.file->readAll())) + { + pic = preview.scaledToHeight(50); + } } File.file->seek(0); } @@ -63,27 +79,27 @@ void FileTransferInstance::onFileTransferInfo(int FriendId, int FileNum, int64_t return; // state = tsProcessing; - QDateTime newtime = QDateTime::currentDateTime(); - int timediff = lastUpdate.secsTo(newtime); + QDateTime now = QDateTime::currentDateTime(); + if (lastUpdateTime.secsTo(now) < 1) //update every 1s + return; + + int timediff = startTime.secsTo(now); if (timediff <= 0) return; - qint64 diff = BytesSent - lastBytesSent; - if (diff < 0) - { - qWarning() << "FileTransferInstance::onFileTransferInfo: Negative transfer speed !"; - diff = 0; - } - long rawspeed = diff / timediff; + + long rawspeed = BytesSent / timediff; + speed = getHumanReadableSize(rawspeed)+"/s"; size = getHumanReadableSize(Filesize); + totalBytes = Filesize; if (!rawspeed) return; int etaSecs = (Filesize - BytesSent) / rawspeed; QTime etaTime(0,0); etaTime = etaTime.addSecs(etaSecs); eta = etaTime.toString("mm:ss"); - lastUpdate = newtime; lastBytesSent = BytesSent; + lastUpdateTime = now; emit stateUpdated(); } @@ -107,7 +123,7 @@ void FileTransferInstance::onFileTransferFinished(ToxFile File) { QImage preview; QFile previewFile(File.filePath); - if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= 1024*1024*25) // Don't preview big (>25MiB) images + if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= MAX_PREVIEW_SIZE) // Don't preview big (>25MiB) images { if (preview.loadFromData(previewFile.readAll())) { @@ -129,6 +145,7 @@ void FileTransferInstance::onFileTransferAccepted(ToxFile File) remotePaused = false; state = tsProcessing; + startTime = QDateTime::currentDateTime(); emit stateUpdated(); } @@ -188,7 +205,7 @@ void FileTransferInstance::acceptRecvRequest() QString path; while (true) { - path = QFileDialog::getSaveFileName(0, tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename)); + path = QFileDialog::getSaveFileName(0, tr("Save a file","Title of the file saving dialog"), QDir::home().filePath(filename)); if (path.isEmpty()) return; else @@ -208,6 +225,8 @@ void FileTransferInstance::acceptRecvRequest() Core::getInstance()->acceptFileRecvRequest(friendId, fileNum, path); state = tsProcessing; + startTime = QDateTime::currentDateTime(); + emit stateUpdated(); } @@ -277,6 +296,12 @@ QString FileTransferInstance::getHtmlImage() rightBtnImg = QImage(":/ui/fileTransferInstance/pauseGreyFileButton.png"); res = draw2ButtonsForm("silver", leftBtnImg, rightBtnImg); + } else if (state == tsBroken) + { + QImage leftBtnImg(":/ui/fileTransferInstance/stopFileButton.png"); + QImage rightBtnImg(":/ui/fileTransferInstance/pauseGreyFileButton.png"); + + res = draw2ButtonsForm("red", leftBtnImg, rightBtnImg); } else if (state == tsCanceled) { res = drawButtonlessForm("red"); @@ -326,7 +351,7 @@ QString FileTransferInstance::drawButtonlessForm(const QString &type) imgBStr = ""; } - QString content = "

" + filename + "

" + size + "

"; + QString content = "

" + filenameElided + "

" + size + "

"; return wrapIntoForm(content, type, imgAStr, imgBStr); } @@ -352,29 +377,89 @@ QString FileTransferInstance::draw2ButtonsForm(const QString &type, const QImage QString imgBstr = ""; QString content; - content += "

" + filename + "

"; - content += "

" + getHumanReadableSize(lastBytesSent) + " / " + size + " (" + speed + " ETA: " + eta + ")

\n"; + QString progrBar = ""; + + content = "

" + filenameElided + "

"; + content += ""; + content += ""; + content += ""; + content += ""; + content += "
" + size + "" + speed + "ETA: " + eta + "
"; + content += progrBar; + content += "
"; return wrapIntoForm(content, type, imgAstr, imgBstr); } QString FileTransferInstance::wrapIntoForm(const QString& content, const QString &type, const QString &imgAstr, const QString &imgBstr) { - QString res; + QString w = QString::number(QImage(":/ui/fileTransferInstance/emptyLRedFileButton.png").size().width()); + QString imgLeftA, imgLeftB; - res = "\n"; + if (type == "green") + { + imgLeftA = ""; + imgLeftB = ""; + } + + if (type == "silver") + { + imgLeftA = ""; + imgLeftB = ""; + } + + if (type == "red") + { + imgLeftA = ""; + imgLeftB = ""; + } + + QString res; + res = "
\n"; res += "\n"; + res += "\n"; res += insertMiniature(type); - res += "\n"; res += "\n"; res += "\n"; res += "
\n"; + res += "
" + imgLeftA + "
" + imgLeftB + "
\n"; + res += "
\n"; + res += "\n"; res += "
"; res += content; res += "
\n"; res += "
\n"; - res += "
" + imgAstr + "
" + imgBstr+ "
\n"; + res += "
" + imgAstr + "
" + imgBstr + "
\n"; res += "
\n"; return res; } + +QImage FileTransferInstance::drawProgressBarImg(const double &part, int w, int h) +{ + QImage progressBar(w, h, QImage::Format_Mono); + + QPainter qPainter(&progressBar); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::black); + qPainter.drawRect(0, 0, w - 1, h - 1); + + qPainter.setBrush(Qt::SolidPattern); + qPainter.setPen(Qt::black); + qPainter.drawRect(1, 0, (w - 2) * (part), h - 1); + + return progressBar; +} + +void FileTransferInstance::onFileTransferBrokenUnbroken(ToxFile File, bool broken) +{ + if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction) + return; + + if (broken) + state = tsBroken; + else + state = tsProcessing; + + emit stateUpdated(); +} diff --git a/filetransferinstance.h b/filetransferinstance.h index b81b03102..f32c10f92 100644 --- a/filetransferinstance.h +++ b/filetransferinstance.h @@ -28,7 +28,7 @@ class FileTransferInstance : public QObject { Q_OBJECT public: - enum TransfState {tsPending, tsProcessing, tsPaused, tsFinished, tsCanceled}; + enum TransfState {tsPending, tsProcessing, tsPaused, tsFinished, tsCanceled, tsBroken}; public: explicit FileTransferInstance(ToxFile File); @@ -43,6 +43,7 @@ public slots: void onFileTransferAccepted(ToxFile File); void onFileTransferPaused(int FriendId, int FileNum, ToxFile::FileDirection Direction); void onFileTransferRemotePausedUnpaused(ToxFile File, bool paused); + void onFileTransferBrokenUnbroken(ToxFile File, bool broken); void pressFromHtml(QString); signals: @@ -62,6 +63,7 @@ private: QString draw2ButtonsForm(const QString &type, const QImage &imgA, const QImage &imgB); QString insertMiniature(const QString &type); QString wrapIntoForm(const QString &content, const QString &type, const QString &imgAstr, const QString &imgBstr); + QImage drawProgressBarImg(const double &part, int w, int h); private: static uint Idconter; @@ -71,8 +73,9 @@ private: bool remotePaused; QImage pic; QString filename, size, speed, eta; - QDateTime lastUpdate; - long long lastBytesSent; + QString filenameElided; + QDateTime startTime, lastUpdateTime; + long long lastBytesSent, totalBytes; int fileNum; int friendId; QString savePath; diff --git a/friend.cpp b/friend.cpp index 6676cea81..5f48197dd 100644 --- a/friend.cpp +++ b/friend.cpp @@ -36,19 +36,17 @@ Friend::~Friend() void Friend::setName(QString name) { - widget->name.setText(name); - widget->name.setToolTip(name); // for overlength names + widget->setName(name); chatForm->setName(name); } void Friend::setStatusMessage(QString message) { - widget->statusMessage.setText(message); - widget->statusMessage.setToolTip(message); // for overlength messsages + widget->setStatusMsg(message); chatForm->setStatusMessage(message); } QString Friend::getName() { - return widget->name.text(); + return widget->getName(); } diff --git a/group.cpp b/group.cpp index 4898f525a..d74610131 100644 --- a/group.cpp +++ b/group.cpp @@ -24,16 +24,14 @@ #include Group::Group(int GroupId, QString Name) - : groupId(GroupId), nPeers{0}, hasPeerInfo{false}, peerInfoTimer{new QTimer} + : groupId(GroupId), nPeers{0} { widget = new GroupWidget(groupId, Name); chatForm = new GroupChatForm(this); - connect(peerInfoTimer, SIGNAL(timeout()), this, SLOT(queryPeerInfo())); - peerInfoTimer->setInterval(500); - peerInfoTimer->setSingleShot(false); - //peerInfoTimer.start(); - //in groupchats, we only notify on messages containing your name + //in groupchats, we only notify on messages containing your name <-- dumb + // sound notifications should be on all messages, but system popup notification + // on naming is appropriate hasNewMessages = 0; userWasMentioned = 0; } @@ -42,53 +40,6 @@ Group::~Group() { delete chatForm; delete widget; - delete peerInfoTimer; -} - -void Group::queryPeerInfo() -{ - const Core* core = Core::getInstance(); - 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 names = core->getGroupPeerNames(groupId); - if (names.isEmpty()) - { - qDebug() << "Group::queryPeerInfo: Can't get names of peers"; - return; - } - for (int i=0; ionUserListChanged(); - chatForm->onUserListChanged(); - - if (namesOk) - { - qDebug() << "Group::queryPeerInfo: Successfully loaded names"; - hasPeerInfo = true; - peerInfoTimer->stop(); - } } void Group::addPeer(int peerId, QString name) diff --git a/group.h b/group.h index fed4b11ee..cd263538f 100644 --- a/group.h +++ b/group.h @@ -25,7 +25,6 @@ struct Friend; class GroupWidget; class GroupChatForm; -class QTimer; class Group : public QObject { @@ -37,17 +36,12 @@ public: void removePeer(int peerId); void updatePeer(int peerId, QString newName); -private slots: - void queryPeerInfo(); - public: int groupId; QMap peers; int nPeers; GroupWidget* widget; GroupChatForm* chatForm; - bool hasPeerInfo; - QTimer* peerInfoTimer; int hasNewMessages, userWasMentioned; }; diff --git a/img/avatar_mask.png b/img/avatar_mask.png new file mode 100644 index 000000000..2527e3365 Binary files /dev/null and b/img/avatar_mask.png differ diff --git a/img/contact.png b/img/contact.png index e98b4d7ec..3b4950144 100644 Binary files a/img/contact.png and b/img/contact.png differ diff --git a/img/contact_dark.png b/img/contact_dark.png index 584a4669e..a1c77479a 100644 Binary files a/img/contact_dark.png and b/img/contact_dark.png differ diff --git a/img/group.png b/img/group.png index eadc7487b..3afec19c0 100644 Binary files a/img/group.png and b/img/group.png differ diff --git a/img/group_2x.png b/img/group_2x.png new file mode 100644 index 000000000..eadc7487b Binary files /dev/null and b/img/group_2x.png differ diff --git a/img/group_dark.png b/img/group_dark.png index 55147f6c9..b5875a234 100644 Binary files a/img/group_dark.png and b/img/group_dark.png differ diff --git a/img/icons/qtox-128x128.png b/img/icons/qtox-128x128.png new file mode 100644 index 000000000..a790ae173 Binary files /dev/null and b/img/icons/qtox-128x128.png differ diff --git a/img/icons/qtox-14x14.png b/img/icons/qtox-14x14.png new file mode 100644 index 000000000..54890d417 Binary files /dev/null and b/img/icons/qtox-14x14.png differ diff --git a/img/icons/qtox-16x16.png b/img/icons/qtox-16x16.png new file mode 100644 index 000000000..d2337959e Binary files /dev/null and b/img/icons/qtox-16x16.png differ diff --git a/img/icons/qtox-192x192.png b/img/icons/qtox-192x192.png new file mode 100644 index 000000000..a9a2e5daa Binary files /dev/null and b/img/icons/qtox-192x192.png differ diff --git a/img/icons/qtox-22x22.png b/img/icons/qtox-22x22.png new file mode 100644 index 000000000..7fd03360f Binary files /dev/null and b/img/icons/qtox-22x22.png differ diff --git a/img/icons/qtox-24x24.png b/img/icons/qtox-24x24.png new file mode 100644 index 000000000..243c7d27c Binary files /dev/null and b/img/icons/qtox-24x24.png differ diff --git a/img/icons/qtox-256x256.png b/img/icons/qtox-256x256.png new file mode 100644 index 000000000..ea5cefa74 Binary files /dev/null and b/img/icons/qtox-256x256.png differ diff --git a/img/icons/qtox-32x32.png b/img/icons/qtox-32x32.png new file mode 100644 index 000000000..a33fc5df5 Binary files /dev/null and b/img/icons/qtox-32x32.png differ diff --git a/img/icons/qtox-36x36.png b/img/icons/qtox-36x36.png new file mode 100644 index 000000000..f51945091 Binary files /dev/null and b/img/icons/qtox-36x36.png differ diff --git a/img/icons/qtox-48x48.png b/img/icons/qtox-48x48.png new file mode 100644 index 000000000..9ab7b0174 Binary files /dev/null and b/img/icons/qtox-48x48.png differ diff --git a/img/icons/qtox-512x512.png b/img/icons/qtox-512x512.png new file mode 100644 index 000000000..b02bbdfea Binary files /dev/null and b/img/icons/qtox-512x512.png differ diff --git a/img/icons/qtox-64x64.png b/img/icons/qtox-64x64.png new file mode 100644 index 000000000..2ff34310b Binary files /dev/null and b/img/icons/qtox-64x64.png differ diff --git a/img/icons/qtox-72x72.png b/img/icons/qtox-72x72.png new file mode 100644 index 000000000..5d33db7f8 Binary files /dev/null and b/img/icons/qtox-72x72.png differ diff --git a/img/icons/qtox-96x96.png b/img/icons/qtox-96x96.png new file mode 100644 index 000000000..b97f35edb Binary files /dev/null and b/img/icons/qtox-96x96.png differ diff --git a/img/icons/qtox.svg b/img/icons/qtox.svg new file mode 100644 index 000000000..4369cd63e --- /dev/null +++ b/img/icons/qtox.svg @@ -0,0 +1,4 @@ + + + + diff --git a/img/settings/av.png b/img/settings/av.png new file mode 100644 index 000000000..f96f88ea8 Binary files /dev/null and b/img/settings/av.png differ diff --git a/img/settings/general.png b/img/settings/general.png new file mode 100644 index 000000000..61d9e2bf1 Binary files /dev/null and b/img/settings/general.png differ diff --git a/img/settings/identity.png b/img/settings/identity.png new file mode 100644 index 000000000..806bb525e Binary files /dev/null and b/img/settings/identity.png differ diff --git a/img/settings/privacy.png b/img/settings/privacy.png new file mode 100644 index 000000000..763478290 Binary files /dev/null and b/img/settings/privacy.png differ diff --git a/main.cpp b/main.cpp index 1cdac827e..e21c1c647 100644 --- a/main.cpp +++ b/main.cpp @@ -15,7 +15,7 @@ */ #include "widget/widget.h" -#include "settings.h" +#include "misc/settings.h" #include #include #include @@ -27,6 +27,10 @@ int main(int argc, char *argv[]) a.setApplicationName("qTox"); a.setOrganizationName("Tox"); + // Windows platform plugins DLL hell fix + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); + a.addLibraryPath("platforms"); + // Load translations QTranslator translator; if (Settings::getInstance().getUseTranslations()) diff --git a/mainwindow.ui b/mainwindow.ui index 7f9cd12c5..78c16d345 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 716 - 543 + 710 + 537 @@ -37,193 +37,6 @@ 0 - - - - - 0 - 0 - - - - - 0 - 23 - - - - - 16777215 - 23 - - - - - 4 - - - 5 - - - 0 - - - 1 - - - 0 - - - - - - 16 - 16 - - - - - 16 - 16 - - - - - 16 - 16 - - - - QToolButton::InstantPopup - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 15 - 1 - - - - - - - - - 100 - 22 - - - - - 16777215 - 22 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 134 - 20 - - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - 22 - 22 - - - - true - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - 22 - 22 - - - - true - - - - - - - - 22 - 22 - - - - - 22 - 22 - - - - - 22 - 22 - - - - true - - - - - - @@ -548,458 +361,13 @@ QSplitter:handle{ false - + 0 0 - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 255 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - 0 @@ -1027,424 +395,6 @@ QSplitter:handle{ 0 - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 255 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - @@ -1452,29 +402,20 @@ QSplitter:handle{ 0 - - + + + Qt::Horizontal + + + QSizePolicy::Maximum + + - 40 - 40 + 5 + 20 - - - 40 - 40 - - - - - - - :/img/contact.png - - - true - - + @@ -2092,8 +1033,8 @@ QSplitter:handle{ 0 0 - 263 - 373 + 288 + 400 @@ -2111,424 +1052,6 @@ QSplitter:handle{ 0 - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 42 - 42 - 42 - - - - - - - 35 - 35 - 35 - - - - - - - 14 - 14 - 14 - - - - - - - 18 - 18 - 18 - - - - - - - 14 - 14 - 14 - - - - - - - 255 - 255 - 255 - - - - - - - 14 - 14 - 14 - - - - - - - 28 - 28 - 28 - - - - - - - 28 - 28 - 28 - - - - - - - 0 - 0 - 0 - - - - - - - 28 - 28 - 28 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - true - 0 @@ -2596,7 +1119,7 @@ QSplitter:handle{ - :/img/group.png:/img/group.png + :/img/group_2x.png:/img/group_2x.png true @@ -3124,13 +1647,13 @@ QSplitter:handle{ 0 - 60 + 57 16777215 - 60 + 57 @@ -3227,8 +1750,8 @@ QSplitter:handle{ 0 0 - 716 - 23 + 710 + 25 diff --git a/cdata.cpp b/misc/cdata.cpp similarity index 100% rename from cdata.cpp rename to misc/cdata.cpp diff --git a/cdata.h b/misc/cdata.h similarity index 100% rename from cdata.h rename to misc/cdata.h diff --git a/cstring.cpp b/misc/cstring.cpp similarity index 72% rename from cstring.cpp rename to misc/cstring.cpp index 6d7815e9c..b9217fbc8 100644 --- a/cstring.cpp +++ b/misc/cstring.cpp @@ -17,10 +17,23 @@ #include "cstring.h" #include -CString::CString(const QString& string) +CString::CString(const QString& string) : + CString(string.toUtf8()) { - cString = new uint8_t[string.length() * MAX_SIZE_OF_UTF8_ENCODED_CHARACTER](); - cStringSize = fromString(string, cString); +} + +CString::CString(const QByteArray& ba_string) +{ + cString = new uint8_t[ba_string.size()](); + cStringSize = ba_string.size(); + memcpy(cString, reinterpret_cast(ba_string.data()), cStringSize); +} + +CString::CString(const CString &cstr) +{ + cStringSize = cstr.cStringSize; + cString = new uint8_t[cStringSize](); + memcpy(cString, cstr.cString, cStringSize); } CString::~CString() diff --git a/cstring.h b/misc/cstring.h similarity index 90% rename from cstring.h rename to misc/cstring.h index a75ed6468..869fa29f2 100644 --- a/cstring.h +++ b/misc/cstring.h @@ -20,24 +20,26 @@ #include class QString; +class QByteArray; class CString { public: explicit CString(const QString& string); + explicit CString(const QByteArray& ba_string); + explicit CString(const CString& cstr); ~CString(); uint8_t* data(); uint16_t size(); static QString toString(const uint8_t* cMessage, const uint16_t cMessageSize); + static uint16_t fromString(const QString& message, uint8_t* cMessage); 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); }; #endif // CSTRING_H diff --git a/settings.cpp b/misc/settings.cpp similarity index 83% rename from settings.cpp rename to misc/settings.cpp index 2390257e6..aa8aa4047 100644 --- a/settings.cpp +++ b/misc/settings.cpp @@ -110,7 +110,11 @@ void Settings::load() enableIPv6 = s.value("enableIPv6", true).toBool(); useTranslations = s.value("useTranslations", true).toBool(); makeToxPortable = s.value("makeToxPortable", false).toBool(); - currentProfile = s.value("currentProfile", "").toString(); + forceTCP = s.value("forceTCP", false).toBool(); + useProxy = s.value("useProxy", false).toBool(); + proxyAddr = s.value("proxyAddr", "").toString(); + proxyPort = s.value("proxyPort", 0).toInt(); + currentProfile = s.value("currentProfile", "").toString(); s.endGroup(); s.beginGroup("Widgets"); @@ -131,7 +135,6 @@ void Settings::load() timestampFormat = s.value("timestampFormat", "hh:mm").toString(); minimizeOnClose = s.value("minimizeOnClose", false).toBool(); useNativeStyle = s.value("nativeStyle", false).toBool(); - useNativeDecoration = s.value("nativeDecoration", true).toBool(); s.endGroup(); s.beginGroup("State"); @@ -211,6 +214,10 @@ void Settings::save(QString path) s.setValue("enableIPv6", enableIPv6); s.setValue("useTranslations",useTranslations); s.setValue("makeToxPortable",makeToxPortable); + s.setValue("useProxy", useProxy); + s.setValue("forceTCP", forceTCP); + s.setValue("proxyAddr", proxyAddr); + s.setValue("proxyPort", proxyPort); s.setValue("currentProfile", currentProfile); s.endGroup(); @@ -232,7 +239,6 @@ void Settings::save(QString path) s.setValue("timestampFormat", timestampFormat); s.setValue("minimizeOnClose", minimizeOnClose); s.setValue("nativeStyle", useNativeStyle); - s.setValue("nativeDecoration", useNativeDecoration); s.endGroup(); s.beginGroup("State"); @@ -259,6 +265,58 @@ QString Settings::getSettingsDirPath() #endif } +QPixmap Settings::getSavedAvatar(const QString &ownerId) +{ + QDir dir(getSettingsDirPath()); + QString filePath = dir.filePath("avatars/"+ownerId.left(64)+".png"); + QFileInfo info(filePath); + QPixmap pic; + if (!info.exists()) + { + QString filePath = dir.filePath("avatar_"+ownerId.left(64)); + if (!QFileInfo(filePath).exists()) // try without truncation, for old self avatars + filePath = dir.filePath("avatar_"+ownerId); + pic.load(filePath); + saveAvatar(pic, ownerId); + QFile::remove(filePath); + } + else + pic.load(filePath); + return pic; +} + +void Settings::saveAvatar(QPixmap& pic, const QString& ownerId) +{ + QDir dir(getSettingsDirPath()); + dir.mkdir("avatars/"); + // ignore nospam (good idea, and also the addFriend funcs which call getAvatar don't have it) + QString filePath = dir.filePath("avatars/"+ownerId.left(64)+".png"); + pic.save(filePath, "png"); +} + +void Settings::saveAvatarHash(const QByteArray& hash, const QString& ownerId) +{ + QDir dir(getSettingsDirPath()); + dir.mkdir("avatars/"); + QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash")); + if (!file.open(QIODevice::WriteOnly)) + return; + file.write(hash); + file.close(); +} + +QByteArray Settings::getAvatarHash(const QString& ownerId) +{ + QDir dir(getSettingsDirPath()); + dir.mkdir("avatars/"); + QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash")); + if (!file.open(QIODevice::ReadOnly)) + return QByteArray(); + QByteArray out = file.readAll(); + file.close(); + return out; +} + const QList& Settings::getDhtServerList() const { return dhtServerList; @@ -313,6 +371,45 @@ void Settings::setUseTranslations(bool newValue) useTranslations = newValue; } +bool Settings::getForceTCP() const +{ + return forceTCP; +} + +void Settings::setForceTCP(bool newValue) +{ + forceTCP = newValue; +} + +bool Settings::getUseProxy() const +{ + return useProxy; +} +void Settings::setUseProxy(bool newValue) +{ + useProxy = newValue; +} + +QString Settings::getProxyAddr() const +{ + return proxyAddr; +} + +void Settings::setProxyAddr(const QString& newValue) +{ + proxyAddr = newValue; +} + +int Settings::getProxyPort() const +{ + return proxyPort; +} + +void Settings::setProxyPort(int newValue) +{ + proxyPort = newValue; +} + bool Settings::getEnableLogging() const { return enableLogging; @@ -438,16 +535,6 @@ void Settings::setUseNativeStyle(bool value) useNativeStyle = value; } -bool Settings::getUseNativeDecoration() const -{ - return useNativeDecoration; -} - -void Settings::setUseNativeDecoration(bool value) -{ - useNativeDecoration = value; -} - QByteArray Settings::getWindowGeometry() const { return windowGeometry; diff --git a/settings.h b/misc/settings.h similarity index 88% rename from settings.h rename to misc/settings.h index 291c09270..a7eeb9d8b 100644 --- a/settings.h +++ b/misc/settings.h @@ -19,6 +19,7 @@ #include #include +#include class Settings : public QObject { @@ -54,12 +55,30 @@ public: bool getUseTranslations() const; void setUseTranslations(bool newValue); + bool getForceTCP() const; + void setForceTCP(bool newValue); + + QString getProxyAddr() const; + void setProxyAddr(const QString& newValue); + + bool getUseProxy() const; + void setUseProxy(bool newValue); + + int getProxyPort() const; + void setProxyPort(int newValue); + bool getEnableLogging() const; void setEnableLogging(bool newValue); bool getEncryptLogs() const; void setEncryptLogs(bool newValue); + QPixmap getSavedAvatar(const QString& ownerId); + void saveAvatar(QPixmap& pic, const QString& ownerId); + + QByteArray getAvatarHash(const QString& ownerId); + void saveAvatarHash(const QByteArray& hash, const QString& ownerId); + // 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, @@ -117,9 +136,6 @@ public: bool getUseNativeStyle() const; void setUseNativeStyle(bool value); - bool getUseNativeDecoration() const; - void setUseNativeDecoration(bool value); - QByteArray getWindowGeometry() const; void setWindowGeometry(const QByteArray &value); @@ -152,6 +168,13 @@ private: bool enableIPv6; bool useTranslations; static bool makeToxPortable; + + bool forceTCP; + + bool useProxy; + QString proxyAddr; + int proxyPort; + QString currentProfile; bool enableLogging; @@ -167,7 +190,6 @@ private: int emojiFontPointSize; bool minimizeOnClose; bool useNativeStyle; - bool useNativeDecoration; QByteArray windowGeometry; QByteArray windowState; QByteArray splitterState; @@ -182,7 +204,7 @@ private: signals: //void dataChanged(); - void dhtServerListChanged(bool reset = false); + void dhtServerListChanged(); void logStorageOptsChanged(); void smileyPackChanged(); void emojiFontChanged(); diff --git a/smileypack.cpp b/misc/smileypack.cpp similarity index 81% rename from smileypack.cpp rename to misc/smileypack.cpp index 5af07194f..57a754c76 100644 --- a/smileypack.cpp +++ b/misc/smileypack.cpp @@ -40,24 +40,38 @@ SmileyPack& SmileyPack::getInstance() return smileyPack; } -QList > SmileyPack::listSmileyPacks(const QString &path) +QList > SmileyPack::listSmileyPacks(const QStringList &paths) { QList > smileyPacks; - QDir dir(path); - foreach (const QString& subdirectory, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + for (QString path : paths) { - dir.cd(subdirectory); + if (path.leftRef(1) == "~") + path.replace(0, 1, QDir::homePath()); - QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files); - if (entries.size() > 0) // does it contain a file called emoticons.xml? + QDir dir(path); + if (!dir.exists()) + continue; + + for (const QString& subdirectory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { - QString packageName = dir.dirName(); - QString relPath = QDir(QCoreApplication::applicationDirPath()).relativeFilePath(entries[0].absoluteFilePath()); - smileyPacks << QPair(packageName, relPath); - } + dir.cd(subdirectory); - dir.cdUp(); + QFileInfoList entries = dir.entryInfoList(QStringList() << "emoticons.xml", QDir::Files); + if (entries.size() > 0) // does it contain a file called emoticons.xml? + { + QString packageName = dir.dirName(); + QString absPath = entries[0].absoluteFilePath(); + QString relPath = QDir(QCoreApplication::applicationDirPath()).relativeFilePath(absPath); + + if (relPath.leftRef(2) == "..") + smileyPacks << QPair(packageName, absPath); + else + smileyPacks << QPair(packageName, relPath); // use relative path for subdirectories + } + + dir.cdUp(); + } } return smileyPacks; diff --git a/smileypack.h b/misc/smileypack.h similarity index 76% rename from smileypack.h rename to misc/smileypack.h index 9f4ea31ba..1d4c9ef30 100644 --- a/smileypack.h +++ b/misc/smileypack.h @@ -22,7 +22,10 @@ #include #include -#define SMILEYPACK_DEFAULT_PATH "./smileys" +#define SMILEYPACK_SEARCH_PATHS \ + { \ + "./smileys", "/usr/share/qtox/smileys", "/usr/share/emoticons", "~/.kde4/share/emoticons", "~/.kde/share/emoticons" \ + } //maps emoticons to smileys class SmileyPack : public QObject @@ -30,10 +33,10 @@ class SmileyPack : public QObject Q_OBJECT public: static SmileyPack& getInstance(); - static QList> listSmileyPacks(const QString& path = SMILEYPACK_DEFAULT_PATH); + static QList > listSmileyPacks(const QStringList& paths = SMILEYPACK_SEARCH_PATHS); static bool isValid(const QString& filename); - bool load(const QString &filename); + bool load(const QString& filename); QString smileyfied(QString msg); QList getEmoticons() const; QString getAsRichText(const QString& key); diff --git a/misc/style.cpp b/misc/style.cpp new file mode 100644 index 000000000..6d7b191a2 --- /dev/null +++ b/misc/style.cpp @@ -0,0 +1,144 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "style.h" +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include + +// helper functions +QFont appFont(int pixelSize, int weight) +{ + QFont font; + font.setPixelSize(pixelSize); + font.setWeight(weight); + return font; +} + +QString qssifyFont(QFont font) +{ + return QString("%1 %2px \"%3\"") + .arg(font.weight()*8) + .arg(font.pixelSize()) + .arg(font.family()); +} + +QString Style::getStylesheet(const QString &filename) +{ + if (!Settings::getInstance().getUseNativeStyle()) + { + QFile file(filename); + if (file.open(QFile::ReadOnly | QFile::Text)) + return resolve(file.readAll()); + else + qWarning() << "Style: Stylesheet " << filename << " not found"; + } + + return QString(); +} + +QColor Style::getColor(Style::ColorPalette entry) +{ + // colors as defined in + // https://github.com/ItsDuke/Tox-UI/blob/master/UI%20GUIDELINES.md + static QColor palette[] = { + QColor("#6bc260"), + QColor("#cebf44"), + QColor("#c84e4e"), + QColor("#000000"), + QColor("#1c1c1c"), + QColor("#414141"), + QColor("#414141").lighter(120), + QColor("#d1d1d1"), + QColor("#ffffff"), + }; + + return palette[entry]; +} + +QFont Style::getFont(Style::Font font) +{ + // fonts as defined in + // https://github.com/ItsDuke/Tox-UI/blob/master/UI%20GUIDELINES.md + + static int defSize = QFontInfo(QFont()).pixelSize(); + + static QFont fonts[] = { + appFont(defSize + 2, QFont::Bold), + appFont(defSize , QFont::Normal), + appFont(defSize , QFont::Bold), + appFont(defSize - 1, QFont::Normal), + appFont(defSize - 1, QFont::Bold), + appFont(defSize - 2, QFont::Normal), + appFont(defSize - 2, QFont::Light), + }; + + return fonts[font]; +} + +QString Style::resolve(QString qss) +{ + static QMap dict = { + // colors + {"@green", getColor(Green).name()}, + {"@yellow", getColor(Yellow).name()}, + {"@red", getColor(Red).name()}, + {"@black", getColor(Black).name()}, + {"@darkGrey", getColor(DarkGrey).name()}, + {"@mediumGrey", getColor(MediumGrey).name()}, + {"@mediumGreyLight", getColor(MediumGreyLight).name()}, + {"@lightGrey", getColor(LightGrey).name()}, + {"@white", getColor(White).name()}, + + // fonts + {"@extraBig", qssifyFont(getFont(ExtraBig))}, + {"@big", qssifyFont(getFont(Big))}, + {"@bigBold", qssifyFont(getFont(BigBold))}, + {"@medium", qssifyFont(getFont(Medium))}, + {"@mediumBold", qssifyFont(getFont(MediumBold))}, + {"@small", qssifyFont(getFont(Small))}, + {"@smallLight", qssifyFont(getFont(SmallLight))}, + }; + + for (const QString& key : dict.keys()) + { + qss.replace(QRegularExpression(QString("%1\\b").arg(key)), dict[key]); + } + + return qss; +} + +void Style::repolish(QWidget *w) +{ + w->style()->unpolish(w); + w->style()->polish(w); + + for (QObject* o : w->children()) + { + QWidget* c = qobject_cast(o); + if (c) + { + c->style()->unpolish(c); + c->style()->polish(c); + } + } +} diff --git a/misc/style.h b/misc/style.h new file mode 100644 index 000000000..a07064e04 --- /dev/null +++ b/misc/style.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 STYLE_H +#define STYLE_H + +#include +#include + +class QString; +class QWidget; + +class Style +{ +public: + enum ColorPalette + { + Green, + Yellow, + Red, + Black, + DarkGrey, + MediumGrey, + MediumGreyLight, + LightGrey, + White, + }; + + enum Font + { + ExtraBig, // [SystemDefault + 2]px, bold + Big, // [SystemDefault ]px + BigBold, // [SystemDefault ]px, bold + Medium, // [SystemDefault - 1]px + MediumBold, // [SystemDefault - 1]px, bold + Small, // [SystemDefault - 2]px + SmallLight // [SystemDefault - 2]px, light + }; + + static QString getStylesheet(const QString& filename); + static QColor getColor(ColorPalette entry); + static QFont getFont(Font font); + static QString resolve(QString qss); + static void repolish(QWidget* w); + +private: + Style(); +}; + +#endif // STYLE_H diff --git a/qTox.desktop b/qTox.desktop new file mode 100644 index 000000000..afd74b440 --- /dev/null +++ b/qTox.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=qTox +GenericName=Tox client +Comment=qTox is a powerful Tox client that follows the Tox design guidelines. +TryExec=qtox +Exec=qtox +Icon=qtox +Categories=InstantMessaging;;AudioVideo;Network; +Terminal=false +MimeType=x-scheme-handler/tox; diff --git a/qtox.pro b/qtox.pro index 7b52d779c..14e233ad1 100644 --- a/qtox.pro +++ b/qtox.pro @@ -26,13 +26,21 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = qtox TEMPLATE = app FORMS += \ - mainwindow.ui + mainwindow.ui \ + widget/form/settings/generalsettings.ui \ + widget/form/settings/avsettings.ui \ + widget/form/settings/identitysettings.ui CONFIG += c++11 TRANSLATIONS = translations/de.ts \ translations/fr.ts \ translations/it.ts \ - translations/ru.ts + translations/ru.ts \ + translations/pirate.ts \ + translations/pl.ts \ + translations/fi.ts \ + translations/mannol.ts \ + translations/uk.ts RESOURCES += res.qrc @@ -44,8 +52,9 @@ contains(JENKINS,YES) { # Rules for Windows, Mac OSX, and Linux win32 { - LIBS += -L$$PWD/libs/lib -llibopencv_core249 -llibopencv_highgui249 -llibopencv_imgproc249 -lOpenAL32 - LIBS += $$PWD/libs/lib/libtoxav.a $$PWD/libs/lib/libopus.a $$PWD/libs/lib/libvpx.a $$PWD/libs/lib/libtoxcore.a -lws2_32 $$PWD/libs/lib/libsodium.a -lpthread -liphlpapi + LIBS += -liphlpapi -L$$PWD/libs/lib -ltoxav -ltoxcore -lvpx -lpthread + LIBS += -L$$PWD/libs/lib -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lOpenAL32 -lopus + LIBS += -lz -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32 } else { macx { LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui @@ -54,7 +63,10 @@ win32 { contains(STATICPKG, YES) { target.path = /usr/bin INSTALLS += target - LIBS += -L$$PWD/libs/lib/ -Wl,-Bstatic -ltoxcore -ltoxav -lsodium -Wl,-Bdynamic -lopus -lvpx -lopenal -lopencv_core -lopencv_highgui + LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic + LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig + LIBS += -Wl,-Bdynamic -ltbb -lv4l1 -lv4l2 -lgnutls -lrtmp -lgnutls -lavformat -lavcodec -lavutil -lavfilter -lswscale -lusb-1.0 + } else { LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui } @@ -79,7 +91,12 @@ win32 { HEADERS += widget/form/addfriendform.h \ widget/form/chatform.h \ widget/form/groupchatform.h \ - widget/form/settingsform.h \ + widget/form/settingswidget.h \ + widget/form/settings/genericsettings.h \ + widget/form/settings/generalform.h \ + widget/form/settings/identityform.h \ + widget/form/settings/privacyform.h \ + widget/form/settings/avform.h \ widget/form/filesform.h \ widget/tool/chattextedit.h \ widget/tool/friendrequestdialog.h \ @@ -89,34 +106,43 @@ HEADERS += widget/form/addfriendform.h \ friend.h \ group.h \ grouplist.h \ - settings.h \ + misc/settings.h \ core.h \ friendlist.h \ - cdata.h \ - cstring.h \ + misc/cdata.h \ + misc/cstring.h \ widget/selfcamview.h \ widget/camera.h \ widget/netcamview.h \ - smileypack.h \ + misc/smileypack.h \ widget/emoticonswidget.h \ - style.h \ + misc/style.h \ widget/adjustingscrollarea.h \ widget/croppinglabel.h \ widget/friendlistwidget.h \ widget/genericchatroomwidget.h \ widget/form/genericchatform.h \ - widget/tool/chataction.h \ + widget/tool/chatactions/chataction.h \ widget/chatareawidget.h \ filetransferinstance.h \ corestructs.h \ coredefines.h \ - coreav.h + coreav.h \ + widget/tool/chatactions/messageaction.h \ + widget/tool/chatactions/filetransferaction.h \ + widget/tool/chatactions/systemmessageaction.h \ + widget/tool/chatactions/actionaction.h \ + widget/maskablepixmapwidget.h SOURCES += \ widget/form/addfriendform.cpp \ widget/form/chatform.cpp \ widget/form/groupchatform.cpp \ - widget/form/settingsform.cpp \ + widget/form/settingswidget.cpp \ + widget/form/settings/generalform.cpp \ + widget/form/settings/identityform.cpp \ + widget/form/settings/privacyform.cpp \ + widget/form/settings/avform.cpp \ widget/form/filesform.cpp \ widget/tool/chattextedit.cpp \ widget/tool/friendrequestdialog.cpp \ @@ -129,22 +155,27 @@ SOURCES += \ group.cpp \ grouplist.cpp \ main.cpp \ - settings.cpp \ - cdata.cpp \ - cstring.cpp \ + misc/settings.cpp \ + misc/cdata.cpp \ + misc/cstring.cpp \ widget/selfcamview.cpp \ widget/camera.cpp \ widget/netcamview.cpp \ - smileypack.cpp \ + misc/smileypack.cpp \ widget/emoticonswidget.cpp \ - style.cpp \ + misc/style.cpp \ widget/adjustingscrollarea.cpp \ widget/croppinglabel.cpp \ widget/friendlistwidget.cpp \ coreav.cpp \ widget/genericchatroomwidget.cpp \ widget/form/genericchatform.cpp \ - widget/tool/chataction.cpp \ + widget/tool/chatactions/chataction.cpp \ widget/chatareawidget.cpp \ filetransferinstance.cpp \ - corestructs.cpp + corestructs.cpp \ + widget/tool/chatactions/messageaction.cpp \ + widget/tool/chatactions/filetransferaction.cpp \ + widget/tool/chatactions/systemmessageaction.cpp \ + widget/tool/chatactions/actionaction.cpp \ + widget/maskablepixmapwidget.cpp diff --git a/res.qrc b/res.qrc index cbf038a58..375312b1d 100644 --- a/res.qrc +++ b/res.qrc @@ -84,18 +84,6 @@ ui/videoButton/videoButtonYellowPressed.png img/group_dark.png ui/window/applicationIcon.png - ui/window/closeButton.png - ui/window/closeButtonHover.png - ui/window/closeButtonPressed.png - ui/window/maximizeButton.png - ui/window/maximizeButtonHover.png - ui/window/maximizeButtonPressed.png - ui/window/minimizeButton.png - ui/window/minimizeButtonHover.png - ui/window/minimizeButtonPressed.png - ui/window/restoreButton.png - ui/window/restoreButtonHover.png - ui/window/restoreButtonPressed.png ui/friendList/friendList.css ui/window/window.css img/status/dot_busy.png @@ -135,5 +123,22 @@ ui/fileTransferInstance/emptyRGreenFileButton.png ui/fileTransferInstance/emptyRRedFileButton.png audio/notification.pcm + audio/ToxicIncomingCall.pcm + img/settings/general.png + img/settings/identity.png + img/settings/privacy.png + img/settings/av.png + translations/pl.qm + translations/fi.qm + translations/mannol.qm + translations/uk.qm + img/avatar_mask.png + img/group_2x.png + ui/chatroomWidgets/genericChatroomWidget.css + ui/fileTransferInstance/sliverRTEdge.png + ui/window/statusPanel.css + ui/settings/mainContent.css + ui/settings/mainHead.css + translations/pirate.qm diff --git a/smileys/default/Kappa.png b/smileys/default/Kappa.png new file mode 100644 index 000000000..a7ebbec4d Binary files /dev/null and b/smileys/default/Kappa.png differ diff --git a/smileys/default/emoticons.xml b/smileys/default/emoticons.xml index d1fe8c715..c6f816f7b 100644 --- a/smileys/default/emoticons.xml +++ b/smileys/default/emoticons.xml @@ -82,4 +82,7 @@ XD + + :Kappa: + diff --git a/tools/buildPackages.sh b/tools/buildPackages.sh index dc01f4a82..501d7b429 100755 --- a/tools/buildPackages.sh +++ b/tools/buildPackages.sh @@ -56,9 +56,19 @@ done if [[ $OPT_APT = "true" ]]; then echo "Installing missing tools (if any)..." if [[ $EUID -ne 0 && $OPT_SUDO = "true" ]]; then - sudo apt-get install wget debhelper cdbs devscripts alien tar gzip build-essential + sudo apt-get install wget debhelper cdbs devscripts alien tar gzip build-essential sudo autoconf libtool pkg-config libvpx-dev -y else - apt-get install wget debhelper cdbs devscripts alien tar gzip build-essential + apt-get install wget debhelper cdbs devscripts alien tar gzip build-essential sudo autoconf libtool pkg-config libvpx-dev -y + fi +fi + +# Get the requried dependencies if needed +if [[ $OPT_APT = "true" ]]; then + echo "Installing missing dependencies (if any)..." + if [[ $EUID -ne 0 && $OPT_SUDO = "true" ]]; then + sudo apt-get install qt5-qmake libopenal-dev libopencv-dev libopus-dev -y + else + apt-get install qt5-qmake libopenal-dev libopencv-dev libopus-dev -y fi fi @@ -77,7 +87,9 @@ mv qTox-master $VERNAME # Build packages cd $VERNAME -debuild -us -uc +./bootstrap.sh --local +debuild -us -uc -aamd64 +debuild -us -uc -ai386 cd .. # alien warns that it should probably be run as root... diff --git a/tools/publish.py b/tools/publish.py new file mode 100644 index 000000000..ea9624dfd --- /dev/null +++ b/tools/publish.py @@ -0,0 +1,45 @@ +#!/usr/bin/python2.7 + +from github3 import login, GitHub +from getpass import getpass, getuser +import time +import datetime +import sys +try: + import readline +except ImportError: + pass + +platform='' +if (len(sys.argv) >= 3): + platform=sys.argv[2] + +versionNumber = str(time.time()) +if (platform != ''): + version = 'qtox-'+platform+'-'+versionNumber +else: + version = 'qtox-'+versionNumber +if (platform == 'windows'): + title = 'qTox Windows '+datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') +elif (platform == 'linux'): + title = 'qTox Linux '+datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') +else: + title = 'qTox '+datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S') +user = "tux3" +password = "" +if password == "": + password = getpass('GitHub password for {0}: '.format(user)) + +# Obviously you could also prompt for an OAuth token +if not (user and password): + print("Cowardly refusing to login without a username and password.") + sys.exit(1) + +g = login(user, password) +repo = g.repository('tux3', 'qTox') +release = repo.create_release(version,'master',title,'This is an automated release of qTox, published by qTox\'s continous integration server.',False,False) + +if (len(sys.argv) >= 2): + file = open(sys.argv[1], 'r') + release.upload_asset('application/octet-stream',sys.argv[1],file) + diff --git a/translations/de.qm b/translations/de.qm index c5132443e..8a6769d20 100644 Binary files a/translations/de.qm and b/translations/de.qm differ diff --git a/translations/de.ts b/translations/de.ts index ab61ba9ad..f8b790182 100644 --- a/translations/de.ts +++ b/translations/de.ts @@ -1,75 +1,102 @@ + + AVPage + + + Video Settings + + + + + + Show video preview + On a button + + + + + Hide video preview + On a button + + + AddFriendForm - + Add Friends Freunde hinzufügen - + Tox ID Tox ID of the person you're sending a friend request to Tox ID - + Message The message you send in friend requests Nachricht - + Send friend request Freundschaftseinladung versenden - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Lass uns Toxen! - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Bitte gib eine gültige Tox ID ein - + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + + + + This address does not exist The DNS gives the Tox ID associated to toxme.se addresses - + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Fehler beim Auflösen des DNS - + Unexpected number of text records Error with the DNS Unererwartete Anzahl von Texteinträgen - + Unexpected number of values in text record Error with the DNS Unerwartete Anzahl von Werten innerhalb des Texteintrages - + The DNS lookup does not contain any Tox ID Error with the DNS Der DNS Eintrag enthält keine gültige TOX ID - + The DNS lookup does not contain a valid Tox ID Error with the DNS Der DNS Eintrag enthält keine gültige TOX ID @@ -78,74 +105,77 @@ Camera - Camera eror - Kamerafehler + Kamerafehler - Camera format %1 not supported, can't use the camera - Kameraformat %1 wird nicht unterstützt. Die Kamera kann nicht verwendet werden + Kameraformat %1 wird nicht unterstützt. Die Kamera kann nicht verwendet werden ChatForm - + Send a file Datei versenden - - Save chat log - Chatverlauf speichern + Chatverlauf speichern CopyableElideLabel - Copy - Kopieren + Kopieren - FileTransfertWidget + FileTransferInstance - + Save a file Title of the file saving dialog - Datei speichern + Datei speichern - + Location not writable Title of permissions popup - + You do not have permission to write that location. Choose another, or cancel the save dialog. text of permissions popup + + FileTransfertWidget + + Save a file + Title of the file saving dialog + Datei speichern + + FilesForm - + Transfered Files "Headline" of the window - + Downloads - + Uploads @@ -189,69 +219,149 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Tox ID kopieren - + Invite in group Menu to invite a friend in a groupchat In Gruppe einladen - + Remove friend Menu to remove the friend from our friendlist Freund entfernen + + GeneralPage + + + General Settings + + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + IPv6 aktivieren (empfohlen) + + + + Use translations + Text on a checkbox to enable translations + + + + + Make Tox portable + Text on a checkbox to make qTox a portable application + + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + + + + + Theme + + + + + Smiley Pack + + + + + GenericChatForm + + + + Save chat log + Chatverlauf speichern + + GroupChatForm - + %1 users in chat Number of users in chat %1 Personen im Chat - + <Unknown> <Unbekannt> - + %1 users in chat %1 Personen im Chat - Save chat log - Chatverlauf speichern + Chatverlauf speichern GroupWidget - - + + %1 users in chat %1 Personen im Chat - - + + 0 users in chat 0 Personen im Chat - + Quit group Menu to quit a groupchat Gruppe verlassen + + IdentityPage + + + Public Information + + + + + Name + Username/nick + Benutzername + + + + Status + Status message + Status + + + + Tox ID + Tox ID + + + + Your Tox ID + + + MainWindow @@ -303,73 +413,81 @@ SelfCamView - + Tox video test Title of the window to test the video/webcam Tox Video testen + + SettingsDialog + + + qTox – Settings + + + + + General + + + + + Identity + + + + + Privacy + + + + + Audio/Video + + + + + Ok + + + + + Cancel + + + + + Apply + + + SettingsForm - User Settings "Headline" of the window - Einstellungen + Einstellungen - Name Username/nick - Benutzername + Benutzername - Status Status message - Status + Status - - (click here to copy) - Click on this text to copy TID to clipboard - - - - Test video Text on a button to test the video/webcam - Video testen + Video testen - Enable IPv6 (recommended) Text on a checkbox to enable IPv6 - IPv6 aktivieren (empfohlen) - - - - Use translations - Text on a checkbox to enable translations - - - - - Make Tox portable - Text on a checkbox to make qTox a portable application - - - - - Save settings to the working directory instead of the usual conf dir - describes makeToxPortable checkbox - - - - - Smiley Pack - Text on smiley pack label - + IPv6 aktivieren (empfohlen) @@ -395,22 +513,28 @@ Strg+Q - + Online Button to set your status to 'Online' Online - + Away Button to set your status to 'Away' Abwesend - + Busy Button to set your status to 'Busy' Beschäftigt + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <Unbekannt> + diff --git a/translations/fi.qm b/translations/fi.qm new file mode 100644 index 000000000..5d27bcc6d Binary files /dev/null and b/translations/fi.qm differ diff --git a/translations/fi.ts b/translations/fi.ts new file mode 100644 index 000000000..ccd2f7272 --- /dev/null +++ b/translations/fi.ts @@ -0,0 +1,540 @@ + + + + + AVPage + + + Video Settings + Videoasetukset + + + + + Show video preview + On a button + Videon esikatselu + + + + Hide video preview + On a button + Lopeta esikatselu + + + + AddFriendForm + + + Add Friends + Lisää kontakti + + + + Tox ID + Tox ID of the person you're sending a friend request to + Tox ID + + + + Message + The message you send in friend requests + Viesti + + + + Send friend request + Lähetä kontaktipyyntö + + + + Tox me maybe? + Default message in friend requests if the field is left blank. Write something appropriate! + Lisää kontakteihin? + + + + Please fill in a valid Tox ID + Tox ID of the friend you're sending a friend request to + Anna kelvollinen Tox ID + + + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Et voi lisätä itseäsi kontaktiksi + + + + This address does not exist + The DNS gives the Tox ID associated to toxme.se addresses + Osoitetta ei ole olemassa + + + + Error while looking up DNS + The DNS gives the Tox ID associated to toxme.se addresses + Virhe DNS pyynnössä + + + + Unexpected number of text records + Error with the DNS + Virheellinen määrä tekstitietueita + + + + Unexpected number of values in text record + Error with the DNS + Odottamaton määrä tekstitietueita + + + + The DNS lookup does not contain any Tox ID + Error with the DNS + DNS vastaus ei sisällä Tox ID:tä + + + + + The DNS lookup does not contain a valid Tox ID + Error with the DNS + DNS vastaus ei sisällä kelvollista Tox ID:tä + + + + Camera + + Camera eror + Kamerafehler + + + Camera format %1 not supported, can't use the camera + Kameraformat %1 wird nicht unterstützt. Die Kamera kann nicht verwendet werden + + + + ChatForm + + + Send a file + Lähetä tiedosto + + + Save chat log + Chatverlauf speichern + + + + CopyableElideLabel + + Copy + Kopieren + + + + FileTransferInstance + + + Save a file + Title of the file saving dialog + Tallenna tiedosto + + + + Location not writable + Title of permissions popup + Kohteeseen ei voi tallentaa + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + Käyttöoikeudet eivät riitä kohteeseen tallentamiseen. Valitse toinen kohde tai peru. + + + + FileTransfertWidget + + Save a file + Title of the file saving dialog + Datei speichern + + + + FilesForm + + + Transfered Files + "Headline" of the window + Tiedostojen siirrot + + + + Downloads + Ladatut + + + + Uploads + Lähetetyt + + + + FriendRequestDialog + + + Friend request + Title of the window to aceept/deny a friend request + Kontaktipyyntö + + + + Someone wants to make friends with you + Sinulle on lähetetty kontaktipyyntö + + + + User ID: + Käyttäjän ID: + + + + Friend request message: + Kontaktipyynnön viesti: + + + + Accept + Accept a friend request + Hyväksy + + + + Reject + Reject a friend request + Hylkää + + + + FriendWidget + + + Copy friend ID + Menu to copy the Tox ID of that friend + Kopioi kontaktin ID + + + + Invite in group + Menu to invite a friend in a groupchat + Kutsu ryhmään + + + + Remove friend + Menu to remove the friend from our friendlist + Poista kontakti + + + + GeneralPage + + + General Settings + Yleiset asetukset + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Aktivoi IPv6 (suositeltu) + + + + Use translations + Text on a checkbox to enable translations + Käytä käännöksiä + + + + Make Tox portable + Text on a checkbox to make qTox a portable application + Tee ohjelmasta siirrettävä + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Tallenna asetukset työhakemistoon normaalin asetushakemiston sijaan. + + + + Theme + Teema + + + + Smiley Pack + Hymiö-paketti + + + + GenericChatForm + + + + Save chat log + Tallenna keskustelu + + + + GroupChatForm + + + %1 users in chat + Number of users in chat + %1 henkilö keskustelussa + + + + <Unknown> + <Tuntematon> + + + + %1 users in chat + %1 henkilöä keskustelussa + + + Save chat log + Chatverlauf speichern + + + + GroupWidget + + + + %1 users in chat + %1 henkilöä keskustelussa + + + + + 0 users in chat + 0 henkilöä keskustelussa + + + + Quit group + Menu to quit a groupchat + Sulje ryhmä + + + + IdentityPage + + + Public Information + Julkiset tiedot + + + + Name + Username/nick + Nimi + + + + Status + Status message + Tila + + + + Tox ID + Tox ID + + + + Your Tox ID + Sinun Tox ID + + + + MainWindow + + + qTox + + + + + Your name + Sinun nimesi + + + + Your status + Sinun tilasi + + + + Add friends + Lisää kontakti + + + + Create a group chat + Luo keskusteluryhmä + + + + View completed file transfers + Näytä valmiit tiedostojensiirrot + + + + Change your settings + Muuta asetuksiasi + + + + Close + Sulje + + + + Ctrl+Q + Ctrl+Q + + + + SelfCamView + + + Tox video test + Title of the window to test the video/webcam + Tox videotesti + + + + SettingsDialog + + + qTox – Settings + qTox - Asetukset + + + + General + Yleiset + + + + Identity + Identiteetti + + + + Privacy + Yksityisyys + + + + Audio/Video + Audio/Video + + + + Ok + Ok + + + + Cancel + Peru + + + + Apply + Ota käyttöön + + + + SettingsForm + + User Settings + "Headline" of the window + Einstellungen + + + Name + Username/nick + Benutzername + + + Status + Status message + Status + + + Test video + Text on a button to test the video/webcam + Video testen + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + IPv6 aktivieren (empfohlen) + + + + Widget + + Tox + Tox + + + Your name + Dein Name + + + Your status + Dein Status + + + Close + Schließen + + + Ctrl+Q + Strg+Q + + + + Online + Button to set your status to 'Online' + Online + + + + Away + Button to set your status to 'Away' + Poissa + + + + Busy + Button to set your status to 'Busy' + Kiireinen + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <tuntematon> + + + diff --git a/translations/fr.qm b/translations/fr.qm index e3d68b2e1..ff6462596 100644 Binary files a/translations/fr.qm and b/translations/fr.qm differ diff --git a/translations/fr.ts b/translations/fr.ts index 435d022c7..c43857b12 100644 --- a/translations/fr.ts +++ b/translations/fr.ts @@ -1,75 +1,136 @@ + + AVForm + + + Audio/Video settings + + + + + Hide video preview + On a button + Cacher l'aperçu vidéo + + + + Show video preview + On a button + Montrer l'aperçu vidéo + + + + AVPage + + Video Settings + Options vidéo + + + Show video preview + On a button + Montrer l'aperçu vidéo + + + Hide video preview + On a button + Cacher l'aperçu vidéo + + + + AVSettings + + + Form + + + + + Video settings + + + + + Show video preview + Montrer l'aperçu vidéo + + AddFriendForm - + Add Friends Ajouter des amis - + Tox ID Tox ID of the person you're sending a friend request to ID Tox - + Message The message you send in friend requests Message - + Send friend request Envoyer la demande d'ami - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Je souhaiterais vous ajouter à mes contacts - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Merci de remplir un ID Tox valide - - This address does not exist - The DNS gives the Tox ID associated to toxme.se addresses - + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Vous ne pouvez pas vous ajouter vous même en temps qu'ami! - + + This address does not exist + The DNS gives the Tox ID associated to toxme.se addresses + Cette addresse n'existe pas + + + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Erreur en consultant le serveur DNS - + Unexpected number of text records Error with the DNS Nombre d'entrées texte innatendu - + Unexpected number of values in text record Error with the DNS Nombre d'entrées numériques dans l'entrée texte innatendu - + The DNS lookup does not contain any Tox ID Error with the DNS La réponse DNS ne contient aucun ID Tox - + The DNS lookup does not contain a valid Tox ID Error with the DNS La réponse DNS ne contient pas d'ID Tox valide @@ -78,76 +139,93 @@ Camera - Camera eror - Erreur de caméra + Erreur de caméra - Camera format %1 not supported, can't use the camera - Format %1 de la caméra non supporté, impossible de l'utiliser + Format %1 de la caméra non supporté, impossible de l'utiliser ChatForm - + Send a file Envoyer un fichier - - Save chat log - Sauvegarder l'historique de conversation + Sauvegarder l'historique de conversation CopyableElideLabel - Copy - Copier + Copier - FileTransfertWidget + Core - + + Encrypted profile + + + + + Your tox profile seems to be encrypted, qTox can't open it +Do you want to erase this profile ? + + + + + FileTransferInstance + + Save a file Title of the file saving dialog Sauvegarder un fichier - + Location not writable Title of permissions popup - + Impossible d'écrire ici - + You do not have permission to write that location. Choose another, or cancel the save dialog. text of permissions popup - + Vous n'avez pas la permission d'écrire ici. Choisissez un audre endroit, ou annulez. + + + + FileTransfertWidget + + Save a file + Title of the file saving dialog + Sauvegarder un fichier FilesForm - + Transfered Files "Headline" of the window - + Transfers - + Downloads - + Téléchargements - + Uploads - + Envois @@ -189,187 +267,392 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Copier l'ID ami - + Invite in group Menu to invite a friend in a groupchat Inviter dans un groupe - + Remove friend Menu to remove the friend from our friendlist Supprimer ami - GroupChatForm + GeneralForm - - %1 users in chat - Number of users in chat - %1 personnes + + General Settings + Options Générales + + + + GeneralPage + + General Settings + Options Générales - - <Unknown> - <Inconnu> + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Activer IPv6 (recommandé) - - %1 users in chat - %1 personnes + Use translations + Text on a checkbox to enable translations + Utiliser les traductions - + Make Tox portable + Text on a checkbox to make qTox a portable application + Rendre Tox portable + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Sauvegarde les options dans le dossier courant au lieu du dossier de configuration habituel + + + Theme + Thème + + + Smiley Pack + Pack de smileys + + + + GeneralSettings + + + Form + + + + + General Settings + Options Générales + + + + Use translations + Text on a checkbox to enable translations + Utiliser les traductions + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Sauvegarde les options dans le dossier courant au lieu du dossier de configuration habituel + + + + Make Tox portable + Rendre Tox portable + + + + Theme + Thème + + + + Smiley Pack + Text on smiley pack label + Pack de smileys + + + + Connection Settings + + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Activer IPv6 (recommandé) + + + + This allows, e.g., toxing over Tor. It adds load to the Tox network however, so use only when necessary. + force tcp checkbox tooltip + + + + + Disable UDP (not recommended) + Text on checkbox to disable UDP + + + + + Use proxy (SOCKS5) + + + + + Address + Text on proxy addr label + + + + + Port + Text on proxy port label + + + + + GenericChatForm + + + Save chat log Sauvegarder l'historique de conversation - GroupWidget + GroupChatForm - - + + %1 users in chat + Number of users in chat + %1 personnes + + + <Unknown> + <Inconnu> + + + %1 users in chat %1 personnes - - + Save chat log + Sauvegarder l'historique de conversation + + + + GroupWidget + + + + %1 users in chat + %1 personnes + + + + 0 users in chat 0 personnes - + Quit group Menu to quit a groupchat Quitter le groupe + + IdentityForm + + + Your identity + + + + + IdentityPage + + Public Information + Informations Publiques + + + Name + Username/nick + Nom + + + Status + Status message + Status + + + Tox ID + ID Tox + + + Your Tox ID + Votre ID Tox + + + + IdentitySettings + + + Form + + + + + Public Information + Informations Publiques + + + + Name + Nom + + + + Status + Status + + + + Tox ID + ID Tox + + + + Your Tox ID (click to copy) + + + MainWindow qTox - + qTox - + Your name - Votre nom + Votre nom - + Your status - Votre status + Votre status - + Add friends - + Ajouter des amis - + Create a group chat - + Creer un groupe - + View completed file transfers - + Voir les transfers de fichiers terminés - + Change your settings - + Changer les options - + Close - Fermer + Fermer - + Ctrl+Q - Ctrl+Q + Ctrl+Q + + + + PrivacyForm + + + Privacy settings + SelfCamView - + Tox video test Title of the window to test the video/webcam Test vidéo Tox + + SettingsDialog + + qTox – Settings + qTox — Options + + + General + General + + + Identity + Identité + + + Privacy + Vie Privée + + + Audio/Video + Audio/Vidéo + + + Ok + Ok + + + Cancel + Annuler + + + Apply + Appliquer + + SettingsForm - User Settings "Headline" of the window - Configuration + Configuration - Name Username/nick - Nom + Nom - Status Status message - Status + Status - - (click here to copy) - Click on this text to copy TID to clipboard - - - - Test video Text on a button to test the video/webcam - Tester la vidéo + Tester la vidéo - Enable IPv6 (recommended) Text on a checkbox to enable IPv6 - Activer IPv6 (recommandé) - - - - Use translations - Text on a checkbox to enable translations - - - - - Make Tox portable - Text on a checkbox to make qTox a portable application - - - - - Save settings to the working directory instead of the usual conf dir - describes makeToxPortable checkbox - - - - - Smiley Pack - Text on smiley pack label - + Activer IPv6 (recommandé) @@ -395,22 +678,66 @@ Ctrl+Q - + Online Button to set your status to 'Online' - Connecté + Connecté - + Away Button to set your status to 'Away' - Indisponnible + Indisponnible - + Busy Button to set your status to 'Busy' - Occupé + Occupé + + + + Choose a profile picture + + + + + + + Error + + + + + Unable to open this file + + + + + Unable to read this image + + + + + This image is too big + + + + + Toxcore failed to start, the application will terminate after you close this message. + + + + + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. + popup text + + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <Inconnu> diff --git a/translations/it.qm b/translations/it.qm index 890aaaea2..ace34002b 100644 Binary files a/translations/it.qm and b/translations/it.qm differ diff --git a/translations/it.ts b/translations/it.ts index 65a34eb9f..114976d15 100644 --- a/translations/it.ts +++ b/translations/it.ts @@ -1,75 +1,119 @@ + + AVForm + + + Audio/Video settings + Impostazioni Audio/Video + + + + Hide video preview + On a button + Ferma webcam + + + + Show video preview + On a button + Prova webcam + + + + AVSettings + + + Form + Form + + + + Video settings + Impostazioni Video + + + + Show video preview + Prova webcam + + AddFriendForm - + Add Friends Aggiungi Contatto - + Tox ID Tox ID of the person you're sending a friend request to Tox ID - + Message The message you send in friend requests Messaggio - + Send friend request Invia richiesta d'amicizia - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Permettimi di aggiungerti alla mia lista contatti - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Inserisci un Tox ID valido - + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Non puoi aggiungere te stesso come contatto! + + + This address does not exist The DNS gives the Tox ID associated to toxme.se addresses Questo indirizzo non esiste - + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Errore nel consultare il server DNS - + Unexpected number of text records Error with the DNS Numero inaspettato di text-records - + Unexpected number of values in text record Error with the DNS Numero inaspettato di valori nel text-record - + The DNS lookup does not contain any Tox ID Error with the DNS La risposta del server DNS non contiene nessun Tox ID - + The DNS lookup does not contain a valid Tox ID Error with the DNS La risposta del server DNS non contiene un Tox ID valido @@ -78,27 +122,41 @@ ChatForm - + Send a file Invia un file + + Core + + + Encrypted profile + Profilo criptato + + + + Your tox profile seems to be encrypted, qTox can't open it +Do you want to erase this profile ? + Il tuo profilo Tox sembra essere criptato, qTox non può aprirlo\nVuoi eliminare questo profilo? + + FileTransferInstance - + Save a file Title of the file saving dialog Salva file - + Location not writable Title of permissions popup Errore - + You do not have permission to write that location. Choose another, or cancel the save dialog. text of permissions popup Non hai sufficienti permessi per scrivere in questa locazione. Scegli un'altra posizione, o annulla il salvataggio. @@ -107,18 +165,18 @@ FilesForm - + Transfered Files "Headline" of the window Files Trasferiti - + Downloads Ricevuti - + Uploads Inviati @@ -162,29 +220,118 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Copia Tox ID del contatto - + Invite in group Menu to invite a friend in a groupchat Invita nel gruppo - + Remove friend Menu to remove the friend from our friendlist Rimuovi contatto + + GeneralForm + + + General Settings + Impostazioni Generali + + + + GeneralSettings + + + Form + Form + + + + General Settings + Impostazioni Generali + + + + Connection Settings + Impostazioni Connessione + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Abilita IPv6 (consigliato) + + + + Use translations + Text on a checkbox to enable translations + Abilita traduzioni + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Slava le impostazioni nella directory di lavoro corrente, invece della directory di default + + + + Make Tox portable + Rendi qTox portabile + + + + Theme + Impostazioni Tema + + + + Smiley Pack + Text on smiley pack label + Emoticons + + + + Use proxy (SOCKS5) + Usa proxy (SOCKS5) + + + + Address + Text on proxy addr label + IP + + + + Port + Text on proxy port label + Porta + + + + Disable UDP (not recommended) + Text on checkbox to disable UDP + Disabilita connessioni UDP (non raccomandato) + + + + This allows, e.g., toxing over Tor. It adds load to the Tox network however, so use only when necessary. + force tcp checkbox tooltip + Questo permette di usare qTox con Tor; tuttavia aggiunge carico alla rete Tox, quindi usalo solo se necessario. + + GenericChatForm - - + + Save chat log Salva il log della chat @@ -192,18 +339,13 @@ GroupChatForm - + %1 users in chat Number of users in chat %1 utenti in chat - - <Unknown> - <Sconosciuto> - - - + %1 users in chat %1 utenti in chat @@ -211,24 +353,65 @@ GroupWidget - - + + %1 users in chat %1 utenti in chat - - + + 0 users in chat 0 utenti in chat - + Quit group Menu to quit a groupchat Esci dal gruppo + + IdentityForm + + + Your identity + Il tuo profilo + + + + IdentitySettings + + + Form + Form + + + + Public Information + Informazioni Pubbliche + + + + Name + Nome + + + + Status + Stato + + + + Tox ID + Tox ID + + + + Your Tox ID (click to copy) + (clicca qui per copiare) + + MainWindow @@ -237,137 +420,126 @@ qTox - + Your name qTox User - + Your status Toxing on qTox - + Add friends Aggiungi contatto - + Create a group chat Crea un gruppo - + View completed file transfers Visualizza i trasferimenti completati - + Change your settings Cambia le impostazioni - + Close Chiudi - + Ctrl+Q Ctrl+Q + + PrivacyForm + + + Privacy settings + Impostazioni privacy + + SelfCamView - + Tox video test Title of the window to test the video/webcam qTox video test - - SettingsForm - - - User Settings - "Headline" of the window - Impostazioni - - - - Name - Username/nick - Nome - - - - Status - Status message - Stato - - - - (click here to copy) - Click on this text to copy TID to clipboard - (clicca qui per copiare) - - - - Test video - Text on a button to test the video/webcam - Prova la webcam - - - - Enable IPv6 (recommended) - Text on a checkbox to enable IPv6 - Abilita IPv6 (consigliato) - - - - Use translations - Text on a checkbox to enable translations - Abilita traduzioni - - - - Make Tox portable - Text on a checkbox to make qTox a portable application - Rendi qTox portabile - - - - Save settings to the working directory instead of the usual conf dir - describes makeToxPortable checkbox - Slava le impostazioni nella directory di lavoro corrente, invece della directory di default - - - - Smiley Pack - Text on smiley pack label - Emoticons - - Widget - + Online Button to set your status to 'Online' Online - + Away Button to set your status to 'Away' Assente - + Busy Button to set your status to 'Busy' Occupato + + + Choose a profile picture + Scegli un'immagine per il profilo + + + + + + Error + Errore + + + + Unable to open this file + Impossibile aprire il file + + + + Unable to read this image + Impossibile leggere l'immagine + + + + This image is too big + L'immagine è troppo grande + + + + Toxcore failed to start, the application will terminate after you close this message. + Impossibile avviare Toxcore.\nqTox terminerà dopo che avrai chiuso questo messaggio. + + + + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. + popup text + Impossibile avviare Toxcore con le tue impostazione proxy.\nqTox non può funzionare correttamente, per favore modifica le impostazioni e riavvia il programma. + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <Sconosciuto> + diff --git a/translations/mannol.qm b/translations/mannol.qm new file mode 100644 index 000000000..1d4f6d7b6 Binary files /dev/null and b/translations/mannol.qm differ diff --git a/translations/mannol.ts b/translations/mannol.ts new file mode 100644 index 000000000..ccc08ff82 --- /dev/null +++ b/translations/mannol.ts @@ -0,0 +1,458 @@ + + + + + AVPage + + + Video Settings + + + + + + Show video preview + On a button + + + + + Hide video preview + On a button + + + + + AddFriendForm + + + Add Friends + + + + + Tox ID + Tox ID of the person you're sending a friend request to + + + + + Message + The message you send in friend requests + + + + + Send friend request + + + + + Tox me maybe? + Default message in friend requests if the field is left blank. Write something appropriate! + + + + + Please fill in a valid Tox ID + Tox ID of the friend you're sending a friend request to + + + + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + + + + + This address does not exist + The DNS gives the Tox ID associated to toxme.se addresses + + + + + Error while looking up DNS + The DNS gives the Tox ID associated to toxme.se addresses + + + + + Unexpected number of text records + Error with the DNS + + + + + Unexpected number of values in text record + Error with the DNS + + + + + The DNS lookup does not contain any Tox ID + Error with the DNS + + + + + + The DNS lookup does not contain a valid Tox ID + Error with the DNS + + + + + ChatForm + + + Send a file + + + + + FileTransferInstance + + + Save a file + Title of the file saving dialog + + + + + Location not writable + Title of permissions popup + + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + + + + + FilesForm + + + Transfered Files + "Headline" of the window + + + + + Downloads + + + + + Uploads + + + + + FriendRequestDialog + + + Friend request + Title of the window to aceept/deny a friend request + + + + + Someone wants to make friends with you + Ayy ! You have mothafucking friend request you little bitch ! + + + + User ID: + + + + + Friend request message: + + + + + Accept + Accept a friend request + + + + + Reject + Reject a friend request + + + + + FriendWidget + + + Copy friend ID + Menu to copy the Tox ID of that friend + + + + + Invite in group + Menu to invite a friend in a groupchat + + + + + Remove friend + Menu to remove the friend from our friendlist + + + + + GeneralPage + + + General Settings + + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + + + + + Use translations + Text on a checkbox to enable translations + + + + + Make Tox portable + Text on a checkbox to make qTox a portable application + + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + + + + + Theme + + + + + Smiley Pack + + + + + GenericChatForm + + + + Save chat log + + + + + GroupChatForm + + + %1 users in chat + Number of users in chat + + + + + <Unknown> + + + + + %1 users in chat + + + + + GroupWidget + + + + %1 users in chat + + + + + + 0 users in chat + + + + + Quit group + Menu to quit a groupchat + + + + + IdentityPage + + + Public Information + + + + + Name + Username/nick + + + + + Status + Status message + + + + + Tox ID + + + + + Your Tox ID + + + + + MainWindow + + + qTox + + + + + Your name + + + + + Your status + + + + + Add friends + + + + + Create a group chat + + + + + View completed file transfers + + + + + Change your settings + + + + + Close + + + + + Ctrl+Q + + + + + SelfCamView + + + Tox video test + Title of the window to test the video/webcam + + + + + SettingsDialog + + + qTox – Settings + + + + + General + + + + + Identity + + + + + Privacy + + + + + Audio/Video + + + + + Ok + + + + + Cancel + + + + + Apply + + + + + Widget + + + Online + Button to set your status to 'Online' + + + + + Away + Button to set your status to 'Away' + + + + + Busy + Button to set your status to 'Busy' + + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + + + + diff --git a/translations/pirate.qm b/translations/pirate.qm new file mode 100644 index 000000000..c245f536e Binary files /dev/null and b/translations/pirate.qm differ diff --git a/translations/pirate.ts b/translations/pirate.ts new file mode 100644 index 000000000..85a149a95 --- /dev/null +++ b/translations/pirate.ts @@ -0,0 +1,545 @@ + + + + + AVForm + + + Audio/Video settings + Hollerin'/Crystals o' Far-Seein' settings + + + + Hide video preview + On a button + Hide crystals o' Far-Seein' preview + + + + Show video preview + On a button + See crystals o' Far-Seein' preview + + + + AVSettings + + + Form + + + + + Video settings + Crystals o' Far-Seein' settings + + + + Show video preview + See crystals o' Far-Seein' preview + + + + AddFriendForm + + + Add Friends + Recruit yer crew + + + + Tox ID + Tox ID of the person you're sending a friend request to + Tox ID + + + + Message + The message you send in friend requests + Parchment Bottle of recruiting + + + + Send friend request + Send recruitment letter + + + + Tox me maybe? + Default message in friend requests if the field is left blank. Write something appropriate! + Tox, arrr? + + + + Please fill in a valid Tox ID + Tox ID of the friend you're sending a friend request to + Throw in here Tox ID, but a rightful one! + + + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Yer cannot add yerself in yer crew! + + + + This address does not exist + The DNS gives the Tox ID associated to toxme.se addresses + Recruitment address yer usin' ain't there + + + + Error while looking up DNS + The DNS gives the Tox ID associated to toxme.se addresses + DNS witchery errored upon looking + + + + Unexpected number of text records + Error with the DNS + All numbery that DNS brought upon was unexpectable + + + + Unexpected number of values in text record + Error with the DNS + All things + + + + The DNS lookup does not contain any Tox ID + Error with the DNS + + + + + + The DNS lookup does not contain a valid Tox ID + Error with the DNS + + + + + ChatForm + + + Send a file + Send treasure + + + + Core + + + Encrypted profile + + + + + Your tox profile seems to be encrypted, qTox can't open it +Do you want to erase this profile ? + + + + + FileTransferInstance + + + Save a file + Title of the file saving dialog + Receive treasure + + + + Location not writable + Title of permissions popup + Cannot bury treasure here + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + Captain did't give permission bury that treasure here! Find another location or abandon hope of keepin' it. + + + + FilesForm + + + Transfered Files + "Headline" of the window + Treasures given/received + + + + Downloads + Received + + + + Uploads + Sent + + + + FriendRequestDialog + + + Friend request + Title of the window to aceept/deny a friend request + Recruitment + + + + Someone wants to make friends with you + + + + + User ID: + + + + + Friend request message: + + + + + Accept + Accept a friend request + Arr + + + + Reject + Reject a friend request + No + + + + FriendWidget + + + Copy friend ID + Menu to copy the Tox ID of that friend + + + + + Invite in group + Menu to invite a friend in a groupchat + Recruit in group + + + + Remove friend + Menu to remove the friend from our friendlist + Throw out traitor + + + + GeneralForm + + + General Settings + O' ship settings + + + + GeneralSettings + + + Form + + + + + General Settings + O' ship settings + + + + Use translations + Text on a checkbox to enable translations + Linguistics use + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + + + + + Make Tox portable + + + + + Theme + Lookin' + + + + Smiley Pack + Text on smiley pack label + Teeth showin' box + + + + Connection Settings + Sailin' setting + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Use IPv6 (good for ya) + + + + This allows, e.g., toxing over Tor. It adds load to the Tox network however, so use only when necessary. + force tcp checkbox tooltip + + + + + Disable UDP (not recommended) + Text on checkbox to disable UDP + + + + + Use proxy (SOCKS5) + + + + + Address + Text on proxy addr label + + + + + Port + Text on proxy port label + + + + + GenericChatForm + + + + Save chat log + Write log of yer talkin' + + + + GroupChatForm + + + %1 users in chat + Number of users in chat + %1 mates in chat + + + + %1 users in chat + %1 mates in chat + + + + GroupWidget + + + + %1 users in chat + %1 mates in chat + + + + + 0 users in chat + + + + + Quit group + Menu to quit a groupchat + + + + + IdentityForm + + + Your identity + Yer self + + + + IdentitySettings + + + Form + + + + + Public Information + + + + + Name + + + + + Status + + + + + Tox ID + + + + + Your Tox ID (click to copy) + Yer Tox ID (hit to steal) + + + + MainWindow + + + qTox + qTox + + + + Your name + Yer name + + + + Your status + Yer status + + + + Add friends + Recruit crew + + + + Create a group chat + Make a chat with yer mates + + + + View completed file transfers + See treasures given and received + + + + Change your settings + Change yer setting' + + + + Close + + + + + Ctrl+Q + + + + + PrivacyForm + + + Privacy settings + Secrecy settin' + + + + SelfCamView + + + Tox video test + Title of the window to test the video/webcam + Tox crystals o' Far-Seein' test + + + + Widget + + + Online + Button to set your status to 'Online' + + + + + Away + Button to set your status to 'Away' + + + + + Busy + Button to set your status to 'Busy' + + + + + Choose a profile picture + Choose yer flag + + + + + + Error + + + + + Unable to open this file + Treasure cannot be opened + + + + Unable to read this image + Cannot see treasure + + + + This image is too big + Yer flag too big + + + + Toxcore failed to start, the application will terminate after you close this message. + + + + + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. + popup text + + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + + + + diff --git a/translations/pl.qm b/translations/pl.qm new file mode 100644 index 000000000..b9b482a49 Binary files /dev/null and b/translations/pl.qm differ diff --git a/translations/pl.ts b/translations/pl.ts new file mode 100644 index 000000000..a91d8a8a4 --- /dev/null +++ b/translations/pl.ts @@ -0,0 +1,667 @@ + + + + + AVForm + + + Audio/Video settings + Ustawienia audio/video + + + + Hide video preview + On a button + Ukryj podgląd wideo + + + + Show video preview + On a button + Pokaż podgląd wideo + + + + AVPage + + Video Settings + Ustawienia wideo + + + Show video preview + On a button + Pokaż podgląd wideo + + + Hide video preview + On a button + Ukryj podgląd wideo + + + + AVSettings + + + Form + + + + + Video settings + Ustawienia wideo + + + + Show video preview + Pokaż podgląd wideo + + + + AddFriendForm + + + Add Friends + Dodaj znajomych + + + + Tox ID + Tox ID of the person you're sending a friend request to + Tox ID + + + + Message + The message you send in friend requests + Wiadomość + + + + Send friend request + Wyślij zapytanie do znajomego + + + + Tox me maybe? + Default message in friend requests if the field is left blank. Write something appropriate! + Może Tox ze mną? + + + + Please fill in a valid Tox ID + Tox ID of the friend you're sending a friend request to + Proszę wpisać poprawny Tox ID + + + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Nie możesz dodać siebie jako znajomego! + + + + This address does not exist + The DNS gives the Tox ID associated to toxme.se addresses + Ten adres nie istnieje + + + + Error while looking up DNS + The DNS gives the Tox ID associated to toxme.se addresses + Błąd podczas sprawdzania DNS + + + + Unexpected number of text records + Error with the DNS + Nieoczekiwana liczba wpisów tekstowych + + + + Unexpected number of values in text record + Error with the DNS + Nieoczekiwana liczba wartości we wpisie tekstowym + + + + The DNS lookup does not contain any Tox ID + Error with the DNS + DNS nie zawiera żadnego Tox ID + + + + + The DNS lookup does not contain a valid Tox ID + Error with the DNS + DNS nie zawiera poprawnego Tox ID + + + + ChatForm + + + Send a file + Wyślij plik + + + + Core + + + Encrypted profile + Zaszyfrowany profil + + + + Your tox profile seems to be encrypted, qTox can't open it +Do you want to erase this profile ? + Twój profil zdaje się być zaszyfrowany, qTox nie jest w stanie go otworzyć +Czy chcesz usunąć ten profil ? + + + + FileTransferInstance + + + Save a file + Title of the file saving dialog + Zapisz plik + + + + Location not writable + Title of permissions popup + Nie można zapisać w lokacji + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + Nie masz uprawnienia by zapisać w tej lokacji. Wybierz inną lub anuluj zapis. + + + + FilesForm + + + Transfered Files + "Headline" of the window + Przesłane pliki + + + + Downloads + Pobrane + + + + Uploads + Wysłane + + + + FriendRequestDialog + + + Friend request + Title of the window to aceept/deny a friend request + no idea how it shoule be translated + Zapytanie znajomego + + + + Someone wants to make friends with you + Ktoś chce być twoim znajomym + + + + User ID: + ID użytkownika: + + + + Friend request message: + better wording needed? + Wiadomość w zapytaniu do znajomej/go: + + + + Accept + Accept a friend request + Zaakceptuj + + + + Reject + Reject a friend request + Odrzuć + + + + FriendWidget + + + Copy friend ID + Menu to copy the Tox ID of that friend + Kopiuj ID znajomej/go + + + + Invite in group + Menu to invite a friend in a groupchat + Zaproś do grupy + + + + Remove friend + Menu to remove the friend from our friendlist + Usuń znajomego + + + + GeneralForm + + + General Settings + Główne ustawienia + + + + GeneralPage + + General Settings + Główne ustawienia + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Użyj IPv6 (rekomendowane) + + + Use translations + Text on a checkbox to enable translations + Użyj tłumaczenia + + + Make Tox portable + Text on a checkbox to make qTox a portable application + Zrób Tox przenośnym + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Zamiast domyślnego katalogu użyj obecnego do zapisania ustawień + + + Theme + Motyw + + + Smiley Pack + needs better translation + Paczka uśmiechów + + + + GeneralSettings + + + Form + + + + + General Settings + Główne ustawienia + + + + Use translations + Text on a checkbox to enable translations + Użyj tłumaczenia + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Zamiast domyślnego katalogu użyj obecnego do zapisania ustawień + + + + Make Tox portable + Zrób Tox przenośnym + + + + Theme + Motyw + + + + Smiley Pack + Text on smiley pack label + better translation? anyone? + Paczka uśmiechów + + + + Connection Settings + Ustawienia połączenia + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Użyj IPv6 (rekomendowane) + + + + This allows, e.g., toxing over Tor. It adds load to the Tox network however, so use only when necessary. + force tcp checkbox tooltip + To pozwala n.p. na toxowanie przez Tora. Niestety obciąża to sieć Tox, więc używaj tylko w razie potrzeby. + + + + Disable UDP (not recommended) + Text on checkbox to disable UDP + Wyłącz UDP (nie rekomendowane) + + + + Use proxy (SOCKS5) + Użyj proxy (SOCKS5) + + + + Address + Text on proxy addr label + Adres + + + + Port + Text on proxy port label + Port + + + + GenericChatForm + + + + Save chat log + Zapisz historię rozmowy + + + + GroupChatForm + + + %1 users in chat + Number of users in chat + %1 użytkowników w czacie + + + <Unknown> + <Nieznany/a> + + + + %1 users in chat + %1 użytkowników w czacie + + + + GroupWidget + + + + %1 users in chat + %1 użytkowników w czacie + + + + + 0 users in chat + 0 użytkowników w czacie + + + + Quit group + Menu to quit a groupchat + Opuść grupę + + + + IdentityForm + + + Your identity + Twoja tożsamość + + + + IdentityPage + + Public Information + Informacja publiczna + + + Name + Username/nick + Nick + + + Status + Status message + Status + + + Tox ID + Tox ID + + + Your Tox ID + Twój Tox ID + + + + IdentitySettings + + + Form + + + + + Public Information + Informacja publiczna + + + + Name + Nick + + + + Status + Status + + + + Tox ID + Tox ID + + + + Your Tox ID (click to copy) + Twój Tox ID (kliknij by skopiować) + + + + MainWindow + + + qTox + qTox + + + + Your name + Twój nick + + + + Your status + Twój status + + + + Add friends + Dodaj znajomych + + + + Create a group chat + Utwórz czat grupowy + + + + View completed file transfers + Zobacz zakończone transfery plików + + + + Change your settings + translated as "change settings"; seems to be simpler this way + Zmień ustawienia + + + + Close + Zamknij + + + + Ctrl+Q + Ctrl+Q + + + + PrivacyForm + + + Privacy settings + Ustawienia prywatności + + + + SelfCamView + + + Tox video test + Title of the window to test the video/webcam + Tox test wideo + + + + SettingsDialog + + qTox – Settings + qTox – Ustawienia + + + General + Główne + + + Identity + Tożsamość + + + Privacy + Prywatność + + + Audio/Video + Audio/Wideo + + + Ok + Ok + + + Cancel + Anuluj + + + Apply + Zastosuj + + + + Widget + + + Online + Button to set your status to 'Online' + Online + + + + Away + Button to set your status to 'Away' + Nieobecny/a + + + + Busy + Button to set your status to 'Busy' + Zajęty/a + + + + Choose a profile picture + Wybierz obrazek profilu + + + + + + Error + Błąd + + + + Unable to open this file + Nie można otworzyć tego pliku + + + + Unable to read this image + Nie można odczytać tego obrazka + + + + This image is too big + Ten obrazek jest zbyt wielki + + + + Toxcore failed to start, the application will terminate after you close this message. + Nie udało się uruchomić Toxcore, aplikacja zamknie się po zamknięciu tej wiadomości. + + + + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. + popup text + Nie udało się uruchomić toxcore z twoimi ustawieniami proxy. qTox nie może działać, proszę zmodyfikuj ustawienia i zrestartuj. + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <Nieznany/a> + + + diff --git a/translations/ru.qm b/translations/ru.qm index c77294313..f3d7a3e2a 100644 Binary files a/translations/ru.qm and b/translations/ru.qm differ diff --git a/translations/ru.ts b/translations/ru.ts index 1e2118770..274e26a7c 100644 --- a/translations/ru.ts +++ b/translations/ru.ts @@ -1,153 +1,188 @@ - + + + AVForm + + + Audio/Video settings + Настройки аудио/видео + + + + Hide video preview + On a button + Спрятать видео-превью + + + + Show video preview + On a button + Посмотреть видео-превью + + + + AVSettings + + + + Form + + + + + + Video settings + Настройки видео + + + + + Show video preview + Посмотреть видео-превью + + AddFriendForm - + Add Friends - Добавление друзей + Добавить друзей - + Tox ID Tox ID of the person you're sending a friend request to Tox ID - + Message The message you send in friend requests Сообщение - + Send friend request Мне не нравится, но другого не придумал, и фейсбук использует это Отправить запрос на добавление в друзья - + Tox me maybe? Default message in friend requests if the field is left blank. Write something appropriate! Вот таким нехитрым и незамысловатым образом решаются сложные переводчиские проблемы Добавь меня, а? - + Please fill in a valid Tox ID Tox ID of the friend you're sending a friend request to Пожалуйста, введите корректный Tox ID - + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Вы не можете добавить себя как друга! + + + This address does not exist The DNS gives the Tox ID associated to toxme.se addresses Нет такого адреса - + Error while looking up DNS The DNS gives the Tox ID associated to toxme.se addresses Ошибка при просмотре DNS - + Unexpected number of text records Error with the DNS Непредвиденное количество текстовых записей - + Unexpected number of values in text record Error with the DNS Непредвиденное количество значений в текстовой записи - + The DNS lookup does not contain any Tox ID Error with the DNS В ответе DNS ни одного Tox ID - + The DNS lookup does not contain a valid Tox ID Error with the DNS Ответ DNS не содержит корректных Tox ID - - Camera - - - Camera eror - Ошибка камеры - - - - Camera format %1 not supported, can't use the camera - Формат камеры %1 не поддерживается, невозможно использовать камеру - - ChatForm - + Send a file Отправить файл + + + Core - - - Save chat log - Сохранить лог чата + + Encrypted profile + Зашифрованный профиль + + + + Your tox profile seems to be encrypted, qTox can't open it +Do you want to erase this profile ? + Похоже, ваш tox-профиль зашифрован, qTox не может открыть его +Вы хотите удалить этот профиль? - CopyableElideLabel + FileTransferInstance - - Copy - Копировать - - - - FileTransfertWidget - - + Save a file Title of the file saving dialog Сохранить файл - + Location not writable Title of permissions popup - + Непригодная для записи локация - + You do not have permission to write that location. Choose another, or cancel the save dialog. text of permissions popup - + У вас нет прав записи в эту локацию. Выберете другую или закройте диалог сохранения. FilesForm - + Transfered Files "Headline" of the window Переданные файлы - + Downloads Загрузки - + Uploads Выгрузки @@ -193,246 +228,362 @@ FriendWidget - + Copy friend ID Menu to copy the Tox ID of that friend Копировать ID друга - + Invite in group Menu to invite a friend in a groupchat Пригласить в группу - + Remove friend Menu to remove the friend from our friendlist Удалить друга - GroupChatForm + GeneralForm - - %1 users in chat - Number of users in chat - %1 пользователей в чате + + General Settings + Общие настройки - - <Unknown> - <Неизвестно> + + Bad port + title of bad port popup + Неправильный порт - - %1 users in chat - %1 пользователей в чате + + The port you entered is invalid; please enter another. + text of bad port popup + Введёный порт неверен; введите другой. + + + + GeneralSettings + + + + Form + - + + + General Settings + Общие настройки + + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Включить IPv6 (рекомендуется) + + + + + Use translations + Text on a checkbox to enable translations + Использовать перевод + + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Сохранять настройки в рабочую директорию вместо страндартной папки настроек + + + + + Make Tox portable + Портативный режим + + + + + Theme + Тема + + + + + Smiley Pack + Text on smiley pack label + Набор смайликов + + + + + Proxy settings + Настройки прокси + + + + + Disable UDP (not recommended) + Text on checkbox to disable UDP + Выключить UDP (не рекомендуется) + + + + + This allows, e.g., toxing over Tor. It adds load to the Tox network however, so use only when necessary. + force tcp checkbox tooltip + Это позволяет, например, использовать tox поверх Tor. Однако это добавляет нагрузку на сеть Tox, так что используйте только в случае необходимости. + + + + + Proxy address + Text on proxy addr label + Адрес прокси + + + + + Proxy port + Text on proxy port label + Порт прокси + + + + GenericChatForm + + + Save chat log Сохранить лог чата + + GroupChatForm + + + %1 users in chat + Number of users in chat + %1 пользователей в чате + + + + %1 users in chat + %1 пользователей в чате + + GroupWidget - + Quit group Menu to quit a groupchat Покинуть группу - - + + %1 users in chat %1 пользователей в чате - - + + 0 users in chat Ни одного пользователя в чате + + IdentityForm + + + Your identity + Ваша идентификация + + + + IdentitySettings + + + + Form + + + + + + Public Information + Публичные данные + + + + + Name + Имя + + + + + Status + Статус + + + + + Tox ID + Tox ID + + + + + Your Tox ID (click to copy) + Ваш Tox ID (нажмите на него, чтобы скопировать) + + MainWindow + qTox - + qTox - + + Your name - Ваше имя + Ваше имя - + + Your status - Ваш статус + Ваш статус - + + Add friends - Добавить друзей + Добавить друзей - + + Create a group chat - Создать групповой чат + Создать групповой чат - + + View completed file transfers - + Завершённые файлопередачи - + + Change your settings - Изменить ваши настройки + Изменить ваши настройки - + + Close - Закрыть + Закрыть - + + Ctrl+Q - Ctrl+Q + Ctrl+Q + + + + PrivacyForm + + + Privacy settings + Настройки приватности SelfCamView - + Tox video test Title of the window to test the video/webcam Проверка видео - - SettingsForm - - - User Settings - "Headline" of the window - Пользовательские настройки - - - - Name - Username/nick - Имя - - - - Status - Status message - Статус - - - - (click here to copy) - Click on this text to copy TID to clipboard - (нажмите здесь чтобы скопировать) - - - - Test video - Text on a button to test the video/webcam - Проверить видео - - - - Enable IPv6 (recommended) - Text on a checkbox to enable IPv6 - Включить IPv6 (рекомендуется) - - - - Use translations - Text on a checkbox to enable translations - Так гораздо понятнее, чем «использовать переводы» - Русскоязычный интерфейс - - - - Make Tox portable - Text on a checkbox to make qTox a portable application - Портативный режим - - - - Save settings to the working directory instead of the usual conf dir - describes makeToxPortable checkbox - - - - - Smiley Pack - Text on smiley pack label - - - Widget - Tox - Tox - - - Your name - Ваше имя - - - Your status - Ваш статус - - - Add friends - Добавить друзей - - - Create a group chat - Создать групповой чат - - - (button inactive currently) - (кнопка на данный момент неактивна) - - - Change your settings - Изменить ваши настройки - - - Close - Закрыть - - - Ctrl+Q - Ctrl+Q - - - + Online Button to set your status to 'Online' - В сети + В сети - + Away Button to set your status to 'Away' Вероятно, это не столь долгое путешествие - Отошёл + Отошёл - + Busy Button to set your status to 'Busy' - Занят + Занят + + + + Choose a profile picture + Выбрать картинку для профиля + + + + + + Error + Ошибка + + + + Unable to open this file + Невозможно открыть файл + + + + Unable to read this image + Невозможно прочесть это изображение + + + + This image is too big + Это изображение слишком большое + + + + Toxcore failed to start, the application will terminate after you close this message. + Не удалось запустить toxcore, приложение будет завершено после того как вы закроете это сообщение. + + + + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. + popup text + Не удалось запустить toxcore с вашими настройками прокси, qTox не может работать; измените ваши настройки и перезапустите его. + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <Неизвестно> diff --git a/translations/uk.qm b/translations/uk.qm new file mode 100644 index 000000000..53c004e9b Binary files /dev/null and b/translations/uk.qm differ diff --git a/translations/uk.ts b/translations/uk.ts new file mode 100644 index 000000000..70d143cdc --- /dev/null +++ b/translations/uk.ts @@ -0,0 +1,485 @@ + + + + + AVPage + + + Video Settings + Параметри відео + + + + + Show video preview + On a button + Показати вікно попереднього перегляду + + + + Hide video preview + On a button + Приховати вікно попереднього перегляду + + + + AddFriendForm + + + Add Friends + Додати друзів + + + + Tox ID + Tox ID of the person you're sending a friend request to + Tox ID + + + + Message + The message you send in friend requests + Повідомлення + + + + Send friend request + Надіслати запит на дружбу + + + + Tox me maybe? + Default message in friend requests if the field is left blank. Write something appropriate! + Може поспілкуємось? + + + + Please fill in a valid Tox ID + Tox ID of the friend you're sending a friend request to + Заповніть коректним Tox ID + + + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Ви не можете додати самого себе до друзів! + + + + This address does not exist + The DNS gives the Tox ID associated to toxme.se addresses + Цієї адреси не існує + + + + Error while looking up DNS + The DNS gives the Tox ID associated to toxme.se addresses + Помилка під час перегляду DNS + + + + Unexpected number of text records + Error with the DNS + Неочікуване число текстових записів + + + + Unexpected number of values in text record + Error with the DNS + Неочікуване число значень в текстових записах + + + + The DNS lookup does not contain any Tox ID + Error with the DNS + Відповідь DNS не містить жодного Tox ID + + + + + The DNS lookup does not contain a valid Tox ID + Error with the DNS + Відповідь DNS не містить жодного коректного Tox ID + + + + ChatForm + + + Send a file + Надіслати файл + + + + FileTransferInstance + + + Save a file + Title of the file saving dialog + Зберегти файл + + + + Location not writable + Title of permissions popup + Немає прав на запис + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + Ви не маєте прав на запис за цим розташуванням. Оберіть інше місце призначення, або скасуйте передачу. + + + + FilesForm + + + Transfered Files + "Headline" of the window + Передані файли + + + + Downloads + Завантажені + + + + Uploads + Вивантажені + + + + FriendRequestDialog + + + Friend request + Title of the window to aceept/deny a friend request + Запит на дружбу + + + + Someone wants to make friends with you + Дехто хоче долучитися до переліку друзів з вами + + + + User ID: + ID користувача: + + + + Friend request message: + Повідомлення запиту: + + + + Accept + Accept a friend request + Прийняти + + + + Reject + Reject a friend request + Відхилити + + + + FriendWidget + + + Copy friend ID + Menu to copy the Tox ID of that friend + Копіювати дружній ID + + + + Invite in group + Menu to invite a friend in a groupchat + Запросити до групи + + + + Remove friend + Menu to remove the friend from our friendlist + Вилучити з друзів + + + + GeneralPage + + + General Settings + Основні параметри + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Дозволити IPv6 (рекомендовано) + + + + Use translations + Text on a checkbox to enable translations + Використовувати мову системи + + + + Make Tox portable + Text on a checkbox to make qTox a portable application + Портативний запуск + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Зберігати налаштування в робочий теці + + + + Theme + Графічна тема + + + + Smiley Pack + Графічний пакунок емоційних картинок + + + + GenericChatForm + + + + Save chat log + Зберегти чат + + + + GroupChatForm + + + %1 users in chat + Number of users in chat + Користувачів у чаті: %1 + + + + <Unknown> + <Невідомо> + + + + %1 users in chat + Користувачів у чаті: %1 + + + + GroupWidget + + + + %1 users in chat + Користувачів у чаті: %1 + + + + + 0 users in chat + Немає користувачів + + + + Quit group + Menu to quit a groupchat + Вийти з групи + + + + IdentityPage + + + Public Information + Публічна інформація + + + + Name + Username/nick + Ім'я + + + + Status + Status message + Статус + + + + Tox ID + Tox ID + + + + Your Tox ID + Ваш Tox ID + + + + MainWindow + + + qTox + qTox + + + + Your name + Ваше ім'я + + + + Your status + Ваш статус + + + + Add friends + Додати друзів + + + + Create a group chat + Створити груповий чат + + + + View completed file transfers + Переглянути завершені передачі файлів + + + + Change your settings + Змінити параметри + + + + Close + Закрити + + + + Ctrl+Q + Ctrl+Q + + + + SelfCamView + + + Tox video test + Title of the window to test the video/webcam + Перевірка відео tox + + + + SettingsDialog + + + qTox – Settings + qTox - Параметри + + + + General + Основні + + + + Identity + Ідентифікація + + + + Privacy + Приватність + + + + Audio/Video + Аудіо/Відео + + + + Ok + Гаразд + + + + Cancel + Скасувати + + + + Apply + Застосувати + + + + Widget + + + Online + Button to set your status to 'Online' + В мережі + + + + Away + Button to set your status to 'Away' + Відійшов + + + + Busy + Button to set your status to 'Busy' + Зайнятий + + + + Choose a profile picture + Оберіть зображення для профілю + + + + + + Error + Помилка + + + + Unable to open this file + Неможливо відкрити цей файл + + + + Unable to read this image + Неможливо прочитати це зображення + + + + This image is too big + Зображення завелике + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <Невідомо> + + + diff --git a/ui/chatArea/innerStyle.css b/ui/chatArea/innerStyle.css index 61380b073..823c8dfbe 100644 --- a/ui/chatArea/innerStyle.css +++ b/ui/chatArea/innerStyle.css @@ -1,64 +1,75 @@ -div.name_me { - color: #646464; - font-weight: bold; - padding-right: 3px; -} - div.name { - color: #000000; - font-weight: bold; - padding-right: 3px; + color: @black; + font: @bigBold; } div.message { - color: #000000; - padding-right: 3px; - padding-left: 3px; + color: @black; + font: @big; +} + +div.action { + color: #1818FF; + font: @big; } div.date { - color: #000000; - padding-left: 3px; - white-space: nowrap; + color: @black; + font: @big; } -div.quote { - background-color: #6bc260; +div.name_me { + color: @mediumGrey; + font: @big; +} + +div.message_me { + color: @black; + font: @big; +} + +div.date_me { + color: @black; + font: @big; +} + +span.quote { + color: #6bc260; } div.green { margin-top: 12px; margin-bottom: 12px; - margin-left: 12px; + margin-left: 0px; margin-right: 12px; - color: #ffffff; - background-color: #6bc260; - font-size: 10px; + color: @white; + background-color: @green; + font: @small; } div.silver { margin-top: 12px; margin-bottom: 12px; - margin-left: 12px; + margin-left: 0px; margin-right: 12px; - color: #000000; - background-color: #d1d1d1; - font-size: 10px; + color: @black; + background-color: @lightGrey; + font: @small; } div.red { margin-top: 12px; margin-bottom: 12px; - margin-left: 12px; + margin-left: 0px; margin-right: 12px; - color: #ffffff; - background-color: rgb(200,78,78); - font-size: 10px; + color: @white; + background-color: @red; + font: @small; } div.button { margin-top: 0px; margin-bottom: 0px; margin-left: 0px; - color: #ffffff; + color: @white; } diff --git a/ui/chatroomWidgets/genericChatroomWidget.css b/ui/chatroomWidgets/genericChatroomWidget.css new file mode 100644 index 000000000..4ed573fb0 --- /dev/null +++ b/ui/chatroomWidgets/genericChatroomWidget.css @@ -0,0 +1,38 @@ +GenericChatroomWidget +{ + background-color: @mediumGrey; +} + +GenericChatroomWidget[active="true"] +{ + background-color: @white; +} + +GenericChatroomWidget[active="false"]:hover +{ + background-color: @mediumGreyLight; +} + +GenericChatroomWidget[active="true"] > QLabel#status +{ + font: @medium; + color: @mediumGrey; +} + +GenericChatroomWidget[active="false"] > QLabel#status +{ + font: @medium; + color: @lightGrey; +} + +GenericChatroomWidget[active="true"] > QLabel#name +{ + font: @big; + color: @darkGrey; +} + +GenericChatroomWidget[active="false"] > QLabel#name +{ + font: @big; + color: @white; +} \ No newline at end of file diff --git a/ui/emoticonWidget/dot_page.png b/ui/emoticonWidget/dot_page.png index c7c2aa172..e74d514e2 100644 Binary files a/ui/emoticonWidget/dot_page.png and b/ui/emoticonWidget/dot_page.png differ diff --git a/ui/emoticonWidget/dot_page_current.png b/ui/emoticonWidget/dot_page_current.png index aa101903a..7b18c42c2 100644 Binary files a/ui/emoticonWidget/dot_page_current.png and b/ui/emoticonWidget/dot_page_current.png differ diff --git a/ui/emoticonWidget/dot_page_hover.png b/ui/emoticonWidget/dot_page_hover.png index 1c23931b9..822dbb6c6 100644 Binary files a/ui/emoticonWidget/dot_page_hover.png and b/ui/emoticonWidget/dot_page_hover.png differ diff --git a/ui/emoticonWidget/emoticonWidget.css b/ui/emoticonWidget/emoticonWidget.css index a1aab7de3..093db5122 100644 --- a/ui/emoticonWidget/emoticonWidget.css +++ b/ui/emoticonWidget/emoticonWidget.css @@ -35,6 +35,6 @@ QRadioButton::indicator::checked QMenu { - background-color: rgb(240,240,240); /* sets background of the menu */ - border: 1px solid; + background-color: @mediumGrey; /* sets background of the menu */ + border: 0px solid; } diff --git a/ui/fileTransferInstance/sliverRTEdge.png b/ui/fileTransferInstance/sliverRTEdge.png new file mode 100644 index 000000000..dd363d107 Binary files /dev/null and b/ui/fileTransferInstance/sliverRTEdge.png differ diff --git a/ui/friendList/friendList.css b/ui/friendList/friendList.css index 55b39e1e6..8358f5a04 100644 --- a/ui/friendList/friendList.css +++ b/ui/friendList/friendList.css @@ -3,18 +3,16 @@ QScrollArea { } QScrollBar:vertical { - background: transparent; - width: 14px; - margin-top: 2px; - margin-bottom: 2px; + background: rgb(65,65,65); + width: 16px; + padding: 0px 3px 0px 3px; } QScrollBar:handle:vertical { background: rgba(18, 18, 18, 204); min-height: 20px; - border-radius: 3px; - margin-left: 3px; - margin-right: 1px; + border-radius: 5px; + margin: 3px 0px 3px 0px; } QScrollBar:handle:vertical:hover { diff --git a/ui/msgEdit/msgEdit.css b/ui/msgEdit/msgEdit.css index 2509f57fc..bbc967ccc 100644 --- a/ui/msgEdit/msgEdit.css +++ b/ui/msgEdit/msgEdit.css @@ -1,5 +1,5 @@ QTextEdit { - border-color: #c4c1bd; + border-color: @lightGrey; border-style: solid; border-width: 1px 0 1px 1px; } diff --git a/ui/settings/mainContent.css b/ui/settings/mainContent.css new file mode 100644 index 000000000..e08dc24b5 --- /dev/null +++ b/ui/settings/mainContent.css @@ -0,0 +1,36 @@ +QCheckBox +{ + color: black; +} + +QLabel +{ + color: black; +} + +QGroupBox::title +{ + color: black; + background-color: white; +} + +QWidget +{ + color: black; + background-color: white; +} + +QComboBox +{ + background-color: white; +} + +QComboBox:on QComboBox:off QComboBox:drop-down +{ + background: rgba(18, 18, 18, 204); +} + +QComboBox:active +{ + background: white; +} diff --git a/ui/settings/mainHead.css b/ui/settings/mainHead.css new file mode 100644 index 000000000..3a97d7d35 --- /dev/null +++ b/ui/settings/mainHead.css @@ -0,0 +1,5 @@ +QWidget +{ + color: black; + background: white; +} diff --git a/ui/window/closeButton.png b/ui/window/closeButton.png deleted file mode 100644 index ce57b0e9e..000000000 Binary files a/ui/window/closeButton.png and /dev/null differ diff --git a/ui/window/closeButtonHover.png b/ui/window/closeButtonHover.png deleted file mode 100644 index 8b756f08b..000000000 Binary files a/ui/window/closeButtonHover.png and /dev/null differ diff --git a/ui/window/closeButtonPressed.png b/ui/window/closeButtonPressed.png deleted file mode 100644 index caa1e4074..000000000 Binary files a/ui/window/closeButtonPressed.png and /dev/null differ diff --git a/ui/window/maximizeButton.png b/ui/window/maximizeButton.png deleted file mode 100644 index d3223796c..000000000 Binary files a/ui/window/maximizeButton.png and /dev/null differ diff --git a/ui/window/maximizeButtonHover.png b/ui/window/maximizeButtonHover.png deleted file mode 100644 index dae87a3b5..000000000 Binary files a/ui/window/maximizeButtonHover.png and /dev/null differ diff --git a/ui/window/maximizeButtonPressed.png b/ui/window/maximizeButtonPressed.png deleted file mode 100644 index 719b3f8bc..000000000 Binary files a/ui/window/maximizeButtonPressed.png and /dev/null differ diff --git a/ui/window/minimizeButton.png b/ui/window/minimizeButton.png deleted file mode 100644 index 8773e5359..000000000 Binary files a/ui/window/minimizeButton.png and /dev/null differ diff --git a/ui/window/minimizeButtonHover.png b/ui/window/minimizeButtonHover.png deleted file mode 100644 index 982c032b0..000000000 Binary files a/ui/window/minimizeButtonHover.png and /dev/null differ diff --git a/ui/window/minimizeButtonPressed.png b/ui/window/minimizeButtonPressed.png deleted file mode 100644 index c72c1a5e9..000000000 Binary files a/ui/window/minimizeButtonPressed.png and /dev/null differ diff --git a/ui/window/restoreButton.png b/ui/window/restoreButton.png deleted file mode 100644 index d3223796c..000000000 Binary files a/ui/window/restoreButton.png and /dev/null differ diff --git a/ui/window/restoreButtonHover.png b/ui/window/restoreButtonHover.png deleted file mode 100644 index dae87a3b5..000000000 Binary files a/ui/window/restoreButtonHover.png and /dev/null differ diff --git a/ui/window/restoreButtonPressed.png b/ui/window/restoreButtonPressed.png deleted file mode 100644 index 719b3f8bc..000000000 Binary files a/ui/window/restoreButtonPressed.png and /dev/null differ diff --git a/ui/window/statusPanel.css b/ui/window/statusPanel.css new file mode 100644 index 000000000..4683bc15a --- /dev/null +++ b/ui/window/statusPanel.css @@ -0,0 +1,70 @@ +#statusPanel +{ + background-color: @darkGrey; +} + +#statusPanel > #statusHead > #nameLabel { + font: @extraBig; + color: @white; +} + +#statusPanel > #statusHead > #statusLabel { + font: @medium; + color: @lightGrey; +} + +#statusPanel > #statusHead > #statusButton +{ + background: none; + background-color: @mediumGrey; + border: none; + border-radius: 6px; + width: 20px; + height: 40px; +} + +#statusPanel > #statusHead > #statusButton[status="online"] +{ + image: url(":ui/statusButton/dot_online.png") center center; +} + +#statusPanel > #statusHead > #statusButton[status="away"] +{ + image: url(":ui/statusButton/dot_idle.png") center center; +} + +#statusPanel > #statusHead > #statusButton[status="busy"] +{ + image: url(":ui/statusButton/dot_busy.png") center center; +} + +#statusPanel > #statusHead > #statusButton[status="offline"] +{ + image: url(":ui/statusButton/dot_away.png") center center; +} + +/*Bugged in Qt, but it's probably better to leave enabled so that users can tell it's clickable*/ +#statusPanel > #statusHead > #statusButton:hover +{ + background-color: @mediumGreyLight; +} + +#statusPanel > #statusHead > #statusButton:pressed +{ + background-color: @mediumGrey; +} + +#statusPanel > #statusHead > #statusButton:focus { + outline: none; +} + +#statusPanel > #statusHead > #statusButton::menu-indicator {image: none;} + +#statusPanel > #statusHead > #statusButton::menu-indicator:pressed, #statusPanel > #statusHead > #statusButton::menu-indicator:open +{ + image: url(":ui/statusButton/menu_indicator.png"); + subcontrol-origin: padding; + subcontrol-position: bottom center; + position: relative; + bottom: 2px; +} diff --git a/widget/adjustingscrollarea.cpp b/widget/adjustingscrollarea.cpp index bb273fa90..29315ba8b 100644 --- a/widget/adjustingscrollarea.cpp +++ b/widget/adjustingscrollarea.cpp @@ -21,14 +21,19 @@ #include #include -AdjustingScrollArea::AdjustingScrollArea(QWidget *parent) : - QScrollArea(parent) +AdjustingScrollArea::AdjustingScrollArea(QWidget *parent) + : QScrollArea(parent) { } void AdjustingScrollArea::resizeEvent(QResizeEvent *ev) { + int scrollBarWidth = verticalScrollBar()->isVisible() ? verticalScrollBar()->sizeHint().width() : 0; + + if (layoutDirection() == Qt::RightToLeft) + setViewportMargins(-scrollBarWidth, 0, 0, 0); + updateGeometry(); QScrollArea::resizeEvent(ev); } diff --git a/widget/adjustingscrollarea.h b/widget/adjustingscrollarea.h index a1645cbba..584e4e875 100644 --- a/widget/adjustingscrollarea.h +++ b/widget/adjustingscrollarea.h @@ -25,12 +25,8 @@ class AdjustingScrollArea : public QScrollArea public: explicit AdjustingScrollArea(QWidget *parent = 0); - virtual void resizeEvent(QResizeEvent *ev); + virtual void resizeEvent(QResizeEvent *ev) override; virtual QSize sizeHint() const override; -signals: - -public slots: - }; #endif // ADJUSTINGSCROLLAREA_H diff --git a/widget/chatareawidget.cpp b/widget/chatareawidget.cpp index 203633386..2ce9fc267 100644 --- a/widget/chatareawidget.cpp +++ b/widget/chatareawidget.cpp @@ -15,14 +15,18 @@ */ #include "chatareawidget.h" -#include "widget/tool/chataction.h" +#include "widget/tool/chatactions/chataction.h" #include #include #include #include +#include +#include -ChatAreaWidget::ChatAreaWidget(QWidget *parent) : - QTextBrowser(parent) +ChatAreaWidget::ChatAreaWidget(QWidget *parent) + : QTextBrowser(parent) + , tableFrmt(nullptr) + , nameWidth(75) { setReadOnly(true); viewport()->setCursor(Qt::ArrowCursor); @@ -32,17 +36,7 @@ ChatAreaWidget::ChatAreaWidget(QWidget *parent) : setOpenExternalLinks(false); setOpenLinks(false); setAcceptRichText(false); - - chatTextTable = textCursor().insertTable(1,3); - - QTextTableFormat tableFormat; - tableFormat.setColumnWidthConstraints({QTextLength(QTextLength::VariableLength,0), - QTextLength(QTextLength::PercentageLength,100), - QTextLength(QTextLength::VariableLength,0)}); - tableFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None); - chatTextTable->setFormat(tableFormat); - chatTextTable->format().setCellSpacing(2); - chatTextTable->format().setWidth(QTextLength(QTextLength::PercentageLength,100)); + setFrameStyle(QFrame::NoFrame); nameFormat.setAlignment(Qt::AlignRight); nameFormat.setNonBreakableLines(true); @@ -58,17 +52,22 @@ ChatAreaWidget::~ChatAreaWidget() for (ChatAction* action : messages) delete action; messages.clear(); + + if (tableFrmt) + delete tableFrmt; } void ChatAreaWidget::mouseReleaseEvent(QMouseEvent * event) { QTextEdit::mouseReleaseEvent(event); - int pos = this->document()->documentLayout()->hitTest(event->pos(), Qt::ExactHit); + QPointF documentHitPost(event->pos().x() + horizontalScrollBar()->value(), event->pos().y() + verticalScrollBar()->value()); + int pos = this->document()->documentLayout()->hitTest(documentHitPost, Qt::ExactHit); if (pos > 0) { QTextCursor cursor(document()); - cursor.setPosition(pos); - if( ! cursor.atEnd() ) + cursor.setPosition(pos); + + if(!cursor.atEnd()) { cursor.setPosition(pos+1); @@ -84,6 +83,7 @@ void ChatAreaWidget::mouseReleaseEvent(QMouseEvent * event) int middlepos = data.indexOf("."); QString widgetID = data.left(middlepos); QString widgetBtn = data.right(data.length() - middlepos - 1); + qDebug() << "ChatAreaWidget::mouseReleaseEvent:" << widgetID << widgetBtn; emit onFileTranfertInterract(widgetID, widgetBtn); } } @@ -103,18 +103,17 @@ void ChatAreaWidget::insertMessage(ChatAction *msgAction) checkSlider(); - int row = chatTextTable->rows() - 1; - chatTextTable->cellAt(row,0).firstCursorPosition().setBlockFormat(nameFormat); - chatTextTable->cellAt(row,2).firstCursorPosition().setBlockFormat(dateFormat); - QTextCursor cur = chatTextTable->cellAt(row,1).firstCursorPosition(); + QTextTable *chatTextTable = getMsgTable(); + QTextCursor cur = chatTextTable->cellAt(0, 2).firstCursorPosition(); cur.clearSelection(); cur.setKeepPositionOnInsert(true); - chatTextTable->cellAt(row,0).firstCursorPosition().insertHtml(msgAction->getName()); - chatTextTable->cellAt(row,1).firstCursorPosition().insertHtml(msgAction->getMessage()); - chatTextTable->cellAt(row,2).firstCursorPosition().insertHtml(msgAction->getDate()); - chatTextTable->appendRows(1); + chatTextTable->cellAt(0, 0).firstCursorPosition().setBlockFormat(nameFormat); + chatTextTable->cellAt(0, 0).firstCursorPosition().insertHtml(msgAction->getName()); + chatTextTable->cellAt(0, 2).firstCursorPosition().insertHtml(msgAction->getMessage()); + chatTextTable->cellAt(0, 4).firstCursorPosition().setBlockFormat(dateFormat); + chatTextTable->cellAt(0, 4).firstCursorPosition().insertHtml(msgAction->getDate()); - msgAction->setTextCursor(cur); + msgAction->setup(cur, this); messages.append(msgAction); } @@ -131,3 +130,35 @@ void ChatAreaWidget::checkSlider() QScrollBar* scroll = verticalScrollBar(); lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); } + +QTextTable *ChatAreaWidget::getMsgTable() +{ + if (tableFrmt == nullptr) + { + tableFrmt = new QTextTableFormat(); + tableFrmt->setCellSpacing(2); + tableFrmt->setBorderStyle(QTextFrameFormat::BorderStyle_None); + tableFrmt->setColumnWidthConstraints({QTextLength(QTextLength::FixedLength,nameWidth), + QTextLength(QTextLength::FixedLength,2), + QTextLength(QTextLength::PercentageLength,100), + QTextLength(QTextLength::FixedLength,2), + QTextLength(QTextLength::VariableLength,0)}); + } + + QTextCursor tc = textCursor(); + tc.movePosition(QTextCursor::End); + QTextTable *chatTextTable = tc.insertTable(1, 5, *tableFrmt); + + return chatTextTable; +} + +void ChatAreaWidget::setNameColWidth(int w) +{ + if (tableFrmt != nullptr) + { + delete tableFrmt; + tableFrmt = nullptr; + } + + nameWidth = w; +} diff --git a/widget/chatareawidget.h b/widget/chatareawidget.h index c30b12c61..d1e2dbe8f 100644 --- a/widget/chatareawidget.h +++ b/widget/chatareawidget.h @@ -31,6 +31,9 @@ public: virtual ~ChatAreaWidget(); void insertMessage(ChatAction *msgAction); + int nameColWidth() {return nameWidth;} + void setNameColWidth(int w); + signals: void onFileTranfertInterract(QString widgetName, QString buttonName); @@ -43,11 +46,13 @@ private slots: private: void checkSlider(); + QTextTable* getMsgTable(); + QTextTableFormat* tableFrmt; QList messages; bool lockSliderToBottom; int sliderPosition; - QTextTable *chatTextTable; + int nameWidth; QTextBlockFormat nameFormat, dateFormat; }; diff --git a/widget/emoticonswidget.cpp b/widget/emoticonswidget.cpp index bab7abe8d..8383c79a9 100644 --- a/widget/emoticonswidget.cpp +++ b/widget/emoticonswidget.cpp @@ -15,8 +15,8 @@ */ #include "emoticonswidget.h" -#include "smileypack.h" -#include "style.h" +#include "misc/smileypack.h" +#include "misc/style.h" #include #include @@ -27,7 +27,7 @@ EmoticonsWidget::EmoticonsWidget(QWidget *parent) : QMenu(parent) { - setStyleSheet(Style::get(":/ui/emoticonWidget/emoticonWidget.css")); + setStyleSheet(Style::getStylesheet(":/ui/emoticonWidget/emoticonWidget.css")); setLayout(&layout); layout.addWidget(&stack); @@ -66,6 +66,7 @@ EmoticonsWidget::EmoticonsWidget(QWidget *parent) : { QRadioButton* pageButton = new QRadioButton; pageButton->setProperty("pageIndex", i); + pageButton->setCursor(Qt::PointingHandCursor); pageButton->setChecked(i == 0); buttonLayout->addWidget(pageButton); @@ -80,6 +81,7 @@ EmoticonsWidget::EmoticonsWidget(QWidget *parent) : button->setIcon(SmileyPack::getInstance().getAsIcon(set[0])); button->setToolTip(set.join(" ")); button->setProperty("sequence", set[0]); + button->setCursor(Qt::PointingHandCursor); button->setFlat(true); connect(button, &QPushButton::clicked, this, &EmoticonsWidget::onSmileyClicked); diff --git a/widget/form/addfriendform.cpp b/widget/form/addfriendform.cpp index 3a44ae672..55f5e80dd 100644 --- a/widget/form/addfriendform.cpp +++ b/widget/form/addfriendform.cpp @@ -95,8 +95,8 @@ void AddFriendForm::onSendTriggered() if (id.isEmpty()) { showWarning(tr("Please fill in a valid Tox ID","Tox ID of the friend you're sending a friend request to")); } else if (isToxId(id)) { - if (id.toUpper() == Core::getInstance()->getSelfId().toUpper()) - showWarning(tr("You can't add yourself as a friend !","When trying to add your own Tox ID as friend")); + if (id.toUpper() == Core::getInstance()->getSelfId().toString().toUpper()) + showWarning(tr("You can't add yourself as a friend!","When trying to add your own Tox ID as friend")); else emit friendRequested(id, getMessage()); this->toxId.setText(""); diff --git a/widget/form/chatform.cpp b/widget/form/chatform.cpp index 0114a92db..a7e0a47b8 100644 --- a/widget/form/chatform.cpp +++ b/widget/form/chatform.cpp @@ -19,28 +19,41 @@ #include #include #include +#include +#include +#include +#include #include "chatform.h" #include "friend.h" #include "widget/friendwidget.h" #include "filetransferinstance.h" -#include "widget/tool/chataction.h" +#include "widget/tool/chatactions/filetransferaction.h" #include "widget/netcamview.h" #include "widget/chatareawidget.h" #include "widget/tool/chattextedit.h" #include "core.h" #include "widget/widget.h" +#include "widget/maskablepixmapwidget.h" +#include "widget/croppinglabel.h" +#include "misc/style.h" ChatForm::ChatForm(Friend* chatFriend) : f(chatFriend) { nameLabel->setText(f->getName()); - avatarLabel->setPixmap(QPixmap(":/img/contact_dark.png")); + + avatar->setPixmap(QPixmap(":/img/contact_dark.png"), Qt::transparent); statusMessageLabel = new CroppingLabel(); + statusMessageLabel->setFont(Style::getFont(Style::Medium)); + QPalette pal; pal.setColor(QPalette::WindowText, Style::getColor(Style::MediumGrey)); + statusMessageLabel->setPalette(pal); + netcam = new NetCamView(); headTextLayout->addWidget(statusMessageLabel); headTextLayout->addStretch(); + headTextLayout->setSpacing(0); connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend); connect(Core::getInstance(), &Core::videoFrameReceived, netcam, &NetCamView::updateDisplay); @@ -51,6 +64,9 @@ ChatForm::ChatForm(Friend* chatFriend) connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered); connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle())); connect(chatWidget, &ChatAreaWidget::onFileTranfertInterract, this, &ChatForm::onFileTansBtnClicked); + connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed); + + setAcceptDrops(true); } ChatForm::~ChatForm() @@ -70,9 +86,18 @@ void ChatForm::onSendTriggered() if (msg.isEmpty()) return; QString name = Widget::getInstance()->getUsername(); + if (msg.startsWith("/me ")) + { + msg = msg.right(msg.length() - 4); + addMessage(name, msg, true); + emit sendAction(f->friendId, msg); + } + else + { + addMessage(name, msg, false); + emit sendMessage(f->friendId, msg); + } msgEdit->clear(); - addMessage(name, msg); - emit sendMessage(f->friendId, msg); } void ChatForm::onAttachClicked() @@ -111,13 +136,14 @@ void ChatForm::startFileSend(ToxFile file) connect(Core::getInstance(), SIGNAL(fileTransferAccepted(ToxFile)), fileTrans, SLOT(onFileTransferAccepted(ToxFile))); connect(Core::getInstance(), SIGNAL(fileTransferPaused(int,int,ToxFile::FileDirection)), fileTrans, SLOT(onFileTransferPaused(int,int,ToxFile::FileDirection))); connect(Core::getInstance(), SIGNAL(fileTransferRemotePausedUnpaused(ToxFile,bool)), fileTrans, SLOT(onFileTransferRemotePausedUnpaused(ToxFile,bool))); + connect(Core::getInstance(), SIGNAL(fileTransferBrokenUnbroken(ToxFile, bool)), fileTrans, SLOT(onFileTransferBrokenUnbroken(ToxFile, bool))); QString name = Widget::getInstance()->getUsername(); if (name == previousName) name = ""; previousName = Widget::getInstance()->getUsername(); - chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), true)); + chatWidget->insertMessage(new FileTransferAction(fileTrans, getElidedName(name), QTime::currentTime().toString("hh:mm"), true)); } void ChatForm::onFileRecvRequest(ToxFile file) @@ -134,9 +160,10 @@ void ChatForm::onFileRecvRequest(ToxFile file) connect(Core::getInstance(), SIGNAL(fileTransferAccepted(ToxFile)), fileTrans, SLOT(onFileTransferAccepted(ToxFile))); connect(Core::getInstance(), SIGNAL(fileTransferPaused(int,int,ToxFile::FileDirection)), fileTrans, SLOT(onFileTransferPaused(int,int,ToxFile::FileDirection))); connect(Core::getInstance(), SIGNAL(fileTransferRemotePausedUnpaused(ToxFile,bool)), fileTrans, SLOT(onFileTransferRemotePausedUnpaused(ToxFile,bool))); + connect(Core::getInstance(), SIGNAL(fileTransferBrokenUnbroken(ToxFile, bool)), fileTrans, SLOT(onFileTransferBrokenUnbroken(ToxFile, bool))); Widget* w = Widget::getInstance(); - if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow()) + if (!w->isFriendWidgetCurActiveWidget(f)|| w->isMinimized() || !w->isActiveWindow()) { w->newMessageAlert(); f->hasNewEvents=true; @@ -148,7 +175,7 @@ void ChatForm::onFileRecvRequest(ToxFile file) name = ""; previousName = f->getName(); - chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), false)); + chatWidget->insertMessage(new FileTransferAction(fileTrans, getElidedName(name), QTime::currentTime().toString("hh:mm"), false)); } void ChatForm::onAvInvite(int FriendId, int CallId, bool video) @@ -177,7 +204,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video) } Widget* w = Widget::getInstance(); - if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow()) + if (!w->isFriendWidgetCurActiveWidget(f)|| w->isMinimized() || !w->isActiveWindow()) { w->newMessageAlert(); f->hasNewEvents=true; @@ -455,3 +482,47 @@ void ChatForm::onFileTansBtnClicked(QString widgetName, QString buttonName) else qDebug() << "no filetransferwidget: " << id; } + +void ChatForm::onFileSendFailed(int FriendId, const QString &fname) +{ + if (FriendId != f->friendId) + return; + + addSystemInfoMessage("File: \"" + fname + "\" failed to send.", "red"); +} + +void ChatForm::onAvatarChange(int FriendId, const QPixmap &pic) +{ + if (FriendId != f->friendId) + return; + + avatar->setPixmap(pic); +} + +void ChatForm::dragEnterEvent(QDragEnterEvent *ev) +{ + if (ev->mimeData()->hasUrls()) + ev->acceptProposedAction(); +} + +void ChatForm::dropEvent(QDropEvent *ev) +{ + if (ev->mimeData()->hasUrls()) + { + for (QUrl url : ev->mimeData()->urls()) + { + QFileInfo info(url.path()); + + if (info.exists()) + Core::getInstance()->sendFile(f->friendId, info.fileName(), info.absoluteFilePath(), info.size()); + } + } +} + +void ChatForm::onAvatarRemoved(int FriendId) +{ + if (FriendId != f->friendId) + return; + + avatar->setPixmap(QPixmap(":/img/contact_dark.png"), Qt::transparent); +} diff --git a/widget/form/chatform.h b/widget/form/chatform.h index 93bc2d6b4..13eba9b7d 100644 --- a/widget/form/chatform.h +++ b/widget/form/chatform.h @@ -23,6 +23,7 @@ struct Friend; class FileTransferInstance; class NetCamView; +class QPixmap; class ChatForm : public GenericChatForm { @@ -55,6 +56,8 @@ public slots: void onAvPeerTimeout(int FriendId, int CallId); void onAvMediaChange(int FriendId, int CallId, bool video); void onMicMuteToggle(); + void onAvatarChange(int FriendId, const QPixmap& pic); + void onAvatarRemoved(int FriendId); private slots: void onSendTriggered(); @@ -65,6 +68,12 @@ private slots: void onHangupCallTriggered(); void onCancelCallTriggered(); void onFileTansBtnClicked(QString widgetName, QString buttonName); + void onFileSendFailed(int FriendId, const QString &fname); + +protected: + // drag & drop + void dragEnterEvent(QDragEnterEvent* ev); + void dropEvent(QDropEvent* ev); private: Friend* f; diff --git a/widget/form/genericchatform.cpp b/widget/form/genericchatform.cpp index f5681f1c6..08e8040bb 100644 --- a/widget/form/genericchatform.cpp +++ b/widget/form/genericchatform.cpp @@ -17,32 +17,39 @@ #include "genericchatform.h" #include "ui_mainwindow.h" #include -#include "smileypack.h" +#include "misc/smileypack.h" #include "widget/emoticonswidget.h" -#include "style.h" +#include "misc/style.h" #include "widget/widget.h" -#include "settings.h" -#include "widget/tool/chataction.h" +#include "misc/settings.h" +#include "widget/tool/chatactions/messageaction.h" +#include "widget/tool/chatactions/systemmessageaction.h" +#include "widget/tool/chatactions/actionaction.h" #include "widget/chatareawidget.h" #include "widget/tool/chattextedit.h" +#include "widget/maskablepixmapwidget.h" -GenericChatForm::GenericChatForm(QObject *parent) : - QObject(parent) +GenericChatForm::GenericChatForm(QWidget *parent) : + QWidget(parent) { curRow = 0; - mainWidget = new QWidget(); headWidget = new QWidget(); + headWidget = new QWidget(); nameLabel = new CroppingLabel(); - avatarLabel = new QLabel(); + nameLabel->setFont(Style::getFont(Style::MediumBold)); + QPalette pal; pal.setColor(QPalette::WindowText, Style::getColor(Style::DarkGrey)); + nameLabel->setPalette(pal); + + avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png"); QHBoxLayout *headLayout = new QHBoxLayout(), *mainFootLayout = new QHBoxLayout(); headTextLayout = new QVBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout(); QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout(); chatWidget = new ChatAreaWidget(); - chatWidget->document()->setDefaultStyleSheet(Style::get(":ui/chatArea/innerStyle.css")); - chatWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css")); + chatWidget->document()->setDefaultStyleSheet(Style::getStylesheet(":ui/chatArea/innerStyle.css")); + chatWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatArea.css")); msgEdit = new ChatTextEdit(); @@ -61,29 +68,29 @@ GenericChatForm::GenericChatForm(QObject *parent) : footButtonsSmall->setSpacing(2); - msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css")); + msgEdit->setStyleSheet(Style::getStylesheet(":/ui/msgEdit/msgEdit.css")); msgEdit->setFixedHeight(50); msgEdit->setFrameStyle(QFrame::NoFrame); - sendButton->setStyleSheet(Style::get(":/ui/sendButton/sendButton.css")); - fileButton->setStyleSheet(Style::get(":/ui/fileButton/fileButton.css")); - emoteButton->setStyleSheet(Style::get(":/ui/emoteButton/emoteButton.css")); + sendButton->setStyleSheet(Style::getStylesheet(":/ui/sendButton/sendButton.css")); + fileButton->setStyleSheet(Style::getStylesheet(":/ui/fileButton/fileButton.css")); + emoteButton->setStyleSheet(Style::getStylesheet(":/ui/emoteButton/emoteButton.css")); callButton->setObjectName("green"); - callButton->setStyleSheet(Style::get(":/ui/callButton/callButton.css")); + callButton->setStyleSheet(Style::getStylesheet(":/ui/callButton/callButton.css")); videoButton->setObjectName("green"); - videoButton->setStyleSheet(Style::get(":/ui/videoButton/videoButton.css")); + videoButton->setStyleSheet(Style::getStylesheet(":/ui/videoButton/videoButton.css")); - QString volButtonStylesheet = Style::get(":/ui/volButton/volButton.css"); + QString volButtonStylesheet = Style::getStylesheet(":/ui/volButton/volButton.css"); volButton->setObjectName("green"); volButton->setStyleSheet(volButtonStylesheet); - QString micButtonStylesheet = Style::get(":/ui/micButton/micButton.css"); + QString micButtonStylesheet = Style::getStylesheet(":/ui/micButton/micButton.css"); micButton->setObjectName("green"); micButton->setStyleSheet(micButtonStylesheet); - mainWidget->setLayout(mainLayout); + setLayout(mainLayout); mainLayout->addWidget(chatWidget); mainLayout->addLayout(mainFootLayout); mainLayout->setMargin(0); @@ -98,7 +105,7 @@ GenericChatForm::GenericChatForm(QObject *parent) : mainFootLayout->setSpacing(0); headWidget->setLayout(headLayout); - headLayout->addWidget(avatarLabel); + headLayout->addWidget(avatar); headLayout->addLayout(headTextLayout); headLayout->addLayout(volMicLayout); headLayout->addWidget(callButton); @@ -128,10 +135,10 @@ void GenericChatForm::setName(const QString &newName) void GenericChatForm::show(Ui::MainWindow &ui) { - ui.mainContent->layout()->addWidget(mainWidget); + ui.mainContent->layout()->addWidget(this); ui.mainHead->layout()->addWidget(headWidget); - mainWidget->show(); headWidget->show(); + QWidget::show(); } void GenericChatForm::onChatContextMenuRequested(QPoint pos) @@ -160,21 +167,29 @@ void GenericChatForm::onSaveLogClicked() file.close(); } -void GenericChatForm::addMessage(QString author, QString message, QDateTime datetime) +void GenericChatForm::addMessage(QString author, QString message, bool isAction, QDateTime datetime) { QString date = datetime.toString(Settings::getInstance().getTimestampFormat()); bool isMe = (author == Widget::getInstance()->getUsername()); - if (previousName == author) - chatWidget->insertMessage(new MessageAction("", message, date, isMe)); - else chatWidget->insertMessage(new MessageAction(author , message, date, isMe)); - previousName = author; -} + if (!isAction && message.startsWith("/me ")) + { // always render actions regardless of what core thinks + isAction = true; + message = message.right(message.length()-4); + } -GenericChatForm::~GenericChatForm() -{ - delete mainWidget; - delete headWidget; + if (isAction) + { + chatWidget->insertMessage(new ActionAction (getElidedName(author), message, date, isMe)); + previousName = ""; // next msg has a name regardless + return; + } + else if (previousName == author) + chatWidget->insertMessage(new MessageAction("", message, date, isMe)); + else + chatWidget->insertMessage(new MessageAction(getElidedName(author), message, date, isMe)); + + previousName = author; } void GenericChatForm::onEmoteButtonClicked() @@ -208,3 +223,19 @@ void GenericChatForm::focusInput() { msgEdit->setFocus(); } + +void GenericChatForm::addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime) +{ + previousName = ""; + QString date = datetime.toString(Settings::getInstance().getTimestampFormat()); + + chatWidget->insertMessage(new SystemMessageAction(message, type, date)); +} + +QString GenericChatForm::getElidedName(const QString& name) +{ + // update this whenever you change the font in innerStyle.css + QFontMetrics fm(Style::getFont(Style::BigBold)); + + return fm.elidedText(name, Qt::ElideRight, chatWidget->nameColWidth()); +} diff --git a/widget/form/genericchatform.h b/widget/form/genericchatform.h index 95940cda5..b8c373d23 100644 --- a/widget/form/genericchatform.h +++ b/widget/form/genericchatform.h @@ -17,7 +17,7 @@ #ifndef GENERICCHATFORM_H #define GENERICCHATFORM_H -#include +#include #include #include @@ -30,24 +30,26 @@ class QPushButton; class CroppingLabel; class ChatTextEdit; class ChatAreaWidget; +class MaskablePixmapWidget; namespace Ui { class MainWindow; } -class GenericChatForm : public QObject +class GenericChatForm : public QWidget { Q_OBJECT public: - GenericChatForm(QObject *parent = 0); - virtual ~GenericChatForm(); + GenericChatForm(QWidget *parent = 0); virtual void setName(const QString &newName); virtual void show(Ui::MainWindow &ui); - void addMessage(QString author, QString message, QDateTime datetime=QDateTime::currentDateTime()); + void addMessage(QString author, QString message, bool isAction = false, QDateTime datetime=QDateTime::currentDateTime()); + void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime=QDateTime::currentDateTime()); signals: void sendMessage(int, QString); + void sendAction(int, QString); public slots: void focusInput(); @@ -59,9 +61,11 @@ protected slots: void onEmoteInsertRequested(QString str); protected: + QString getElidedName(const QString& name); + CroppingLabel *nameLabel; - QLabel *avatarLabel; - QWidget *mainWidget, *headWidget; + MaskablePixmapWidget *avatar; + QWidget *headWidget; QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton; QVBoxLayout *headTextLayout; ChatTextEdit *msgEdit; diff --git a/widget/form/groupchatform.cpp b/widget/form/groupchatform.cpp index 907787fd3..5e97d22df 100644 --- a/widget/form/groupchatform.cpp +++ b/widget/form/groupchatform.cpp @@ -19,7 +19,12 @@ #include "widget/groupwidget.h" #include "widget/tool/chattextedit.h" #include "widget/croppinglabel.h" +#include "widget/maskablepixmapwidget.h" +#include "core.h" +#include "misc/style.h" #include +#include +#include GroupChatForm::GroupChatForm(Group* chatGroup) : group(chatGroup) @@ -36,10 +41,14 @@ GroupChatForm::GroupChatForm(Group* chatGroup) QFont small; small.setPixelSize(10); - nameLabel->setText(group->widget->name.text()); - nusersLabel->setFont(small); + nameLabel->setText(group->widget->getName()); + + nusersLabel->setFont(Style::getFont(Style::Medium)); nusersLabel->setText(GroupChatForm::tr("%1 users in chat","Number of users in chat").arg(group->peers.size())); - avatarLabel->setPixmap(QPixmap(":/img/group_dark.png")); + QPalette pal; pal.setColor(QPalette::WindowText, Style::getColor(Style::MediumGrey)); + nusersLabel->setPalette(pal); + + avatar->setPixmap(QPixmap(":/img/group_dark.png"), Qt::transparent); QString names; for (QString& s : group->peers) @@ -58,11 +67,8 @@ GroupChatForm::GroupChatForm(Group* chatGroup) connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered())); connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered())); -} - -GroupChatForm::~GroupChatForm() -{ + setAcceptDrops(true); } void GroupChatForm::onSendTriggered() @@ -74,17 +80,6 @@ void GroupChatForm::onSendTriggered() emit sendMessage(group->groupId, msg); } -void GroupChatForm::addGroupMessage(QString message, int peerId) -{ - QString msgAuthor; - if (group->peers.contains(peerId)) - msgAuthor = group->peers[peerId]; - else - msgAuthor = tr(""); - - addMessage(msgAuthor, message); -} - void GroupChatForm::onUserListChanged() { nusersLabel->setText(tr("%1 users in chat").arg(group->nPeers)); @@ -94,3 +89,19 @@ void GroupChatForm::onUserListChanged() names.chop(2); namesList->setText(names); } + +void GroupChatForm::dragEnterEvent(QDragEnterEvent *ev) +{ + if (ev->mimeData()->hasFormat("friend")) + ev->acceptProposedAction(); +} + +void GroupChatForm::dropEvent(QDropEvent *ev) +{ + if (ev->mimeData()->hasFormat("friend")) + { + int friendId = ev->mimeData()->data("friend").toInt(); + Core::getInstance()->groupInviteFriend(friendId, group->groupId); + } +} + diff --git a/widget/form/groupchatform.h b/widget/form/groupchatform.h index cdf20a113..6b3984b06 100644 --- a/widget/form/groupchatform.h +++ b/widget/form/groupchatform.h @@ -27,13 +27,17 @@ class GroupChatForm : public GenericChatForm Q_OBJECT public: GroupChatForm(Group* chatGroup); - ~GroupChatForm(); - void addGroupMessage(QString message, int peerId); + void onUserListChanged(); private slots: void onSendTriggered(); +protected: + // drag & drop + void dragEnterEvent(QDragEnterEvent* ev); + void dropEvent(QDropEvent* ev); + private: Group* group; QLabel *nusersLabel, *namesList; diff --git a/widget/form/settings/avform.cpp b/widget/form/settings/avform.cpp new file mode 100644 index 000000000..4b7afef25 --- /dev/null +++ b/widget/form/settings/avform.cpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "avform.h" +#include "widget/camera.h" +#include "ui_avsettings.h" + +AVForm::AVForm(Camera* cam) : + GenericForm(tr("Audio/Video settings"), QPixmap(":/img/settings/av.png")) +{ + bodyUI = new Ui::AVSettings; + bodyUI->setupUi(this); + + camView = new SelfCamView(cam, this); + bodyUI->videoGroup->layout()->addWidget(camView); + camView->hide(); // hide by default + + connect(bodyUI->testVideoBtn, &QPushButton::clicked, this, &AVForm::onTestVideoPressed); +} + +AVForm::~AVForm() +{ + delete bodyUI; +} + +void AVForm::showTestVideo() +{ + bodyUI->testVideoBtn->setText(tr("Hide video preview","On a button")); + camView->show(); +} + +void AVForm::closeTestVideo() +{ + bodyUI->testVideoBtn->setText(tr("Show video preview","On a button")); + camView->close(); +} + +void AVForm::onTestVideoPressed() +{ + if (camView->isVisible()) + closeTestVideo(); + else + showTestVideo(); +} diff --git a/widget/form/settings/avform.h b/widget/form/settings/avform.h new file mode 100644 index 000000000..e75f742da --- /dev/null +++ b/widget/form/settings/avform.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 AVFORM_H +#define AVFORM_H + +#include "genericsettings.h" +#include "widget/selfcamview.h" +#include +#include +#include + +namespace Ui { +class AVSettings; +} + +class Camera; + +class AVForm : public GenericForm +{ + Q_OBJECT +public: + AVForm(Camera* cam); + ~AVForm(); + +private slots: + void onTestVideoPressed(); + +private: + Ui::AVSettings *bodyUI; + + SelfCamView* camView; + + void showTestVideo(); + void closeTestVideo(); + +}; + +#endif diff --git a/widget/form/settings/avsettings.ui b/widget/form/settings/avsettings.ui new file mode 100644 index 000000000..77fbb18b4 --- /dev/null +++ b/widget/form/settings/avsettings.ui @@ -0,0 +1,50 @@ + + + AVSettings + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Video settings + + + + + + Show video preview + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/widget/form/settings/generalform.cpp b/widget/form/settings/generalform.cpp new file mode 100644 index 000000000..b8138874e --- /dev/null +++ b/widget/form/settings/generalform.cpp @@ -0,0 +1,114 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "ui_generalsettings.h" +#include "generalform.h" +#include "widget/form/settingswidget.h" +#include "widget/widget.h" +#include "misc/settings.h" +#include "misc/smileypack.h" +#include + +GeneralForm::GeneralForm() : + GenericForm(tr("General Settings"), QPixmap(":/img/settings/general.png")) +{ + bodyUI = new Ui::GeneralSettings; + bodyUI->setupUi(this); + + bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6()); + bodyUI->cbUseTranslations->setChecked(Settings::getInstance().getUseTranslations()); + bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable()); + + for (auto entry : SmileyPack::listSmileyPacks()) + { + bodyUI->smileyPackBrowser->addItem(entry.first, entry.second); + } + bodyUI->smileyPackBrowser->setCurrentIndex(bodyUI->smileyPackBrowser->findData(Settings::getInstance().getSmileyPack())); + + bodyUI->cbUDPDisabled->setChecked(Settings::getInstance().getForceTCP()); + bodyUI->proxyAddr->setText(Settings::getInstance().getProxyAddr()); + int port = Settings::getInstance().getProxyPort(); + if (port != -1) + bodyUI->proxyPort->setValue(port); + + bodyUI->cbUseProxy->setChecked(Settings::getInstance().getUseProxy()); + onUseProxyUpdated(); + + connect(bodyUI->cbEnableIPv6, &QCheckBox::stateChanged, this, &GeneralForm::onEnableIPv6Updated); + connect(bodyUI->cbUseTranslations, &QCheckBox::stateChanged, this, &GeneralForm::onUseTranslationUpdated); + connect(bodyUI->cbMakeToxPortable, &QCheckBox::stateChanged, this, &GeneralForm::onMakeToxPortableUpdated); + connect(bodyUI->smileyPackBrowser, SIGNAL(currentIndexChanged(int)), this, SLOT(onSmileyBrowserIndexChanged(int))); + // new syntax can't handle overloaded signals... (at least not in a pretty way) + connect(bodyUI->cbUDPDisabled, &QCheckBox::stateChanged, this, &GeneralForm::onUDPUpdated); + connect(bodyUI->proxyAddr, &QLineEdit::editingFinished, this, &GeneralForm::onProxyAddrEdited); + connect(bodyUI->proxyPort, SIGNAL(valueChanged(int)), this, SLOT(onProxyPortEdited(int))); + connect(bodyUI->cbUseProxy, &QCheckBox::stateChanged, this, &GeneralForm::onUseProxyUpdated); +} + +GeneralForm::~GeneralForm() +{ + delete bodyUI; +} + +void GeneralForm::onEnableIPv6Updated() +{ + Settings::getInstance().setEnableIPv6(bodyUI->cbEnableIPv6->isChecked()); +} + +void GeneralForm::onUseTranslationUpdated() +{ + Settings::getInstance().setUseTranslations(bodyUI->cbUseTranslations->isChecked()); +} + +void GeneralForm::onMakeToxPortableUpdated() +{ + Settings::getInstance().setMakeToxPortable(bodyUI->cbMakeToxPortable->isChecked()); +} + +void GeneralForm::onSmileyBrowserIndexChanged(int index) +{ + QString filename = bodyUI->smileyPackBrowser->itemData(index).toString(); + Settings::getInstance().setSmileyPack(filename); +} + +void GeneralForm::onUDPUpdated() +{ + Settings::getInstance().setForceTCP(bodyUI->cbUDPDisabled->isChecked()); +} + +void GeneralForm::onProxyAddrEdited() +{ + Settings::getInstance().setProxyAddr(bodyUI->proxyAddr->text()); +} + +void GeneralForm::onProxyPortEdited(int port) +{ + if (port > 0) + { + Settings::getInstance().setProxyPort(port); + } else { + Settings::getInstance().setProxyPort(-1); + } +} + +void GeneralForm::onUseProxyUpdated() +{ + bool state = bodyUI->cbUseProxy->isChecked(); + + bodyUI->proxyAddr->setEnabled(state); + bodyUI->proxyPort->setEnabled(state); + Settings::getInstance().setUseProxy(state); +} diff --git a/widget/form/settings/generalform.h b/widget/form/settings/generalform.h new file mode 100644 index 000000000..ddfde9522 --- /dev/null +++ b/widget/form/settings/generalform.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 GENERALFORM_H +#define GENERALFORM_H + +#include "genericsettings.h" +#include +#include + +namespace Ui { +class GeneralSettings; +} + +class GeneralForm : public GenericForm +{ + Q_OBJECT +public: + GeneralForm(); + ~GeneralForm(); + +private slots: + void onEnableIPv6Updated(); + void onUseTranslationUpdated(); + void onMakeToxPortableUpdated(); + void onSmileyBrowserIndexChanged(int index); + void onUDPUpdated(); + void onProxyAddrEdited(); + void onProxyPortEdited(int port); + void onUseProxyUpdated(); + +private: + Ui::GeneralSettings *bodyUI; +}; + +#endif diff --git a/widget/form/settings/generalsettings.ui b/widget/form/settings/generalsettings.ui new file mode 100644 index 000000000..653f15204 --- /dev/null +++ b/widget/form/settings/generalsettings.ui @@ -0,0 +1,152 @@ + + + GeneralSettings + + + + 0 + 0 + 527 + 367 + + + + Form + + + + 6 + + + 6 + + + 6 + + + + + General Settings + + + + + + Use translations + + + + + + + Save settings to the working directory instead of the usual conf dir + + + Make Tox portable + + + + + + + + + + Theme + + + + + + Smiley Pack + + + + + + + + + + + + + Connection Settings + + + + + + Enable IPv6 (recommended) + + + + + + + This allows, e.g., toxing over Tor. It adds load to the Tox network however, so use only when necessary. + + + Disable UDP (not recommended) + + + + + + + Use proxy (SOCKS5) + + + + + + + + + Address + + + + + + + + + + Port + + + + + + + 0 + + + 65535 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/widget/form/settings/genericsettings.h b/widget/form/settings/genericsettings.h new file mode 100644 index 000000000..e79ec7b06 --- /dev/null +++ b/widget/form/settings/genericsettings.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 GENERICFORM_H +#define GENERICFORM_H + +#include +#include "widget/form/settingswidget.h" + +class GenericForm : public QWidget +{ + Q_OBJECT +public: + GenericForm(const QString &name, const QPixmap &icon) : formName(name), formIcon(icon) {;} + ~GenericForm() {;} + + virtual void updateContent() {;} + QString getFormName() {return formName;} + QPixmap getFormIcon() {return formIcon;} + +protected: + QString formName; + QPixmap formIcon; +}; + +#endif diff --git a/widget/form/settings/identityform.cpp b/widget/form/settings/identityform.cpp new file mode 100644 index 000000000..5aff0e513 --- /dev/null +++ b/widget/form/settings/identityform.cpp @@ -0,0 +1,90 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "ui_identitysettings.h" +#include "identityform.h" +#include "widget/form/settingswidget.h" +#include "widget/croppinglabel.h" +#include "core.h" +#include +#include +#include +#include + +IdentityForm::IdentityForm() : + GenericForm(tr("Your identity"), QPixmap(":/img/settings/identity.png")) +{ + bodyUI = new Ui::IdentitySettings; + bodyUI->setupUi(this); + + // tox + toxId = new ClickableTE(); + QFont small; + small.setPixelSize(13); + small.setKerning(false); + +// toxId->setTextInteractionFlags(Qt::TextSelectableByMouse); + toxId->setReadOnly(true); +// toxId->setFrameStyle(QFrame::NoFrame); +// toxId->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +// toxId->setFixedHeight(toxId->document()->size().height()*2); + toxId->setFont(small); + + bodyUI->toxGroup->layout()->addWidget(toxId); + + connect(bodyUI->toxIdLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked())); + connect(toxId, SIGNAL(clicked()), this, SLOT(copyIdClicked())); + connect(bodyUI->userName, SIGNAL(editingFinished()), this, SLOT(onUserNameEdited())); + connect(bodyUI->statusMessage, SIGNAL(editingFinished()), this, SLOT(onStatusMessageEdited())); +} + +IdentityForm::~IdentityForm() +{ +} + +void IdentityForm::copyIdClicked() +{ + toxId->selectAll(); + QString txt = toxId->text(); + txt.replace('\n',""); + QApplication::clipboard()->setText(txt); +} + +void IdentityForm::onUserNameEdited() +{ + Core::getInstance()->setUsername(bodyUI->userName->text()); +} + +void IdentityForm::onStatusMessageEdited() +{ + Core::getInstance()->setStatusMessage(bodyUI->statusMessage->text()); +} + +void IdentityForm::updateContent() +{ + toxId->setText(Core::getInstance()->getSelfId().toString()); +} + +void IdentityForm::setUserName(const QString &name) +{ + bodyUI->userName->setText(name); +} + +void IdentityForm::setStatusMessage(const QString &msg) +{ + bodyUI->statusMessage->setText(msg); +} diff --git a/widget/form/settings/identityform.h b/widget/form/settings/identityform.h new file mode 100644 index 000000000..2a4b07a77 --- /dev/null +++ b/widget/form/settings/identityform.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 IDENTITYFORM_H +#define IDENTITYFORM_H + +#include "genericsettings.h" +#include +#include +#include +#include + +class CroppingLabel; + +namespace Ui { +class IdentitySettings; +} + +class ClickableTE : public QLineEdit +{ + Q_OBJECT +public: + +signals: + void clicked(); +protected: + void mouseReleaseEvent(QMouseEvent*) {emit clicked();} +}; + +class IdentityForm : public GenericForm +{ + Q_OBJECT +public: + IdentityForm(); + ~IdentityForm(); + + void setUserName(const QString &name); + void setStatusMessage(const QString &msg); + + virtual void updateContent(); + +signals: + void userNameChanged(QString); + void statusMessageChanged(QString); + +private slots: + void copyIdClicked(); + void onUserNameEdited(); + void onStatusMessageEdited(); + +private: + Ui::IdentitySettings* bodyUI; + + ClickableTE* toxId; +}; + +#endif diff --git a/widget/form/settings/identitysettings.ui b/widget/form/settings/identitysettings.ui new file mode 100644 index 000000000..d27300155 --- /dev/null +++ b/widget/form/settings/identitysettings.ui @@ -0,0 +1,86 @@ + + + IdentitySettings + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Public Information + + + + + + Name + + + + + + + + + + Status + + + + + + + + + + + + + Tox ID + + + + + + Your Tox ID (click to copy) + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + CroppingLabel + QLabel +
widget/croppinglabel.h
+
+
+ + +
diff --git a/style.cpp b/widget/form/settings/privacyform.cpp similarity index 61% rename from style.cpp rename to widget/form/settings/privacyform.cpp index 609dcc277..c51dc0878 100644 --- a/style.cpp +++ b/widget/form/settings/privacyform.cpp @@ -14,22 +14,14 @@ See the COPYING file for more details. */ -#include "style.h" -#include "settings.h" +#include "privacyform.h" +#include "widget/form/settingswidget.h" -#include -#include - -QString Style::get(const QString &filename) +PrivacyForm::PrivacyForm() : + GenericForm(tr("Privacy settings"), QPixmap(":/img/settings/privacy.png")) +{ +} + +PrivacyForm::~PrivacyForm() { - if (!Settings::getInstance().getUseNativeStyle()) - { - QFile file(filename); - if (file.open(QFile::ReadOnly | QFile::Text)) - return file.readAll(); - else - qWarning() << "Style " << filename << " not found"; - } - - return QString(); } diff --git a/style.h b/widget/form/settings/privacyform.h similarity index 78% rename from style.h rename to widget/form/settings/privacyform.h index 4bd42d77f..ae1e61956 100644 --- a/style.h +++ b/widget/form/settings/privacyform.h @@ -14,17 +14,20 @@ See the COPYING file for more details. */ -#ifndef STYLE_H -#define STYLE_H +#ifndef PRIVACYFORM_H +#define PRIVACYFORM_H -class QString; +#include "genericsettings.h" -class Style +class PrivacyForm : public GenericForm { + Q_OBJECT public: - static QString get(const QString& filename); + PrivacyForm(); + ~PrivacyForm(); + private: - Style(); + }; -#endif // STYLE_H +#endif diff --git a/widget/form/settingsform.cpp b/widget/form/settingsform.cpp deleted file mode 100644 index 6f05e49ad..000000000 --- a/widget/form/settingsform.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - This file is part of qTox, a Qt-based graphical interface for Tox. - - This program is libre 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 "settingsform.h" -#include "widget/widget.h" -#include "settings.h" -#include "smileypack.h" -#include "ui_mainwindow.h" -#include -#include -#include -#include -#include -#include -#include "core.h" - -SettingsForm::SettingsForm() - : QObject() -{ - main = new QWidget(), head = new QWidget(); - hboxcont1 = new QWidget(), hboxcont2 = new QWidget(); - QFont bold, small; - bold.setBold(true); - small.setPixelSize(13); - small.setKerning(false); - headLabel.setText(tr("User Settings","\"Headline\" of the window")); - headLabel.setFont(bold); - - idLabel.setText("Tox ID " + tr("(click here to copy)", "Click on this text to copy TID to clipboard")); - id.setFont(small); - id.setTextInteractionFlags(Qt::TextSelectableByMouse); - id.setReadOnly(true); - id.setFrameStyle(QFrame::NoFrame); - id.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - id.setFixedHeight(id.document()->size().height()*2); - - profilesLabel.setText(tr("Available profiles:", "Labels the profile selection box")); - loadConf.setText(tr("Load profile", "button to load selected profile")); - exportConf.setText(tr("Export profile", "button to save selected profile elsewhere")); - delConf.setText(tr("Delete profile", "button to delete selected profile from disk")); - delConf.setToolTip(tr("This is useful to remain safe on public computers", "describes the delete profile button")); - //delConf.setWhatsThis(tr("This is useful to remain safe on public computers", "describes the delete profile button")); - importConf.setText(tr("Import profile", "button to locate a profile")); - - videoTest.setText(tr("Test video","Text on a button to test the video/webcam")); - enableIPv6.setText(tr("Enable IPv6 (recommended)","Text on a checkbox to enable IPv6")); - enableIPv6.setChecked(Settings::getInstance().getEnableIPv6()); - useTranslations.setText(tr("Use translations","Text on a checkbox to enable translations")); - useTranslations.setChecked(Settings::getInstance().getUseTranslations()); - makeToxPortable.setText(tr("Make Tox portable","Text on a checkbox to make qTox a portable application")); - makeToxPortable.setChecked(Settings::getInstance().getMakeToxPortable()); - makeToxPortable.setToolTip(tr("Save settings to the working directory instead of the usual conf dir","describes makeToxPortable checkbox")); - - smileyPackLabel.setText(tr("Smiley Pack", "Text on smiley pack label")); - for (auto entry : SmileyPack::listSmileyPacks()) - smileyPackBrowser.addItem(entry.first, entry.second); - smileyPackBrowser.setCurrentIndex(smileyPackBrowser.findData(Settings::getInstance().getSmileyPack())); - - main->setLayout(&layout); - layout.addWidget(&idLabel); - layout.addWidget(&id); - cbox.addWidget(&profilesLabel); - cbox.addWidget(&profiles); - hboxcont1->setLayout(&cbox); - layout.addWidget(hboxcont1); - buttons.addWidget(&loadConf); - buttons.addWidget(&exportConf); - buttons.addWidget(&delConf); - hboxcont2->setLayout(&buttons); - layout.addWidget(hboxcont2); - layout.addWidget(&importConf); - layout.addWidget(&videoTest); - layout.addWidget(&enableIPv6); - layout.addWidget(&useTranslations); - layout.addWidget(&makeToxPortable); - layout.addWidget(&smileyPackLabel); - layout.addWidget(&smileyPackBrowser); - layout.addStretch(); - - head->setLayout(&headLayout); - headLayout.addWidget(&headLabel); - - connect(&loadConf, SIGNAL(clicked()), this, SLOT(onLoadClicked())); - connect(&exportConf, SIGNAL(clicked()), this, SLOT(onExportClicked())); - connect(&delConf, SIGNAL(clicked()), this, SLOT(onDeleteClicked())); - connect(&importConf, SIGNAL(clicked()), this, SLOT(onImportClicked())); - connect(&videoTest, SIGNAL(clicked()), this, SLOT(onTestVideoClicked())); - connect(&enableIPv6, SIGNAL(stateChanged(int)), this, SLOT(onEnableIPv6Updated())); - connect(&useTranslations, SIGNAL(stateChanged(int)), this, SLOT(onUseTranslationUpdated())); - connect(&makeToxPortable, SIGNAL(stateChanged(int)), this, SLOT(onMakeToxPortableUpdated())); - connect(&idLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked())); - connect(&smileyPackBrowser, SIGNAL(currentIndexChanged(int)), this, SLOT(onSmileyBrowserIndexChanged(int))); -} - -SettingsForm::~SettingsForm() -{ -} - -QList SettingsForm::searchProfiles() -{ - QList out; - QDir dir(Settings::getSettingsDirPath()); - dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); - dir.setNameFilters(QStringList("*.tox")); - for(QFileInfo file : dir.entryInfoList()) - { - out += file.completeBaseName(); - } - return out; -} - -QString SettingsForm::getSelectedSavePath() -{ - return Settings::getSettingsDirPath() + QDir::separator() + profiles.currentText() + Core::getInstance()->TOX_EXT; -} - -void SettingsForm::setFriendAddress(const QString& friendAddress) -{ - QString txt{friendAddress}; - txt.insert(38,'\n'); - id.setText(txt); -} - -void SettingsForm::show(Ui::MainWindow &ui) -{ - profiles.clear(); - for (QString profile : searchProfiles()) - { - profiles.addItem(profile); - } - if (QString current = Settings::getInstance().getCurrentProfile()) - profiles.setCurrentText(current); - ui.mainContent->layout()->addWidget(main); - ui.mainHead->layout()->addWidget(head); - main->show(); - head->show(); -} - -void SettingsForm::onLoadClicked() -{ - Core::getInstance()->switchConfiguration(profiles.currentText()); -} - -void SettingsForm::onExportClicked() -{ - QString current = getSelectedSavePath(); - QString path = QFileDialog::getSaveFileName(0, tr("Export profile", "save dialog title"), QDir::homePath() + QDir::separator() + profiles.currentText() + Core::getInstance()->TOX_EXT, tr("Tox save file (*.tox)", "save dialog filter")); - QFile::copy(getSelectedSavePath(), path); -} - -void SettingsForm::onDeleteClicked() -{ - if (Settings::getInstance().getCurrentProfile() == profiles.currentText()) - { - QMessageBox::warning(main, tr("Profile currently loaded","current profile deletion warning title"), tr("This profile is currently in use. Please load a different profile before deleting this one.","current profile deletion warning text")); - } - else - { - QMessageBox::StandardButton resp = QMessageBox::question(main, - tr("Deletion imminent!","deletion confirmation title"), tr("Are you sure you want to delete this profile?","deletion confirmation text"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (resp == QMessageBox::Yes) - { - QFile::remove(getSelectedSavePath()); - profiles.removeItem(profiles.currentIndex()); - } - } -} - -void SettingsForm::onImportClicked() -{ - QString path = QFileDialog::getOpenFileName(0, tr("Import profile", "import dialog title"), QDir::homePath(), tr("Tox save file (*.tox)", "import dialog filter")); - QFileInfo info(path); - QString profile = info.completeBaseName(); - QString profilePath = Settings::getSettingsDirPath() + profile + Core::getInstance()->TOX_EXT; - Settings::getInstance().setCurrentProfile(profile); - QFile::copy(path, profilePath); - profiles.addItem(profile); - Core::getInstance()->switchConfiguration(profile); -} - -void SettingsForm::onTestVideoClicked() -{ - Widget::getInstance()->showTestCamview(); -} - -void SettingsForm::onEnableIPv6Updated() -{ - Settings::getInstance().setEnableIPv6(enableIPv6.isChecked()); -} - -void SettingsForm::copyIdClicked() -{ - id.selectAll(); - QString txt = id.toPlainText(); - txt.replace('\n',""); - QApplication::clipboard()->setText(txt); -} - -void SettingsForm::onUseTranslationUpdated() -{ - Settings::getInstance().setUseTranslations(useTranslations.isChecked()); -} - -void SettingsForm::onMakeToxPortableUpdated() -{ - Settings::getInstance().setMakeToxPortable(makeToxPortable.isChecked()); -} - -void SettingsForm::onSmileyBrowserIndexChanged(int index) -{ - QString filename = smileyPackBrowser.itemData(index).toString(); - Settings::getInstance().setSmileyPack(filename); -} diff --git a/widget/form/settingsform.h b/widget/form/settingsform.h deleted file mode 100644 index e35f588da..000000000 --- a/widget/form/settingsform.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - This file is part of qTox, a Qt-based graphical interface for Tox. - - This program is libre 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 SETTINGSFORM_H -#define SETTINGSFORM_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "widget/croppinglabel.h" - -namespace Ui {class MainWindow;} -class QString; - -class SettingsForm : public QObject -{ - Q_OBJECT -public: - SettingsForm(); - ~SettingsForm(); - - void show(Ui::MainWindow &ui); - static QList searchProfiles(); - -public slots: - void setFriendAddress(const QString& friendAddress); - -private slots: - void onLoadClicked(); - void onExportClicked(); - void onDeleteClicked(); - void onImportClicked(); - void onTestVideoClicked(); - void onEnableIPv6Updated(); - void onUseTranslationUpdated(); - void onMakeToxPortableUpdated(); - void onSmileyBrowserIndexChanged(int index); - void copyIdClicked(); - -private: - QLabel headLabel, smileyPackLabel; - QTextEdit id; - CroppingLabel idLabel; - QLabel profilesLabel; - QComboBox profiles; - QPushButton loadConf, exportConf, delConf, importConf, videoTest; - QHBoxLayout cbox, buttons; - QCheckBox enableIPv6, useTranslations, makeToxPortable; - QVBoxLayout layout, headLayout; - QWidget *main, *head, *hboxcont1, *hboxcont2; - QComboBox smileyPackBrowser; - QString getSelectedSavePath(); -}; - -#endif // SETTINGSFORM_H diff --git a/widget/form/settingswidget.cpp b/widget/form/settingswidget.cpp new file mode 100644 index 000000000..b7539a4c5 --- /dev/null +++ b/widget/form/settingswidget.cpp @@ -0,0 +1,92 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "settingswidget.h" +#include "widget/widget.h" +#include "ui_mainwindow.h" +#include "widget/camera.h" +#include "widget/form/settings/generalform.h" +#include "widget/form/settings/identityform.h" +#include "widget/form/settings/privacyform.h" +#include "widget/form/settings/avform.h" +#include +#include + +SettingsWidget::SettingsWidget(Camera* cam, QWidget* parent) + : QWidget(parent) +{ + body = new QWidget(this); + QVBoxLayout *bodyLayout = new QVBoxLayout(); + body->setLayout(bodyLayout); + + head = new QWidget(this); + QHBoxLayout *headLayout = new QHBoxLayout(); + head->setLayout(headLayout); + + imgLabel = new QLabel(); + headLayout->addWidget(imgLabel); + + nameLabel = new QLabel(); + QFont bold; + bold.setBold(true); + nameLabel->setFont(bold); + headLayout->addWidget(nameLabel); + headLayout->addStretch(1); + + settingsWidgets = new QStackedWidget; + bodyLayout->addWidget(settingsWidgets); + + tabBar = new QTabBar; + bodyLayout->addWidget(tabBar); + + GeneralForm *gfrm = new GeneralForm; + ifrm = new IdentityForm; + PrivacyForm *pfrm = new PrivacyForm; + AVForm *avfrm = new AVForm(cam); + + GenericForm *cfgForms[] = {gfrm, ifrm, pfrm, avfrm}; + for (auto cfgForm : cfgForms) + { + tabBar->addTab(cfgForm->getFormIcon(), ""); + settingsWidgets->addWidget(cfgForm); + } + tabBar->setIconSize(QSize(20, 20)); + tabBar->setShape(QTabBar::RoundedSouth); + + connect(tabBar, &QTabBar::currentChanged, this, &SettingsWidget::onTabChanged); +} + +SettingsWidget::~SettingsWidget() +{ +} + +void SettingsWidget::show(Ui::MainWindow& ui) +{ + ui.mainContent->layout()->addWidget(body); + ui.mainHead->layout()->addWidget(head); + body->show(); + head->show(); + onTabChanged(tabBar->currentIndex()); +} + +void SettingsWidget::onTabChanged(int index) +{ + this->settingsWidgets->setCurrentIndex(index); + GenericForm *currentWidget = static_cast(this->settingsWidgets->widget(index)); + currentWidget->updateContent(); + nameLabel->setText(currentWidget->getFormName()); + imgLabel->setPixmap(currentWidget->getFormIcon().scaledToHeight(40, Qt::SmoothTransformation)); +} diff --git a/widget/form/settingswidget.h b/widget/form/settingswidget.h new file mode 100644 index 000000000..431e52c1c --- /dev/null +++ b/widget/form/settingswidget.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 SETTINGSWIDGET_H +#define SETTINGSWIDGET_H + +#include +#include +class Camera; +class GenericForm; +class GeneralForm; +class IdentityForm; +class PrivacyForm; +class AVForm; +class QTabBar; +class QStackedWidget; +class QLabel; + +namespace Ui {class MainWindow;} + +class SettingsWidget : public QWidget +{ + Q_OBJECT +public: + SettingsWidget(Camera* cam, QWidget* parent = nullptr); + ~SettingsWidget(); + + void show(Ui::MainWindow &ui); + IdentityForm *getIdentityForm() {return ifrm;} + +private slots: + void onTabChanged(int); + +private: + QWidget *head, *body; // keep the others private + IdentityForm *ifrm; + QStackedWidget *settingsWidgets; + QTabBar *tabBar; + QLabel *nameLabel, *imgLabel; +}; + +#endif // SETTINGSWIDGET_H diff --git a/widget/friendlistwidget.h b/widget/friendlistwidget.h index dae6b1c61..f8c3adf8c 100644 --- a/widget/friendlistwidget.h +++ b/widget/friendlistwidget.h @@ -23,6 +23,7 @@ class QLayout; class QGridLayout; +class QPixmap; class FriendListWidget : public QWidget { diff --git a/widget/friendwidget.cpp b/widget/friendwidget.cpp index e1744002d..2b830f107 100644 --- a/widget/friendwidget.cpp +++ b/widget/friendwidget.cpp @@ -22,59 +22,23 @@ #include "friend.h" #include "core.h" #include "widget/form/chatform.h" +#include "widget/maskablepixmapwidget.h" +#include "widget/croppinglabel.h" +#include "misc/style.h" #include #include +#include +#include +#include +#include FriendWidget::FriendWidget(int FriendId, QString id) : friendId(FriendId) + , isDefaultAvatar{true} { - setMouseTracking(true); - setAutoFillBackground(true); - setFixedHeight(55); - setLayout(&layout); - layout.setSpacing(0); - layout.setMargin(0); - layout.setStretchFactor(this, 100); - textLayout.setSpacing(0); - textLayout.setMargin(0); - setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft - - avatar.setPixmap(QPixmap(":img/contact.png")); - name.setText(id); - //statusPic.setAlignment(Qt::AlignHCenter); + avatar->setPixmap(QPixmap(":img/contact.png"), Qt::transparent); 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); - QPalette pal2; - pal2.setColor(QPalette::WindowText,Qt::white); - name.setPalette(pal2); - QPalette pal3; - pal3.setColor(QPalette::Background, QColor(65,65,65,255)); - this->setPalette(pal3); - - textLayout.addStretch(); - textLayout.addWidget(&name); - textLayout.addWidget(&statusMessage); - textLayout.addStretch(); - - layout.addSpacing(20); - layout.addWidget(&avatar); - layout.addSpacing(5); - layout.addLayout(&textLayout); - layout.addSpacing(5); - layout.addWidget(&statusPic); - layout.addSpacing(5); - - isActiveWidget = 0; - - layout.invalidate(); - layout.update(); - layout.activate(); - updateGeometry(); + nameLabel->setText(id); } void FriendWidget::contextMenuEvent(QContextMenuEvent * event) @@ -86,7 +50,7 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event) QMap groupActions; for (Group* group : GroupList::groupList) { - QAction* groupAction = inviteMenu->addAction(group->widget->name.text()); + QAction* groupAction = inviteMenu->addAction(group->widget->getName()); groupActions[groupAction] = group; } if (groupActions.isEmpty()) @@ -120,40 +84,18 @@ void FriendWidget::contextMenuEvent(QContextMenuEvent * event) void FriendWidget::setAsActiveChatroom() { - isActiveWidget = 1; + setActive(true); - QFont small; - small.setPixelSize(10); - statusMessage.setFont(small); - QPalette pal; - pal.setColor(QPalette::WindowText,Qt::darkGray); - statusMessage.setPalette(pal); - QPalette pal2; - pal2.setColor(QPalette::WindowText,Qt::black); - name.setPalette(pal2); - QPalette pal3; - pal3.setColor(QPalette::Background, Qt::white); - this->setPalette(pal3); - avatar.setPixmap(QPixmap(":img/contact_dark.png")); + if (isDefaultAvatar) + avatar->setPixmap(QPixmap(":img/contact_dark.png"), Qt::transparent); } void FriendWidget::setAsInactiveChatroom() { - isActiveWidget = 0; + setActive(false); - QFont small; - small.setPixelSize(10); - statusMessage.setFont(small); - QPalette pal; - pal.setColor(QPalette::WindowText,Qt::gray); - statusMessage.setPalette(pal); - QPalette pal2; - pal2.setColor(QPalette::WindowText,Qt::white); - name.setPalette(pal2); - QPalette pal3; - pal3.setColor(QPalette::Background, QColor(65,65,65,255)); - this->setPalette(pal3); - avatar.setPixmap(QPixmap(":img/contact.png")); + if (isDefaultAvatar) + avatar->setPixmap(QPixmap(":img/contact.png"), Qt::transparent); } void FriendWidget::updateStatusLight() @@ -190,3 +132,50 @@ void FriendWidget::resetEventFlags() Friend* f = FriendList::findFriend(friendId); f->hasNewEvents = 0; } + +void FriendWidget::onAvatarChange(int FriendId, const QPixmap& pic) +{ + if (FriendId != friendId) + return; + + isDefaultAvatar = false; + avatar->setPixmap(pic); + avatar->autopickBackground(); +} + +void FriendWidget::onAvatarRemoved(int FriendId) +{ + if (FriendId != friendId) + return; + + isDefaultAvatar = true; + + if (isActive()) + avatar->setPixmap(QPixmap(":img/contact_dark.png"), Qt::transparent); + else + avatar->setPixmap(QPixmap(":img/contact.png"), Qt::transparent); +} + +void FriendWidget::mousePressEvent(QMouseEvent *ev) +{ + if (ev->button() == Qt::LeftButton) + dragStartPos = ev->pos(); +} + +void FriendWidget::mouseMoveEvent(QMouseEvent *ev) +{ + if (!(ev->buttons() & Qt::LeftButton)) + return; + + if ((dragStartPos - ev->pos()).manhattanLength() > QApplication::startDragDistance()) + { + QDrag* drag = new QDrag(this); + QMimeData* mdata = new QMimeData; + mdata->setData("friend", QString::number(friendId).toLatin1()); + + drag->setMimeData(mdata); + drag->setPixmap(avatar->getPixmap()); + + drag->exec(Qt::CopyAction | Qt::MoveAction); + } +} diff --git a/widget/friendwidget.h b/widget/friendwidget.h index da158e68d..a04a5e76e 100644 --- a/widget/friendwidget.h +++ b/widget/friendwidget.h @@ -20,7 +20,9 @@ #include #include "genericchatroomwidget.h" -#include "croppinglabel.h" + +class QPixmap; +class MaskablePixmapWidget; struct FriendWidget : public GenericChatroomWidget { @@ -39,10 +41,18 @@ signals: void removeFriend(int friendId); void copyFriendIdToClipboard(int friendId); +public slots: + void onAvatarChange(int FriendId, const QPixmap& pic); + void onAvatarRemoved(int FriendId); + +protected: + void mousePressEvent(QMouseEvent* ev); + void mouseMoveEvent(QMouseEvent* ev); + public: int friendId; - QLabel avatar, statusPic; - CroppingLabel name, statusMessage; + bool isDefaultAvatar; + QPoint dragStartPos; }; #endif // FRIENDWIDGET_H diff --git a/widget/genericchatroomwidget.cpp b/widget/genericchatroomwidget.cpp index 234da1222..8e875bd3e 100644 --- a/widget/genericchatroomwidget.cpp +++ b/widget/genericchatroomwidget.cpp @@ -15,56 +15,82 @@ */ #include "genericchatroomwidget.h" +#include "misc/style.h" +#include "widget/maskablepixmapwidget.h" +#include "croppinglabel.h" #include +#include -GenericChatroomWidget::GenericChatroomWidget(QWidget *parent) : - QWidget(parent) +GenericChatroomWidget::GenericChatroomWidget(QWidget *parent) + : QFrame(parent) { + setFixedHeight(55); + + setLayout(&layout); + layout.setSpacing(0); + layout.setMargin(0); + textLayout.setSpacing(0); + textLayout.setMargin(0); + setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft + + // avatar + avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png"); + + // status text + statusMessageLabel = new CroppingLabel(this); + statusMessageLabel->setObjectName("status"); + + // name text + nameLabel = new CroppingLabel(this); + nameLabel->setObjectName("name"); + + textLayout.addStretch(); + textLayout.addWidget(nameLabel); + textLayout.addWidget(statusMessageLabel); + textLayout.addStretch(); + + layout.addSpacing(20); + layout.addWidget(avatar); + layout.addSpacing(10); + layout.addLayout(&textLayout); + layout.addSpacing(10); + layout.addWidget(&statusPic); + layout.addSpacing(10); + layout.activate(); + + setProperty("active", false); + setStyleSheet(Style::getStylesheet(":/ui/chatroomWidgets/genericChatroomWidget.css")); } -int GenericChatroomWidget::isActive() +bool GenericChatroomWidget::isActive() { - return isActiveWidget; + return property("active").toBool(); } -void GenericChatroomWidget::mousePressEvent(QMouseEvent *event) +void GenericChatroomWidget::setActive(bool active) { - if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton) - { - if (isActive()) - { - QPalette pal; - pal.setColor(QPalette::Background, QColor(250,250,250,255)); - this->setPalette(pal); - } - else - { - QPalette pal; - pal.setColor(QPalette::Background, QColor(85,85,85,255)); - this->setPalette(pal); - } - } + setProperty("active", active); + Style::repolish(this); } -void GenericChatroomWidget::leaveEvent(QEvent *) +void GenericChatroomWidget::setName(const QString &name) { - if (isActive() != 1) - { - QPalette pal; - pal.setColor(QPalette::Background, lastColor); - this->setPalette(pal); - } + nameLabel->setText(name); } -void GenericChatroomWidget::enterEvent(QEvent *) +void GenericChatroomWidget::setStatusMsg(const QString &status) { - if (isActive() != 1) - { - QPalette pal; - pal.setColor(QPalette::Background, QColor(75,75,75,255)); - lastColor = this->palette().background().color(); - this->setPalette(pal); - } + statusMessageLabel->setText(status); +} + +QString GenericChatroomWidget::getName() const +{ + return nameLabel->text(); +} + +QString GenericChatroomWidget::getStatusMsg() const +{ + return statusMessageLabel->text(); } void GenericChatroomWidget::mouseReleaseEvent(QMouseEvent*) diff --git a/widget/genericchatroomwidget.h b/widget/genericchatroomwidget.h index 14e46c16e..1dd6d8250 100644 --- a/widget/genericchatroomwidget.h +++ b/widget/genericchatroomwidget.h @@ -17,23 +17,24 @@ #ifndef GENERICCHATROOMWIDGET_H #define GENERICCHATROOMWIDGET_H -#include +#include #include #include +#include + +class CroppingLabel; +class MaskablePixmapWidget; namespace Ui { class MainWindow; } -class GenericChatroomWidget : public QWidget +class GenericChatroomWidget : public QFrame { Q_OBJECT public: GenericChatroomWidget(QWidget *parent = 0); - void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent (QMouseEvent* event); - void leaveEvent(QEvent *); - void enterEvent(QEvent *); virtual void setAsActiveChatroom(){;} virtual void setAsInactiveChatroom(){;} @@ -41,7 +42,14 @@ public: virtual void setChatForm(Ui::MainWindow &){;} virtual void resetEventFlags(){;} - int isActive(); + bool isActive(); + void setActive(bool active); + + void setName(const QString& name); + void setStatusMsg(const QString& status); + + QString getName() const; + QString getStatusMsg() const; signals: void chatroomWidgetClicked(GenericChatroomWidget* widget); @@ -49,10 +57,12 @@ signals: public slots: protected: - int isActiveWidget; QColor lastColor; QHBoxLayout layout; QVBoxLayout textLayout; + MaskablePixmapWidget* avatar; + QLabel statusPic; + CroppingLabel *nameLabel, *statusMessageLabel; }; #endif // GENERICCHATROOMWIDGET_H diff --git a/widget/groupwidget.cpp b/widget/groupwidget.cpp index b8c1cbcc0..05d04cf76 100644 --- a/widget/groupwidget.cpp +++ b/widget/groupwidget.cpp @@ -17,8 +17,10 @@ #include "groupwidget.h" #include "grouplist.h" #include "group.h" -#include "settings.h" +#include "misc/settings.h" #include "widget/form/groupchatform.h" +#include "widget/maskablepixmapwidget.h" +#include "misc/style.h" #include #include #include @@ -28,52 +30,15 @@ GroupWidget::GroupWidget(int GroupId, QString Name) : groupId{GroupId} { - setMouseTracking(true); - setAutoFillBackground(true); - setLayout(&layout); - setFixedHeight(55); - layout.setSpacing(0); - layout.setMargin(0); - textLayout.setSpacing(0); - textLayout.setMargin(0); - setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft - - avatar.setPixmap(QPixmap(":img/group.png")); + avatar->setPixmap(QPixmap(":img/group.png"), Qt::transparent); statusPic.setPixmap(QPixmap(":img/status/dot_online.png")); - name.setText(Name); - QFont small; - small.setPixelSize(10); - nusers.setFont(small); - QPalette pal; - pal.setColor(QPalette::WindowText,Qt::gray); - nusers.setPalette(pal); - QPalette pal2; - pal2.setColor(QPalette::WindowText,Qt::white); - name.setPalette(pal2); - QPalette pal3; - pal3.setColor(QPalette::Background, QColor(65,65,65,255)); - this->setPalette(pal3); + nameLabel->setText(Name); + Group* g = GroupList::findGroup(groupId); if (g) - nusers.setText(GroupWidget::tr("%1 users in chat").arg(g->peers.size())); + statusMessageLabel->setText(GroupWidget::tr("%1 users in chat").arg(g->peers.size())); else - nusers.setText(GroupWidget::tr("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(); - layout.addSpacing(5); - layout.addWidget(&statusPic); - layout.addSpacing(5); - - isActiveWidget = 0; + statusMessageLabel->setText(GroupWidget::tr("0 users in chat")); } void GroupWidget::contextMenuEvent(QContextMenuEvent * event) @@ -84,83 +49,44 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent * event) QAction* selectedItem = menu.exec(pos); if (selectedItem == quitGroup) - { - hide(); - show(); //Toggle visibility to work around bug of repaintEvent() not being fired on parent widget when this is hidden - hide(); emit removeGroup(groupId); - return; - } } void GroupWidget::onUserListChanged() { Group* g = GroupList::findGroup(groupId); if (g) - nusers.setText(tr("%1 users in chat").arg(g->nPeers)); + statusMessageLabel->setText(tr("%1 users in chat").arg(g->nPeers)); else - nusers.setText(tr("0 users in chat")); + statusMessageLabel->setText(tr("0 users in chat")); } void GroupWidget::setAsActiveChatroom() { - isActiveWidget = 1; - - QFont small; - small.setPixelSize(10); - nusers.setFont(small); - QPalette pal; - pal.setColor(QPalette::WindowText,Qt::darkGray); - nusers.setPalette(pal); - QPalette pal2; - pal2.setColor(QPalette::WindowText,Qt::black); - name.setPalette(pal2); - QPalette pal3; - pal3.setColor(QPalette::Background, Qt::white); - this->setPalette(pal3); - avatar.setPixmap(QPixmap(":img/group_dark.png")); + setActive(true); + avatar->setPixmap(QPixmap(":img/group_dark.png"), Qt::transparent); } void GroupWidget::setAsInactiveChatroom() { - isActiveWidget = 0; - - QFont small; - small.setPixelSize(10); - nusers.setFont(small); - QPalette pal; - pal.setColor(QPalette::WindowText,Qt::gray); - nusers.setPalette(pal); - QPalette pal2; - pal2.setColor(QPalette::WindowText,Qt::white); - name.setPalette(pal2); - QPalette pal3; - pal3.setColor(QPalette::Background, QColor(65,65,65,255)); - this->setPalette(pal3); - avatar.setPixmap(QPixmap(":img/group.png")); + setActive(false); + avatar->setPixmap(QPixmap(":img/group.png"), Qt::transparent); } void GroupWidget::updateStatusLight() { Group *g = GroupList::findGroup(groupId); - if (Settings::getInstance().getUseNativeDecoration()) + if (g->hasNewMessages == 0) { - if (g->hasNewMessages == 0) - { - statusPic.setPixmap(QPixmap(":img/status/dot_online.png")); - } else { - if (g->userWasMentioned == 0) statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); - else statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); - } - } else { - if (g->hasNewMessages == 0) - { - statusPic.setPixmap(QPixmap(":img/status/dot_groupchat.png")); - } else { - if (g->userWasMentioned == 0) statusPic.setPixmap(QPixmap(":img/status/dot_groupchat_newmessages.png")); - else statusPic.setPixmap(QPixmap(":img/status/dot_groupchat_notification.png")); - } + statusPic.setPixmap(QPixmap(":img/status/dot_online.png")); + } + else + { + if (g->userWasMentioned == 0) + statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); + else + statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); } } diff --git a/widget/groupwidget.h b/widget/groupwidget.h index 54d1ee533..2d35000a0 100644 --- a/widget/groupwidget.h +++ b/widget/groupwidget.h @@ -39,7 +39,6 @@ signals: public: int groupId; - QLabel avatar, name, nusers, statusPic; }; #endif // GROUPWIDGET_H diff --git a/widget/maskablepixmapwidget.cpp b/widget/maskablepixmapwidget.cpp new file mode 100644 index 000000000..f0015d6d0 --- /dev/null +++ b/widget/maskablepixmapwidget.cpp @@ -0,0 +1,135 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "maskablepixmapwidget.h" +#include + +MaskablePixmapWidget::MaskablePixmapWidget(QWidget *parent, QSize size, QString maskName) + : QWidget(parent) + , renderTarget(size) + , backgroundColor(Qt::white) + , clickable(false) +{ + setFixedSize(size); + + QPixmap pmapMask = QPixmap(maskName); + + if (!pmapMask.isNull()) + mask = QPixmap(maskName).scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + +void MaskablePixmapWidget::autopickBackground() +{ + QImage pic = pixmap.toImage(); + + if (pic.isNull()) + return; + + int r = 0; + int g = 0; + int b = 0; + int weight = 0; + + for (int x=0;xclickable = clickable; + + if (clickable) + setCursor(Qt::PointingHandCursor); + else + unsetCursor(); +} + +void MaskablePixmapWidget::setPixmap(const QPixmap &pmap, QColor background) +{ + if (!pmap.isNull()) + { + pixmap = pmap.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + backgroundColor = background; + + update(); + } +} + +void MaskablePixmapWidget::setPixmap(const QPixmap &pmap) +{ + if (!pmap.isNull()) + { + pixmap = pmap.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + autopickBackground(); + + update(); + } +} + +QPixmap MaskablePixmapWidget::getPixmap() const +{ + return renderTarget; +} + +void MaskablePixmapWidget::paintEvent(QPaintEvent *) +{ + renderTarget.fill(Qt::transparent); + + QPoint offset((width() - pixmap.size().width())/2,(height() - pixmap.size().height())/2); // centering the pixmap + + QPainter painter(&renderTarget); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.fillRect(0,0,width(),height(),backgroundColor); + painter.drawPixmap(offset,pixmap); + painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + painter.drawPixmap(0,0,mask); + painter.end(); + + painter.begin(this); + painter.drawPixmap(0,0,renderTarget); +} + +void MaskablePixmapWidget::mousePressEvent(QMouseEvent*) +{ + if(clickable) + emit clicked(); +} diff --git a/widget/maskablepixmapwidget.h b/widget/maskablepixmapwidget.h new file mode 100644 index 000000000..86e690aae --- /dev/null +++ b/widget/maskablepixmapwidget.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 MASKABLEPIXMAPWIDGET_H +#define MASKABLEPIXMAPWIDGET_H + +#include + +class MaskablePixmapWidget : public QWidget +{ + Q_OBJECT +public: + MaskablePixmapWidget(QWidget *parent, QSize size, QString maskName = QString()); + + void autopickBackground(); + void setBackground(QColor color); + void setClickable(bool clickable); + void setPixmap(const QPixmap &pmap, QColor background); + void setPixmap(const QPixmap &pmap); + QPixmap getPixmap() const; + +signals: + void clicked(); + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void mousePressEvent(QMouseEvent *); + +private: + QPixmap pixmap; + QPixmap mask; + QPixmap renderTarget; + QSize size; + QString maskName; + QColor backgroundColor; + bool clickable; +}; + +#endif // MASKABLEPIXMAPWIDGET_H diff --git a/widget/netcamview.cpp b/widget/netcamview.cpp index 4268ea8dd..fc94393d5 100644 --- a/widget/netcamview.cpp +++ b/widget/netcamview.cpp @@ -59,7 +59,7 @@ NetCamView::NetCamView(QWidget* parent) setWindowTitle("Tox video"); setMinimumSize(320,240); - displayLabel->setScaledContents(true); + displayLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(displayLabel); } @@ -73,10 +73,10 @@ void NetCamView::updateDisplay(vpx_image* frame) core->increaseVideoBusyness(); - QImage img = convert(*frame); + img = convert(*frame); vpx_img_free(frame); - displayLabel->setPixmap(QPixmap::fromImage(img)); + displayLabel->setPixmap(QPixmap::fromImage(img).scaled(displayLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); core->decreaseVideoBusyness(); } @@ -108,3 +108,9 @@ QImage NetCamView::convert(vpx_image& frame) return img; } + +void NetCamView::resizeEvent(QResizeEvent *e) +{ + Q_UNUSED(e) + displayLabel->setPixmap(QPixmap::fromImage(img).scaled(displayLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); +} diff --git a/widget/netcamview.h b/widget/netcamview.h index 0aba0edd9..a8698eaa3 100644 --- a/widget/netcamview.h +++ b/widget/netcamview.h @@ -24,6 +24,7 @@ class QShowEvent; class QPainter; class QLabel; class QHBoxLayout; +class QImage; class vpx_image; class NetCamView : public QWidget @@ -39,10 +40,14 @@ public slots: private: static QImage convert(vpx_image& frame); +protected: + void resizeEvent(QResizeEvent *e); + private: QLabel *displayLabel; QImage lastFrame; QHBoxLayout* mainLayout; + QImage img; }; #endif // NETCAMVIEW_H diff --git a/widget/selfcamview.cpp b/widget/selfcamview.cpp index 6436e24d9..7e9c3d252 100644 --- a/widget/selfcamview.cpp +++ b/widget/selfcamview.cpp @@ -36,7 +36,7 @@ SelfCamView::SelfCamView(Camera* Cam, QWidget* parent) updateDisplayTimer->setInterval(5); updateDisplayTimer->setSingleShot(false); - displayLabel->setScaledContents(true); + displayLabel->setAlignment(Qt::AlignCenter); mainLayout->addWidget(displayLabel); @@ -59,6 +59,11 @@ void SelfCamView::showEvent(QShowEvent* event) void SelfCamView::updateDisplay() { - displayLabel->setPixmap(QPixmap::fromImage(cam->getLastImage())); + displayLabel->setPixmap(QPixmap::fromImage(cam->getLastImage()).scaled(displayLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } +void SelfCamView::resizeEvent(QResizeEvent *e) +{ + Q_UNUSED(e) + updateDisplay(); +} diff --git a/widget/selfcamview.h b/widget/selfcamview.h index a8cc29679..26a8b315c 100644 --- a/widget/selfcamview.h +++ b/widget/selfcamview.h @@ -42,6 +42,9 @@ private: void showEvent(QShowEvent*); void paint(QPainter *painter); +protected: + void resizeEvent(QResizeEvent *e); + private: QLabel *displayLabel; QHBoxLayout* mainLayout; diff --git a/widget/tool/chataction.cpp b/widget/tool/chataction.cpp deleted file mode 100644 index 0847abdda..000000000 --- a/widget/tool/chataction.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - Copyright (C) 2014 by Project Tox - - This file is part of qTox, a Qt-based graphical interface for Tox. - - This program is libre 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 "chataction.h" -#include "smileypack.h" -#include -#include -#include "filetransferinstance.h" - -QString ChatAction::toHtmlChars(const QString &str) -{ - static QList> replaceList = {{"&","&"}, {">",">"}, {"<","<"}}; - QString res = str; - - for (auto &it : replaceList) - res = res.replace(it.first,it.second); - - return res; -} - -QString ChatAction::QImage2base64(const QImage &img) -{ - QByteArray ba; - QBuffer buffer(&ba); - buffer.open(QIODevice::WriteOnly); - img.save(&buffer, "PNG"); // writes image into ba in PNG format - return ba.toBase64(); -} - -QString ChatAction::getName() -{ - if (isMe) - return QString("
" + toHtmlChars(name) + "
"); - else - return QString("
" + toHtmlChars(name) + "
"); -} - -QString ChatAction::getDate() -{ - QString res = "
" + date + "
"; - return res; -} - -MessageAction::MessageAction(const QString &author, const QString &message, const QString &date, const bool &me) : - ChatAction(me, author, date), - message(message) -{ -} - -void MessageAction::setTextCursor(QTextCursor cursor) -{ - // When this function is called, we're supposed to only update ourselve when needed - // Nobody should ask us to do anything with our content, we're on our own - // Except we never udpate on our own, so we can safely free our resources - - (void) cursor; - message.clear(); - message.squeeze(); - name.clear(); - name.squeeze(); - date.clear(); - date.squeeze(); -} - -QString MessageAction::getMessage() -{ - QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message)); - - // detect urls - QRegExp exp("(www\\.|http[s]?:\\/\\/|ftp:\\/\\/)\\S+"); - int offset = 0; - while ((offset = exp.indexIn(message_, offset)) != -1) - { - QString url = exp.cap(); - - // add scheme if not specified - if (exp.cap(1) == "www.") - url.prepend("http://"); - - QString htmledUrl = QString("%1").arg(url); - message_.replace(offset, exp.cap().length(), htmledUrl); - - offset += htmledUrl.length(); - } - - // detect text quotes - QStringList messageLines = message_.split("\n"); - message_ = ""; - for (QString& s : messageLines) - { - if (QRegExp("^[ ]*>.*").exactMatch(s)) - message_ += "
" + s.right(s.length()-4) + "

"; - else - message_ += s + "
"; - } - message_ = message_.left(message_.length()-4); - - return QString("
" + message_ + "
"); -} - -FileTransferAction::FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me) : - ChatAction(me, author, date) -{ - w = widget; - - connect(w, &FileTransferInstance::stateUpdated, this, &FileTransferAction::updateHtml); -} - -FileTransferAction::~FileTransferAction() -{ -} - -QString FileTransferAction::getMessage() -{ - QString widgetHtml; - if (w != nullptr) - widgetHtml = w->getHtmlImage(); - else - widgetHtml = "
EMPTY CONTENT
"; - return widgetHtml; -} - -void FileTransferAction::setTextCursor(QTextCursor cursor) -{ - cur = cursor; - cur.setKeepPositionOnInsert(true); - int end=cur.selectionEnd(); - cur.setPosition(cur.position()); - cur.setPosition(end, QTextCursor::KeepAnchor); -} - -void FileTransferAction::updateHtml() -{ - if (cur.isNull()) - return; - - int pos = cur.selectionStart(); - cur.removeSelectedText(); - cur.setKeepPositionOnInsert(false); - cur.insertHtml(getMessage()); - cur.setKeepPositionOnInsert(true); - int end = cur.position(); - cur.setPosition(pos); - cur.setPosition(end, QTextCursor::KeepAnchor); - - // Free our ressources if we'll never need to update again - if (w->getState() == FileTransferInstance::TransfState::tsCanceled - || w->getState() == FileTransferInstance::TransfState::tsFinished) - { - name.clear(); - name.squeeze(); - date.clear(); - date.squeeze(); - cur = QTextCursor(); - } -} diff --git a/widget/tool/chatactions/actionaction.cpp b/widget/tool/chatactions/actionaction.cpp new file mode 100644 index 000000000..8533b7cd8 --- /dev/null +++ b/widget/tool/chatactions/actionaction.cpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "actionaction.h" +#include "misc/smileypack.h" + +ActionAction::ActionAction(const QString &author, const QString &message, const QString &date, const bool& me) : + ChatAction(me, author, date), + message(message) +{ +} + +void ActionAction::setup(QTextCursor cursor, QTextEdit *) +{ + // When this function is called, we're supposed to only update ourselve when needed + // Nobody should ask us to do anything with our content, we're on our own + // Except we never udpate on our own, so we can safely free our resources + + (void) cursor; + message.clear(); + message.squeeze(); + name.clear(); + name.squeeze(); + date.clear(); + date.squeeze(); +} + +QString ActionAction::getName() +{ + return QString("
*
"); +} + +QString ActionAction::getMessage() +{ + QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message)); + + // detect urls + QRegExp exp("(www\\.|http[s]?:\\/\\/|ftp:\\/\\/)\\S+"); + int offset = 0; + while ((offset = exp.indexIn(message_, offset)) != -1) + { + QString url = exp.cap(); + + // add scheme if not specified + if (exp.cap(1) == "www.") + url.prepend("http://"); + + QString htmledUrl = QString("%1").arg(url); + message_.replace(offset, exp.cap().length(), htmledUrl); + + offset += htmledUrl.length(); + } + + return QString("
%1 %2
").arg(name).arg(message_); +} diff --git a/widget/tool/chatactions/actionaction.h b/widget/tool/chatactions/actionaction.h new file mode 100644 index 000000000..ec4e5f4a8 --- /dev/null +++ b/widget/tool/chatactions/actionaction.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 ACTIONACTION_H +#define ACTIONACTION_H + +#include "widget/tool/chatactions/chataction.h" + +class ActionAction : public ChatAction +{ +public: + ActionAction(const QString &author, const QString &message, const QString& date, const bool&); + virtual ~ActionAction(){;} + virtual QString getMessage(); + virtual QString getName(); + virtual void setup(QTextCursor cursor, QTextEdit*) override; + +private: + QString message; +}; + +#endif // MESSAGEACTION_H diff --git a/widget/tool/chatactions/chataction.cpp b/widget/tool/chatactions/chataction.cpp new file mode 100644 index 000000000..6978711a6 --- /dev/null +++ b/widget/tool/chatactions/chataction.cpp @@ -0,0 +1,55 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "chataction.h" +#include +#include + +QString ChatAction::toHtmlChars(const QString &str) +{ + static QList> replaceList = {{"&","&"}, {">",">"}, {"<","<"}}; + QString res = str; + + for (auto &it : replaceList) + res = res.replace(it.first,it.second); + + return res; +} + +QString ChatAction::QImage2base64(const QImage &img) +{ + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + img.save(&buffer, "PNG"); // writes image into ba in PNG format + return ba.toBase64(); +} + +QString ChatAction::getName() +{ + if (isMe) + return QString("
%2
").arg("name_me").arg(toHtmlChars(name)); + else + return QString("
%2
").arg("name").arg(toHtmlChars(name)); +} + +QString ChatAction::getDate() +{ + if (isMe) + return QString("
" + toHtmlChars(date) + "
"); + else + return QString("
" + toHtmlChars(date) + "
"); +} diff --git a/widget/tool/chataction.h b/widget/tool/chatactions/chataction.h similarity index 58% rename from widget/tool/chataction.h rename to widget/tool/chatactions/chataction.h index a324a41c6..5c3eed650 100644 --- a/widget/tool/chataction.h +++ b/widget/tool/chatactions/chataction.h @@ -21,13 +21,14 @@ #include class FileTransferInstance; +class QTextEdit; class ChatAction : public QObject { public: ChatAction(const bool &me, const QString &author, const QString &date) : isMe(me), name(author), date(date) {;} virtual ~ChatAction(){;} - virtual void setTextCursor(QTextCursor cursor){(void)cursor;} ///< Call once, and then you MUST let the object update itself + virtual void setup(QTextCursor cursor, QTextEdit* textEdit) = 0; ///< Call once, and then you MUST let the object update itself virtual QString getName(); virtual QString getMessage() = 0; @@ -42,33 +43,4 @@ protected: QString name, date; }; -class MessageAction : public ChatAction -{ -public: - MessageAction(const QString &author, const QString &message, const QString &date, const bool &me); - virtual ~MessageAction(){;} - virtual QString getMessage(); - virtual void setTextCursor(QTextCursor cursor) final; - -private: - QString message; -}; - -class FileTransferAction : public ChatAction -{ - Q_OBJECT -public: - FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me); - virtual ~FileTransferAction(); - virtual QString getMessage(); - virtual void setTextCursor(QTextCursor cursor) final; - -private slots: - void updateHtml(); - -private: - FileTransferInstance *w; - QTextCursor cur; -}; - #endif // CHATACTION_H diff --git a/widget/tool/chatactions/filetransferaction.cpp b/widget/tool/chatactions/filetransferaction.cpp new file mode 100644 index 000000000..59f2c9250 --- /dev/null +++ b/widget/tool/chatactions/filetransferaction.cpp @@ -0,0 +1,88 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "filetransferaction.h" +#include "filetransferinstance.h" + +#include +#include + +FileTransferAction::FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me) + : ChatAction(me, author, date) + , edit(nullptr) +{ + w = widget; + + connect(w, &FileTransferInstance::stateUpdated, this, &FileTransferAction::updateHtml); +} + +FileTransferAction::~FileTransferAction() +{ +} + +QString FileTransferAction::getMessage() +{ + QString widgetHtml; + if (w != nullptr) + widgetHtml = w->getHtmlImage(); + else + widgetHtml = "
EMPTY CONTENT
"; + return widgetHtml; +} + +void FileTransferAction::setup(QTextCursor cursor, QTextEdit *textEdit) +{ + cur = cursor; + cur.setKeepPositionOnInsert(true); + int end=cur.selectionEnd(); + cur.setPosition(cur.position()); + cur.setPosition(end, QTextCursor::KeepAnchor); + + edit = textEdit; +} + +void FileTransferAction::updateHtml() +{ + if (cur.isNull() || !edit) + return; + + // save old slider value + int vSliderVal = edit->verticalScrollBar()->value(); + + // update content + int pos = cur.selectionStart(); + cur.removeSelectedText(); + cur.setKeepPositionOnInsert(false); + cur.insertHtml(getMessage()); + cur.setKeepPositionOnInsert(true); + int end = cur.position(); + cur.setPosition(pos); + cur.setPosition(end, QTextCursor::KeepAnchor); + + // restore old slider value + edit->verticalScrollBar()->setValue(vSliderVal); + + // Free our ressources if we'll never need to update again + if (w->getState() == FileTransferInstance::TransfState::tsCanceled + || w->getState() == FileTransferInstance::TransfState::tsFinished) + { + name.clear(); + name.squeeze(); + date.clear(); + date.squeeze(); + cur = QTextCursor(); + } +} diff --git a/widget/tool/chatactions/filetransferaction.h b/widget/tool/chatactions/filetransferaction.h new file mode 100644 index 000000000..31982cd45 --- /dev/null +++ b/widget/tool/chatactions/filetransferaction.h @@ -0,0 +1,40 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 FILETRANSFERACTION_H +#define FILETRANSFERACTION_H + +#include "widget/tool/chatactions/chataction.h" + +class FileTransferAction : public ChatAction +{ + Q_OBJECT +public: + FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me); + virtual ~FileTransferAction(); + virtual QString getMessage(); + virtual void setup(QTextCursor cursor, QTextEdit* textEdit) override; + +private slots: + void updateHtml(); + +private: + FileTransferInstance *w; + QTextCursor cur; + QTextEdit* edit; +}; + +#endif // FILETRANSFERACTION_H diff --git a/widget/tool/chatactions/messageaction.cpp b/widget/tool/chatactions/messageaction.cpp new file mode 100644 index 000000000..6445279ea --- /dev/null +++ b/widget/tool/chatactions/messageaction.cpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "messageaction.h" +#include "misc/smileypack.h" + +MessageAction::MessageAction(const QString &author, const QString &message, const QString &date, const bool &me) : + ChatAction(me, author, date), + message(message) +{ +} + +void MessageAction::setup(QTextCursor cursor, QTextEdit *) +{ + // When this function is called, we're supposed to only update ourselve when needed + // Nobody should ask us to do anything with our content, we're on our own + // Except we never udpate on our own, so we can safely free our resources + + (void) cursor; + message.clear(); + message.squeeze(); + name.clear(); + name.squeeze(); + date.clear(); + date.squeeze(); +} + +QString MessageAction::getMessage() +{ + QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message)); + + // detect urls + QRegExp exp("(www\\.|http[s]?:\\/\\/|ftp:\\/\\/)\\S+"); + int offset = 0; + while ((offset = exp.indexIn(message_, offset)) != -1) + { + QString url = exp.cap(); + + // add scheme if not specified + if (exp.cap(1) == "www.") + url.prepend("http://"); + + QString htmledUrl = QString("%1").arg(url); + message_.replace(offset, exp.cap().length(), htmledUrl); + + offset += htmledUrl.length(); + } + + // detect text quotes + QStringList messageLines = message_.split("\n"); + message_ = ""; + for (QString& s : messageLines) + { + if (QRegExp("^[ ]*>.*").exactMatch(s)) + message_ += ">" + s.right(s.length()-4) + "
"; + else + message_ += s + "
"; + } + message_ = message_.left(message_.length()-4); + + if (isMe) + return QString("
" + message_ + "
"); + else + return QString("
" + message_ + "
"); +} diff --git a/widget/tool/chatactions/messageaction.h b/widget/tool/chatactions/messageaction.h new file mode 100644 index 000000000..65f8e6465 --- /dev/null +++ b/widget/tool/chatactions/messageaction.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 MESSAGEACTION_H +#define MESSAGEACTION_H + +#include "widget/tool/chatactions/chataction.h" + +class MessageAction : public ChatAction +{ +public: + MessageAction(const QString &author, const QString &message, const QString &date, const bool &me); + virtual ~MessageAction(){;} + virtual QString getMessage(); + virtual void setup(QTextCursor cursor, QTextEdit*) override; + +private: + QString message; +}; + +#endif // MESSAGEACTION_H diff --git a/widget/tool/chatactions/systemmessageaction.cpp b/widget/tool/chatactions/systemmessageaction.cpp new file mode 100644 index 000000000..a50f55e47 --- /dev/null +++ b/widget/tool/chatactions/systemmessageaction.cpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 "systemmessageaction.h" + +SystemMessageAction::SystemMessageAction(const QString &message, const QString &type, const QString &date) : + ChatAction(false, QString(), date), + message(message), + type(type) +{ +} + +QString SystemMessageAction::getMessage() +{ + return QString("
" + message + "
"); +} + +void SystemMessageAction::setup(QTextCursor cursor, QTextEdit *) +{ + // When this function is called, we're supposed to only update ourselve when needed + // Nobody should ask us to do anything with our content, we're on our own + // Except we never udpate on our own, so we can safely free our resources + + (void) cursor; + message.clear(); + message.squeeze(); + name.clear(); + name.squeeze(); + date.clear(); + date.squeeze(); + type.clear(); + type.squeeze(); +} diff --git a/widget/tool/chatactions/systemmessageaction.h b/widget/tool/chatactions/systemmessageaction.h new file mode 100644 index 000000000..6767a3f73 --- /dev/null +++ b/widget/tool/chatactions/systemmessageaction.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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 SYSTEMMESSAGEACTION_H +#define SYSTEMMESSAGEACTION_H + +#include "widget/tool/chatactions/chataction.h" + +class SystemMessageAction : public ChatAction +{ +public: + SystemMessageAction(const QString &message, const QString& type, const QString &date); + virtual ~SystemMessageAction(){;} + virtual void setup(QTextCursor cursor, QTextEdit*) override; + + virtual QString getName() {return QString();} + virtual QString getMessage(); + +private: + QString message; + QString type; +}; + +#endif // SYSTEMMESSAGEACTION_H diff --git a/widget/widget.cpp b/widget/widget.cpp index f2c20cb23..f610e8187 100644 --- a/widget/widget.cpp +++ b/widget/widget.cpp @@ -17,7 +17,7 @@ #include "widget.h" #include "ui_mainwindow.h" #include "core.h" -#include "settings.h" +#include "misc/settings.h" #include "friend.h" #include "friendlist.h" #include "widget/tool/friendrequestdialog.h" @@ -26,19 +26,22 @@ #include "group.h" #include "widget/groupwidget.h" #include "widget/form/groupchatform.h" -#include "style.h" +#include "misc/style.h" #include "selfcamview.h" #include "widget/friendlistwidget.h" #include "camera.h" #include "widget/form/chatform.h" +#include "widget/maskablepixmapwidget.h" #include #include #include #include +#include #include #include #include #include +#include #include Widget *Widget::instance{nullptr}; @@ -58,60 +61,21 @@ Widget::Widget(QWidget *parent) restoreState(Settings::getInstance().getWindowState()); ui->mainSplitter->restoreState(Settings::getInstance().getSplitterState()); - if (Settings::getInstance().getUseNativeDecoration()) - { - ui->titleBar->hide(); - this->layout()->setContentsMargins(0, 0, 0, 0); + layout()->setContentsMargins(0, 0, 0, 0); + ui->friendList->setStyleSheet(Style::getStylesheet(":ui/friendList/friendList.css")); - ui->friendList->setObjectName("friendList"); - ui->friendList->setStyleSheet(Style::get(":ui/friendList/friendList.css")); - } - else - { - this->setObjectName("activeWindow"); - this->setStyleSheet(Style::get(":ui/window/window.css")); - ui->statusPanel->setStyleSheet(QString("")); - ui->friendList->setStyleSheet(QString("")); - - ui->friendList->setObjectName("friendList"); - ui->friendList->setStyleSheet(Style::get(":ui/friendList/friendList.css")); - - ui->tbMenu->setIcon(QIcon(":ui/window/applicationIcon.png")); - ui->pbMin->setObjectName("minimizeButton"); - ui->pbMax->setObjectName("maximizeButton"); - ui->pbClose->setObjectName("closeButton"); - - setWindowFlags(Qt::CustomizeWindowHint); - setWindowFlags(Qt::FramelessWindowHint); - - addAction(ui->actionClose); - - connect(ui->pbMin, SIGNAL(clicked()), this, SLOT(minimizeBtnClicked())); - connect(ui->pbMax, SIGNAL(clicked()), this, SLOT(maximizeBtnClicked())); - connect(ui->pbClose, SIGNAL(clicked()), this, SLOT(close())); - - m_titleMode = FullTitle; - moveWidget = false; - inResizeZone = false; - allowToResize = false; - resizeVerSup = false; - resizeHorEsq = false; - resizeDiagSupEsq = false; - resizeDiagSupDer = false; - - if (isMaximized()) - { - showMaximized(); - ui->pbMax->setObjectName("restoreButton"); - } - } - - isWindowMinimized = 0; + profilePicture = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png"); + profilePicture->setPixmap(QPixmap(":/img/contact_dark.png")); + profilePicture->setClickable(true); + ui->horizontalLayout_3->insertWidget(0,profilePicture); + ui->horizontalLayout_3->insertSpacing(1, 7); ui->mainContent->setLayout(new QVBoxLayout()); ui->mainHead->setLayout(new QVBoxLayout()); ui->mainHead->layout()->setMargin(0); ui->mainHead->layout()->setSpacing(0); + ui->mainHead->setStyleSheet(Style::getStylesheet(":ui/settings/mainHead.css")); + ui->mainContent->setStyleSheet(Style::getStylesheet(":ui/settings/mainContent.css")); contactListWidget = new FriendListWidget(); ui->friendList->setWidget(contactListWidget); @@ -120,13 +84,7 @@ Widget::Widget(QWidget *parent) ui->nameLabel->setEditable(true); ui->statusLabel->setEditable(true); - // delay setting username and message until Core inits - //ui->nameLabel->setText(core->getUsername()); - ui->nameLabel->setStyleSheet("QLabel { color : white; font-size: 11pt; font-weight:bold;}"); - //ui->statusLabel->setText(core->getStatusMessage()); - ui->statusLabel->setStyleSheet("QLabel { color : white; font-size: 8pt;}"); - - ui->statusButton->setStyleSheet(Style::get(":/ui/statusButton/statusButton.css")); + ui->statusPanel->setStyleSheet(Style::getStylesheet(":/ui/window/statusPanel.css")); QMenu *statusButtonMenu = new QMenu(ui->statusButton); QAction* setStatusOnline = statusButtonMenu->addAction(Widget::tr("Online","Button to set your status to 'Online'")); @@ -137,25 +95,15 @@ Widget::Widget(QWidget *parent) setStatusBusy->setIcon(QIcon(":ui/statusButton/dot_busy.png")); ui->statusButton->setMenu(statusButtonMenu); - ui->titleBar->setMouseTracking(true); - ui->LTitle->setMouseTracking(true); - ui->tbMenu->setMouseTracking(true); - ui->pbMin->setMouseTracking(true); - ui->pbMax->setMouseTracking(true); - ui->pbClose->setMouseTracking(true); - ui->statusHead->setMouseTracking(true); - - //ui->friendList->viewport()->installEventFilter(this); - // disable proportional scaling ui->mainSplitter->setStretchFactor(0,0); ui->mainSplitter->setStretchFactor(1,1); - ui->statusButton->setObjectName("offline"); - ui->statusButton->style()->polish(ui->statusButton); + ui->statusButton->setProperty("status", "offline"); + Style::repolish(ui->statusButton); camera = new Camera; - camview = new SelfCamView(camera); + settingsWidget = new SettingsWidget(camera); // Disable some widgets until we're connected to the DHT ui->statusButton->setEnabled(false); @@ -163,8 +111,10 @@ Widget::Widget(QWidget *parent) qRegisterMetaType("Status"); qRegisterMetaType("vpx_image"); qRegisterMetaType("uint8_t"); + qRegisterMetaType("uint16_t"); qRegisterMetaType("int32_t"); qRegisterMetaType("int64_t"); + qRegisterMetaType("QPixmap"); qRegisterMetaType("ToxFile"); qRegisterMetaType("ToxFile::FileDirection"); @@ -177,10 +127,11 @@ Widget::Widget(QWidget *parent) connect(core, &Core::connected, this, &Widget::onConnected); connect(core, &Core::disconnected, this, &Widget::onDisconnected); connect(core, &Core::failedToStart, this, &Widget::onFailedToStartCore); + connect(core, &Core::badProxy, this, &Widget::onBadProxyCore); connect(core, &Core::statusSet, this, &Widget::onStatusSet); connect(core, &Core::usernameSet, this, &Widget::setUsername); connect(core, &Core::statusMessageSet, this, &Widget::setStatusMessage); - connect(core, &Core::friendAddressGenerated, &settingsForm, &SettingsForm::setFriendAddress); + connect(core, &Core::selfAvatarChanged, this, &Widget::onSelfAvatarLoaded); connect(core, SIGNAL(fileDownloadFinished(const QString&)), &filesForm, SLOT(onFileDownloadComplete(const QString&))); connect(core, SIGNAL(fileUploadFinished(const QString&)), &filesForm, SLOT(onFileUploadComplete(const QString&))); connect(core, &Core::friendAdded, this, &Widget::addFriend); @@ -190,14 +141,16 @@ Widget::Widget(QWidget *parent) connect(core, &Core::friendUsernameChanged, this, &Widget::onFriendUsernameChanged); connect(core, &Core::friendStatusChanged, this, &Widget::onFriendStatusChanged); connect(core, &Core::friendStatusMessageChanged, this, &Widget::onFriendStatusMessageChanged); - connect(core, &Core::friendUsernameLoaded, this, &Widget::onFriendUsernameLoaded); - connect(core, &Core::friendStatusMessageLoaded, this, &Widget::onFriendStatusMessageLoaded); connect(core, &Core::friendRequestReceived, this, &Widget::onFriendRequestReceived); connect(core, &Core::friendMessageReceived, this, &Widget::onFriendMessageReceived); connect(core, &Core::groupInviteReceived, this, &Widget::onGroupInviteReceived); connect(core, &Core::groupMessageReceived, this, &Widget::onGroupMessageReceived); connect(core, &Core::groupNamelistChanged, this, &Widget::onGroupNamelistChanged); connect(core, &Core::emptyGroupCreated, this, &Widget::onEmptyGroupCreated); + connect(core, &Core::avInvite, this, &Widget::playRingtone); + + connect(core, SIGNAL(messageSentResult(int,QString,int)), this, SLOT(onMessageSendResult(int,QString,int))); + connect(core, SIGNAL(groupSentResult(int,QString,int)), this, SLOT(onGroupSendResult(int,QString,int))); connect(this, &Widget::statusSet, core, &Core::setStatus); connect(this, &Widget::friendRequested, core, &Core::requestFriendship); @@ -209,7 +162,10 @@ Widget::Widget(QWidget *parent) connect(ui->settingsButton, SIGNAL(clicked()), this, SLOT(onSettingsClicked())); connect(ui->nameLabel, SIGNAL(textChanged(QString,QString)), this, SLOT(onUsernameChanged(QString,QString))); connect(ui->statusLabel, SIGNAL(textChanged(QString,QString)), this, SLOT(onStatusMessageChanged(QString,QString))); + connect(profilePicture, SIGNAL(clicked()), this, SLOT(onAvatarClicked())); connect(setStatusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline())); +// connect(settingsWidget->getIdentityForm(), &IdentityForm::userNameChanged, Core::getInstance(), &Core::setUsername); +// connect(settingsWidget->getIdentityForm(), &IdentityForm::statusMessageChanged, Core::getInstance(), &Core::setStatusMessage); connect(setStatusAway, SIGNAL(triggered()), this, SLOT(setStatusAway())); connect(setStatusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy())); connect(&friendForm, SIGNAL(friendRequested(QString,QString)), this, SIGNAL(friendRequested(QString,QString))); @@ -222,15 +178,12 @@ Widget::Widget(QWidget *parent) Widget::~Widget() { core->saveConfiguration(); - instance = nullptr; coreThread->exit(); coreThread->wait(500); // In case of deadlock (can happen with QtAudio/PA bugs) if (!coreThread->isFinished()) coreThread->terminate(); delete core; - delete camview; - - hideMainForms(); + delete settingsWidget; for (Friend* f : FriendList::friendList) delete f; @@ -239,6 +192,7 @@ Widget::~Widget() delete g; GroupList::groupList.clear(); delete ui; + instance = nullptr; } Widget* Widget::getInstance() @@ -263,7 +217,7 @@ void Widget::closeEvent(QCloseEvent *event) QString Widget::getUsername() { - return ui->nameLabel->text(); + return core->getUsername(); } Camera* Widget::getCamera() @@ -271,6 +225,55 @@ Camera* Widget::getCamera() return camera; } +void Widget::onAvatarClicked() +{ + QString filename = QFileDialog::getOpenFileName(this, tr("Choose a profile picture"), QDir::homePath()); + if (filename == "") + return; + QFile file(filename); + file.open(QIODevice::ReadOnly); + if (!file.isOpen()) + { + QMessageBox::critical(this, tr("Error"), tr("Unable to open this file")); + return; + } + + QPixmap pic; + if (!pic.loadFromData(file.readAll())) + { + QMessageBox::critical(this, tr("Error"), tr("Unable to read this image")); + return; + } + + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::WriteOnly); + pic.save(&buffer, "PNG"); + buffer.close(); + + if (bytes.size() >= TOX_AVATAR_MAX_DATA_LENGTH) + { + pic = pic.scaled(64,64, Qt::KeepAspectRatio, Qt::SmoothTransformation); + bytes.clear(); + buffer.open(QIODevice::WriteOnly); + pic.save(&buffer, "PNG"); + buffer.close(); + } + + if (bytes.size() >= TOX_AVATAR_MAX_DATA_LENGTH) + { + QMessageBox::critical(this, tr("Error"), tr("This image is too big")); + return; + } + + core->setAvatar(TOX_AVATAR_FORMAT_PNG, bytes); +} + +void Widget::onSelfAvatarLoaded(const QPixmap& pic) +{ + profilePicture->setPixmap(pic); +} + void Widget::onConnected() { ui->statusButton->setEnabled(true); @@ -286,36 +289,43 @@ void Widget::onDisconnected() void Widget::onFailedToStartCore() { QMessageBox critical(this); - critical.setText("Toxcore failed to start, the application will terminate after you close this message."); + critical.setText(tr("Toxcore failed to start, the application will terminate after you close this message.")); critical.setIcon(QMessageBox::Critical); critical.exec(); qApp->quit(); } +void Widget::onBadProxyCore() +{ + QMessageBox critical(this); + critical.setText(tr("toxcore failed to start with your proxy settings. qTox cannot run; please modify your " + "settings and restart.", "popup text")); + critical.setIcon(QMessageBox::Critical); + critical.exec(); + onSettingsClicked(); +} + void Widget::onStatusSet(Status status) { //We have to use stylesheets here, there's no way to //prevent the button icon from moving when pressed otherwise - if (status == Status::Online) + switch (status) { - ui->statusButton->setObjectName("online"); - ui->statusButton->style()->polish(ui->statusButton); - } - else if (status == Status::Away) - { - ui->statusButton->setObjectName("away"); - ui->statusButton->style()->polish(ui->statusButton); - } - else if (status == Status::Busy) - { - ui->statusButton->setObjectName("busy"); - ui->statusButton->style()->polish(ui->statusButton); - } - else if (status == Status::Offline) - { - ui->statusButton->setObjectName("offline"); - ui->statusButton->style()->polish(ui->statusButton); + case Status::Online: + ui->statusButton->setProperty("status" ,"online"); + break; + case Status::Away: + ui->statusButton->setProperty("status" ,"away"); + break; + case Status::Busy: + ui->statusButton->setProperty("status" ,"busy"); + break; + case Status::Offline: + ui->statusButton->setProperty("status" ,"offline"); + break; } + + Style::repolish(ui->statusButton); } void Widget::onAddClicked() @@ -339,7 +349,7 @@ void Widget::onTransferClicked() void Widget::onSettingsClicked() { hideMainForms(); - settingsForm.show(*ui); + settingsWidget->show(*ui); activeChatroomWidget = nullptr; } @@ -350,7 +360,7 @@ void Widget::hideMainForms() item->widget()->hide(); while ((item = ui->mainContent->layout()->takeAt(0)) != 0) item->widget()->hide(); - + if (activeChatroomWidget != nullptr) { activeChatroomWidget->setAsInactiveChatroom(); @@ -368,6 +378,7 @@ void Widget::setUsername(const QString& username) { ui->nameLabel->setText(username); ui->nameLabel->setToolTip(username); // for overlength names + settingsWidget->getIdentityForm()->setUserName(username); } void Widget::onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage) @@ -381,12 +392,12 @@ void Widget::setStatusMessage(const QString &statusMessage) { ui->statusLabel->setText(statusMessage); ui->statusLabel->setToolTip(statusMessage); // for overlength messsages + settingsWidget->getIdentityForm()->setStatusMessage(statusMessage); } void Widget::addFriend(int friendId, const QString &userId) { - - qDebug() << "Widget: Adding friend with id "+userId; + qDebug() << "Widget: Adding friend with id" << userId; Friend* newfriend = FriendList::addFriend(friendId, userId); QLayout* layout = contactListWidget->getFriendLayout(Status::Offline); layout->addWidget(newfriend->widget); @@ -395,6 +406,7 @@ void Widget::addFriend(int friendId, const QString &userId) connect(newfriend->widget, SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int))); connect(newfriend->widget, SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newfriend->chatForm, SLOT(focusInput())); connect(newfriend->chatForm, SIGNAL(sendMessage(int,QString)), core, SLOT(sendMessage(int,QString))); + connect(newfriend->chatForm, &GenericChatForm::sendAction, core, &Core::sendAction); connect(newfriend->chatForm, SIGNAL(sendFile(int32_t, QString, QString, long long)), core, SLOT(sendFile(int32_t, QString, QString, long long))); connect(newfriend->chatForm, SIGNAL(answerCall(int)), core, SLOT(answerCall(int))); connect(newfriend->chatForm, SIGNAL(hangupCall(int)), core, SLOT(hangupCall(int))); @@ -413,6 +425,19 @@ void Widget::addFriend(int friendId, const QString &userId) connect(core, &Core::avRequestTimeout, newfriend->chatForm, &ChatForm::onAvRequestTimeout); connect(core, &Core::avPeerTimeout, newfriend->chatForm, &ChatForm::onAvPeerTimeout); connect(core, &Core::avMediaChange, newfriend->chatForm, &ChatForm::onAvMediaChange); + connect(core, &Core::friendAvatarChanged, newfriend->chatForm, &ChatForm::onAvatarChange); + connect(core, &Core::friendAvatarChanged, newfriend->widget, &FriendWidget::onAvatarChange); + connect(core, &Core::friendAvatarRemoved, newfriend->chatForm, &ChatForm::onAvatarRemoved); + connect(core, &Core::friendAvatarRemoved, newfriend->widget, &FriendWidget::onAvatarRemoved); + + // Try to get the avatar from the cache + QPixmap avatar = Settings::getInstance().getSavedAvatar(userId); + if (!avatar.isNull()) + { + qWarning() << "Widget: loadded avatar for id" << userId; + newfriend->chatForm->onAvatarChange(friendId, avatar); + newfriend->widget->onAvatarChange(friendId, avatar); + } } void Widget::addFriendFailed(const QString&) @@ -430,10 +455,6 @@ void Widget::onFriendStatusChanged(int friendId, Status status) f->friendStatus = status; f->widget->updateStatusLight(); - - // Workaround widget style after returning to list - if (f->widget->isActive()) - f->widget->setAsActiveChatroom(); } void Widget::onFriendStatusMessageChanged(int friendId, const QString& message) @@ -442,7 +463,9 @@ void Widget::onFriendStatusMessageChanged(int friendId, const QString& message) if (!f) return; - f->setStatusMessage(message); + QString str = message; str.replace('\n', ' '); + str.remove('\r'); str.remove(QChar((char)0)); // null terminator... + f->setStatusMessage(str); } void Widget::onFriendUsernameChanged(int friendId, const QString& username) @@ -451,25 +474,9 @@ void Widget::onFriendUsernameChanged(int friendId, const QString& username) if (!f) return; - f->setName(username); -} - -void Widget::onFriendStatusMessageLoaded(int friendId, const QString& message) -{ - Friend* f = FriendList::findFriend(friendId); - if (!f) - return; - - f->setStatusMessage(message); -} - -void Widget::onFriendUsernameLoaded(int friendId, const QString& username) -{ - Friend* f = FriendList::findFriend(friendId); - if (!f) - return; - - f->setName(username); + QString str = username; str.replace('\n', ' '); + str.remove('\r'); str.remove(QChar((char)0)); // null terminator... + f->setName(str); } void Widget::onChatroomWidgetClicked(GenericChatroomWidget *widget) @@ -486,17 +493,17 @@ void Widget::onChatroomWidgetClicked(GenericChatroomWidget *widget) widget->updateStatusLight(); } -void Widget::onFriendMessageReceived(int friendId, const QString& message) +void Widget::onFriendMessageReceived(int friendId, const QString& message, bool isAction) { Friend* f = FriendList::findFriend(friendId); if (!f) return; - f->chatForm->addMessage(f->getName(), message); + f->chatForm->addMessage(f->getName(), message, isAction); if (activeChatroomWidget != nullptr) { - if ((static_cast(f->widget) != activeChatroomWidget) || isWindowMinimized || !isActiveWindow()) + if ((static_cast(f->widget) != activeChatroomWidget) || isMinimized() || !isActiveWindow()) { f->hasNewEvents = 1; newMessageAlert(); @@ -531,6 +538,26 @@ void Widget::newMessageAlert() alSourcePlay(core->alMainSource); } +void Widget::playRingtone() +{ + QApplication::alert(this); + + static QFile sndFile1(":audio/ToxicIncomingCall.pcm"); // for whatever reason this plays slower/downshifted from what any other program plays the file as... but whatever + static QByteArray sndData1; + if (sndData1.isEmpty()) + { + sndFile1.open(QIODevice::ReadOnly); + sndData1 = sndFile1.readAll(); + sndFile1.close(); + } + + ALuint buffer; + alGenBuffers(1, &buffer); + alBufferData(buffer, AL_FORMAT_MONO16, sndData1.data(), sndData1.size(), 44100); + alSourcei(core->alMainSource, AL_BUFFER, buffer); + alSourcePlay(core->alMainSource); +} + void Widget::onFriendRequestReceived(const QString& userId, const QString& message) { FriendRequestDialog dialog(this, userId, message); @@ -572,9 +599,9 @@ void Widget::copyFriendIdToClipboard(int friendId) } } -void Widget::onGroupInviteReceived(int32_t friendId, const uint8_t* publicKey) +void Widget::onGroupInviteReceived(int32_t friendId, const uint8_t* publicKey,uint16_t length) { - int groupId = core->joinGroupchat(friendId, publicKey); + int groupId = core->joinGroupchat(friendId, publicKey,length); if (groupId == -1) { qWarning() << "Widget::onGroupInviteReceived: Unable to accept invitation"; @@ -582,21 +609,21 @@ void Widget::onGroupInviteReceived(int32_t friendId, const uint8_t* publicKey) } } -void Widget::onGroupMessageReceived(int groupnumber, int friendgroupnumber, const QString& message) +void Widget::onGroupMessageReceived(int groupnumber, const QString& message, const QString& author) { Group* g = GroupList::findGroup(groupnumber); if (!g) return; - g->chatForm->addGroupMessage(message, friendgroupnumber); + g->chatForm->addMessage(author, message); - if ((static_cast(g->widget) != activeChatroomWidget) || isWindowMinimized || !isActiveWindow()) + if ((static_cast(g->widget) != activeChatroomWidget) || isMinimized() || !isActiveWindow()) { g->hasNewMessages = 1; + newMessageAlert(); // sound alert on any message, not just naming user if (message.contains(core->getUsername(), Qt::CaseInsensitive)) { - newMessageAlert(); - g->userWasMentioned = 1; + g->userWasMentioned = 1; // useful for highlighting line or desktop notifications } g->widget->updateStatusLight(); } @@ -665,11 +692,6 @@ Group *Widget::createGroup(int groupId) return newgroup; } -void Widget::showTestCamview() -{ - camview->show(); -} - void Widget::onEmptyGroupCreated(int groupId) { createGroup(groupId); @@ -688,368 +710,18 @@ bool Widget::isFriendWidgetCurActiveWidget(Friend* f) bool Widget::event(QEvent * e) { - - if( e->type() == QEvent::WindowStateChange ) + if (e->type() == QEvent::WindowActivate) { - if(windowState().testFlag(Qt::WindowMinimized) == true) - { - isWindowMinimized = 1; - } - } - else if (e->type() == QEvent::WindowActivate) - { - if (!Settings::getInstance().getUseNativeDecoration()) - { - this->setObjectName("activeWindow"); - this->style()->polish(this); - } - isWindowMinimized = 0; if (activeChatroomWidget != nullptr) { activeChatroomWidget->resetEventFlags(); activeChatroomWidget->updateStatusLight(); } } - else if (e->type() == QEvent::WindowDeactivate && !Settings::getInstance().getUseNativeDecoration()) - { - this->setObjectName("inactiveWindow"); - this->style()->polish(this); - } - else if (e->type() == QEvent::MouseMove && !Settings::getInstance().getUseNativeDecoration()) - { - QMouseEvent *k = (QMouseEvent *)e; - int xMouse = k->pos().x(); - int yMouse = k->pos().y(); - int wWidth = this->geometry().width(); - int wHeight = this->geometry().height(); - - if (moveWidget) - { - inResizeZone = false; - moveWindow(k); - } - else if (allowToResize) - resizeWindow(k); - else if (xMouse >= wWidth - PIXELS_TO_ACT or allowToResize) - { - inResizeZone = true; - - if (yMouse >= wHeight - PIXELS_TO_ACT) - { - setCursor(Qt::SizeFDiagCursor); - resizeWindow(k); - } - else if (yMouse <= PIXELS_TO_ACT) - { - setCursor(Qt::SizeBDiagCursor); - resizeWindow(k); - } - - } - else - { - inResizeZone = false; - setCursor(Qt::ArrowCursor); - } - - e->accept(); - } return QWidget::event(e); } -void Widget::mousePressEvent(QMouseEvent *e) -{ - if (!Settings::getInstance().getUseNativeDecoration()) - { - if (e->button() == Qt::LeftButton) - { - if (inResizeZone) - { - allowToResize = true; - - if (e->pos().y() <= PIXELS_TO_ACT) - { - if (e->pos().x() <= PIXELS_TO_ACT) - resizeDiagSupEsq = true; - else if (e->pos().x() >= geometry().width() - PIXELS_TO_ACT) - resizeDiagSupDer = true; - else - resizeVerSup = true; - } - else if (e->pos().x() <= PIXELS_TO_ACT) - resizeHorEsq = true; - } - else if (e->pos().x() >= PIXELS_TO_ACT and e->pos().x() < ui->titleBar->geometry().width() - and e->pos().y() >= PIXELS_TO_ACT and e->pos().y() < ui->titleBar->geometry().height()) - { - moveWidget = true; - dragPosition = e->globalPos() - frameGeometry().topLeft(); - } - } - - e->accept(); - } -} - -void Widget::mouseReleaseEvent(QMouseEvent *e) -{ - if (!Settings::getInstance().getUseNativeDecoration()) - { - moveWidget = false; - allowToResize = false; - resizeVerSup = false; - resizeHorEsq = false; - resizeDiagSupEsq = false; - resizeDiagSupDer = false; - - e->accept(); - } -} - -void Widget::mouseDoubleClickEvent(QMouseEvent *e) -{ - if (!Settings::getInstance().getUseNativeDecoration()) - { - if (e->pos().x() < ui->tbMenu->geometry().right() and e->pos().y() < ui->tbMenu->geometry().bottom() - and e->pos().x() >= ui->tbMenu->geometry().x() and e->pos().y() >= ui->tbMenu->geometry().y() - and ui->tbMenu->isVisible()) - close(); - else if (e->pos().x() < ui->titleBar->geometry().width() - and e->pos().y() < ui->titleBar->geometry().height() - and m_titleMode != FullScreenMode) - maximizeBtnClicked(); - e->accept(); - } -} - -void Widget::paintEvent (QPaintEvent *) -{ - QStyleOption opt; - opt.init (this); - QPainter p(this); - style()->drawPrimitive (QStyle::PE_Widget, &opt, &p, this); -} - -void Widget::moveWindow(QMouseEvent *e) -{ - if (!Settings::getInstance().getUseNativeDecoration()) - { - if (e->buttons() & Qt::LeftButton) - { - move(e->globalPos() - dragPosition); - e->accept(); - } - } -} - -void Widget::resizeWindow(QMouseEvent *e) -{ - if (!Settings::getInstance().getUseNativeDecoration()) - { - if (allowToResize) - { - int xMouse = e->pos().x(); - int yMouse = e->pos().y(); - int wWidth = geometry().width(); - int wHeight = geometry().height(); - - if (cursor().shape() == Qt::SizeVerCursor) - { - if (resizeVerSup) - { - int newY = geometry().y() + yMouse; - int newHeight = wHeight - yMouse; - - if (newHeight > minimumSizeHint().height()) - { - resize(wWidth, newHeight); - move(geometry().x(), newY); - } - } - else - resize(wWidth, yMouse+1); - } - else if (cursor().shape() == Qt::SizeHorCursor) - { - if (resizeHorEsq) - { - int newX = geometry().x() + xMouse; - int newWidth = wWidth - xMouse; - - if (newWidth > minimumSizeHint().width()) - { - resize(newWidth, wHeight); - move(newX, geometry().y()); - } - } - else - resize(xMouse, wHeight); - } - else if (cursor().shape() == Qt::SizeBDiagCursor) - { - int newX = 0; - int newWidth = 0; - int newY = 0; - int newHeight = 0; - - if (resizeDiagSupDer) - { - newX = geometry().x(); - newWidth = xMouse; - newY = geometry().y() + yMouse; - newHeight = wHeight - yMouse; - } - else - { - newX = geometry().x() + xMouse; - newWidth = wWidth - xMouse; - newY = geometry().y(); - newHeight = yMouse; - } - - if (newWidth >= minimumSizeHint().width() and newHeight >= minimumSizeHint().height()) - { - resize(newWidth, newHeight); - move(newX, newY); - } - else if (newWidth >= minimumSizeHint().width()) - { - resize(newWidth, wHeight); - move(newX, geometry().y()); - } - else if (newHeight >= minimumSizeHint().height()) - { - resize(wWidth, newHeight); - move(geometry().x(), newY); - } - } - else if (cursor().shape() == Qt::SizeFDiagCursor) - { - if (resizeDiagSupEsq) - { - int newX = geometry().x() + xMouse; - int newWidth = wWidth - xMouse; - int newY = geometry().y() + yMouse; - int newHeight = wHeight - yMouse; - - if (newWidth >= minimumSizeHint().width() and newHeight >= minimumSizeHint().height()) - { - resize(newWidth, newHeight); - move(newX, newY); - } - else if (newWidth >= minimumSizeHint().width()) - { - resize(newWidth, wHeight); - move(newX, geometry().y()); - } - else if (newHeight >= minimumSizeHint().height()) - { - resize(wWidth, newHeight); - move(geometry().x(), newY); - } - } - else - resize(xMouse+1, yMouse+1); - } - - e->accept(); - } - } -} - -void Widget::setCentralWidget(QWidget *widget, const QString &widgetName) -{ - connect(widget, SIGNAL(cancelled()), this, SLOT(close())); - - centralLayout->addWidget(widget); - //ui->centralWidget->setLayout(centralLayout); - ui->LTitle->setText(widgetName); -} - -void Widget::setTitlebarMode(const TitleMode &flag) -{ - m_titleMode = flag; - - switch (m_titleMode) - { - case CleanTitle: - ui->tbMenu->setHidden(true); - ui->pbMin->setHidden(true); - ui->pbMax->setHidden(true); - ui->pbClose->setHidden(true); - break; - case OnlyCloseButton: - ui->tbMenu->setHidden(true); - ui->pbMin->setHidden(true); - ui->pbMax->setHidden(true); - break; - case MenuOff: - ui->tbMenu->setHidden(true); - break; - case MaxMinOff: - ui->pbMin->setHidden(true); - ui->pbMax->setHidden(true); - break; - case FullScreenMode: - ui->pbMax->setHidden(true); - showMaximized(); - break; - case MaximizeModeOff: - ui->pbMax->setHidden(true); - break; - case MinimizeModeOff: - ui->pbMin->setHidden(true); - break; - case FullTitle: - ui->tbMenu->setVisible(true); - ui->pbMin->setVisible(true); - ui->pbMax->setVisible(true); - ui->pbClose->setVisible(true); - break; - break; - default: - ui->tbMenu->setVisible(true); - ui->pbMin->setVisible(true); - ui->pbMax->setVisible(true); - ui->pbClose->setVisible(true); - break; - } - ui->LTitle->setVisible(true); -} - -void Widget::setTitlebarMenu(QMenu *menu, const QString &icon) -{ - ui->tbMenu->setMenu(menu); - ui->tbMenu->setIcon(QIcon(icon)); -} - -void Widget::maximizeBtnClicked() -{ - if (isFullScreen() or isMaximized()) - { - ui->pbMax->setIcon(QIcon(":/ui/images/app_max.png")); - setWindowState(windowState() & ~Qt::WindowFullScreen & ~Qt::WindowMaximized); - } - else - { - ui->pbMax->setIcon(QIcon(":/ui/images/app_rest.png")); - setWindowState(windowState() | Qt::WindowFullScreen | Qt::WindowMaximized); - } -} - -void Widget::minimizeBtnClicked() -{ - if (isMinimized()) - { - setWindowState(windowState() & ~Qt::WindowMinimized); - } - else - { - setWindowState(windowState() | Qt::WindowMinimized); - } -} - void Widget::setStatusOnline() { core->setStatus(Status::Online); @@ -1065,17 +737,24 @@ void Widget::setStatusBusy() core->setStatus(Status::Busy); } -bool Widget::eventFilter(QObject *, QEvent *event) +void Widget::onMessageSendResult(int friendId, const QString& message, int messageId) { - if (event->type() == QEvent::Wheel) - { - QWheelEvent * whlEvnt = static_cast< QWheelEvent * >( event ); - whlEvnt->angleDelta().setX(0); - } - return false; + Q_UNUSED(message) + Friend* f = FriendList::findFriend(friendId); + if (!f) + return; + + if (!messageId) + f->chatForm->addSystemInfoMessage("Message failed to send", "red"); } -bool Widget::getIsWindowMinimized() +void Widget::onGroupSendResult(int groupId, const QString& message, int result) { - return static_cast(isWindowMinimized); + Q_UNUSED(message) + Group* g = GroupList::findGroup(groupId); + if (!g) + return; + + if (result == -1) + g->chatForm->addSystemInfoMessage("Message failed to send", "red"); } diff --git a/widget/widget.h b/widget/widget.h index 206ae30fd..115fccab0 100644 --- a/widget/widget.h +++ b/widget/widget.h @@ -19,7 +19,8 @@ #include #include "widget/form/addfriendform.h" -#include "widget/form/settingsform.h" +#include "widget/form/settingswidget.h" +#include "widget/form/settings/identityform.h" #include "widget/form/filesform.h" #include "corestructs.h" @@ -38,6 +39,7 @@ class QMenu; class Core; class Camera; class FriendListWidget; +class MaskablePixmapWidget; class Widget : public QMainWindow { @@ -45,16 +47,12 @@ class Widget : public QMainWindow public: explicit Widget(QWidget *parent = 0); - enum TitleMode { CleanTitle = 0, OnlyCloseButton, MenuOff, MaxMinOff, FullScreenMode, MaximizeModeOff, MinimizeModeOff, FullTitle }; - void setTitlebarMode(const TitleMode &flag); - void setTitlebarMenu(QMenu *menu, const QString &icon = ""); void setCentralWidget(QWidget *widget, const QString &widgetName); QString getUsername(); Core* getCore(); QThread* getCoreThread(); Camera* getCamera(); static Widget* getInstance(); - void showTestCamview(); void newMessageAlert(); bool isFriendWidgetCurActiveWidget(Friend* f); bool getIsWindowMinimized(); @@ -71,8 +69,6 @@ signals: void statusMessageChanged(const QString& statusMessage); private slots: - void maximizeBtnClicked(); - void minimizeBtnClicked(); void onConnected(); void onDisconnected(); void onStatusSet(Status status); @@ -81,10 +77,11 @@ private slots: void onTransferClicked(); void onSettingsClicked(); void onFailedToStartCore(); + void onBadProxyCore(); + void onAvatarClicked(); + void onSelfAvatarLoaded(const QPixmap &pic); void onUsernameChanged(const QString& newUsername, const QString& oldUsername); void onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage); - //void onUsernameChanged(); - //void onStatusMessageChanged(); void setUsername(const QString& username); void setStatusMessage(const QString &statusMessage); void addFriend(int friendId, const QString& userId); @@ -92,14 +89,12 @@ private slots: void onFriendStatusChanged(int friendId, Status status); void onFriendStatusMessageChanged(int friendId, const QString& message); void onFriendUsernameChanged(int friendId, const QString& username); - void onFriendStatusMessageLoaded(int friendId, const QString& message); - void onFriendUsernameLoaded(int friendId, const QString& username); void onChatroomWidgetClicked(GenericChatroomWidget *); - void onFriendMessageReceived(int friendId, const QString& message); + void onFriendMessageReceived(int friendId, const QString& message, bool isAction); void onFriendRequestReceived(const QString& userId, const QString& message); void onEmptyGroupCreated(int groupId); - void onGroupInviteReceived(int32_t friendId, const uint8_t *publicKey); - void onGroupMessageReceived(int groupnumber, int friendgroupnumber, const QString& message); + void onGroupInviteReceived(int32_t friendId, const uint8_t *publicKey,uint16_t length); + void onGroupMessageReceived(int groupnumber, const QString& message, const QString& author); void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change); void removeFriend(int friendId); void clearFriends(); @@ -108,12 +103,13 @@ private slots: void setStatusOnline(); void setStatusAway(); void setStatusBusy(); - -protected slots: - void moveWindow(QMouseEvent *e); + void onMessageSendResult(int friendId, const QString& message, int messageId); + void onGroupSendResult(int groupId, const QString& message, int result); + void playRingtone(); private: void hideMainForms(); + virtual bool event(QEvent * e); Group* createGroup(int groupId); void removeFriend(Friend* f); @@ -121,33 +117,17 @@ private: Ui::MainWindow *ui; QSplitter *centralLayout; QPoint dragPosition; - TitleMode m_titleMode; - bool moveWidget; - bool inResizeZone; - bool allowToResize; - bool resizeVerSup; - bool resizeHorEsq; - bool resizeDiagSupEsq; - bool resizeDiagSupDer; - void mousePressEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void mouseDoubleClickEvent(QMouseEvent *e); - void paintEvent (QPaintEvent *); - void resizeWindow(QMouseEvent *e); - bool event(QEvent *event); - int isWindowMinimized; Core* core; QThread* coreThread; AddFriendForm friendForm; - SettingsForm settingsForm; + SettingsWidget* settingsWidget; FilesForm filesForm; static Widget* instance; GenericChatroomWidget* activeChatroomWidget; FriendListWidget* contactListWidget; - SelfCamView* camview; Camera* camera; + MaskablePixmapWidget* profilePicture; bool notify(QObject *receiver, QEvent *event); - bool eventFilter(QObject *, QEvent *event); }; #endif // WIDGET_H