Improve various details

This commit is contained in:
Martchus 2016-09-01 16:34:30 +02:00
parent 4737268e20
commit 2360b2a482
30 changed files with 1351 additions and 406 deletions

View File

@ -19,11 +19,15 @@ set(HEADER_FILES
data/syncthingconnection.h data/syncthingconnection.h
data/syncthingdirectorymodel.h data/syncthingdirectorymodel.h
data/syncthingdevicemodel.h data/syncthingdevicemodel.h
data/syncthingconfig.h
data/utils.h
) )
set(SRC_FILES set(SRC_FILES
data/syncthingconnection.cpp data/syncthingconnection.cpp
data/syncthingdirectorymodel.cpp data/syncthingdirectorymodel.cpp
data/syncthingdevicemodel.cpp data/syncthingdevicemodel.cpp
data/syncthingconfig.cpp
data/utils.cpp
) )
set(WIDGETS_HEADER_FILES set(WIDGETS_HEADER_FILES
@ -86,6 +90,7 @@ set(REQUIRED_ICONS
network-card network-card
window-close window-close
edit-copy edit-copy
edit-paste
preferences-other preferences-other
view-barcode view-barcode
folder-open folder-open
@ -98,6 +103,13 @@ set(REQUIRED_ICONS
network-server network-server
folder-sync folder-sync
internet-web-browser internet-web-browser
network-connect
system-run
system-search
preferences-desktop
preferences-desktop-notification
preferences-desktop-icons
preferences-desktop-locale
) )
# find c++utilities # find c++utilities

View File

@ -2,14 +2,27 @@
Qt 5-based tray application for [Syncthing](https://github.com/syncthing/syncthing) Qt 5-based tray application for [Syncthing](https://github.com/syncthing/syncthing)
* Still under development
* Designed to work under any desktop environment with tray icon support * Designed to work under any desktop environment with tray icon support
* Tested under Plasma 5 and Openbox/qt5ct/Tint2
* Could be shown as regular window if no tray icon support is available
* Doesn't require desktop environment specific libraries * Doesn't require desktop environment specific libraries
* Provides quick access to most frequently used features but does not intend to replace the official web UI * Provides quick access to most frequently used features but does not intend to replace the official web UI
* Check state of directories and devices
* Check current traffic statistics
* Display further details about direcoties and devices, like last file, last
scan, ...
* Trigger re-scan of a specific directory
* Open a directory with the default file browser
* Pause/resume devices
* Shows Syncthing notifications * Shows Syncthing notifications
* Does *not* allow configuring Syncthing itself (currently I do not intend to add this feature as it could cause more harm than good when not implemented correctly)
* Provides quick access to the official web UI * Provides quick access to the official web UI
* Utilizes either Qt WebKit or Qt WebEngine * Utilizes either Qt WebKit or Qt WebEngine
* Can be built without web view support as well (then the web UI is opened in the regular browser) * Can be built without web view support as well (then the web UI is opened in the regular browser)
* Still under development; the following features are planned
* Connect to multiple instances of Syncthing at a time
* Add option to conveniently add the tray to the applications launched when the desktop environment starts
* Add option to launch Syncthing when the tray is started and log stdout/stderr (would make sense for me under Windows, otherwise starting Syncthing via systemd is more preferable of course)
## Screenshots ## Screenshots
### Under Openbox/Tint2 ### Under Openbox/Tint2
@ -36,3 +49,9 @@ The following Qt 5 modules are requried: core network gui widgets webenginewidge
#### Select Qt modules for WebView #### Select Qt modules for WebView
* If Qt WebKitWidgets is installed on the system, the tray will link against it. Otherwise it will link against Qt WebEngineWidgets. * If Qt WebKitWidgets is installed on the system, the tray will link against it. Otherwise it will link against Qt WebEngineWidgets.
* To force usage of Qt WebKit/Qt WebEngine or to disable both add `-DWEBVIEW_PROVIDER=webkit/webengine/none` to the CMake arguments. * To force usage of Qt WebKit/Qt WebEngine or to disable both add `-DWEBVIEW_PROVIDER=webkit/webengine/none` to the CMake arguments.
BTW: I still prefer the deprecated Qt WebKit because
* I currently don't know how to allow a particular self-signed certificate in Qt WebEngine. (Currently any self-signed certificate is accepted!)
* Qt WebEngine can not be built with mingw-w64.
* Qt WebEngine is more buggy in my experience.
* security issues are not a concern because no other website than the Syncthing web UI is shown.

View File

@ -1,79 +1,87 @@
#include "./settings.h" #include "./settings.h"
#include "../gui/trayicon.h" #include "../gui/trayicon.h"
#include "../gui/traywidget.h" #include "../gui/traywidget.h"
#include "resources/config.h" #include "resources/config.h"
#include <c++utilities/application/argumentparser.h> #include <c++utilities/application/argumentparser.h>
#include <c++utilities/application/commandlineutils.h> #include <c++utilities/application/commandlineutils.h>
#include <c++utilities/application/failure.h> #include <c++utilities/application/failure.h>
#include <qtutilities/resources/qtconfigarguments.h> #include <qtutilities/resources/qtconfigarguments.h>
#include <qtutilities/resources/resources.h> #include <qtutilities/resources/resources.h>
#include <qtutilities/resources/importplugin.h> #include <qtutilities/resources/importplugin.h>
#include <qtutilities/settingsdialog/qtsettings.h> #include <qtutilities/settingsdialog/qtsettings.h>
#include <QApplication> #include <QApplication>
#include <QMessageBox> #include <QMessageBox>
#include <iostream> #include <iostream>
using namespace std; using namespace std;
using namespace ApplicationUtilities; using namespace ApplicationUtilities;
using namespace QtGui; using namespace QtGui;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
SET_APPLICATION_INFO; SET_APPLICATION_INFO;
// setup argument parser // setup argument parser
ArgumentParser parser; ArgumentParser parser;
HelpArgument helpArg(parser); HelpArgument helpArg(parser);
// Qt configuration arguments // Qt configuration arguments
QT_CONFIG_ARGUMENTS qtConfigArgs; QT_CONFIG_ARGUMENTS qtConfigArgs;
Argument windowedArg("windowed", 'w', "shows the UI in a regular window"); Argument windowedArg("windowed", 'w', "opens the tray menu as a regular window");
windowedArg.setCombinable(true); windowedArg.setCombinable(true);
qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&windowedArg); qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&windowedArg);
parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &helpArg}); parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &helpArg});
try { try {
parser.parseArgs(argc, argv); parser.parseArgs(argc, argv);
if(qtConfigArgs.qtWidgetsGuiArg().isPresent()) { if(qtConfigArgs.qtWidgetsGuiArg().isPresent()) {
SET_QT_APPLICATION_INFO; SET_QT_APPLICATION_INFO;
QApplication application(argc, argv); QApplication application(argc, argv);
Settings::restore(); Settings::restore();
Settings::qtSettings().apply(); Settings::qtSettings().apply();
qtConfigArgs.applySettings(true); qtConfigArgs.applySettings(true);
LOAD_QT_TRANSLATIONS; LOAD_QT_TRANSLATIONS;
QtUtilitiesResources::init(); QtUtilitiesResources::init();
int res; int res;
#ifndef QT_NO_SYSTEMTRAYICON if(windowedArg.isPresent()) {
if(QSystemTrayIcon::isSystemTrayAvailable()) { TrayWidget trayWidget;
if(windowedArg.isPresent()) { trayWidget.show();
TrayWidget trayWidget; res = application.exec();
trayWidget.show(); } else {
res = application.exec(); #ifndef QT_NO_SYSTEMTRAYICON
} else { if(QSystemTrayIcon::isSystemTrayAvailable()) {
application.setQuitOnLastWindowClosed(false); application.setQuitOnLastWindowClosed(false);
TrayIcon trayIcon; TrayIcon trayIcon;
trayIcon.show(); trayIcon.show();
res = application.exec(); if(Settings::firstLaunch()) {
} trayIcon.trayMenu().widget()->showSettingsDialog();
} else { QMessageBox msgBox;
QMessageBox::critical(nullptr, QApplication::applicationName(), QApplication::translate("main", "The system tray is (currently) not available.")); msgBox.setIcon(QMessageBox::Information);
res = -1; msgBox.setText(QCoreApplication::translate("main", "You must configure how to connect to Syncthing when using Syncthing Tray the first time."));
} msgBox.setInformativeText(QCoreApplication::translate("main", "Note that the settings dialog allows importing URL, credentials and API-key from the local Syncthing configuration."));
#else msgBox.exec();
QMessageBox::critical(nullptr, QApplication::applicationName(), QApplication::translate("main", "The Qt libraries have not been built with tray icon support.")); }
res = -2; res = application.exec();
#endif } else {
Settings::save(); QMessageBox::critical(nullptr, QApplication::applicationName(), QApplication::translate("main", "The system tray is (currently) not available. You could open the tray menu as a regular window using the -w flag, though."));
QtUtilitiesResources::cleanup(); res = -1;
return res; }
} #else
} catch(const Failure &ex) { QMessageBox::critical(nullptr, QApplication::applicationName(), QApplication::translate("main", "The Qt libraries have not been built with tray icon support. You could open the tray menu as a regular window using the -w flag, though."));
CMD_UTILS_START_CONSOLE; res = -2;
cout << "Unable to parse arguments. " << ex.what() << "\nSee --help for available commands." << endl; #endif
} }
Settings::save();
return 0; QtUtilitiesResources::cleanup();
} return res;
}
} catch(const Failure &ex) {
CMD_UTILS_START_CONSOLE;
cout << "Unable to parse arguments. " << ex.what() << "\nSee --help for available commands." << endl;
}
return 0;
}

View File

