diff --git a/INSTALL.md b/INSTALL.md index b03f17c7e..40d3fa0d2 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -230,45 +230,18 @@ The following steps assume that MinGW is installed at "C:\MinGW". If you decided ###Setting up Path -Add MinGW/MSYS binaries to the system path to make them globally accessible. +Add MinGW/MSYS/CMake binaries to the system path to make them globally accessible. Open Control Panel -> System and Security -> System -> Advanced system settings -> Environment Variables... In the second box search for the PATH variable and press Edit... The input box "Variable value:" should already contain some directories. Each directory is separated with a semicolon. -Extend the input box by adding ";C:\MinGW\bin;C:\MinGW\msys\1.0\bin". The very first semicolon must only be added if it is missing. +Extend the input box by adding ";C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\Program Files (x86)\CMake 2.8\bin". +The very first semicolon must only be added if it is missing. CMake may be added by installer automatically. ###Cloning the Repository Clone the repository (https://github.com/tux3/qTox.git) with your preferred Git client. [SmartGit](http://www.syntevo.com/smartgit/) is very nice for this task. The following steps assume that you cloned the repository at "C:\qTox". If you decided to choose another location, replace corresponding parts. -###Tox Core - -[jenkins.libtoxcore.so](http://jenkins.libtoxcore.so/job/libtoxcore-win32-i686/lastSuccessfulBuild/artifact/libtoxcore-win32-i686.zip) -provides a prebuild package of Tox Core. Download this package and extract its content to "C:\qTox\libs". You may have to create the directory "libs". -If you prefer to compile Tox Core on your own follow the instructions at https://github.com/irungentoo/toxcore/blob/master/INSTALL.md#windows - -###OpenCV - -Unfortunately there are no prebuild packages for OpenCV compiled with MinGW. Thus, you have to create your own. -First of all download and install the most recent version of CMake from -[cmake.org](http://www.cmake.org/cmake/resources/software.html). -Afterwards download OpenCV in version 2.4.9 from [sourceforge.net](http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/2.4.9/opencv-2.4.9.zip/download) and extract the content of the source archive to "C:\qTox\libs". Furthermore, create a new directory named "opencv-build" in "C:\qTox\libs". -Now you should have the two directories "opencv-2.4.9" and "opencv-build" inside your "C:\qTox\libs" directory. - -Run CMake (cmake-gui) and set up the input boxes "Where is the source code:" and "Where to build the binaries" with "C:\qTox\libs\opencv-2.4.9" and "C:\qTox\libs\opencv-build". Press configure and choose "MSYS Makefiles" in the drop down menu with "Use default native compilers". To start initial configuration press Finish. Given that qTox only needs some components of OpenCV it's recommended to disable not required modules. Furthermore, this will decrease compilation time of OpenCV dramatically. Each module begins with "BUILD_opencv_" and can be disabled by deselecting its entry. Use the "Search" input box for convenience. Disable all modules except of "core", "highgui" and "imgproc" (highgui depends on imgproc and will automatically be disabled if imgproc is disabled). For maximum performance search for "CMAKE_BUILD_TYPE" and set this value to "Release". Finally, make sure "CMAKE_INSTALL_PREFIX" points to "C:\qTox\libs\opencv-build\install" (should be by default). To update the configuration press Configure again. To generate the Makefiles press Generate. - -Open a new command prompt within "C:\qTox\libs\opencv-build" (HINT: Use shift + right click -> "Open command window here" on the directory within Windows Explorer). Compile and install OpenCV with the following command. It's not recommended to use -j for multicore compilation, because it freezes the terminal from time to time. -```bash -make -make install -``` - -After OpenCV was successfully installed to "C:\qTox\libs\opencv-build\install" copy the dlls "libopencv_core249.dll", "libopencv_highgui249.dll" and "libopencv_imgproc249.dll" located at "C:\qTox\libs\opencv-build\install\x86\mingw\bin" to "C:\qTox\libs\lib". Afterwards copy the content of the directory "C:\qTox\libs\opencv-build\install\include" to "C:\qTox\libs\include". Finally, you have to patch the file "C:\qTox\libs\include\opencv2\opencv.hpp" because it includes all modules of OpenCV regardless of your configuration. Open this file with your preferred text editor and remove all includes except of "opencv2/core/core_c.h", "opencv2/core/core.hpp", "opencv2/imgproc/imgproc_c.h", "opencv2/imgproc/imgproc.hpp", "opencv2/highgui/highgui_c.h" and "opencv2/highgui/highgui.hpp". OpenCV is now ready to use. Feel free to delete the directories "opencv-2.4.9" and "opencv-build", but you don't need to. - -###OpenAL Soft -As for OpenCV there are no prebuild packages of OpenAL Softe compiled with MinGW, but the installation process is very similar to OpenCV. Download the most recent source archive of OpenAL Soft from [http://kcat.strangesoft.net](http://kcat.strangesoft.net/openal.html#download). Extract its content to "C:\qTox\libs". Besides the source folder itself you'll find the file "pax_global_header". It is not required and can be deleted. Create the directory "openal-build" next to source folder. Now you should have the two directories "openal-soft-x.y.z" where x.y.z is the version of OpenAL and "openal-build" inside your "C:\qTox\libs" directory. Run CMake (cmake-gui) and setup the source and build location. Run the initial configuration and use "MSYS Makefiles" with "Use default native compilers". The only thing you need to configure is "CMAKE_INSTALL_PREFIX" which does not point to "C:\qTox\libs\openal-build\install" by default. Configure the project and generate the Makefiles. Compile and install OpenAL Soft with: -```bash -make -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. \ No newline at end of file +### Getting dependencies +Run bootstrap.bat in cloned C:\qTox directory +Script will download rest of dependencies compile them and put to appropriate directories. diff --git a/README.md b/README.md index 2c55936ed..a99c8f526 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,10 @@ qTox is a powerful Tox client that tries to follow the Tox design guidelines whi This client runs on Windows, Linux and Mac natively.
-Windows 64 bit download
-Windows 32 bit download (for older hardware)
-Mac OS X download
+ +Windows 64 bit download
+Windows 32 bit download (for older hardware)
+Mac OS X download
Linux binary download
Linux packages
@@ -37,6 +38,6 @@ This client runs on Windows, Linux and Mac natively.
##Developer overview: -[GitStats](http://104.219.184.93/index.html)
+[GitStats](https://tux3-dev.tox.im/)
[Mac & Linux jenkins](https://jenkins.libtoxcore.so/user/tux3/my-views/view/qTox/)
-[Windows jenkins](http://104.219.184.93:8080)
+[Windows jenkins](https://tux3-dev.tox.im/jenkins)
diff --git a/bootstrap.bat b/bootstrap.bat index 09178b958..9ed12d602 100644 --- a/bootstrap.bat +++ b/bootstrap.bat @@ -1,4 +1,4 @@ -@mkdir %~dp0libs -%~dp0tools\wget --no-check-certificate http://jenkins.libtoxcore.so/job/libtoxcore-win32-i686/lastSuccessfulBuild/artifact/libtoxcore-win32-i686.zip -O %~dp0libs\libtoxcore-latest.zip -%~dp0tools\unzip -o %~dp0libs\libtoxcore-latest.zip -d %~dp0libs\ -@del %~dp0libs\libtoxcore-latest.zip \ No newline at end of file +@echo off + +sh bootstrap.sh +@pause diff --git a/bootstrap.sh b/bootstrap.sh index d705a02c0..c5e7d2f61 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,5 +1,12 @@ #!/bin/bash +WINDOWS_VERSION=$(cmd.exe /c ver 2>/dev/null | grep "Microsoft Windows") +if [ ! -z "$WINDOWS_VERSION" ]; then + cd windows + ./bootstrap.sh + exit $? +fi + ################ parameters ################ # directory where the script is located SCRIPT_DIR=$( cd $(dirname $0); pwd -P) @@ -67,7 +74,7 @@ while [ $# -ge 1 ] ; do echo "" fi - # print help + # print help echo "Use this script to install/update libsodium and libtoxcore in ${INSTALL_DIR}" echo "" echo "usage:" diff --git a/install_libfilteraudio.sh b/install_libfilteraudio.sh index 010bcbbac..fb36f7330 100755 --- a/install_libfilteraudio.sh +++ b/install_libfilteraudio.sh @@ -14,6 +14,17 @@ else INCLUDE_DIR="$2/include/" fi +WINDOWS_VERSION=$(cmd.exe /c ver 2>/dev/null | grep "Microsoft Windows") +if [ ! -z "$WINDOWS_VERSION" ]; then + EXT="dll" + BIN_DIR="$2/bin/" + STATIC_EXT="$EXT.a" +else + BIN_DIR=$LIB_DIR + EXT="so" + STATIC_EXT="a" +fi + echo "Cloning filter_audio from GitHub.com" git clone https://github.com/irungentoo/filter_audio.git $SOURCE_DIR @@ -22,16 +33,21 @@ cd $SOURCE_DIR gcc -c -fPIC filter_audio.c aec/*.c agc/*.c ns/*.c other/*.c -lm -lpthread echo "Creating shared object file" -gcc *.o -shared -o libfilteraudio.so +gcc *.o -shared -o libfilteraudio.$EXT -Wl,--out-implib,libfilteraudio.$STATIC_EXT echo "Cleaning up" rm *.o -muhcmd="cp libfilteraudio.so $LIB_DIR" +muhcmd="cp libfilteraudio.$EXT $BIN_DIR" [ -z "$2" ] && muhcmd="sudo $muhcmd" echo "Installing libfilteraudio.so with $muhcmd" $muhcmd +muhcmd="cp libfilteraudio.$STATIC_EXT $LIB_DIR" +[ -z "$2" ] && muhcmd="sudo $muhcmd" +echo "Installing libfilteraudio.$STATIC_EXT with $muhcmd" +$muhcmd + muhcmd="cp *.h $INCLUDE_DIR" [ -z "$2" ] && muhcmd="sudo $muhcmd" echo "Installing include files with $muhcmd" diff --git a/qtox.pro b/qtox.pro index f1cdfb7ae..026c8a247 100644 --- a/qtox.pro +++ b/qtox.pro @@ -54,6 +54,23 @@ TIMESTAMP = $$system($1 2>null||echo 0||a;rm null;date +%s||echo 0) # I'm so sor DEFINES += TIMESTAMP=$$TIMESTAMP DEFINES += LOG_TO_FILE +contains(ENABLE_SYSTRAY_UNITY_BACKEND, YES) { + DEFINES += ENABLE_SYSTRAY_UNITY_BACKEND + + INCLUDEPATH += "/usr/include/libappindicator-0.1" + INCLUDEPATH += "/usr/include/gtk-2.0" + INCLUDEPATH += "/usr/include/glib-2.0" + INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/glib-2.0/include" + INCLUDEPATH += "/usr/include/cairo" + INCLUDEPATH += "/usr/include/pango-1.0" + INCLUDEPATH += "/usr/lib/x86_64-linux-gnu/gtk-2.0/include" + INCLUDEPATH += "/usr/include/gdk-pixbuf-2.0" + INCLUDEPATH += "/usr/include/atk-1.0" + INCLUDEPATH += "/usr/include/libdbusmenu-glib-0.4" + + LIBS += -lgobject-2.0 -lappindicator -lgtk-x11-2.0 +} + contains(DISABLE_PLATFORM_EXT, YES) { } else { @@ -75,9 +92,9 @@ contains(JENKINS,YES) { # Rules for Windows, Mac OSX, and Linux win32 { RC_FILE = windows/qtox.rc - LIBS += -liphlpapi -L$$PWD/libs/lib -lsodium -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lvpx -lpthread - LIBS += -L$$PWD/libs/lib -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lOpenAL32 -lopus - LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32 -lz + LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread + LIBS += -L$$PWD/libs/lib -lopencv_core249 -lopencv_highgui249 -lopencv_imgproc249 -lOpenAL32 -lopus + LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -lws2_32 -liphlpapi -lz contains(DEFINES, QTOX_FILTER_AUDIO) { contains(STATICPKG, YES) { @@ -195,7 +212,9 @@ HEADERS += src/widget/form/addfriendform.h \ src/chatlog/content/notificationicon.h \ src/chatlog/content/timestamp.h \ src/chatlog/documentcache.h \ - src/chatlog/pixmapcache.h + src/chatlog/pixmapcache.h \ + src/widget/callconfirmwidget.h \ + src/widget/systemtrayicon.h \ SOURCES += \ src/widget/form/addfriendform.cpp \ @@ -268,7 +287,9 @@ SOURCES += \ src/chatlog/content/notificationicon.cpp \ src/chatlog/content/timestamp.cpp \ src/chatlog/documentcache.cpp \ - src/chatlog/pixmapcache.cpp + src/chatlog/pixmapcache.cpp \ + src/widget/callconfirmwidget.cpp \ + src/widget/systemtrayicon.cpp contains(DEFINES, QTOX_FILTER_AUDIO) { HEADERS += src/audiofilterer.h diff --git a/res.qrc b/res.qrc index de2986aa8..1fe9d0f92 100644 --- a/res.qrc +++ b/res.qrc @@ -124,6 +124,7 @@ translations/fi.qm translations/fr.qm translations/it.qm + translations/lt.qm translations/mannol.qm translations/pirate.qm translations/pl.qm @@ -226,5 +227,8 @@ ui/fileTransferInstance/arrow_white.svg ui/fileTransferInstance/browse.svg ui/fileTransferInstance/filetransferWidget.css + ui/acceptCall/acceptCall.png + ui/rejectCall/rejectCall.png + ui/volButton/volButtonDisabled.png diff --git a/simple_make.sh b/simple_make.sh index 23a8682ac..9f954d7f6 100755 --- a/simple_make.sh +++ b/simple_make.sh @@ -6,8 +6,8 @@ if which apt-get; then elif which pacman; then sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss elif which yum; then - yum groupinstall "Development Tools" - yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel + sudo yum groupinstall "Development Tools" + sudo yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel else echo "Unknown package manager, attempting to compile anyways" fi diff --git a/src/audio.cpp b/src/audio.cpp index 64e0b0961..ecc3c95ce 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -41,6 +41,7 @@ ALCdevice* Audio::alInDev{nullptr}; ALCdevice* Audio::alOutDev{nullptr}; ALCcontext* Audio::alContext{nullptr}; ALuint Audio::alMainSource{0}; +float Audio::outputVolume{1.0}; void audioDebugLog(QString msg) { @@ -64,6 +65,18 @@ Audio& Audio::getInstance() return *instance; } +Audio::~Audio() +{ + qDebug() << "Deleting Audio"; + audioThread->exit(0); + audioThread->wait(); + if (audioThread->isRunning()) + audioThread->terminate(); + delete audioThread; + delete audioInLock; + delete audioOutLock; +} + void Audio::suscribeInput() { if (!alInDev) @@ -249,7 +262,10 @@ void Audio::playGroupAudio(int group, int peer, const int16_t* data, return; if (!call.alSources.contains(peer)) + { alGenSources(1, &call.alSources[peer]); + alSourcef(call.alSources[peer], AL_GAIN, outputVolume); + } playAudioBuffer(call.alSources[peer], data, samples, channels, sample_rate); } @@ -289,6 +305,7 @@ void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, u ALint state; alGetSourcei(alSource, AL_SOURCE_STATE, &state); + alSourcef(alSource, AL_GAIN, outputVolume); if (state != AL_PLAYING) alSourcePlay(alSource); } diff --git a/src/audio.h b/src/audio.h index 6fb8e7107..d43e60071 100644 --- a/src/audio.h +++ b/src/audio.h @@ -73,9 +73,11 @@ public: static QThread* audioThread; static ALCcontext* alContext; static ALuint alMainSource; + static float outputVolume; private: explicit Audio()=default; + ~Audio(); static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate); private: diff --git a/src/autoupdate.cpp b/src/autoupdate.cpp index 4d6dbb87e..604442749 100644 --- a/src/autoupdate.cpp +++ b/src/autoupdate.cpp @@ -65,9 +65,13 @@ const QString AutoUpdater::checkURI = AutoUpdater::updateServer+"/qtox/"+AutoUpd const QString AutoUpdater::flistURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/flist"; const QString AutoUpdater::filesURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/files/"; bool AutoUpdater::abortFlag{false}; +std::atomic_bool AutoUpdater::isDownloadingUpdate{false}; bool AutoUpdater::isUpdateAvailable() { + if (isDownloadingUpdate) + return false; + VersionInfo newVersion = getUpdateVersion(); if (newVersion.timestamp <= TIMESTAMP || newVersion.versionString.isEmpty() || newVersion.versionString == GIT_VERSION) @@ -280,26 +284,33 @@ bool AutoUpdater::downloadUpdate() if (platform.isEmpty()) return false; + bool expectFalse = false; + if (!isDownloadingUpdate.compare_exchange_strong(expectFalse,true)) + return false; + // Get a list of files to update QByteArray newFlistData = getUpdateFlist(); QList newFlist = parseFlist(newFlistData); QList diff = genUpdateDiff(newFlist); if (abortFlag) + { + isDownloadingUpdate = false; return false; + } qDebug() << "AutoUpdater: Need to update "< updateFlist = parseFlist(updateFlistData); QList diff = genUpdateDiff(updateFlist); - // If the update wasn't downloaded correctly, redownload it - // We don't check signatures to not block qTox too long, the updater will do it anyway + // Check that we have every file for (UpdateFileMeta fileMeta : diff) { if (!QFile::exists(updateDirStr+fileMeta.installpath)) - { - QtConcurrent::run(&AutoUpdater::downloadUpdate); return false; - } QFile f(updateDirStr+fileMeta.installpath); if (f.size() != (int64_t)fileMeta.size) - { - QtConcurrent::run(&AutoUpdater::downloadUpdate); return false; - } } return true; @@ -446,6 +474,9 @@ fail: void AutoUpdater::checkUpdatesAsyncInteractive() { + if (isDownloadingUpdate) + return; + QtConcurrent::run(&AutoUpdater::checkUpdatesAsyncInteractiveWorker); } @@ -454,7 +485,12 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker() if (!isUpdateAvailable()) return; - if (Widget::getInstance()->askMsgboxQuestion(QObject::tr("Update", "The title of a message box"), + // If there's already an update dir, resume updating, otherwise ask the user + QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/"; + QDir updateDir(updateDirStr); + + if ((updateDir.exists() && QFile(updateDirStr+"flist").exists()) + || Widget::getInstance()->askMsgboxQuestion(QObject::tr("Update", "The title of a message box"), QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts."))) { downloadUpdate(); @@ -464,4 +500,5 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker() void AutoUpdater::abortUpdates() { abortFlag = true; + isDownloadingUpdate = false; } diff --git a/src/autoupdate.h b/src/autoupdate.h index 3937e3b20..fc9420ed1 100644 --- a/src/autoupdate.h +++ b/src/autoupdate.h @@ -21,9 +21,10 @@ #include #include #include +#include -/// For now we only support auto updates on Windows, although extending it is not a technical issue. -/// Linux and Mac users are expected to use their package managers or update manually through official channels. +/// For now we only support auto updates on Windows and OS X, although extending it is not a technical issue. +/// Linux users are expected to use their package managers or update manually through official channels. #ifdef Q_OS_WIN #define AUTOUPDATE_ENABLED 1 #elif defined(Q_OS_OSX) @@ -122,6 +123,7 @@ private: static const QString updaterBin; ///< Path to the qtox-updater binary static unsigned char key[]; static bool abortFlag; ///< If true, try to abort everything. + static std::atomic_bool isDownloadingUpdate; ///< We'll pretend there's no new update available if we're already updating }; #endif // AUTOUPDATE_H diff --git a/src/core.cpp b/src/core.cpp index 95acc75bc..5cfafd48c 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -84,7 +84,7 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : } // OpenAL init - QString outDevDescr = Settings::getInstance().getOutDev(); ; + QString outDevDescr = Settings::getInstance().getOutDev(); Audio::openOutput(outDevDescr); QString inDevDescr = Settings::getInstance().getInDev(); Audio::openInput(inDevDescr); @@ -92,9 +92,19 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) : Core::~Core() { + qDebug() << "Deleting Core"; + clearPassword(Core::ptMain); clearPassword(Core::ptHistory); + toxTimer->stop(); + coreThread->exit(0); + while (coreThread->isRunning()) + { + qApp->processEvents(); + coreThread->wait(500); + } + if (tox) { toxav_kill(toxav); @@ -355,7 +365,7 @@ void Core::bootstrapDht() } static int j = qrand() % listSize; - qDebug() << "Core: Bootstraping to the DHT ..."; + qDebug() << "Core: Bootstrapping 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 @@ -363,10 +373,10 @@ void Core::bootstrapDht() 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() + qDebug() << QString("Core: Bootstrapping from ")+dhtServer.name+QString(", addr ")+dhtServer.address.toLatin1().data() +QString(", port ")+QString().setNum(dhtServer.port); else - qDebug() << "Core: Error bootstraping from "+dhtServer.name; + qDebug() << "Core: Error bootstrapping from "+dhtServer.name; j++; i++; @@ -731,7 +741,7 @@ void Core::acceptFriendRequest(const QString& userId) void Core::requestFriendship(const QString& friendAddress, const QString& message) { - const QString userId = friendAddress.mid(0, TOX_CLIENT_ID_SIZE * 2); + const QString userId = friendAddress.mid(0, TOX_PUBLIC_KEY_SIZE * 2); if (hasFriendWithAddress(friendAddress)) { @@ -1240,8 +1250,6 @@ bool Core::loadConfiguration(QString path) // tox core is already decrypted if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs()) { - bool error = true; - // get salt QFile file(HistoryKeeper::getHistoryPath()); file.open(QIODevice::ReadOnly); @@ -1255,6 +1263,7 @@ bool Core::loadConfiguration(QString path) } else { + bool error = true; do { while (!pwsaltedkeys[ptHistory]) @@ -1294,6 +1303,9 @@ bool Core::loadConfiguration(QString path) void Core::saveConfiguration() { + if (QThread::currentThread() != coreThread) + return (void) QMetaObject::invokeMethod(this, "saveConfiguration"); + QString dir = Settings::getSettingsDirPath(); QDir directory(dir); if (!directory.exists() && !directory.mkpath(directory.absolutePath())) { @@ -1320,6 +1332,9 @@ void Core::saveConfiguration() void Core::saveConfiguration(const QString& path) { + if (QThread::currentThread() != coreThread) + return (void) QMetaObject::invokeMethod(this, "saveConfiguration", Q_ARG(const QString&, path)); + if (!tox) { qWarning() << "Core::saveConfiguration: Tox not started, aborting!"; @@ -1329,27 +1344,28 @@ void Core::saveConfiguration(const QString& path) Settings::getInstance().save(); QSaveFile configurationFile(path); - if (!configurationFile.open(QIODevice::WriteOnly)) { + if (!configurationFile.open(QIODevice::WriteOnly)) + { qCritical() << "File " << path << " cannot be opened"; return; } qDebug() << "Core: writing tox_save to " << path; - uint32_t fileSize; bool encrypt = Settings::getInstance().getEncryptTox(); + uint32_t fileSize; + bool encrypt = Settings::getInstance().getEncryptTox(); if (encrypt) fileSize = tox_encrypted_size(tox); else fileSize = tox_size(tox); - if (fileSize > 0 && fileSize <= INT32_MAX) { + if (fileSize > 0 && fileSize <= INT32_MAX) + { uint8_t *data = new uint8_t[fileSize]; - if (encrypt) { if (!pwsaltedkeys[ptMain]) { - // probably zero chance event Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Will be saved without encryption!")); tox_save(tox, data); } @@ -1364,7 +1380,9 @@ void Core::saveConfiguration(const QString& path) } } else + { tox_save(tox, data); + } configurationFile.write(reinterpret_cast(data), fileSize); configurationFile.commit(); @@ -1420,7 +1438,7 @@ void Core::loadFriends() // assuming there are not that many friends to fill up the whole stack int32_t *ids = new int32_t[friendCount]; tox_get_friendlist(tox, ids, friendCount); - uint8_t clientId[TOX_CLIENT_ID_SIZE]; + uint8_t clientId[TOX_PUBLIC_KEY_SIZE]; for (int32_t i = 0; i < static_cast(friendCount); ++i) { if (tox_get_client_id(tox, ids[i], clientId) == 0) { emit friendAdded(ids[i], CUserId::toString(clientId)); @@ -1481,7 +1499,7 @@ ToxID Core::getGroupPeerToxID(int groupId, int peerId) const { ToxID peerToxID; - uint8_t rawID[TOX_CLIENT_ID_SIZE]; + uint8_t rawID[TOX_PUBLIC_KEY_SIZE]; int res = tox_group_peer_pubkey(tox, groupId, peerId, rawID); if (res == -1) { @@ -1687,14 +1705,14 @@ bool Core::hasFriendWithAddress(const QString &addr) const return false; } - QString pubkey = addr.left(TOX_CLIENT_ID_SIZE * 2); + QString pubkey = addr.left(TOX_PUBLIC_KEY_SIZE * 2); return hasFriendWithPublicKey(pubkey); } bool Core::hasFriendWithPublicKey(const QString &pubkey) const { // Valid length check - if (pubkey.length() != (TOX_CLIENT_ID_SIZE * 2)) + if (pubkey.length() != (TOX_PUBLIC_KEY_SIZE * 2)) { return false; } @@ -1727,9 +1745,9 @@ bool Core::hasFriendWithPublicKey(const QString &pubkey) const QString Core::getFriendAddress(int friendNumber) const { // If we don't know the full address of the client, return just the id, otherwise get the full address - uint8_t rawid[TOX_CLIENT_ID_SIZE]; + uint8_t rawid[TOX_PUBLIC_KEY_SIZE]; tox_get_client_id(tox, friendNumber, rawid); - QByteArray data((char*)rawid,TOX_CLIENT_ID_SIZE); + QByteArray data((char*)rawid,TOX_PUBLIC_KEY_SIZE); QString id = data.toHex().toUpper(); QString addr = Settings::getInstance().getFriendAdress(id); diff --git a/src/core.h b/src/core.h index aa66eb96c..82181865e 100644 --- a/src/core.h +++ b/src/core.h @@ -64,9 +64,6 @@ public: bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key int joinGroupchat(int32_t friendNumber, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite void quitGroupChat(int groupId) const; ///< Quit a groupchat - - void saveConfiguration(); - void saveConfiguration(const QString& path); QString getIDString() const; ///< Get the 12 first characters of our Tox ID @@ -88,6 +85,9 @@ public slots: void bootstrapDht(); ///< Connects us to the Tox network void switchConfiguration(const QString& profile); ///< Load a different profile and restart the core + void saveConfiguration(); + void saveConfiguration(const QString& path); + void acceptFriendRequest(const QString& userId); void requestFriendship(const QString& friendAddress, const QString& message); void groupInviteFriend(int friendId, int groupId); @@ -117,6 +117,7 @@ public slots: void pauseResumeFileRecv(int friendId, int fileNum); void answerCall(int callId); + void rejectCall(int callId); void hangupCall(int callId); void startCall(int friendId, bool video=false); void cancelCall(int callId, int friendId); diff --git a/src/coreav.cpp b/src/coreav.cpp index b26be579c..49abdf278 100644 --- a/src/coreav.cpp +++ b/src/coreav.cpp @@ -26,7 +26,7 @@ ToxCall Core::calls[TOXAV_MAX_CALLS]; #ifdef QTOX_FILTER_AUDIO -AudioFilterer * Core::filterer[TOXAV_MAX_CALLS] { nullptr}; +AudioFilterer * Core::filterer[TOXAV_MAX_CALLS] {nullptr}; #endif const int Core::videobufsize{TOXAV_MAX_VIDEO_WIDTH * TOXAV_MAX_VIDEO_HEIGHT * 4}; uint8_t* Core::videobuf; @@ -159,6 +159,13 @@ void Core::hangupCall(int callId) toxav_hangup(toxav, callId); } +void Core::rejectCall(int callId) +{ + qDebug() << QString("Core: rejecting call %1").arg(callId); + calls[callId].active = false; + toxav_reject(toxav, callId, nullptr); +} + void Core::startCall(int friendId, bool video) { int callId; @@ -201,7 +208,7 @@ void Core::cancelCall(int callId, int friendId) { qDebug() << QString("Core: Cancelling call with %1").arg(friendId); calls[callId].active = false; - toxav_cancel(toxav, callId, friendId, 0); + toxav_cancel(toxav, callId, friendId, nullptr); } void Core::cleanupCall(int callId) @@ -245,10 +252,11 @@ void Core::sendCallAudio(int callId, ToxAv* toxav) const int framesize = (calls[callId].codecSettings.audio_frame_duration * calls[callId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels; const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels; - uint8_t buf[bufsize], dest[bufsize]; + uint8_t buf[bufsize]; if (Audio::tryCaptureSamples(buf, framesize)) { + uint8_t dest[bufsize]; int r; if ((r = toxav_prepare_audio_frame(toxav, callId, dest, framesize*2, (int16_t*)buf, framesize)) < 0) { @@ -551,6 +559,7 @@ void Core::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, un ALint state; alGetSourcei(alSource, AL_SOURCE_STATE, &state); + alSourcef(alSource, AL_GAIN, Audio::outputVolume); if (state != AL_PLAYING) { alSourcePlay(alSource); @@ -618,9 +627,8 @@ void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) if (Audio::tryCaptureSamples(buf, framesize)) { - int r; - if ((r = toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf, - framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate)) < 0) + if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf, + framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate) < 0) { qDebug() << "Core: toxav_group_send_audio error"; groupCalls[groupId].sendAudioTimer->start(); diff --git a/src/corestructs.cpp b/src/corestructs.cpp index 5ed31df65..980fd1301 100644 --- a/src/corestructs.cpp +++ b/src/corestructs.cpp @@ -33,6 +33,13 @@ bool ToxFile::open(bool write) return write ? file->open(QIODevice::ReadWrite) : file->open(QIODevice::ReadOnly); } +ToxID::ToxID(const ToxID& other) +{ + publicKey = other.publicKey; + noSpam = other.noSpam; + checkSum = other.checkSum; +} + QString ToxID::toString() const { return publicKey + noSpam + checkSum; diff --git a/src/corestructs.h b/src/corestructs.h index 9826b4c0a..657d58f7b 100644 --- a/src/corestructs.h +++ b/src/corestructs.h @@ -16,6 +16,9 @@ enum class Status : int {Online = 0, Away, Busy, Offline}; struct ToxID { + ToxID()=default; + ToxID(const ToxID& other); + QString publicKey; QString noSpam; QString checkSum; diff --git a/src/friend.cpp b/src/friend.cpp index e30b9845f..856e2a58c 100644 --- a/src/friend.cpp +++ b/src/friend.cpp @@ -23,12 +23,11 @@ #include "src/misc/settings.h" Friend::Friend(int FriendId, const ToxID &UserId) - : friendId(FriendId) + : userName{Core::getInstance()->getPeerName(UserId)}, + userID{UserId}, friendId{FriendId} { hasNewEvents = 0; friendStatus = Status::Offline; - userID = UserId; - userName = Core::getInstance()->getPeerName(UserId); if (userName.size() == 0) userName = UserId.publicKey; diff --git a/src/friend.h b/src/friend.h index 7117acc1c..b259b6975 100644 --- a/src/friend.h +++ b/src/friend.h @@ -27,7 +27,9 @@ struct Friend { public: Friend(int FriendId, const ToxID &UserId); + Friend(const Friend& other)=delete; ~Friend(); + Friend& operator=(const Friend& other)=delete; void setName(QString name); void setAlias(QString name); diff --git a/src/historykeeper.h b/src/historykeeper.h index 300232e33..4adbdc79f 100644 --- a/src/historykeeper.h +++ b/src/historykeeper.h @@ -63,10 +63,10 @@ private: void updateAliases(); QPair getChatID(const QString &id_str, ChatType ct); int getAliasID(const QString &id_str); - QString wrapMessage(const QString &str); - QString unWrapMessage(const QString &str); - ChatType convertToChatType(int); + static QString wrapMessage(const QString &str); + static QString unWrapMessage(const QString &str); + static ChatType convertToChatType(int); GenericDdInterface *db; QMap aliases; diff --git a/src/main.cpp b/src/main.cpp index ef378bfbc..863ff230e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,12 +66,24 @@ int main(int argc, char *argv[]) parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("uri", QObject::tr("Tox URI to parse")); - parser.addOption(QCommandLineOption("P", QObject::tr("Starts new instance and loads specified profile."), QObject::tr("profile"))); + parser.addOption(QCommandLineOption("p", QObject::tr("Starts new instance and loads specified profile."), QObject::tr("profile"))); parser.process(a); Settings::getInstance(); // Build our Settings singleton as soon as QApplication is ready, not before - if (parser.isSet("P")) - Settings::getInstance().setCurrentProfile(parser.value("P")); + if (parser.isSet("p")) + { + QString profile = parser.value("p"); + if (QDir(Settings::getSettingsDirPath()).exists(profile + ".tox")) + { + qDebug() << "Setting profile to" << profile; + Settings::getInstance().setCurrentProfile(profile); + } + else + { + qWarning() << "Error: -p profile" << profile + ".tox" << "doesn't exist"; + return EXIT_FAILURE; + } + } sodium_init(); // For the auto-updater @@ -82,7 +94,7 @@ int main(int argc, char *argv[]) if (logfile.open(QIODevice::Append)) { logFile->setDevice(&logfile); - *logFile << QDateTime::currentDateTime().toString("\nyyyy-dd-MM HH:mm:ss' file logger starting\n'"); + *logFile << QDateTime::currentDateTime().toString("\nyyyy-MM-dd HH:mm:ss' file logger starting\n'"); qInstallMessageHandler(myMessageHandler); } else @@ -156,7 +168,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } } - else if (!ipc.isCurrentOwner() && !parser.isSet("P")) + else if (!ipc.isCurrentOwner() && !parser.isSet("p")) { time_t event = ipc.postEvent("$activate"); ipc.waitUntilProcessed(event); @@ -165,6 +177,7 @@ int main(int argc, char *argv[]) } // Run + a.setQuitOnLastWindowClosed(false); Widget* w = Widget::getInstance(); int errorcode = a.exec(); diff --git a/src/misc/cdata.cpp b/src/misc/cdata.cpp index 18f13ca93..28025de94 100644 --- a/src/misc/cdata.cpp +++ b/src/misc/cdata.cpp @@ -36,7 +36,7 @@ uint8_t* CData::data() return cData; } -uint16_t CData::size() +uint16_t CData::size() const { return cDataSize; } @@ -56,7 +56,7 @@ uint16_t CData::fromString(const QString& data, uint8_t* cData) // CUserId -const uint16_t CUserId::SIZE{TOX_CLIENT_ID_SIZE}; +const uint16_t CUserId::SIZE{TOX_PUBLIC_KEY_SIZE}; CUserId::CUserId(const QString &userId) : CData(userId, SIZE < userId.size() ? userId.size() : SIZE) diff --git a/src/misc/cdata.h b/src/misc/cdata.h index 6433e9bac..3b37b0627 100644 --- a/src/misc/cdata.h +++ b/src/misc/cdata.h @@ -24,11 +24,13 @@ class CData { public: uint8_t* data(); - uint16_t size(); + uint16_t size() const; protected: explicit CData(const QString& data, uint16_t byteSize); + CData(const CData& other)=delete; virtual ~CData(); + CData& operator=(const CData& other)=delete; static QString toString(const uint8_t* cData, const uint16_t cDataSize); diff --git a/src/misc/cstring.cpp b/src/misc/cstring.cpp index b9217fbc8..8624ae8eb 100644 --- a/src/misc/cstring.cpp +++ b/src/misc/cstring.cpp @@ -46,7 +46,7 @@ uint8_t* CString::data() return cString; } -uint16_t CString::size() +uint16_t CString::size() const { return cStringSize; } diff --git a/src/misc/cstring.h b/src/misc/cstring.h index 869fa29f2..ee21a99a9 100644 --- a/src/misc/cstring.h +++ b/src/misc/cstring.h @@ -31,7 +31,7 @@ public: ~CString(); uint8_t* data(); - uint16_t size(); + uint16_t size() const; static QString toString(const uint8_t* cMessage, const uint16_t cMessageSize); static uint16_t fromString(const QString& message, uint8_t* cMessage); diff --git a/src/misc/settings.cpp b/src/misc/settings.cpp index 4efacb541..85d3d3289 100644 --- a/src/misc/settings.cpp +++ b/src/misc/settings.cpp @@ -29,12 +29,7 @@ #include #include - -#ifdef Q_OS_LINUX -#define SHOW_SYSTEM_TRAY_DEFAULT (bool) false -#else // OS is not linux #define SHOW_SYSTEM_TRAY_DEFAULT (bool) true -#endif const QString Settings::OLDFILENAME = "settings.ini"; const QString Settings::FILENAME = "qtox.ini"; @@ -136,12 +131,15 @@ void Settings::load() currentProfile = s.value("currentProfile", "").toString(); autoAwayTime = s.value("autoAwayTime", 10).toInt(); checkUpdates = s.value("checkUpdates", false).toBool(); + showWindow = s.value("showWindow", true).toBool(); showInFront = s.value("showInFront", false).toBool(); + groupAlwaysNotify = s.value("groupAlwaysNotify", false).toBool(); fauxOfflineMessaging = s.value("fauxOfflineMessaging", true).toBool(); autoSaveEnabled = s.value("autoSaveEnabled", false).toBool(); globalAutoAcceptDir = s.value("globalAutoAcceptDir", QStandardPaths::locate(QStandardPaths::HomeLocation, QString(), QStandardPaths::LocateDirectory) ).toString(); + compactLayout = s.value("compactLayout", false).toBool(); s.endGroup(); s.beginGroup("Advanced"); @@ -201,6 +199,10 @@ void Settings::load() filterAudio = s.value("filterAudio", false).toBool(); s.endGroup(); + s.beginGroup("Video"); + camVideoRes = s.value("camVideoRes",QSize()).toSize(); + s.endGroup(); + // Read the embedded DHT bootsrap nodes list if needed if (dhtServerList.isEmpty()) { @@ -289,8 +291,11 @@ void Settings::save(QString path, bool writeFriends) s.setValue("currentProfile", currentProfile); s.setValue("autoAwayTime", autoAwayTime); s.setValue("checkUpdates", checkUpdates); + s.setValue("showWindow", showWindow); s.setValue("showInFront", showInFront); + s.setValue("groupAlwaysNotify", groupAlwaysNotify); s.setValue("fauxOfflineMessaging", fauxOfflineMessaging); + s.setValue("compactLayout", compactLayout); s.setValue("autoSaveEnabled", autoSaveEnabled); s.setValue("globalAutoAcceptDir", globalAutoAcceptDir); s.endGroup(); @@ -344,6 +349,10 @@ void Settings::save(QString path, bool writeFriends) s.setValue("filterAudio", filterAudio); s.endGroup(); + s.beginGroup("Video"); + s.setValue("camVideoRes",camVideoRes); + s.endGroup(); + if (!writeFriends || currentProfile.isEmpty()) // Core::switchConfiguration return; @@ -561,7 +570,17 @@ bool Settings::getShowInFront() const void Settings::setShowInFront(bool newValue) { - showInFront = newValue; + showInFront = newValue; +} + +bool Settings::getGroupAlwaysNotify() const +{ + return groupAlwaysNotify; +} + +void Settings::setGroupAlwaysNotify(bool newValue) +{ + groupAlwaysNotify = newValue; } QString Settings::getTranslation() const @@ -854,6 +873,16 @@ void Settings::setCheckUpdates(bool newValue) checkUpdates = newValue; } +bool Settings::getShowWindow() const +{ + return showWindow; +} + +void Settings::setShowWindow(bool newValue) +{ + showWindow = newValue; +} + QByteArray Settings::getSplitterState() const { return splitterState; @@ -914,6 +943,16 @@ void Settings::setFilterAudio(bool newValue) filterAudio = newValue; } +QSize Settings::getCamVideoRes() const +{ + return camVideoRes; +} + +void Settings::setCamVideoRes(QSize newValue) +{ + camVideoRes = newValue; +} + QString Settings::getFriendAdress(const QString &publicKey) const { QString key = ToxID::fromString(publicKey).publicKey; @@ -986,6 +1025,17 @@ void Settings::setFauxOfflineMessaging(bool value) fauxOfflineMessaging = value; } +bool Settings::getCompactLayout() const +{ + return compactLayout; +} + +void Settings::setCompactLayout(bool value) +{ + compactLayout = value; + emit compactLayoutChanged(); +} + int Settings::getThemeColor() const { return themeColor; diff --git a/src/misc/settings.h b/src/misc/settings.h index d05be0a03..86a9bdf31 100644 --- a/src/misc/settings.h +++ b/src/misc/settings.h @@ -115,9 +115,15 @@ public: bool getCheckUpdates() const; void setCheckUpdates(bool newValue); + bool getShowWindow() const; + void setShowWindow(bool newValue); + bool getShowInFront() const; void setShowInFront(bool newValue); + bool getGroupAlwaysNotify() const; + void setGroupAlwaysNotify(bool newValue); + QPixmap getSavedAvatar(const QString& ownerId); void saveAvatar(QPixmap& pic, const QString& ownerId); @@ -133,6 +139,9 @@ public: bool getFilterAudio() const; void setFilterAudio(bool newValue); + QSize getCamVideoRes() const; + void setCamVideoRes(QSize newValue); + // Assume all widgets have unique names // Don't use it to save every single thing you want to save, use it // for some general purpose widgets, such as MainWindows or Splitters, @@ -223,6 +232,9 @@ public: bool getFauxOfflineMessaging() const; void setFauxOfflineMessaging(bool value); + bool getCompactLayout() const; + void setCompactLayout(bool compact); + public: void save(bool writeFriends = true); void save(QString path, bool writeFriends = true); @@ -246,6 +258,7 @@ private: bool dontShowDhtDialog; bool fauxOfflineMessaging; + bool compactLayout; bool enableIPv6; QString translation; static bool makeToxPortable; @@ -255,7 +268,9 @@ private: bool lightTrayIcon; bool useEmoticons; bool checkUpdates; + bool showWindow; bool showInFront; + bool groupAlwaysNotify; bool forceTCP; @@ -305,6 +320,9 @@ private: QString outDev; bool filterAudio; + // Video + QSize camVideoRes; + struct friendProp { QString alias; @@ -323,6 +341,7 @@ signals: void smileyPackChanged(); void emojiFontChanged(); void timestampFormatChanged(); + void compactLayoutChanged(); }; #endif // SETTINGS_HPP diff --git a/src/misc/smileypack.cpp b/src/misc/smileypack.cpp index 258b3ae9c..73c07d979 100644 --- a/src/misc/smileypack.cpp +++ b/src/misc/smileypack.cpp @@ -130,7 +130,8 @@ bool SmileyPack::load(const QString& filename) while (!stringElement.isNull()) { - QString emoticon = stringElement.text(); + QString emoticon = stringElement.text() + .replace("<","<").replace(">",">"); filenameTable.insert(emoticon, file); cacheSmiley(file); // preload all smileys diff --git a/src/platform/timer_win.cpp b/src/platform/timer_win.cpp index 31542f76f..f48c5cad4 100644 --- a/src/platform/timer_win.cpp +++ b/src/platform/timer_win.cpp @@ -22,7 +22,7 @@ uint32_t Platform::getIdleTime() { - LASTINPUTINFO info = { 0 }; + LASTINPUTINFO info = { 0, 0 }; info.cbSize = sizeof(info); if (GetLastInputInfo(&info)) return GetTickCount() - info.dwTime; diff --git a/src/toxdns.cpp b/src/toxdns.cpp index 5afdc73a0..7364f180a 100644 --- a/src/toxdns.cpp +++ b/src/toxdns.cpp @@ -221,8 +221,8 @@ fallbackOnTox1: toxIdStr = queryTox1(record, silent); #elif TOX1_ASK_FALLBACK QMessageBox::StandardButton btn = QMessageBox::warning(nullptr, "qTox", tr("It appears that qTox has to use the old tox1 protocol.\n\ -Unfortunately tox1 is not secure. Should it be used anyway?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::No); - if (btn == QMessageBox::Ok) +Unfortunately tox1 is not secure. Should it be used anyway?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + if (btn == QMessageBox::Yes) queryTox1(record, silent); #endif return toxIdStr; diff --git a/src/video/camera.cpp b/src/video/camera.cpp index 9d30944ae..74f5677e8 100644 --- a/src/video/camera.cpp +++ b/src/video/camera.cpp @@ -99,7 +99,7 @@ double Camera::getProp(Camera::Prop prop) return worker->getProp(int(prop)); } -void Camera::onNewFrameAvailable(const VideoFrame frame) +void Camera::onNewFrameAvailable(const VideoFrame &frame) { emit frameAvailable(frame); diff --git a/src/video/camera.h b/src/video/camera.h index 061e9b27a..364ae577c 100644 --- a/src/video/camera.h +++ b/src/video/camera.h @@ -21,7 +21,7 @@ #include #include #include "vpx/vpx_image.h" -#include "opencv2/opencv.hpp" +#include "opencv2/highgui/highgui.hpp" #include "src/video/videosource.h" class CameraWorker; @@ -79,7 +79,7 @@ private: CameraWorker* worker; private slots: - void onNewFrameAvailable(const VideoFrame frame); + void onNewFrameAvailable(const VideoFrame& frame); }; diff --git a/src/video/cameraworker.cpp b/src/video/cameraworker.cpp index da98e58f4..d4c40731d 100644 --- a/src/video/cameraworker.cpp +++ b/src/video/cameraworker.cpp @@ -29,6 +29,12 @@ CameraWorker::CameraWorker(int index) qRegisterMetaType>(); } +CameraWorker::~CameraWorker() +{ + if (clock) + delete clock; +} + void CameraWorker::onStart() { clock = new QTimer(this); @@ -112,7 +118,7 @@ void CameraWorker::_probeResolutions() //qDebug() << "PROBING:" << res << " got " << w << h; - if (!resolutions.contains(QSize(w,h))) + if (w>0 && h>0 && !resolutions.contains(QSize(w,h))) resolutions.append(QSize(w,h)); } diff --git a/src/video/cameraworker.h b/src/video/cameraworker.h index 742449b5f..5bda28240 100644 --- a/src/video/cameraworker.h +++ b/src/video/cameraworker.h @@ -24,7 +24,7 @@ #include #include -#include "opencv2/opencv.hpp" +#include "opencv2/highgui/highgui.hpp" #include "videosource.h" class QTimer; @@ -34,6 +34,7 @@ class CameraWorker : public QObject Q_OBJECT public: CameraWorker(int index); + ~CameraWorker(); void doWork(); void suspend(); @@ -48,7 +49,7 @@ public slots: signals: void started(); - void newFrameAvailable(const VideoFrame frame); + void newFrameAvailable(const VideoFrame& frame); void resProbingFinished(QList res); void propProbingFinished(int prop, double val); diff --git a/src/video/netvideosource.h b/src/video/netvideosource.h index 4ad6dfd51..c3171727e 100644 --- a/src/video/netvideosource.h +++ b/src/video/netvideosource.h @@ -19,7 +19,7 @@ #include "videosource.h" -class vpx_image; +struct vpx_image; class NetVideoSource : public VideoSource { diff --git a/src/video/videosource.h b/src/video/videosource.h index 4dd14151a..1c9646812 100644 --- a/src/video/videosource.h +++ b/src/video/videosource.h @@ -32,7 +32,7 @@ public: virtual void unsubscribe() = 0; signals: - void frameAvailable(const VideoFrame frame); + void frameAvailable(const VideoFrame& frame); }; diff --git a/src/widget/callconfirmwidget.cpp b/src/widget/callconfirmwidget.cpp new file mode 100644 index 000000000..03cc7912f --- /dev/null +++ b/src/widget/callconfirmwidget.cpp @@ -0,0 +1,93 @@ +#include "callconfirmwidget.h" +#include "widget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) : + QWidget(Widget::getInstance()), anchor(Anchor), + rectW{120}, rectH{85}, + spikeW{30}, spikeH{15}, + roundedFactor{20}, + rectRatio{static_cast(rectH)/static_cast(rectW)} +{ + setWindowFlags(Qt::SubWindow); + + QPalette palette; + palette.setColor(QPalette::WindowText, Qt::white); + setPalette(palette); + + QVBoxLayout *layout = new QVBoxLayout(this); + QLabel *callLabel = new QLabel(QObject::tr("Incoming call..."), this); + callLabel->setAlignment(Qt::AlignHCenter); + QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal, this); + QPushButton *accept = new QPushButton(this), *reject = new QPushButton(this); + accept->setFlat(true); + reject->setFlat(true); + accept->setStyleSheet("QPushButton{border:none;}"); + reject->setStyleSheet("QPushButton{border:none;}"); + accept->setIcon(QIcon(":/ui/acceptCall/acceptCall.png")); + reject->setIcon(QIcon(":/ui/rejectCall/rejectCall.png")); + accept->setIconSize(accept->size()); + reject->setIconSize(reject->size()); + + buttonBox->addButton(accept, QDialogButtonBox::AcceptRole); + buttonBox->addButton(reject, QDialogButtonBox::RejectRole); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &CallConfirmWidget::accepted); + connect(buttonBox, &QDialogButtonBox::rejected, this, &CallConfirmWidget::rejected); + + connect(Widget::getInstance(), &Widget::resized, this, &CallConfirmWidget::reposition); + + layout->setMargin(12); + layout->addSpacing(spikeH); + layout->addWidget(callLabel); + layout->addWidget(buttonBox); + + setFixedSize(rectW,rectH+spikeH); + reposition(); +} + +void CallConfirmWidget::reposition() +{ + Widget* w = Widget::getInstance(); + QPoint pos = anchor->mapToGlobal({(anchor->width()-rectW)/2,anchor->height()})-w->mapToGlobal({0,0}); + + // We don't want the widget to overflow past the right of the screen + int xOverflow=0; + if (pos.x() + rectW > w->width()) + xOverflow = pos.x() + rectW - w->width(); + pos.rx() -= xOverflow; + + mainRect = {0,spikeH,rectW,rectH}; + brush = QBrush(QColor(65,65,65)); + spikePoly = QPolygon({{(rectW-spikeW)/2+xOverflow,spikeH}, + {rectW/2+xOverflow,0}, + {(rectW+spikeW)/2+xOverflow,spikeH}}); + + move(pos); + update(); +} + +void CallConfirmWidget::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setBrush(brush); + painter.setPen(Qt::NoPen); + + painter.drawRoundRect(mainRect, roundedFactor*rectRatio, roundedFactor); + painter.drawPolygon(spikePoly); +} + +void CallConfirmWidget::showEvent(QShowEvent*) +{ + reposition(); + update(); +} diff --git a/src/widget/callconfirmwidget.h b/src/widget/callconfirmwidget.h new file mode 100644 index 000000000..e14484c97 --- /dev/null +++ b/src/widget/callconfirmwidget.h @@ -0,0 +1,45 @@ +#ifndef CALLCONFIRMWIDGET_H +#define CALLCONFIRMWIDGET_H + +#include +#include +#include +#include + +class QPaintEvent; +class QShowEvent; + +/// This is a widget with dialog buttons to accept/reject a call +/// It tracks the position of another widget called the anchor +/// and looks like a bubble at the bottom of that widget. +class CallConfirmWidget : public QWidget +{ + Q_OBJECT +public: + explicit CallConfirmWidget(const QWidget *Anchor); + +signals: + void accepted(); + void rejected(); + +protected: + virtual void paintEvent(QPaintEvent* event) override; + virtual void showEvent(QShowEvent * event) override; + +protected slots: + void reposition(); ///< Recalculate our positions to track the anchor + +private: + const QWidget* anchor; ///< The widget we're going to be tracking + + QRect mainRect; + QPolygon spikePoly; + QBrush brush; + + const int rectW, rectH; + const int spikeW, spikeH; + const int roundedFactor; ///< By how much are the corners of the main rect rounded + const qreal rectRatio; ///< Used to correct the rounding factors on non-square rects +}; + +#endif // CALLCONFIRMWIDGET_H diff --git a/src/widget/form/addfriendform.cpp b/src/widget/form/addfriendform.cpp index efacf17e8..9c1d106e1 100644 --- a/src/widget/form/addfriendform.cpp +++ b/src/widget/form/addfriendform.cpp @@ -98,8 +98,8 @@ void AddFriendForm::onSendTriggered() if (Settings::getInstance().getProxyType() != ProxyType::ptNone) { QMessageBox::StandardButton btn = QMessageBox::warning(main, "qTox", tr("qTox needs to use the Tox DNS, but can't do it through a proxy.\n\ -Ignore the proxy and connect to the Internet directly?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::No); - if (btn != QMessageBox::Ok) +Ignore the proxy and connect to the Internet directly?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + if (btn != QMessageBox::Yes) return; } diff --git a/src/widget/form/chatform.cpp b/src/widget/form/chatform.cpp index cd13bc427..a3e0b598e 100644 --- a/src/widget/form/chatform.cpp +++ b/src/widget/form/chatform.cpp @@ -24,13 +24,20 @@ #include #include #include "chatform.h" -#include "src/historykeeper.h" -#include "src/widget/form/loadhistorydialog.h" +#include "src/core.h" #include "src/friend.h" +#include "src/filetransferinstance.h" +#include "src/historykeeper.h" +#include "src/misc/style.h" +#include "src/misc/settings.h" +#include "src/misc/cstring.h" +#include "src/widget/callconfirmwidget.h" #include "src/widget/friendwidget.h" #include "src/widget/netcamview.h" +#include "src/widget/chatareawidget.h" +#include "src/widget/form/loadhistorydialog.h" #include "src/widget/tool/chattextedit.h" -#include "src/core.h" +#include "src/widget/tool/chatactions/filetransferaction.h" #include "src/widget/widget.h" #include "src/widget/maskablepixmapwidget.h" #include "src/widget/croppinglabel.h" @@ -56,8 +63,13 @@ ChatForm::ChatForm(Friend* chatFriend) statusMessageLabel->setFont(Style::getFont(Style::Medium)); statusMessageLabel->setMinimumHeight(Style::getFont(Style::Medium).pixelSize()); + callConfirm = nullptr; + + typingTimer.setSingleShot(true); + netcam = new NetCamView(); - timer = nullptr; + callDurationTimer = nullptr; + disableCallButtonsTimer = nullptr; chatWidget->setTypingNotification(ChatMessage::createTypingNotification()); @@ -80,6 +92,7 @@ ChatForm::ChatForm(Friend* chatFriend) connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle())); connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed); connect(this, SIGNAL(chatAreaCleared()), this, SLOT(clearReciepts())); + connect(&typingTimer, &QTimer::timeout, this, [=]{Core::getInstance()->sendTyping(f->getFriendID(), false);}); connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig) { if (text != orig) emit aliasChanged(text); } ); @@ -90,6 +103,7 @@ ChatForm::ChatForm(Friend* chatFriend) ChatForm::~ChatForm() { delete netcam; + delete callConfirm; } void ChatForm::setStatusMessage(QString newMessage) @@ -132,6 +146,8 @@ void ChatForm::onSendTriggered() rec = Core::getInstance()->sendMessage(f->getFriendID(), qt_msg); registerReceipt(rec, id, ma); + + msgEdit->setLastMessage(msg); //set last message only when sending it } msgEdit->clear(); @@ -145,11 +161,10 @@ void ChatForm::onTextEditChanged() else isNowTyping = msgEdit->toPlainText().length() > 0; - if (isTyping != isNowTyping) - { - isTyping = isNowTyping; - Core::getInstance()->sendTyping(f->getFriendID(), isTyping); - } + if (isNowTyping) + typingTimer.start(3000); + + Core::getInstance()->sendTyping(f->getFriendID(), isNowTyping); } void ChatForm::onAttachClicked() @@ -245,20 +260,30 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video) videoButton->disconnect(); if (video) { + callConfirm = new CallConfirmWidget(videoButton); + if (isVisible()) + callConfirm->show(); + connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered); + connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered); + callButton->setObjectName("grey"); - callButton->style()->polish(callButton); videoButton->setObjectName("yellow"); - videoButton->style()->polish(videoButton); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered())); + connect(videoButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered); } else { + callConfirm = new CallConfirmWidget(callButton); + if (isVisible()) + callConfirm->show(); + connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered); + connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered); + callButton->setObjectName("yellow"); - callButton->style()->polish(callButton); videoButton->setObjectName("grey"); - videoButton->style()->polish(videoButton); - connect(callButton, SIGNAL(clicked()), this, SLOT(onAnswerCallTriggered())); + connect(callButton, &QPushButton::clicked, this, &ChatForm::onAnswerCallTriggered); } + callButton->style()->polish(callButton); + videoButton->style()->polish(videoButton); insertChatMessage(ChatMessage::createChatInfoMessage(tr("%1 calling").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime())); @@ -287,49 +312,38 @@ void ChatForm::onAvStart(int FriendId, int CallId, bool video) if (video) { callButton->setObjectName("grey"); - callButton->style()->polish(callButton); videoButton->setObjectName("red"); - videoButton->style()->polish(videoButton); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered())); + connect(videoButton, SIGNAL(clicked()), + this, SLOT(onHangupCallTriggered())); netcam->show(Core::getInstance()->getVideoSourceFromCall(CallId), f->getDisplayedName()); } else { callButton->setObjectName("red"); - callButton->style()->polish(callButton); videoButton->setObjectName("grey"); - videoButton->style()->polish(videoButton); - connect(callButton, SIGNAL(clicked()), this, SLOT(onHangupCallTriggered())); + connect(callButton, SIGNAL(clicked()), + this, SLOT(onHangupCallTriggered())); } + callButton->style()->polish(callButton); + videoButton->style()->polish(videoButton); startCounter(); } void ChatForm::onAvCancel(int FriendId, int) { - if (FriendId != f->getFriendID()) return; qDebug() << "onAvCancel"; + delete callConfirm; + callConfirm = nullptr; + stopCounter(); - audioInputFlag = false; - audioOutputFlag = false; - micButton->setObjectName("green"); - micButton->style()->polish(micButton); - volButton->setObjectName("green"); - volButton->style()->polish(volButton); - callButton->disconnect(); - videoButton->disconnect(); - callButton->setObjectName("green"); - callButton->style()->polish(callButton); - videoButton->setObjectName("green"); - videoButton->style()->polish(videoButton); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); + enableCallButtons(); netcam->hide(); @@ -343,21 +357,11 @@ void ChatForm::onAvEnd(int FriendId, int) qDebug() << "onAvEnd"; - audioInputFlag = false; - audioOutputFlag = false; - micButton->setObjectName("green"); - micButton->style()->polish(micButton); - volButton->setObjectName("green"); - volButton->style()->polish(volButton); - callButton->disconnect(); - videoButton->disconnect(); - callButton->setObjectName("green"); - callButton->style()->polish(callButton); - videoButton->setObjectName("green"); - videoButton->style()->polish(videoButton); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); + delete callConfirm; + callConfirm = nullptr; + enableCallButtons(); + netcam->hide(); stopCounter(); @@ -379,7 +383,8 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video) callButton->style()->polish(callButton); videoButton->setObjectName("yellow"); videoButton->style()->polish(videoButton); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered())); + connect(videoButton, SIGNAL(clicked()), + this, SLOT(onCancelCallTriggered())); } else { @@ -387,7 +392,8 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video) callButton->style()->polish(callButton); videoButton->setObjectName("grey"); videoButton->style()->polish(videoButton); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCancelCallTriggered())); + connect(callButton, SIGNAL(clicked()), + this, SLOT(onCancelCallTriggered())); } addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime()); @@ -431,22 +437,10 @@ void ChatForm::onAvEnding(int FriendId, int) qDebug() << "onAvEnding"; - audioInputFlag = false; - audioOutputFlag = false; - micButton->setObjectName("green"); - micButton->style()->polish(micButton); - volButton->setObjectName("green"); - volButton->style()->polish(volButton); - callButton->disconnect(); - videoButton->disconnect(); - callButton->setObjectName("green"); - callButton->style()->polish(callButton); - callButton->disconnect(); - videoButton->setObjectName("green"); - videoButton->style()->polish(videoButton); - videoButton->disconnect(); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); + delete callConfirm; + callConfirm = nullptr; + + enableCallButtons(); netcam->hide(); @@ -460,23 +454,11 @@ void ChatForm::onAvRequestTimeout(int FriendId, int) qDebug() << "onAvRequestTimeout"; - audioInputFlag = false; - audioOutputFlag = false; - micButton->setObjectName("green"); - micButton->style()->polish(micButton); - volButton->setObjectName("green"); - volButton->style()->polish(volButton); - callButton->disconnect(); - videoButton->disconnect(); - callButton->setObjectName("green"); - callButton->style()->polish(callButton); - callButton->disconnect(); - videoButton->setObjectName("green"); - videoButton->style()->polish(videoButton); - videoButton->disconnect(); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); + delete callConfirm; + callConfirm = nullptr; + enableCallButtons(); + netcam->hide(); } @@ -487,22 +469,10 @@ void ChatForm::onAvPeerTimeout(int FriendId, int) qDebug() << "onAvPeerTimeout"; - audioInputFlag = false; - audioOutputFlag = false; - micButton->setObjectName("green"); - micButton->style()->polish(micButton); - volButton->setObjectName("green"); - volButton->style()->polish(volButton); - callButton->disconnect(); - videoButton->disconnect(); - callButton->setObjectName("green"); - callButton->style()->polish(callButton); - callButton->disconnect(); - videoButton->setObjectName("green"); - videoButton->style()->polish(videoButton); - videoButton->disconnect(); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); + delete callConfirm; + callConfirm = nullptr; + + enableCallButtons(); netcam->hide(); } @@ -514,22 +484,10 @@ void ChatForm::onAvRejected(int FriendId, int) qDebug() << "onAvRejected"; - audioInputFlag = false; - audioOutputFlag = false; - micButton->setObjectName("green"); - micButton->style()->polish(micButton); - volButton->setObjectName("green"); - volButton->style()->polish(volButton); - callButton->disconnect(); - videoButton->disconnect(); - callButton->setObjectName("green"); - callButton->style()->polish(callButton); - callButton->disconnect(); - videoButton->setObjectName("green"); - videoButton->style()->polish(videoButton); - videoButton->disconnect(); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); + delete callConfirm; + callConfirm = nullptr; + + enableCallButtons(); insertChatMessage(ChatMessage::createChatInfoMessage(tr("Call rejected"), ChatMessage::INFO, QDateTime::currentDateTime())); @@ -557,6 +515,12 @@ void ChatForm::onAnswerCallTriggered() { qDebug() << "onAnswerCallTriggered"; + if (callConfirm) + { + delete callConfirm; + callConfirm = nullptr; + } + audioInputFlag = true; audioOutputFlag = true; emit answerCall(callId); @@ -569,16 +533,32 @@ void ChatForm::onHangupCallTriggered() audioInputFlag = false; audioOutputFlag = false; emit hangupCall(callId); - micButton->setObjectName("green"); - micButton->style()->polish(micButton); - volButton->setObjectName("green"); - volButton->style()->polish(volButton); + + enableCallButtons(); +} + +void ChatForm::onRejectCallTriggered() +{ + qDebug() << "onRejectCallTriggered"; + + if (callConfirm) + { + delete callConfirm; + callConfirm = nullptr; + } + + audioInputFlag = false; + audioOutputFlag = false; + emit rejectCall(callId); + + enableCallButtons(); + } void ChatForm::onCallTriggered() { qDebug() << "onCallTriggered"; - + audioInputFlag = true; audioOutputFlag = true; callButton->disconnect(); @@ -589,7 +569,7 @@ void ChatForm::onCallTriggered() void ChatForm::onVideoCallTriggered() { qDebug() << "onVideoCallTriggered"; - + audioInputFlag = true; audioOutputFlag = true; callButton->disconnect(); @@ -604,37 +584,80 @@ void ChatForm::onAvCallFailed(int FriendId) qDebug() << "onAvCallFailed"; - audioInputFlag = false; - audioOutputFlag = false; - callButton->disconnect(); - videoButton->disconnect(); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); + delete callConfirm; + callConfirm = nullptr; + + enableCallButtons(); } void ChatForm::onCancelCallTriggered() { qDebug() << "onCancelCallTriggered"; + enableCallButtons(); + + netcam->hide(); + emit cancelCall(callId, f->getFriendID()); +} + +void ChatForm::enableCallButtons() +{ + qDebug() << "enableCallButtons"; + + audioInputFlag = false; + audioOutputFlag = false; + + micButton->setObjectName("grey"); + micButton->style()->polish(micButton); + micButton->disconnect(); + volButton->setObjectName("grey"); + volButton->style()->polish(volButton); + volButton->disconnect(); + + callButton->setObjectName("grey"); + callButton->style()->polish(callButton); + callButton->disconnect(); + videoButton->setObjectName("grey"); + videoButton->style()->polish(videoButton); + videoButton->disconnect(); + + if(disableCallButtonsTimer == nullptr) + { + disableCallButtonsTimer = new QTimer(); + connect(disableCallButtonsTimer, SIGNAL(timeout()), + this, SLOT(onEnableCallButtons())); + disableCallButtonsTimer->start(1500); // 1.5sec + qDebug() << "timer started!!"; + } + +} + +void ChatForm::onEnableCallButtons() +{ + qDebug() << "onEnableCallButtons"; audioInputFlag = false; audioOutputFlag = false; micButton->setObjectName("green"); micButton->style()->polish(micButton); volButton->setObjectName("green"); volButton->style()->polish(volButton); - callButton->disconnect(); - videoButton->disconnect(); callButton->setObjectName("green"); callButton->style()->polish(callButton); - callButton->disconnect(); videoButton->setObjectName("green"); videoButton->style()->polish(videoButton); - videoButton->disconnect(); - connect(callButton, SIGNAL(clicked()), this, SLOT(onCallTriggered())); - connect(videoButton, SIGNAL(clicked()), this, SLOT(onVideoCallTriggered())); - - netcam->hide(); - emit cancelCall(callId, f->getFriendID()); + + connect(callButton, SIGNAL(clicked()), + this, SLOT(onCallTriggered())); + connect(videoButton, SIGNAL(clicked()), + this, SLOT(onVideoCallTriggered())); + connect(micButton, SIGNAL(clicked()), + this, SLOT(onMicMuteToggle())); + connect(volButton, SIGNAL(clicked()), + this, SLOT(onVolMuteToggle())); + + disableCallButtonsTimer->stop(); + delete disableCallButtonsTimer; + disableCallButtonsTimer = nullptr; } void ChatForm::onMicMuteToggle() @@ -695,6 +718,20 @@ void ChatForm::dropEvent(QDropEvent *ev) { QFileInfo info(url.path()); + QFile file(info.absoluteFilePath()); + if (!file.exists() || !file.open(QIODevice::ReadOnly)) + { + QMessageBox::warning(this, tr("File not read"), tr("qTox wasn't able to open %1").arg(info.fileName())); + continue; + } + if (file.isSequential()) + { + QMessageBox::critical(0, tr("Bad Idea"), tr("You're trying to send a special (sequential) file, that's not going to work!")); + file.close(); + continue; + } + file.close(); + if (info.exists()) Core::getInstance()->sendFile(f->getFriendID(), info.fileName(), info.absoluteFilePath(), info.size()); } @@ -805,11 +842,11 @@ void ChatForm::onLoadHistory() void ChatForm::startCounter() { - if (!timer) + if (!callDurationTimer) { - timer = new QTimer(); - connect(timer, SIGNAL(timeout()), this, SLOT(updateTime())); - timer->start(1000); + callDurationTimer = new QTimer(); + connect(callDurationTimer, SIGNAL(timeout()), this, SLOT(onUpdateTime())); + callDurationTimer->start(1000); timeElapsed.start(); callDuration->show(); } @@ -817,19 +854,20 @@ void ChatForm::startCounter() void ChatForm::stopCounter() { - if (timer) + if (callDurationTimer) { addSystemInfoMessage(tr("Call with %1 ended. %2").arg(f->getDisplayedName(),secondsToDHMS(timeElapsed.elapsed()/1000)), ChatMessage::INFO, QDateTime::currentDateTime()); - timer->stop(); + callDurationTimer->stop(); callDuration->setText(""); callDuration->hide(); - timer = nullptr; - delete timer; + + delete callDurationTimer; + callDurationTimer = nullptr; } } -void ChatForm::updateTime() +void ChatForm::onUpdateTime() { callDuration->setText(secondsToDHMS(timeElapsed.elapsed()/1000)); } @@ -915,3 +953,17 @@ void ChatForm::deliverOfflineMsgs() registerReceipt(rec, iter.key(), iter.value()); } } + +void ChatForm::show(Ui::MainWindow &ui) +{ + GenericChatForm::show(ui); + + if (callConfirm) + callConfirm->show(); +} + +void ChatForm::hideEvent(QHideEvent*) +{ + if (callConfirm) + callConfirm->hide(); +} diff --git a/src/widget/form/chatform.h b/src/widget/form/chatform.h index 18616b803..047db5137 100644 --- a/src/widget/form/chatform.h +++ b/src/widget/form/chatform.h @@ -19,15 +19,19 @@ #include "genericchatform.h" #include "src/corestructs.h" +#include #include #include #include -#include + struct Friend; class FileTransferInstance; class NetCamView; class QPixmap; +class CallConfirmWidget; +class QHideEvent; +class QMoveEvent; class ChatForm : public GenericChatForm { @@ -41,6 +45,8 @@ public: void dischargeReceipt(int receipt); void setFriendTyping(bool isTyping); + virtual void show(Ui::MainWindow &ui); + signals: void sendFile(int32_t friendId, QString, QString, long long); void startCall(int friendId); @@ -48,6 +54,7 @@ signals: void answerCall(int callId); void hangupCall(int callId); void cancelCall(int callId, int friendId); + void rejectCall(int callId); void micMuteToggle(int callId); void volMuteToggle(int callId); void aliasChanged(const QString& alias); @@ -83,15 +90,18 @@ private slots: void onAnswerCallTriggered(); void onHangupCallTriggered(); void onCancelCallTriggered(); + void onRejectCallTriggered(); void onFileSendFailed(int FriendId, const QString &fname); void onLoadHistory(); - void updateTime(); + void onUpdateTime(); + void onEnableCallButtons(); protected: // drag & drop void dragEnterEvent(QDragEnterEvent* ev); void dropEvent(QDropEvent* ev); void registerReceipt(int receipt, int messageID, ChatMessage::Ptr msg); + virtual void hideEvent(QHideEvent* event); private: Friend* f; @@ -99,7 +109,9 @@ private: NetCamView* netcam; int callId; QLabel *callDuration; - QTimer *timer; + QTimer *callDurationTimer; + QTimer typingTimer; + QTimer *disableCallButtonsTimer; QElapsedTimer timeElapsed; QHash ftransWidgets; @@ -109,6 +121,8 @@ private: QHash receipts; QMap undeliveredMsgs; bool isTyping; + CallConfirmWidget *callConfirm; + void enableCallButtons(); }; #endif // CHATFORM_H diff --git a/src/widget/form/genericchatform.cpp b/src/widget/form/genericchatform.cpp index 5511df2e9..77aba3d7e 100644 --- a/src/widget/form/genericchatform.cpp +++ b/src/widget/form/genericchatform.cpp @@ -55,9 +55,12 @@ GenericChatForm::GenericChatForm(QWidget *parent) *mainFootLayout = new QHBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout(), - *footButtonsSmall = new QVBoxLayout(), - *volMicLayout = new QVBoxLayout(); - headTextLayout = new QVBoxLayout(); + *footButtonsSmall = new QVBoxLayout(), + *micButtonsLayout = new QVBoxLayout(); + + QGridLayout *buttonsLayout = new QGridLayout(); + + headTextLayout = new QVBoxLayout(); chatWidget = new ChatLog(this); chatWidget->setBusyNotification(ChatMessage::createBusyNotification()); @@ -123,28 +126,37 @@ GenericChatForm::GenericChatForm(QWidget *parent) mainFootLayout->addWidget(sendButton); mainFootLayout->setSpacing(0); - headTextLayout->addStretch(); + headTextLayout->addStretch(); headTextLayout->addWidget(nameLabel); - - volMicLayout->addWidget(micButton, Qt::AlignTop); - volMicLayout->addSpacing(2); - volMicLayout->addWidget(volButton, Qt::AlignBottom); + headTextLayout->addStretch(); + + micButtonsLayout->setSpacing(0); + micButtonsLayout->addWidget(micButton, Qt::AlignTop | Qt::AlignRight); + micButtonsLayout->addSpacing(4); + micButtonsLayout->addWidget(volButton, Qt::AlignTop | Qt::AlignRight); + + buttonsLayout->addLayout(micButtonsLayout, 0, 0, 2, 1, Qt::AlignTop | Qt::AlignRight); + buttonsLayout->addWidget(callButton, 0, 1, 2, 1, Qt::AlignTop); + buttonsLayout->addWidget(videoButton, 0, 2, 2, 1, Qt::AlignTop); + buttonsLayout->setVerticalSpacing(0); + buttonsLayout->setHorizontalSpacing(4); + + headLayout->addWidget(avatar, Qt::AlignTop | Qt::AlignLeft); + headLayout->addSpacing(5); + headLayout->addLayout(headTextLayout, Qt::AlignTop | Qt::AlignAbsolute); + headLayout->addLayout(buttonsLayout, Qt::AlignTop | Qt::AlignRight); headWidget->setLayout(headLayout); - headLayout->addWidget(avatar); - headLayout->addSpacing(5); - headLayout->addLayout(headTextLayout); - headLayout->addLayout(volMicLayout); - headLayout->addWidget(callButton); - headLayout->addSpacing(3); - headLayout->addWidget(videoButton); - headLayout->setSpacing(0); - + //Fix for incorrect layouts on OS X as per //https://bugreports.qt-project.org/browse/QTBUG-14591 sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + micButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + volButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + callButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + videoButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); menu.addActions(chatWidget->actions()); menu.addSeparator(); diff --git a/src/widget/form/settings/advancedform.cpp b/src/widget/form/settings/advancedform.cpp index 44d4ecd13..6a4887217 100644 --- a/src/widget/form/settings/advancedform.cpp +++ b/src/widget/form/settings/advancedform.cpp @@ -30,6 +30,7 @@ AdvancedForm::AdvancedForm() : bodyUI->dbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); bodyUI->dbLabel->setOpenExternalLinks(true); + bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable()); bodyUI->syncTypeComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); bodyUI->syncTypeComboBox->addItems({tr("FULL - very safe, slowest (recommended)"), tr("NORMAL - almost as safe as FULL, about 20% faster than FULL"), @@ -38,6 +39,7 @@ AdvancedForm::AdvancedForm() : int index = 2 - static_cast(Settings::getInstance().getDbSyncType()); bodyUI->syncTypeComboBox->setCurrentIndex(index); + connect(bodyUI->cbMakeToxPortable, &QCheckBox::stateChanged, this, &AdvancedForm::onMakeToxPortableUpdated); connect(bodyUI->syncTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onDbSyncTypeUpdated())); connect(bodyUI->resetButton, SIGNAL(clicked()), this, SLOT(resetToDefault())); } @@ -47,6 +49,11 @@ AdvancedForm::~AdvancedForm() delete bodyUI; } +void AdvancedForm::onMakeToxPortableUpdated() +{ + Settings::getInstance().setMakeToxPortable(bodyUI->cbMakeToxPortable->isChecked()); +} + void AdvancedForm::onDbSyncTypeUpdated() { int index = 2 - bodyUI->syncTypeComboBox->currentIndex(); diff --git a/src/widget/form/settings/advancedform.h b/src/widget/form/settings/advancedform.h index 2b0d3cbcb..4a1fecf22 100644 --- a/src/widget/form/settings/advancedform.h +++ b/src/widget/form/settings/advancedform.h @@ -33,6 +33,7 @@ public: virtual ~AdvancedForm(); private slots: + void onMakeToxPortableUpdated(); void onDbSyncTypeUpdated(); void resetToDefault(); diff --git a/src/widget/form/settings/advancedsettings.ui b/src/widget/form/settings/advancedsettings.ui index 33df4b8ee..d4213713c 100644 --- a/src/widget/form/settings/advancedsettings.ui +++ b/src/widget/form/settings/advancedsettings.ui @@ -29,6 +29,16 @@ + + + + Save settings to the working directory instead of the usual conf dir + + + Make Tox portable + + + diff --git a/src/widget/form/settings/avform.cpp b/src/widget/form/settings/avform.cpp index 31f9b6dd9..3c170821e 100644 --- a/src/widget/form/settings/avform.cpp +++ b/src/widget/form/settings/avform.cpp @@ -47,6 +47,7 @@ AVForm::AVForm() : connect(bodyUI->outDevCombobox, qcomboboxIndexChanged, this, &AVForm::onOutDevChanged); connect(bodyUI->filterAudio, SIGNAL(toggled(bool)), this, SLOT(onFilterAudioToggled(bool))); connect(bodyUI->rescanButton, &QPushButton::clicked, this, [=](){getAudioInDevices(); getAudioOutDevices();}); + bodyUI->playbackSlider->setValue(100); } AVForm::~AVForm() @@ -67,6 +68,10 @@ void AVForm::present() Camera::getInstance()->probeProp(Camera::HUE); Camera::getInstance()->probeResolutions(); + + bodyUI->videoModescomboBox->blockSignals(true); + bodyUI->videoModescomboBox->addItem(tr("Initializing Camera...")); + bodyUI->videoModescomboBox->blockSignals(false); } void AVForm::on_ContrastSlider_sliderMoved(int position) @@ -89,9 +94,11 @@ void AVForm::on_HueSlider_sliderMoved(int position) Camera::getInstance()->setProp(Camera::HUE, position / 100.0); } -void AVForm::on_videoModescomboBox_activated(int index) +void AVForm::on_videoModescomboBox_currentIndexChanged(int index) { - Camera::getInstance()->setResolution(bodyUI->videoModescomboBox->itemData(index).toSize()); + QSize res = bodyUI->videoModescomboBox->itemData(index).toSize(); + Settings::getInstance().setCamVideoRes(res); + Camera::getInstance()->setResolution(res); } void AVForm::onPropProbingFinished(Camera::Prop prop, double val) @@ -117,11 +124,25 @@ void AVForm::onPropProbingFinished(Camera::Prop prop, double val) void AVForm::onResProbingFinished(QList res) { + QSize savedRes = Settings::getInstance().getCamVideoRes(); + int savedResIndex = -1; bodyUI->videoModescomboBox->clear(); - for (QSize r : res) + bodyUI->videoModescomboBox->blockSignals(true); + for (int i=0; ivideoModescomboBox->addItem(QString("%1x%2").arg(QString::number(r.width()),QString::number(r.height())), r); + if (r == savedRes) + savedResIndex = i; + } + //reset index, otherwise cameras with only one resolution won't get initialized + bodyUI->videoModescomboBox->setCurrentIndex(-1); + bodyUI->videoModescomboBox->blockSignals(false); - bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1); + if (savedResIndex != -1) + bodyUI->videoModescomboBox->setCurrentIndex(savedResIndex); + else + bodyUI->videoModescomboBox->setCurrentIndex(bodyUI->videoModescomboBox->count()-1); } void AVForm::hideEvent(QHideEvent *) @@ -129,36 +150,47 @@ void AVForm::hideEvent(QHideEvent *) bodyUI->CamVideoSurface->setSource(nullptr); } +void AVForm::showEvent(QShowEvent *) +{ + bodyUI->CamVideoSurface->setSource(Camera::getInstance()); +} + void AVForm::getAudioInDevices() { QString settingsInDev = Settings::getInstance().getInDev(); - bool inDevFound = false; + int inDevIndex = 0; bodyUI->inDevCombobox->clear(); const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); if (pDeviceList) { + //prevent currentIndexChanged to be fired while adding items + bodyUI->inDevCombobox->blockSignals(true); while (*pDeviceList) { int len = strlen(pDeviceList); - QString inDev = QString::fromLocal8Bit(pDeviceList,len); +#ifdef Q_OS_WIN32 + QString inDev = QString::fromUtf8(pDeviceList,len); +#else + QString inDev = QString::fromLocal8Bit(pDeviceList,len); +#endif bodyUI->inDevCombobox->addItem(inDev); if (settingsInDev == inDev) { - bodyUI->inDevCombobox->setCurrentIndex(bodyUI->inDevCombobox->count()-1); - inDevFound = true; + inDevIndex = bodyUI->inDevCombobox->count()-1; } pDeviceList += len+1; } + //addItem changes currentIndex -> reset + bodyUI->inDevCombobox->setCurrentIndex(-1); + bodyUI->inDevCombobox->blockSignals(false); } - - if (!inDevFound) - Settings::getInstance().setInDev(bodyUI->inDevCombobox->itemText(0)); + bodyUI->inDevCombobox->setCurrentIndex(inDevIndex); } void AVForm::getAudioOutDevices() { QString settingsOutDev = Settings::getInstance().getOutDev(); - bool outDevFound = false; + int outDevIndex = 0; bodyUI->outDevCombobox->clear(); const ALchar *pDeviceList; if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) @@ -167,22 +199,28 @@ void AVForm::getAudioOutDevices() pDeviceList = alcGetString(NULL, ALC_DEVICE_SPECIFIER); if (pDeviceList) { + //prevent currentIndexChanged to be fired while adding items + bodyUI->outDevCombobox->blockSignals(true); while (*pDeviceList) { int len = strlen(pDeviceList); - QString outDev = QString::fromLocal8Bit(pDeviceList,len); +#ifdef Q_OS_WIN32 + QString outDev = QString::fromUtf8(pDeviceList,len); +#else + QString outDev = QString::fromLocal8Bit(pDeviceList,len); +#endif bodyUI->outDevCombobox->addItem(outDev); if (settingsOutDev == outDev) { - bodyUI->outDevCombobox->setCurrentIndex(bodyUI->outDevCombobox->count()-1); - outDevFound = true; + outDevIndex = bodyUI->outDevCombobox->count()-1; } pDeviceList += len+1; } + //addItem changes currentIndex -> reset + bodyUI->outDevCombobox->setCurrentIndex(-1); + bodyUI->outDevCombobox->blockSignals(false); } - - if (!outDevFound) - Settings::getInstance().setOutDev(bodyUI->outDevCombobox->itemText(0)); + bodyUI->outDevCombobox->setCurrentIndex(outDevIndex); } void AVForm::onInDevChanged(const QString &deviceDescriptor) @@ -201,3 +239,28 @@ void AVForm::onFilterAudioToggled(bool filterAudio) { Settings::getInstance().setFilterAudio(filterAudio); } + +void AVForm::on_HueSlider_valueChanged(int value) +{ + Camera::getInstance()->setProp(Camera::HUE, value / 100.0); +} + +void AVForm::on_BrightnessSlider_valueChanged(int value) +{ + Camera::getInstance()->setProp(Camera::BRIGHTNESS, value / 100.0); +} + +void AVForm::on_SaturationSlider_valueChanged(int value) +{ + Camera::getInstance()->setProp(Camera::SATURATION, value / 100.0); +} + +void AVForm::on_ContrastSlider_valueChanged(int value) +{ + Camera::getInstance()->setProp(Camera::CONTRAST, value / 100.0); +} + +void AVForm::on_playbackSlider_valueChanged(int value) +{ + Audio::getInstance().outputVolume = value / 100.0; +} diff --git a/src/widget/form/settings/avform.h b/src/widget/form/settings/avform.h index 64b34d410..9b3980189 100644 --- a/src/widget/form/settings/avform.h +++ b/src/widget/form/settings/avform.h @@ -47,18 +47,28 @@ private slots: void on_SaturationSlider_sliderMoved(int position); void on_BrightnessSlider_sliderMoved(int position); void on_HueSlider_sliderMoved(int position); - void on_videoModescomboBox_activated(int index); + void on_videoModescomboBox_currentIndexChanged(int index); // audio void onInDevChanged(const QString& deviceDescriptor); void onOutDevChanged(const QString& deviceDescriptor); void onFilterAudioToggled(bool filterAudio); + void on_playbackSlider_valueChanged(int value); // camera void onPropProbingFinished(Camera::Prop prop, double val); void onResProbingFinished(QList res); virtual void hideEvent(QHideEvent*); + virtual void showEvent(QShowEvent*); + + void on_HueSlider_valueChanged(int value); + + void on_BrightnessSlider_valueChanged(int value); + + void on_SaturationSlider_valueChanged(int value); + + void on_ContrastSlider_valueChanged(int value); private: Ui::AVSettings *bodyUI; diff --git a/src/widget/form/settings/avsettings.ui b/src/widget/form/settings/avsettings.ui index e46c7d43f..81cb23026 100644 --- a/src/widget/form/settings/avsettings.ui +++ b/src/widget/form/settings/avsettings.ui @@ -60,6 +60,9 @@ Qt::Horizontal + + Use slider to set volume of your speakers. + @@ -67,6 +70,10 @@ Qt::Horizontal + + Use slider to set volume of your microphone. +WARNING: slider is not supposed to work yet. + @@ -101,6 +108,9 @@ Filter audio + + Filter sound from your microphone, so that people hearing you would get better sound. + @@ -122,6 +132,13 @@ Resolution + + Set resolution of your camera. +The higher values, the better video quality your friends may get. +Note though that with better video quality there is needed better internet connection. +Sometimes your connection may not be good enough to handle higher video quality, +which may lead to problems with video calls. + @@ -132,6 +149,13 @@ 0 + + Set resolution of your camera. +The higher values, the better video quality your friends may get. +Note though that with better video quality there is needed better internet connection. +Sometimes your connection may not be good enough to handle higher video quality, +which may lead to problems with video calls. + diff --git a/src/widget/form/settings/generalform.cpp b/src/widget/form/settings/generalform.cpp index 726406fb9..8b74a1941 100644 --- a/src/widget/form/settings/generalform.cpp +++ b/src/widget/form/settings/generalform.cpp @@ -30,8 +30,8 @@ #include "src/autoupdate.h" -static QStringList locales = {"bg", "de", "en", "es", "fr", "it", "mannol", "pirate", "pl", "pt", "ru", "fi", "sv", "uk"}; -static QStringList langs = {"Български", "Deutsch", "English", "Español", "Français", "Italiano", "mannol", "Pirate", "Polski", "Português", "Русский", "Suomi", "Svenska", "Українська"}; +static QStringList locales = {"bg", "de", "en", "es", "fr", "it", "lt", "mannol", "pirate", "pl", "pt", "ru", "fi", "sv", "uk"}; +static QStringList langs = {"Български", "Deutsch", "English", "Español", "Français", "Italiano", "Lietuvių", "mannol", "Pirate", "Polski", "Português", "Русский", "Suomi", "Svenska", "Українська"}; static QStringList timeFormats = {"hh:mm AP", "hh:mm", "hh:mm:ss AP", "hh:mm:ss"}; @@ -45,16 +45,14 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : bodyUI->checkUpdates->setVisible(AUTOUPDATE_ENABLED); bodyUI->checkUpdates->setChecked(Settings::getInstance().getCheckUpdates()); - bodyUI->trayBehavior->addStretch(); bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6()); for (int i = 0; i < langs.size(); i++) bodyUI->transComboBox->insertItem(i, langs[i]); bodyUI->transComboBox->setCurrentIndex(locales.indexOf(Settings::getInstance().getTranslation())); - bodyUI->cbMakeToxPortable->setChecked(Settings::getInstance().getMakeToxPortable()); bool showSystemTray = Settings::getInstance().getShowSystemTray(); - + bodyUI->showSystemTray->setChecked(showSystemTray); bodyUI->startInTray->setChecked(Settings::getInstance().getAutostartInTray()); bodyUI->startInTray->setEnabled(showSystemTray); @@ -68,8 +66,11 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : bodyUI->useEmoticons->setChecked(Settings::getInstance().getUseEmoticons()); bodyUI->autoacceptFiles->setChecked(Settings::getInstance().getAutoSaveEnabled()); bodyUI->autoSaveFilesDir->setText(Settings::getInstance().getGlobalAutoAcceptDir()); + bodyUI->showWindow->setChecked(Settings::getInstance().getShowWindow()); bodyUI->showInFront->setChecked(Settings::getInstance().getShowInFront()); + bodyUI->groupAlwaysNotify->setChecked(Settings::getInstance().getGroupAlwaysNotify()); bodyUI->cbFauxOfflineMessaging->setChecked(Settings::getInstance().getFauxOfflineMessaging()); + bodyUI->cbCompactLayout->setChecked(Settings::getInstance().getCompactLayout()); for (auto entry : SmileyPack::listSmileyPacks()) { @@ -117,7 +118,6 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : //general connect(bodyUI->checkUpdates, &QCheckBox::stateChanged, this, &GeneralForm::onCheckUpdateChanged); connect(bodyUI->transComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onTranslationUpdated())); - connect(bodyUI->cbMakeToxPortable, &QCheckBox::stateChanged, this, &GeneralForm::onMakeToxPortableUpdated); connect(bodyUI->showSystemTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetShowSystemTray); connect(bodyUI->startInTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetAutostartInTray); connect(bodyUI->closeToTray, &QCheckBox::stateChanged, this, &GeneralForm::onSetCloseToTray); @@ -125,7 +125,9 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : connect(bodyUI->lightTrayIcon, &QCheckBox::stateChanged, this, &GeneralForm::onSetLightTrayIcon); connect(bodyUI->statusChanges, &QCheckBox::stateChanged, this, &GeneralForm::onSetStatusChange); connect(bodyUI->autoAwaySpinBox, SIGNAL(editingFinished()), this, SLOT(onAutoAwayChanged())); + connect(bodyUI->showWindow, &QCheckBox::stateChanged, this, &GeneralForm::onShowWindowChanged); connect(bodyUI->showInFront, &QCheckBox::stateChanged, this, &GeneralForm::onSetShowInFront); + connect(bodyUI->groupAlwaysNotify, &QCheckBox::stateChanged, this, &GeneralForm::onSetGroupAlwaysNotify); connect(bodyUI->autoacceptFiles, &QCheckBox::stateChanged, this, &GeneralForm::onAutoAcceptFileChange); if (bodyUI->autoacceptFiles->isChecked()) connect(bodyUI->autoSaveFilesDir, SIGNAL(clicked()), this, SLOT(onAutoSaveDirChange())); @@ -144,6 +146,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : connect(bodyUI->proxyPort, SIGNAL(valueChanged(int)), this, SLOT(onProxyPortEdited(int))); connect(bodyUI->reconnectButton, &QPushButton::clicked, this, &GeneralForm::onReconnectClicked); connect(bodyUI->cbFauxOfflineMessaging, &QCheckBox::stateChanged, this, &GeneralForm::onFauxOfflineMessaging); + connect(bodyUI->cbCompactLayout, &QCheckBox::stateChanged, this, &GeneralForm::onCompactLayout); #ifndef QTOX_PLATFORM_EXT bodyUI->autoAwayLabel->setEnabled(false); // these don't seem to change the appearance of the widgets, @@ -167,11 +170,6 @@ void GeneralForm::onTranslationUpdated() Widget::getInstance()->setTranslation(); } -void GeneralForm::onMakeToxPortableUpdated() -{ - Settings::getInstance().setMakeToxPortable(bodyUI->cbMakeToxPortable->isChecked()); -} - void GeneralForm::onSetShowSystemTray() { Settings::getInstance().setShowSystemTray(bodyUI->showSystemTray->isChecked()); @@ -330,9 +328,19 @@ void GeneralForm::onCheckUpdateChanged() Settings::getInstance().setCheckUpdates(bodyUI->checkUpdates->isChecked()); } +void GeneralForm::onShowWindowChanged() +{ + Settings::getInstance().setShowWindow(bodyUI->showWindow->isChecked()); +} + void GeneralForm::onSetShowInFront() { - Settings::getInstance().setShowInFront(bodyUI->showInFront->isChecked()); + Settings::getInstance().setShowInFront(bodyUI->showInFront->isChecked()); +} + +void GeneralForm::onSetGroupAlwaysNotify() +{ + Settings::getInstance().setGroupAlwaysNotify(bodyUI->groupAlwaysNotify->isChecked()); } void GeneralForm::onFauxOfflineMessaging() @@ -340,6 +348,12 @@ void GeneralForm::onFauxOfflineMessaging() Settings::getInstance().setFauxOfflineMessaging(bodyUI->cbFauxOfflineMessaging->isChecked()); } +void GeneralForm::onCompactLayout() +{ + Settings::getInstance().setCompactLayout(bodyUI->cbCompactLayout->isChecked()); + emit parent->compactToggled(bodyUI->cbCompactLayout->isChecked()); +} + void GeneralForm::onThemeColorChanged(int) { int index = bodyUI->themeColorCBox->currentIndex(); diff --git a/src/widget/form/settings/generalform.h b/src/widget/form/settings/generalform.h index a560cf7ae..d21f5fd52 100644 --- a/src/widget/form/settings/generalform.h +++ b/src/widget/form/settings/generalform.h @@ -33,7 +33,6 @@ public: private slots: void onEnableIPv6Updated(); void onTranslationUpdated(); - void onMakeToxPortableUpdated(); void onSetShowSystemTray(); void onSetAutostartInTray(); void onSetCloseToTray(); @@ -54,8 +53,11 @@ private slots: void onAutoAcceptFileChange(); void onAutoSaveDirChange(); void onCheckUpdateChanged(); + void onShowWindowChanged(); void onSetShowInFront(); + void onSetGroupAlwaysNotify(); void onFauxOfflineMessaging(); + void onCompactLayout(); void onThemeColorChanged(int); private: diff --git a/src/widget/form/settings/generalsettings.ui b/src/widget/form/settings/generalsettings.ui index 762b39447..c67f7b0be 100644 --- a/src/widget/form/settings/generalsettings.ui +++ b/src/widget/form/settings/generalsettings.ui @@ -6,7 +6,7 @@ 0 0 - 545 + 573 644 @@ -34,16 +34,16 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - + 0 - -173 - 511 - 797 + 0 + 544 + 785 - + 9 @@ -57,14 +57,14 @@ - + The translation may not load until qTox restarts. - Translation + Language: @@ -81,27 +81,30 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + - - - - Save settings to the working directory instead of the usual conf dir - - - Make Tox portable - - - - System tray integration + System tray - - - QFormLayout::AllNonFixedFieldsGrow - + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + @@ -109,103 +112,131 @@ - - - - - - - 0 - 0 - - - - Start in tray - - - - - - - - 0 - 0 - - - - Close to tray - - - - - - - - 0 - 0 - - - - Minimize to tray - - - - - Light icon + + Enable light tray icon. + + + + + + 0 + 0 + + + + Start in tray + + + qTox will start minimized in tray. + + + + + + + + 0 + 0 + + + + Close to tray + + + After pressing close (X) qTox will minimize to tray, +instead of closing itself. + + + + + + + + 0 + 0 + + + + Minimize to tray + + + After pressing minimize (_) qTox will minimize itself to tray, +instead of system taskbar. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + - - - - Show contacts' status changes - - - - Check for updates on startup (unstable) + Check for updates on startup - - - Focus qTox when a message is received + + + 0 - - - - - - Faux offline messaging - - - - - - - - - Provided in minutes - - - Qt::LeftToRight - + + - Auto away after (0 to disable) + Save to: + + + Set where files will be saved. - + + + + You can set this on a per-friend basis by right clicking them. + + + Autoaccept files + + + + + + + + 0 + 0 + + + + PushButton + + + Set where files will be saved. + + + + @@ -230,46 +261,116 @@ - - - - - - + + - You can set this on a per-friend basis by right clicking them. + Your status is changed to Away after set period of inactivity. + + + Qt::LeftToRight - Autoaccept files + Auto away after (0 to disable): + + + + + + + + + + + + Chat + + + + + + Always notify about new messages in groupchats. + + + Group chats always notify + + + + + + + Show contacts' status changes + + + + + + + On new message: + + + + + + + 0 + + + + + Show qTox's window when you receive new message. + + + Show window - - - - - Save files in - - - - - - - - 0 - 0 - - - - PushButton - - - - + + + Focus qTox when you receive message. + + + Focus window + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Messages you are trying to send to your friends when they are not online +will be sent to them when they will appear online to you. + + + Faux offline messaging + + + + + + + Your contact list will be shown in compact mode. +qTox's restart needed. + + + Compact contact list + + + @@ -306,7 +407,7 @@ - Smiley Pack + Smiley Pack: @@ -378,47 +479,13 @@ - + - Style + Emoticon size: - - - - 0 - 0 - - - - - - - - Theme color - - - - - - - - 0 - 0 - - - - - - - - Emoticon size - - - - @@ -443,10 +510,44 @@ + + + + Style: + + + + + + + + 0 + 0 + + + + + + + + Theme color: + + + + + + + + 0 + 0 + + + + - Timestamp format + Timestamp format: @@ -478,32 +579,76 @@ 9 - - - Enable IPv6 (recommended) - - - - - - - Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary. - - - Enable UDP (recommended) - - - - - + - + + + Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary. + - Proxy type + Enable UDP (recommended) + + + Enable IPv6 (recommended) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + + + 65535 + + + + + + + Proxy type: + + + + + + + Address: + + + + + + + + + + Port + + + + @@ -530,40 +675,6 @@ - - - - 6 - - - - - Address - - - - - - - - - - Port - - - - - - - 0 - - - 65535 - - - - - @@ -630,5 +741,21 @@ + + showWindow + toggled(bool) + showInFront + setEnabled(bool) + + + 329 + 349 + + + 451 + 349 + + + diff --git a/src/widget/form/settings/identityform.cpp b/src/widget/form/settings/identityform.cpp index e1f46d7e4..a5cfc2aa6 100644 --- a/src/widget/form/settings/identityform.cpp +++ b/src/widget/form/settings/identityform.cpp @@ -46,9 +46,9 @@ IdentityForm::IdentityForm() : bodyUI->toxGroup->layout()->addWidget(toxId); - timer.setInterval(1000); + timer.setInterval(750); timer.setSingleShot(true); - connect(&timer, &QTimer::timeout, this, [=]() {bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text().replace(" ✔", ""));}); + connect(&timer, &QTimer::timeout, this, [=]() {bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text().replace(" ✔", "")); hasCheck = false;}); connect(bodyUI->toxIdLabel, SIGNAL(clicked()), this, SLOT(copyIdClicked())); connect(toxId, SIGNAL(clicked()), this, SLOT(copyIdClicked())); @@ -89,7 +89,11 @@ void IdentityForm::copyIdClicked() QApplication::clipboard()->setText(txt); toxId->setCursorPosition(0); - bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text() + " ✔"); + if (!hasCheck) + { + bodyUI->toxIdLabel->setText(bodyUI->toxIdLabel->text() + " ✔"); + hasCheck = true; + } timer.start(); } diff --git a/src/widget/form/settings/identityform.h b/src/widget/form/settings/identityform.h index b30ec8070..843d7e7b1 100644 --- a/src/widget/form/settings/identityform.h +++ b/src/widget/form/settings/identityform.h @@ -74,6 +74,7 @@ private: Ui::IdentitySettings* bodyUI; Core* core; QTimer timer; + bool hasCheck = false; ClickableTE* toxId; }; diff --git a/src/widget/form/settings/identitysettings.ui b/src/widget/form/settings/identitysettings.ui index e5c25f94a..c2d85d082 100644 --- a/src/widget/form/settings/identitysettings.ui +++ b/src/widget/form/settings/identitysettings.ui @@ -81,6 +81,10 @@ Tox ID + + This bunch of characters tells other Tox clients how to contact you. +Share it with your friends to communicate. + @@ -109,6 +113,9 @@ + + Currently selected profile. + 0 @@ -124,7 +131,7 @@ - Switching profiles is disabled during calls + Load selected profile and switch to it. Load @@ -136,6 +143,9 @@ Rename + + Rename selected profile. + @@ -143,12 +153,16 @@ Export + + Allows you to export your Tox profile to a file. +Profile does not contain your history. + - This is useful to remain safe on public computers + Delete selected profile. Delete @@ -164,12 +178,15 @@ Import a profile + + Import Tox profile from a .tox file. + - Switching profiles is disabled during calls + Create new Tox ID and switch to it. New Tox ID diff --git a/src/widget/form/settings/privacysettings.ui b/src/widget/form/settings/privacysettings.ui index 72262c327..0e5ba073e 100644 --- a/src/widget/form/settings/privacysettings.ui +++ b/src/widget/form/settings/privacysettings.ui @@ -44,7 +44,7 @@ - + Your friends will be able to see when you are typing. Send Typing Notifications @@ -54,7 +54,8 @@ - + History keeping is still in development. +Save format changes are possible, which may result in data loss. Keep History (unstable) @@ -101,6 +102,12 @@ Nospam + + Nospam is part of your Tox ID. +It is there to help you change your Tox ID when you feel like you are getting too much spam friend requests. +When you change nospam, your current contacts still can communicate with you, +but new contacts need to know your new Tox ID to be able to add you. + diff --git a/src/widget/form/settingswidget.h b/src/widget/form/settingswidget.h index 2c415f696..0449e16cf 100644 --- a/src/widget/form/settingswidget.h +++ b/src/widget/form/settingswidget.h @@ -42,15 +42,15 @@ public: void show(Ui::MainWindow &ui); void setBodyHeadStyle(QString style); +signals: + void setShowSystemTray(bool newValue); + void compactToggled(bool compact); + private slots: void onTabChanged(int); -signals: - void setShowSystemTray(bool newValue); - private: - QWidget *head, *body; // keep the others private - IdentityForm *ifrm; + QWidget *head, *body; QTabWidget *settingsWidgets; QLabel *nameLabel, *imgLabel; }; diff --git a/src/widget/form/tabcompleter.cpp b/src/widget/form/tabcompleter.cpp index 2fbaa48c7..382fb6c8f 100644 --- a/src/widget/form/tabcompleter.cpp +++ b/src/widget/form/tabcompleter.cpp @@ -28,7 +28,8 @@ const QString TabCompleter::nickSuffix = QString(": "); TabCompleter::TabCompleter(ChatTextEdit* msgEdit, Group* group) - : QObject(msgEdit), msgEdit(msgEdit), group(group), enabled(false) + : QObject{msgEdit}, msgEdit{msgEdit}, group{group}, + enabled{false}, lastCompletionLength{0} { } @@ -70,9 +71,11 @@ void TabCompleter::complete() if (nextCompletion != completionMap.end()) { // clear previous completion - for (int i = 0; i < lastCompletionLength; i++) { - msgEdit->keyPressEvent(new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier)); - } + auto cur = msgEdit->textCursor(); + cur.setPosition(cur.selectionEnd()); + msgEdit->setTextCursor(cur); + for (int i = 0; i < lastCompletionLength; i++) + msgEdit->textCursor().deletePreviousChar(); // insert completion msgEdit->insertPlainText(*nextCompletion); @@ -95,7 +98,6 @@ void TabCompleter::complete() } } - void TabCompleter::reset() { enabled = false; diff --git a/src/widget/form/tabcompleter.h b/src/widget/form/tabcompleter.h index f48b1a797..23a3fe000 100644 --- a/src/widget/form/tabcompleter.h +++ b/src/widget/form/tabcompleter.h @@ -40,7 +40,7 @@ public slots: private: struct SortableString { - inline SortableString(const QString &n) { contents = n; } + inline SortableString(const QString &n) : contents{n} {} bool operator<(const SortableString &other) const; QString contents; }; diff --git a/src/widget/genericchatroomwidget.cpp b/src/widget/genericchatroomwidget.cpp index 724e2e419..8a46eab04 100644 --- a/src/widget/genericchatroomwidget.cpp +++ b/src/widget/genericchatroomwidget.cpp @@ -16,6 +16,7 @@ #include "genericchatroomwidget.h" #include "src/misc/style.h" +#include "src/misc/settings.h" #include "maskablepixmapwidget.h" #include "croppinglabel.h" #include @@ -24,17 +25,17 @@ 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 + setProperty("compact", Settings::getInstance().getCompactLayout()); // avatar - avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png"); + if (property("compact").toBool()) + { + avatar = new MaskablePixmapWidget(this, QSize(20,20), ":/img/avatar_mask.png"); + } + else + { + avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.png"); + } // status text statusMessageLabel = new CroppingLabel(this); @@ -44,24 +45,65 @@ GenericChatroomWidget::GenericChatroomWidget(QWidget *parent) 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(); + onCompactChanged(property("compact").toBool()); setProperty("active", false); setStyleSheet(Style::getStylesheet(":/ui/chatroomWidgets/genericChatroomWidget.css")); } +void GenericChatroomWidget::onCompactChanged(bool _compact) +{ + delete textLayout; // has to be first, deleted by layout + delete layout; + + setProperty("compact", _compact); + + layout = new QHBoxLayout; + textLayout = new QVBoxLayout; + + setLayout(layout); + layout->setSpacing(0); + layout->setMargin(0); + textLayout->setSpacing(0); + textLayout->setMargin(0); + setLayoutDirection(Qt::LeftToRight); // parent might have set Qt::RightToLeft + + // avatar + if (property("compact").toBool()) + { + setFixedHeight(25); + avatar->setSize(QSize(20,20)); + layout->addSpacing(18); + layout->addWidget(avatar); + layout->addSpacing(5); + layout->addWidget(nameLabel); + layout->addWidget(statusMessageLabel); + layout->addSpacing(5); + layout->addWidget(&statusPic); + layout->addSpacing(5); + layout->activate(); + } + else + { + setFixedHeight(55); + avatar->setSize(QSize(40,40)); + 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(); + } + + Style::repolish(this); +} + bool GenericChatroomWidget::isActive() { return property("active").toBool(); @@ -102,3 +144,14 @@ void GenericChatroomWidget::reloadTheme() { setStyleSheet(Style::getStylesheet(":/ui/chatroomWidgets/genericChatroomWidget.css")); } + +bool GenericChatroomWidget::isCompact() const +{ + return compact; +} + +void GenericChatroomWidget::setCompact(bool compact) +{ + this->compact = compact; + Style::repolish(this); +} diff --git a/src/widget/genericchatroomwidget.h b/src/widget/genericchatroomwidget.h index 333ef109c..ce8d84fa1 100644 --- a/src/widget/genericchatroomwidget.h +++ b/src/widget/genericchatroomwidget.h @@ -53,18 +53,25 @@ public: void reloadTheme(); + bool isCompact() const; + void setCompact(bool compact); + + Q_PROPERTY(bool compact READ isCompact WRITE setCompact) + signals: void chatroomWidgetClicked(GenericChatroomWidget* widget); public slots: + void onCompactChanged(bool compact); protected: QColor lastColor; - QHBoxLayout layout; - QVBoxLayout textLayout; + QHBoxLayout* layout = nullptr; + QVBoxLayout* textLayout = nullptr; MaskablePixmapWidget* avatar; QLabel statusPic; - CroppingLabel *nameLabel, *statusMessageLabel; + CroppingLabel* nameLabel, * statusMessageLabel; + bool compact; friend class Style; ///< To update our stylesheets }; diff --git a/src/widget/groupwidget.cpp b/src/widget/groupwidget.cpp index 7d8892cda..60c5d2297 100644 --- a/src/widget/groupwidget.cpp +++ b/src/widget/groupwidget.cpp @@ -99,16 +99,9 @@ void GroupWidget::updateStatusLight() Group *g = GroupList::findGroup(groupId); if (!g->getEventFlag()) - { statusPic.setPixmap(QPixmap(":img/status/dot_online.png")); - } else - { - if (!g->getMentionedFlag()) - statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); - else - statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); - } + statusPic.setPixmap(QPixmap(":img/status/dot_online_notification.png")); } void GroupWidget::setChatForm(Ui::MainWindow &ui) diff --git a/src/widget/maskablepixmapwidget.cpp b/src/widget/maskablepixmapwidget.cpp index 5f8d2ea99..282ff9e27 100644 --- a/src/widget/maskablepixmapwidget.cpp +++ b/src/widget/maskablepixmapwidget.cpp @@ -19,16 +19,11 @@ MaskablePixmapWidget::MaskablePixmapWidget(QWidget *parent, QSize size, QString maskName) : QWidget(parent) - , renderTarget(size) + , maskName(maskName) , backgroundColor(Qt::white) , clickable(false) { - setFixedSize(size); - - QPixmap pmapMask = QPixmap(maskName); - - if (!pmapMask.isNull()) - mask = QPixmap(maskName).scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + setSize(size); } void MaskablePixmapWidget::autopickBackground() @@ -63,16 +58,11 @@ void MaskablePixmapWidget::autopickBackground() QColor color = QColor::fromRgb(r,g,b); backgroundColor = QColor::fromRgb(0xFFFFFF ^ color.rgb()); + manualColor = false; update(); } -void MaskablePixmapWidget::setBackground(QColor color) -{ - backgroundColor = color; - update(); -} - void MaskablePixmapWidget::setClickable(bool clickable) { this->clickable = clickable; @@ -87,9 +77,10 @@ void MaskablePixmapWidget::setPixmap(const QPixmap &pmap, QColor background) { if (!pmap.isNull()) { + unscaled = pmap; pixmap = pmap.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); backgroundColor = background; - + manualColor = true; update(); } } @@ -98,25 +89,44 @@ void MaskablePixmapWidget::setPixmap(const QPixmap &pmap) { if (!pmap.isNull()) { + unscaled = pmap; pixmap = pmap.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); autopickBackground(); - update(); } } QPixmap MaskablePixmapWidget::getPixmap() const { - return renderTarget; + return *renderTarget; +} + +void MaskablePixmapWidget::setSize(QSize size) +{ + setFixedSize(size); + delete renderTarget; + renderTarget = new QPixmap(size); + + QPixmap pmapMask = QPixmap(maskName); + if (!pmapMask.isNull()) + mask = pmapMask.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + if (!unscaled.isNull()) + { + pixmap = unscaled.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + if (!manualColor) + autopickBackground(); + update(); + } } void MaskablePixmapWidget::paintEvent(QPaintEvent *) { - renderTarget.fill(Qt::transparent); + renderTarget->fill(Qt::transparent); QPoint offset((width() - pixmap.size().width())/2,(height() - pixmap.size().height())/2); // centering the pixmap - QPainter painter(&renderTarget); + QPainter painter(renderTarget); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.fillRect(0,0,width(),height(),backgroundColor); painter.drawPixmap(offset,pixmap); @@ -125,7 +135,7 @@ void MaskablePixmapWidget::paintEvent(QPaintEvent *) painter.end(); painter.begin(this); - painter.drawPixmap(0,0,renderTarget); + painter.drawPixmap(0,0,*renderTarget); } void MaskablePixmapWidget::mousePressEvent(QMouseEvent*) diff --git a/src/widget/maskablepixmapwidget.h b/src/widget/maskablepixmapwidget.h index 86e690aae..15104f9bb 100644 --- a/src/widget/maskablepixmapwidget.h +++ b/src/widget/maskablepixmapwidget.h @@ -26,11 +26,11 @@ 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; + void setSize(QSize size); signals: void clicked(); @@ -40,13 +40,12 @@ protected: virtual void mousePressEvent(QMouseEvent *); private: - QPixmap pixmap; - QPixmap mask; - QPixmap renderTarget; + QPixmap pixmap, mask, unscaled; // a lot of memory... + QPixmap* renderTarget = nullptr; // pointer to dynamically call the constructor QSize size; QString maskName; QColor backgroundColor; - bool clickable; + bool clickable, manualColor = false; }; #endif // MASKABLEPIXMAPWIDGET_H diff --git a/src/widget/systemtrayicon.cpp b/src/widget/systemtrayicon.cpp new file mode 100644 index 000000000..a31929d3e --- /dev/null +++ b/src/widget/systemtrayicon.cpp @@ -0,0 +1,162 @@ +#include "systemtrayicon.h" +#include +#include +#include +#include +#include +#include "src/misc/settings.h" + +SystemTrayIcon::SystemTrayIcon() +{ + QString desktop = getenv("XDG_CURRENT_DESKTOP"); + if (false); + #ifdef ENABLE_SYSTRAY_UNITY_BACKEND + else if (desktop.toLower() == "unity") + { + QString settingsDir = Settings::getSettingsDirPath(); + QFile iconFile(settingsDir+"/icon.png"); + if (iconFile.open(QIODevice::Truncate | QIODevice::WriteOnly)) + { + QFile resIconFile(":/img/icon.png"); + if (resIconFile.open(QIODevice::ReadOnly)) + iconFile.write(resIconFile.readAll()); + resIconFile.close(); + iconFile.close(); + } + backendType = SystrayBackendType::Unity; + unityMenu = gtk_menu_new(); + unityIndicator = app_indicator_new_with_path( + "qTox", + "icon", + APP_INDICATOR_CATEGORY_APPLICATION_STATUS, + settingsDir.toStdString().c_str() + ); + app_indicator_set_menu(unityIndicator, GTK_MENU(unityMenu)); + } + #endif + else + { + qtIcon = new QSystemTrayIcon; + backendType = SystrayBackendType::Qt; + connect(qtIcon, &QSystemTrayIcon::activated, this, &SystemTrayIcon::activated); + } +} + +QString SystemTrayIcon::extractIconToFile(QIcon icon, QString name) +{ + QString iconPath; + (void) icon; + (void) name; +#ifdef ENABLE_SYSTRAY_UNITY_BACKEND + iconPath = Settings::getSettingsDirPath()+"/"+name+".png"; + QSize iconSize = icon.actualSize(QSize{64,64}); + icon.pixmap(iconSize).save(iconPath); +#endif + return iconPath; +} + +void SystemTrayIcon::setContextMenu(QMenu* menu) +{ + if (false); + #ifdef ENABLE_SYSTRAY_UNITY_BACKEND + else if (backendType == SystrayBackendType::Unity) + { + for (QAction* a : menu->actions()) + { + gtk_image_menu_item_new(); + QString aText = a->text().replace('&',""); + GtkWidget* item; + if (a->isSeparator()) + item = gtk_menu_item_new(); + else if (a->icon().isNull()) + item = gtk_menu_item_new_with_label(aText.toStdString().c_str()); + else + { + QString iconPath = extractIconToFile(a->icon(),"iconmenu"+a->icon().name()); + GtkWidget* image = gtk_image_new_from_file(iconPath.toStdString().c_str()); + item = gtk_image_menu_item_new_with_label(aText.toStdString().c_str()); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); + gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item),TRUE); + } + gtk_menu_shell_append(GTK_MENU_SHELL(unityMenu), item); + void (*callback)(GtkMenu*, gpointer data) = [](GtkMenu*, gpointer a) + { + ((QAction*)a)->activate(QAction::Trigger); + }; + g_signal_connect(item, "activate", G_CALLBACK(callback), a); + gtk_widget_show(item); + } + app_indicator_set_menu(unityIndicator, GTK_MENU(unityMenu)); + DbusmenuServer *menuServer; + DbusmenuMenuitem *rootMenuItem; + g_object_get(unityIndicator, "dbus-menu-server", &menuServer, NULL); + g_object_get(menuServer, "root-node", &rootMenuItem, NULL); + void (*callback)(DbusmenuMenuitem *, gpointer) = + [](DbusmenuMenuitem *, gpointer data) + { + ((SystemTrayIcon*)data)->activated(QSystemTrayIcon::Unknown); + }; + g_signal_connect(rootMenuItem, "about-to-show", G_CALLBACK(callback), this); + } + #endif + else + { + qtIcon->setContextMenu(menu); + } +} + +void SystemTrayIcon::show() +{ + setVisible(true); +} + +void SystemTrayIcon::hide() +{ + setVisible(false); +} + +void SystemTrayIcon::setVisible(bool newState) +{ + if (false); + #ifdef ENABLE_SYSTRAY_UNITY_BACKEND + else if (backendType == SystrayBackendType::Unity) + { + if (newState) + app_indicator_set_status(unityIndicator, APP_INDICATOR_STATUS_ACTIVE); + else + app_indicator_set_status(unityIndicator, APP_INDICATOR_STATUS_PASSIVE); + } + #endif + else + { + if (newState) + qtIcon->show(); + else + qtIcon->hide(); + } +} + +void SystemTrayIcon::setIcon(QIcon &&icon) +{ + if (false); + #ifdef ENABLE_SYSTRAY_UNITY_BACKEND + else if (backendType == SystrayBackendType::Unity) + { + // Alternate file names or appindicator will not reload the icon + if (app_indicator_get_icon(unityIndicator) == QString("icon2")) + { + extractIconToFile(icon,"icon"); + app_indicator_set_icon_full(unityIndicator, "icon","qtox"); + } + else + { + extractIconToFile(icon,"icon2"); + app_indicator_set_icon_full(unityIndicator, "icon2","qtox"); + } + } + #endif + else + { + qtIcon->setIcon(icon); + } +} diff --git a/src/widget/systemtrayicon.h b/src/widget/systemtrayicon.h new file mode 100644 index 000000000..aa5120cd0 --- /dev/null +++ b/src/widget/systemtrayicon.h @@ -0,0 +1,36 @@ +#ifndef SYSTEMTRAYICON_H +#define SYSTEMTRAYICON_H + +#include "systemtrayicon_private.h" +#include + +class QSystemTrayIcon; +class QMenu; + +class SystemTrayIcon : public QObject +{ + Q_OBJECT +public: + SystemTrayIcon(); + void setContextMenu(QMenu* menu); + void show(); + void hide(); + void setVisible(bool); + void setIcon(QIcon&& icon); + +signals: + void activated(QSystemTrayIcon::ActivationReason); + +private: + QString extractIconToFile(QIcon icon, QString name="icon"); + +private: + SystrayBackendType backendType; + QSystemTrayIcon* qtIcon; +#ifdef ENABLE_SYSTRAY_UNITY_BACKEND + AppIndicator *unityIndicator; + GtkWidget *unityMenu; +#endif +}; + +#endif // SYSTEMTRAYICON_H diff --git a/src/widget/systemtrayicon_private.h b/src/widget/systemtrayicon_private.h new file mode 100644 index 000000000..3aa4b53a2 --- /dev/null +++ b/src/widget/systemtrayicon_private.h @@ -0,0 +1,35 @@ +#ifndef SYSTEMTRAYICON_PRIVATE_H +#define SYSTEMTRAYICON_PRIVATE_H + +#include + +#ifdef ENABLE_SYSTRAY_UNITY_BACKEND +#ifdef signals +#undef signals +#endif +extern "C" { + #include + #include + #include +} +#define signals public +#endif + +enum class SystrayBackendType +{ + Qt, +#ifdef ENABLE_SYSTRAY_UNITY_BACKEND + Unity +#endif +}; + +union SystrayBackend +{ + QSystemTrayIcon *qt; +#ifdef ENABLE_SYSTRAY_UNITY_BACKEND + AppIndicator *unity; +#endif +}; + + +#endif // SYSTEMTRAYICON_PRIVATE_H diff --git a/src/widget/tool/chattextedit.cpp b/src/widget/tool/chattextedit.cpp index c1b31d162..08526fc5b 100644 --- a/src/widget/tool/chattextedit.cpp +++ b/src/widget/tool/chattextedit.cpp @@ -25,17 +25,26 @@ ChatTextEdit::ChatTextEdit(QWidget *parent) : } void ChatTextEdit::keyPressEvent(QKeyEvent * event) -{ +{ int key = event->key(); - if ((key == Qt::Key_Enter || key == Qt::Key_Return) && !(event->modifiers() && Qt::ShiftModifier)) + if ((key == Qt::Key_Enter || key == Qt::Key_Return) && !(event->modifiers() & Qt::ShiftModifier)) emit enterPressed(); else if (key == Qt::Key_Tab) emit tabPressed(); - else if (key == Qt::Key_Backspace) // because of the backspace() hack in tabber, we can't emit on these - QTextEdit::keyPressEvent(event); + else if (key == Qt::Key_Up && this->toPlainText().isEmpty()) + { + this->setText(lastMessage); + this->setFocus(); + this->moveCursor(QTextCursor::MoveOperation::End,QTextCursor::MoveMode::MoveAnchor); + } else { emit keyPressed(); QTextEdit::keyPressEvent(event); } } + +void ChatTextEdit::setLastMessage(QString lm) +{ + lastMessage = lm; +} diff --git a/src/widget/tool/chattextedit.h b/src/widget/tool/chattextedit.h index 6bab07cda..0d36cd477 100644 --- a/src/widget/tool/chattextedit.h +++ b/src/widget/tool/chattextedit.h @@ -25,14 +25,15 @@ class ChatTextEdit : public QTextEdit public: explicit ChatTextEdit(QWidget *parent = 0); virtual void keyPressEvent(QKeyEvent * event) override; - + void setLastMessage(QString lm); + signals: void enterPressed(); void tabPressed(); void keyPressed(); -public slots: - +private: + QString lastMessage; }; #endif // CHATTEXTEDIT_H diff --git a/src/widget/tool/friendrequestdialog.cpp b/src/widget/tool/friendrequestdialog.cpp index cb7868e4b..ef0fa805b 100644 --- a/src/widget/tool/friendrequestdialog.cpp +++ b/src/widget/tool/friendrequestdialog.cpp @@ -26,6 +26,7 @@ FriendRequestDialog::FriendRequestDialog(QWidget *parent, const QString &userId, const QString &message) : QDialog(parent) { + setAttribute(Qt::WA_QuitOnClose, false); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowTitle(tr("Friend request","Title of the window to aceept/deny a friend request")); diff --git a/src/widget/videosurface.cpp b/src/widget/videosurface.cpp index 48ae9d015..379b11afd 100644 --- a/src/widget/videosurface.cpp +++ b/src/widget/videosurface.cpp @@ -22,15 +22,17 @@ #include VideoSurface::VideoSurface(QWidget* parent) - : QGLWidget(QGLFormat(QGL::SampleBuffers | QGL::SingleBuffer), parent) - , source(nullptr) + : QGLWidget(QGLFormat(QGL::SingleBuffer), parent) + , source{nullptr} , pbo{nullptr, nullptr} - , textureId(0) - , pboAllocSize(0) - , hasSubscribed(false) - , pboIndex(0) + , bgrProgramm{nullptr} + , yuvProgramm{nullptr} + , textureId{0} + , pboAllocSize{0} + , hasSubscribed{false} + , pboIndex{0} { - setAutoBufferSwap(false); + } VideoSurface::VideoSurface(VideoSource *source, QWidget* parent) @@ -65,8 +67,8 @@ void VideoSurface::setSource(VideoSource *src) void VideoSurface::initializeGL() { + QGLWidget::initializeGL(); qDebug() << "VideoSurface: Init"; - // pbo pbo[0] = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer); pbo[0]->setUsagePattern(QOpenGLBuffer::StreamDraw); @@ -180,14 +182,6 @@ void VideoSurface::paintGL() pbo[nextPboIndex]->release(); } - // render pbo - static float values[] = { - -1, -1, - 1, -1, - -1, 1, - 1, 1 - }; - // background glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); @@ -220,6 +214,14 @@ void VideoSurface::paintGL() if (programm) { + // render pbo + static float values[] = { + -1, -1, + 1, -1, + -1, 1, + 1, 1 + }; + programm->bind(); programm->setAttributeArray(0, GL_FLOAT, values, 2); programm->enableAttributeArray(0); @@ -259,7 +261,7 @@ void VideoSurface::unsubscribe() } } -void VideoSurface::onNewFrameAvailable(const VideoFrame newFrame) +void VideoSurface::onNewFrameAvailable(const VideoFrame& newFrame) { mutex.lock(); frame = newFrame; diff --git a/src/widget/videosurface.h b/src/widget/videosurface.h index 18bfd11c4..c0e313e68 100644 --- a/src/widget/videosurface.h +++ b/src/widget/videosurface.h @@ -44,7 +44,7 @@ protected: void unsubscribe(); private slots: - void onNewFrameAvailable(const VideoFrame newFrame); + void onNewFrameAvailable(const VideoFrame &newFrame); private: VideoSource* source; diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 7673bea4b..382b4af11 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -36,6 +36,7 @@ #include "src/autoupdate.h" #include "src/audio.h" #include "src/platform/timer.h" +#include "systemtrayicon.h" #include #include #include @@ -56,8 +57,7 @@ void toxActivateEventHandler(const QByteArray& data) { if (data != "$activate") return; - Widget::getInstance()->show(); - Widget::getInstance()->activateWindow(); + Widget::getInstance()->forceShow(); } Widget *Widget::instance{nullptr}; @@ -85,7 +85,7 @@ void Widget::init() if (QSystemTrayIcon::isSystemTrayAvailable()) { - icon = new QSystemTrayIcon(this); + icon = new SystemTrayIcon; updateTrayIcon(); trayMenu = new QMenu; @@ -113,7 +113,8 @@ void Widget::init() this, SLOT(onIconClick(QSystemTrayIcon::ActivationReason))); - if (Settings::getInstance().getShowSystemTray()){ + if (Settings::getInstance().getShowSystemTray()) + { icon->show(); if (Settings::getInstance().getAutostartInTray() == false) this->show(); @@ -214,7 +215,7 @@ void Widget::init() addFriendForm = new AddFriendForm; settingsWidget = new SettingsWidget(); - connect(settingsWidget, SIGNAL(setShowSystemTray(bool)), this, SLOT(onSetShowSystemTray(bool))); + connect(settingsWidget, &SettingsWidget::setShowSystemTray, this, &Widget::onSetShowSystemTray); connect(core, &Core::connected, this, &Widget::onConnected); connect(core, &Core::disconnected, this, &Widget::onDisconnected); @@ -314,16 +315,16 @@ void Widget::updateTrayIcon() Widget::~Widget() { + qDebug() << "Deleting Widget"; core->saveConfiguration(); - coreThread->exit(); - coreThread->wait(500); // In case of deadlock (can happen with QtAudio/PA bugs) - if (!coreThread->isFinished()) - coreThread->terminate(); AutoUpdater::abortUpdates(); delete core; + icon->hide(); + hideMainForms(); delete settingsWidget; delete addFriendForm; delete filesForm; + delete idleTimer; FriendList::clear(); GroupList::clear(); @@ -359,6 +360,7 @@ void Widget::closeEvent(QCloseEvent *event) { saveWindowGeometry(); saveSplitterGeometry(); + qApp->exit(0); QWidget::closeEvent(event); } } @@ -378,6 +380,8 @@ void Widget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); saveWindowGeometry(); + + emit resized(); } QString Widget::detectProfile() @@ -569,6 +573,13 @@ void Widget::setWindowTitle(const QString& title) QMainWindow::setWindowTitle("qTox - " + title); } +void Widget::forceShow() +{ + hide(); // Workaround to force minimized window to be restored + show(); + activateWindow(); +} + void Widget::onAddClicked() { hideMainForms(); @@ -591,21 +602,38 @@ void Widget::onTransferClicked() void Widget::onIconClick(QSystemTrayIcon::ActivationReason reason) { - switch (reason) { + switch (reason) + { case QSystemTrayIcon::Trigger: - if (this->isHidden() == true) { - this->show(); - this->activateWindow(); + if (isHidden()) + { + show(); + activateWindow(); + } + else if (isMinimized()) + { + forceShow(); + } + else + { + hide(); + } + + break; } - else - this->hide(); - case QSystemTrayIcon::DoubleClick: + case QSystemTrayIcon::DoubleClick: + forceShow(); break; case QSystemTrayIcon::MiddleClick: + hide(); + break; + case QSystemTrayIcon::Unknown: + if (isHidden()) + forceShow(); break; default: - ; + break; } } @@ -633,15 +661,18 @@ void Widget::hideMainForms() void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername) { - ui->nameLabel->setText(oldUsername); // restore old username until Core tells us to set it - ui->nameLabel->setToolTip(oldUsername); // for overlength names + setUsername(oldUsername); // restore old username until Core tells us to set it core->setUsername(newUsername); } void Widget::setUsername(const QString& username) { ui->nameLabel->setText(username); - ui->nameLabel->setToolTip(username); // for overlength names + ui->nameLabel->setToolTip(username); // for overlength names + QString sanename = username; + sanename.remove(QRegExp("[\\t\\n\\v\\f\\r\\x0000]")); + nameMention = QRegExp("\\b" + QRegExp::escape(username) + "\\b", Qt::CaseInsensitive); + sanitizedNameMention = QRegExp("\\b" + QRegExp::escape(sanename) + "\\b", Qt::CaseInsensitive); } void Widget::onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage) @@ -665,6 +696,10 @@ void Widget::addFriend(int friendId, const QString &userId) QLayout* layout = contactListWidget->getFriendLayout(Status::Offline); layout->addWidget(newfriend->getFriendWidget()); + if (Settings::getInstance().getEnableLogging()) + newfriend->getChatForm()->loadHistory(QDateTime::currentDateTime().addDays(-7), true); + + connect(settingsWidget, &SettingsWidget::compactToggled, newfriend->getFriendWidget(), &GenericChatroomWidget::onCompactChanged); connect(newfriend->getFriendWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*))); connect(newfriend->getFriendWidget(), SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int))); connect(newfriend->getFriendWidget(), SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int))); @@ -674,6 +709,7 @@ void Widget::addFriend(int friendId, const QString &userId) connect(newfriend->getChatForm(), SIGNAL(sendFile(int32_t, QString, QString, long long)), core, SLOT(sendFile(int32_t, QString, QString, long long))); connect(newfriend->getChatForm(), SIGNAL(answerCall(int)), core, SLOT(answerCall(int))); connect(newfriend->getChatForm(), SIGNAL(hangupCall(int)), core, SLOT(hangupCall(int))); + connect(newfriend->getChatForm(), SIGNAL(rejectCall(int)), core, SLOT(rejectCall(int))); connect(newfriend->getChatForm(), SIGNAL(startCall(int)), core, SLOT(startCall(int))); connect(newfriend->getChatForm(), SIGNAL(startVideoCall(int,bool)), core, SLOT(startCall(int,bool))); connect(newfriend->getChatForm(), SIGNAL(cancelCall(int,int)), core, SLOT(cancelCall(int,int))); @@ -803,25 +839,11 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool QDateTime timestamp = QDateTime::currentDateTime(); f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true); - if (isAction) - HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, "/me " + message, f->getToxID().publicKey, timestamp, true); - else - HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, message, f->getToxID().publicKey, timestamp, true); - - if (activeChatroomWidget != nullptr) - { - if ((static_cast(f->getFriendWidget()) != activeChatroomWidget) || isMinimized() || !isActiveWindow()) - { - f->setEventFlag(true); - newMessageAlert(f->getFriendWidget()); - } - } - else - { - f->setEventFlag(true); - newMessageAlert(f->getFriendWidget()); - } + HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, isAction ? "/me " + message : message, + f->getToxID().publicKey, timestamp, true); + f->setEventFlag(static_cast(f->getFriendWidget()) != activeChatroomWidget); + newMessageAlert(f->getFriendWidget()); f->getFriendWidget()->updateStatusLight(); } @@ -836,17 +858,22 @@ void Widget::onReceiptRecieved(int friendId, int receipt) void Widget::newMessageAlert(GenericChatroomWidget* chat) { + bool inactiveWindow = isMinimized() || !isActiveWindow(); + if (!inactiveWindow && activeChatroomWidget != nullptr && chat == activeChatroomWidget) + return; + QApplication::alert(this); - static QFile sndFile(":audio/notification.pcm"); - if ((isMinimized() || !isActiveWindow()) && Settings::getInstance().getShowInFront()) + if (Settings::getInstance().getShowWindow()) { - this->show(); - showNormal(); - activateWindow(); - emit chat->chatroomWidgetClicked(chat); + show(); + if (inactiveWindow && Settings::getInstance().getShowInFront()) + setWindowState(Qt::WindowActive); } + + static QFile sndFile(":audio/notification.pcm"); static QByteArray sndData; + if (sndData.isEmpty()) { sndFile.open(QIODevice::ReadOnly); @@ -950,24 +977,21 @@ void Widget::onGroupMessageReceived(int groupnumber, int peernumber, const QStri return; ToxID author = Core::getInstance()->getGroupPeerToxID(groupnumber, peernumber); - QString name = core->getUsername(); - - bool targeted = (!author.isMine()) && message.contains(name, Qt::CaseInsensitive); + bool targeted = !author.isMine() && (message.contains(nameMention) || message.contains(sanitizedNameMention)); if (targeted && !isAction) g->getChatForm()->addAlertMessage(author, message, QDateTime::currentDateTime()); else g->getChatForm()->addMessage(author, message, isAction, QDateTime::currentDateTime(), true); - if ((static_cast(g->getGroupWidget()) != activeChatroomWidget) || isMinimized() || !isActiveWindow()) - { - g->setEventFlag(true); - if (targeted) - { - newMessageAlert(g->getGroupWidget()); - g->setMentionedFlag(true); // useful for highlighting line or desktop notifications - } - g->getGroupWidget()->updateStatusLight(); - } + g->setEventFlag(static_cast(g->getGroupWidget()) != activeChatroomWidget); + + if (targeted || Settings::getInstance().getGroupAlwaysNotify()) + newMessageAlert(g->getGroupWidget()); + + if (targeted) + g->setMentionedFlag(true); // useful for highlighting line or desktop notifications + + g->getGroupWidget()->updateStatusLight(); } void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Change) diff --git a/src/widget/widget.h b/src/widget/widget.h index ad6ad8b04..9f86c52d6 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -44,6 +44,7 @@ class FriendListWidget; class MaskablePixmapWidget; class QTimer; class QTranslator; +class SystemTrayIcon; class Widget : public QMainWindow { @@ -81,6 +82,7 @@ public: public slots: void onSettingsClicked(); void setWindowTitle(const QString& title); + void forceShow(); signals: void friendRequestAccepted(const QString& userId); @@ -90,6 +92,7 @@ signals: void usernameChanged(const QString& username); void statusMessageChanged(const QString& statusMessage); void changeProfile(const QString& profile); + void resized(); private slots: void onConnected(); @@ -147,7 +150,7 @@ private: void saveSplitterGeometry(); QString askProfiles(); QString detectProfile(); - QSystemTrayIcon *icon; + SystemTrayIcon *icon; QMenu *trayMenu; QAction *statusOnline, *statusAway, @@ -171,6 +174,7 @@ private: Status beforeDisconnect = Status::Offline; QTimer* idleTimer; QTranslator* translator; + QRegExp nameMention, sanitizedNameMention; }; void toxActivateEventHandler(const QByteArray& data); diff --git a/translations/de.ts b/translations/de.ts index e54e3a6b2..5abcccc6c 100644 --- a/translations/de.ts +++ b/translations/de.ts @@ -8,6 +8,11 @@ Audio/Video Audio/Video + + + Initializing Camera... + Initialisiert Kamera... + AVSettings @@ -141,17 +146,17 @@ Soll der Proxy ignoriert und eine direkte Internetverbindung genutzt werden?Fortgeschritten - + FULL - very safe, slowest (recommended) VOLL - sehr sicher, langsam (empfohlen) - + NORMAL - almost as safe as FULL, about 20% faster than FULL NORMAL - fast so sicher wie VOLL, ca. 20% schneller - + OFF - disables all safety, when something goes wrong your history may be lost, fastest (not recommended) AUS - keinerlei Sicherheit, geht etwas schief, kann die Historie verloren gehen, ist am schnellsten (nicht empfohlen) @@ -165,21 +170,32 @@ Soll der Proxy ignoriert und eine direkte Internetverbindung genutzt werden? + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Speichert die Einstellungen im Arbeits- statt im normalen Konfigurationsverzeichnis + + + + Make Tox portable + Macht Tox portabel + + + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTANT NOTE</span></p><p><span style=" color:#ff0000;">Unless you </span><span style=" font-weight:600; color:#ff0000;">really</span><span style=" color:#ff0000;"> know what you are doing, please do </span><span style=" font-weight:600; color:#ff0000;">not</span><span style=" color:#ff0000;"> change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</span></p></body></html> <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">WICHTIGE NOTIZ</span></p><p><span style=" color:#ff0000;">Wenn Sie nicht </span><span style=" font-weight:600; color:#ff0000;">wirklich</span><span style=" color:#ff0000;"> wissen, was Sie tun, ändern Sie hier bitte </span><span style=" font-weight:600; color:#ff0000;">nichts</span><span style=" color:#ff0000;">. Hier getätigte Änderungen können zu Problemen mit qTox führen, evtl. sogar zum Datenverlust, z.B. der Historie.</span></p></body></html> - + Reset to default settings Grundeinstellungen wiederherstellen - + History Historie - + <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Synchronous writing to DB</span></a></p></body></html> <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Synchrones Schreiben in die DB</span></a></p></body></html> @@ -187,70 +203,79 @@ Soll der Proxy ignoriert und eine direkte Internetverbindung genutzt werden? ChatForm - + Load History... Historie laden... - + Send a file Datei versenden - + + File not read Datei nicht gelesen - + + qTox wasn't able to open %1 qTox konnte %1 nicht öffnen - + + Bad Idea Schlechte Idee - + + You're trying to send a special (sequential) file, that's not going to work! Sie versuchen eine spezielle (sequentielle) Datei zu senden, das funktioniert nicht! - + %1 is calling %1 ruft an - + %1 stopped calling %1 hat den Anruf beendet - + Calling to %1 Rufe %1 an - + Call rejected Anruf abgewiesen - + Failed to send file "%1" Senden der Datei "%1" fehlgeschlagen - + Call with %1 ended. %2 Anruf zu %1 beendet. %2 - + Call duration: Anrufdauer: + + + is typing... + tippt gerade... + ChatTextEdit @@ -263,104 +288,104 @@ Soll der Proxy ignoriert und eine direkte Internetverbindung genutzt werden? Core - + Toxing on qTox Toxen mit qTox - + qTox User qTox Benutzer - + Friend is already added Freund wurde schon hinzugefügt - + Encryption error Verschlüsselungsfehler - + The .tox file is encrypted, but encryption was not checked, continuing regardless. Die .tox Datei ist verschlüsselt, aber die Verschlüsselung wurde nicht geprüft, Vorgang wird trotzdem fortgesetzt. - + Tox datafile decryption password Entschlüsselungspasswort für Tox Datendatei - - - + + + Password error Passwortfehler - - + + Failed to setup password. Empty password. Setzen des Passwortes fehlgeschlagen. Leeres Passwort. - + Try Again Nochmal versuchen - + Change profile Profil ändern - + Reinit current profile Aktuelles Profil erneut starten - + Wrong password has been entered Es wurde ein falsches Passwort eingegeben - + History Log decryption password Passwort zur Entschlüsselung der Historie - + Encrypted log Verschlüsselte Logdatei - + Your history is encrypted with different password. Do you want to try another password? Ihre Historie wurde mit einem anderen Passwort verschlüsselt. Wollen Sie ein weiteres probieren? - + Due to incorret password history will be disabled. Falsches Passwort, Historie wird deaktiviert. - + History Historie - + NO Password KEIN Passwort - + Will be saved without encryption! Wird ohne Verschlüsselung gespeichert! @@ -413,34 +438,34 @@ Wollen Sie ein weiteres probieren? FriendRequestDialog - + Friend request Title of the window to aceept/deny a friend request Freundschaftsanfrage - + Someone wants to make friends with you Jemand möchte Ihr Freund werden - + User ID: Benutzer ID: - + Friend request message: Freundschaftsanfrage: - + Accept Accept a friend request Annehmen - + Reject Reject a friend request Ablehnen @@ -504,25 +529,25 @@ Alias: Allgemein - - + + None Kein - + Choose an auto accept directory popup title Wählen Sie ein Verzeichnis - + Call active popup title Anwahl aktiviert - + You can't disconnect while a call is active! popup text Abbruch während der Anwahl nicht möglich! @@ -542,99 +567,105 @@ Alias: Änderung wird erst nach Neustart von qTox aktiv. - - Translation - Sprache - - - - Save settings to the working directory instead of the usual conf dir - describes makeToxPortable checkbox - Speichert die Einstellungen im Arbeits- statt im normalen Konfigurationsverzeichnis - - - - Make Tox portable - Macht Tox portabel - - - - System tray integration - Systemtray Integration - - - + Show system tray icon - Im Systemtray zeigen + In der Systemleiste anzeigen - + Start in tray - Ins Tray starten + In die Systemleiste starten - + Close to tray - Ins Tray schließen + In die Systemleiste schließen - + Minimize to tray - Ins Tray minimieren + In die Systemleiste minimieren - + Light icon Helles Icon - + + Language: + Sprache: + + + + System tray + Systemleiste + + + + Check for updates on startup + Beim Starten auf Updates überprüfen + + + + Save to: + Speichern in: + + + + Auto away after (0 to disable): + Automatisch abwesend nach (0 zum Deaktivieren): + + + + Chat + Chat + + + + Group chats always notify + Über Gruppenchats immer informieren + + + Show contacts' status changes Zeigt Statusänderungen der Kontakte - - Check for updates on startup (unstable) - Prüft beim Start auf Updates (nicht stabil) - - - - Focus qTox when a message is received - Bringt qTox in den Vordergrund, wenn eine Nachricht eintrifft - - - + Faux offline messaging Imitiert Offline Benachrichtigung - + Compact contact list (restart required) + Kompakte Darstellung der Kontaktliste (Neustart benötigt) + + + Provided in minutes Angabe in Minuten - Auto away after (0 to disable) Automatisch abwesend nach (0 deaktiviert) - + Set to 0 to disable Zum Deaktivieren auf 0 setzen - + You can set this on a per-friend basis by right clicking them. autoaccept cb tooltip Sie können dies durch Rechtsklick auf den jeweiligen Freund festlegen. - + Use emoticons Emoticons benutzen - Smiley Pack Text on smiley pack label Emoticon Paket @@ -655,82 +686,150 @@ Alias: Emoticon Größe - + px Pixel - Timestamp format Zeitformat - + Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary. force tcp checkbox tooltip Wenn deaktiviert kann z.B. über Tor getoxt werden. Dies belastet das Tox Netzwerk zusätzlich und sollte nur deaktiviert werden wenn notwendig. - + Enable UDP (recommended) Text on checkbox to disable UDP UDP aktivieren (empfohlen) - + + Proxy type: + Proxy Typ: + + + + Address: + Text on proxy addr label + Adresse: + + + + SOCKS5 + SOCKS5 + + + + HTTP + HTTP + + + Reconnect reconnect button Erneut verbinden - + minutes Minuten - + Autoaccept files Dateien automatisch annehmen - Save files in - Speichern unter + Speichern unter - + PushButton Schaltfläche + On new message: + Bei neuer Nachricht: + + + + Show window + Zeige Fenster + + + + Focus window + Bringe Fenster in den Vordergrund + + + + Compact contact list + Kompakte Kontaktliste + + + Theme Benutzeroberfläche - + + Smiley Pack: + Text on smiley pack label + Smiley Paket: + + + + Emoticon size: + Emoticon Größe: + + + + Style: + Stil: + + + + Theme color: + Fensterfarbe: + + + + Timestamp format: + Zeitformat: + + + Connection Settings Verbindungseinstellungen - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 IPv6 aktivieren (empfohlen) - - Use proxy (SOCKS5) - Proxy benutzen (SOCKS5) + Proxy type + Proxy Typ + + + + None + Keinen - Address Text on proxy addr label Adresse - + Port Text on proxy port label Port @@ -739,53 +838,53 @@ Alias: GenericChatForm - + Send message Nachricht senden - + Smileys Smileys - + Send file(s) Datei(en) senden - + Audio call: RED means you're on a call Sprachanruf: ROT bedeutet verbunden - + Video call: RED means you're on a call Videoanruf: ROT bedeutet verbunden - + Toggle speakers volume: RED is OFF Schaltet den Lautsprecher ein/aus: ROT ist AUS - + Toggle microphone: RED is OFF Schaltet das Mikrofon ein/aus: ROT ist AUS - - + + Save chat log Chatverlauf speichern - + Clear displayed messages Angezeigte Nachrichten ausblenden - + Cleared Ausgeblendet @@ -850,87 +949,87 @@ Titel: Identität - + Call active popup title Anruf aktiv - + You can't switch profiles while a call is active! popup text Das Profil kann während eines Anrufes nicht gewechselt werden! - + Rename "%1" renaming a profile "%1" umbenennen - + Profile already exists rename confirm title Profil existiert bereits - + A profile named "%1" already exists. Do you want to erase it? rename confirm text Ein Profil mit dem Namen "%1" existiert bereits. Wollen Sie es löschen? - + Export profile save dialog title Profil exportieren - + Tox save file (*.tox) save dialog filter Toxdatei speichern (*.tox) - + Failed to remove file Entfernen der Datei fehlgeschlagen - + The file you chose to overwrite could not be removed first. Die gewählte Datei kann nicht überschrieben werden. - + Failed to copy file Kopieren der Datei fehlgeschlagen - + The file you chose could not be written to. Die gewählte Datei kann nicht überschrieben werden. - + Profile currently loaded current profile deletion warning title Profil ist zurzeit geladen - + This profile is currently in use. Please load a different profile before deleting this one. current profile deletion warning text Dieses Profil ist aktuell in Gebrauch. Bitte laden Sie vor dem Löschen ein anderes Profil. - + Deletion imminent! deletion confirmation title Löschen steht bevor! - + Are you sure you want to delete this profile? Associated friend information and chat logs will be deleted as well. deletion confirmation text @@ -938,37 +1037,37 @@ Associated friend information and chat logs will be deleted as well. Dazugehörige Informationen und Chatprotokolle werden ebenfalls gelöscht. - + Import profile import dialog title Profil importieren - + Tox save file (*.tox) import dialog filter Toxdatei speichern (*.tox) - + Ignoring non-Tox file popup title Keine Toxdatei, wird ignoriert - + Warning: you've chosen a file that is not a Tox save file; ignoring. popup text Warnung: Sie haben eine Datei gewählt, die keine Toxdatei ist, wird ignoriert. - + Profile already exists import confirm title Profil existiert bereits - + A profile named "%1" already exists. Do you want to erase it? import confirm text Ein Profil mit dem Namen "%1" existiert bereits. Wollen Sie es löschen? @@ -1157,7 +1256,7 @@ Soll die alte Historiedatei gelöscht werden? PrivacySettings - Typing Notification + Send Typing Notifications Tippen anzeigen @@ -1199,13 +1298,13 @@ Soll die alte Historiedatei gelöscht werden? QObject - + Update The title of a message box Update - + An update is available, do you want to download it now? It will be installed when qTox restarts. Ein Update steht zur Verfügung, soll es heruntergeladen werden? @@ -1219,7 +1318,7 @@ Es wird beim Neustart von qTox installiert. Starts new instance and loads specified profile. - Startet eine neue Instanz und lädt das festgelegte Profil. + Startet eine neue Instanz und lädt das festgelegte Profil. @@ -1229,7 +1328,7 @@ Es wird beim Neustart von qTox installiert. Default - Default + Standard @@ -1291,6 +1390,11 @@ Es wird beim Neustart von qTox installiert. Default message in Tox URI friend requests. Write something appropriate! Lass uns toxen! + + + Incoming call... + Eingehender Anruf... + SetPasswordDialog @@ -1356,6 +1460,15 @@ Es wird beim Neustart von qTox installiert. Error with the DNS Der DNS Eintrag enthält keine gültige TOX ID + + + + It appears that qTox has to use the old tox1 protocol. +Unfortunately tox1 is not secure. Should it be used anyway? + Es scheint, dass qTox das veraltete tox1-Protokoll nutzen muss. +Dieses Protokoll ist nicht sicher. +Soll es dennoch genutzt werden? + ToxURIDialog @@ -1439,116 +1552,116 @@ Es wird beim Neustart von qTox installiert. Beschäftigt - + Choose a profile Wählen Sie ein Profil - + Please choose which identity to use Wählen Sie die Identität, die benutzt werden soll - + Choose a profile picture Wählen Sie ein Profilbild - - - + + + Error Fehler - + Unable to open this file Kann diese Datei nicht öffnen - + Unable to read this image Kann dieses Bild nicht einlesen - + This image is too big Dieses Bild ist zu groß - + Toxcore failed to start, the application will terminate after you close this message. Tox startet nicht, die Anwendung wird nach Schließen dieses Fensters beendet. - + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. popup text Tox startet nicht mit ihren Proxy-Einstellungen. qTox funktioniert nicht, ändern Sie bitte Ihre Einstellungen und starten qTox neu. - + Add friend Freund hinzufügen - + File transfers Dateiübertragungen - + Settings Einstellungen - + Couldn't request friendship Freundschaftsanfrage fehlgeschlagen - + away contact status Abwesend - + busy contact status Beschäftigt - + offline contact status Offline - + online contact status Online - + %1 is now %2 e.g. "Dubslow is now online" %1 ist nun %2 - + <Unknown> Placeholder when we don't know someone's name in a group chat <Unbekannt> - + %1 has set the title to %2 %1 hat den Titel auf %2 gesetzt - + Message failed to send Senden der Nachricht fehlgeschlagen diff --git a/translations/fr.ts b/translations/fr.ts index 48cc108ea..6e6bba4f2 100644 --- a/translations/fr.ts +++ b/translations/fr.ts @@ -4,10 +4,15 @@ AVForm - + Audio/Video Audio/Vidéo + + + Initializing Camera... + Initialisation de la caméra... + AVSettings @@ -16,12 +21,12 @@ qTox - + Video Settings Options vidéo - + Resolution Résolution @@ -51,22 +56,32 @@ Microphone - + + Rescan audio devices + Redetecter le matériel audio + + + + Filter audio + Filtrer le son + + + Hue Teinte - + Brightness Luminosité - + Saturation Saturation - + Contrast Contraste @@ -116,8 +131,14 @@ qTox needs to use the Tox DNS, but can't do it through a proxy. -Ignore the proxy and connect to the Internet directly ? +Ignore the proxy and connect to the Internet directly? qTox as besoin d'utiliser le DNS Tox, mais ne peut pas le faire avec un proxy. +Ignorer le proxy et se connecter directement à Internet ? + + + qTox needs to use the Tox DNS, but can't do it through a proxy. +Ignore the proxy and connect to the Internet directly ? + qTox as besoin d'utiliser le DNS Tox, mais ne peut pas le faire avec un proxy. Ignorer le proxy et se connecter directement à Internet ? @@ -163,55 +184,134 @@ Ignorer le proxy et se connecter directement à Internet ? La réponse DNS ne contient pas d'ID Tox valide + + AdvancedForm + + + Advanced + Avancé + + + + FULL - very safe, slowest (recommended) + COMPLET - très sûr, le plus lent (recommendé) + + + + NORMAL - almost as safe as FULL, about 20% faster than FULL + NORMAL - presque aussi sûr que COMPLET, à peu près 20% plus rapide que COMPLET + + + + OFF - disables all safety, when something goes wrong your history may be lost, fastest (not recommended) + DESACTIVE - désactive toutes les sécurités, en cas de problème l'historique peut être perdu, le plus rapide (non recommandé) + + + + AdvancedSettings + + + Form + qTox + + + + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTANT NOTE</span></p><p><span style=" color:#ff0000;">Unless you </span><span style=" font-weight:600; color:#ff0000;">really</span><span style=" color:#ff0000;"> know what you are doing, please do </span><span style=" font-weight:600; color:#ff0000;">not</span><span style=" color:#ff0000;"> change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</span></p></body></html> + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">NOTE IMPORTANTE</span></p><p><span style=" color:#ff0000;">A moins de </span><span style=" font-weight:600; color:#ff0000;">vraiment</span><span style=" color:#ff0000;"> savoir ce que vous êtes en train de faire, merci de ne </span><span style=" font-weight:600; color:#ff0000;">pas</span><span style=" color:#ff0000;"> changer quoi que ce soit ici. Tout changement ici peut conduire à des problèmes avec qTox et même la perte de vos données, comme l'historique.</span></p></body></html> + + + + Reset to default settings + Retour aux options par default + + + + History + Historique + + + + <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Synchronous writing to DB</span></a></p></body></html> + Ecriture synchronisée à la BDD + + ChatForm - + Load History... Charger l'historique... - + Send a file Envoyer un fichier - + + + File not read + Fichier non lu + + + + + qTox wasn't able to open %1 + qTox n'as pas pu ouvrir %1 + + + + Bad Idea Mauvaise idée - + + You're trying to send a special (sequential) file, that's not going to work! Vous êtes en train d'essayer d'envoyer un fichier spécial (sequentiel), ça ne fonctionnera pas ! - - %1 calling - %1 appel + + %1 is calling + %1 appelle - + + is typing... + est en train d'écrire... + + + %1 calling + %1 appel + + + %1 stopped calling %1 a arreté l'appel - + Calling to %1 Appel de %1 en cours - + Call rejected Appel rejeté - + + Failed to send file "%1" + Impossible d'envoyer "%1" + + + Call with %1 ended. %2 Appel avec %1 terminé. %2 - + Call duration: Durée de l'appel: @@ -227,104 +327,118 @@ Ignorer le proxy et se connecter directement à Internet ? Core - + Toxing on qTox Je Tox sur qTox - + qTox User Utilisateur de qTox - + Friend is already added Ce contact est déjà dans vos contacts - + Encryption error Erreur de chiffrement - + The .tox file is encrypted, but encryption was not checked, continuing regardless. Le fichier .tox est chiffré, mais le chiffrement n'a pas été activé. Le problème sera ignoré. - + Tox datafile decryption password Mot de passe des données Tox - - - + + + Password error Mot de passe invalide - - + + Failed to setup password. Empty password. Impossible de mettre en place le mot de passe. Le mot de passe est vide. - + Try Again Réessayer - + Change profile Changer de profil - + Reinit current profile Réinitialiser le profil courant - + Wrong password has been entered Un mauvais mot de passe a été entré - + History Log decryption password Mot de passe de l'historique - + + Your history is encrypted with different password. +Do you want to try another password? + Votre historique est chiffré avec un mot de passe différent. +Voulez-vous essayer un autre mot de passe? + + + + History + Historique + + + + Due to incorret password history will be disabled. + L'historique va être désactivé à cause d''un mauvais mot de passe. + + Your history is encrypted with different password Do you want to try another password? - Votre historique de conversation est chiffré avec un autre mot de passe + Votre historique de conversation est chiffré avec un autre mot de passe Voulez-vous essayer un mot de passe différent? - + Encrypted log Historique chiffré - Loggin - Historique + Historique - Due to incorret password logging will be disabled - Due à l'utilisation d'un mauvais mot de passe, l'historique sera désactivé + Due à l'utilisation d'un mauvais mot de passe, l'historique sera désactivé - + NO Password PAS de mot de passe - + Will be saved without encryption! L'historique sera sauvegardé sans être chiffré ! @@ -332,25 +446,25 @@ Voulez-vous essayer un mot de passe différent? 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. - + ETA ETA @@ -436,14 +550,20 @@ Voulez-vous essayer un mot de passe différent? Automatiquement accepter les fichiers de ce contact - + User alias Alias utilisateur - + + You can also set this by clicking the chat form name. +Alias: + Vous pouvez aussi modifier ceci en cliquant sur le nom dans le chat. +Alias: + + Alias: - Alias: + Alias: Manually accept files from this friend @@ -476,30 +596,30 @@ Voulez-vous essayer un mot de passe différent? GeneralForm - + General Général - - + + None Aucun - + Choose an auto accept directory popup title Choisir un dossier de téléchargement - + Call active popup title Appel en cours - + You can't disconnect while a call is active! popup text Vous ne pouvez pas vous déconnecter avec un appel en cours ! @@ -538,27 +658,27 @@ Voulez-vous essayer un mot de passe différent? Rendre Tox portable - + Start in tray Démarrer dans la barre d'état - + Close to tray Fermer dans la barre d'état - + Minimize to tray Minimiser dans la barre d'état - + Show contacts' status changes Montrer les changements de status des contacts - + Provided in minutes En minutes @@ -567,17 +687,17 @@ Voulez-vous essayer un mot de passe différent? Se rendre absent après (0 pour désactiver): - + Set to 0 to disable Mettre à 0 pour désactiver - + minutes minutes - + Theme Thème @@ -611,19 +731,39 @@ Voulez-vous essayer un mot de passe différent? Style: - + Enable UDP (recommended) Text on checkbox to disable UDP Activer UDP (recommandé) - + + Proxy type + Type de proxy + + + + None + Aucun + + + + SOCKS5 + SOCKS5 + + + + HTTP + HTTP + + + Reconnect reconnect button Se reconnecter - + Connection Settings Options de réseau @@ -633,79 +773,110 @@ Voulez-vous essayer un mot de passe différent? Traduction - + + System tray integration + Intégration avec la barre d'état + + + + Show system tray icon + Montrer l'icône de barre d'état + + + + Light icon + Icône claire + + + Check for updates on startup (unstable) Chercher des mises à jour au démarrage (instable) - + Focus qTox when a message is received Montrer la fênetre qTox quand un message est reçu - + Faux offline messaging Retransmettre les messages en cas d'échec - + + Compact layout + Agencement compact + + + Auto away after (0 to disable) Indisponible après (0 désactive) - + + You can set this on a per-friend basis by right clicking them. + autoaccept cb tooltip + Vous pouvez regler ceci pour chaque contact avec un clique-droit sur leur nom. + + + Autoaccept files Auto-accepter les fichiers - + Save files in Sauvegarder les fichiers vers - + PushButton PushButton - + Use emoticons Utiliser les émoticones - + Smiley Pack Text on smiley pack label Pack de smileys - + Style Style - + + Theme color + Couleur du thème + + + Emoticon size Taille d'émoticone - + px px - + Timestamp format Format de timestamp - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 Activer IPv6 (recommandé) - + Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary. force tcp checkbox tooltip Désactiver ceci permet par exemple d'utiliser Tox à travers Tor, mais ce n'est à utiliser que si nécessaire, car cela ralenti le réseau Tox. @@ -721,18 +892,17 @@ Voulez-vous essayer un mot de passe différent? Désactiver UDP (non recommandé) - Use proxy (SOCKS5) - Utiliser un proxy (SOCKS5) + Utiliser un proxy (SOCKS5) - + Address Text on proxy addr label Addresse - + Port Text on proxy port label Port @@ -741,53 +911,69 @@ Voulez-vous essayer un mot de passe différent? GenericChatForm - + Send message Envoyer un message - + Smileys Émoticônes - + Send file(s) Envoyer des fichiers - - - Audio call - Appel audio - - - - Video call - Appel vidéo - - - - Toggle speakers volume - Couper les haut-parleurs - - Toggle microphone - Couper le microphone + Audio call: RED means you're on a call + Appel audio: ROUGE veut dire en cours d'appel - - + + Video call: RED means you're on a call + Appel vidéo: ROUGE veut dire en cours d'appel + + + + Toggle speakers volume: RED is OFF + Changer volume des haut-parleurs: ROUGE veut dire ETEINT + + + + Toggle microphone: RED is OFF + Changer état du micrphone: ROUGE veut dire ETEINT + + + Audio call + Appel audio + + + Video call + Appel vidéo + + + Toggle speakers volume + Couper les haut-parleurs + + + Toggle microphone + Couper le microphone + + + + Save chat log Sauvegarder l'historique de conversation - + Clear displayed messages Effacer les messages affichés - + Cleared Effacé @@ -801,7 +987,7 @@ Voulez-vous essayer un mot de passe différent? %1 personnes - + %1 users in chat %1 personnes @@ -809,23 +995,40 @@ Voulez-vous essayer un mot de passe différent? GroupWidget - - + + %1 users in chat %1 personnes - - + + 0 users in chat Le groupe est vide - + + Set title... + Changer le titre... + + + Quit group Menu to quit a groupchat Quitter le groupe + + + Group title + Titre du groupe + + + + You can also set this by clicking the chat form name. +Title: + Vous pouvez aussi changer ceci en cliquant sur le nom du chat. +Titre: + IdentityForm @@ -916,42 +1119,49 @@ Voulez-vous essayer un mot de passe différent? - Are you sure you want to delete this profile? + Are you sure you want to delete this profile? +Associated friend information and chat logs will be deleted as well. deletion confirmation text - Êtes-vous sur de vouloir supprimer ce profil ? + Êtes-vous sur de vouloir supprimer ce profil ? +Les informations des contacts et historique associés vont être perdu. - + Are you sure you want to delete this profile? + deletion confirmation text + Êtes-vous sur de vouloir supprimer ce profil ? + + + Import profile import dialog title Importer un profil - + Tox save file (*.tox) import dialog filter Fichier sauvegarde Tox (*.tox) - + Ignoring non-Tox file popup title Fichier incompatible avec Tox ignoré - + Warning: you've chosen a file that is not a Tox save file; ignoring. popup text Attention: Vous avez sélectionné un fichier qui n'est pas une sauvegarde Tox, il sera ignoré. - + Profile already exists import confirm title Ce profil existe déjà - + A profile named "%1" already exists. Do you want to erase it? import confirm text Un profil appelé "%1" existe déjà. Voulez-vous le supprimer ? @@ -1091,27 +1301,27 @@ Voulez-vous essayer un mot de passe différent? Votre status - + Add friends Ajouter des contacts - + Create a group chat Créer un groupe - + View completed file transfers Voir les transfers de fichiers terminés - + Change your settings Changer les options - + Close Fermer @@ -1136,12 +1346,12 @@ Voulez-vous essayer un mot de passe différent? Vie privée - + Encrypted log Historique chiffré - + You already have history log file encrypted with different password Do you want to delete old history file? Vous avez déjà un historique chiffré avec un autre mot de passe @@ -1155,9 +1365,13 @@ Voulez vous supprimer l'ancien historique ? qTox - Typing Notification - Notifications d'écriture + Notifications d'écriture + + + + Send Typing Notifications + Envoyer des notifications d'écriture @@ -1179,6 +1393,21 @@ Voulez vous supprimer l'ancien historique ? Encrypt History Chiffrer l'historique + + + Nospam + Antispam + + + + HHHHHHHH + HHHHHHHH + + + + Generate random nospam + Generer un antispam aléatoire + QObject @@ -1223,16 +1452,22 @@ Voulez vous supprimer l'ancien historique ? Je souhaiterais vous ajouter à mes contacts - + Update The title of a message box Mise à jour - + + An update is available, do you want to download it now? +It will be installed when qTox restarts. + Une mise à jour est disponible, voulez vous la télécharger maintenant? +Elle sera installée au prochain démarrage de qTox. + + An update is available, do you want to download it now ? It will be installed when qTox restarts. - Une mise à jour est disponible, voulez vous la télécharger maintenant ? + Une mise à jour est disponible, voulez vous la télécharger maintenant ? Elle sera installée au prochain démarrage de qTox. @@ -1240,6 +1475,41 @@ Elle sera installée au prochain démarrage de qTox. Tox URI to parse URL Tox à utiliser + + + Starts new instance and loads specified profile. + Démarrer une nouvelle instance et charger le profil spécifié. + + + + profile + profil + + + + Default + Defaut + + + + Blue + Bleu + + + + Olive + Olive + + + + Red + Rouge + + + + Violet + Violet + SetPasswordDialog @@ -1309,6 +1579,14 @@ Elle sera installée au prochain démarrage de qTox. Error with the DNS La réponse DNS ne contient pas d'ID Tox valide + + + + It appears that qTox has to use the old tox1 protocol. +Unfortunately tox1 is not secure. Should it be used anyway? + Il semble que qTox a besoin d'utiliser l'ancien protocole tox1. +Cepandant tox1 n'est pas sécurisé. Voulez-vous l'utiliser tout de même? + ToxURIDialog @@ -1319,9 +1597,13 @@ Elle sera installée au prochain démarrage de qTox. Ajouter un contact - Do you want to add %1 as a friend ? - Voulez-vous ajouter %1 à vos contacts ? + Voulez-vous ajouter %1 à vos contacts ? + + + + Do you want to add %1 as a friend? + Voulez-vous ajouter %1 à vos contacts? @@ -1361,154 +1643,158 @@ Elle sera installée au prochain démarrage de qTox. occupé - + Online Connecté - + Away Absent - + Busy Occupé - + &Quit &Quitter - Change status to: - Changer le status par: + Changer le status par: - + Online Button to set your status to 'Online' Connecté - + Away Button to set your status to 'Away' Absent - + Busy Button to set your status to 'Busy' Occupé - + Choose a profile Choisir un profil - + Please choose which identity to use Merci de choisir l'identité à utiliser - + Choose a profile picture Choisissez une image de profil - - - + + + Error Erreur - + Unable to open this file Impossible d'ouvrir ce fichier - + Unable to read this image Impossible de lire cette image - + This image is too big Cette image est trop volumineuse - + Toxcore failed to start, the application will terminate after you close this message. ToxCore n'as pas pu démarrer correctement, l'application va s'arrêter quand vous fermerez cette alerte. - + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. popup text ToxCore n'as pas pu démarrer avec ces paramètres proxy. Merci de modifier ou désactiver vos paramètres et redémarrer l'application. - + Add friend Ajouter un contact - + File transfers Transfers - + Settings Options - + Couldn't request friendship Impossible d'envoyer la demande de contact - + away contact status absent - + busy contact status occupé - + offline contact status hors ligne - + online contact status connecté - + %1 is now %2 e.g. "Dubslow is now online" %1 est maintenant %2 - + <Unknown> Placeholder when we don't know someone's name in a group chat <Inconnu> - + + %1 has set the title to %2 + %1 a changé le titre en %2 + + + Message failed to send Le message n'as pu être envoyé diff --git a/translations/i18n.pri b/translations/i18n.pri index d76b12b0c..d13f328e2 100644 --- a/translations/i18n.pri +++ b/translations/i18n.pri @@ -6,6 +6,7 @@ TRANSLATIONS = translations/es.ts \ translations/fi.ts \ translations/fr.ts \ translations/it.ts \ + translations/lt.ts \ translations/mannol.ts \ translations/pirate.ts \ translations/pl.ts \ diff --git a/translations/it.ts b/translations/it.ts index 116745236..d8c436cda 100644 --- a/translations/it.ts +++ b/translations/it.ts @@ -8,16 +8,21 @@ Audio/Video Audio/Video + + + Initializing Camera... + Inizializzazione Webcam... + AVSettings - + Video Settings Impostazioni Video - + Resolution Risoluzione @@ -36,43 +41,76 @@ Audio Settings Impostazioni Audio + + + Use slider to set volume of your speakers. +WARNING: slider is not supposed to work yet. + Usa lo slider per impostare il volume degli altoparlanti. +ATTENZIONE: lo slider è ancora non funzionante. + + Use slider to set volume of your microphone. +WARNING: slider is not supposed to work yet. + Usa lo slider per impostare il volume del microfono. +ATTENZIONE: lo slider è ancora non funzionante. + + + Playback device Dispositivo di output - + Capture device Dispositivo di input - + Rescan audio devices Cerca dispositivi audio - + Filter audio Filtra audio - + + Filter sound from your microphone, so that people hearing you would get better sound. + Filtra l'audio del microfono, così le persone che chiami ti sentiranno meglio. + + + + + Set resolution of your camera. +The higher values, the better video quality your friends may get. +Note though that with better video quality there is needed better internet connection. +Sometimes your connection may not be good enough to handle higher video quality, +which may lead to problems with video calls. + Imposta la risoluzione della webcam. +Più alto il valore, migliore sarà la qualità del video che i tuoi contatti riceveranno. +NOTA: più alta è la qualità video, più veloce deve essere la connessione ad internet. +Può capitare che la tua connessione ad internet non sia abbastanza veloce per gestire +qualità video elevate, questo può causare problemi con le chiamate video. + + + Hue Colore - + Brightness Luminoistà - + Saturation Saturazione - + Contrast Contrasto @@ -141,17 +179,17 @@ Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox?Avanzate - + FULL - very safe, slowest (recommended) COMPLETO - molto sicuro; lento (consigliato) - + NORMAL - almost as safe as FULL, about 20% faster than FULL NORMALE - quasi sicuro come COMPLETO, ma circa 20% più veloce - + OFF - disables all safety, when something goes wrong your history may be lost, fastest (not recommended) DISABILITATO - disabilita tutta la sicurezza, quando qualcosa va male i log possono essere persi; veloce (non consigliato) @@ -165,21 +203,32 @@ Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox? + 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 + + + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTANT NOTE</span></p><p><span style=" color:#ff0000;">Unless you </span><span style=" font-weight:600; color:#ff0000;">really</span><span style=" color:#ff0000;"> know what you are doing, please do </span><span style=" font-weight:600; color:#ff0000;">not</span><span style=" color:#ff0000;"> change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</span></p></body></html> <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTATNTE</span></p><p><span style=" color:#ff0000;">Se non sai </span><span style=" font-weight:600; color:#ff0000;">esattamente</span><span style=" color:#ff0000;"> cosa stai facendo, </span><span style=" font-weight:600; color:#ff0000;">non</span><span style=" color:#ff0000;"> cambiare niente. Le modifiche possono portare a problemi nel funzionamento di qTox e/o alla perdita di dati (per esempio i log delle chat).</span></p></body></html> - + Reset to default settings Reimposta impostazioni di default - + History Chat Log - + <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Synchronous writing to DB</span></a></p></body></html> <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Scritture sincrone sul DB @@ -187,72 +236,76 @@ Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox? ChatForm - + Load History... Carica log... - + Send a file Invia un file - + + File not read Impossibile leggere il file - + + qTox wasn't able to open %1 qTox non è riuscito ad aprire %1 - + + Bad Idea Pessima idea - + + You're trying to send a special (sequential) file, that's not going to work! Stai cercando di inviare un file speciale (sequenziale), questo non funzionerà! - + %1 is calling %1 ti sta chiamando - + %1 stopped calling %1 ha fermato la chiamata - + Calling to %1 Stai chiamando %1 - + Failed to send file "%1" Invio del file "%1" fallito - + Call with %1 ended. %2 Chiamata con %1 terminata. %2 - + Call duration: Durata chiamata: - + is typing... sta scrivendo... - + Call rejected Chiamata rifiutata @@ -268,105 +321,105 @@ Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox? Core - + Toxing on qTox Toxing on qTox - + qTox User qTox User - + Friend is already added Questo contatto è già presente nella tua lista - + Encryption error Errore crittografia - + The .tox file is encrypted, but encryption was not checked, continuing regardless. Il Tox datafile è criptato, ma la crittografia non è abilitata nelle impostazioni. Continuo ignorando le impostazioni. - + Tox datafile decryption password Password per decriptare il Tox datafile - - - + + + Password error Errore password - - + + Failed to setup password. Empty password. Impossibile impostare la password. Password vuota. - + Try Again Riprova - + Change profile Cambia profilo - + Reinit current profile Reinizializza il profilo corrente - + Wrong password has been entered È stata inserita una password sbagliata - + History Log decryption password Password per decriptare i log - + Encrypted log Log criptato - + Your history is encrypted with different password. Do you want to try another password? I log delle chat sono criptati con una password diversa. Vuoi provare ad inserire un'altra password? - + History Chat Log - + Due to incorret password history will be disabled. Password errata, i log delle chat non saranno caricati. - + NO Password Nessuna password - + Will be saved without encryption! Il Tox datafile sarà salvato senza password! @@ -419,34 +472,34 @@ Vuoi provare ad inserire un'altra password? FriendRequestDialog - + Friend request Title of the window to aceept/deny a friend request Richiesta d'amicizia - + Someone wants to make friends with you Qualcuno vuole chattare con te - + User ID: ID Utente: - + Friend request message: Messaggio della richiesta d'amicizia: - + Accept Accept a friend request Accetta - + Reject Reject a friend request Rifiuta @@ -510,25 +563,25 @@ Soprannome: Generale - - + + None Nessuno - + Choose an auto accept directory popup title Scegli dove salvare i files accettati automaticamente - + Call active popup title Chiamata in corso - + You can't disconnect while a call is active! popup text Non puoi disconnetterti mentre c'è una chiamata in corso! @@ -548,210 +601,291 @@ Soprannome: La traduzione potrebbe non essere caricata fino al prossimo riavvio di qTox. - + + Language: + Lingua: + + + + System tray + Barra di sistema + + + Show system tray icon Mostra icona nella traybar - + Light icon - Icona brillante + Usa icona brillante - + + Enable light tray icon. + toolTip for light icon setting + Abilita icona brillante nella trybar. + + + + qTox will start minimized in tray. + toolTip for Start in tray setting + qTox sarà avviato minimizzato nella traybar. + + + + After pressing close (X) qTox will minimize to tray, +instead of closing itself. + toolTip for close to tray setting + Premendo l'icona "chiudi" (X) qTox sarà minimizzato +nella traybar invece che essere chiuso. + + + + After pressing minimize (_) qTox will minimize itself to tray, +instead of system taskbar. + toolTip for minimize to tray setting + Premendo l'icona "minimizza" (_) qTox sarà minimizzato +nella traybar invece che nella taskbar. + + + + Check for updates on startup + Controlla aggiornamenti all'avvio + + + + Save to: + Salva in: + + + + + Set where files will be saved. + Scegli dove salvare i files ricevuti. + + + Set to 0 to disable Imposta 0 per disabilitare - + + Your status is changed to Away after set time of inactivity. + Il tuo stato sarà cambaito in "Assente" dopo l'ammontare di tempo impostato. + + + + Auto away after (0 to disable): + Mostra come assente dopo (0 per disabilitare): + + + + Chat + Chat + + + + Always notify about new messages in groupchats. + toolTip for Group chat always notify + Mostra sempre le notifiche per i nuovi messaggi nelle chat di gruppo. + + + + Group chats always notify + Usa notifiche per chat di gruppo + + + + On new message: + Quando arriva un nuovo messaggio: + + + + Show qTox's window when you receive new message. + tooltip for Show window setting + Porta la finestra di qTox in primo piano quando arriva un nuovo messaggio. + + + + Show window + Mostra finestra + + + + Focus qTox when you receive message. + toolTip for Show window setting + Dai il focus alla finestra di qTox quando arriva un nuovo messaggio. + + + + Focus window + Dai il focus alla finestra + + + + Messages you are trying to send to your friends when they are not online +will be sent to them when they will appear online to you. + toolTip for Faux offline messaging setting + I messaggi che invii ai contatti offline saranno inviati quando appaiono online. + + + + Your contact list will be shown in compact mode. +qTox's restart needed. + toolTip for compact layout setting + La lista contatti sarà visualizzata in modalità compatta. +Richiede riavvio. + + + + Compact contact list + Usa lista contatti compatta + + + + Smiley Pack: + Text on smiley pack label + Emoticons: + + + + Emoticon size: + Dimensione: + + + + Style: + Stile: + + + + Theme color: + Colore tema: + + + + Timestamp format: + Formato data/ora: + + + Connection Settings Impostazioni Connessione - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 Abilita IPv6 (consigliato) - - 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 + + Proxy type: + Proxy: - - Translation - Lingua + + Address: + Text on proxy addr label + Indirizzo: - - Make Tox portable - Rendi qTox portabile - - - - System tray integration - Integrazione Traybar - - - + Start in tray Avvia nella traybar - + Close to tray Chiudi nella traybar - + Minimize to tray Minimizza nella traybar - + Show contacts' status changes Mostra quando i contatti cambiano stato - - Check for updates on startup (unstable) - Controlla aggiornamenti all'avvio (unstable) - - - - Focus qTox when a message is received - Dai il focus a qTox quando arriva un messaggio - - - + Faux offline messaging - Falsi messaggi offline + Usa falsi messaggi offline - - Provided in minutes - Espresso in minuti - - - - Auto away after (0 to disable) - Imposta assenza dopo - - - + minutes minuti - + You can set this on a per-friend basis by right clicking them. autoaccept cb tooltip Puoi impostare questa preferenza per ogni singolo contatto usando il click destro sul suo nome. - + Autoaccept files Accetta automaticamente i trasferimenti di files - - Save files in - Salva i files in - - - + PushButton Sfoglia - + Theme Impostazioni Tema - + Use emoticons Usa emoticons - - Smiley Pack - Text on smiley pack label - Emoticons - - - - Style - Stile - - - - Theme color - Colore - - - - Emoticon size - Dimensione emoticons - - - + px px - - Timestamp format - Formato data/ora - - - + Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary. force tcp checkbox tooltip Disabilitando questo sarà possibile usare qTox con Tor. Tuttavia verrà aggiunto carico alla rete Tox, quindi disabilitare solo se necessario. - + Enable UDP (recommended) Text on checkbox to disable UDP Abilita UDP (consigliato) - - Proxy type - Proxy - - - + None Nessuno - + SOCKS5 SOCKS 5 - + HTTP HTTP - - Address - Text on proxy addr label - IP - - - + Port Text on proxy port label Porta - + Reconnect reconnect button Riconnetti @@ -760,53 +894,53 @@ Soprannome: GenericChatForm - + Send message Invia messaggio - + Smileys Emoticons - + Send file(s) Invia file(s) - + Audio call: RED means you're on a call Chiamata audio: ROSSO significa che la chiamata è in corso - + Video call: RED means you're on a call Videochiamata: ROSSO significa che la chiamata è in corso - + Toggle speakers volume: RED is OFF Imposta volume altoparlanti: ROSSO è SPENTO - + Toggle microphone: RED is OFF Imposta microfono: ROSSO è SPENTO - - + + Save chat log Salva il log della chat - + Clear displayed messages Rimuovi messaggi visualizzati - + Cleared Pulito @@ -871,87 +1005,87 @@ Nome gruppo: Identità - + Call active popup title Chiamata in corso - + You can't switch profiles while a call is active! popup text Non puoi cambiare profilo durante una chiamata! - + Rename "%1" renaming a profile Rinomina "%1" - + Profile already exists rename confirm title Profilo già esistente - + A profile named "%1" already exists. Do you want to erase it? rename confirm text Un profilo chiamato "%1" esiste già. Vuoi sovrascriverlo? - + Export profile save dialog title Esporta profilo - + Tox save file (*.tox) save dialog filter Tox save file (*.tox) - + Failed to remove file Impossibile rimuovere il file - + The file you chose to overwrite could not be removed first. Il file che hai scelto di sovrascrivere non può essere prima rimosso. - + Failed to copy file Impossibile copiare il file - + The file you chose could not be written to. Il file che hai scelto non può essere copiato. - + Profile currently loaded current profile deletion warning title Profilo attualmente in uso - + This profile is currently in use. Please load a different profile before deleting this one. current profile deletion warning text Questo profilo è attualmente in uso. Per favore carica un profilo differente prima di eliminare questo. - + Deletion imminent! deletion confirmation title Eliminazione imminente! - + Are you sure you want to delete this profile? Associated friend information and chat logs will be deleted as well. deletion confirmation text @@ -959,37 +1093,37 @@ Associated friend information and chat logs will be deleted as well. I contatti e i log delle chat associati ad esso, saranno eliminati. - + Import profile import dialog title Importa profilo - + Tox save file (*.tox) import dialog filter Tox save file (*.tox) - + Ignoring non-Tox file popup title File ignorato - + Warning: you've chosen a file that is not a Tox save file; ignoring. popup text Attenzione: hai scelto un file che non contiente un profilo Tox.\nQuesto file verrà ignorato. - + Profile already exists import confirm title Profilo già esistente - + A profile named "%1" already exists. Do you want to erase it? import confirm text Un profilo chiamato "%1" esiste già. Vuoi sovrascriverlo? @@ -1018,65 +1152,104 @@ I contatti e i log delle chat associati ad esso, saranno eliminati.Tox ID - + + This bunch of characters tells other Tox clients how to contact you. +Share it with your friends to communicate. + Tox ID tooltip + Qesto mucchio di caratteri serve agli altri client Tox per contattarti. +Condivilo con chi vuoi comunicare. + + + Your Tox ID (click to copy) (clicca qui per copiare) - + Profiles Gestione Profili - + Available profiles: Profili disponibili: - + + Currently selected profile. + toolTip for currently set profile + Profilo corrente. + + + + Load selected profile and switch to it. + tooltip for loading profile button + Carica il profilo selezionato. + + + Load load profile button Carica - - - Switching profiles is disabled during calls - tooltip - Non puoi cambiare profilo durante una chiamata - - - + Rename rename profile button Rinomina - + + Rename selected profile. + tooltip for renaming profile button + Rinomina il profilo selezionato. + + + Export export profile button Esporta - + + Allows you to export your Tox profile to a file. +Profile does not contain your history. + tooltip for profile exporting button + Esporta il profilo corrente in un file. +I profili non contengono la cronologia messaggi. + + + + Delete selected profile. + delete profile button tooltip + Elimina il profilo selezionato. + + + Delete delete profile button Elimina - - This is useful to remain safe on public computers - delete profile button tooltip - Utile per preservare la tua sicurezza su computer pubblici - - - + Import a profile import profile button Importa profilo - + + Import Tox profile from a .tox file. + tooltip for importing profile button + Importa un profilo da un file ".tox". + + + + Create new Tox ID and switch to it. + tooltip for creating new Tox ID button + Crea un nuovo profilo (Tox ID) e iniza subito ad usarlo. + + + New Tox ID new profile button Nuovo profilo @@ -1176,6 +1349,12 @@ Vuoi eliminare il vecchio file? PrivacySettings + + + Your friends will be able to see when you are typing. + tooltip for typing notifications setting + I tuoi contatti potranno vedere se stai digitando un messaggio. + Send Typing Notifications @@ -1187,32 +1366,52 @@ Vuoi eliminare il vecchio file? Salva log delle chat (unstable) - + + History is available in qTox, but we cannot promise that one day it will not dissapear without a trace :-/ +We will try our best to make sure that this will not happen though! + toolTip for history setting + La cronologia messaggi è disponibile in qTox, ma non possiamo promettere che un giorno non sparisca senza lasciare tracce :-/ +Faremo del nostro meglio per assicurarci che questo non accada! + + + Encryption Crittografiia - + Encrypt Tox datafile Cripta Tox datafile - + Encrypt History Cripta log delle chat - + Nospam Nospam - + + Nospam is part of your Tox ID. +It is there to help you change your Tox ID when you feel like you are getting too much spam friend requests. +When you change nospam, your current contacts still can communicate with you, +but new contacts need to know your new Tox ID to be able to add you. + toolTip for nospam + Il valore "nospam" fa parte del tuo Tox ID. +Lo si deve modificare quando si ritiene che si stanno ricevendo troppe richiste d'amicizia da persone sconosciute. +Quando modifichi il valore "nospam", i contatti che hai già aggiunto potranno continuare a comunicare con te, +ma i nuovi contatti dovranno conoscere il tuo nuovo Tox ID per aggiungerti. + + + HHHHHHHH HHHHHHHH - + Generate random nospam Genera valore nospam casuale @@ -1260,13 +1459,13 @@ Vuoi eliminare il vecchio file? %1.tox è stato importato con successo - + Update The title of a message box Nuova versione - + An update is available, do you want to download it now? It will be installed when qTox restarts. È disponibile una nuova versione di qTox, vuoi scaricarla adesso? @@ -1312,6 +1511,11 @@ Verrà installata al riavvio del programma. Violet Viola + + + Incoming call... + Chiamata in arrivo... + SetPasswordDialog @@ -1446,134 +1650,134 @@ Usare comunque il protocollo tox1? &Esci - + 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 Scegli un profilo - + Please choose which identity to use Per favore scegli quale identità usare - + 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. - + Add friend Aggiungi contatto - + File transfers Files trasferiti - + Settings Impostazioni - + Couldn't request friendship Impossibile inviare la richiesta d'amicizia - + away contact status assente - + busy contact status occupato - + offline contact status offline - + online contact status online - + %1 is now %2 e.g. "Dubslow is now online" %1 è ora %2 - + <Unknown> Placeholder when we don't know someone's name in a group chat <Sconosciuto> - + %1 has set the title to %2 %1 ha impostato il titolo in %2 - + Message failed to send Impossibile inviare il messaggio diff --git a/translations/lt.ts b/translations/lt.ts new file mode 100644 index 000000000..c907a1ae7 --- /dev/null +++ b/translations/lt.ts @@ -0,0 +1,1579 @@ + + + + + AVForm + + + Audio/Video + Garsas ir vaizdas + + + + AVSettings + + + Audio Settings + Garso įrenginiai + + + + Microphone + Įrašymas + + + + Playback + Išvestis + + + + Playback device + Išvesties įrenginys + + + + Capture device + Įrašymo įrenginys + + + + Rescan audio devices + Aptikti garso įrenginius iš naujo + + + + Filter audio + Filtruoti garso signalą + + + + Video Settings + Vaizdo nustatymai + + + + Resolution + Raiška + + + + Hue + Atspalviai + + + + Brightness + Ryškumas + + + + Saturation + Sodrumas + + + + Contrast + Kontrastas + + + + AddFriendForm + + + Add Friends + Pridėti kontaktą + + + + Tox ID + Tox ID of the person you're sending a friend request to + Tox ID + + + + Message + The message you send in friend requests + Prisistatymo žinutė + + + + Send friend request + Siųsti užklausą + + + + Tox me maybe? + Default message in friend requests if the field is left blank. Write something appropriate! + Gal susirašinėjam per Tox? + + + + Please fill in a valid Tox ID + Tox ID of the friend you're sending a friend request to + Įveskite tinkamą kontakto Tox ID + + + + You can't add yourself as a friend! + When trying to add your own Tox ID as friend + Negalite naudoti savo Tox ID! + + + + qTox needs to use the Tox DNS, but can't do it through a proxy. +Ignore the proxy and connect to the Internet directly? + qTox naudoja Tox DNS, bet tai neįmanoma per įgaliotąjį (proxy) serverį. +Ignoruoti įgaliotąjį serverį ir jungtis prie interneto tiesiogiai? + + + + This Tox ID does not exist + DNS error + Tokio Tox ID nėra + + + + AdvancedForm + + + Advanced + Kita + + + + FULL - very safe, slowest (recommended) + VISADA – saugiai, bet lėtai (rekomenduojama) + + + + NORMAL - almost as safe as FULL, about 20% faster than FULL + ĮPRASTAI – beveik taip pat saugiai, bet 20 % greičiau + + + + OFF - disables all safety, when something goes wrong your history may be lost, fastest (not recommended) + NELAUKTI – apsauga išjungta: kilus problemoms gali dingti pokalbių žurnalas – greičiausia (nerekomenduojama) + + + + AdvancedSettings + + + Form + Forma + + + + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTANT NOTE</span></p><p><span style=" color:#ff0000;">Unless you </span><span style=" font-weight:600; color:#ff0000;">really</span><span style=" color:#ff0000;"> know what you are doing, please do </span><span style=" font-weight:600; color:#ff0000;">not</span><span style=" color:#ff0000;"> change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</span></p></body></html> + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">SVARBUS PRANEŠIMAS</span></p><p><span style=" color:#ff0000;">Nebent </span><span style=" font-weight:600; color:#ff0000;">tikrai</span><span style=" color:#ff0000;"> išmanote kaip elgtis, </span><span style=" font-weight:600; color:#ff0000;">nieko</span><span style=" color:#ff0000;"> čia nekeiskite. Pakeitimai gali sukelti problemų naudotis qTox bei net duomenų (pvz., pokalbių žurnalo) praradimą.</span></p></body></html> + + + + Reset to default settings + Atstatyti pradinius nustatymus + + + + History + Pokalbių žurnalas + + + + <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Synchronous writing to DB</span></a></p></body></html> + <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Laukti, kol pildoma duomenų bazė</span></a></p></body></html> + + + + ChatForm + + + Load History... + Įkelti pokalbių žurnalą... + + + + Send a file + Siųsti failą + + + + File not read + Failas neperskaitomas + + + + qTox wasn't able to open %1 + qTox nepavyko atidaryti %1 + + + + Bad Idea + Bloga mintis + + + + You're trying to send a special (sequential) file, that's not going to work! + Bandote siųsti ypatingą (nuoseklųjį) failą, tai nepavyks! + + + + %1 is calling + %1 skambina + + + + %1 stopped calling + %1 nutraukė skambutį + + + + Calling to %1 + Skambiname: %1 + + + + Call rejected + Skambutis atmestas + + + + Failed to send file "%1" + Nepavyko nusiųsti failo „%1“ + + + + Call with %1 ended. %2 + Pokalbis su %1 baigėsi. %2 + + + + Call duration: + Pokalbio trukmė: + + + + is typing... + rašo žinutę... + + + + ChatTextEdit + + + Type your message here... + Įveskite čia savo žinutę... + + + + Core + + + Toxing on qTox + Naudoju qTox + + + + qTox User + qTox naudotojas + + + + Friend is already added + Toks kontaktas jau pridėtas + + + + Encryption error + Šifro klaida + + + + The .tox file is encrypted, but encryption was not checked, continuing regardless. + .tox failas užšifruotas: nors šifras nepatikrintas, tęsiame darbą. + + + + Tox datafile decryption password + Slaptažodis Tox duomenų failui iššifruoti + + + + + + Password error + Slaptažodis netinka + + + + + Failed to setup password. +Empty password. + Nepavyko nustatyti slaptažodžio. +Slaptažodis tuščias. + + + + Try Again + Bandykite dar kartą + + + + Change profile + Pakeisti profilį + + + + Reinit current profile + Išvalyti dabartinį profilį + + + + Wrong password has been entered + Įvestas slaptažodis netinka + + + + History Log decryption password + Pokalbių žurnalo šifro slaptažodis + + + + Encrypted log + Žurnalas užšifruotas + + + + Your history is encrypted with different password. +Do you want to try another password? + Jūsų pokalbių žurnalas užšifruotas kitu slaptažodžiu. +Norite išmėginti kitą slaptažodį? + + + + History + Žurnalas + + + + Due to incorret password history will be disabled. + Dėl netinkamo slaptažodžio pokalbių žurnalas išjungtas. + + + + NO Password + NĖRA slaptažodžio + + + + Will be saved without encryption! + Bus saugojama be šifro! + + + + FileTransferInstance + + + Save a file + Title of the file saving dialog + Išsaugoti failą + + + + Location not writable + Title of permissions popup + Įrašyti failo čia neleidžiama + + + + You do not have permission to write that location. Choose another, or cancel the save dialog. + text of permissions popup + Nėra teisių įrašyti failą šioje vietoje. Bandykite įrašyti kitur arba atšaukite dialogo langą. + + + + ETA + Liko + + + + FilesForm + + + Transfered Files + "Headline" of the window + Baigti siųsti failai + + + + Downloads + Parsiųsti + + + + Uploads + Nusiųsti + + + + FriendRequestDialog + + + Friend request + Title of the window to aceept/deny a friend request + Kontakto užklausa + + + + Someone wants to make friends with you + Kažkas nori su Jumis bendrauti + + + + User ID: + Naudojo ID: + + + + Friend request message: + Prisistatymo žinutė: + + + + Accept + Accept a friend request + Priimti kontaktą + + + + Reject + Reject a friend request + Atmesti kontaktą + + + + FriendWidget + + + Invite to group + Menu to invite a friend to a groupchat + Pakviesti į grupės pokalbį + + + + Copy friend ID + Menu to copy the Tox ID of that friend + Nukopijuoti kontakto ID + + + + Set alias... + Nustatyti slapyvardį... + + + + Auto accept files from this friend + context menu entry + Automatiškai priimti failus iš šio kontakto + + + + Remove friend + Menu to remove the friend from our friendlist + Pašalinti kontaktą + + + + Choose an auto accept directory + popup title + Pasirinkite priimamų failų katalogą + + + + User alias + Naudotojo slapyvardis + + + + You can also set this by clicking the chat form name. +Alias: + Slapyvardį pakeisti galima ir spustelėjus pokalbių lange. +Slapyvardis: + + + + GeneralForm + + + General + Bendrosios + + + + + None + Nėra + + + + Choose an auto accept directory + popup title + Pasirinkite priimamų failų katalogą + + + + Call active + popup title + Vyksta pokalbis + + + + You can't disconnect while a call is active! + popup text + Vykstant pokalbiui atsijungti negalite! + + + + GeneralSettings + + + General Settings + Bendrosios nuostatos + + + + + The translation may not load until qTox restarts. + Vertimas gali nepasirodyti, kol nepaleisite qTox iš naujo. + + + + Translation + Vertimas + + + + Save settings to the working directory instead of the usual conf dir + describes makeToxPortable checkbox + Nuostatas saugoti veikiamajame kataloge, o ne įprastame nuostatų kataloge + + + + Make Tox portable + Leisti persinešti Tox programą + + + + System tray integration + Sistemos juostelės integracija + + + + Show system tray icon + Rodyti sistemos juostelėje + + + + Start in tray + Paleidžiant paslėpti juostelėje + + + + Close to tray + Uždarant paslėpti juostelėje + + + + Minimize to tray + Sumažinant paslėpti juostelėje + + + + Light icon + Šviesi piktograma + + + + Show contacts' status changes + Rodyti kontaktų būsenos pokyčius + + + + Check for updates on startup (unstable) + Ieškoti atnaujinimų paleidžiant (nestabili funkcija) + + + + Focus qTox when a message is received + Sufokusuoti qTox gavus žinutę + + + + Faux offline messaging + Kaupti žinutes atsijungus adresatui + + + + Provided in minutes + Laikas minutėmis + + + + Auto away after (0 to disable) + Automatiškai „pasitraukęs“ po („0“ išjungia) + + + + Set to 0 to disable + Išjungsite nustatydami „0“ + + + + minutes + minučių + + + + You can set this on a per-friend basis by right clicking them. + autoaccept cb tooltip + Galite nustatyti individualiai ant bet kurio kontakto spustelėję dešiniuoju pelės klavišu. + + + + Autoaccept files + Automatiškai priimti failus + + + + Save files in + Išsaugoti failus + + + + PushButton + PushButton + + + + Theme + Apipavidalinimas + + + + Use emoticons + Naudoti jaustukus + + + + Smiley Pack + Text on smiley pack label + Jaustukų rinkinys + + + + Style + Stilius + + + + Theme color + Apipavidalinimo spalva + + + + Emoticon size + Jaustukų dydis + + + + px + px + + + + Timestamp format + Laiko formatas + + + + Connection Settings + Ryšio nuostatos + + + + Enable IPv6 (recommended) + Text on a checkbox to enable IPv6 + Įjungti IPv6 (rekomenduojama) + + + + Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary. + force tcp checkbox tooltip + Išjungus galima naudotis Tox protokolu per Tor. Tox tinklas dėl to yra papildomai apkraunamas, todėl atžymėkite tik tada, kai reikia. + + + + Enable UDP (recommended) + Text on checkbox to disable UDP + Įjungti UDP (rekomenduojama) + + + + Proxy type + Įgaliotojo serverio tipas + + + + None + Nėra + + + + SOCKS5 + SOCKS5 + + + + HTTP + HTTP + + + + Address + Text on proxy addr label + Adresas + + + + Port + Text on proxy port label + Prievadas + + + + Reconnect + reconnect button + Prisijungti iš naujo + + + + GenericChatForm + + + Send message + Siųsti žinutę + + + + Smileys + Jaustukai + + + + Send file(s) + Siųsti failą (-us) + + + + Audio call: RED means you're on a call + Pokalbis su garsu: RAUDONA reiškia, kad vyksta pokalbis + + + + Video call: RED means you're on a call + Pokalbis su vaizdu: RAUDONA reiškia, kad vyksta pokalbis + + + + Toggle speakers volume: RED is OFF + Garsiakalbiai: RAUDONA reiškia IŠJUNGTA + + + + Toggle microphone: RED is OFF + Mikrofonas: RAUDONA reiškia IŠJUNGTA + + + + + Save chat log + Išsaugoti pokalbio žurnalą + + + + Clear displayed messages + Išvalyti rodomas žinutes + + + + Cleared + Išvalyta + + + + GroupChatForm + + + %1 users in chat + Number of users in chat + Pokalbyje %1 žmonių + + + + %1 users in chat + Pokalbyje %1 žmonių + + + + GroupWidget + + + + %1 users in chat + Pokalbyje %1 žmonių + + + + + 0 users in chat + Pokalbyje žmonių nėra + + + + Set title... + Nustatyti pavadinimą... + + + + Quit group + Menu to quit a groupchat + Palikti grupės pokalbį + + + + Group title + Grupės pavadinimas + + + + You can also set this by clicking the chat form name. +Title: + Pavadinimą pakeisti galima ir spustelėjus pokalbių lange. +Pavadinimas: + + + + IdentityForm + + + Identity + Tapatybė + + + + Call active + popup title + Vyksta pokalbis + + + + You can't switch profiles while a call is active! + popup text + Vykstant pokalbiui profilių keisti negalima! + + + + Rename "%1" + renaming a profile + Pervadinti „%1“ + + + + Profile already exists + rename confirm title + Toks profilis jau yra + + + + A profile named "%1" already exists. Do you want to erase it? + rename confirm text + Profilis „%1“ jau yra. Ar norite jį ištrinti? + + + + Export profile + save dialog title + Eksportuoti profilį + + + + Tox save file (*.tox) + save dialog filter + Tox failas (*.tox) + + + + Failed to remove file + Failo ištrinti nepavyko + + + + The file you chose to overwrite could not be removed first. + Perrašomo failo pirma ištrinti nepavyko. + + + + Failed to copy file + Failo nukopijuoti nepavyko + + + + The file you chose could not be written to. + Nepavyko įrašyti į pasirinktą failą. + + + + Profile currently loaded + current profile deletion warning title + Profilis aktyvus + + + + This profile is currently in use. Please load a different profile before deleting this one. + current profile deletion warning text + Šis profilis yra aktyvus. Prieš jį ištrindami turite įjungti kitą profilį. + + + + Deletion imminent! + deletion confirmation title + Gresia ištrynimas! + + + + Are you sure you want to delete this profile? +Associated friend information and chat logs will be deleted as well. + deletion confirmation text + Ar tikrai norite ištrinti šį profilį? +Susijusi kontaktų informacija bei pokalbių žurnalas bus taip pat ištrinti. + + + + Import profile + import dialog title + Importuoti profilį + + + + Tox save file (*.tox) + import dialog filter + Tox failas (*.tox) + + + + Ignoring non-Tox file + popup title + Rodomi tik Tox failai + + + + Warning: you've chosen a file that is not a Tox save file; ignoring. + popup text + Įspėjimas: pasirinktas failas nėra Tox failas – praleista. + + + + Profile already exists + import confirm title + Toks profilis jau yra + + + + A profile named "%1" already exists. Do you want to erase it? + import confirm text + Profilis „%1“ jau yra. Ar norite jį ištrinti? + + + + IdentitySettings + + + Public Information + Vieša informacija + + + + Name + Vardas + + + + Status + Būsena + + + + Tox ID + Tox ID + + + + Your Tox ID (click to copy) + Jūsų Tox ID (spustelėję nukopijuosite) + + + + Profiles + Profiliai + + + + Available profiles: + Išsaugoti profiliai: + + + + + Switching profiles is disabled during calls + tooltip + Pokalbio metu keisti profilio negalima + + + + Load + load profile button + Aktyvuoti + + + + Rename + rename profile button + Pervadinti + + + + Export + export profile button + Eksportuoti + + + + This is useful to remain safe on public computers + delete profile button tooltip + Tai naudinga, jei norite likti saugūs prie viešai prieinamų kompiuterių + + + + Delete + delete profile button + Ištrinti + + + + Import a profile + import profile button + Importuoti profilį + + + + New Tox ID + new profile button + Naujas Tox ID + + + + InputPasswordDialog + + + Password Dialog + Slaptažodis + + + + Input password: + Įveskite slaptažodį: + + + + LoadHistoryDialog + + + Load History Dialog + Įkelti žurnalą + + + + Load history from: + Įkelti pokalbių žurnalą nuo: + + + + MainWindow + + + Your name + Jūsų vardas + + + + Your status + Jūsų būsena + + + + Add friends + Pridėti kontaktą + + + + Create a group chat + Sukurti grupės pokalbį + + + + View completed file transfers + Rodyti baigtus siųsti failus + + + + Change your settings + Keisti nuostatas + + + + Close + Uždaryti + + + + NetCamView + + + Tox video + Tox vaizdas + + + + PrivacyForm + + + Privacy + Privatumas + + + + Encrypted log + Užšifruotas žurnalas + + + + You already have history log file encrypted with different password +Do you want to delete old history file? + Jau turite kitu slaptažodžiu šifruotą žurnalą +Ar norite senąjį pokalbių žurnalą ištrinti? + + + + PrivacySettings + + + Send Typing Notifications + Rodyti, kad rašote žinutę + + + + Keep History (unstable) + Išsaugoti žurnalą (nestabili funkcija) + + + + Encryption + Šifravimas + + + + Encrypt Tox datafile + Šifruoti Tox duomenų failą + + + + Encrypt History + Šifruoti pokalbių žurnalą + + + + Nospam + Tox ID apsauga nuo nepageidaujamų kontaktų (nospam) + + + + HHHHHHHH + HHHHHHHH + + + + Generate random nospam + Sugeneruoti atsitiktinį + + + + QObject + + + Update + The title of a message box + Atnaujinimas + + + + An update is available, do you want to download it now? +It will be installed when qTox restarts. + Rasta nauja programos versija, ar norite ją parsisiųsti? +Ji bus įdiegta paleidus qTox iš naujo. + + + + Tox URI to parse + analizuoti Tox URI + + + + Starts new instance and loads specified profile. + Atidaro naują langą ir aktyvuoja nurodytą profilį. + + + + profile + profilis + + + + Default + Numatyta + + + + Blue + Mėlyna + + + + Olive + Alyvinė + + + + Red + Raudona + + + + Violet + Violetinė + + + + Ignoring non-Tox file + popup title + Praleidžiamas failas + + + + Warning: you've chosen a file that is not a Tox save file; ignoring. + popup text + Įspėjimas: pasirinktas failas nėra Tox failas – praleista. + + + + Profile already exists + import confirm title + Toks profilis jau yra + + + + A profile named "%1" already exists. Do you want to erase it? + import confirm text + Profilis „%1“ jau yra. Ar norite jį ištrinti? + + + + Profile imported + Profilis importuotas + + + + %1.tox was successfully imported + %1.tox sėkmingai importuotas + + + + Tox me maybe? + Default message in Tox URI friend requests. Write something appropriate! + Gal susirašinėjam per Tox? + + + + SetPasswordDialog + + + Type Password + Įveskite slaptažodį + + + + Repeat Password + Pakartokite slaptažodį + + + + ToxDNS + + + The connection timed out + The DNS gives the Tox ID associated to toxme.se addresses + Ryšio sudaryti nepavyko + + + + This address does not exist + The DNS gives the Tox ID associated to toxme.se addresses + Tokio adreso nėra + + + + Error while looking up DNS + The DNS gives the Tox ID associated to toxme.se addresses + Klaida gaunant DNS duomenis + + + + No text record found + Error with the DNS + DNS įrašas nerastas + + + + Unexpected number of values in text record + Error with the DNS + Netinkamas DNS įrašas + + + + The version of Tox DNS used by this server is not supported + Error with the DNS + Šio serverio Tox DNS versija nepalaikoma + + + + The DNS lookup does not contain any Tox ID + Error with the DNS + DNS atsake nėra Tox ID + + + + + The DNS lookup does not contain a valid Tox ID + Error with the DNS + DNS atsake nėra tinkamo Tox ID + + + + + It appears that qTox has to use the old tox1 protocol. +Unfortunately tox1 is not secure. Should it be used anyway? + Panašu, kad qTox turi naudoti senąjį tox1 protokolą. +Deja tox1 protokolas turi saugumo spragų. Tęsti? + + + + ToxURIDialog + + + Add a friend + Title of the window to add a friend through Tox URI + Pridėti kontaktą + + + + Do you want to add %1 as a friend? + Ar norite pridėti %1 į kontaktus? + + + + User ID: + Naudotojo ID: + + + + Friend request message: + Prisistatymo žinutė: + + + + Send + Send a friend request + Siųsti + + + + Cancel + Don't send a friend request + Atšaukti + + + + Widget + + + Online + Prisijungęs + + + + Away + Pasitraukęs + + + + Busy + Užsiėmęs + + + + &Quit + &Baigti + + + + Online + Button to set your status to 'Online' + Prisijungęs + + + + Away + Button to set your status to 'Away' + Pasitraukęs + + + + Busy + Button to set your status to 'Busy' + Užsiėmęs + + + + Choose a profile + Pasirinkti profilį + + + + Please choose which identity to use + Pasirinkite, kurią tapatybę naudoti + + + + Choose a profile picture + Pasirinkite profilio paveikslėlį + + + + + + Error + Klaida + + + + Unable to open this file + Nepavyko atidaryti failo + + + + Unable to read this image + Nepavyko perskaityti paveikslėlio + + + + This image is too big + Paveikslėlis per didelis + + + + Toxcore failed to start, the application will terminate after you close this message. + Toxcore paleisti nepavyko: programa išsijungs uždarius šį pranešimą. + + + + toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart. + popup text + Toxcore neprisijungia su Jūsų įgaliotojo serverio nustatymais. qTox negali dirbti – pakeiskite nustatymus ir prisijunkite iš naujo. + + + + Add friend + Pridėti kontaktą + + + + File transfers + Failų siuntimai + + + + Settings + Nuostatos + + + + Couldn't request friendship + Nepavyko nusiųsti užklausos + + + + away + contact status + pasitraukęs (-usi) + + + + busy + contact status + užsiėmęs (-usi) + + + + offline + contact status + neprisijungęs (-usi) + + + + online + contact status + prisijungęs (-usi) + + + + %1 is now %2 + e.g. "Dubslow is now online" + %1 dabar %2 + + + + <Unknown> + Placeholder when we don't know someone's name in a group chat + <Nepažįstamas> + + + + %1 has set the title to %2 + %1 nustatė pavadinimą „%2“ + + + + Message failed to send + Nepavyko nusiųsti žinutės + + + diff --git a/translations/pl.ts b/translations/pl.ts index 4c7666729..c7daad941 100644 --- a/translations/pl.ts +++ b/translations/pl.ts @@ -8,6 +8,11 @@ Audio/Video Audio/Wideo + + + Initializing Camera... + Inicjowanie kamery... + AVSettings @@ -17,24 +22,24 @@ Ustawienia audio - + Playback device better translation? Urządzenie wyjściowe - + Capture device better translation? Urządzenie wejściowe - + Video Settings Ustawienia wideo - + Resolution Rozdzielczość @@ -49,27 +54,64 @@ Mikrofon - + + Use slider to set volume of your speakers. +WARNING: slider is not supposed to work yet. + + + + + Use slider to set volume of your microphone. +WARNING: slider is not supposed to work yet. + + + + Rescan audio devices Ponownie skanuj urządzenia audio - + + Filter audio + better translation? + Filtruj audio + + + + Filter sound from your microphone, so that people hearing you would get better sound. + Filtruj dźwięk z twojego mikrofonu, by twoi znajomi mogli odebrać lepszą jakość dźwięku. + + + + + Set resolution of your camera. +The higher values, the better video quality your friends may get. +Note though that with better video quality there is needed better internet connection. +Sometimes your connection may not be good enough to handle higher video quality, +which may lead to problems with video calls. + Ustaw rodzielczość swojej kamery. +Im większa wartość, tym lepsza jakość obrazu twoi znajomi otrzymają. +Do lepszej jakości obrazu potrzebne jest jednak lepsze połączenie z internetem. +Może się zdarzyć, iż twoje połączenie nie jest wystarczająco dobre do wyższej jakości, +co może spowodować problemy z połączeniami wideo. + + + Hue Odcień - + Brightness Jasność - + Saturation Nasycenie - + Contrast Kontrast @@ -138,17 +180,17 @@ Zignorować proxy i połączyć się z Internetem bezpośrednio? Zaawansowane - + FULL - very safe, slowest (recommended) Pełne - bardzo bezpieczne, najwolniejsze (rekomendowane) - + NORMAL - almost as safe as FULL, about 20% faster than FULL Normalne - prawie tak bezpieczne jak Pełne, około 20% od niego szybsze - + OFF - disables all safety, when something goes wrong your history may be lost, fastest (not recommended) Wyłączone - wyłącza bezpieczeństwo, gdy coś pójdzie źle twoja historia może zostać utracona. Najszybsze (nie rekomendowane) @@ -162,21 +204,33 @@ Zignorować proxy i połączyć się z Internetem bezpośrednio? - <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTANT NOTE</span></p><p><span style=" color:#ff0000;">Unless you </span><span style=" font-weight:600; color:#ff0000;">really</span><span style=" color:#ff0000;"> know what you are doing, please do </span><span style=" font-weight:600; color:#ff0000;">not</span><span style=" color:#ff0000;"> change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</span></p></body></html> - + 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 + + + + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">IMPORTANT NOTE</span></p><p><span style=" color:#ff0000;">Unless you </span><span style=" font-weight:600; color:#ff0000;">really</span><span style=" color:#ff0000;"> know what you are doing, please do </span><span style=" font-weight:600; color:#ff0000;">not</span><span style=" color:#ff0000;"> change anything here. Changes made here may lead to problems with qTox, and even to loss of your data, e.g. history.</span></p></body></html> + better translation? + <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">UWAGA</span></p><p><span style=" color:#ff0000;">O ile nie wiesz </span><span style=" font-weight:600; color:#ff0000;">naprawdę</span><span style=" color:#ff0000;"> co robisz, proszę, </span><span style=" font-weight:600; color:#ff0000;">nie</span><span style=" color:#ff0000;"> zmieniaj tutaj niczego. Zmiany tutaj mogą prowadzić do problemów z qToxem, a nawet do utraty twoich danych, eg. historii.</span></p></body></html> + + + Reset to default settings Reset do domyślnych ustawień - + History Historia - + <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Synchronous writing to DB</span></a></p></body></html> better translation? <html><head/><body><p><a href="http://www.sqlite.org/pragma.html#pragma_synchronous"><span style=" text-decoration: underline; color:#0000ff;">Zsynchronizowany zapis do DB</span></a></p></body></html> @@ -185,68 +239,82 @@ Zignorować proxy i połączyć się z Internetem bezpośrednio? ChatForm - + Load History... Wczytaj historię... - + Send a file Wyślij plik - + + File not read better translation? Nie otwarto pliku - + + qTox wasn't able to open %1 qTox nie był w stanie otworzyć %1 - + + Bad Idea Zły pomysł - + + You're trying to send a special (sequential) file, that's not going to work! Nie można przesłać tego pliku! - + %1 is calling %1 dzwoni - + %1 stopped calling ? Nieodebrana rozmowa od %1 - + Calling to %1 at least gender neutral... Dzwonisz do %1 - + Call rejected Połączenie zostało odrzucone - + + Failed to send file "%1" + Nie udało się wysłać pliku "%1" + + + Call with %1 ended. %2 Rozmowa z %1 została zakończona. %2 - + Call duration: Czas trwania rozmowy: + + + is typing... + pisze... + ChatTextEdit @@ -259,108 +327,108 @@ Zignorować proxy i połączyć się z Internetem bezpośrednio? Core - + Toxing on qTox better translation? Toxuję na qTox - + qTox User Użytkownik qToxa - + Friend is already added Znajomy jest już dodany - + Encryption error Błąd szyfrowania - + The .tox file is encrypted, but encryption was not checked, continuing regardless. better translation? Plik .tox jest zaszyfrowany, lecz szyfrowanie nie zostało sprawdzone. Kontynuowanie. - + Tox datafile decryption password Hasło odszyfrowujące plik danych Tox - - - + + + Password error better translation? Błąd hasła - - + + Failed to setup password. Empty password. Nie udało się ustawić hasła. Puste hasło. - + Try Again Spróbuj ponownie - + Change profile Zmień profil - + Reinit current profile Zainicjuj ponownie obecny profil - + Wrong password has been entered Podano złe hasło - + History Log decryption password Hasło odszyfrowujące historię - + Encrypted log Zaszyfrowany log - + Your history is encrypted with different password. Do you want to try another password? Twoja historia jest zaszyfrowana innym hasłem Czy chcesz spróbować z innym hasłem? - + History Historia - + Due to incorret password history will be disabled. Historia będzie wyłączona z powodu złego hasła. - + NO Password better translation? BRAK hasła - + Will be saved without encryption! Zostanie zapisane bez szyfrowania! @@ -413,34 +481,34 @@ Czy chcesz spróbować z innym hasłem? FriendRequestDialog - + Friend request Title of the window to aceept/deny a friend request Prośba o dodanie do kontaktów - + Someone wants to make friends with you Ktoś chce zostać Twoim znajomym - + User ID: ID użytkownika: - + Friend request message: Treść zapytania: - + Accept Accept a friend request Zaakceptuj - + Reject Reject a friend request Odrzuć @@ -505,26 +573,26 @@ Alias: Główne - - + + None Brak - + Choose an auto accept directory popup title better translation? Wybierz domyślną ścieżkę dla plików - + Call active popup title Aktywna rozmowa - + You can't disconnect while a call is active! popup text Nie możesz się rozłączyć w trakcie rozmowy! @@ -544,191 +612,301 @@ Alias: Zmiana języka może wymagać restart aplikacji. - - 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ń + + Language: + Język: - - Make Tox portable - Zrób Tox przenośnym + + System tray + better translation? it /could/ be translated as "Zasobnik systemowy", but I personally dislike that translation, so… + Tray systemowy - + + Enable light tray icon. + toolTip for light icon setting + better translation? + Użyj jasnej ikony w trayu. + + + Start in tray Uruchamiaj w trayu - + + qTox will start minimized in tray. + toolTip for Start in tray setting + qTox będzię się uruchamiał zminimalizowany w trayu. + + + Close to tray Zamykaj do traya - + + After pressing close (X) qTox will minimize to tray, +instead of closing itself. + toolTip for close to tray setting + Po naciśnięciu zakończ (X) qTox zminimalizuje się do traya, +zamias zakończyć swe działanie. + + + Minimize to tray Minimalizuj do traya - + + After pressing minimize (_) qTox will minimize itself to tray, +instead of system taskbar. + toolTip for minimize to tray setting + better translation? + Po naciścięciu minimalizuj (_) qTox zminimalizuje się to traya, +zamiast do paska zadań. + + + + Check for updates on startup + Sprawdzaj aktualizacje przy starcie + + + + Save to: + Zapisz do: + + + + + Set where files will be saved. + Ustaw gdzie pliki będą zapisywane. + + + + Auto away after (0 to disable): + Automatycznie nieobecny/a po (0 by wyłączyć): + + + + Chat + Czat + + + + Always notify about new messages in groupchats. + toolTip for Group chat always notify + Zawsze powiadamiaj o nowych wiadomościach w czacie grupowym. + + + + Group chats always notify + Zawsze powiadamiaj o czacie grupowym + + + Show contacts' status changes Pokazuj zmiany statusów - - Provided in minutes - Podane w minutach + + Focus qTox when you receive message. + toolTip for Focus window setting + better translation? + Przywołaj qTox gdy otrzyma się nową wiadomość. - + + Proxy type: + Typ proxy: + + + + Address: + Text on proxy addr label + Adres: + + + Set to 0 to disable Ustaw na 0 by wyłączyć - + + Your status is changed to Away after set period of inactivity. + Twój status zmieni się na nieobecny/a po ustawionym okresie braku aktywności. + + + + On new message: + Przy nowej wiadomości: + + + + Show qTox's window when you receive new message. + tooltip for Show window setting + Pokaż okno qToxa gdy otrzymasz nową wiadomość. + + + + Show window + Pokaż okno + + + + Focus window + better translation? + Przywołaj qTox + + + + Messages you are trying to send to your friends when they are not online +will be sent to them when they will appear online to you. + toolTip for Faux offline messaging setting + Wiadomości które chcesz wysłać do znajomych, którzy nie są online, +zostaną wysłane gdy znajomi pokażą się online dla ciebie. + + + + Your contact list will be shown in compact mode. +qTox's restart needed. + toolTip for compact layout setting + Twoja lista znajomych zostanie pokazana w trybie kompaktowym. +Potrzebny restart qToxa. + + + + Compact contact list + Kompaktowa lista kontaktów + + + + Smiley Pack: + Text on smiley pack label + Paczka Uśmiechów: + + + + Emoticon size: + Rozmiar emotikonów: + + + + Style: + Styl: + + + + Theme color: + Kolor motywu: + + + + Timestamp format: + Format czasu: + + + Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary. force tcp checkbox tooltip Wyłączenie pozwala np. na toxowanie przez Tora. Niestety obciąża to sieć Tox, więc używaj tylko w razie potrzeby. - + Enable UDP (recommended) Text on checkbox to disable UDP Używaj UDP (zalecane) - + + None + Brak + + + + SOCKS5 + SOCKS5 + + + + HTTP + HTTP + + + Reconnect reconnect button Połącz ponownie - + minutes min - - Translation - Tłumaczenie - - - + Show system tray icon better translation? Pokaż ikonę w trayu - - Check for updates on startup (unstable) - Sprawdzaj czy są dostępne aktualizacje przy uruchomieniu (niestabilne) + + Light icon + Jasna ikona - - Focus qTox when a message is received - better translation? - Przywołaj qTox gdy otrzyma się nową wiadomość - - - + Faux offline messaging better translation? Faux wiadomości offline - - Auto away after (0 to disable) - Automatycznie nieobecny/a po (0 by wyłączyć) - - - + You can set this on a per-friend basis by right clicking them. autoaccept cb tooltip better translation? Możesz to ustawić dla każdego znajomego klikając nań prawym. - + Autoaccept files Automatycznie akceptuj pliki - - Save files in - Zapisz pliki w - - - + PushButton - + Theme Motyw - + Use emoticons better translation? Używaj emotikony - - Smiley Pack - Text on smiley pack label - Paczka Uśmiechów - - - - Style - Styl - - - - Theme color - Kolor motywu - - - - Emoticon size - better translation? - Rozmiar emotikonów - - - + px px - - Timestamp format - Format czasu - - - + Connection Settings Ustawienia połączenia - + Enable IPv6 (recommended) Text on a checkbox to enable IPv6 Używaj IPv6 (zalecane) - - Use proxy (SOCKS5) - Używaj proxy (SOCKS5) - - - - Address - Text on proxy addr label - Adres - - - + Port Text on proxy port label Port @@ -737,55 +915,55 @@ Alias: GenericChatForm - + Send message Wyślij wiadomość - + Smileys Uśmiechy - + Send file(s) Wyślij plik(i) - + Audio call: RED means you're on a call better translation? Rozmowa audio: Czerwone oznacza aktywną rozmowę - + Video call: RED means you're on a call better translation? Rozmowa wideo: Czerwone oznacza aktywną rozmowę - + Toggle speakers volume: RED is OFF Włącz/Wyłącz dźwięk: Czerwony - wyłączony - + Toggle microphone: RED is OFF Włącz/Wyłącz mikrofon: Czerwony - wyłączony - - + + Save chat log Zapisz historię rozmowy - + Clear displayed messages Wyczyść wyświetlane wiadomości - + Cleared Wyczyszczono @@ -851,124 +1029,126 @@ Nazwa: Tożsamość - + Call active popup title Rozmowa w trakcie - + You can't switch profiles while a call is active! popup text Nie można zmienić profilu podczas aktywnego połączenia! - + Rename "%1" renaming a profile Zmień nazwę "%1" - + Profile already exists rename confirm title Profil już istnieje - + A profile named "%1" already exists. Do you want to erase it? rename confirm text Profil pod nazwą "%1" już istnieje. Czy chcesz go usunąć? - + Export profile save dialog title Eksportuj profil - + Tox save file (*.tox) save dialog filter Plik zapisu Tox (*.tox) - + Failed to remove file Nie udało się usunąć pliku - + The file you chose to overwrite could not be removed first. better translation? Nie udało się usunąć wybranego pliku do nadpisania. - + Failed to copy file Nie udało się skopiować pliku - + The file you chose could not be written to. Nie udało się zapisać do wybranego pliku. - + Profile currently loaded current profile deletion warning title Profil obecnie załadowany - + This profile is currently in use. Please load a different profile before deleting this one. current profile deletion warning text Ten profil jest obecnie w użyciu. Proszę załaduj inny profil przed usunięciem tego. - + Deletion imminent! deletion confirmation title Usuwanie profilu! - - Are you sure you want to delete this profile? + + Are you sure you want to delete this profile? +Associated friend information and chat logs will be deleted as well. deletion confirmation text - Czy na pewno chcesz usunąć ten profil? + Czy na pewno chcesz usunąć ten profil? +Wszystkie powiązane dane i historia także zostaną usunięte. - + Import profile import dialog title Importuj profil - + Tox save file (*.tox) import dialog filter Plik zapisu Tox (*.tox) - + Ignoring non-Tox file popup title Zignorowano niepoprawny plik profilu - + Warning: you've chosen a file that is not a Tox save file; ignoring. popup text Ostrzeżenie: Wybrano plik który nie jest plikiem zapisu Tox; zignorowano. - + Profile already exists import confirm title Profil już istnieje - + A profile named "%1" already exists. Do you want to erase it? import confirm text Profil pod nazwą "%1" już istnieje. Czy chcesz go usunąć? @@ -997,65 +1177,105 @@ Nazwa: Tox ID - + + This bunch of characters tells other Tox clients how to contact you. +Share it with your friends to communicate. + Tox ID tooltip + Ta gromada znaków mówi innym klientom Tox jak się z tobą skontaktować. +Podziel się tym ze znajomymi by się komunikować. + + + Your Tox ID (click to copy) Twój Tox ID (kliknij by skopiować) - + Profiles Profile - + Available profiles: Dostępne profile: - - - Switching profiles is disabled during calls - tooltip - Zmiana profilu w czasie rozmowy jest wyłączona + + Currently selected profile. + toolTip for currently set profile + Obecnie wybrany profil. - + + Load selected profile and switch to it. + tooltip for loading profile button + better translation? + Załaduj wybrany profil i przełącz się nań. + + + Load load profile button Wczytaj - + Rename rename profile button Zmień nazwę - + + Rename selected profile. + tooltip for renaming profile button + Zmień nazwę wybranego profilu. + + + Export export profile button Eksportuj - + + Allows you to export your Tox profile to a file. +Profile does not contain your history. + tooltip for profile exporting button + Pozwala na wyeksportowanie twojego profilu Tox do pliku. +Profil nie zawiera twojej historii. + + + + Delete selected profile. + delete profile button tooltip + Usuń wybrany profil. + + + Delete delete profile button Usuń - - This is useful to remain safe on public computers - delete profile button tooltip - Użyteczne by pozostać bezpiecznym na publicznych komputerach - - - + Import a profile import profile button Importuj profil - + + Import Tox profile from a .tox file. + tooltip for importing profile button + Importuj profil Tox z pliku .tox . + + + + Create new Tox ID and switch to it. + tooltip for creating new Tox ID button + Utwórz nowy Tox ID i przełącz się nań. + + + New Tox ID new profile button Nowy Tox ID @@ -1156,44 +1376,72 @@ Czy chcesz usunąć stary plik historii? PrivacySettings + + + Your friends will be able to see when you are typing. + tooltip for typing notifications setting + Twoi znajomi będą w stanie zobaczyć kiedy do nich piszesz. + - Typing Notification + Send Typing Notifications better translation? Pokazuj gdy tekst jest pisany - + + History keeping is still in development. +Save format changes are possible, which may result in data loss. + toolTip for Keep History setting + better translation? + Zachowanie historii jest wciąż w trakcie rozwoju. +Możlie, iż format zapisu zostanie zmieniony, co może skutkować utratą danych. + + + Keep History (unstable) Zachowaj historię (niestabilne) - + Encryption Szyfrowanie - + Encrypt Tox datafile Zaszyfruj plik danych Tox - + Encrypt History Zaszyfruj historię - + Nospam Nospam - + + Nospam is part of your Tox ID. +It is there to help you change your Tox ID when you feel like you are getting too much spam friend requests. +When you change nospam, your current contacts still can communicate with you, +but new contacts need to know your new Tox ID to be able to add you. + toolTip for nospam + Nospam jest częścią twojego Tox ID. +Z jego pomocą możesz zmienić swój Tox ID gdy poczujesz, +iż otrzymujesz za dużo spamujących zapytań o dodanie do znajomych. +Po zmianie nospamu twoi obecni znajomi wciąż będą się w stanie z tobą rozmawiać, +ale nowi będą musieli znać twój nowy Tox ID by móc cię dodać. + + + HHHHHHHH - + Generate random nospam Wygeneruj losowy nospam @@ -1242,13 +1490,13 @@ Czy chcesz usunąć stary plik historii? Może Tox ze mną? - + Update The title of a message box Aktualizacja - + An update is available, do you want to download it now? It will be installed when qTox restarts. Aktualizacja jest dostępna, czy chcesz ją teraz pobrać? @@ -1295,6 +1543,12 @@ Zostanie zainstalowana po restarcie qToxa. Violet Fioletowy + + + Incoming call... + better translation? + Rozmowa przychodząca... + SetPasswordDialog @@ -1364,6 +1618,14 @@ Zostanie zainstalowana po restarcie qToxa. Error with the DNS DNS nie zawiera poprawnego Tox ID + + + + It appears that qTox has to use the old tox1 protocol. +Unfortunately tox1 is not secure. Should it be used anyway? + Wygląda na to, że qTox musi użyć stary protokół tox1. +Niestety tox1 nie jest bezpieczny. Czy ma być użyty pomimo tego? + ToxURIDialog @@ -1404,160 +1666,155 @@ Zostanie zainstalowana po restarcie qToxa. Widget - + Online Online - + Away Nieobecny/a - + Busy Zajęty/a - + &Quit &Wyjdź - - Change status to: - Zmień status na: - - - + 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 Wybierz profil - + Please choose which identity to use Proszę wybierz która tożsamość ma być użyta - + 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. - + Add friend Dodaj znajomych - + File transfers Transfery plików - + Settings Ustawienia - + Couldn't request friendship better translation? - Nie udało się dodać do znajomych + Nie udało się dodać do znajomych - + away contact status nieobecna/y - + busy contact status zajęta/y - + offline contact status offline - + online contact status online - + %1 is now %2 e.g. "Dubslow is now online" %1 jest teraz %2 - + <Unknown> Placeholder when we don't know someone's name in a group chat <Nieznany/a> - + %1 has set the title to %2 %1 ustawił(a) nazwę na %2 - + Message failed to send Nie udało się wysłać wiadmości diff --git a/ui/acceptCall/acceptCall.png b/ui/acceptCall/acceptCall.png new file mode 100644 index 000000000..c892aba9b Binary files /dev/null and b/ui/acceptCall/acceptCall.png differ diff --git a/ui/chatArea/innerStyle.css b/ui/chatArea/innerStyle.css index 4c7b95b2c..5639d1af4 100644 --- a/ui/chatArea/innerStyle.css +++ b/ui/chatArea/innerStyle.css @@ -30,3 +30,8 @@ div.alert_name { background-color: @orange; font: @bigBold; } + +a { + color: blue; +} + diff --git a/ui/chatroomWidgets/genericChatroomWidget.css b/ui/chatroomWidgets/genericChatroomWidget.css index c83bb9958..09feb23c1 100644 --- a/ui/chatroomWidgets/genericChatroomWidget.css +++ b/ui/chatroomWidgets/genericChatroomWidget.css @@ -1,5 +1,5 @@ -GenericChatroomWidget -{ +GenericChatroomWidget +{ background-color: @themeMedium; } @@ -8,31 +8,55 @@ GenericChatroomWidget[active="true"] background-color: @white; } -GenericChatroomWidget[active="false"]:hover -{ - background-color: @themeLight; -} +GenericChatroomWidget[active="false"]:hover +{ + background-color: @themeLight; +} -GenericChatroomWidget[active="true"] > QLabel#status +GenericChatroomWidget[active="true"][compact="true"] > QLabel#status +{ + font: @small; + color: @mediumGrey; +} + +GenericChatroomWidget[active="false"][compact="true"] > QLabel#status +{ + font: @small; + color: @lightGrey; +} + +GenericChatroomWidget[active="true"][compact="true"] > QLabel#name +{ + font: @medium; + color: @darkGrey; +} + +GenericChatroomWidget[active="false"][compact="true"] > QLabel#name +{ + font: @medium; + color: @white; +} + +GenericChatroomWidget[active="true"][compact="false"] > QLabel#status { font: @medium; color: @mediumGrey; } -GenericChatroomWidget[active="false"] > QLabel#status +GenericChatroomWidget[active="false"][compact="false"] > QLabel#status { font: @medium; color: @lightGrey; } -GenericChatroomWidget[active="true"] > QLabel#name +GenericChatroomWidget[active="true"][compact="false"] > QLabel#name { font: @big; color: @darkGrey; } -GenericChatroomWidget[active="false"] > QLabel#name +GenericChatroomWidget[active="false"][compact="false"] > QLabel#name { font: @big; color: @white; -} \ No newline at end of file +} diff --git a/ui/micButton/micButton.css b/ui/micButton/micButton.css index ec784ae7a..df03739ee 100644 --- a/ui/micButton/micButton.css +++ b/ui/micButton/micButton.css @@ -4,8 +4,8 @@ QPushButton#green background-image: url(":/ui/micButton/micButton.png"); background-repeat: none; border: none; - width: 25px; - height: 20px; + width: 22px; + height: 18px; } QPushButton#green:hover @@ -19,8 +19,8 @@ QPushButton#red background-image: url(":/ui/micButton/micButtonPressed.png"); background-repeat: none; border: none; - width: 25px; - height: 20px; + width: 22px; + height: 18px; } QPushButton#grey @@ -29,8 +29,8 @@ QPushButton#grey background-image: url(":/ui/micButton/micButtonDisabled.png"); background-repeat: none; border: none; - width: 25px; - height: 20px; + width: 22px; + height: 18px; } QPushButton:focus { diff --git a/ui/rejectCall/rejectCall.png b/ui/rejectCall/rejectCall.png new file mode 100644 index 000000000..a0480be5c Binary files /dev/null and b/ui/rejectCall/rejectCall.png differ diff --git a/ui/settings/mainHead.css b/ui/settings/mainHead.css index a393850f4..1f883175e 100644 --- a/ui/settings/mainHead.css +++ b/ui/settings/mainHead.css @@ -1,4 +1,4 @@ -QWidget +QWidget .QLabel, QWidget .QLineEdit { color: black; background: white; diff --git a/ui/volButton/volButton.css b/ui/volButton/volButton.css index 8210f4819..2d3b33546 100644 --- a/ui/volButton/volButton.css +++ b/ui/volButton/volButton.css @@ -4,7 +4,7 @@ QPushButton#green background-image: url(":/ui/volButton/volButton.png"); background-repeat: none; border: none; - width: 25px; + width: 22px; height: 18px; } @@ -19,7 +19,17 @@ QPushButton#red background-image: url(":/ui/volButton/volButtonPressed.png"); background-repeat: none; border: none; - width: 25px; + width: 22px; + height: 18px; +} + +QPushButton#grey +{ + background-color: transparent; + background-image: url(":/ui/volButton/volButtonDisabled.png"); + background-repeat: none; + border: none; + width: 22px; height: 18px; } diff --git a/ui/volButton/volButtonDisabled.png b/ui/volButton/volButtonDisabled.png new file mode 100644 index 000000000..78862daab Binary files /dev/null and b/ui/volButton/volButtonDisabled.png differ diff --git a/updater/widget.cpp b/updater/widget.cpp index 940af6c33..678c6a770 100644 --- a/updater/widget.cpp +++ b/updater/widget.cpp @@ -58,6 +58,7 @@ Widget::~Widget() void Widget::setProgress(int value) { ui->progress->setValue(value); + ui->progress->repaint(); qApp->processEvents(); } @@ -126,7 +127,7 @@ void Widget::update() setProgress(5); /// 2. Check the update (5-50%) - float checkProgressStep = 45/diff.size(); + float checkProgressStep = 45.0/(float)diff.size(); float checkProgress = 5; for (UpdateFileMeta fileMeta : diff) { @@ -152,21 +153,23 @@ void Widget::update() setProgress(50); /// 3. Install the update (50-95%) - float installProgressStep = 45/diff.size(); + float installProgressStep = 45.0/(float)diff.size(); float installProgress = 50; for (UpdateFileMeta fileMeta : diff) { // Backup old files if (QFile(fileMeta.installpath).exists()) { + QFile(fileMeta.installpath+".bak").remove(); QFile(fileMeta.installpath).rename(fileMeta.installpath+".bak"); backups.append(fileMeta.installpath); } // Install new ones + QDir().mkpath(QFileInfo(fileMeta.installpath).absolutePath()); QFile fileFile(updateDirStr+fileMeta.installpath); if (!fileFile.copy(fileMeta.installpath)) - fatalError(tr("Unable to copy the update's files.")); + fatalError(tr("Unable to copy the update's files from ")+(updateDirStr+fileMeta.installpath)+" to "+fileMeta.installpath); installProgress += installProgressStep; setProgress(installProgress); } diff --git a/windows/bootstrap.sh b/windows/bootstrap.sh new file mode 100644 index 000000000..f67effa12 --- /dev/null +++ b/windows/bootstrap.sh @@ -0,0 +1,109 @@ +#!/bin/sh +QTOX_DIR=`pwd`/.. + +if [ -d $QTOX_DIR/libs ]; then + echo "Remove ./libs and redownload/recompile dependencies?" + read -p "m/a/N (missing/all/no): " yn + yn=$(echo $yn | tr "[:upper:]" "[:lower:]") + if [ "$yn" == "a" ]; then + rm -rf $QTOX_DIR/libs + elif [ "$yn" == "n" ]; then + exit -1 + elif [ "$yn" != "m" ]; then + exit -1 + fi +fi + +mkdir -p $QTOX_DIR/libs +cd $QTOX_DIR/libs + + +## toxcore +if [ ! -f "libtoxcore-win32-i686.zip" ]; then + wget --no-check-certificate http://jenkins.libtoxcore.so/job/libtoxcore-win32-i686/lastSuccessfulBuild/artifact/libtoxcore-win32-i686.zip + rm -rf include/tox +fi + +if [ ! -d "include/tox" ]; then + $QTOX_DIR/tools/unzip -o libtoxcore-win32-i686.zip -d ./ +fi + +## filter_audio +if [ ! -f "bin/libfilteraudio.dll" ]; then + $QTOX_DIR/install_libfilteraudio.sh $QTOX_DIR/libs/filter_audio $QTOX_DIR/libs +fi + + +## OpenAL +if [ ! -f "openal-soft-1.16.0.tar.bz2" ]; then + wget http://kcat.strangesoft.net/openal-releases/openal-soft-1.16.0.tar.bz2 + rm -rf openal-soft-1.16.0 +fi + +if [ ! -d "openal-soft-1.16.0" ]; then + tar -xvf openal-soft-1.16.0.tar.bz2 + rm bin/OpenAL32.dll +fi + +if [ ! -f "bin/OpenAL32.dll" ]; then + pushd openal-soft-1.16.0/build + cmake -G "MSYS Makefiles" -DQT_QMAKE_EXECUTABLE=NOTFOUND -DCMAKE_BUILD_TYPE=Release -DALSOFT_REQUIRE_DSOUND=NO -DCMAKE_INSTALL_PREFIX=$QTOX_DIR/libs .. + make + make install + popd +fi + +## opencv +if [ ! -f "opencv-2.4.9.tar.gz" ]; then + wget --no-check-certificate https://github.com/Itseez/opencv/archive/2.4.9.tar.gz -O opencv-2.4.9.tar.gz + rm -rf opencv-2.4.9 +fi + +if [ ! -d "opencv-2.4.9" ]; then + tar -xvf opencv-2.4.9.tar.gz + rm bin/libopencv_core249.dll +fi + +if [ ! -f "bin/libopencv_core249.dll" ]; then + mkdir opencv-2.4.9/build + pushd opencv-2.4.9/build + cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$QTOX_DIR/libs \ + -DBUILD_opencv_apps=NO \ + -DBUILD_opencv_calib3d=NO \ + -DBUILD_opencv_contrib=NO \ + -DBUILD_opencv_core=YES \ + -DBUILD_opencv_features2d=NO \ + -DBUILD_opencv_flann=NO \ + -DBUILD_opencv_gpu=NO \ + -DBUILD_opencv_highgui=YES \ + -DBUILD_opencv_imgproc=YES \ + -DBUILD_opencv_legacy=NO \ + -DBUILD_opencv_ml=NO \ + -DBUILD_opencv_nonfree=NO \ + -DBUILD_opencv_objdetect=NO \ + -DBUILD_opencv_ocl=NO \ + -DBUILD_opencv_photo=NO \ + -DBUILD_opencv_stiching=NO \ + -DBUILD_opencv_superres=NO \ + -DBUILD_opencv_ts=NO \ + -DBUILD_opencv_video=NO \ + -DBUILD_opencv_videostab=NO \ + -DBUILD_opencv_world=NO \ + -DWITH_QT=NO \ + -DBUILD_EXAMPLES=NO \ + .. + + make + make install + for arch in x86 x64 + do + if [ -d $QTOX_DIR/libs/$arch/mingw ]; then + mv $QTOX_DIR/libs/$arch/mingw/bin/* $QTOX_DIR/libs/bin/ + mv $QTOX_DIR/libs/$arch/mingw/lib/* $QTOX_DIR/libs/lib/ + rm -rf $QTOX_DIR/libs/$arch + fi + done + popd +fi + +echo **Done** diff --git a/windows/qtox64.nsi b/windows/qtox64.nsi new file mode 100644 index 000000000..273e833f2 --- /dev/null +++ b/windows/qtox64.nsi @@ -0,0 +1,357 @@ +################### +#META +################### +!define APP_NAME "qTox" +!define COMP_NAME "Tox" +!define WEB_SITE "https://github.com/tux3/qTox" +!define VERSION "1.0.0.0" +!define DESCRIPTION "qTox Installer" +!define COPYRIGHT "The Tox Project" +!define INSTALLER_NAME "setup-qtox.exe" +!define MAIN_APP_EXE "bin\qtox.exe" +!define INSTALL_TYPE "SetShellVarContext current" +!define REG_ROOT "HKCU" +!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\qtox.exe" +!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" +!define REG_START_MENU "Start Menu Folder" +var SM_Folder + +Name "${APP_NAME}" +Caption "${APP_NAME}" +OutFile "${INSTALLER_NAME}" +BrandingText "${APP_NAME}" +InstallDir "$PROGRAMFILES64\${APP_NAME}" +SetCompressor /SOLID lzma + +VIProductVersion "${VERSION}" +VIAddVersionKey "ProductName" "${APP_NAME}" +VIAddVersionKey "CompanyName" "${COMP_NAME}" +VIAddVersionKey "LegalCopyright" "${COPYRIGHT}" +VIAddVersionKey "FileDescription" "${DESCRIPTION}" +VIAddVersionKey "FileVersion" "${VERSION}" + +############## +#UNINSTALL LOG +############## +;AddItem macro + !macro AddItem Path + FileWrite $UninstLog "${Path}$\r$\n" + !macroend + +;File macro + !macro File FileName + IfFileExists "$OUTDIR\${FileName}" +2 + FileWrite $UninstLog "$OUTDIR\${FileName}$\r$\n" + File "${FileName}" + !macroend + +;CreateShortcut macro + !macro CreateShortcut FilePath FilePointer Pamameters Icon IconIndex + FileWrite $UninstLog "${FilePath}$\r$\n" + CreateShortcut "${FilePath}" "${FilePointer}" "${Pamameters}" "${Icon}" "${IconIndex}" + !macroend + +;Copy files macro + !macro CopyFiles SourcePath DestPath + IfFileExists "${DestPath}" +2 + FileWrite $UninstLog "${DestPath}$\r$\n" + CopyFiles "${SourcePath}" "${DestPath}" + !macroend + +;Rename macro + !macro Rename SourcePath DestPath + IfFileExists "${DestPath}" +2 + FileWrite $UninstLog "${DestPath}$\r$\n" + Rename "${SourcePath}" "${DestPath}" + !macroend + +;CreateDirectory macro + !macro CreateDirectory Path + CreateDirectory "${Path}" + FileWrite $UninstLog "${Path}$\r$\n" + !macroend + +;SetOutPath macro + !macro SetOutPath Path + SetOutPath "${Path}" + FileWrite $UninstLog "${Path}$\r$\n" + !macroend + +;WriteUninstaller macro + !macro WriteUninstaller Path + WriteUninstaller "${Path}" + FileWrite $UninstLog "${Path}$\r$\n" + !macroend + +;WriteIniStr macro + !macro WriteIniStr IniFile SectionName EntryName NewValue + IfFileExists "${IniFile}" +2 + FileWrite $UninstLog "${IniFile}$\r$\n" + WriteIniStr "${IniFile}" "${SectionName}" "${EntryName}" "${NewValue}" + !macroend + +;WriteRegStr macro + !macro WriteRegStr RegRoot UnInstallPath Key Value + FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n" + WriteRegStr "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}" + !macroend + + +;WriteRegDWORD macro + !macro WriteRegDWORD RegRoot UnInstallPath Key Value + FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n" + WriteRegDWORD "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}" + !macroend + +;BackupFile macro + !macro BackupFile FILE_DIR FILE BACKUP_TO + IfFileExists "${BACKUP_TO}\*.*" +2 + CreateDirectory "${BACKUP_TO}" + IfFileExists "${FILE_DIR}\${FILE}" 0 +2 + Rename "${FILE_DIR}\${FILE}" "${BACKUP_TO}\${FILE}" + !macroend + +;RestoreFile macro + !macro RestoreFile BUP_DIR FILE RESTORE_TO + IfFileExists "${BUP_DIR}\${FILE}" 0 +2 + Rename "${BUP_DIR}\${FILE}" "${RESTORE_TO}\${FILE}" + !macroend + +;BackupFiles macro + !macro BackupFiles FILE_DIR FILE BACKUP_TO + IfFileExists "${BACKUP_TO}\*.*" +2 + CreateDirectory "22222" + IfFileExists "${FILE_DIR}\${FILE}" 0 +7 + FileWrite $UninstLog "${FILE_DIR}\${FILE}$\r$\n" + FileWrite $UninstLog "${BACKUP_TO}\${FILE}$\r$\n" + FileWrite $UninstLog "FileBackup$\r$\n" + Rename "${FILE_DIR}\${FILE}" "${BACKUP_TO}\${FILE}" + SetOutPath "${FILE_DIR}" + File "${FILE}" #After the Original file is backed up write the new file. + !macroend + +;RestoreFiles macro + !macro RestoreFiles BUP_FILE RESTORE_FILE + IfFileExists "${BUP_FILE}" 0 +2 + CopyFiles "${BUP_FILE}" "${RESTORE_FILE}" + !macroend + +################### +#PREPARE UNINST LOG +################### + ;Set the name of the uninstall log + !define UninstLog "uninstall.log" + Var UninstLog + + ;Uninstall log file missing. + LangString UninstLogMissing ${LANG_ENGLISH} "${UninstLog} not found!$\r$\nUninstallation cannot proceed!" + + ;AddItem macro + !define AddItem "!insertmacro AddItem" + + ;BackupFile macro + !define BackupFile "!insertmacro BackupFile" + + ;BackupFiles macro + !define BackupFiles "!insertmacro BackupFiles" + + ;Copy files macro + !define CopyFiles "!insertmacro CopyFiles" + + ;CreateDirectory macro + !define CreateDirectory "!insertmacro CreateDirectory" + + ;CreateShortcut macro + !define CreateShortcut "!insertmacro CreateShortcut" + + ;File macro + !define File "!insertmacro File" + + ;Rename macro + !define Rename "!insertmacro Rename" + + ;RestoreFile macro + !define RestoreFile "!insertmacro RestoreFile" + + ;RestoreFiles macro + !define RestoreFiles "!insertmacro RestoreFiles" + + ;SetOutPath macro + !define SetOutPath "!insertmacro SetOutPath" + + ;WriteRegDWORD macro + !define WriteRegDWORD "!insertmacro WriteRegDWORD" + + ;WriteRegStr macro + !define WriteRegStr "!insertmacro WriteRegStr" + + ;WriteUninstaller macro + !define WriteUninstaller "!insertmacro WriteUninstaller" + + Section -openlogfile + CreateDirectory "$INSTDIR" + IfFileExists "$INSTDIR\${UninstLog}" +3 + FileOpen $UninstLog "$INSTDIR\${UninstLog}" w + Goto +4 + SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL + FileOpen $UninstLog "$INSTDIR\${UninstLog}" a + FileSeek $UninstLog 0 END + SectionEnd + +############## +#MODERN UI +############## +!include "MUI.nsh" + +!define MUI_ABORTWARNING +!define MUI_FINISHPAGE_NOAUTOCLOSE +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_DIRECTORY +!ifdef REG_START_MENU + !define MUI_STARTMENUPAGE_NODISABLE + !define MUI_STARTMENUPAGE_DEFAULTFOLDER "qTox" + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}" + !insertmacro MUI_PAGE_STARTMENU Application $SM_Folder +!endif +!insertmacro MUI_PAGE_INSTFILES + +Function finishpageaction +${CreateShortcut} "$DESKTOP\qTox.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "" "" +FunctionEnd + +!define MUI_FINISHPAGE_SHOWREADME "" +!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut" +!define MUI_FINISHPAGE_SHOWREADME_FUNCTION finishpageaction + +!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}" +!define MUI_FINISHPAGE_LINK "Find qTox on GitHub" +!define MUI_FINISHPAGE_LINK_LOCATION "https://github.com/tux3/qTox" +!insertmacro MUI_PAGE_FINISH + +!define MUI_UNABORTWARNING +!define MUI_UNFINISHPAGE_NOAUTOCLOSE +!insertmacro MUI_UNPAGE_WELCOME +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + +!insertmacro MUI_LANGUAGE "English" + +################# +#INSTALL +################# +Section "Install" + # Install files + ${SetOutPath} "$INSTDIR" + ${WriteUninstaller} "uninstall.exe" + + ${CreateDirectory} "bin" + ${SetOutPath} "$INSTDIR\bin" + ${File} "qtox\*.*" + + ${CreateDirectory} "imageformats" + ${SetOutPath} "$INSTDIR\bin\imageformats" + ${File} "qtox\imageformats\*.*" + ${SetOutPath} "$INSTDIR\bin" + + ${CreateDirectory} "platforms" + ${SetOutPath} "$INSTDIR\bin\platforms" + ${File} "qtox\platforms\*.*" + ${SetOutPath} "$INSTDIR\bin" + + ${CreateDirectory} "sqldrivers" + ${SetOutPath} "$INSTDIR\bin\sqldrivers" + ${File} "qtox\sqldrivers\*.*" + ${SetOutPath} "$INSTDIR\bin" + + # Create shortcuts + ${CreateDirectory} "$SMPROGRAMS\qTox" + ${CreateShortCut} "$SMPROGRAMS\qTox\qTox.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "" "" + ${CreateShortCut} "$SMPROGRAMS\qTox\Uninstall qTox.lnk" "$INSTDIR\uninstall.exe" "" "" "" + + # Write setup/app info into the registry + ${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}" + ${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "Path" "$INSTDIR\bin\" + ${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe" + + # Register the tox: protocol + ${WriteRegStr} HKCR "tox" "" "URL:tox Protocol" + ${WriteRegStr} HKCR "tox" "URL Protocol" "" + ${WriteRegStr} HKCR "tox\shell\open\command" "" "$INSTDIR\${MAIN_APP_EXE} %1" + + # Register the .tox file associations + ${WriteRegStr} "HKCR" "Applications\qtox.exe\SupportedTypes" ".tox" "" + ${WriteRegStr} HKCR ".tox" "" "toxsave" + ${WriteRegStr} HKCR "toxsave" "" "Tox save file" + ${WriteRegStr} HKCR "toxsave\DefaultIcon" "" "$INSTDIR\${MAIN_APP_EXE}" + ${WriteRegStr} HKCR "toxsave\shell\open\command" "" "$INSTDIR\${MAIN_APP_EXE} %1" +SectionEnd + + +################ +#UNINSTALL +################ +Section Uninstall + ;If there's no uninstall log, we'll try anyway to clean what we can + IfFileExists "$INSTDIR\${UninstLog}" +3 + Goto noLog + + Push $R0 + Push $R1 + Push $R2 + SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL + FileOpen $UninstLog "$INSTDIR\${UninstLog}" r + StrCpy $R1 -1 + + GetLineCount: + ClearErrors + FileRead $UninstLog $R0 + IntOp $R1 $R1 + 1 + StrCpy $R0 $R0 -2 + Push $R0 + IfErrors 0 GetLineCount + + Pop $R0 + + LoopRead: + StrCmp $R1 0 LoopDone + Pop $R0 + + IfFileExists "$R0\*.*" 0 +3 + RMDir $R0 #is dir + Goto +9 + IfFileExists $R0 0 +3 + Delete $R0 #is file + Goto +6 + StrCmp $R0 "${REG_ROOT} ${REG_APP_PATH}" 0 +3 + DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" #is Reg Element + Goto +3 + StrCmp $R0 "${REG_ROOT} ${UNINSTALL_PATH}" 0 +2 + DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" #is Reg Element + + IntOp $R1 $R1 - 1 + Goto LoopRead + LoopDone: + FileClose $UninstLog + Delete "$INSTDIR\${UninstLog}" + noLog: + Delete /REBOOTOK "$INSTDIR\uninstall.exe" + RMDir /r /REBOOTOK "$INSTDIR\bin" + RMDir /REBOOTOK "$INSTDIR" + Pop $R2 + Pop $R1 + Pop $R0 + + ;Remove start menu entries + RMDir /r /REBOOTOK "$SMPROGRAMS\qTox" + + ;Remove registry keys + DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" + DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" + DeleteRegKey HKCR "Applications\qtox.exe" + DeleteRegKey HKCR ".tox" + DeleteRegKey HKCR "tox" + DeleteRegKey HKCR "toxsave" +SectionEnd