@ -1,4 +1,5 @@
#include "./settings.h" #include "./settings.h"
#include <qtutilities/settingsdialog/qtsettings.h> #include <qtutilities/settingsdialog/qtsettings.h>
#include <QString> #include <QString>
@ -10,7 +11,13 @@ using namespace Media;
namespace Settings { namespace Settings {
// tray bool &firstLaunch()
{
static bool v = false;
return v;
}
// connection
QString &syncthingUrl() QString &syncthingUrl()
{ {
static QString v; static QString v;
@ -36,12 +43,14 @@ QByteArray &apiKey()
static QByteArray v; static QByteArray v;
return v; return v;
} }
// notifications
bool &notifyOnDisconnect() bool &notifyOnDisconnect()
{ {
static bool v = true; static bool v = true;
return v; return v;
} }
bool &notifyOnErrors() bool &notifyOnInternalErrors()
{ {
static bool v = true; static bool v = true;
return v; return v;
@ -56,11 +65,20 @@ bool &showSyncthingNotifications()
static bool v = true; static bool v = true;
return v; return v;
} }
// appearance
bool &showTraffic() bool &showTraffic()
{ {
static bool v = true; static bool v = true;
return v; return v;
} }
QSize &trayMenuSize()
{
static QSize v(350, 300);
return v;
}
// autostart/launcher
bool &launchSynchting() bool &launchSynchting()
{ {
static bool v = false; static bool v = false;
@ -108,16 +126,18 @@ void restore()
QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName());
settings.beginGroup(QStringLiteral("tray")); settings.beginGroup(QStringLiteral("tray"));
firstLaunch() = !settings.contains(QStringLiteral("syncthingUrl"));
syncthingUrl() = settings.value(QStringLiteral("syncthingUrl"), QStringLiteral("http://localhost:8080/")).toString(); syncthingUrl() = settings.value(QStringLiteral("syncthingUrl"), QStringLiteral("http://localhost:8080/")).toString();
authEnabled() = settings.value(QStringLiteral("authEnabled"), false).toBool(); authEnabled() = settings.value(QStringLiteral("authEnabled"), false).toBool();
userName() = settings.value(QStringLiteral("userName")).toString(); userName() = settings.value(QStringLiteral("userName")).toString();
password() = settings.value(QStringLiteral("password")).toString(); password() = settings.value(QStringLiteral("password")).toString();
apiKey() = settings.value(QStringLiteral("apiKey")).toByteArray(); apiKey() = settings.value(QStringLiteral("apiKey")).toByteArray();
notifyOnDisconnect() = settings.value(QStringLiteral("notifyOnDisconnect"), true).toBool(); notifyOnDisconnect() = settings.value(QStringLiteral("notifyOnDisconnect"), true).toBool();
notifyOnErrors() = settings.value(QStringLiteral("notifyOnErrors"), true).toBool(); notifyOnInternalErrors() = settings.value(QStringLiteral("notifyOnErrors"), true).toBool();
notifyOnSyncComplete() = settings.value(QStringLiteral("notifyOnSyncComplete"), true).toBool(); notifyOnSyncComplete() = settings.value(QStringLiteral("notifyOnSyncComplete"), true).toBool();
showSyncthingNotifications() = settings.value(QStringLiteral("showSyncthingNotifications"), true).toBool(); showSyncthingNotifications() = settings.value(QStringLiteral("showSyncthingNotifications"), true).toBool();
showTraffic() = settings.value(QStringLiteral("showTraffic"), true).toBool(); showTraffic() = settings.value(QStringLiteral("showTraffic"), true).toBool();
trayMenuSize() = settings.value(QStringLiteral("trayMenuSize"), trayMenuSize()).toSize();
launchSynchting() = settings.value(QStringLiteral("launchSynchting"), false).toBool(); launchSynchting() = settings.value(QStringLiteral("launchSynchting"), false).toBool();
syncthingCommand() = settings.value(QStringLiteral("syncthingCommand"), QStringLiteral("syncthing")).toString(); syncthingCommand() = settings.value(QStringLiteral("syncthingCommand"), QStringLiteral("syncthing")).toString();
settings.endGroup(); settings.endGroup();
@ -145,10 +165,11 @@ void save()
settings.setValue(QStringLiteral("password"), password()); settings.setValue(QStringLiteral("password"), password());
settings.setValue(QStringLiteral("apiKey"), apiKey()); settings.setValue(QStringLiteral("apiKey"), apiKey());
settings.setValue(QStringLiteral("notifyOnDisconnect"), notifyOnDisconnect()); settings.setValue(QStringLiteral("notifyOnDisconnect"), notifyOnDisconnect());
settings.setValue(QStringLiteral("notifyOnErrors"), notifyOnErrors()); settings.setValue(QStringLiteral("notifyOnErrors"), notifyOnInternalErrors());
settings.setValue(QStringLiteral("notifyOnSyncComplete"), notifyOnSyncComplete()); settings.setValue(QStringLiteral("notifyOnSyncComplete"), notifyOnSyncComplete());
settings.setValue(QStringLiteral("showSyncthingNotifications"), showSyncthingNotifications()); settings.setValue(QStringLiteral("showSyncthingNotifications"), showSyncthingNotifications());
settings.setValue(QStringLiteral("showTraffic"), showTraffic()); settings.setValue(QStringLiteral("showTraffic"), showTraffic());
settings.setValue(QStringLiteral("trayMenuSize"), trayMenuSize());
settings.setValue(QStringLiteral("launchSynchting"), launchSynchting()); settings.setValue(QStringLiteral("launchSynchting"), launchSynchting());
settings.setValue(QStringLiteral("syncthingCommand"), syncthingCommand()); settings.setValue(QStringLiteral("syncthingCommand"), syncthingCommand());
settings.endGroup(); settings.endGroup();

View File

@ -7,6 +7,7 @@
QT_FORWARD_DECLARE_CLASS(QByteArray) QT_FORWARD_DECLARE_CLASS(QByteArray)
QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QSize)
namespace Media { namespace Media {
enum class TagUsage; enum class TagUsage;
@ -19,21 +20,30 @@ class QtSettings;
namespace Settings { namespace Settings {
bool &firstLaunch();
// connection
QString &syncthingUrl(); QString &syncthingUrl();
bool &authEnabled(); bool &authEnabled();
QString &userName(); QString &userName();
QString &password(); QString &password();
QByteArray &apiKey(); QByteArray &apiKey();
// notifications
bool &notifyOnDisconnect(); bool &notifyOnDisconnect();
bool &notifyOnErrors(); bool &notifyOnInternalErrors();
bool &notifyOnSyncComplete(); bool &notifyOnSyncComplete();
bool &showSyncthingNotifications(); bool &showSyncthingNotifications();
bool &showTraffic();
// apprearance
bool &showTraffic();
QSize &trayMenuSize();
// autostart/launcher
bool &launchSynchting(); bool &launchSynchting();
QString &syncthingCommand(); QString &syncthingCommand();
// web view
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT) #if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
bool &webViewDisabled(); bool &webViewDisabled();
double &webViewZoomFactor(); double &webViewZoomFactor();

70
data/syncthingconfig.cpp Normal file
View File

@ -0,0 +1,70 @@
#include "syncthingconfig.h"
#include <QStandardPaths>
#include <QXmlStreamReader>
#include <QFile>
namespace Data {
/*!
* \struct SyncthingConfig
* \brief The SyncthingConfig struct holds the configuration of Syncthing itself read from config.xml in the Syncthing home directory.
* \remarks Only a few fields are required since most of the Syncthing config can be accessed via SyncthingConnection class.
*/
QString SyncthingConfig::locateConfigFile()
{
QString path = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("syncthing/config.xml"));
if(path.isEmpty()) {
path = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("Syncthing/config.xml"));
}
return path;
}
QString SyncthingConfig::locateHttpsCertificate()
{
QString path = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("syncthing/https-cert.pem"));
if(path.isEmpty()) {
path = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("Syncthing/https-cert.pem"));
}
return path;
}
bool SyncthingConfig::restore(const QString &configFilePath)
{
QFile configFile(configFilePath);
if(!configFile.open(QFile::ReadOnly)) {
return false;
}
QXmlStreamReader xmlReader(&configFile);
bool ok = false;
#include <qtutilities/misc/xmlparsermacros.h>
children {
// only version 16 supported, try to parse other versions anyways since the changes might not affect
// the few parts read here
version = attribute("version").toString();
children {
iftag("gui") {
ok = true;
guiEnabled = attributeFlag("enabled");
guiEnforcesSecureConnection = attributeFlag("tls");
children {
iftag("address") {
guiAddress = text;
} eliftag("user") {
guiUser = text;
} eliftag("password") {
guiPasswordHash = text;
} eliftag("apikey") {
guiApiKey = text;
} else_skip
}
} else_skip
}
}
#include <qtutilities/misc/undefxmlparsermacros.h>
return ok;
}
} // namespace Data

26
data/syncthingconfig.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef DATA_SYNCTHINGCONFIG_H
#define DATA_SYNCTHINGCONFIG_H
#include <QString>
namespace Data {
struct SyncthingConfig
{
QString version;
bool guiEnabled = false;
bool guiEnforcesSecureConnection = false;
QString guiAddress;
QString guiUser;
QString guiPasswordHash;
QString guiApiKey;
static QString locateConfigFile();
static QString locateHttpsCertificate();
bool restore(const QString &configFilePath);
};
} // namespace Data
#endif // DATA_SYNCTHINGCONFIG_H

View File

@ -1,4 +1,5 @@
#include "./syncthingconnection.h" #include "./syncthingconnection.h"
#include "./syncthingconfig.h"
#include <c++utilities/conversion/conversionexception.h> #include <c++utilities/conversion/conversionexception.h>
@ -13,6 +14,7 @@
#include <QAuthenticator> #include <QAuthenticator>
#include <QStringBuilder> #include <QStringBuilder>
#include <QTimer> #include <QTimer>
#include <QHostAddress>
#include <utility> #include <utility>
@ -56,6 +58,13 @@ bool SyncthingDir::assignStatus(const QString &statusStr)
newStatus = DirStatus::Unknown; newStatus = DirStatus::Unknown;
} }
if(newStatus != status) { if(newStatus != status) {
switch(status) {
case DirStatus::Scanning:
lastScanTime = DateTime::now();
break;
default:
;
}
status = newStatus; status = newStatus;
return true; return true;
} }
@ -67,6 +76,8 @@ bool SyncthingDir::assignStatus(const QString &statusStr)
* \brief The SyncthingConnection class allows Qt applications to access Syncthing. * \brief The SyncthingConnection class allows Qt applications to access Syncthing.
*/ */
QList<QSslError> SyncthingConnection::m_expectedCertificateErrors;
/*! /*!
* \brief Constructs a new instance ready to connect. To establish the connection, call connect(). * \brief Constructs a new instance ready to connect. To establish the connection, call connect().
*/ */
@ -84,10 +95,12 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
m_totalOutgoingRate(0), m_totalOutgoingRate(0),
m_configReply(nullptr), m_configReply(nullptr),
m_statusReply(nullptr), m_statusReply(nullptr),
m_connectionsReply(nullptr),
m_eventsReply(nullptr), m_eventsReply(nullptr),
m_unreadNotifications(false), m_unreadNotifications(false),
m_hasConfig(false), m_hasConfig(false),
m_hasStatus(false) m_hasStatus(false),
m_lastFileDeleted(false)
{} {}
/*! /*!
@ -226,17 +239,21 @@ QNetworkRequest SyncthingConnection::prepareRequest(const QString &path, const Q
/*! /*!
* \brief Requests asynchronously data using the rest API. * \brief Requests asynchronously data using the rest API.
*/ */
inline QNetworkReply *SyncthingConnection::requestData(const QString &path, const QUrlQuery &query, bool rest) QNetworkReply *SyncthingConnection::requestData(const QString &path, const QUrlQuery &query, bool rest)
{ {
return networkAccessManager().get(prepareRequest(path, query, rest)); auto *reply = networkAccessManager().get(prepareRequest(path, query, rest));
reply->ignoreSslErrors(m_expectedCertificateErrors);
return reply;
} }
/*! /*!
* \brief Posts asynchronously data using the rest API. * \brief Posts asynchronously data using the rest API.
*/ */
inline QNetworkReply *SyncthingConnection::postData(const QString &path, const QUrlQuery &query, const QByteArray &data) QNetworkReply *SyncthingConnection::postData(const QString &path, const QUrlQuery &query, const QByteArray &data)
{ {
return networkAccessManager().post(prepareRequest(path, query), data); auto *reply = networkAccessManager().post(prepareRequest(path, query), data);
reply->ignoreSslErrors(m_expectedCertificateErrors);
return reply;
} }
SyncthingDir *SyncthingConnection::findDirInfo(const QString &dir, int &row) SyncthingDir *SyncthingConnection::findDirInfo(const QString &dir, int &row)
@ -270,6 +287,8 @@ void SyncthingConnection::continueConnecting()
{ {
if(m_keepPolling && m_hasConfig && m_hasStatus) { if(m_keepPolling && m_hasConfig && m_hasStatus) {
requestConnections(); requestConnections();
requestDirStatistics();
requestDeviceStatistics();
// since config and status could be read successfully, let's poll for events // since config and status could be read successfully, let's poll for events
m_lastEventId = 0; m_lastEventId = 0;
requestEvents(); requestEvents();
@ -318,13 +337,29 @@ void SyncthingConnection::requestStatus()
/*! /*!
* \brief Requests current connections asynchronously. * \brief Requests current connections asynchronously.
* *
* The signal devStatusChanged() is emitted for each device where the connection status has changed updated; error() is emitted in the error case. * The signal devStatusChanged() is emitted for each device where the connection status has changed; error() is emitted in the error case.
*/ */
void SyncthingConnection::requestConnections() void SyncthingConnection::requestConnections()
{ {
QObject::connect(m_connectionsReply = requestData(QStringLiteral("system/connections"), QUrlQuery()), &QNetworkReply::finished, this, &SyncthingConnection::readConnections); QObject::connect(m_connectionsReply = requestData(QStringLiteral("system/connections"), QUrlQuery()), &QNetworkReply::finished, this, &SyncthingConnection::readConnections);
} }
/*!
* \brief Requests directory statistics asynchronously.
*/
void SyncthingConnection::requestDirStatistics()
{
QObject::connect(requestData(QStringLiteral("stats/folder"), QUrlQuery()), &QNetworkReply::finished, this, &SyncthingConnection::readDirStatistics);
}
/*!
* \brief Requests device statistics asynchronously.
*/
void SyncthingConnection::requestDeviceStatistics()
{
QObject::connect(requestData(QStringLiteral("stats/device"), QUrlQuery()), &QNetworkReply::finished, this, &SyncthingConnection::readDeviceStatistics);
}
/*! /*!
* \brief Requests the Syncthing events (since the last successful call) asynchronously. * \brief Requests the Syncthing events (since the last successful call) asynchronously.
* *
@ -399,6 +434,37 @@ void SyncthingConnection::requestLog(std::function<void (const std::vector<Synct
}); });
} }
/*!
* \brief Locates and loads the (self-signed) certificate used by the Syncthing GUI.
*/
void SyncthingConnection::loadSelfSignedCertificate()
{
// only possible if the Syncthing instance is running on the local machine
const QString host(QUrl(syncthingUrl()).host());
if(host.compare(QLatin1String("localhost"), Qt::CaseInsensitive) != 0 && !QHostAddress().isLoopback()) {
return;
}
// ensure current exceptions for self-signed certificates are cleared
m_expectedCertificateErrors.clear();
// find cert
const QString certPath = !m_configDir.isEmpty() ? (m_configDir + QStringLiteral("/https-cert.pem")) : SyncthingConfig::locateHttpsCertificate();
if(certPath.isEmpty()) {
emit error(tr("Unable to locate certificate used by Syncthing GUI."));
return;
}
// add exception
const QList<QSslCertificate> cert = QSslCertificate::fromPath(certPath);
if(cert.isEmpty()) {
emit error(tr("Unable to load certificate used by Syncthing GUI."));
return;
}
m_expectedCertificateErrors << QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert.at(0));
m_expectedCertificateErrors << QSslError(QSslError::UnableToVerifyFirstCertificate, cert.at(0));
m_expectedCertificateErrors << QSslError(QSslError::SelfSignedCertificate, cert.at(0));
m_expectedCertificateErrors << QSslError(QSslError::HostNameMismatch, cert.at(0));
}
/*! /*!
* \brief Reads results of requestConfig(). * \brief Reads results of requestConfig().
*/ */
@ -556,7 +622,7 @@ void SyncthingConnection::readConnections()
double transferTime; double transferTime;
if(!m_lastConnectionsUpdate.isNull() && ((transferTime = (DateTime::gmtNow() - m_lastConnectionsUpdate).totalSeconds()) != 0.0)) { if(!m_lastConnectionsUpdate.isNull() && ((transferTime = (DateTime::gmtNow() - m_lastConnectionsUpdate).totalSeconds()) != 0.0)) {
m_totalIncomingRate = (totalIncomingTraffic - m_totalIncomingTraffic) * 0.008 / transferTime, m_totalIncomingRate = (totalIncomingTraffic - m_totalIncomingTraffic) * 0.008 / transferTime,
m_totalOutgoingRate = (totalOutgoingTraffic - m_totalOutgoingTraffic) * 0.008 / transferTime; m_totalOutgoingRate = (totalOutgoingTraffic - m_totalOutgoingTraffic) * 0.008 / transferTime;
} else { } else {
m_totalIncomingRate = m_totalOutgoingRate = 0.0; m_totalIncomingRate = m_totalOutgoingRate = 0.0;
} }
@ -611,6 +677,108 @@ void SyncthingConnection::readConnections()
} }
} }
/*!
* \brief Reads results of requestDirStatistics().
* \remarks TODO
*/
void SyncthingConnection::readDirStatistics()
{
auto *reply = static_cast<QNetworkReply *>(sender());
reply->deleteLater();
switch(reply->error()) {
case QNetworkReply::NoError: {
QJsonParseError jsonError;
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
if(jsonError.error == QJsonParseError::NoError) {
const QJsonObject replyObj(replyDoc.object());
int index = 0;
for(SyncthingDir &dirInfo : m_dirs) {
const QJsonObject dirObj(replyObj.value(dirInfo.id).toObject());
if(!dirObj.isEmpty()) {
bool mod = false;
try {
dirInfo.lastScanTime = DateTime::fromIsoStringLocal(dirObj.value(QStringLiteral("lastScan")).toString().toUtf8().data());
mod = true;
} catch(const ConversionException &) {
dirInfo.lastScanTime = DateTime();
}
const QJsonObject lastFileObj(dirObj.value(QStringLiteral("lastFile")).toObject());
if(!lastFileObj.isEmpty()) {
dirInfo.lastFileName = lastFileObj.value(QStringLiteral("filename")).toString();
mod = true;
if(!dirInfo.lastFileName.isEmpty()) {
dirInfo.lastFileDeleted = lastFileObj.value(QStringLiteral("deleted")).toBool(false);
try {
dirInfo.lastFileTime = DateTime::fromIsoStringLocal(lastFileObj.value(QStringLiteral("at")).toString().toUtf8().data());
if(dirInfo.lastFileTime > m_lastFileTime) {
m_lastFileTime = dirInfo.lastFileTime,
m_lastFileName = dirInfo.lastFileName,
m_lastFileDeleted = dirInfo.lastFileDeleted;
}
} catch(const ConversionException &) {
dirInfo.lastFileTime = DateTime();
}
}
}
if(mod) {
emit dirStatusChanged(dirInfo, index);
}
}
++index;
}
} else {
emit error(tr("Unable to parse directory statistics: ") + jsonError.errorString());
}
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request directory statistics: ") + reply->errorString());
}
}
/*!
* \brief Reads results of requestDeviceStatistics().
* \remarks TODO
*/
void SyncthingConnection::readDeviceStatistics()
{
auto *reply = static_cast<QNetworkReply *>(sender());
reply->deleteLater();
switch(reply->error()) {
case QNetworkReply::NoError: {
QJsonParseError jsonError;
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
if(jsonError.error == QJsonParseError::NoError) {
const QJsonObject replyObj(replyDoc.object());
int index = 0;
for(SyncthingDev &devInfo : m_devs) {
const QJsonObject devObj(replyObj.value(devInfo.id).toObject());
if(!devObj.isEmpty()) {
try {
devInfo.lastSeen = DateTime::fromIsoStringLocal(devObj.value(QStringLiteral("lastSeen")).toString().toUtf8().data());
emit devStatusChanged(devInfo, index);
} catch(const ConversionException &) {
devInfo.lastSeen = DateTime();
}
}
++index;
}
// since there seems no event for this data, just request every minute, FIXME: make interval configurable
if(m_keepPolling) {
QTimer::singleShot(60000, Qt::VeryCoarseTimer, this, SLOT(requestConnections()));
}
} else {
emit error(tr("Unable to parse device statistics: ") + jsonError.errorString());
}
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request device statistics: ") + reply->errorString());
}
}
/*! /*!
* \brief Reads results of requestEvents(). * \brief Reads results of requestEvents().
*/ */
@ -635,7 +803,7 @@ void SyncthingConnection::readEvents()
m_lastEventId = event.value(QStringLiteral("id")).toInt(m_lastEventId); m_lastEventId = event.value(QStringLiteral("id")).toInt(m_lastEventId);
DateTime eventTime; DateTime eventTime;
try { try {
eventTime = DateTime::fromIsoString(event.value(QStringLiteral("time")).toString().toLocal8Bit().data()).first; eventTime = DateTime::fromIsoStringGmt(event.value(QStringLiteral("time")).toString().toLocal8Bit().data());
} catch(const ConversionException &) { } catch(const ConversionException &) {
// ignore conversion error // ignore conversion error
} }
@ -651,6 +819,10 @@ void SyncthingConnection::readEvents()
readDirEvent(eventType, eventData); readDirEvent(eventType, eventData);
} else if(eventType.startsWith(QLatin1String("Device"))) { } else if(eventType.startsWith(QLatin1String("Device"))) {
readDeviceEvent(eventTime, eventType, eventData); readDeviceEvent(eventTime, eventType, eventData);
} else if(eventType == QLatin1String("ItemStarted")) {
readItemStarted(eventTime, eventData);
} else if(eventType == QLatin1String("ItemFinished")) {
readItemFinished(eventTime, eventData);
} }
} }
} else { } else {
@ -727,8 +899,13 @@ void SyncthingConnection::readStatusChangedEvent(const QJsonObject &eventData)
* \remarks TODO * \remarks TODO
*/ */
void SyncthingConnection::readDownloadProgressEvent(const QJsonObject &eventData) void SyncthingConnection::readDownloadProgressEvent(const QJsonObject &eventData)
{} {
VAR_UNUSED(eventData)
}
/*!
* \brief Reads results of requestEvents().
*/
void SyncthingConnection::readDirEvent(const QString &eventType, const QJsonObject &eventData) void SyncthingConnection::readDirEvent(const QString &eventType, const QJsonObject &eventData)
{ {
const QString dir(eventData.value(QStringLiteral("folder")).toString()); const QString dir(eventData.value(QStringLiteral("folder")).toString());
@ -743,6 +920,7 @@ void SyncthingConnection::readDirEvent(const QString &eventType, const QJsonObje
const QJsonObject error(errorVal.toObject()); const QJsonObject error(errorVal.toObject());
if(!error.isEmpty()) { if(!error.isEmpty()) {
dirInfo->errors.emplace_back(error.value(QStringLiteral("error")).toString(), error.value(QStringLiteral("path")).toString()); dirInfo->errors.emplace_back(error.value(QStringLiteral("error")).toString(), error.value(QStringLiteral("path")).toString());
dirInfo->status = DirStatus::OutOfSync;
emit newNotification(dirInfo->errors.back().message); emit newNotification(dirInfo->errors.back().message);
} }
} }
@ -774,9 +952,9 @@ void SyncthingConnection::readDirEvent(const QString &eventType, const QJsonObje
} }
} else if(eventType == QLatin1String("FolderScanProgress")) { } else if(eventType == QLatin1String("FolderScanProgress")) {
// FIXME: for some reason current is always 0 // FIXME: for some reason current is always 0
int current = eventData.value(QStringLiteral("current")).toInt(0); int current = eventData.value(QStringLiteral("current")).toInt(0),
int total = eventData.value(QStringLiteral("total")).toInt(0); total = eventData.value(QStringLiteral("total")).toInt(0),
int rate = eventData.value(QStringLiteral("rate")).toInt(0); rate = eventData.value(QStringLiteral("rate")).toInt(0);
if(current > 0 && total > 0) { if(current > 0 && total > 0) {
dirInfo->progressPercentage = current * 100 / total; dirInfo->progressPercentage = current * 100 / total;
dirInfo->progressRate = rate; dirInfo->progressRate = rate;
@ -830,6 +1008,46 @@ void SyncthingConnection::readDeviceEvent(DateTime eventTime, const QString &eve
} }
} }
/*!
* \brief Reads results of requestEvents().
*/
void SyncthingConnection::readItemStarted(DateTime eventTime, const QJsonObject &eventData)
{
VAR_UNUSED(eventTime)
VAR_UNUSED(eventData)
}
/*!
* \brief Reads results of requestEvents().
*/
void SyncthingConnection::readItemFinished(DateTime eventTime, const QJsonObject &eventData)
{
const QString dir(eventData.value(QStringLiteral("folder")).toString());
if(!dir.isEmpty()) {
int index;
if(SyncthingDir *dirInfo = findDirInfo(dir, index)) {
const QString error(eventData.value(QStringLiteral("error")).toString()),
item(eventData.value(QStringLiteral("item")).toString());
if(error.isEmpty()) {
if(dirInfo->lastFileTime.isNull() || eventTime < dirInfo->lastFileTime) {
dirInfo->lastFileTime = eventTime,
dirInfo->lastFileName = item,
dirInfo->lastFileDeleted = (eventData.value(QStringLiteral("action")) != QLatin1String("delete"));
if(eventTime > m_lastFileTime) {
m_lastFileTime = dirInfo->lastFileTime,
m_lastFileName = dirInfo->lastFileName,
m_lastFileDeleted = dirInfo->lastFileDeleted;
}
emit dirStatusChanged(*dirInfo, index);
}
} else {
dirInfo->errors.emplace_back(error, item);
emit newNotification(error);
}
}
}
}
/*! /*!
* \brief Reads results of rescan(). * \brief Reads results of rescan().
*/ */
@ -841,7 +1059,7 @@ void SyncthingConnection::readRescan()
case QNetworkReply::NoError: case QNetworkReply::NoError:
break; break;
default: default:
emit error(tr("Unable to request rescan: ") + reply->errorString()); emit error(tr("Unable to request rescan: ") + reply->errorString());
} }
} }
@ -856,7 +1074,7 @@ void SyncthingConnection::readPauseResume()
case QNetworkReply::NoError: case QNetworkReply::NoError:
break; break;
default: default:
emit error(tr("Unable to request pause/resume: ") + reply->errorString()); emit error(tr("Unable to request pause/resume: ") + reply->errorString());
} }
} }

View File

@ -4,6 +4,8 @@
#include <c++utilities/chrono/datetime.h> #include <c++utilities/chrono/datetime.h>
#include <QObject> #include <QObject>
#include <QList>
#include <QSslError>
#include <functional> #include <functional>
#include <vector> #include <vector>
@ -59,13 +61,17 @@ struct SyncthingDir
bool autoNormalize = false; bool autoNormalize = false;
int rescanInterval = 0; int rescanInterval = 0;
int minDiskFreePercentage = 0; int minDiskFreePercentage = 0;
DirStatus status = DirStatus::Unknown; DirStatus status = DirStatus::Idle;
int progressPercentage = 0; int progressPercentage = 0;
int progressRate = 0; int progressRate = 0;
std::vector<DirErrors> errors; std::vector<DirErrors> errors;
int globalBytes = 0, globalDeleted = 0, globalFiles = 0; int globalBytes = 0, globalDeleted = 0, globalFiles = 0;
int localBytes = 0, localDeleted = 0, localFiles = 0; int localBytes = 0, localDeleted = 0, localFiles = 0;
int neededByted = 0, neededFiles = 0; int neededByted = 0, neededFiles = 0;
ChronoUtilities::DateTime lastScanTime;
ChronoUtilities::DateTime lastFileTime;
QString lastFileName;
bool lastFileDeleted = false;
bool assignStatus(const QString &statusStr); bool assignStatus(const QString &statusStr);
}; };
@ -98,6 +104,7 @@ struct SyncthingDev
QString connectionAddress; QString connectionAddress;
QString connectionType; QString connectionType;
QString clientVersion; QString clientVersion;
ChronoUtilities::DateTime lastSeen;
}; };
struct SyncthingLogEntry struct SyncthingLogEntry
@ -141,8 +148,10 @@ public:
const std::vector<SyncthingDev> &devInfo() const; const std::vector<SyncthingDev> &devInfo() const;
void requestQrCode(const QString &text, std::function<void (const QPixmap &)> callback); void requestQrCode(const QString &text, std::function<void (const QPixmap &)> callback);
void requestLog(std::function<void (const std::vector<SyncthingLogEntry> &)> callback); void requestLog(std::function<void (const std::vector<SyncthingLogEntry> &)> callback);
static const QList<QSslError> &expectedCertificateErrors();
public Q_SLOTS: public Q_SLOTS:
void loadSelfSignedCertificate();
void connect(); void connect();
void disconnect(); void disconnect();
void reconnect(); void reconnect();
@ -226,6 +235,8 @@ private Q_SLOTS:
void requestConfig(); void requestConfig();
void requestStatus(); void requestStatus();
void requestConnections(); void requestConnections();
void requestDirStatistics();
void requestDeviceStatistics();
void requestEvents(); void requestEvents();
void abortAllRequests(); void abortAllRequests();
@ -234,12 +245,16 @@ private Q_SLOTS:
void readDevs(const QJsonArray &devs); void readDevs(const QJsonArray &devs);
void readStatus(); void readStatus();
void readConnections(); void readConnections();
void readDirStatistics();
void readDeviceStatistics();
void readEvents(); void readEvents();
void readStartingEvent(const QJsonObject &eventData); void readStartingEvent(const QJsonObject &eventData);
void readStatusChangedEvent(const QJsonObject &eventData); void readStatusChangedEvent(const QJsonObject &eventData);
void readDownloadProgressEvent(const QJsonObject &eventData); void readDownloadProgressEvent(const QJsonObject &eventData);
void readDirEvent(const QString &eventType, const QJsonObject &eventData); void readDirEvent(const QString &eventType, const QJsonObject &eventData);
void readDeviceEvent(ChronoUtilities::DateTime eventTime, const QString &eventType, const QJsonObject &eventData); void readDeviceEvent(ChronoUtilities::DateTime eventTime, const QString &eventType, const QJsonObject &eventData);
void readItemStarted(ChronoUtilities::DateTime eventTime, const QJsonObject &eventData);
void readItemFinished(ChronoUtilities::DateTime eventTime, const QJsonObject &eventData);
void readRescan(); void readRescan();
void readPauseResume(); void readPauseResume();
@ -277,6 +292,10 @@ private:
std::vector<SyncthingDir> m_dirs; std::vector<SyncthingDir> m_dirs;
std::vector<SyncthingDev> m_devs; std::vector<SyncthingDev> m_devs;
ChronoUtilities::DateTime m_lastConnectionsUpdate; ChronoUtilities::DateTime m_lastConnectionsUpdate;
ChronoUtilities::DateTime m_lastFileTime;
QString m_lastFileName;
bool m_lastFileDeleted;
static QList<QSslError> m_expectedCertificateErrors;
}; };
/*! /*!
@ -419,6 +438,15 @@ inline const std::vector<SyncthingDev> &SyncthingConnection::devInfo() const
return m_devs; return m_devs;
} }
/*!
* \brief Returns a list of all expected certificate errors.
* \remarks This list is shared by all instances and updated via loadSelfSignedCertificate().
*/
inline const QList<QSslError> &SyncthingConnection::expectedCertificateErrors()
{
return m_expectedCertificateErrors;
}
} }
#endif // SYNCTHINGCONNECTION_H #endif // SYNCTHINGCONNECTION_H

View File

@ -1,5 +1,8 @@
#include "./syncthingdevicemodel.h" #include "./syncthingdevicemodel.h"
#include "./syncthingconnection.h" #include "./syncthingconnection.h"
#include "./utils.h"
using namespace ChronoUtilities;
namespace Data { namespace Data {
@ -83,9 +86,10 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const
switch(index.row()) { switch(index.row()) {
case 0: return tr("ID"); case 0: return tr("ID");
case 1: return tr("Addresses"); case 1: return tr("Addresses");
case 2: return tr("Compression"); case 2: return tr("Last seen");
case 3: return tr("Certificate"); case 3: return tr("Compression");
case 4: return tr("Introducer"); case 4: return tr("Certificate");
case 5: return tr("Introducer");
} }
break; break;
case 1: // attribute values case 1: // attribute values
@ -93,13 +97,44 @@ QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const
switch(index.row()) { switch(index.row()) {
case 0: return dev.id; case 0: return dev.id;
case 1: return dev.addresses.join(QStringLiteral(", ")); case 1: return dev.addresses.join(QStringLiteral(", "));
case 2: return dev.compression; case 2: return dev.lastSeen.isNull() ? tr("unknown or own device") : QString::fromLatin1(dev.lastSeen.toString(DateTimeOutputFormat::DateAndTime, true).data());
case 3: return dev.certName.isEmpty() ? tr("none") : dev.certName; case 3: return dev.compression;
case 4: return dev.introducer ? tr("yes") : tr("no"); case 4: return dev.certName.isEmpty() ? tr("none") : dev.certName;
case 5: return dev.introducer ? tr("yes") : tr("no");
} }
break; break;
} }
break; break;
case Qt::ForegroundRole:
switch(index.column()) {
case 1:
const SyncthingDev &dev = m_devs[index.parent().row()];
switch(index.row()) {
case 2:
if(dev.lastSeen.isNull()) {
return QColor(Qt::gray);
}
break;
case 4:
if(dev.certName.isEmpty()) {
return QColor(Qt::gray);
}
break;
}
}
break;
case Qt::ToolTipRole:
switch(index.column()) {
case 1:
switch(index.row()) {
case 2:
const SyncthingDev &dev = m_devs[index.parent().row()];
if(!dev.lastSeen.isNull()) {
return agoString(dev.lastSeen);
}
break;
}
}
default: default:
; ;
} }
@ -196,7 +231,7 @@ int SyncthingDeviceModel::rowCount(const QModelIndex &parent) const
if(!parent.isValid()) { if(!parent.isValid()) {
return m_devs.size(); return m_devs.size();
} else if(!parent.parent().isValid()) { } else if(!parent.parent().isValid()) {
return 5; return 6;
} else { } else {
return 0; return 0;
} }

View File

@ -1,5 +1,8 @@
#include "./syncthingdirectorymodel.h" #include "./syncthingdirectorymodel.h"
#include "./syncthingconnection.h" #include "./syncthingconnection.h"
#include "./utils.h"
using namespace ChronoUtilities;
namespace Data { namespace Data {
@ -86,6 +89,8 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const
case 2: return tr("Devices"); case 2: return tr("Devices");
case 3: return tr("Read-only"); case 3: return tr("Read-only");
case 4: return tr("Rescan interval"); case 4: return tr("Rescan interval");
case 5: return tr("Last scan");
case 6: return tr("Last file");
} }
break; break;
case 1: // attribute values case 1: // attribute values
@ -96,10 +101,53 @@ QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const
case 2: return dir.devices.join(QStringLiteral(", ")); case 2: return dir.devices.join(QStringLiteral(", "));
case 3: return dir.readOnly ? tr("yes") : tr("no"); case 3: return dir.readOnly ? tr("yes") : tr("no");
case 4: return QStringLiteral("%1 s").arg(dir.rescanInterval); case 4: return QStringLiteral("%1 s").arg(dir.rescanInterval);
case 5: return dir.lastScanTime.isNull() ? tr("unknown") : QString::fromLatin1(dir.lastScanTime.toString(DateTimeOutputFormat::DateAndTime, true).data());
case 6: return dir.lastFileName.isEmpty() ? tr("unknown") : dir.lastFileName;
} }
break; break;
} }
break; break;
case Qt::ForegroundRole:
switch(index.column()) {
case 1:
const SyncthingDir &dir = m_dirs[index.parent().row()];
switch(index.row()) {
case 5:
if(dir.lastScanTime.isNull()) {
return QColor(Qt::gray);
}
break;
case 6:
if(dir.lastFileName.isEmpty()) {
return QColor(Qt::gray);
} else if(dir.lastFileDeleted) {
return QColor(Qt::red);
}
break;
}
}
break;
case Qt::ToolTipRole:
switch(index.column()) {
case 1:
const SyncthingDir &dir = m_dirs[index.parent().row()];
switch(index.row()) {
case 5:
if(!dir.lastScanTime.isNull()) {
return agoString(dir.lastScanTime);
}
break;
case 6:
if(!dir.lastFileTime.isNull()) {
if(dir.lastFileDeleted) {
return tr("Deleted at %1").arg(QString::fromLatin1(dir.lastFileTime.toString(DateTimeOutputFormat::DateAndTime, true).data()));
} else {
return tr("Updated at %1").arg(QString::fromLatin1(dir.lastFileTime.toString(DateTimeOutputFormat::DateAndTime, true).data()));
}
}
break;
}
}
default: default:
; ;
} }
@ -177,7 +225,7 @@ int SyncthingDirectoryModel::rowCount(const QModelIndex &parent) const
if(!parent.isValid()) { if(!parent.isValid()) {
return m_dirs.size(); return m_dirs.size();
} else if(!parent.parent().isValid()) { } else if(!parent.parent().isValid()) {
return 5; return 7;
} else { } else {
return 0; return 0;
} }

22
data/utils.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "utils.h"
#include <c++utilities/chrono/datetime.h>
#include <QString>
#include <QCoreApplication>
using namespace ChronoUtilities;
namespace Data {
QString agoString(DateTime dateTime)
{
const TimeSpan delta(DateTime::now() - dateTime);
if(!delta.isNegative() && static_cast<uint64>(delta.totalTicks()) > (TimeSpan::ticksPerMinute / 4uL)) {
return QCoreApplication::translate("Data::Utils", "%1 ago").arg(QString::fromLatin1(delta.toString(TimeSpanOutputFormat::WithMeasures, true).data()));
} else {
return QCoreApplication::translate("Data::Utils", "right now");
}
}
}

18
data/utils.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef DATA_UTILS_H
#define DATA_UTILS_H
#include <QtGlobal>
QT_FORWARD_DECLARE_CLASS(QString)
namespace ChronoUtilities {
class DateTime;
}
namespace Data {
QString agoString(ChronoUtilities::DateTime dateTime);
}
#endif // DATA_UTILS_H

View File

@ -2,29 +2,85 @@
<ui version="4.0"> <ui version="4.0">
<class>QtGui::AppearanceOptionPage</class> <class>QtGui::AppearanceOptionPage</class>
<widget class="QWidget" name="QtGui::AppearanceOptionPage"> <widget class="QWidget" name="QtGui::AppearanceOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>283</width>
<height>74</height>
</rect>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Appearance</string> <string>Appearance</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <property name="windowIcon">
<item> <iconset theme="preferences-desktop"/>
<widget class="QCheckBox" name="showTrafficCheckBox"> </property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="sizeLabel">
<property name="text"> <property name="text">
<string>Show traffic</string> <string>Menu size</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="2" column="0">
<spacer name="verticalSpacer"> <widget class="QLabel" name="guiElementsLabel">
<property name="orientation"> <property name="text">
<enum>Qt::Vertical</enum> <string>Optional GUI elements</string>
</property> </property>
<property name="sizeHint" stdset="0"> </widget>
<size> </item>
<width>20</width> <item row="2" column="1">
<height>40</height> <widget class="QCheckBox" name="showTrafficCheckBox">
</size> <property name="text">
<string>Traffic statistics</string>
</property> </property>
</spacer> </widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="widthSpinBox">
<property name="minimum">
<number>150</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sizeTimesLabel">
<property name="text">
<string>x</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="heightSpinBox">
<property name="minimum">
<number>150</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sizePxLabel">
<property name="text">
<string>px</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

@ -2,9 +2,20 @@
<ui version="4.0"> <ui version="4.0">
<class>QtGui::AutostartOptionPage</class> <class>QtGui::AutostartOptionPage</class>
<widget class="QWidget" name="QtGui::AutostartOptionPage"> <widget class="QWidget" name="QtGui::AutostartOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>52</height>
</rect>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Autostart</string> <string>Autostart</string>
</property> </property>
<property name="windowIcon">
<iconset theme="system-run"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">

View File

@ -2,17 +2,12 @@
<ui version="4.0"> <ui version="4.0">
<class>QtGui::ConnectionOptionPage</class> <class>QtGui::ConnectionOptionPage</class>
<widget class="QWidget" name="QtGui::ConnectionOptionPage"> <widget class="QWidget" name="QtGui::ConnectionOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>433</width>
<height>267</height>
</rect>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Connection</string> <string>Connection</string>
</property> </property>
<property name="windowIcon">
<iconset theme="network-connect"/>
</property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="urlLabel"> <widget class="QLabel" name="urlLabel">
@ -71,28 +66,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="11" column="1">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="statusLabel"> <widget class="QLabel" name="statusLabel">
<property name="text"> <property name="text">
<string>disconnected</string> <string>disconnected</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0"> <item row="11" column="0">
<widget class="QLabel" name="statusTextLabel"> <widget class="QLabel" name="statusTextLabel">
<property name="text"> <property name="text">
<string>Status</string> <string>Status</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="12" column="1">
<widget class="QPushButton" name="connectPushButton"> <widget class="QPushButton" name="connectPushButton">
<property name="text"> <property name="text">
<string>Apply connection settings and try to reconnect</string> <string>Apply connection settings and try to reconnect</string>
@ -113,6 +101,33 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1">
<widget class="QPushButton" name="insertFromConfigFilePushButton">
<property name="styleSheet">
<string notr="true">font-weight: bold;</string>
</property>
<property name="text">
<string>Insert values from local Syncthing configuration</string>
</property>
<property name="icon">
<iconset theme="edit-paste">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="Line" name="line">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>

View File

@ -2,43 +2,46 @@
<ui version="4.0"> <ui version="4.0">
<class>QtGui::NotificationsOptionPage</class> <class>QtGui::NotificationsOptionPage</class>
<widget class="QWidget" name="QtGui::NotificationsOptionPage"> <widget class="QWidget" name="QtGui::NotificationsOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>175</width>
<height>156</height>
</rect>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Notifications</string> <string>Notifications</string>
</property> </property>
<property name="windowIcon">
<iconset theme="preferences-desktop-notification">
<normaloff>.</normaloff>.</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="notifyOnLabel">
<property name="text">
<string>Notify on</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="notifyOnDisconnectCheckBox"> <widget class="QCheckBox" name="notifyOnDisconnectCheckBox">
<property name="text"> <property name="text">
<string>Notify on disconnect</string> <string>disconnect</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="notifyOnErrorsCheckBox"> <widget class="QCheckBox" name="notifyOnErrorsCheckBox">
<property name="text"> <property name="text">
<string>Notify on internal errors</string> <string>internal errors</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="showSyncthingNotificationsCheckBox"> <widget class="QCheckBox" name="showSyncthingNotificationsCheckBox">
<property name="text"> <property name="text">
<string>Notify on Syncthing errors</string> <string>errors/notifications from Syncthing</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="notifyOnSyncCompleteCheckBox"> <widget class="QCheckBox" name="notifyOnSyncCompleteCheckBox">
<property name="text"> <property name="text">
<string>Notify on sync complete</string> <string>sync complete</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -2,6 +2,7 @@
#include "../application/settings.h" #include "../application/settings.h"
#include "../data/syncthingconnection.h" #include "../data/syncthingconnection.h"
#include "../data/syncthingconfig.h"
#include "ui_connectionoptionpage.h" #include "ui_connectionoptionpage.h"
#include "ui_notificationsoptionpage.h" #include "ui_notificationsoptionpage.h"
@ -9,6 +10,8 @@
#include "ui_autostartoptionpage.h" #include "ui_autostartoptionpage.h"
#include "ui_webviewoptionpage.h" #include "ui_webviewoptionpage.h"
#include "resources/config.h"
#include <tagparser/mediafileinfo.h> #include <tagparser/mediafileinfo.h>
#include <tagparser/backuphelper.h> #include <tagparser/backuphelper.h>
@ -16,6 +19,10 @@
#include <qtutilities/settingsdialog/optioncategorymodel.h> #include <qtutilities/settingsdialog/optioncategorymodel.h>
#include <qtutilities/settingsdialog/qtsettings.h> #include <qtutilities/settingsdialog/qtsettings.h>
#include <QFileDialog>
#include <QMessageBox>
#include <QHostAddress>
#include <functional> #include <functional>
using namespace std; using namespace std;
@ -40,12 +47,49 @@ QWidget *ConnectionOptionPage::setupWidget()
updateConnectionStatus(); updateConnectionStatus();
QObject::connect(m_connection, &SyncthingConnection::statusChanged, bind(&ConnectionOptionPage::updateConnectionStatus, this)); QObject::connect(m_connection, &SyncthingConnection::statusChanged, bind(&ConnectionOptionPage::updateConnectionStatus, this));
QObject::connect(ui()->connectPushButton, &QPushButton::clicked, bind(&ConnectionOptionPage::applyAndReconnect, this)); QObject::connect(ui()->connectPushButton, &QPushButton::clicked, bind(&ConnectionOptionPage::applyAndReconnect, this));
QObject::connect(ui()->insertFromConfigFilePushButton, &QPushButton::clicked, bind(&ConnectionOptionPage::insertFromConfigFile, this));
return w; return w;
} }
void ConnectionOptionPage::insertFromConfigFile()
{
if(hasBeenShown()) {
QString configFile = SyncthingConfig::locateConfigFile();
if(configFile.isEmpty()) {
// allow user to select config file manually if it could not be located
configFile = QFileDialog::getOpenFileName(widget(), QCoreApplication::translate("QtGui::ConnectionOptionPage", "Select Syncthing config file") + QStringLiteral(" - " APP_NAME));
}
if(configFile.isEmpty()) {
return;
}
SyncthingConfig config;
if(!config.restore(configFile)) {
QMessageBox::critical(widget(), widget()->windowTitle() + QStringLiteral(" - " APP_NAME), QCoreApplication::translate("QtGui::ConnectionOptionPage", "Unable to parse the Syncthing config file."));
return;
}
if(!config.guiAddress.isEmpty()) {
ui()->urlLineEdit->selectAll();
ui()->urlLineEdit->insert(((config.guiEnforcesSecureConnection || !QHostAddress(config.guiAddress.mid(0, config.guiAddress.indexOf(QChar(':')))).isLoopback()) ? QStringLiteral("https://") : QStringLiteral("http://")) + config.guiAddress);
}
if(!config.guiUser.isEmpty() || !config.guiPasswordHash.isEmpty()) {
ui()->authCheckBox->setChecked(true);
ui()->userNameLineEdit->selectAll();
ui()->userNameLineEdit->insert(config.guiUser);
} else {
ui()->authCheckBox->setChecked(false);
}
if(!config.guiApiKey.isEmpty()) {
ui()->apiKeyLineEdit->selectAll();
ui()->apiKeyLineEdit->insert(config.guiApiKey);
}
}
}
void ConnectionOptionPage::updateConnectionStatus() void ConnectionOptionPage::updateConnectionStatus()
{ {
ui()->statusLabel->setText(m_connection->statusText()); if(hasBeenShown()) {
ui()->statusLabel->setText(m_connection->statusText());
}
} }
bool ConnectionOptionPage::apply() bool ConnectionOptionPage::apply()
@ -97,7 +141,7 @@ bool NotificationsOptionPage::apply()
{ {
if(hasBeenShown()) { if(hasBeenShown()) {
notifyOnDisconnect() = ui()->notifyOnDisconnectCheckBox->isChecked(); notifyOnDisconnect() = ui()->notifyOnDisconnectCheckBox->isChecked();
notifyOnErrors() = ui()->notifyOnErrorsCheckBox->isChecked(); notifyOnInternalErrors() = ui()->notifyOnErrorsCheckBox->isChecked();
notifyOnSyncComplete() = ui()->notifyOnSyncCompleteCheckBox->isChecked(); notifyOnSyncComplete() = ui()->notifyOnSyncCompleteCheckBox->isChecked();
showSyncthingNotifications() = ui()->showSyncthingNotificationsCheckBox->isChecked(); showSyncthingNotifications() = ui()->showSyncthingNotificationsCheckBox->isChecked();
} }
@ -108,7 +152,7 @@ void NotificationsOptionPage::reset()
{ {
if(hasBeenShown()) { if(hasBeenShown()) {
ui()->notifyOnDisconnectCheckBox->setChecked(notifyOnDisconnect()); ui()->notifyOnDisconnectCheckBox->setChecked(notifyOnDisconnect());
ui()->notifyOnErrorsCheckBox->setChecked(notifyOnErrors()); ui()->notifyOnErrorsCheckBox->setChecked(notifyOnInternalErrors());
ui()->notifyOnSyncCompleteCheckBox->setChecked(notifyOnSyncComplete()); ui()->notifyOnSyncCompleteCheckBox->setChecked(notifyOnSyncComplete());
ui()->showSyncthingNotificationsCheckBox->setChecked(showSyncthingNotifications()); ui()->showSyncthingNotificationsCheckBox->setChecked(showSyncthingNotifications());
} }
@ -125,6 +169,8 @@ AppearanceOptionPage::~AppearanceOptionPage()
bool AppearanceOptionPage::apply() bool AppearanceOptionPage::apply()
{ {
if(hasBeenShown()) { if(hasBeenShown()) {
trayMenuSize().setWidth(ui()->widthSpinBox->value());
trayMenuSize().setHeight(ui()->heightSpinBox->value());
showTraffic() = ui()->showTrafficCheckBox->isChecked(); showTraffic() = ui()->showTrafficCheckBox->isChecked();
} }
return true; return true;
@ -133,6 +179,8 @@ bool AppearanceOptionPage::apply()
void AppearanceOptionPage::reset() void AppearanceOptionPage::reset()
{ {
if(hasBeenShown()) { if(hasBeenShown()) {
ui()->widthSpinBox->setValue(trayMenuSize().width());
ui()->heightSpinBox->setValue(trayMenuSize().height());
ui()->showTrafficCheckBox->setChecked(showTraffic()); ui()->showTrafficCheckBox->setChecked(showTraffic());
} }
} }

View File

@ -23,6 +23,7 @@ public:
ConnectionOptionPage(Data::SyncthingConnection *connection, QWidget *parentWidget = nullptr); ConnectionOptionPage(Data::SyncthingConnection *connection, QWidget *parentWidget = nullptr);
private: private:
DECLARE_SETUP_WIDGETS DECLARE_SETUP_WIDGETS
void insertFromConfigFile();
void updateConnectionStatus(); void updateConnectionStatus();
void applyAndReconnect(); void applyAndReconnect();
Data::SyncthingConnection *m_connection; Data::SyncthingConnection *m_connection;

View File

@ -43,7 +43,7 @@ TrayIcon::TrayIcon(QObject *parent) :
// connect signals and slots // connect signals and slots
SyncthingConnection *connection = &(m_trayMenu.widget()->connection()); SyncthingConnection *connection = &(m_trayMenu.widget()->connection());
connect(this, &TrayIcon::activated, this, &TrayIcon::handleActivated); connect(this, &TrayIcon::activated, this, &TrayIcon::handleActivated);
connect(connection, &SyncthingConnection::error, this, &TrayIcon::showSyncthingError); connect(connection, &SyncthingConnection::error, this, &TrayIcon::showInternalError);
connect(connection, &SyncthingConnection::newNotification, this, &TrayIcon::showSyncthingNotification); connect(connection, &SyncthingConnection::newNotification, this, &TrayIcon::showSyncthingNotification);
connect(connection, &SyncthingConnection::statusChanged, this, &TrayIcon::updateStatusIconAndText); connect(connection, &SyncthingConnection::statusChanged, this, &TrayIcon::updateStatusIconAndText);
} }
@ -72,17 +72,17 @@ void TrayIcon::handleActivated(QSystemTrayIcon::ActivationReason reason)
} }
} }
void TrayIcon::showSyncthingError(const QString &errorMsg) void TrayIcon::showInternalError(const QString &errorMsg)
{ {
if(Settings::notifyOnErrors()) { if(Settings::notifyOnInternalErrors()) {
showMessage(tr("Syncthing error"), errorMsg, QSystemTrayIcon::Critical); showMessage(tr("Error"), errorMsg, QSystemTrayIcon::Critical);
} }
} }
void TrayIcon::showSyncthingNotification(const QString &message) void TrayIcon::showSyncthingNotification(const QString &message)
{ {
if(Settings::showSyncthingNotifications()) { if(Settings::showSyncthingNotifications()) {
showMessage(tr("Syncthing notification"), message, QSystemTrayIcon::Information); showMessage(tr("Syncthing notification"), message, QSystemTrayIcon::Warning);
} }
} }

View File

@ -22,10 +22,11 @@ class TrayIcon : public QSystemTrayIcon
public: public:
TrayIcon(QObject *parent = nullptr); TrayIcon(QObject *parent = nullptr);
TrayMenu &trayMenu();
private slots: private slots:
void handleActivated(QSystemTrayIcon::ActivationReason reason); void handleActivated(QSystemTrayIcon::ActivationReason reason);
void showSyncthingError(const QString &errorMsg); void showInternalError(const QString &errorMsg);
void showSyncthingNotification(const QString &message); void showSyncthingNotification(const QString &message);
void updateStatusIconAndText(Data::SyncthingStatus status); void updateStatusIconAndText(Data::SyncthingStatus status);
@ -43,6 +44,11 @@ private:
Data::SyncthingStatus m_status; Data::SyncthingStatus m_status;
}; };
inline TrayMenu &TrayIcon::trayMenu()
{
return m_trayMenu;
}
} }
#endif // TRAY_ICON_H #endif // TRAY_ICON_H

View File

@ -1,6 +1,8 @@
#include "./traymenu.h" #include "./traymenu.h"
#include "./traywidget.h" #include "./traywidget.h"
#include "../application/settings.h"
#include <QHBoxLayout> #include <QHBoxLayout>
namespace QtGui { namespace QtGui {
@ -17,7 +19,7 @@ TrayMenu::TrayMenu(QWidget *parent) :
QSize TrayMenu::sizeHint() const QSize TrayMenu::sizeHint() const
{ {
return QSize(350, 300); return Settings::trayMenuSize();
} }
} }

View File

@ -95,6 +95,7 @@ TrayWidget::TrayWidget(TrayMenu *parent) :
connect(m_ui->settingsPushButton, &QPushButton::clicked, this, &TrayWidget::showSettingsDialog); connect(m_ui->settingsPushButton, &QPushButton::clicked, this, &TrayWidget::showSettingsDialog);
connect(&m_connection, &SyncthingConnection::statusChanged, this, &TrayWidget::updateStatusButton); connect(&m_connection, &SyncthingConnection::statusChanged, this, &TrayWidget::updateStatusButton);
connect(&m_connection, &SyncthingConnection::trafficChanged, this, &TrayWidget::updateTraffic); connect(&m_connection, &SyncthingConnection::trafficChanged, this, &TrayWidget::updateTraffic);
connect(&m_connection, &SyncthingConnection::newNotification, this, &TrayWidget::handleNewNotification);
connect(m_ui->dirsTreeView, &DirView::openDir, this, &TrayWidget::openDir); connect(m_ui->dirsTreeView, &DirView::openDir, this, &TrayWidget::openDir);
connect(m_ui->dirsTreeView, &DirView::scanDir, this, &TrayWidget::scanDir); connect(m_ui->dirsTreeView, &DirView::scanDir, this, &TrayWidget::scanDir);
connect(m_ui->devsTreeView, &DevView::pauseResumeDev, this, &TrayWidget::pauseResumeDev); connect(m_ui->devsTreeView, &DevView::pauseResumeDev, this, &TrayWidget::pauseResumeDev);
@ -109,7 +110,7 @@ void TrayWidget::showSettingsDialog()
{ {
if(!m_settingsDlg) { if(!m_settingsDlg) {
m_settingsDlg = new SettingsDialog(&m_connection, this); m_settingsDlg = new SettingsDialog(&m_connection, this);
m_settingsDlg->setWindowTitle(tr("Settings - Syncthing tray")); m_settingsDlg->setWindowTitle(tr("Settings") + QStringLiteral(" - " APP_NAME));
connect(m_settingsDlg, &SettingsDialog::applied, this, &TrayWidget::applySettings); connect(m_settingsDlg, &SettingsDialog::applied, this, &TrayWidget::applySettings);
#ifndef SYNCTHINGTRAY_NO_WEBVIEW #ifndef SYNCTHINGTRAY_NO_WEBVIEW
if(m_webViewDlg) { if(m_webViewDlg) {
@ -128,8 +129,8 @@ void TrayWidget::showSettingsDialog()
void TrayWidget::showAboutDialog() void TrayWidget::showAboutDialog()
{ {
if(!m_aboutDlg) { if(!m_aboutDlg) {
m_aboutDlg = new AboutDialog(this, QString(), QStringLiteral(APP_AUTHOR "\nfallback icons from KDE/Breeze project\nSyncthing icons from Syncthing project"), QString(), QString(), tr("Tray application for Syncthing"), QImage(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg"))); m_aboutDlg = new AboutDialog(this, QString(), QStringLiteral(APP_AUTHOR "\nfallback icons from KDE/Breeze project\nSyncthing icons from Syncthing project"), QString(), QString(), QStringLiteral(APP_DESCRIPTION), QImage(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
m_aboutDlg->setWindowTitle(tr("About - Syncthing Tray")); m_aboutDlg->setWindowTitle(tr("About") + QStringLiteral(" - " APP_NAME));
m_aboutDlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg"))); m_aboutDlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
} }
m_aboutDlg->show(); m_aboutDlg->show();
@ -167,7 +168,7 @@ void TrayWidget::showWebUi()
void TrayWidget::showOwnDeviceId() void TrayWidget::showOwnDeviceId()
{ {
auto *dlg = new QDialog(this); auto *dlg = new QDialog(this);
dlg->setWindowTitle(tr("Own device ID - Syncthing Tray")); dlg->setWindowTitle(tr("Own device ID") + QStringLiteral(" - " APP_NAME));
dlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg"))); dlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setBackgroundRole(QPalette::Background); dlg->setBackgroundRole(QPalette::Background);
@ -201,7 +202,7 @@ void TrayWidget::showOwnDeviceId()
void TrayWidget::showLog() void TrayWidget::showLog()
{ {
auto *dlg = new QDialog(this); auto *dlg = new QDialog(this);
dlg->setWindowTitle(tr("Log - Syncthing")); dlg->setWindowTitle(tr("Log") + QStringLiteral(" - " APP_NAME));
dlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg"))); dlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setAttribute(Qt::WA_DeleteOnClose);
auto *layout = new QVBoxLayout(dlg); auto *layout = new QVBoxLayout(dlg);
@ -255,6 +256,7 @@ void TrayWidget::applySettings()
} else { } else {
m_connection.setCredentials(QString(), QString()); m_connection.setCredentials(QString(), QString());
} }
m_connection.loadSelfSignedCertificate();
m_connection.reconnect(); m_connection.reconnect();
m_ui->trafficFrame->setVisible(Settings::showTraffic()); m_ui->trafficFrame->setVisible(Settings::showTraffic());
if(Settings::showTraffic()) { if(Settings::showTraffic()) {
@ -268,7 +270,7 @@ void TrayWidget::openDir(const QModelIndex &dirIndex)
if(QDir(dir->path).exists()) { if(QDir(dir->path).exists()) {
DesktopUtils::openLocalFileOrDir(dir->path); DesktopUtils::openLocalFileOrDir(dir->path);
} else { } else {
QMessageBox::warning(this, QCoreApplication::applicationName(), tr("The directly <i>%1</i> does not exist on the local machine.").arg(dir->path)); QMessageBox::warning(this, QCoreApplication::applicationName(), tr("The directory <i>%1</i> does not exist on the local machine.").arg(dir->path));
} }
} }
} }
@ -335,4 +337,9 @@ void TrayWidget::handleWebViewDeleted()
m_webViewDlg = nullptr; m_webViewDlg = nullptr;
} }
void TrayWidget::handleNewNotification(const QString &msg)
{
// FIXME
}
} }

View File

@ -55,6 +55,7 @@ private slots:
void changeStatus(); void changeStatus();
void updateTraffic(); void updateTraffic();
void handleWebViewDeleted(); void handleWebViewDeleted();
void handleNewNotification(const QString &msg);
private: private:
TrayMenu *m_menu; TrayMenu *m_menu;

View File

@ -13,12 +13,16 @@
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) #if defined(SYNCTHINGTRAY_USE_WEBENGINE)
# include <QWebEngineSettings> # include <QWebEngineSettings>
# include <QWebEngineView> # include <QWebEngineView>
# include <QWebEngineCertificateError>
#elif defined(SYNCTHINGTRAY_USE_WEBKIT) #elif defined(SYNCTHINGTRAY_USE_WEBKIT)
# include <QWebSettings> # include <QWebSettings>
# include <QWebView> # include <QWebView>
# include <QWebFrame> # include <QWebFrame>
# include <QSslError>
#endif #endif
using namespace Data;
namespace QtGui { namespace QtGui {
WebPage::WebPage(WEB_VIEW_PROVIDER *view) : WebPage::WebPage(WEB_VIEW_PROVIDER *view) :
@ -32,6 +36,7 @@ WebPage::WebPage(WEB_VIEW_PROVIDER *view) :
settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true); settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
setNetworkAccessManager(&Data::networkAccessManager()); setNetworkAccessManager(&Data::networkAccessManager());
connect(&Data::networkAccessManager(), &QNetworkAccessManager::authenticationRequired, this, static_cast<void(WebPage::*)(QNetworkReply *, QAuthenticator *)>(&WebPage::supplyCredentials)); connect(&Data::networkAccessManager(), &QNetworkAccessManager::authenticationRequired, this, static_cast<void(WebPage::*)(QNetworkReply *, QAuthenticator *)>(&WebPage::supplyCredentials));
connect(&Data::networkAccessManager(), &QNetworkAccessManager::sslErrors, this, static_cast<void(WebPage::*)(QNetworkReply *, const QList<QSslError> &errors)>(&WebPage::handleSslErrors));
#endif #endif
if(!m_view) { if(!m_view) {
// delegate to external browser if no view is assigned // delegate to external browser if no view is assigned
@ -50,9 +55,24 @@ WEB_PAGE_PROVIDER *WebPage::createWindow(WEB_PAGE_PROVIDER::WebWindowType type)
return new WebPage; return new WebPage;
} }
#ifdef SYNCTHINGTRAY_USE_WEBENGINE
bool WebPage::certificateError(const QWebEngineCertificateError &certificateError)
{
switch(certificateError.error()) {
case QWebEngineCertificateError::CertificateCommonNameInvalid:
case QWebEngineCertificateError::CertificateAuthorityInvalid:
// FIXME: only ignore the error if the used certificate matches the certificate
// known to be used by the Syncthing GUI
return true;
default:
return false;
}
}
#endif
void WebPage::delegateToExternalBrowser(const QUrl &url) void WebPage::delegateToExternalBrowser(const QUrl &url)
{ {
openUrlExternal(url); QDesktopServices::openUrl(url);
// this page and the associated view are useless // this page and the associated view are useless
m_view->deleteLater(); m_view->deleteLater();
deleteLater(); deleteLater();
@ -78,10 +98,14 @@ void WebPage::supplyCredentials(QAuthenticator *authenticator)
} }
} }
void WebPage::openUrlExternal(const QUrl &url) #ifdef SYNCTHINGTRAY_USE_WEBKIT
void WebPage::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{ {
QDesktopServices::openUrl(url); if(reply->request().url().host() == m_view->url().host()) {
reply->ignoreSslErrors(SyncthingConnection::expectedCertificateErrors());
}
} }
#endif
} }

View File

@ -13,6 +13,7 @@
QT_FORWARD_DECLARE_CLASS(WEB_VIEW_PROVIDER) QT_FORWARD_DECLARE_CLASS(WEB_VIEW_PROVIDER)
QT_FORWARD_DECLARE_CLASS(QAuthenticator) QT_FORWARD_DECLARE_CLASS(QAuthenticator)
QT_FORWARD_DECLARE_CLASS(QNetworkReply) QT_FORWARD_DECLARE_CLASS(QNetworkReply)
QT_FORWARD_DECLARE_CLASS(QSslError)
namespace QtGui { namespace QtGui {
@ -22,17 +23,20 @@ class WebPage : public WEB_PAGE_PROVIDER
public: public:
WebPage(WEB_VIEW_PROVIDER *view = nullptr); WebPage(WEB_VIEW_PROVIDER *view = nullptr);
public slots:
void openUrlExternal(const QUrl &url);
protected: protected:
WEB_PAGE_PROVIDER *createWindow(WebWindowType type); WEB_PAGE_PROVIDER *createWindow(WebWindowType type);
#ifdef SYNCTHINGTRAY_USE_WEBENGINE
bool certificateError(const QWebEngineCertificateError &certificateError);
#endif
private slots: private slots:
void delegateToExternalBrowser(const QUrl &url); void delegateToExternalBrowser(const QUrl &url);
void supplyCredentials(const QUrl &requestUrl, QAuthenticator *authenticator); void supplyCredentials(const QUrl &requestUrl, QAuthenticator *authenticator);
void supplyCredentials(QNetworkReply *reply, QAuthenticator *authenticator); void supplyCredentials(QNetworkReply *reply, QAuthenticator *authenticator);
void supplyCredentials(QAuthenticator *authenticator); void supplyCredentials(QAuthenticator *authenticator);
#ifdef SYNCTHINGTRAY_USE_WEBKIT
void handleSslErrors(QNetworkReply *, const QList<QSslError> &errors);
#endif
private: private:
WEB_VIEW_PROVIDER *m_view; WEB_VIEW_PROVIDER *m_view;

View File

@ -3,7 +3,6 @@
#include "./webpage.h" #include "./webpage.h"
#include "../application/settings.h" #include "../application/settings.h"
#include "../data/syncthingconnection.h"
#include <qtutilities/misc/dialogutils.h> #include <qtutilities/misc/dialogutils.h>
@ -29,6 +28,7 @@ WebViewDialog::WebViewDialog(QWidget *parent) :
m_view->setPage(new WebPage(m_view)); m_view->setPage(new WebPage(m_view));
applySettings(); applySettings();
if(Settings::webViewGeometry().isEmpty()) { if(Settings::webViewGeometry().isEmpty()) {
@ -60,4 +60,4 @@ void QtGui::WebViewDialog::closeEvent(QCloseEvent *event)
} }
#endif #endif // SYNCTHINGTRAY_NO_WEBVIEW

View File

@ -29,5 +29,5 @@ private:
} }
#endif #endif // SYNCTHINGTRAY_NO_WEBVIEW
#endif // WEBVIEW_DIALOG_H #endif // WEBVIEW_DIALOG_H

View File

@ -4,89 +4,119 @@
<context> <context>
<name>Data::SyncthingConnection</name> <name>Data::SyncthingConnection</name>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="108"/> <location filename="../data/syncthingconnection.cpp" line="120"/>
<source>disconnected</source> <source>disconnected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="110"/> <location filename="../data/syncthingconnection.cpp" line="122"/>
<source>connected</source> <source>connected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="112"/> <location filename="../data/syncthingconnection.cpp" line="124"/>
<source>connected, notifications available</source> <source>connected, notifications available</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="114"/> <location filename="../data/syncthingconnection.cpp" line="126"/>
<source>connected, paused</source> <source>connected, paused</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="116"/> <location filename="../data/syncthingconnection.cpp" line="128"/>
<source>connected, synchronizing</source> <source>connected, synchronizing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="118"/> <location filename="../data/syncthingconnection.cpp" line="130"/>
<source>unknown</source> <source>unknown</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="393"/> <location filename="../data/syncthingconnection.cpp" line="427"/>
<source>Unable to parse Syncthing log: </source> <source>Unable to parse Syncthing log: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="397"/> <location filename="../data/syncthingconnection.cpp" line="431"/>
<source>Unable to request system log: </source> <source>Unable to request system log: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="425"/> <location filename="../data/syncthingconnection.cpp" line="452"/>
<location filename="../data/syncthingconnection.cpp" line="525"/> <source>Unable to locate certificate used by Syncthing GUI.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="458"/>
<source>Unable to load certificate used by Syncthing GUI.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="490"/>
<location filename="../data/syncthingconnection.cpp" line="590"/>
<source>Unable to parse Syncthing config: </source> <source>Unable to parse Syncthing config: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="430"/> <location filename="../data/syncthingconnection.cpp" line="495"/>
<location filename="../data/syncthingconnection.cpp" line="530"/> <location filename="../data/syncthingconnection.cpp" line="595"/>
<source>Unable to request Syncthing config: </source> <source>Unable to request Syncthing config: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="605"/> <location filename="../data/syncthingconnection.cpp" line="670"/>
<source>Unable to parse connections: </source> <source>Unable to parse connections: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="610"/> <location filename="../data/syncthingconnection.cpp" line="675"/>
<source>Unable to request connections: </source> <source>Unable to request connections: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="657"/> <location filename="../data/syncthingconnection.cpp" line="725"/>
<source>Unable to parse directory statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="730"/>
<source>Unable to request directory statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="767"/>
<source>Unable to parse device statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="772"/>
<source>Unable to request device statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="823"/>
<source>Unable to parse Syncthing events: </source> <source>Unable to parse Syncthing events: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="679"/> <location filename="../data/syncthingconnection.cpp" line="845"/>
<source>Unable to request Syncthing events: </source> <source>Unable to request Syncthing events: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="844"/> <location filename="../data/syncthingconnection.cpp" line="1051"/>
<source>Unable to request rescan: </source> <source>Unable to request rescan: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="859"/> <location filename="../data/syncthingconnection.cpp" line="1066"/>
<source>Unable to request pause/resume: </source> <source>Unable to request pause/resume: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="361"/> <location filename="../data/syncthingconnection.cpp" line="395"/>
<source>Unable to request QR-Code: </source> <source>Unable to request QR-Code: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -94,93 +124,103 @@
<context> <context>
<name>Data::SyncthingDeviceModel</name> <name>Data::SyncthingDeviceModel</name>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="58"/> <location filename="../data/syncthingdevicemodel.cpp" line="61"/>
<location filename="../data/syncthingdevicemodel.cpp" line="84"/> <location filename="../data/syncthingdevicemodel.cpp" line="87"/>
<source>ID</source> <source>ID</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="59"/> <location filename="../data/syncthingdevicemodel.cpp" line="62"/>
<source>Status</source> <source>Status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="85"/> <location filename="../data/syncthingdevicemodel.cpp" line="88"/>
<source>Addresses</source> <source>Addresses</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="86"/> <location filename="../data/syncthingdevicemodel.cpp" line="89"/>
<source>Last seen</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdevicemodel.cpp" line="90"/>
<source>Compression</source> <source>Compression</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="87"/> <location filename="../data/syncthingdevicemodel.cpp" line="91"/>
<source>Certificate</source> <source>Certificate</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="88"/> <location filename="../data/syncthingdevicemodel.cpp" line="92"/>
<source>Introducer</source> <source>Introducer</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="97"/> <location filename="../data/syncthingdevicemodel.cpp" line="102"/>
<source>none</source> <source>none</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="98"/> <location filename="../data/syncthingdevicemodel.cpp" line="103"/>
<source>yes</source> <source>yes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="98"/> <location filename="../data/syncthingdevicemodel.cpp" line="103"/>
<source>no</source> <source>no</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="120"/> <location filename="../data/syncthingdevicemodel.cpp" line="155"/>
<source>Unknown status</source> <source>Unknown status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="122"/> <location filename="../data/syncthingdevicemodel.cpp" line="157"/>
<source>Idle</source> <source>Idle</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="123"/> <location filename="../data/syncthingdevicemodel.cpp" line="158"/>
<source>Disconnected</source> <source>Disconnected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="124"/> <location filename="../data/syncthingdevicemodel.cpp" line="159"/>
<source>Synchronizing (%1 %)</source> <source>Synchronizing (%1 %)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="124"/> <location filename="../data/syncthingdevicemodel.cpp" line="159"/>
<source>Synchronizing</source> <source>Synchronizing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="121"/> <location filename="../data/syncthingdevicemodel.cpp" line="156"/>
<source>Own device</source> <source>Own device</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="117"/> <location filename="../data/syncthingdevicemodel.cpp" line="100"/>
<source>unknown or own device</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdevicemodel.cpp" line="152"/>
<source>Paused</source> <source>Paused</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="125"/> <location filename="../data/syncthingdevicemodel.cpp" line="160"/>
<source>Out of sync</source> <source>Out of sync</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="126"/> <location filename="../data/syncthingdevicemodel.cpp" line="161"/>
<source>Rejected</source> <source>Rejected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -188,109 +228,168 @@
<context> <context>
<name>Data::SyncthingDirectoryModel</name> <name>Data::SyncthingDirectoryModel</name>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="58"/> <location filename="../data/syncthingdirectorymodel.cpp" line="61"/>
<location filename="../data/syncthingdirectorymodel.cpp" line="84"/> <location filename="../data/syncthingdirectorymodel.cpp" line="87"/>
<source>ID</source> <source>ID</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="59"/> <location filename="../data/syncthingdirectorymodel.cpp" line="62"/>
<source>Status</source> <source>Status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="85"/> <location filename="../data/syncthingdirectorymodel.cpp" line="88"/>
<source>Path</source> <source>Path</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="86"/> <location filename="../data/syncthingdirectorymodel.cpp" line="89"/>
<source>Devices</source> <source>Devices</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="88"/> <location filename="../data/syncthingdirectorymodel.cpp" line="91"/>
<source>Rescan interval</source> <source>Rescan interval</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/> <location filename="../data/syncthingdirectorymodel.cpp" line="92"/>
<source>Last scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="93"/>
<source>Last file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="102"/>
<source>yes</source> <source>yes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/> <location filename="../data/syncthingdirectorymodel.cpp" line="102"/>
<source>no</source> <source>no</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="118"/> <location filename="../data/syncthingdirectorymodel.cpp" line="104"/>
<location filename="../data/syncthingdirectorymodel.cpp" line="105"/>
<source>unknown</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="143"/>
<source>Deleted at %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="145"/>
<source>Updated at %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="166"/>
<source>Idle</source> <source>Idle</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/> <location filename="../data/syncthingdirectorymodel.cpp" line="167"/>
<source>Scanning (%1 %)</source> <source>Scanning (%1 %)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/> <location filename="../data/syncthingdirectorymodel.cpp" line="168"/>
<source>Synchronizing (%1 %)</source> <source>Synchronizing (%1 %)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="122"/> <location filename="../data/syncthingdirectorymodel.cpp" line="170"/>
<source>Out of sync</source> <source>Out of sync</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/> <location filename="../data/syncthingdirectorymodel.cpp" line="167"/>
<source>Scanning</source> <source>Scanning</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="87"/> <location filename="../data/syncthingdirectorymodel.cpp" line="90"/>
<source>Read-only</source> <source>Read-only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="117"/> <location filename="../data/syncthingdirectorymodel.cpp" line="165"/>
<source>Unknown status</source> <source>Unknown status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/> <location filename="../data/syncthingdirectorymodel.cpp" line="168"/>
<source>Synchronizing</source> <source>Synchronizing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="121"/> <location filename="../data/syncthingdirectorymodel.cpp" line="169"/>
<source>Paused</source> <source>Paused</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>Data::Utils</name>
<message>
<location filename="../data/utils.cpp" line="16"/>
<source>%1 ago</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/utils.cpp" line="18"/>
<source>right now</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>QtGui::AppearanceOptionPage</name> <name>QtGui::AppearanceOptionPage</name>
<message> <message>
<location filename="../gui/appearanceoptionpage.ui" line="6"/> <location filename="../gui/appearanceoptionpage.ui" line="14"/>
<source>Appearance</source> <source>Appearance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/appearanceoptionpage.ui" line="12"/> <location filename="../gui/appearanceoptionpage.ui" line="23"/>
<source>Show traffic</source> <source>Menu size</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="30"/>
<source>Optional GUI elements</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="37"/>
<source>Traffic statistics</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="56"/>
<source>x</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="76"/>
<source>px</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
<name>QtGui::AutostartOptionPage</name> <name>QtGui::AutostartOptionPage</name>
<message> <message>
<location filename="../gui/autostartoptionpage.ui" line="6"/> <location filename="../gui/autostartoptionpage.ui" line="14"/>
<source>Autostart</source> <source>Autostart</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/autostartoptionpage.ui" line="12"/> <location filename="../gui/autostartoptionpage.ui" line="23"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Not implemented yet&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This will allow launching the tray when the desktop environment starts and to launch Syncthing when the tray is started.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source> <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Not implemented yet&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This will allow launching the tray when the desktop environment starts and to launch Syncthing when the tray is started.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -298,50 +397,65 @@
<context> <context>
<name>QtGui::ConnectionOptionPage</name> <name>QtGui::ConnectionOptionPage</name>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="14"/> <location filename="../gui/connectionoptionpage.ui" line="6"/>
<source>Connection</source> <source>Connection</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="20"/> <location filename="../gui/connectionoptionpage.ui" line="15"/>
<source>Syncthing URL</source> <source>Syncthing URL</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="30"/> <location filename="../gui/connectionoptionpage.ui" line="25"/>
<source>Authentication</source> <source>Authentication</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="43"/> <location filename="../gui/connectionoptionpage.ui" line="38"/>
<source>User</source> <source>User</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="84"/> <location filename="../gui/connectionoptionpage.ui" line="72"/>
<source>disconnected</source> <source>disconnected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="91"/> <location filename="../gui/connectionoptionpage.ui" line="79"/>
<source>Status</source> <source>Status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="98"/> <location filename="../gui/connectionoptionpage.ui" line="86"/>
<source>Apply connection settings and try to reconnect</source> <source>Apply connection settings and try to reconnect</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="112"/> <location filename="../gui/connectionoptionpage.ui" line="100"/>
<source>API key</source> <source>API key</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="53"/> <location filename="../gui/connectionoptionpage.ui" line="110"/>
<source>Insert values from local Syncthing configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/connectionoptionpage.ui" line="48"/>
<source>Password</source> <source>Password</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/settingsdialog.cpp" line="60"/>
<source>Select Syncthing config file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/settingsdialog.cpp" line="67"/>
<source>Unable to parse the Syncthing config file.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>QtGui::DevView</name> <name>QtGui::DevView</name>
@ -387,35 +501,40 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="20"/> <location filename="../gui/notificationsoptionpage.ui" line="23"/>
<source>Notify on disconnect</source> <source>Notify on</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="27"/> <location filename="../gui/notificationsoptionpage.ui" line="30"/>
<source>Notify on internal errors</source> <source>disconnect</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="34"/> <location filename="../gui/notificationsoptionpage.ui" line="37"/>
<source>Notify on Syncthing errors</source> <source>internal errors</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="41"/> <location filename="../gui/notificationsoptionpage.ui" line="44"/>
<source>Notify on sync complete</source> <source>Syncthing errors</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/notificationsoptionpage.ui" line="51"/>
<source>sync complete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
<name>QtGui::SettingsDialog</name> <name>QtGui::SettingsDialog</name>
<message> <message>
<location filename="../gui/settingsdialog.cpp" line="211"/> <location filename="../gui/settingsdialog.cpp" line="259"/>
<source>Tray</source> <source>Tray</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/settingsdialog.cpp" line="219"/> <location filename="../gui/settingsdialog.cpp" line="267"/>
<source>Web view</source> <source>Web view</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -524,8 +643,8 @@
<message> <message>
<location filename="../gui/traywidget.ui" line="230"/> <location filename="../gui/traywidget.ui" line="230"/>
<location filename="../gui/traywidget.ui" line="250"/> <location filename="../gui/traywidget.ui" line="250"/>
<location filename="../gui/traywidget.cpp" line="321"/> <location filename="../gui/traywidget.cpp" line="322"/>
<location filename="../gui/traywidget.cpp" line="328"/> <location filename="../gui/traywidget.cpp" line="329"/>
<source>unknown</source> <source>unknown</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -551,11 +670,13 @@
</message> </message>
<message> <message>
<location filename="../gui/traywidget.ui" line="80"/> <location filename="../gui/traywidget.ui" line="80"/>
<location filename="../gui/traywidget.cpp" line="132"/>
<source>About</source> <source>About</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/traywidget.ui" line="134"/> <location filename="../gui/traywidget.ui" line="134"/>
<location filename="../gui/traywidget.cpp" line="112"/>
<source>Settings</source> <source>Settings</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -569,16 +690,6 @@
<source>View own device ID</source> <source>View own device ID</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/traywidget.cpp" line="112"/>
<source>Settings - Syncthing tray</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="131"/>
<source>Tray application for Syncthing</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="81"/> <location filename="../gui/traywidget.cpp" line="81"/>
<source>Rescan all directories</source> <source>Rescan all directories</source>
@ -589,16 +700,6 @@
<source>Show Syncthing log</source> <source>Show Syncthing log</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/traywidget.cpp" line="132"/>
<source>About - Syncthing Tray</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="170"/>
<source>Own device ID - Syncthing Tray</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="180"/> <location filename="../gui/traywidget.cpp" line="180"/>
<source>device ID is unknown</source> <source>device ID is unknown</source>
@ -609,11 +710,6 @@
<source>Copy to clipboard</source> <source>Copy to clipboard</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/traywidget.cpp" line="204"/>
<source>Log - Syncthing</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="231"/> <location filename="../gui/traywidget.cpp" line="231"/>
<source>Not connected to Syncthing, click to connect</source> <source>Not connected to Syncthing, click to connect</source>
@ -640,7 +736,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="271"/> <location filename="../gui/traywidget.cpp" line="170"/>
<source>Own device ID</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="204"/>
<source>Log</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="272"/>
<source>The directly &lt;i&gt;%1&lt;/i&gt; does not exist on the local machine.</source> <source>The directly &lt;i&gt;%1&lt;/i&gt; does not exist on the local machine.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -648,7 +754,7 @@
<context> <context>
<name>QtGui::WebViewDialog</name> <name>QtGui::WebViewDialog</name>
<message> <message>
<location filename="../gui/webviewdialog.cpp" line="26"/> <location filename="../gui/webviewdialog.cpp" line="25"/>
<source>Syncthing</source> <source>Syncthing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -657,7 +763,7 @@
<name>QtGui::WebViewOptionPage</name> <name>QtGui::WebViewOptionPage</name>
<message> <message>
<location filename="../gui/webviewoptionpage.ui" line="14"/> <location filename="../gui/webviewoptionpage.ui" line="14"/>
<location filename="../gui/settingsdialog.cpp" line="173"/> <location filename="../gui/settingsdialog.cpp" line="221"/>
<source>General</source> <source>General</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -687,7 +793,7 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/settingsdialog.cpp" line="175"/> <location filename="../gui/settingsdialog.cpp" line="223"/>
<source>Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine. <source>Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine.
The Web UI will be opened in the default web browser instead.</source> The Web UI will be opened in the default web browser instead.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -696,13 +802,23 @@ The Web UI will be opened in the default web browser instead.</source>
<context> <context>
<name>main</name> <name>main</name>
<message> <message>
<location filename="../application/main.cpp" line="61"/> <location filename="../application/main.cpp" line="62"/>
<source>The system tray is (currently) not available.</source> <source>You must configure how to connect to Syncthing when using Syncthing Tray the first time.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../application/main.cpp" line="65"/> <location filename="../application/main.cpp" line="63"/>
<source>The Qt libraries have not been built with tray icon support.</source> <source>Note that the settings dialog allows importing URL, credentials and API-key from the local Syncthing configuration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../application/main.cpp" line="68"/>
<source>The system tray is (currently) not available. You could open the tray menu as a regular window using the -w flag, though.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../application/main.cpp" line="72"/>
<source>The Qt libraries have not been built with tray icon support. You could open the tray menu as a regular window using the -w flag, though.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>

View File

@ -4,89 +4,119 @@
<context> <context>
<name>Data::SyncthingConnection</name> <name>Data::SyncthingConnection</name>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="108"/> <location filename="../data/syncthingconnection.cpp" line="120"/>
<source>disconnected</source> <source>disconnected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="110"/> <location filename="../data/syncthingconnection.cpp" line="122"/>
<source>connected</source> <source>connected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="112"/> <location filename="../data/syncthingconnection.cpp" line="124"/>
<source>connected, notifications available</source> <source>connected, notifications available</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="114"/> <location filename="../data/syncthingconnection.cpp" line="126"/>
<source>connected, paused</source> <source>connected, paused</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="116"/> <location filename="../data/syncthingconnection.cpp" line="128"/>
<source>connected, synchronizing</source> <source>connected, synchronizing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="118"/> <location filename="../data/syncthingconnection.cpp" line="130"/>
<source>unknown</source> <source>unknown</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="393"/> <location filename="../data/syncthingconnection.cpp" line="427"/>
<source>Unable to parse Syncthing log: </source> <source>Unable to parse Syncthing log: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="397"/> <location filename="../data/syncthingconnection.cpp" line="431"/>
<source>Unable to request system log: </source> <source>Unable to request system log: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="425"/> <location filename="../data/syncthingconnection.cpp" line="452"/>
<location filename="../data/syncthingconnection.cpp" line="525"/> <source>Unable to locate certificate used by Syncthing GUI.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="458"/>
<source>Unable to load certificate used by Syncthing GUI.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="490"/>
<location filename="../data/syncthingconnection.cpp" line="590"/>
<source>Unable to parse Syncthing config: </source> <source>Unable to parse Syncthing config: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="430"/> <location filename="../data/syncthingconnection.cpp" line="495"/>
<location filename="../data/syncthingconnection.cpp" line="530"/> <location filename="../data/syncthingconnection.cpp" line="595"/>
<source>Unable to request Syncthing config: </source> <source>Unable to request Syncthing config: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="605"/> <location filename="../data/syncthingconnection.cpp" line="670"/>
<source>Unable to parse connections: </source> <source>Unable to parse connections: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="610"/> <location filename="../data/syncthingconnection.cpp" line="675"/>
<source>Unable to request connections: </source> <source>Unable to request connections: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="657"/> <location filename="../data/syncthingconnection.cpp" line="725"/>
<source>Unable to parse directory statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="730"/>
<source>Unable to request directory statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="767"/>
<source>Unable to parse device statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="772"/>
<source>Unable to request device statistics: </source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingconnection.cpp" line="823"/>
<source>Unable to parse Syncthing events: </source> <source>Unable to parse Syncthing events: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="679"/> <location filename="../data/syncthingconnection.cpp" line="845"/>
<source>Unable to request Syncthing events: </source> <source>Unable to request Syncthing events: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="844"/> <location filename="../data/syncthingconnection.cpp" line="1051"/>
<source>Unable to request rescan: </source> <source>Unable to request rescan: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="859"/> <location filename="../data/syncthingconnection.cpp" line="1066"/>
<source>Unable to request pause/resume: </source> <source>Unable to request pause/resume: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingconnection.cpp" line="361"/> <location filename="../data/syncthingconnection.cpp" line="395"/>
<source>Unable to request QR-Code: </source> <source>Unable to request QR-Code: </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -94,93 +124,103 @@
<context> <context>
<name>Data::SyncthingDeviceModel</name> <name>Data::SyncthingDeviceModel</name>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="58"/> <location filename="../data/syncthingdevicemodel.cpp" line="61"/>
<location filename="../data/syncthingdevicemodel.cpp" line="84"/> <location filename="../data/syncthingdevicemodel.cpp" line="87"/>
<source>ID</source> <source>ID</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="59"/> <location filename="../data/syncthingdevicemodel.cpp" line="62"/>
<source>Status</source> <source>Status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="85"/> <location filename="../data/syncthingdevicemodel.cpp" line="88"/>
<source>Addresses</source> <source>Addresses</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="86"/> <location filename="../data/syncthingdevicemodel.cpp" line="89"/>
<source>Last seen</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdevicemodel.cpp" line="90"/>
<source>Compression</source> <source>Compression</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="87"/> <location filename="../data/syncthingdevicemodel.cpp" line="91"/>
<source>Certificate</source> <source>Certificate</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="88"/> <location filename="../data/syncthingdevicemodel.cpp" line="92"/>
<source>Introducer</source> <source>Introducer</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="97"/> <location filename="../data/syncthingdevicemodel.cpp" line="102"/>
<source>none</source> <source>none</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="98"/> <location filename="../data/syncthingdevicemodel.cpp" line="103"/>
<source>yes</source> <source>yes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="98"/> <location filename="../data/syncthingdevicemodel.cpp" line="103"/>
<source>no</source> <source>no</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="120"/> <location filename="../data/syncthingdevicemodel.cpp" line="155"/>
<source>Unknown status</source> <source>Unknown status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="122"/> <location filename="../data/syncthingdevicemodel.cpp" line="157"/>
<source>Idle</source> <source>Idle</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="123"/> <location filename="../data/syncthingdevicemodel.cpp" line="158"/>
<source>Disconnected</source> <source>Disconnected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="124"/> <location filename="../data/syncthingdevicemodel.cpp" line="159"/>
<source>Synchronizing (%1 %)</source> <source>Synchronizing (%1 %)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="124"/> <location filename="../data/syncthingdevicemodel.cpp" line="159"/>
<source>Synchronizing</source> <source>Synchronizing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="121"/> <location filename="../data/syncthingdevicemodel.cpp" line="156"/>
<source>Own device</source> <source>Own device</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="117"/> <location filename="../data/syncthingdevicemodel.cpp" line="100"/>
<source>unknown or own device</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdevicemodel.cpp" line="152"/>
<source>Paused</source> <source>Paused</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="125"/> <location filename="../data/syncthingdevicemodel.cpp" line="160"/>
<source>Out of sync</source> <source>Out of sync</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdevicemodel.cpp" line="126"/> <location filename="../data/syncthingdevicemodel.cpp" line="161"/>
<source>Rejected</source> <source>Rejected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -188,109 +228,168 @@
<context> <context>
<name>Data::SyncthingDirectoryModel</name> <name>Data::SyncthingDirectoryModel</name>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="58"/> <location filename="../data/syncthingdirectorymodel.cpp" line="61"/>
<location filename="../data/syncthingdirectorymodel.cpp" line="84"/> <location filename="../data/syncthingdirectorymodel.cpp" line="87"/>
<source>ID</source> <source>ID</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="59"/> <location filename="../data/syncthingdirectorymodel.cpp" line="62"/>
<source>Status</source> <source>Status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="85"/> <location filename="../data/syncthingdirectorymodel.cpp" line="88"/>
<source>Path</source> <source>Path</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="86"/> <location filename="../data/syncthingdirectorymodel.cpp" line="89"/>
<source>Devices</source> <source>Devices</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="88"/> <location filename="../data/syncthingdirectorymodel.cpp" line="91"/>
<source>Rescan interval</source> <source>Rescan interval</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/> <location filename="../data/syncthingdirectorymodel.cpp" line="92"/>
<source>Last scan</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="93"/>
<source>Last file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="102"/>
<source>yes</source> <source>yes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/> <location filename="../data/syncthingdirectorymodel.cpp" line="102"/>
<source>no</source> <source>no</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="118"/> <location filename="../data/syncthingdirectorymodel.cpp" line="104"/>
<location filename="../data/syncthingdirectorymodel.cpp" line="105"/>
<source>unknown</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="143"/>
<source>Deleted at %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="145"/>
<source>Updated at %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/syncthingdirectorymodel.cpp" line="166"/>
<source>Idle</source> <source>Idle</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/> <location filename="../data/syncthingdirectorymodel.cpp" line="167"/>
<source>Scanning (%1 %)</source> <source>Scanning (%1 %)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/> <location filename="../data/syncthingdirectorymodel.cpp" line="168"/>
<source>Synchronizing (%1 %)</source> <source>Synchronizing (%1 %)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="122"/> <location filename="../data/syncthingdirectorymodel.cpp" line="170"/>
<source>Out of sync</source> <source>Out of sync</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/> <location filename="../data/syncthingdirectorymodel.cpp" line="167"/>
<source>Scanning</source> <source>Scanning</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="87"/> <location filename="../data/syncthingdirectorymodel.cpp" line="90"/>
<source>Read-only</source> <source>Read-only</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="117"/> <location filename="../data/syncthingdirectorymodel.cpp" line="165"/>
<source>Unknown status</source> <source>Unknown status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/> <location filename="../data/syncthingdirectorymodel.cpp" line="168"/>
<source>Synchronizing</source> <source>Synchronizing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../data/syncthingdirectorymodel.cpp" line="121"/> <location filename="../data/syncthingdirectorymodel.cpp" line="169"/>
<source>Paused</source> <source>Paused</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>Data::Utils</name>
<message>
<location filename="../data/utils.cpp" line="16"/>
<source>%1 ago</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../data/utils.cpp" line="18"/>
<source>right now</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>QtGui::AppearanceOptionPage</name> <name>QtGui::AppearanceOptionPage</name>
<message> <message>
<location filename="../gui/appearanceoptionpage.ui" line="6"/> <location filename="../gui/appearanceoptionpage.ui" line="14"/>
<source>Appearance</source> <source>Appearance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/appearanceoptionpage.ui" line="12"/> <location filename="../gui/appearanceoptionpage.ui" line="23"/>
<source>Show traffic</source> <source>Menu size</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="30"/>
<source>Optional GUI elements</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="37"/>
<source>Traffic statistics</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="56"/>
<source>x</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/appearanceoptionpage.ui" line="76"/>
<source>px</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
<name>QtGui::AutostartOptionPage</name> <name>QtGui::AutostartOptionPage</name>
<message> <message>
<location filename="../gui/autostartoptionpage.ui" line="6"/> <location filename="../gui/autostartoptionpage.ui" line="14"/>
<source>Autostart</source> <source>Autostart</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/autostartoptionpage.ui" line="12"/> <location filename="../gui/autostartoptionpage.ui" line="23"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Not implemented yet&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This will allow launching the tray when the desktop environment starts and to launch Syncthing when the tray is started.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source> <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Not implemented yet&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This will allow launching the tray when the desktop environment starts and to launch Syncthing when the tray is started.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -298,50 +397,65 @@
<context> <context>
<name>QtGui::ConnectionOptionPage</name> <name>QtGui::ConnectionOptionPage</name>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="14"/> <location filename="../gui/connectionoptionpage.ui" line="6"/>
<source>Connection</source> <source>Connection</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="20"/> <location filename="../gui/connectionoptionpage.ui" line="15"/>
<source>Syncthing URL</source> <source>Syncthing URL</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="30"/> <location filename="../gui/connectionoptionpage.ui" line="25"/>
<source>Authentication</source> <source>Authentication</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="43"/> <location filename="../gui/connectionoptionpage.ui" line="38"/>
<source>User</source> <source>User</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="84"/> <location filename="../gui/connectionoptionpage.ui" line="72"/>
<source>disconnected</source> <source>disconnected</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="91"/> <location filename="../gui/connectionoptionpage.ui" line="79"/>
<source>Status</source> <source>Status</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="98"/> <location filename="../gui/connectionoptionpage.ui" line="86"/>
<source>Apply connection settings and try to reconnect</source> <source>Apply connection settings and try to reconnect</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="112"/> <location filename="../gui/connectionoptionpage.ui" line="100"/>
<source>API key</source> <source>API key</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/connectionoptionpage.ui" line="53"/> <location filename="../gui/connectionoptionpage.ui" line="110"/>
<source>Insert values from local Syncthing configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/connectionoptionpage.ui" line="48"/>
<source>Password</source> <source>Password</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/settingsdialog.cpp" line="60"/>
<source>Select Syncthing config file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/settingsdialog.cpp" line="67"/>
<source>Unable to parse the Syncthing config file.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>QtGui::DevView</name> <name>QtGui::DevView</name>
@ -387,35 +501,40 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="20"/> <location filename="../gui/notificationsoptionpage.ui" line="23"/>
<source>Notify on disconnect</source> <source>Notify on</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="27"/> <location filename="../gui/notificationsoptionpage.ui" line="30"/>
<source>Notify on internal errors</source> <source>disconnect</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="34"/> <location filename="../gui/notificationsoptionpage.ui" line="37"/>
<source>Notify on Syncthing errors</source> <source>internal errors</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/notificationsoptionpage.ui" line="41"/> <location filename="../gui/notificationsoptionpage.ui" line="44"/>
<source>Notify on sync complete</source> <source>Syncthing errors</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/notificationsoptionpage.ui" line="51"/>
<source>sync complete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>
<name>QtGui::SettingsDialog</name> <name>QtGui::SettingsDialog</name>
<message> <message>
<location filename="../gui/settingsdialog.cpp" line="211"/> <location filename="../gui/settingsdialog.cpp" line="259"/>
<source>Tray</source> <source>Tray</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/settingsdialog.cpp" line="219"/> <location filename="../gui/settingsdialog.cpp" line="267"/>
<source>Web view</source> <source>Web view</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -524,8 +643,8 @@
<message> <message>
<location filename="../gui/traywidget.ui" line="230"/> <location filename="../gui/traywidget.ui" line="230"/>
<location filename="../gui/traywidget.ui" line="250"/> <location filename="../gui/traywidget.ui" line="250"/>
<location filename="../gui/traywidget.cpp" line="321"/> <location filename="../gui/traywidget.cpp" line="322"/>
<location filename="../gui/traywidget.cpp" line="328"/> <location filename="../gui/traywidget.cpp" line="329"/>
<source>unknown</source> <source>unknown</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -551,11 +670,13 @@
</message> </message>
<message> <message>
<location filename="../gui/traywidget.ui" line="80"/> <location filename="../gui/traywidget.ui" line="80"/>
<location filename="../gui/traywidget.cpp" line="132"/>
<source>About</source> <source>About</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/traywidget.ui" line="134"/> <location filename="../gui/traywidget.ui" line="134"/>
<location filename="../gui/traywidget.cpp" line="112"/>
<source>Settings</source> <source>Settings</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -569,16 +690,6 @@
<source>View own device ID</source> <source>View own device ID</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/traywidget.cpp" line="112"/>
<source>Settings - Syncthing tray</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="131"/>
<source>Tray application for Syncthing</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="81"/> <location filename="../gui/traywidget.cpp" line="81"/>
<source>Rescan all directories</source> <source>Rescan all directories</source>
@ -589,16 +700,6 @@
<source>Show Syncthing log</source> <source>Show Syncthing log</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/traywidget.cpp" line="132"/>
<source>About - Syncthing Tray</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="170"/>
<source>Own device ID - Syncthing Tray</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="180"/> <location filename="../gui/traywidget.cpp" line="180"/>
<source>device ID is unknown</source> <source>device ID is unknown</source>
@ -609,11 +710,6 @@
<source>Copy to clipboard</source> <source>Copy to clipboard</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="../gui/traywidget.cpp" line="204"/>
<source>Log - Syncthing</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="231"/> <location filename="../gui/traywidget.cpp" line="231"/>
<source>Not connected to Syncthing, click to connect</source> <source>Not connected to Syncthing, click to connect</source>
@ -640,7 +736,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/traywidget.cpp" line="271"/> <location filename="../gui/traywidget.cpp" line="170"/>
<source>Own device ID</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="204"/>
<source>Log</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/traywidget.cpp" line="272"/>
<source>The directly &lt;i&gt;%1&lt;/i&gt; does not exist on the local machine.</source> <source>The directly &lt;i&gt;%1&lt;/i&gt; does not exist on the local machine.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -648,7 +754,7 @@
<context> <context>
<name>QtGui::WebViewDialog</name> <name>QtGui::WebViewDialog</name>
<message> <message>
<location filename="../gui/webviewdialog.cpp" line="26"/> <location filename="../gui/webviewdialog.cpp" line="25"/>
<source>Syncthing</source> <source>Syncthing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -657,7 +763,7 @@
<name>QtGui::WebViewOptionPage</name> <name>QtGui::WebViewOptionPage</name>
<message> <message>
<location filename="../gui/webviewoptionpage.ui" line="14"/> <location filename="../gui/webviewoptionpage.ui" line="14"/>
<location filename="../gui/settingsdialog.cpp" line="173"/> <location filename="../gui/settingsdialog.cpp" line="221"/>
<source>General</source> <source>General</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -687,7 +793,7 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../gui/settingsdialog.cpp" line="175"/> <location filename="../gui/settingsdialog.cpp" line="223"/>
<source>Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine. <source>Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine.
The Web UI will be opened in the default web browser instead.</source> The Web UI will be opened in the default web browser instead.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@ -696,13 +802,23 @@ The Web UI will be opened in the default web browser instead.</source>
<context> <context>
<name>main</name> <name>main</name>
<message> <message>
<location filename="../application/main.cpp" line="61"/> <location filename="../application/main.cpp" line="62"/>
<source>The system tray is (currently) not available.</source> <source>You must configure how to connect to Syncthing when using Syncthing Tray the first time.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../application/main.cpp" line="65"/> <location filename="../application/main.cpp" line="63"/>
<source>The Qt libraries have not been built with tray icon support.</source> <source>Note that the settings dialog allows importing URL, credentials and API-key from the local Syncthing configuration.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../application/main.cpp" line="68"/>
<source>The system tray is (currently) not available. You could open the tray menu as a regular window using the -w flag, though.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../application/main.cpp" line="72"/>
<source>The Qt libraries have not been built with tray icon support. You could open the tray menu as a regular window using the -w flag, though.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>