├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── other_issue.md └── stale.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── LICENSE.LESSER ├── LICENSE.MPL-2.0 ├── LICENSES-windows-distribution.md ├── README.md ├── cli ├── CMakeLists.txt ├── application.cpp ├── application.h ├── args.cpp ├── args.h ├── helper.h ├── jsconsole.cpp ├── jsconsole.h ├── jsdefs.h ├── jsincludes.h ├── main.cpp ├── resources │ └── js │ │ ├── helper.js │ │ └── js.qrc ├── testfiles │ ├── example.js │ └── expected-status.txt └── tests │ └── application.cpp ├── fileitemactionplugin ├── CMakeLists.txt ├── cmake │ └── templates │ │ ├── appdata.xml.in │ │ ├── syncthingfileitemaction.desktop.in │ │ └── syncthingfileitemaction.json.in ├── global.h ├── resources │ ├── body.appdata.xml │ └── screenshots │ │ └── dolphin.png ├── syncthingdiractions.cpp ├── syncthingdiractions.h ├── syncthingfileitemaction.cpp ├── syncthingfileitemaction.h ├── syncthingfileitemactionstaticdata.cpp ├── syncthingfileitemactionstaticdata.h ├── syncthinginfoaction.cpp ├── syncthinginfoaction.h ├── syncthingmenuaction.cpp ├── syncthingmenuaction.h ├── testfiles │ ├── config-no-api-key.xml │ └── config-no-gui-address.xml ├── testing.md └── translations │ ├── syncthingfileitemaction_cs_CZ.ts │ ├── syncthingfileitemaction_de_DE.ts │ ├── syncthingfileitemaction_en_US.ts │ └── syncthingfileitemaction_zh_CN.ts ├── plasmoid ├── .gitignore ├── CMakeLists.txt ├── cmake │ └── templates │ │ ├── metadata.desktop.in │ │ └── metadata.json.in ├── lib │ ├── CMakeLists.txt │ ├── appearanceoptionpage.ui │ ├── cmake │ │ └── templates │ │ │ └── appdata.xml.in │ ├── global.h │ ├── resources │ │ └── body.appdata.xml │ ├── settingsdialog.cpp │ ├── settingsdialog.h │ ├── syncthingapplet.cpp │ └── syncthingapplet.h ├── package5 │ └── contents │ │ └── ui │ │ ├── CompactRepresentation.qml │ │ ├── DetailItem.qml │ │ ├── DetailView.qml │ │ ├── DevicesPage.qml │ │ ├── DirectoriesPage.qml │ │ ├── DownloadsPage.qml │ │ ├── FullRepresentation.qml │ │ ├── IconLabel.qml │ │ ├── RecentChangesPage.qml │ │ ├── StatisticsView.qml │ │ ├── TabButton.qml │ │ ├── TinyButton.qml │ │ ├── ToolBar.qml │ │ ├── ToolButton.qml │ │ ├── ToolTipTrigger.qml │ │ ├── ToolTipView.qml │ │ ├── TopLevelItem.qml │ │ ├── TopLevelView.qml │ │ └── main.qml ├── package6 │ └── contents │ │ └── ui │ │ ├── CompactRepresentation.qml │ │ ├── DetailItem.qml │ │ ├── DetailView.qml │ │ ├── DevicesPage.qml │ │ ├── DirectoriesPage.qml │ │ ├── DownloadsPage.qml │ │ ├── FullRepresentation.qml │ │ ├── IconLabel.qml │ │ ├── RecentChangesPage.qml │ │ ├── StatisticsView.qml │ │ ├── TabButton.qml │ │ ├── TinyButton.qml │ │ ├── ToolBar.qml │ │ ├── ToolButton.qml │ │ ├── ToolTipTrigger.qml │ │ ├── ToolTipView.qml │ │ ├── TopLevelItem.qml │ │ ├── TopLevelView.qml │ │ └── main.qml ├── resources │ └── screenshots │ │ ├── plasmoid-1.png │ │ └── plasmoid-2.png ├── scripts │ ├── inittesting.sh │ ├── settestenv.sh │ └── starttesting.sh ├── testing.md └── translations │ ├── syncthingplasmoid_cs_CZ.ts │ ├── syncthingplasmoid_de_DE.ts │ ├── syncthingplasmoid_en_US.ts │ └── syncthingplasmoid_zh_CN.ts ├── screenshots.md ├── scripts └── dummy.sh ├── syncthing ├── CMakeLists.txt ├── global.h ├── interface.cpp ├── interface.h ├── tests │ └── interfacetests.cpp └── update.sh ├── syncthingconnector ├── CMakeLists.txt ├── global.h ├── org.freedesktop.DBus.Properties.xml ├── org.freedesktop.login1.LoginManager.xml ├── org.freedesktop.systemd1.Manager.xml ├── org.freedesktop.systemd1.Service.xml ├── org.freedesktop.systemd1.Unit.xml ├── qstringhash.h ├── syncthingcompletion.h ├── syncthingconfig.cpp ├── syncthingconfig.h ├── syncthingconnection.cpp ├── syncthingconnection.h ├── syncthingconnection_requests.cpp ├── syncthingconnectionenums.h ├── syncthingconnectionmockhelpers.cpp ├── syncthingconnectionmockhelpers.h ├── syncthingconnectionsettings.cpp ├── syncthingconnectionsettings.h ├── syncthingconnectionstatus.h ├── syncthingdev.cpp ├── syncthingdev.h ├── syncthingdir.cpp ├── syncthingdir.h ├── syncthingignorepattern.cpp ├── syncthingignorepattern.h ├── syncthingnotifier.cpp ├── syncthingnotifier.h ├── syncthingprocess.cpp ├── syncthingprocess.h ├── syncthingservice.cpp ├── syncthingservice.h ├── testfiles │ ├── mocks │ │ ├── browse.json │ │ ├── config.json │ │ ├── connections.json │ │ ├── devicestats.json │ │ ├── empty.json │ │ ├── errors.json │ │ ├── events-01.json │ │ ├── events-02.json │ │ ├── events-03.json │ │ ├── events-04.json │ │ ├── events-05.json │ │ ├── events-06.json │ │ ├── events-07.json │ │ ├── folderstats.json │ │ ├── folderstatus-01.json │ │ ├── folderstatus-02.json │ │ ├── folderstatus-03.json │ │ ├── needed.json │ │ ├── pullerrors-01.json │ │ ├── status.json │ │ └── version.json │ └── testconfig │ │ └── config.xml ├── tests │ ├── connectiontests.cpp │ ├── misctests.cpp │ └── patterntests.cpp ├── translations │ ├── syncthingconnector_cs_CZ.ts │ ├── syncthingconnector_de_DE.ts │ ├── syncthingconnector_en_US.ts │ └── syncthingconnector_zh_CN.ts ├── utils.cpp └── utils.h ├── syncthingmodel ├── CMakeLists.txt ├── colors.h ├── global.h ├── resources │ ├── icons │ │ └── hicolor │ │ │ └── scalable │ │ │ ├── mimetypes │ │ │ └── text-x-generic.svg │ │ │ └── status │ │ │ └── syncthing-default.svg │ └── syncthingmodelicons.qrc ├── syncthingdevicemodel.cpp ├── syncthingdevicemodel.h ├── syncthingdirectorymodel.cpp ├── syncthingdirectorymodel.h ├── syncthingdownloadmodel.cpp ├── syncthingdownloadmodel.h ├── syncthingerrormodel.cpp ├── syncthingerrormodel.h ├── syncthingfilemodel.cpp ├── syncthingfilemodel.h ├── syncthingicons.cpp ├── syncthingicons.h ├── syncthingmodel.cpp ├── syncthingmodel.h ├── syncthingrecentchangesmodel.cpp ├── syncthingrecentchangesmodel.h ├── syncthingsortfiltermodel.cpp ├── syncthingsortfiltermodel.h ├── syncthingstatuscomputionmodel.cpp ├── syncthingstatuscomputionmodel.h ├── syncthingstatusselectionmodel.cpp ├── syncthingstatusselectionmodel.h ├── tests │ └── models.cpp └── translations │ ├── syncthingmodel_cs_CZ.ts │ ├── syncthingmodel_de_DE.ts │ ├── syncthingmodel_en_US.ts │ └── syncthingmodel_zh_CN.ts ├── syncthingwidgets ├── CMakeLists.txt ├── global.h ├── misc │ ├── dbusstatusnotifier.cpp │ ├── dbusstatusnotifier.h │ ├── diffhighlighter.cpp │ ├── diffhighlighter.h │ ├── direrrorsdialog.cpp │ ├── direrrorsdialog.h │ ├── internalerror.cpp │ ├── internalerror.h │ ├── internalerrorsdialog.cpp │ ├── internalerrorsdialog.h │ ├── otherdialogs.cpp │ ├── otherdialogs.h │ ├── statusinfo.cpp │ ├── statusinfo.h │ ├── syncthingkiller.cpp │ ├── syncthingkiller.h │ ├── syncthinglauncher.cpp │ ├── syncthinglauncher.h │ ├── textviewdialog.cpp │ ├── textviewdialog.h │ ├── utils.cpp │ └── utils.h ├── resources │ ├── icons │ │ └── hicolor │ │ │ └── scalable │ │ │ ├── actions │ │ │ ├── document-open.svg │ │ │ ├── edit-paste.svg │ │ │ ├── edit-undo.svg │ │ │ ├── go-down.svg │ │ │ ├── go-up.svg │ │ │ ├── list-add.svg │ │ │ ├── list-remove.svg │ │ │ ├── process-stop.svg │ │ │ ├── tools-wizard.svg │ │ │ └── view-refresh.svg │ │ │ ├── app │ │ │ └── syncthingtray.svg │ │ │ └── apps │ │ │ ├── internet-web-browser.svg │ │ │ ├── preferences-other.svg │ │ │ ├── system-help.svg │ │ │ └── system-run.svg │ └── syncthingwidgetsicons.qrc ├── settings │ ├── appearanceoptionpage.ui │ ├── applywizardpage.ui │ ├── autostartoptionpage.ui │ ├── autostartwizardpage.ui │ ├── builtinwebviewoptionpage.ui │ ├── connectionoptionpage.ui │ ├── generalwebviewoptionpage.ui │ ├── iconsoptionpage.ui │ ├── launcheroptionpage.ui │ ├── mainconfigwizardpage.ui │ ├── notificationsoptionpage.ui │ ├── settings.cpp │ ├── settings.h │ ├── settingsdialog.cpp │ ├── settingsdialog.h │ ├── setupdetection.cpp │ ├── setupdetection.h │ ├── systemdoptionpage.ui │ ├── wizard.cpp │ ├── wizard.h │ └── wizardenums.h ├── tests │ └── wizard.cpp ├── translations │ ├── syncthingwidgets_cs_CZ.ts │ ├── syncthingwidgets_de_DE.ts │ ├── syncthingwidgets_en_US.ts │ └── syncthingwidgets_zh_CN.ts └── webview │ ├── webpage.cpp │ ├── webpage.h │ ├── webviewdefs.h │ ├── webviewdialog.cpp │ ├── webviewdialog.h │ ├── webviewincludes.h │ ├── webviewinterceptor.cpp │ └── webviewinterceptor.h ├── testhelper ├── CMakeLists.txt ├── global.h ├── helper.cpp ├── helper.h ├── syncthingtestinstance.cpp ├── syncthingtestinstance.h └── tests │ └── manualtesting.cpp └── tray ├── CMakeLists.txt ├── android ├── build.gradle ├── res │ ├── drawable-hdpi │ │ └── ic_stat_notify.png │ ├── drawable-mdpi │ │ └── ic_stat_notify.png │ ├── drawable-xhdpi │ │ └── ic_stat_notify.png │ ├── drawable-xxhdpi │ │ └── ic_stat_notify.png │ ├── drawable-xxxhdpi │ │ └── ic_stat_notify.png │ ├── drawable │ │ ├── icon.png │ │ └── splash.xml │ ├── mipmap-anydpi-v26 │ │ └── ic_launcher.xml │ ├── mipmap │ │ ├── ic_launcher_background.png │ │ └── ic_launcher_foreground.png │ ├── values │ │ └── apptheme.xml │ └── xml │ │ └── file_paths.xml ├── signalhandler.cpp └── src │ └── io │ └── github │ └── martchus │ └── syncthingtray │ ├── Activity.java │ ├── Application.java │ ├── DocumentsProvider.java │ ├── SyncthingService.java │ ├── SyncthingServiceBinder.java │ └── Util.java ├── application ├── main.cpp ├── singleinstance.cpp └── singleinstance.h ├── cmake └── templates │ └── bash-completion.sh.in ├── gui ├── devbuttonsitemdelegate.cpp ├── devbuttonsitemdelegate.h ├── devview.cpp ├── devview.h ├── dirbuttonsitemdelegate.cpp ├── dirbuttonsitemdelegate.h ├── dirview.cpp ├── dirview.h ├── downloaditemdelegate.cpp ├── downloaditemdelegate.h ├── downloadview.cpp ├── downloadview.h ├── helper.cpp ├── helper.h ├── qml │ ├── AboutDialog.qml │ ├── AdvancedConfigPage.qml │ ├── AdvancedDevConfigPage.qml │ ├── AdvancedDirConfigPage.qml │ ├── AdvancedPage.qml │ ├── AppControl.qml │ ├── AppWindow.qml │ ├── ArrayElementButtons.qml │ ├── ChangesPage.qml │ ├── CloseDialog.qml │ ├── CopyPasteButtons.qml │ ├── CustomDialog.qml │ ├── CustomFlickable.qml │ ├── CustomListView.qml │ ├── CustomToolButton.qml │ ├── DetailsListView.qml │ ├── DevConfigPage.qml │ ├── DevDelegate.qml │ ├── DevListView.qml │ ├── DevsPage.qml │ ├── DirConfigPage.qml │ ├── DirDelegate.qml │ ├── DirErrorsPage.qml │ ├── DirListView.qml │ ├── DirsPage.qml │ ├── DiscardChangesDialog.qml │ ├── ErrorsDelegate.qml │ ├── ErrorsPage.qml │ ├── ExpandableDelegate.qml │ ├── ExpandableItemDelegate.qml │ ├── ExpandableListView.qml │ ├── FilesPage.qml │ ├── ForkAwesomeIcon.qml │ ├── HelpButton.qml │ ├── HomeDirPage.qml │ ├── IconOnlyButton.qml │ ├── IgnorePatternPage.qml │ ├── ImportPage.qml │ ├── InternalErrorsPage.qml │ ├── LeftDrawer.qml │ ├── LoadingPane.qml │ ├── LogPage.qml │ ├── MainTabBar.qml │ ├── MainTabButton.qml │ ├── MainToolBar.qml │ ├── MenuItemInstantiator.qml │ ├── Meta.qml │ ├── NeededPage.qml │ ├── Notifications.qml │ ├── ObjectConfigDelegate.qml │ ├── ObjectConfigPage.qml │ ├── OutOfSyncDirs.qml │ ├── PageStack.qml │ ├── PendingDevices.qml │ ├── PendingDirs.qml │ ├── SelectiveImportDelegate.qml │ ├── SettingsPage.qml │ ├── StartPage.qml │ ├── Statistics.qml │ ├── StatisticsPage.qml │ ├── Theming.qml │ ├── WebViewPage.qml │ ├── webview-none │ │ └── WebViewItem.qml │ └── webview-webview │ │ └── WebViewItem.qml ├── quick │ ├── app.cpp │ ├── app.h │ ├── quickicon.cpp │ ├── quickicon.h │ └── scenegraph │ │ ├── managedtexturenode.cpp │ │ └── managedtexturenode.h ├── trayicon.cpp ├── trayicon.h ├── traymenu.cpp ├── traymenu.h ├── traywidget.cpp ├── traywidget.h └── traywidget.ui ├── resources ├── AndroidManifest.xml.in ├── Info.plist.in ├── android-svg │ ├── ic_launcher_background.svg │ └── ic_launcher_foreground.svg ├── body.appdata.xml ├── dark-palette.ini ├── ic_stat_notify_full.svg ├── icons │ └── hicolor │ │ ├── 256x256 │ │ └── apps │ │ │ └── syncthingtray.png │ │ └── scalable │ │ ├── actions │ │ ├── application-menu.svg │ │ ├── appointment-new.svg │ │ ├── download.svg │ │ ├── edit-copy.svg │ │ ├── edit-delete.svg │ │ ├── folder-sync.svg │ │ ├── help-about.svg │ │ ├── media-playback-pause.svg │ │ ├── media-playback-start.svg │ │ ├── network-connect.svg │ │ ├── window-close.svg │ │ └── window-pin.svg │ │ ├── apps │ │ ├── help-about.svg │ │ └── syncthingtray.svg │ │ ├── emblems │ │ └── 8 │ │ │ ├── emblem-error.svg │ │ │ └── emblem-warning.svg │ │ └── places │ │ ├── folder-download.svg │ │ ├── folder-open.svg │ │ ├── folder.svg │ │ └── network-workgroup.svg ├── screenshots │ ├── custom-icons.png │ ├── mobile │ │ ├── dark-advanced.jpg │ │ ├── dark-devices.jpg │ │ ├── dark-file-tree-1.jpg │ │ ├── dark-file-tree-2.jpg │ │ ├── dark-filtering.jpg │ │ ├── folder-config.jpg │ │ ├── folders.jpg │ │ ├── import.jpg │ │ ├── run-config.jpg │ │ └── startpage.jpg │ ├── settings.png │ ├── tint2-dark.png │ ├── webview-dark.png │ ├── webview.png │ └── windows-11.png └── syncthingtrayicons.qrc ├── testfiles └── syncthing-testinstance.service └── translations ├── syncthingtray_cs_CZ.ts ├── syncthingtray_de_DE.ts ├── syncthingtray_en_US.ts └── syncthingtray_zh_CN.ts /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - feature request 10 | - enhancement 11 | # Label to use when marking an issue as stale 12 | staleLabel: stale 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | This issue has been automatically marked as stale because it has not had 16 | recent activity. It will be closed if no further activity occurs. Thank you 17 | for your contributions. 18 | # Comment to post when closing a stale issue. Set to `false` to disable 19 | closeComment: false 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | *.slo 3 | *.lo 4 | *.o 5 | *.a 6 | *.la 7 | *.lai 8 | *.so 9 | *.dll 10 | *.dylib 11 | 12 | # Qt-es 13 | /.qmake.cache 14 | /.qmake.stash 15 | *.pro.user 16 | *.txt.user 17 | *.pro.user.* 18 | *.qbs.user 19 | *.qbs.user.* 20 | *.moc 21 | moc_*.cpp 22 | qrc_*.cpp 23 | ui_*.h 24 | Makefile* 25 | *-build-* 26 | .qmlls.ini 27 | 28 | # QtCreator 29 | *.autosave 30 | 31 | # QtCtreator Qml 32 | *.qmlproject.user 33 | *.qmlproject.user.* 34 | 35 | # Go modules 36 | /syncthing/go/pkg 37 | 38 | # Generated files 39 | /*/android/AndroidManifest.xml 40 | 41 | # Created by Java extension of Visual Studio Code 42 | /*/android/.gradle 43 | /*/android/build 44 | 45 | # Dolphin 46 | .directory 47 | 48 | # documentation 49 | /doc 50 | 51 | # tests 52 | /*/testfiles/output.* 53 | /*/testfiles/workingdir/* 54 | 55 | # experimental 56 | scripts/ 57 | 58 | # clang-format 59 | /syncthing/.clang-format 60 | /cli/.clang-format 61 | /syncthingconnector/.clang-format 62 | /fileitemactionplugin/.clang-format 63 | /syncthingmodel/.clang-format 64 | /testhelper/.clang-format 65 | /tray/.clang-format 66 | /plasmoid/lib/.clang-format 67 | /syncthingwidgets/.clang-format 68 | 69 | # clangd 70 | compile_commands.json 71 | .cache 72 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libsyncthing/go/src/github.com/syncthing/syncthing"] 2 | path = syncthing/go/src/github.com/syncthing/syncthing 3 | url = https://github.com/Martchus/syncthing.git 4 | branch = libsyncthing 5 | -------------------------------------------------------------------------------- /cli/args.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_ARGS_H 2 | #define CLI_ARGS_H 3 | 4 | #include 5 | 6 | namespace Cli { 7 | 8 | using namespace CppUtilities; 9 | 10 | struct Args { 11 | Args(); 12 | ArgumentParser parser; 13 | OperationArgument status, log, stop, restart, rescan, rescanAll, pause, resume, waitForIdle, pwd, cat, edit; 14 | OperationArgument statusPwd, rescanPwd, pausePwd, resumePwd; 15 | ConfigValueArgument script, jsLines, dryRun; 16 | ConfigValueArgument stats, dir, dev, allDirs, allDevs; 17 | ConfigValueArgument atLeast, timeout, requireDevsConnected; 18 | ConfigValueArgument editor; 19 | ConfigValueArgument configFile, apiKey, url, credentials, certificate, requestTimeout, generalTimeout; 20 | }; 21 | 22 | } // namespace Cli 23 | 24 | #endif // CLI_ARGS_H 25 | -------------------------------------------------------------------------------- /cli/jsconsole.cpp: -------------------------------------------------------------------------------- 1 | #include "./jsconsole.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | JSConsole::JSConsole(QObject *parent) : QObject(parent) 8 | { 9 | } 10 | 11 | void JSConsole::log(const QString &msg) const 12 | { 13 | cerr << "script: "<< msg.toLocal8Bit().data() << endl; 14 | } 15 | -------------------------------------------------------------------------------- /cli/jsconsole.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_JS_CONSOLE_H 2 | #define CLI_JS_CONSOLE_H 3 | 4 | #include 5 | 6 | class JSConsole : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit JSConsole(QObject *parent = nullptr); 11 | 12 | public Q_SLOTS: 13 | void log(const QString &msg) const; 14 | }; 15 | 16 | #endif // CLI_JS_CONSOLE_H 17 | -------------------------------------------------------------------------------- /cli/jsdefs.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template jsdefs.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGCTL_JAVA_SCRIPT_DEFINES 5 | #define SYNCTHINGCTL_JAVA_SCRIPT_DEFINES 6 | 7 | #include 8 | 9 | #if defined(SYNCTHINGCTL_USE_JSENGINE) 10 | # define SYNCTHINGCTL_JS_ENGINE QJSEngine 11 | # define SYNCTHINGCTL_JS_VALUE QJSValue 12 | # define SYNCTHINGCTL_JS_READONLY 13 | # define SYNCTHINGCTL_JS_UNDELETABLE 14 | # define SYNCTHINGCTL_JS_QOBJECT(engine, obj) engine.newQObject(obj) 15 | # define SYNCTHINGCTL_JS_INT(value) value.toInt() 16 | # define SYNCTHINGCTL_JS_IS_VALID_PROG(program) (!program.isError() && program.isCallable()) 17 | #elif defined(SYNCTHINGCTL_USE_SCRIPT) 18 | # define SYNCTHINGCTL_JS_ENGINE QScriptEngine 19 | # define SYNCTHINGCTL_JS_VALUE QScriptValue 20 | # define SYNCTHINGCTL_JS_READONLY ,QScriptValue::ReadOnly 21 | # define SYNCTHINGCTL_JS_UNDELETABLE ,QScriptValue::Undeletable 22 | # define SYNCTHINGCTL_JS_QOBJECT(engine, obj) engine.newQObject(obj, QScriptEngine::ScriptOwnership) 23 | # define SYNCTHINGCTL_JS_INT(value) value.toInt32() 24 | # define SYNCTHINGCTL_JS_IS_VALID_PROG(program) (!program.isError() && program.isFunction()) 25 | #elif !defined(SYNCTHINGCTL_NO_JSENGINE) 26 | # error "No definition for JavaScript provider present." 27 | #endif 28 | 29 | #ifdef SYNCTHINGCTL_JS_ENGINE 30 | QT_FORWARD_DECLARE_CLASS(SYNCTHINGCTL_JS_ENGINE) 31 | #endif 32 | #ifdef SYNCTHINGCTL_JS_VALUE 33 | QT_FORWARD_DECLARE_CLASS(SYNCTHINGCTL_JS_VALUE) 34 | #endif 35 | 36 | #endif // SYNCTHINGCTL_JAVA_SCRIPT_DEFINES 37 | -------------------------------------------------------------------------------- /cli/jsincludes.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template jsincludes.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGCTL_JAVA_SCRIPT_INCLUDES 5 | #define SYNCTHINGCTL_JAVA_SCRIPT_INCLUDES 6 | 7 | #include 8 | 9 | #if defined(SYNCTHINGCTL_USE_JSENGINE) 10 | # include 11 | # include 12 | #elif defined(SYNCTHINGCTL_USE_SCRIPT) 13 | # include 14 | # include 15 | #elif !defined(SYNCTHINGCTL_NO_JSENGINE) 16 | # error "No definition for JavaScript provider present." 17 | #endif 18 | 19 | #endif // SYNCTHINGCTL_JAVA_SCRIPT_INCLUDES 20 | -------------------------------------------------------------------------------- /cli/main.cpp: -------------------------------------------------------------------------------- 1 | #include "./application.h" 2 | 3 | #include "resources/config.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | SET_APPLICATION_INFO; 13 | CMD_UTILS_HANDLE_VIRTUAL_TERMINAL_PROCESSING; 14 | CMD_UTILS_CONVERT_ARGS_TO_UTF8; 15 | QCoreApplication coreApp(argc, argv); 16 | Cli::Application cliApp; 17 | return cliApp.exec(argc, argv); 18 | } 19 | -------------------------------------------------------------------------------- /cli/resources/js/helper.js: -------------------------------------------------------------------------------- 1 | function findFolder(id) { 2 | var folders = config.folders; 3 | for (var i = 0, count = folders.length; i !== count; ++i) { 4 | var folder = folders[i]; 5 | if (folder.id === id) { 6 | return folder; 7 | } 8 | } 9 | } 10 | 11 | function findDevice(name) { 12 | var devices = config.devices; 13 | for (var i = 0, count = devices.length; i !== count; ++i) { 14 | var device = devices[i]; 15 | if (device.name === name) { 16 | return device; 17 | } 18 | } 19 | } 20 | 21 | function findDeviceById(id) { 22 | var devices = config.devices; 23 | for (var i = 0, count = devices.length; i !== count; ++i) { 24 | var device = devices[i]; 25 | if (device.deviceID === name) { 26 | return device; 27 | } 28 | } 29 | } 30 | 31 | function assignIfPresent(object, property, value) { 32 | if (!object) { 33 | return; 34 | } 35 | object[property] = value; 36 | } 37 | -------------------------------------------------------------------------------- /cli/resources/js/js.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | helper.js 4 | 5 | 6 | -------------------------------------------------------------------------------- /cli/testfiles/example.js: -------------------------------------------------------------------------------- 1 | // example script for changing configuration with syncthingctl 2 | // can be executed like: syncthingctl edit --script example.js 3 | 4 | // the ECMAScript environment is either provided by Qt QML or Qt Script, see http://doc.qt.io/qt-5/qtqml-javascript-hostenvironment.html 5 | // additional helpers are defined in syncthingtray/cli/resources/js/helper.js 6 | 7 | // alter some options 8 | config.gui.useTLS = true; 9 | config.options.relaysEnabled = false; 10 | 11 | // enable file system watcher for all folders starting with "docs-" 12 | var folders = config.folders; 13 | for (var i = 0, count = folders.length; i !== count; ++i) { 14 | var folder = folders[i]; 15 | if (folder.id.indexOf("docs-") === 0) { 16 | folder.fsWatcherDelayS = 50; 17 | folder.fsWatcherEnabled = true; 18 | console.log("enabling file system watcher for folder " + folder.id); 19 | } 20 | } 21 | 22 | // ensure all devices are enabled 23 | var devices = config.devices; 24 | for (var i = 0, count = devices.length; i !== count; ++i) { 25 | var device = devices[i]; 26 | if (device.paused) { 27 | device.paused = false; 28 | console.log("unpausing device " + (device.name ? device.name : device.deviceID)); 29 | } 30 | } 31 | 32 | // pause folder "foo" if the folder exist 33 | assignIfPresent(findFolder("foo"), "paused", true); 34 | -------------------------------------------------------------------------------- /fileitemactionplugin/cmake/templates/appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | @META_PROJECT_RDNS@ 4 | MIT 5 | @META_PROJECT_LICENSE@ 6 | org.kde.dolphin.desktop 7 | @META_APP_NAME@ 8 | @META_APP_DESCRIPTION@ 9 | @META_APP_APPDATA_BODY@ 10 | @META_APP_URL@ 11 | @META_APP_BUGTRACKER_URL@ 12 | @META_APP_AUTHOR@ 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /fileitemactionplugin/cmake/templates/syncthingfileitemaction.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Service 3 | Name=Trigger Syncthing scan 4 | Name[de]="Syncthing-Rescan" im Servicemenü 5 | X-KDE-PluginInfo-Author=@META_APP_AUTHOR@ 6 | X-KDE-PluginInfo-Email=@META_APP_AUTHOR_MAIL@ 7 | X-KDE-PluginInfo-License=@META_PROJECT_LICENSE@ 8 | X-KDE-PluginInfo-Name=@META_ID@ 9 | X-KDE-PluginInfo-Version=@META_VERSION_MAJOR@.@META_VERSION_MINOR@.@META_VERSION_PATCH@ 10 | X-KDE-Library=@META_TARGET_NAME@ 11 | X-KDE-Submenu=Syncthing 12 | 13 | Icon=syncthing.fa 14 | ServiceTypes=KFileItemAction/Plugin 15 | MimeType=application/octet-stream;inode/directory 16 | -------------------------------------------------------------------------------- /fileitemactionplugin/cmake/templates/syncthingfileitemaction.json.in: -------------------------------------------------------------------------------- 1 | { 2 | "KPlugin": { 3 | "Authors": [ 4 | { 5 | "Email": "@META_APP_AUTHOR_MAIL@", 6 | "Name": "@META_APP_AUTHOR@" 7 | } 8 | ], 9 | "Icon": "syncthing.fa", 10 | "Id": "@META_ID@", 11 | "License": "@META_PROJECT_LICENSE@", 12 | "MimeTypes": [ 13 | "application/octet-stream", 14 | "inode/directory" 15 | ], 16 | "Name": "Trigger Syncthing scan", 17 | "Name[de]": "\"Syncthing-Rescan\" im Servicemenü", 18 | "ServiceTypes": ["KFileItemAction/Plugin"], 19 | "Version": "@META_VERSION_MAJOR@.@META_VERSION_MINOR@.@META_VERSION_PATCH@" 20 | }, 21 | "MimeType": "application/octet-stream;inode/directory", 22 | "X-KDE-Submenu": "Syncthing" 23 | } 24 | -------------------------------------------------------------------------------- /fileitemactionplugin/global.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template global.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGFILEITEMACTION_GLOBAL 5 | #define SYNCTHINGFILEITEMACTION_GLOBAL 6 | 7 | #include "syncthingfileitemaction-definitions.h" 8 | #include 9 | 10 | #ifdef SYNCTHINGFILEITEMACTION_STATIC 11 | #define SYNCTHINGFILEITEMACTION_EXPORT 12 | #define SYNCTHINGFILEITEMACTION_IMPORT 13 | #else 14 | #define SYNCTHINGFILEITEMACTION_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT 15 | #define SYNCTHINGFILEITEMACTION_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT 16 | #endif 17 | 18 | /*! 19 | * \def SYNCTHINGFILEITEMACTION_EXPORT 20 | * \brief Marks the symbol to be exported by the syncthingfileitemaction library. 21 | */ 22 | 23 | /*! 24 | * \def SYNCTHINGFILEITEMACTION_IMPORT 25 | * \brief Marks the symbol to be imported from the syncthingfileitemaction library. 26 | */ 27 | 28 | #endif // SYNCTHINGFILEITEMACTION_GLOBAL 29 | -------------------------------------------------------------------------------- /fileitemactionplugin/resources/body.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 |

Allows checking a folder's status from Dolphin and triggering rescans/pause/resume.

3 |
4 | 5 | 6 | Syncthing menu in Dolphin 7 | @META_APP_URL_RAW@/fileitemactionplugin/resources/screenshots/dolphin.png 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /fileitemactionplugin/resources/screenshots/dolphin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/fileitemactionplugin/resources/screenshots/dolphin.png -------------------------------------------------------------------------------- /fileitemactionplugin/syncthingdiractions.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGDIRACTIONS_H 2 | #define SYNCTHINGDIRACTIONS_H 3 | 4 | #include 5 | 6 | #include "./syncthinginfoaction.h" 7 | 8 | class SyncthingFileItemActionStaticData; 9 | 10 | /*! 11 | * \brief The SyncthingDirActions class provides the read-only directory info actions. 12 | */ 13 | class SyncthingDirActions : public QObject { 14 | Q_OBJECT 15 | friend QList &operator<<(QList &, SyncthingDirActions &); 16 | 17 | public: 18 | explicit SyncthingDirActions(const Data::SyncthingDir &dir, const SyncthingFileItemActionStaticData *data = nullptr, QObject *parent = nullptr); 19 | 20 | public Q_SLOTS: 21 | void updateStatus(const std::vector &dirs); 22 | bool updateStatus(const Data::SyncthingDir &dir); 23 | 24 | private: 25 | QString m_dirId; 26 | QAction m_infoAction; 27 | SyncthingInfoAction m_statusAction; 28 | SyncthingInfoAction m_globalStatusAction; 29 | SyncthingInfoAction m_localStatusAction; 30 | SyncthingInfoAction m_lastScanAction; 31 | SyncthingInfoAction m_rescanIntervalAction; 32 | SyncthingInfoAction m_errorsAction; 33 | }; 34 | 35 | QList &operator<<(QList &actions, SyncthingDirActions &dirActions); 36 | 37 | #endif // SYNCTHINGDIRACTIONS_H 38 | -------------------------------------------------------------------------------- /fileitemactionplugin/syncthingfileitemaction.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGFILEITEMACTION_H 2 | #define SYNCTHINGFILEITEMACTION_H 3 | 4 | #include "./syncthingfileitemactionstaticdata.h" 5 | 6 | #include 7 | 8 | class KFileItemListProperties; 9 | 10 | /*! 11 | * \brief The SyncthingFileItemAction class implements the plugin interface. 12 | * \remarks This is the only class directly used by Dolphin. 13 | */ 14 | class SyncthingFileItemAction : public KAbstractFileItemActionPlugin { 15 | Q_OBJECT 16 | 17 | public: 18 | SyncthingFileItemAction(QObject *parent, const QVariantList &args); 19 | QList actions(const KFileItemListProperties &fileItemInfo, QWidget *parentWidget) override; 20 | static QList createActions(const KFileItemListProperties &fileItemInfo, QObject *parent); 21 | static SyncthingFileItemActionStaticData &staticData(); 22 | 23 | protected: 24 | bool eventFilter(QObject *object, QEvent *event) override; 25 | 26 | private: 27 | static SyncthingFileItemActionStaticData s_data; 28 | QWidget *m_parentWidget; 29 | }; 30 | 31 | inline SyncthingFileItemActionStaticData &SyncthingFileItemAction::staticData() 32 | { 33 | return s_data; 34 | } 35 | 36 | #endif // SYNCTHINGFILEITEMACTION_H 37 | -------------------------------------------------------------------------------- /fileitemactionplugin/syncthinginfoaction.cpp: -------------------------------------------------------------------------------- 1 | #include "./syncthinginfoaction.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | SyncthingInfoWidget::SyncthingInfoWidget(const SyncthingInfoAction *action, QWidget *parent) 8 | : QWidget(parent) 9 | , m_textLabel(new QLabel(parent)) 10 | , m_iconLabel(new QLabel(parent)) 11 | { 12 | auto *const layout = new QHBoxLayout(parent); 13 | layout->setContentsMargins(4, 4, 4, 4); 14 | layout->setSpacing(5); 15 | m_iconLabel->setFixedWidth(16); 16 | m_iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); 17 | layout->addWidget(m_iconLabel); 18 | layout->addWidget(m_textLabel); 19 | setLayout(layout); 20 | updateFromAction(action); 21 | connect(action, &QAction::changed, this, &SyncthingInfoWidget::updateFromSender); 22 | } 23 | 24 | void SyncthingInfoWidget::updateFromSender() 25 | { 26 | updateFromAction(qobject_cast(QObject::sender())); 27 | } 28 | 29 | void SyncthingInfoWidget::updateFromAction(const SyncthingInfoAction *action) 30 | { 31 | auto text(action->text()); 32 | text.replace(QChar('&'), QString()); // FIXME: find a better way to get rid of '&' in QAction::text() 33 | m_textLabel->setText(std::move(text)); 34 | m_iconLabel->setPixmap(action->icon().pixmap(16)); 35 | setVisible(action->isVisible()); 36 | } 37 | 38 | SyncthingInfoAction::SyncthingInfoAction(QObject *parent) 39 | : QWidgetAction(parent) 40 | { 41 | } 42 | 43 | QWidget *SyncthingInfoAction::createWidget(QWidget *parent) 44 | { 45 | return new SyncthingInfoWidget(this, parent); 46 | } 47 | -------------------------------------------------------------------------------- /fileitemactionplugin/syncthinginfoaction.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGINFOACTION_H 2 | #define SYNCTHINGINFOACTION_H 3 | 4 | #include 5 | #include 6 | 7 | QT_FORWARD_DECLARE_CLASS(QLabel) 8 | 9 | class SyncthingInfoAction; 10 | 11 | /*! 12 | * \brief The SyncthingInfoWidget class displays a SyncthingInfoAction. 13 | */ 14 | class SyncthingInfoWidget : public QWidget { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit SyncthingInfoWidget(const SyncthingInfoAction *action, QWidget *parent = nullptr); 19 | 20 | private Q_SLOTS: 21 | void updateFromSender(); 22 | void updateFromAction(const SyncthingInfoAction *action); 23 | 24 | private: 25 | QLabel *const m_textLabel; 26 | QLabel *const m_iconLabel; 27 | }; 28 | 29 | /*! 30 | * \brief The SyncthingInfoAction class provides a display-only QAction used to show eg. the directory info. 31 | * \remarks In contrast to a regular QAction, this class has no highlighting on mouseover event. 32 | */ 33 | class SyncthingInfoAction : public QWidgetAction { 34 | Q_OBJECT 35 | 36 | public: 37 | explicit SyncthingInfoAction(QObject *parent = nullptr); 38 | 39 | protected: 40 | QWidget *createWidget(QWidget *parent) override; 41 | }; 42 | 43 | #endif // SYNCTHINGINFOACTION_H 44 | -------------------------------------------------------------------------------- /fileitemactionplugin/syncthingmenuaction.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGMENUACTION_H 2 | #define SYNCTHINGMENUACTION_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | QT_FORWARD_DECLARE_CLASS(QWidget) 11 | 12 | namespace Data { 13 | enum class SyncthingStatus; 14 | } 15 | 16 | /*! 17 | * \brief The SyncthingMenuAction class provides the top-level menu "Syncthing" entry for the context menu. 18 | */ 19 | class SyncthingMenuAction : public QAction { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit SyncthingMenuAction(const KFileItemListProperties &properties = KFileItemListProperties(), 24 | const QList &actions = QList(), QWidget *parentWidget = nullptr); 25 | #ifdef CPP_UTILITIES_DEBUG_BUILD 26 | ~SyncthingMenuAction() override; 27 | #endif 28 | 29 | private Q_SLOTS: 30 | void handleConnectedChanged(); 31 | void updateActionStatus(); 32 | 33 | private: 34 | void createMenu(const QList &actions); 35 | 36 | KFileItemListProperties m_properties; 37 | Data::SyncthingNotifier m_notifier; 38 | QWidget *m_parentWidget; 39 | }; 40 | 41 | #endif // SYNCTHINGMENUACTION_H 42 | -------------------------------------------------------------------------------- /fileitemactionplugin/testfiles/config-no-api-key.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
localhost:4001
4 | nobody 5 | $2a$12$35MnbsQgQNn1hzPYK/lWXOaP.U5D2TO0nuuQy2M4gsqJB4ff4q2RK 6 | 7 | default 8 |
9 |
10 | -------------------------------------------------------------------------------- /fileitemactionplugin/testfiles/config-no-gui-address.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | nobody 5 | $2a$12$35MnbsQgQNn1hzPYK/lWXOaP.U5D2TO0nuuQy2M4gsqJB4ff4q2RK 6 | not-secret 7 | default 8 |
9 |
10 | -------------------------------------------------------------------------------- /fileitemactionplugin/testing.md: -------------------------------------------------------------------------------- 1 | # Testing and debugging Dolphin/KIO plugin with Qt Creator 2 | 1. Build as usual, ensure `NO_FILE_ITEM_ACTION_PLUGIN` is turned off. 3 | 2. For KF5, copy the `*.desktop` file from the build directory to `~/.local/share/kservices5` and enable 4 | it in Dolphin. You may skip this step if a packaged version with the same configuration name is already 5 | installed. As of KF6 this step is not required anymore. 6 | 3. Add new config for run in Qt Creator and set `dolphin` as executable. 7 | 4. In the execution environment, set 8 | * `QT_PLUGIN_PATH` to the build directory of the plugin (for KF5 this directory contains the `so`-file 9 | directly, for KF6 it is in the sub directory `kf6/kfileitemaction`). 10 | * `QT_DEBUG_PLUGINS` to 1 for verbose plugin detection. 11 | * `QT_XCB_NO_GRAB_SERVER` to 1 to prevent grabbing so the debugger is usable (not required under 12 | Wayland). 13 | 5. Ignore the warning that the executable is no debug build. It is sufficient when the plugin is a debug 14 | build. 15 | 16 | ## Testing against a development build of Dolphin 17 | 1. Build the whole dependency chain up to `dolphin` installing it under some custom prefix. 18 | 2. Then follow the usual steps but make sure you build Syncthing Tray against the custom KDE builds. 19 | This is achieved the easiest by using the `debug-kde-custom` CMake preset. This preset uses the 20 | environment variable `KDE_INSTALL_DIR` which must point to the custom prefix used in step 1. 21 | 3. Source the `prefix.sh` script that should be present in the build directory of any KDE library 22 | you built in step 1, e.g. `source kde/plasma-sdk/prefix.sh`. 23 | 4. When setting the environment one needs to be more careful to not override variables set in step 3. 24 | It is the easiest to just start `dolphin` from the shell: 25 | ``` 26 | QT_PLUGIN_PATH=$BUILD_DIR/syncthingtray/debug-kde-custom/syncthingtray/fileitemactionplugin:$QT_PLUGIN_PATH dolphin 27 | ``` 28 | -------------------------------------------------------------------------------- /plasmoid/.gitignore: -------------------------------------------------------------------------------- 1 | # the generated desktop/JSON file is required by inittesting.sh 2 | metadata.desktop 3 | metadata.json 4 | -------------------------------------------------------------------------------- /plasmoid/cmake/templates/metadata.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Name=Syncthing 4 | Icon=@META_TARGET_PREFIX@syncthingtray@META_TARGET_SUFFIX@ 5 | Type=Service 6 | Keywords=syncthing;sync; 7 | X-KDE-ParentApp= 8 | X-KDE-PluginInfo-Author=@META_APP_AUTHOR@ 9 | X-KDE-PluginInfo-Email=@META_APP_AUTHOR_MAIL@ 10 | X-KDE-PluginInfo-License=@META_PROJECT_LICENSE@ 11 | X-KDE-PluginInfo-Name=@META_ID@ 12 | X-KDE-PluginInfo-Version=@META_APP_VERSION@ 13 | X-KDE-PluginInfo-Website=@META_APP_URL@ 14 | X-KDE-ServiceTypes=Plasma/Applet 15 | X-Plasma-NotificationArea=true 16 | X-Plasma-API=declarativeappletscript 17 | X-Plasma-MainScript=ui/main.qml 18 | X-Plasma-NotificationArea=true 19 | X-Plasma-NotificationAreaCategory=SystemServices 20 | X-Plasma-RemoteLocation= 21 | X-KDE-PluginInfo-EnabledByDefault=true 22 | X-KDE-PluginInfo-Category=System Information 23 | X-KDE-Library=@META_TARGET_NAME@ 24 | -------------------------------------------------------------------------------- /plasmoid/cmake/templates/metadata.json.in: -------------------------------------------------------------------------------- 1 | { 2 | "KPackageStructure": "Plasma/Applet", 3 | "KPlugin": { 4 | "Authors": [{"Name": "@META_APP_AUTHOR@"}], 5 | "BugReportUrl": "@META_APP_BUGTRACKER_URL@", 6 | "Category": "Utilities", 7 | "Description": "@META_APP_DESCRIPTION@", 8 | "EnabledByDefault": false, 9 | "FormFactors": ["desktop"], 10 | "Icon": "@META_TARGET_PREFIX@syncthingtray@META_TARGET_SUFFIX@", 11 | "Id": "@META_ID@", 12 | "License": "@META_PROJECT_LICENSE@", 13 | "Name": "Syncthing", 14 | "Version": "@META_APP_VERSION@", 15 | "Website": "@META_APP_URL@" 16 | }, 17 | "X-KDE-Keywords": "syncthing,sync", 18 | "X-Plasma-API": "declarativeappletscript", 19 | "X-Plasma-API-Minimum-Version": "6.0", 20 | "X-Plasma-NotificationAreaCategory": "SystemServices", 21 | "X-Plasma-MainScript": "ui/main.qml" 22 | } 23 | -------------------------------------------------------------------------------- /plasmoid/lib/cmake/templates/appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | @META_PROJECT_RDNS@ 4 | MIT 5 | @META_PROJECT_LICENSE@ 6 | org.kde.plasmashell 7 | @META_APP_NAME@ 8 | @META_APP_DESCRIPTION@ 9 | @META_APP_APPDATA_BODY@ 10 | @META_APP_URL@ 11 | @META_APP_BUGTRACKER_URL@ 12 | @META_APP_AUTHOR@ 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /plasmoid/lib/global.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template global.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGPLASMOID_GLOBAL 5 | #define SYNCTHINGPLASMOID_GLOBAL 6 | 7 | #include "syncthingplasmoid-definitions.h" 8 | #include 9 | 10 | #ifdef SYNCTHINGPLASMOID_STATIC 11 | #define SYNCTHINGPLASMOID_EXPORT 12 | #define SYNCTHINGPLASMOID_IMPORT 13 | #else 14 | #define SYNCTHINGPLASMOID_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT 15 | #define SYNCTHINGPLASMOID_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT 16 | #endif 17 | 18 | /*! 19 | * \def SYNCTHINGPLASMOID_EXPORT 20 | * \brief Marks the symbol to be exported by the syncthingplasmoid library. 21 | */ 22 | 23 | /*! 24 | * \def SYNCTHINGPLASMOID_IMPORT 25 | * \brief Marks the symbol to be imported from the syncthingplasmoid library. 26 | */ 27 | 28 | #endif // SYNCTHINGPLASMOID_GLOBAL 29 | -------------------------------------------------------------------------------- /plasmoid/lib/resources/body.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 |

3 | Syncthing Plasmoid complements the normal web-based UI of Syncthing itself providing the following features: 4 |

5 |
    6 |
  • A Plasmoid showing the status of Syncthing that also allows to quickly perform common actions such as triggering a rescan
  • 7 |
  • Notifications for certain events
  • 8 |
  • Quick access to the normal web-based UI of Syncthing
  • 9 |
  • Integration with systemd
  • 10 |
11 |

12 | This is a KDE-tailored version of Syncthing Tray. Install Syncthing Tray for a desktop environment independent version. 13 |

14 |
15 | 16 | 17 | Using Breeze theme 18 | @META_APP_URL_RAW@/plasmoid/resources/screenshots/plasmoid-1.png 19 | 20 | 21 | Using custom theme 22 | @META_APP_URL_RAW@/plasmoid/resources/screenshots/plasmoid-2.png 23 | 24 | 25 | Icon customization 26 | @META_APP_URL_RAW@/tray/resources/screenshots/custom-icons.png 27 | 28 | 29 | Web view, dark theme 30 | @META_APP_URL_RAW@/tray/resources/screenshots/webview-dark.png 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /plasmoid/lib/settingsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGSDIALOG_H 2 | #define SETTINGSDIALOG_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | QT_FORWARD_DECLARE_CLASS(QKeySequenceEdit) 11 | 12 | namespace QtGui { 13 | class SettingsDialog; 14 | } 15 | 16 | namespace Data { 17 | class SyncthingStatusSelectionModel; 18 | } 19 | 20 | namespace Plasmoid { 21 | class SyncthingApplet; 22 | 23 | BEGIN_DECLARE_OPTION_PAGE_CUSTOM_CTOR(ShortcutOptionPage) 24 | public: 25 | ShortcutOptionPage(SyncthingApplet &applet, QWidget *parentWidget = nullptr); 26 | 27 | private: 28 | DECLARE_SETUP_WIDGETS 29 | SyncthingApplet *m_applet; 30 | QKeySequenceEdit *m_globalShortcutEdit; 31 | END_DECLARE_OPTION_PAGE 32 | 33 | BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(AppearanceOptionPage) 34 | public: 35 | AppearanceOptionPage(SyncthingApplet &applet, QWidget *parentWidget = nullptr); 36 | Data::SyncthingStatusSelectionModel *passiveStatusSelection(); 37 | 38 | private: 39 | DECLARE_SETUP_WIDGETS 40 | SyncthingApplet *m_applet; 41 | Data::SyncthingStatusSelectionModel m_passiveStatusSelection; 42 | END_DECLARE_OPTION_PAGE 43 | 44 | inline Data::SyncthingStatusSelectionModel *AppearanceOptionPage::passiveStatusSelection() 45 | { 46 | return &m_passiveStatusSelection; 47 | } 48 | 49 | class SettingsDialog : public QtGui::SettingsDialog { 50 | public: 51 | SettingsDialog(Plasmoid::SyncthingApplet &applet); 52 | 53 | AppearanceOptionPage *appearanceOptionPage(); 54 | 55 | private: 56 | AppearanceOptionPage *m_appearanceOptionPage; 57 | }; 58 | 59 | inline AppearanceOptionPage *SettingsDialog::appearanceOptionPage() 60 | { 61 | return m_appearanceOptionPage; 62 | } 63 | 64 | QtGui::SettingsDialog *setupSettingsDialog(Plasmoid::SyncthingApplet &applet); 65 | } // namespace Plasmoid 66 | 67 | #endif // SETTINGSDIALOG_H 68 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/CompactRepresentation.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.plasmoid 2.0 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | 6 | MouseArea { 7 | Layout.fillWidth: false 8 | hoverEnabled: true 9 | onClicked: plasmoid.expanded = !plasmoid.expanded 10 | 11 | PlasmaCore.IconItem { 12 | id: icon 13 | anchors.fill: parent 14 | source: plasmoid.nativeInterface.statusIcon 15 | active: parent.containsMouse 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/DetailItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core 2.0 as PlasmaCore 4 | import org.kde.plasma.components 3.0 as PlasmaComponents3 5 | 6 | Item { 7 | id: detailItem 8 | 9 | property string detailName: name ? name : "" 10 | property string detailValue: detail ? detail : "" 11 | 12 | width: detailRow.implicitWidth 13 | height: detailRow.implicitHeight 14 | 15 | RowLayout { 16 | id: detailRow 17 | width: parent.width 18 | 19 | PlasmaCore.IconItem { 20 | source: detailIcon 21 | Layout.leftMargin: units.iconSizes.small * 1.1 22 | Layout.preferredWidth: units.iconSizes.small 23 | Layout.preferredHeight: units.iconSizes.small 24 | opacity: 0.8 25 | } 26 | PlasmaComponents3.Label { 27 | Layout.preferredWidth: 100 28 | text: detailName 29 | font.weight: Font.DemiBold 30 | } 31 | PlasmaComponents3.Label { 32 | Layout.leftMargin: theme.defaultFont.pointSize * 0.9 33 | Layout.fillWidth: true 34 | text: detailValue 35 | elide: Text.ElideRight 36 | horizontalAlignment: Qt.AlignRight 37 | } 38 | } 39 | 40 | MouseArea { 41 | acceptedButtons: Qt.RightButton 42 | anchors.fill: parent 43 | onClicked: { 44 | var view = detailItem.ListView.view 45 | var coordinates = mapToItem(view, mouseX, mouseY) 46 | view.showContextMenu(detailItem, coordinates.x, coordinates.y) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/DetailView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import org.kde.plasma.components 2.0 as PlasmaComponents // for Menu and MenuItem 3 | 4 | ListView { 5 | id: detailView 6 | property DetailItem contextMenuItem: null 7 | currentIndex: -1 8 | interactive: false 9 | height: contentHeight 10 | 11 | PlasmaComponents.Menu { 12 | id: contextMenu 13 | PlasmaComponents.MenuItem { 14 | text: qsTr('Copy value') 15 | icon: "edit-copy" 16 | onClicked: { 17 | var item = detailView.contextMenuItem 18 | if (item) { 19 | plasmoid.nativeInterface.copyToClipboard(item.detailValue) 20 | } 21 | } 22 | } 23 | } 24 | 25 | function showContextMenu(item, x, y) { 26 | contextMenuItem = item 27 | contextMenu.open(x, y) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/IconLabel.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Layouts 1.1 3 | 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 3.0 as PlasmaComponents3 6 | 7 | Item { 8 | property alias iconSource: iconItem.source 9 | property alias iconOpacity: iconItem.opacity 10 | property alias text: label.text 11 | property alias tooltip: tooltipTrigger.tooltip 12 | 13 | implicitWidth: layout.implicitWidth 14 | implicitHeight: layout.implicitHeight 15 | 16 | RowLayout { 17 | id: layout 18 | 19 | Image { 20 | id: iconItem 21 | Layout.preferredWidth: 16 22 | Layout.preferredHeight: 16 23 | height: 16 24 | cache: false 25 | fillMode: Image.PreserveAspectFit 26 | } 27 | PlasmaComponents3.Label { 28 | id: label 29 | } 30 | } 31 | ToolTipTrigger { 32 | id: tooltipTrigger 33 | anchors.fill: layout 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/StatisticsView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Layouts 1.1 3 | 4 | RowLayout { 5 | id: rowLayout 6 | property var statistics 7 | property string context: "?" 8 | 9 | IconLabel { 10 | iconSource: plasmoid.nativeInterface.faUrl + "file-o" 11 | text: statistics.files !== undefined ? statistics.files : "?" 12 | tooltip: context + qsTr(" files") 13 | } 14 | IconLabel { 15 | iconSource: plasmoid.nativeInterface.faUrl + "folder-o" 16 | text: statistics.dirs !== undefined ? statistics.dirs : "?" 17 | tooltip: context + qsTr(" directories") 18 | } 19 | IconLabel { 20 | iconSource: plasmoid.nativeInterface.faUrl + "hdd-o" 21 | text: statistics.bytes !== undefined ? plasmoid.nativeInterface.formatFileSize( 22 | statistics.bytes) : "?" 23 | tooltip: context + qsTr(" size") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/TabButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Templates 2.15 as T 3 | import QtQuick.Layouts 1.1 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 3.0 as PlasmaComponents3 6 | import org.kde.kirigami 2.5 as Kirigami 7 | 8 | PlasmaComponents3.TabButton { 9 | id: root 10 | spacing: 0 11 | 12 | property bool showTabText: plasmoid.nativeInterface.showTabTexts 13 | property string tooltip: "" 14 | 15 | PlasmaComponents3.ToolTip { 16 | id: tooltip 17 | text: root.tooltip !== "" ? root.tooltip : (root.showTabText ? "" : root.text) 18 | visible: text !== "" && parent instanceof T.AbstractButton && (Kirigami.Settings.tabletMode ? parent.pressed : parent.hovered) 19 | } 20 | 21 | contentItem: RowLayout { 22 | spacing: label.visible ? units.smallSpacing : 0 23 | PlasmaCore.ColorScope.inherit: true 24 | width: parent.width 25 | Item { 26 | Layout.fillWidth: true 27 | } 28 | Image { 29 | id: image 30 | Layout.preferredHeight: height 31 | source: root.icon.source 32 | cache: false 33 | height: units.iconSizes.small 34 | fillMode: Image.PreserveAspectFit 35 | } 36 | PlasmaComponents3.Label { 37 | id: label 38 | visible: text.length > 0 39 | text: root.showTabText ? root.text : "" 40 | color: PlasmaCore.ColorScope.textColor 41 | elide: Text.ElideRight 42 | Layout.fillHeight: true 43 | } 44 | Item { 45 | Layout.fillWidth: true 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/TinyButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.components 3.0 as PlasmaComponents3 4 | 5 | PlasmaComponents3.ToolButton { 6 | id: root 7 | 8 | property alias tooltip: tooltip.text 9 | 10 | Layout.fillHeight: true 11 | Layout.preferredWidth: units.iconSizes.smallMedium 12 | Layout.preferredHeight: units.iconSizes.smallMedium 13 | 14 | PlasmaComponents3.ToolTip { 15 | id: tooltip 16 | } 17 | contentItem: Image { 18 | source: root.icon.source 19 | cache: false 20 | height: parent.height 21 | fillMode: Image.PreserveAspectFit 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/ToolButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core 2.0 as PlasmaCore 4 | import org.kde.plasma.components 3.0 as PlasmaComponents3 5 | 6 | PlasmaComponents3.ToolButton { 7 | id: root 8 | Layout.fillHeight: true 9 | contentItem: Grid { 10 | columns: 2 11 | columnSpacing: label.visible ? units.smallSpacing : 0 12 | verticalItemAlignment: Grid.AlignVCenter 13 | PlasmaCore.ColorScope.inherit: true 14 | Image { 15 | source: root.icon.source 16 | cache: false 17 | height: parent.height 18 | fillMode: Image.PreserveAspectFit 19 | } 20 | PlasmaComponents3.Label { 21 | id: label 22 | visible: text.length > 0 23 | text: root.text 24 | color: PlasmaCore.ColorScope.textColor 25 | elide: Text.ElideRight 26 | Layout.fillWidth: true 27 | Layout.fillHeight: true 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/ToolTipTrigger.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import org.kde.plasma.components 3.0 as PlasmaComponents3 3 | 4 | MouseArea { 5 | property alias interval: timer.interval 6 | property alias tooltip: tooltip.text 7 | hoverEnabled: true 8 | Timer { 9 | id: timer 10 | interval: 1000 11 | running: parent.containsMouse && parent.tooltip.length !== 0 12 | onTriggered: tooltip.open() 13 | } 14 | PlasmaComponents3.ToolTip { 15 | id: tooltip 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plasmoid/package5/contents/ui/ToolTipView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core 2.0 as PlasmaCore 4 | import org.kde.plasma.components 3.0 as PlasmaComponents3 5 | import org.kde.plasma.extras 2.0 as PlasmaExtras 6 | 7 | RowLayout { 8 | spacing: units.smallSpacing 9 | 10 | PlasmaCore.IconItem { 11 | id: tooltipIcon 12 | source: plasmoid.nativeInterface.statusIcon 13 | Layout.alignment: Qt.AlignCenter 14 | visible: true 15 | implicitWidth: units.iconSizes.large 16 | Layout.topMargin: units.smallSpacing 17 | Layout.leftMargin: units.smallSpacing 18 | Layout.bottomMargin: units.smallSpacing 19 | Layout.rightMargin: units.smallSpacing 20 | Layout.preferredWidth: implicitWidth 21 | Layout.preferredHeight: implicitWidth 22 | } 23 | 24 | ColumnLayout { 25 | PlasmaExtras.Heading { 26 | id: tooltipMaintext 27 | level: 3 28 | elide: Text.ElideRight 29 | text: plasmoid.toolTipMainText 30 | } 31 | PlasmaComponents3.Label { 32 | id: tooltipSubtext 33 | text: plasmoid.toolTipSubText 34 | opacity: 0.6 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/CompactRepresentation.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.plasmoid 2.0 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.kirigami 2.20 as Kirigami 6 | 7 | MouseArea { 8 | property bool wasExpanded 9 | Layout.fillWidth: false 10 | hoverEnabled: true 11 | 12 | onPressed: wasExpanded = syncthingApplet.expanded 13 | onClicked: mouse => { 14 | if (mouse.button === Qt.MiddleButton) { 15 | Plasmoid.showWebUI(); 16 | } else { 17 | syncthingApplet.expanded = !wasExpanded; 18 | } 19 | } 20 | 21 | Kirigami.Icon { 22 | id: icon 23 | anchors.fill: parent 24 | source: plasmoid.statusIcon 25 | active: parent.containsMouse 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/DetailItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core 2.0 as PlasmaCore 4 | import org.kde.plasma.components 3.0 as PlasmaComponents3 5 | import org.kde.kirigami 2.20 as Kirigami 6 | 7 | Item { 8 | id: detailItem 9 | 10 | property string detailName: name ? name : "" 11 | property string detailValue: detail ? detail : "" 12 | 13 | width: detailRow.implicitWidth 14 | height: detailRow.implicitHeight 15 | 16 | RowLayout { 17 | id: detailRow 18 | width: parent.width 19 | 20 | Kirigami.Icon { 21 | source: detailIcon 22 | Layout.leftMargin: Kirigami.Units.iconSizes.small * 1.1 23 | Layout.preferredWidth: Kirigami.Units.iconSizes.small 24 | Layout.preferredHeight: Kirigami.Units.iconSizes.small 25 | opacity: 0.8 26 | } 27 | PlasmaComponents3.Label { 28 | Layout.preferredWidth: 100 29 | text: detailName 30 | font.weight: Font.DemiBold 31 | } 32 | PlasmaComponents3.Label { 33 | Layout.leftMargin: Kirigami.Theme.defaultFont.pointSize * 0.9 34 | Layout.fillWidth: true 35 | text: detailValue 36 | elide: Text.ElideRight 37 | horizontalAlignment: Qt.AlignRight 38 | } 39 | } 40 | 41 | MouseArea { 42 | acceptedButtons: Qt.RightButton 43 | anchors.fill: parent 44 | onClicked: { 45 | var view = detailItem.ListView.view 46 | var coordinates = mapToItem(view, mouseX, mouseY) 47 | view.showContextMenu(detailItem, coordinates.x, coordinates.y) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/DetailView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import org.kde.plasma.extras 2.0 as PlasmaExtras 3 | 4 | ListView { 5 | id: detailView 6 | property DetailItem contextMenuItem: null 7 | currentIndex: -1 8 | interactive: false 9 | 10 | onCountChanged: { 11 | var d = delegate.createObject(detailView, {detailName: "", detailValue: ""}); 12 | height = count * d.height 13 | d.destroy() 14 | } 15 | 16 | PlasmaExtras.Menu { 17 | id: contextMenu 18 | PlasmaExtras.MenuItem { 19 | text: qsTr('Copy value') 20 | icon: "edit-copy" 21 | onClicked: { 22 | var item = detailView.contextMenuItem 23 | if (item) { 24 | plasmoid.copyToClipboard(item.detailValue) 25 | } 26 | } 27 | } 28 | } 29 | 30 | function showContextMenu(item, x, y) { 31 | contextMenuItem = item 32 | contextMenu.open(x, y) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/IconLabel.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Layouts 1.1 3 | 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 3.0 as PlasmaComponents3 6 | 7 | Item { 8 | property alias iconSource: iconItem.source 9 | property alias iconOpacity: iconItem.opacity 10 | property alias text: label.text 11 | property alias tooltip: tooltipTrigger.tooltip 12 | 13 | implicitWidth: layout.implicitWidth 14 | implicitHeight: layout.implicitHeight 15 | 16 | RowLayout { 17 | id: layout 18 | 19 | Image { 20 | id: iconItem 21 | Layout.preferredWidth: 16 22 | Layout.preferredHeight: 16 23 | height: 16 24 | cache: false 25 | fillMode: Image.PreserveAspectFit 26 | } 27 | PlasmaComponents3.Label { 28 | id: label 29 | } 30 | } 31 | ToolTipTrigger { 32 | id: tooltipTrigger 33 | anchors.fill: layout 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/StatisticsView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Layouts 1.1 3 | 4 | RowLayout { 5 | id: rowLayout 6 | property var statistics 7 | property string context: "?" 8 | 9 | IconLabel { 10 | iconSource: plasmoid.faUrl + "file-o" 11 | text: statistics.files !== undefined ? statistics.files : "?" 12 | tooltip: context + qsTr(" files") 13 | } 14 | IconLabel { 15 | iconSource: plasmoid.faUrl + "folder-o" 16 | text: statistics.dirs !== undefined ? statistics.dirs : "?" 17 | tooltip: context + qsTr(" directories") 18 | } 19 | IconLabel { 20 | iconSource: plasmoid.faUrl + "hdd-o" 21 | text: statistics.bytes !== undefined ? plasmoid.formatFileSize( 22 | statistics.bytes) : "?" 23 | tooltip: context + qsTr(" size") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/TabButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Templates 2.15 as T 3 | import QtQuick.Layouts 1.1 4 | import org.kde.plasma.core 2.0 as PlasmaCore 5 | import org.kde.plasma.components 3.0 as PlasmaComponents3 6 | import org.kde.kirigami 2.5 as Kirigami 7 | 8 | PlasmaComponents3.TabButton { 9 | id: root 10 | spacing: 0 11 | 12 | property bool showTabText: plasmoid.showTabTexts 13 | property string tooltip: "" 14 | 15 | PlasmaComponents3.ToolTip { 16 | id: tooltip 17 | text: root.tooltip !== "" ? root.tooltip : (root.showTabText ? "" : root.text) 18 | visible: text !== "" && parent instanceof T.AbstractButton && (Kirigami.Settings.tabletMode ? parent.pressed : parent.hovered) 19 | } 20 | 21 | contentItem: RowLayout { 22 | spacing: label.visible ? Kirigami.Units.smallSpacing : 0 23 | Kirigami.Theme.inherit: true 24 | width: parent.width 25 | Item { 26 | Layout.fillWidth: true 27 | } 28 | Image { 29 | id: image 30 | Layout.preferredHeight: height 31 | source: root.icon.source 32 | cache: false 33 | height: Kirigami.Units.iconSizes.small 34 | fillMode: Image.PreserveAspectFit 35 | } 36 | PlasmaComponents3.Label { 37 | id: label 38 | visible: text.length > 0 39 | text: root.showTabText ? root.text : "" 40 | color: Kirigami.Theme.textColor 41 | elide: Text.ElideRight 42 | Layout.fillHeight: true 43 | } 44 | Item { 45 | Layout.fillWidth: true 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/TinyButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.components 3.0 as PlasmaComponents3 4 | import org.kde.kirigami 2.20 as Kirigami 5 | 6 | PlasmaComponents3.ToolButton { 7 | id: root 8 | 9 | property alias tooltip: tooltip.text 10 | 11 | Layout.fillHeight: true 12 | Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium 13 | Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium 14 | 15 | PlasmaComponents3.ToolTip { 16 | id: tooltip 17 | } 18 | contentItem: Image { 19 | source: root.icon.source 20 | cache: false 21 | height: parent.height 22 | fillMode: Image.PreserveAspectFit 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/ToolButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.8 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core 2.0 as PlasmaCore 4 | import org.kde.plasma.components 3.0 as PlasmaComponents3 5 | import org.kde.kirigami 2.20 as Kirigami 6 | 7 | PlasmaComponents3.ToolButton { 8 | id: root 9 | Layout.fillHeight: true 10 | contentItem: Grid { 11 | columns: 2 12 | columnSpacing: label.visible ? Kirigami.Units.smallSpacing : 0 13 | verticalItemAlignment: Grid.AlignVCenter 14 | Kirigami.Theme.inherit: true 15 | Image { 16 | source: root.icon.source 17 | cache: false 18 | height: parent.height 19 | fillMode: Image.PreserveAspectFit 20 | } 21 | PlasmaComponents3.Label { 22 | id: label 23 | visible: text.length > 0 24 | text: root.text 25 | color: Kirigami.Theme.textColor 26 | elide: Text.ElideRight 27 | Layout.fillWidth: true 28 | Layout.fillHeight: true 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/ToolTipTrigger.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import org.kde.plasma.components 3.0 as PlasmaComponents3 3 | 4 | MouseArea { 5 | property alias interval: timer.interval 6 | property alias tooltip: tooltip.text 7 | hoverEnabled: true 8 | Timer { 9 | id: timer 10 | interval: 1000 11 | running: parent.containsMouse && parent.tooltip.length !== 0 12 | onTriggered: tooltip.open() 13 | } 14 | PlasmaComponents3.ToolTip { 15 | id: tooltip 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /plasmoid/package6/contents/ui/ToolTipView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.core 2.0 as PlasmaCore 4 | import org.kde.plasma.components 3.0 as PlasmaComponents3 5 | import org.kde.kirigami 2.20 as Kirigami 6 | 7 | RowLayout { 8 | spacing: Kirigami.Units.smallSpacing 9 | 10 | Kirigami.Icon { 11 | id: tooltipIcon 12 | source: plasmoid.statusIcon 13 | Layout.alignment: Qt.AlignCenter 14 | visible: true 15 | implicitWidth: Kirigami.Units.iconSizes.large 16 | Layout.topMargin: Kirigami.Units.smallSpacing 17 | Layout.leftMargin: Kirigami.Units.smallSpacing 18 | Layout.bottomMargin: Kirigami.Units.smallSpacing 19 | Layout.rightMargin: Kirigami.Units.smallSpacing 20 | Layout.preferredWidth: implicitWidth 21 | Layout.preferredHeight: implicitWidth 22 | } 23 | 24 | ColumnLayout { 25 | Kirigami.Heading { 26 | id: tooltipMaintext 27 | level: 3 28 | elide: Text.ElideRight 29 | text: plasmoid.statusText 30 | } 31 | PlasmaComponents3.Label { 32 | id: tooltipSubtext 33 | text: plasmoid.additionalStatusText 34 | opacity: 0.6 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plasmoid/resources/screenshots/plasmoid-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/plasmoid/resources/screenshots/plasmoid-1.png -------------------------------------------------------------------------------- /plasmoid/resources/screenshots/plasmoid-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/plasmoid/resources/screenshots/plasmoid-2.png -------------------------------------------------------------------------------- /plasmoid/scripts/inittesting.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | script_dir=$(dirname "${BASH_SOURCE[0]}") 4 | source "$script_dir/settestenv.sh" 5 | 6 | # use the package dir within the source-tree so one does not need to run CMake again for updating 7 | # build-tree copy all the time 8 | package_dir=$script_dir/../$2 9 | 10 | # copy the generated desktop file back into the source-tree package dir so it can actually be used 11 | meta_data_file=$1 12 | cp --target-directory="$package_dir" "$meta_data_file" 13 | 14 | # install or update the package into the working directory 15 | if ! plasmapkg2 --install "$package_dir"; then 16 | plasmapkg2 --upgrade "$package_dir" 17 | fi 18 | exit $? 19 | -------------------------------------------------------------------------------- /plasmoid/scripts/settestenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # use a sub directory within the build directory which is supposed to be $PWD 5 | export HOME=${TEST_HOME:-$PWD/plasmoid-testing} 6 | echo "HOME directory used for Plasmoid testing: $HOME" 7 | mkdir -p "$HOME" 8 | 9 | # unset XDG_DATA_HOME and XDG_CONFIG_HOME to use Qt's default relying on just HOME (see qtbase/src/corelib/io/qstandardpaths_unix.cpp for defaults) 10 | export XDG_DATA_HOME= 11 | export XDG_CONFIG_HOME= 12 | 13 | # set QT_PLUGIN_PATH if it has not already been set 14 | if ! [[ $QT_PLUGIN_PATH ]]; then 15 | if [ -f "$PWD"/syncthingtray/plasmoid/lib/plasma/applets/*syncthingplasmoid*.so ]; then 16 | export QT_PLUGIN_PATH=$PWD/syncthingtray/plasmoid/lib 17 | elif [ -f "$PWD"/plasmoid/lib/plasma/applets/*syncthingplasmoid*.so ]; then 18 | export QT_PLUGIN_PATH=$PWD/plasmoid/lib 19 | fi 20 | fi 21 | echo "QT_PLUGIN_PATH used for Plasmoid testing: $QT_PLUGIN_PATH" 22 | 23 | # set TEST_FILE_PATH so files for the mocked configuration can be found 24 | export TEST_FILE_PATH=$(dirname "${BASH_SOURCE[0]}")/../testfiles 25 | 26 | # log working directory 27 | echo "Working directory used for Plasmoid testing: $PWD" 28 | 29 | # debug plugin loading (run this script from terminal if Qt Creator suppresses the debugging output) 30 | export QT_DEBUG_PLUGINS=1 31 | -------------------------------------------------------------------------------- /plasmoid/scripts/starttesting.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | script_dir=$(dirname "${BASH_SOURCE[0]}") 4 | source "$script_dir/settestenv.sh" 5 | exec "$@" 6 | -------------------------------------------------------------------------------- /screenshots.md: -------------------------------------------------------------------------------- 1 | # Screenshots 2 | The screenshots are not up-to-date. 3 | 4 | ## Qt Widgets based GUI under Windows 11 5 | ![Qt Widgets based GUI under Windows 11](/tray/resources/screenshots/windows-11.png?raw=true) 6 | 7 | ## Qt Widgets based GUI under Openbox/Tint2 with dark Breeze theme 8 | ![Qt Widgets based GUI under Openbox/Tint2](/tray/resources/screenshots/tint2-dark.png?raw=true) 9 | 10 | ## Plasmoid (for KDE's Plasma shell) 11 | ### Light theme 12 | ![Plasmoid (light theme)](/plasmoid/resources/screenshots/plasmoid-1.png?raw=true) 13 | 14 | ### Dark theme 15 | ![Plasmoid (dark theme)](/plasmoid/resources/screenshots/plasmoid-2.png?raw=true) 16 | 17 | ## Icon customization dialog 18 | ![Plasmoid (customized icons)](/tray/resources/screenshots/custom-icons.png?raw=true) 19 | 20 | ## Settings dialog 21 | ![Settings dialog](/tray/resources/screenshots/settings.png?raw=true) 22 | 23 | ## Web view 24 | ![Web view](/tray/resources/screenshots/webview.png?raw=true) 25 | ![Web view (dark)](/tray/resources/screenshots/webview-dark.png?raw=true) 26 | 27 | ## Syncthing actions for Dolphin 28 | ![Rescan/pause/status](/fileitemactionplugin/resources/screenshots/dolphin.png?raw=true) 29 | -------------------------------------------------------------------------------- /scripts/dummy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function handle_int { 4 | echo "Received SIGINT or SIGTERM, keep running for 15 seconds nevertheless" 5 | sleep 15 6 | exit -5 7 | } 8 | trap "handle_int" SIGINT SIGTERM 9 | 10 | i=0 11 | while [[ true ]]; do 12 | echo "$i : $RANDOM" 13 | i=$((i + 1)) 14 | sleep 1 15 | done 16 | -------------------------------------------------------------------------------- /syncthing/global.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template global.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef LIB_SYNCTHING_GLOBAL 5 | #define LIB_SYNCTHING_GLOBAL 6 | 7 | #include "syncthing-definitions.h" 8 | #include 9 | 10 | #ifdef LIB_SYNCTHING_STATIC 11 | #define LIB_SYNCTHING_EXPORT 12 | #define LIB_SYNCTHING_IMPORT 13 | #else 14 | #define LIB_SYNCTHING_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT 15 | #define LIB_SYNCTHING_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT 16 | #endif 17 | 18 | /*! 19 | * \def LIB_SYNCTHING_EXPORT 20 | * \brief Marks the symbol to be exported by the syncthing library. 21 | */ 22 | 23 | /*! 24 | * \def LIB_SYNCTHING_IMPORT 25 | * \brief Marks the symbol to be imported from the syncthing library. 26 | */ 27 | 28 | #endif // LIB_SYNCTHING_GLOBAL 29 | -------------------------------------------------------------------------------- /syncthing/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | shopt -s nocasematch 4 | shopt -s nullglob 5 | 6 | libsyncthingdir=$(dirname "$0") 7 | syncthingrepodir=$libsyncthingdir/go/src/github.com/syncthing/syncthing 8 | special_release_regex='rc|beta|debian' 9 | latest_tag= 10 | 11 | echo '==> Updating Syncthing repo' 12 | git -C "$syncthingrepodir" remote update 13 | 14 | echo '==> Getting latest tag/release' 15 | for tag in $(git -C "$syncthingrepodir" tag -l --sort=-version:refname 'v*'); do 16 | if [[ $tag =~ $special_release_regex ]]; then 17 | continue 18 | fi 19 | if [[ $tag ]]; then 20 | latest_tag=$tag 21 | break 22 | fi 23 | done 24 | if [[ -z $latest_tag ]]; then 25 | echo 'Unable to find latest release tag' 26 | exit -1 27 | fi 28 | echo "Latest Syncthing release: $latest_tag" 29 | 30 | echo '==> Updating CMakeLists.txt' 31 | sed -i -e "s|^\(set(META_SYNCTHING_VERSION \"\(.*\)\")\).*$|set(META_SYNCTHING_VERSION \"$latest_tag\")|" "$libsyncthingdir/CMakeLists.txt" 32 | 33 | echo '==> Rebasing libsyncthing' 34 | if git -C "$syncthingrepodir" rev-parse --verify "libsyncthing-$latest_tag" > /dev/null; then 35 | echo "Nothing to do: A libsyncthing branch \"libsyncthing-$latest_tag\" for the latest release already exists." 36 | exit 0 37 | fi 38 | git -C "$syncthingrepodir" checkout libsyncthing-latest 39 | git -C "$syncthingrepodir" rebase "$latest_tag" 40 | git -C "$syncthingrepodir" branch "libsyncthing-$latest_tag" 41 | 42 | echo '==> Pushing updated/new libsyncthing branches to GitHub' 43 | git -C "$syncthingrepodir" config remote.all.url > /dev/null && remote=all || remote=martchus 44 | git -C "$syncthingrepodir" push -fu "$remote" libsyncthing-latest:libsyncthing-latest 45 | git -C "$syncthingrepodir" push -u "$remote" "libsyncthing-$latest_tag:libsyncthing-$latest_tag" 46 | -------------------------------------------------------------------------------- /syncthingconnector/global.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template global.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef LIB_SYNCTHING_CONNECTOR_GLOBAL 5 | #define LIB_SYNCTHING_CONNECTOR_GLOBAL 6 | 7 | #include "syncthingconnector-definitions.h" 8 | #include 9 | 10 | #ifdef LIB_SYNCTHING_CONNECTOR_STATIC 11 | #define LIB_SYNCTHING_CONNECTOR_EXPORT 12 | #define LIB_SYNCTHING_CONNECTOR_IMPORT 13 | #else 14 | #define LIB_SYNCTHING_CONNECTOR_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT 15 | #define LIB_SYNCTHING_CONNECTOR_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT 16 | #endif 17 | 18 | /*! 19 | * \def LIB_SYNCTHING_CONNECTOR_EXPORT 20 | * \brief Marks the symbol to be exported by the syncthingconnector library. 21 | */ 22 | 23 | /*! 24 | * \def LIB_SYNCTHING_CONNECTOR_IMPORT 25 | * \brief Marks the symbol to be imported from the syncthingconnector library. 26 | */ 27 | 28 | #endif // LIB_SYNCTHING_CONNECTOR_GLOBAL 29 | -------------------------------------------------------------------------------- /syncthingconnector/org.freedesktop.DBus.Properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /syncthingconnector/qstringhash.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHING_CONNECTOR_QSTRING_HASH_H 2 | #define SYNCTHING_CONNECTOR_QSTRING_HASH_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) 10 | namespace std { 11 | 12 | template <> struct hash { 13 | inline std::size_t operator()(const QString &str) const 14 | { 15 | return qHash(str); 16 | } 17 | }; 18 | 19 | } // namespace std 20 | #endif 21 | 22 | #endif // SYNCTHING_CONNECTOR_QSTRING_HASH_H 23 | -------------------------------------------------------------------------------- /syncthingconnector/syncthingconfig.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_SYNCTHINGCONFIG_H 2 | #define DATA_SYNCTHINGCONFIG_H 3 | 4 | #include "./global.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace Data { 12 | 13 | struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConfigDetails { 14 | QJsonArray folders; 15 | QJsonArray devices; 16 | }; 17 | 18 | struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConfig { 19 | QString version; 20 | bool guiEnabled = false; 21 | bool guiEnforcesSecureConnection = false; 22 | QString guiAddress; 23 | QString guiUser; 24 | QString guiPasswordHash; 25 | QString guiApiKey; 26 | std::optional details; 27 | 28 | static QString locateConfigFile(const QString &fileName); 29 | static QString locateConfigFile(); 30 | static QString locateHttpsCertificate(); 31 | bool restore(const QString &configFilePath, bool detailed = false); 32 | QString syncthingUrl() const; 33 | }; 34 | 35 | } // namespace Data 36 | 37 | #endif // DATA_SYNCTHINGCONFIG_H 38 | -------------------------------------------------------------------------------- /syncthingconnector/syncthingconnectionenums.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGCONNECTION_ENUMS_H 2 | #define SYNCTHINGCONNECTION_ENUMS_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace Data { 9 | enum class SyncthingStatusComputionFlags : quint64; 10 | 11 | enum class SyncthingConnectionLoggingFlags : quint64 { 12 | None, /**< loggingn is disabled */ 13 | FromEnvironment = (1 << 0), /**< environment variables are checked to pull in any of the other flags dynamically */ 14 | ApiCalls = (1 << 1), /**< log calls to Syncthing's REST-API and responses */ 15 | ApiReplies = (1 << 2), /**< log replies from Syncthing's REST-API */ 16 | Events = (1 << 3), /**< log events received via Syncthing's event API */ 17 | DirsOrDevsResetted = (1 << 4), /**< log list of directories/devices when list is reset */ 18 | All = ApiCalls | ApiReplies | Events | DirsOrDevsResetted, /** log as much as possible */ 19 | }; 20 | 21 | } // namespace Data 22 | 23 | CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(Data, Data::SyncthingConnectionLoggingFlags) 24 | 25 | #endif // SYNCTHINGCONNECTION_ENUMS_H 26 | -------------------------------------------------------------------------------- /syncthingconnector/syncthingconnectionmockhelpers.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGCONNECTIONMOCKHELPERS_H 2 | #define SYNCTHINGCONNECTIONMOCKHELPERS_H 3 | 4 | /*! 5 | * \file syncthingconnectionhelper.h 6 | * \brief Provides helper for mocking SyncthingConnection. 7 | * \remarks Only include from syncthingconnection.cpp! 8 | */ 9 | 10 | #include "./global.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace Data { 24 | 25 | LIB_SYNCTHING_CONNECTOR_EXPORT void setupTestData(); 26 | 27 | /*! 28 | * \brief The MockedReply class provides a fake QNetworkReply which will just return data from a specified buffer. 29 | */ 30 | class MockedReply final : public QNetworkReply { 31 | Q_OBJECT 32 | 33 | public: 34 | ~MockedReply() override; 35 | 36 | public Q_SLOTS: 37 | void abort() override; 38 | 39 | public: 40 | // reimplemented from QNetworkReply 41 | void close() override; 42 | qint64 bytesAvailable() const override; 43 | bool isSequential() const override; 44 | qint64 size() const override; 45 | 46 | qint64 readData(char *data, qint64 maxlen) override; 47 | 48 | static MockedReply *forRequest(const QString &method, const QString &path, const QUrlQuery &query, bool rest); 49 | 50 | protected: 51 | explicit MockedReply(QByteArray &&buffer, int delay, QObject *parent = nullptr); 52 | explicit MockedReply(std::string_view view, int delay, QObject *parent = nullptr); 53 | 54 | private Q_SLOTS: 55 | void emitFinished(); 56 | 57 | private: 58 | void init(int delay); 59 | 60 | QByteArray m_buffer; 61 | std::string_view m_view; 62 | const char *m_pos; 63 | qint64 m_bytesLeft; 64 | static int s_eventIndex; 65 | }; 66 | } // namespace Data 67 | 68 | #endif // SYNCTHINGCONNECTIONMOCKHELPERS_H 69 | -------------------------------------------------------------------------------- /syncthingconnector/syncthingdev.cpp: -------------------------------------------------------------------------------- 1 | #include "./syncthingdev.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace CppUtilities; 8 | 9 | namespace Data { 10 | 11 | QString statusString(SyncthingDevStatus status) 12 | { 13 | switch (status) { 14 | case SyncthingDevStatus::Unknown: 15 | return QCoreApplication::translate("SyncthingDevStatus", "Unknown"); 16 | case SyncthingDevStatus::Disconnected: 17 | return QCoreApplication::translate("SyncthingDevStatus", "Disconnected"); 18 | case SyncthingDevStatus::ThisDevice: 19 | return QCoreApplication::translate("SyncthingDevStatus", "This Device"); 20 | case SyncthingDevStatus::Idle: 21 | return QCoreApplication::translate("SyncthingDevStatus", "Idle"); 22 | case SyncthingDevStatus::Synchronizing: 23 | return QCoreApplication::translate("SyncthingDevStatus", "Syncing"); 24 | case SyncthingDevStatus::OutOfSync: 25 | return QCoreApplication::translate("SyncthingDevStatus", "Out of Sync"); 26 | case SyncthingDevStatus::Rejected: 27 | return QCoreApplication::translate("SyncthingDevStatus", "Rejected"); 28 | default: 29 | return QString(); 30 | } 31 | } 32 | 33 | QString SyncthingDev::statusString() const 34 | { 35 | if (paused) { 36 | return QCoreApplication::translate("SyncthingDev", "Paused"); 37 | } 38 | if (status == SyncthingDevStatus::Synchronizing && overallCompletion.needed.bytes) { 39 | return QCoreApplication::translate("SyncthingDev", "Syncing (%1 %, %2)") 40 | .arg(static_cast(overallCompletion.percentage)) 41 | .arg(QString::fromStdString(dataSizeToString(overallCompletion.needed.bytes))); 42 | } 43 | return Data::statusString(status); 44 | } 45 | 46 | } // namespace Data 47 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/browse.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modTime" : "2020-10-02T23:48:52.076996974+02:00", 4 | "name" : "100ANDRO", 5 | "size" : 128, 6 | "type" : "FILE_INFO_TYPE_DIRECTORY" 7 | }, 8 | { 9 | "modTime" : "2020-10-09T13:04:42.4410738+02:00", 10 | "name" : "Camera", 11 | "size" : 128, 12 | "type" : "FILE_INFO_TYPE_DIRECTORY", 13 | "children" : [ 14 | { 15 | "modTime" : "2020-12-16T23:31:34.5009668+01:00", 16 | "name" : "IMG_20201114_124821.jpg", 17 | "size" : 10682189, 18 | "type" : "FILE_INFO_TYPE_FILE" 19 | }, 20 | { 21 | "modTime" : "2020-12-16T23:31:35.0106367+01:00", 22 | "name" : "IMG_20201213_122451.jpg", 23 | "size" : 7936351, 24 | "type" : "FILE_INFO_TYPE_FILE" 25 | }, 26 | { 27 | "modTime" : "2020-12-13T12:25:05.017097469+01:00", 28 | "name" : "IMG_20201213_122504.jpg", 29 | "size" : 8406507, 30 | "type" : "FILE_INFO_TYPE_FILE" 31 | }, 32 | { 33 | "modTime" : "2020-12-13T12:25:06.127097469+01:00", 34 | "name" : "IMG_20201213_122505.jpg", 35 | "size" : 8381931, 36 | "type" : "FILE_INFO_TYPE_FILE" 37 | }, 38 | { 39 | "modTime" : "2020-12-13T12:53:29.707298401+01:00", 40 | "name" : "IMG_20201213_125329.jpg", 41 | "size" : 4388331, 42 | "type" : "FILE_INFO_TYPE_FILE" 43 | } 44 | ] 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/connections.json: -------------------------------------------------------------------------------- 1 | { 2 | "total" : { 3 | "paused" : false, 4 | "clientVersion" : "", 5 | "at" : "2015-11-07T17:29:47.691637262+01:00", 6 | "connected" : false, 7 | "inBytesTotal" : 1479, 8 | "type" : "", 9 | "outBytesTotal" : 1318, 10 | "address" : "" 11 | }, 12 | "connections" : { 13 | "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2" : { 14 | "connected" : true, 15 | "inBytesTotal" : 556, 16 | "paused" : false, 17 | "at" : "2015-11-07T17:29:47.691548971+01:00", 18 | "clientVersion" : "v0.12.1", 19 | "address" : "127.0.0.1:22002", 20 | "type" : "TCP (Client)", 21 | "outBytesTotal" : 550 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/devicestats.json: -------------------------------------------------------------------------------- 1 | { 2 | "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2": { 3 | "lastSeen" : "2015-04-18T11:21:31.3256277+01:00" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/empty.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/errors.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "when": "2014-09-18T12:59:26.549953186+02:00", 5 | "message": "This is an error string" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/events-04.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 6, 4 | "type": "DownloadProgress", 5 | "time": "2018-12-13T00:32:12.9876937Z", 6 | "data": { 7 | "zX8xfl3ygn-": { 8 | "dir\\file2": { 9 | "Total": 80, 10 | "Pulling": 2, 11 | "CopiedFromOrigin": 0, 12 | "Reused": 0, 13 | "CopiedFromElsewhere": 0, 14 | "Pulled": 32, 15 | "BytesTotal": 10420224, 16 | "BytesDone": 8128768 17 | } 18 | } 19 | } 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/events-05.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 7, 4 | "type": "DownloadProgress", 5 | "time": "2018-12-13T00:33:12.9876937Z", 6 | "data": { 7 | } 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/folderstats.json: -------------------------------------------------------------------------------- 1 | { 2 | "GXWxf-3zgnU" : { 3 | "lastScan": "2016-06-02T13:28:01.288181412-04:00", 4 | "lastFile" : { 5 | "filename" : "file/name", 6 | "at" : "2015-04-16T22:04:18.3066971+01:00" 7 | } 8 | }, 9 | "zX8xfl3ygn-" : { 10 | "lastScan": "2011-06-02T13:28:01.288181412-04:00", 11 | "lastFile" : { 12 | "filename" : "another/file/name", 13 | "at" : "2017-04-16T22:04:18.3066971+01:00" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/folderstatus-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalBytes": 13173473780, 3 | "globalDeleted": 1847, 4 | "globalFiles": 42106, 5 | "localBytes": 13173473780, 6 | "localDeleted": 1847, 7 | "localFiles": 42106, 8 | "inSyncBytes": 13173473780, 9 | "inSyncFiles": 42106, 10 | "needBytes": 200, 11 | "needFiles": 101, 12 | "needDirectories": 2, 13 | "needTotalItems": 103, 14 | "ignorePatterns": true, 15 | "invalid": "", 16 | "state": "idle", 17 | "stateChanged": "2015-03-16T21:47:28.750853241+01:00", 18 | "version": 71989 19 | } 20 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/folderstatus-02.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalBytes": 24525452, 3 | "globalDeleted": 8, 4 | "globalFiles": 543, 5 | "globalFolders": 3, 6 | "localBytes": 24525452, 7 | "localDeleted": 8, 8 | "localFiles": 343, 9 | "localFolders": 4, 10 | "inSyncBytes": 13173473780, 11 | "inSyncFiles": 42106, 12 | "needBytes": 0, 13 | "needFiles": 0, 14 | "ignorePatterns": true, 15 | "invalid": "", 16 | "state": "syncing", 17 | "stateChanged": "2018-03-16T21:47:28.750853241+01:00", 18 | "version": 71989 19 | } 20 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/folderstatus-03.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalBytes": 1, 3 | "globalDeleted": 2, 4 | "globalFiles": 3, 5 | "globalFolders": 4, 6 | "localBytes": 1, 7 | "localDeleted": 2, 8 | "localFiles": 3, 9 | "localFolders": 4, 10 | "ignorePatterns": true, 11 | "invalid": "", 12 | "state": "idle", 13 | "stateChanged": "2018-03-16T21:47:28.750853241+01:00", 14 | "version": 21 15 | } 16 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/pullerrors-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "folder": "GXWxf-3zgnU", 3 | "errors": [ 4 | { 5 | "error": "open /Users/jb/src/github.com/syncthing/syncthing/test/s2/h2j/.syncthing.aslkjd.tmp: permission denied", 6 | "path": "h2j/aslkjd" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /syncthingconnector/testfiles/mocks/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "amd64", 3 | "longVersion": "syncthing v0.10.27+3-gea8c3de (go1.4 darwin-amd64 default) jb@syno 2015-03-16 11:01:29 UTC", 4 | "os": "darwin", 5 | "version": "v0.10.27+3-gea8c3de" 6 | } 7 | -------------------------------------------------------------------------------- /syncthingmodel/colors.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_COLORS_H 2 | #define DATA_COLORS_H 3 | 4 | #include 5 | 6 | namespace Data { 7 | 8 | /*! 9 | * \brief The Colors namespace defines default colors (regular and bright version). 10 | */ 11 | namespace Colors { 12 | 13 | inline QColor gray(bool bright) 14 | { 15 | return bright ? QColor(Qt::lightGray) : QColor(Qt::darkGray); 16 | } 17 | 18 | inline QColor red(bool bright) 19 | { 20 | return bright ? QColor(0xFF9A7E) : QColor(Qt::red); 21 | } 22 | 23 | inline QColor green(bool bright) 24 | { 25 | return bright ? QColor(0xA8FF41) : QColor(Qt::darkGreen); 26 | } 27 | 28 | inline QColor blue(bool bright) 29 | { 30 | return bright ? QColor(0x8BCFFF) : QColor(Qt::blue); 31 | } 32 | 33 | inline QColor orange(bool bright) 34 | { 35 | return bright ? QColor(0xFFC500) : QColor(0xA85900); 36 | } 37 | } // namespace Colors 38 | 39 | } // namespace Data 40 | 41 | #endif // DATA_COLORS_H 42 | -------------------------------------------------------------------------------- /syncthingmodel/global.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template global.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef LIB_SYNCTHING_MODEL_GLOBAL 5 | #define LIB_SYNCTHING_MODEL_GLOBAL 6 | 7 | #include "syncthingmodel-definitions.h" 8 | #include 9 | 10 | #ifdef LIB_SYNCTHING_MODEL_STATIC 11 | #define LIB_SYNCTHING_MODEL_EXPORT 12 | #define LIB_SYNCTHING_MODEL_IMPORT 13 | #else 14 | #define LIB_SYNCTHING_MODEL_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT 15 | #define LIB_SYNCTHING_MODEL_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT 16 | #endif 17 | 18 | /*! 19 | * \def LIB_SYNCTHING_MODEL_EXPORT 20 | * \brief Marks the symbol to be exported by the syncthingmodel library. 21 | */ 22 | 23 | /*! 24 | * \def LIB_SYNCTHING_MODEL_IMPORT 25 | * \brief Marks the symbol to be imported from the syncthingmodel library. 26 | */ 27 | 28 | #endif // LIB_SYNCTHING_MODEL_GLOBAL 29 | -------------------------------------------------------------------------------- /syncthingmodel/resources/icons/hicolor/scalable/mimetypes/text-x-generic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /syncthingmodel/resources/icons/hicolor/scalable/status/syncthing-default.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /syncthingmodel/resources/syncthingmodelicons.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/hicolor/scalable/status/syncthing-default.svg 4 | icons/hicolor/scalable/mimetypes/text-x-generic.svg 5 | 6 | 7 | -------------------------------------------------------------------------------- /syncthingmodel/syncthingerrormodel.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_SYNCTHINGERRORMODEL_H 2 | #define DATA_SYNCTHINGERRORMODEL_H 3 | 4 | #include "./syncthingmodel.h" 5 | 6 | namespace Data { 7 | 8 | struct SyncthingError; 9 | 10 | class LIB_SYNCTHING_MODEL_EXPORT SyncthingErrorModel : public SyncthingModel { 11 | Q_OBJECT 12 | 13 | public: 14 | enum SyncthingErrorModelRole { 15 | When = SyncthingModelUserRole + 1, 16 | Message, 17 | }; 18 | 19 | explicit SyncthingErrorModel(SyncthingConnection &connection, QObject *parent = nullptr); 20 | 21 | QHash roleNames() const override; 22 | const QVector &colorRoles() const override; 23 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 24 | QModelIndex parent(const QModelIndex &child) const override; 25 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 26 | QVariant data(const QModelIndex &index, int role) const override; 27 | bool setData(const QModelIndex &index, const QVariant &value, int role) override; 28 | int rowCount(const QModelIndex &parent) const override; 29 | int columnCount(const QModelIndex &parent) const override; 30 | 31 | Q_SIGNALS: 32 | void requestResizeColumns(); 33 | 34 | private Q_SLOTS: 35 | void handleConfigInvalidated() override; 36 | void handleNewConfigAvailable() override; 37 | void handleBeforeNewErrors(const std::vector &oldErrors, const std::vector &newErrors); 38 | void handleNewErrors(const std::vector &newErrors); 39 | void handleForkAwesomeIconsChanged() override; 40 | 41 | private: 42 | enum class Change { None, Reset, Append }; 43 | Change m_currentChange; 44 | int m_currentAppendCount; 45 | }; 46 | 47 | } // namespace Data 48 | 49 | #endif // DATA_SYNCTHINGERRORMODEL_H 50 | -------------------------------------------------------------------------------- /syncthingmodel/syncthingsortfiltermodel.cpp: -------------------------------------------------------------------------------- 1 | #include "./syncthingsortfiltermodel.h" 2 | #include "./syncthingmodel.h" 3 | 4 | #include 5 | 6 | namespace Data { 7 | 8 | bool SyncthingSortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const 9 | { 10 | // show all nested structures 11 | if (sourceParent.isValid()) { 12 | return true; 13 | } 14 | // use default filtering for top-level 15 | return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); 16 | } 17 | 18 | bool SyncthingSortFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const 19 | { 20 | // keep order within nested structures 21 | if (m_behavior == SyncthingSortBehavior::KeepRawOrder || left.parent().isValid() || right.parent().isValid()) { 22 | return left.row() < right.row(); 23 | } 24 | // show pinned items before all other items 25 | const auto leftPinned = left.data(SyncthingModel::IsPinned).toBool(); 26 | const auto rightPinned = right.data(SyncthingModel::IsPinned).toBool(); 27 | if (leftPinned != rightPinned) { 28 | return leftPinned; 29 | } 30 | // use the default sorting for the top-level 31 | return QSortFilterProxyModel::lessThan(left, right); 32 | } 33 | 34 | } // namespace Data 35 | -------------------------------------------------------------------------------- /syncthingmodel/syncthingstatuscomputionmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_SYNCTHINGSTATUSCOMPUTIONMODELL_H 2 | #define DATA_SYNCTHINGSTATUSCOMPUTIONMODELL_H 3 | 4 | #include "./global.h" 5 | 6 | #include 7 | 8 | #define SYNCTHING_MODEL_ENUM_CLASS enum class 9 | namespace Data { 10 | SYNCTHING_MODEL_ENUM_CLASS SyncthingStatusComputionFlags : quint64; 11 | } 12 | #undef SYNCTHING_MODEL_ENUM_CLASS 13 | 14 | namespace Data { 15 | 16 | class LIB_SYNCTHING_MODEL_EXPORT SyncthingStatusComputionModel : public QtUtilities::ChecklistModel { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit SyncthingStatusComputionModel(QObject *parent = nullptr); 21 | QString labelForId(const QVariant &id) const override; 22 | SyncthingStatusComputionFlags statusComputionFlags() const; 23 | void setStatusComputionFlags(SyncthingStatusComputionFlags flags); 24 | }; 25 | 26 | } // namespace Data 27 | 28 | #endif // DATA_SYNCTHINGSTATUSCOMPUTIONMODELL_H 29 | -------------------------------------------------------------------------------- /syncthingmodel/syncthingstatusselectionmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "./syncthingstatusselectionmodel.h" 2 | 3 | #include 4 | 5 | using namespace QtUtilities; 6 | 7 | namespace Data { 8 | 9 | inline static ChecklistItem itemFor(SyncthingStatus status) 10 | { 11 | return ChecklistItem(static_cast(status), QString(), Qt::Unchecked); 12 | } 13 | 14 | SyncthingStatusSelectionModel::SyncthingStatusSelectionModel(QObject *parent) 15 | : ChecklistModel(parent) 16 | { 17 | setItems({ 18 | itemFor(SyncthingStatus::Disconnected), 19 | itemFor(SyncthingStatus::Reconnecting), 20 | itemFor(SyncthingStatus::Idle), 21 | itemFor(SyncthingStatus::Scanning), 22 | itemFor(SyncthingStatus::Paused), 23 | itemFor(SyncthingStatus::Synchronizing), 24 | itemFor(SyncthingStatus::RemoteNotInSync), 25 | itemFor(SyncthingStatus::NoRemoteConnected), 26 | }); 27 | } 28 | 29 | QString SyncthingStatusSelectionModel::labelForId(const QVariant &id) const 30 | { 31 | return SyncthingConnection::statusText(static_cast(id.toInt())); 32 | } 33 | 34 | } // namespace Data 35 | -------------------------------------------------------------------------------- /syncthingmodel/syncthingstatusselectionmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef DATA_SYNCTHINGSTATUSSELECTIONMODELL_H 2 | #define DATA_SYNCTHINGSTATUSSELECTIONMODELL_H 3 | 4 | #include "./global.h" 5 | 6 | #include 7 | 8 | namespace Data { 9 | 10 | class LIB_SYNCTHING_MODEL_EXPORT SyncthingStatusSelectionModel : public QtUtilities::ChecklistModel { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit SyncthingStatusSelectionModel(QObject *parent = nullptr); 15 | QString labelForId(const QVariant &id) const override; 16 | }; 17 | 18 | } // namespace Data 19 | 20 | #endif // DATA_SYNCTHINGSTATUSSELECTIONMODELL_H 21 | -------------------------------------------------------------------------------- /syncthingwidgets/global.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template global.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGWIDGETS_GLOBAL 5 | #define SYNCTHINGWIDGETS_GLOBAL 6 | 7 | #include "syncthingwidgets-definitions.h" 8 | #include 9 | 10 | #ifdef SYNCTHINGWIDGETS_STATIC 11 | #define SYNCTHINGWIDGETS_EXPORT 12 | #define SYNCTHINGWIDGETS_IMPORT 13 | #else 14 | #define SYNCTHINGWIDGETS_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT 15 | #define SYNCTHINGWIDGETS_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT 16 | #endif 17 | 18 | /*! 19 | * \def SYNCTHINGWIDGETS_EXPORT 20 | * \brief Marks the symbol to be exported by the syncthingwidgets library. 21 | */ 22 | 23 | /*! 24 | * \def SYNCTHINGWIDGETS_IMPORT 25 | * \brief Marks the symbol to be imported from the syncthingwidgets library. 26 | */ 27 | 28 | #endif // SYNCTHINGWIDGETS_GLOBAL 29 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/diffhighlighter.cpp: -------------------------------------------------------------------------------- 1 | #include "./diffhighlighter.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace Data; 11 | 12 | namespace QtGui { 13 | 14 | DiffHighlighter::DiffHighlighter(QTextDocument *parent) 15 | : QSyntaxHighlighter(parent) 16 | , m_enabled(true) 17 | { 18 | auto font = QFontDatabase::systemFont(QFontDatabase::FixedFont); 19 | m_baseFormat.setFont(font); 20 | 21 | font.setBold(true); 22 | m_addedFormat.setFont(font); 23 | m_addedFormat.setForeground(Colors::green(true)); 24 | m_deletedFormat.setFont(font); 25 | m_deletedFormat.setForeground(Colors::red(true)); 26 | } 27 | 28 | void DiffHighlighter::highlightBlock(const QString &text) 29 | { 30 | if (text.startsWith(QChar('-'))) { 31 | setFormat(0, static_cast(text.size()), QColor(Qt::red)); 32 | } else if (text.startsWith(QChar('+'))) { 33 | setFormat(0, static_cast(text.size()), QColor(Qt::green)); 34 | } 35 | } 36 | 37 | } // namespace QtGui 38 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/diffhighlighter.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_DIFF_HIGHLIGHTER_H 2 | #define SYNCTHINGWIDGETS_DIFF_HIGHLIGHTER_H 3 | 4 | #include "../global.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace QtGui { 10 | 11 | class SYNCTHINGWIDGETS_EXPORT DiffHighlighter : public QSyntaxHighlighter { 12 | Q_OBJECT 13 | Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) 14 | public: 15 | explicit DiffHighlighter(QTextDocument *parent = nullptr); 16 | 17 | bool isEnabled() const 18 | { 19 | return m_enabled; 20 | } 21 | void setEnabled(bool enabled) 22 | { 23 | if (enabled != m_enabled) { 24 | m_enabled = enabled; 25 | rehighlight(); 26 | } 27 | } 28 | 29 | protected: 30 | void highlightBlock(const QString &text) override; 31 | 32 | private: 33 | QTextCharFormat m_baseFormat, m_addedFormat, m_deletedFormat; 34 | bool m_enabled; 35 | }; 36 | 37 | } // namespace QtGui 38 | 39 | #endif // SYNCTHINGWIDGETS_DIFF_HIGHLIGHTER_H 40 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/direrrorsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_DIRECTORY_ERRORS_DIALOG_H 2 | #define SYNCTHINGWIDGETS_DIRECTORY_ERRORS_DIALOG_H 3 | 4 | #include "./textviewdialog.h" 5 | 6 | QT_FORWARD_DECLARE_CLASS(QLabel) 7 | QT_FORWARD_DECLARE_CLASS(QPushButton) 8 | 9 | namespace Data { 10 | class SyncthingConnection; 11 | struct SyncthingDir; 12 | } // namespace Data 13 | 14 | namespace QtGui { 15 | 16 | class SYNCTHINGWIDGETS_EXPORT DirectoryErrorsDialog : public TextViewDialog { 17 | Q_OBJECT 18 | public: 19 | explicit DirectoryErrorsDialog(const Data::SyncthingConnection &connection, const Data::SyncthingDir &dir, QWidget *parent = nullptr); 20 | ~DirectoryErrorsDialog() override; 21 | 22 | private Q_SLOTS: 23 | void handleDirStatusChanged(const Data::SyncthingDir &dir); 24 | void handleNewDirs(); 25 | void updateErrors(const Data::SyncthingDir &dir); 26 | void removeNonEmptyDirs(); 27 | 28 | private: 29 | const Data::SyncthingConnection &m_connection; 30 | QString m_dirId; 31 | QStringList m_nonEmptyDirs; 32 | QLabel *m_statusLabel; 33 | QPushButton *m_rmNonEmptyDirsButton; 34 | }; 35 | 36 | } // namespace QtGui 37 | 38 | #endif // SYNCTHINGWIDGETS_DIRECTORY_ERRORS_DIALOG_H 39 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/internalerror.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_INTERNAL_ERROR_H 2 | #define SYNCTHINGWIDGETS_INTERNAL_ERROR_H 3 | 4 | #include "../global.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace Data { 14 | class SyncthingConnection; 15 | enum class SyncthingErrorCategory; 16 | } // namespace Data 17 | 18 | namespace QtGui { 19 | 20 | struct SYNCTHINGWIDGETS_EXPORT InternalError { 21 | Q_GADGET 22 | Q_PROPERTY(QString message MEMBER message CONSTANT) 23 | Q_PROPERTY(QUrl url MEMBER url CONSTANT) 24 | Q_PROPERTY(QByteArray response MEMBER response CONSTANT) 25 | Q_PROPERTY(QString when READ whenToString CONSTANT) 26 | 27 | public: 28 | explicit InternalError(const QString &message = QString(), const QUrl &url = QUrl(), const QByteArray &response = QByteArray()); 29 | 30 | static bool isRelevant(const Data::SyncthingConnection &connection, Data::SyncthingErrorCategory category, const QString &message, 31 | int networkError, bool useGlobalSettings = true); 32 | QString whenToString() const; 33 | 34 | QString message; 35 | QUrl url; 36 | QByteArray response; 37 | CppUtilities::DateTime when; 38 | }; 39 | 40 | /*! 41 | * \brief Constructs a new error suitable for display purposes (password in \a url is redacted). 42 | */ 43 | inline InternalError::InternalError(const QString &message, const QUrl &url, const QByteArray &response) 44 | : message(message) 45 | , url(url) 46 | , response(response) 47 | , when(CppUtilities::DateTime::now()) 48 | { 49 | if (!this->url.password().isEmpty()) { 50 | this->url.setPassword(QStringLiteral("redacted")); 51 | } 52 | } 53 | 54 | inline QString InternalError::whenToString() const 55 | { 56 | return QString::fromStdString(when.toString()); 57 | } 58 | 59 | } // namespace QtGui 60 | 61 | #endif // SYNCTHINGWIDGETS_INTERNAL_ERROR_H 62 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/internalerrorsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_INTERNAL_ERRORS_DIALOG_H 2 | #define SYNCTHINGWIDGETS_INTERNAL_ERRORS_DIALOG_H 3 | 4 | #include "./internalerror.h" 5 | #include "./textviewdialog.h" 6 | 7 | #include 8 | 9 | QT_FORWARD_DECLARE_CLASS(QLabel) 10 | 11 | namespace QtGui { 12 | 13 | class SYNCTHINGWIDGETS_EXPORT InternalErrorsDialog : public TextViewDialog { 14 | Q_OBJECT 15 | public: 16 | ~InternalErrorsDialog() override; 17 | static InternalErrorsDialog *instance(); 18 | static bool hasInstance(); 19 | static void addError(InternalError &&newError); 20 | static void addError(const QString &message = QString(), const QUrl &url = QUrl(), const QByteArray &response = QByteArray()); 21 | 22 | Q_SIGNALS: 23 | void errorsCleared(); 24 | 25 | public Q_SLOTS: 26 | static void showInstance(); 27 | static void clearErrors(); 28 | 29 | private Q_SLOTS: 30 | void internalAddError(const InternalError &error); 31 | void updateStatusLabel(); 32 | 33 | private: 34 | InternalErrorsDialog(); 35 | 36 | const QString m_request; 37 | const QString m_response; 38 | QLabel *const m_statusLabel; 39 | static InternalErrorsDialog *s_instance; 40 | static std::vector s_internalErrors; 41 | }; 42 | 43 | inline InternalErrorsDialog *InternalErrorsDialog::instance() 44 | { 45 | return s_instance ? s_instance : (s_instance = new InternalErrorsDialog); 46 | } 47 | 48 | inline bool InternalErrorsDialog::hasInstance() 49 | { 50 | return s_instance != nullptr; 51 | } 52 | 53 | inline void InternalErrorsDialog::showInstance() 54 | { 55 | instance()->show(); 56 | } 57 | } // namespace QtGui 58 | 59 | #endif // SYNCTHINGWIDGETS_INTERNAL_ERRORS_DIALOG_H 60 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/otherdialogs.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_OTHERDIALOGS_H 2 | #define SYNCTHINGWIDGETS_OTHERDIALOGS_H 3 | 4 | #include "../global.h" 5 | 6 | #include 7 | 8 | QT_FORWARD_DECLARE_CLASS(QDialog) 9 | QT_FORWARD_DECLARE_CLASS(QWidget) 10 | 11 | namespace Data { 12 | class SyncthingConnection; 13 | struct SyncthingDir; 14 | } // namespace Data 15 | 16 | namespace QtGui { 17 | class TextViewDialog; 18 | 19 | SYNCTHINGWIDGETS_EXPORT QDialog *ownDeviceIdDialog(Data::SyncthingConnection &connection); 20 | SYNCTHINGWIDGETS_EXPORT QWidget *ownDeviceIdWidget(Data::SyncthingConnection &connection, int size, QWidget *parent = nullptr); 21 | SYNCTHINGWIDGETS_EXPORT QDialog *browseRemoteFilesDialog( 22 | Data::SyncthingConnection &connection, const Data::SyncthingDir &dir, QWidget *parent = nullptr); 23 | SYNCTHINGWIDGETS_EXPORT QDialog *errorNotificationsDialog(Data::SyncthingConnection &connection, QWidget *parent = nullptr); 24 | SYNCTHINGWIDGETS_EXPORT TextViewDialog *ignorePatternsDialog( 25 | Data::SyncthingConnection &connection, const Data::SyncthingDir &dir, QWidget *parent = nullptr); 26 | 27 | } // namespace QtGui 28 | 29 | #endif // SYNCTHINGWIDGETS_OTHERDIALOGS_H 30 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/statusinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_STATUSINFO_H 2 | #define SYNCTHINGWIDGETS_STATUSINFO_H 3 | 4 | #include "../global.h" 5 | 6 | #include 7 | 8 | QT_FORWARD_DECLARE_CLASS(QIcon) 9 | 10 | namespace Data { 11 | class SyncthingConnection; 12 | } 13 | 14 | namespace QtGui { 15 | 16 | class SYNCTHINGWIDGETS_EXPORT StatusInfo { 17 | public: 18 | explicit StatusInfo(); 19 | explicit StatusInfo(const Data::SyncthingConnection &connection, const QString &configurationName = QString()); 20 | 21 | const QString &statusText() const; 22 | const QString &additionalStatusText() const; 23 | const QIcon &statusIcon() const; 24 | void updateConnectionStatus(const Data::SyncthingConnection &connection, const QString &configurationName = QString()); 25 | void updateConnectedDevices(const Data::SyncthingConnection &connection); 26 | 27 | private: 28 | void recomputeAdditionalStatusText(); 29 | 30 | QString m_statusText; 31 | QString m_additionalStatusInfo; 32 | QString m_additionalDeviceInfo; 33 | QString m_additionalStatusText; 34 | const QIcon *m_statusIcon; 35 | }; 36 | 37 | inline StatusInfo::StatusInfo(const Data::SyncthingConnection &connection, const QString &configurationName) 38 | { 39 | updateConnectionStatus(connection, configurationName); 40 | updateConnectedDevices(connection); 41 | } 42 | 43 | inline const QString &StatusInfo::statusText() const 44 | { 45 | return m_statusText; 46 | } 47 | 48 | inline const QString &StatusInfo::additionalStatusText() const 49 | { 50 | return m_additionalStatusText; 51 | } 52 | 53 | inline const QIcon &StatusInfo::statusIcon() const 54 | { 55 | return *m_statusIcon; 56 | } 57 | } // namespace QtGui 58 | 59 | #endif // SYNCTHINGWIDGETS_STATUSINFO_H 60 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/syncthingkiller.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_SYNCTHINGKILLER_H 2 | #define SYNCTHINGWIDGETS_SYNCTHINGKILLER_H 3 | 4 | #include "../global.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace Data { 11 | class SyncthingConnection; 12 | class SyncthingProcess; 13 | } // namespace Data 14 | 15 | namespace QtGui { 16 | 17 | struct ProcessWithConnection { 18 | explicit ProcessWithConnection(Data::SyncthingProcess *process, Data::SyncthingConnection *connection = nullptr); 19 | Data::SyncthingProcess *const process; 20 | Data::SyncthingConnection *const connection; 21 | }; 22 | 23 | inline ProcessWithConnection::ProcessWithConnection(Data::SyncthingProcess *process, Data::SyncthingConnection *connection) 24 | : process(process) 25 | , connection(connection) 26 | { 27 | } 28 | 29 | class SYNCTHINGWIDGETS_EXPORT SyncthingKiller : public QObject { 30 | Q_OBJECT 31 | public: 32 | explicit SyncthingKiller(std::vector &&processes); 33 | 34 | Q_SIGNALS: 35 | void ignored(); 36 | 37 | public Q_SLOTS: 38 | void waitForFinished(); 39 | 40 | private Q_SLOTS: 41 | void confirmKill() const; 42 | 43 | private: 44 | std::vector m_processes; 45 | }; 46 | 47 | } // namespace QtGui 48 | 49 | #endif // SYNCTHINGWIDGETS_SYNCTHINGKILLER_H 50 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/textviewdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_TEXTVIEWDIALOG_H 2 | #define SYNCTHINGWIDGETS_TEXTVIEWDIALOG_H 3 | 4 | #include "../global.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | QT_FORWARD_DECLARE_CLASS(QTextBrowser) 11 | QT_FORWARD_DECLARE_CLASS(QVBoxLayout) 12 | 13 | namespace Data { 14 | class SyncthingConnection; 15 | struct SyncthingDir; 16 | struct SyncthingLogEntry; 17 | } // namespace Data 18 | 19 | namespace QtGui { 20 | 21 | class SYNCTHINGWIDGETS_EXPORT TextViewDialog : public QDialog { 22 | Q_OBJECT 23 | public: 24 | explicit TextViewDialog(const QString &title = QString(), QWidget *parent = nullptr); 25 | 26 | QTextBrowser *browser(); 27 | QVBoxLayout *layout(); 28 | void setCloseHandler(std::function &&closeHandler); 29 | static TextViewDialog *forLogEntries(Data::SyncthingConnection &connection, QObject *gui = nullptr); 30 | static TextViewDialog *forLogEntries(const std::vector &logEntries, const QString &title = QString()); 31 | 32 | Q_SIGNALS: 33 | void reload(); 34 | void save(); 35 | 36 | protected: 37 | void keyPressEvent(QKeyEvent *event) override; 38 | void closeEvent(QCloseEvent *event) override; 39 | 40 | private: 41 | void showLogEntries(const std::vector &logEntries); 42 | 43 | QTextBrowser *m_browser; 44 | QVBoxLayout *m_layout; 45 | std::function m_closeHandler; 46 | }; 47 | 48 | inline QTextBrowser *TextViewDialog::browser() 49 | { 50 | return m_browser; 51 | } 52 | 53 | inline QVBoxLayout *TextViewDialog::layout() 54 | { 55 | return m_layout; 56 | } 57 | 58 | inline void TextViewDialog::setCloseHandler(std::function &&closeHandler) 59 | { 60 | m_closeHandler = std::move(closeHandler); 61 | } 62 | 63 | } // namespace QtGui 64 | 65 | #endif // SYNCTHINGWIDGETS_TEXTVIEWDIALOG_H 66 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "./utils.h" 2 | 3 | // use meta-data of syncthingtray application here 4 | #include "resources/../../tray/resources/config.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace QtGui { 11 | 12 | void handleRelevantControlsChanged(bool visible, int tabIndex, Data::SyncthingConnection &connection) 13 | { 14 | auto flags = connection.pollingFlags(); 15 | CppUtilities::modFlagEnum(flags, Data::SyncthingConnection::PollingFlags::DownloadProgress, visible && tabIndex == 3); 16 | CppUtilities::modFlagEnum(flags, Data::SyncthingConnection::PollingFlags::DiskEvents, visible && tabIndex == 2); 17 | CppUtilities::modFlagEnum(flags, Data::SyncthingConnection::PollingFlags::TrafficStatistics, visible); 18 | CppUtilities::modFlagEnum(flags, Data::SyncthingConnection::PollingFlags::DeviceStatistics, visible && tabIndex == 1); 19 | connection.setPollingFlags(flags); 20 | } 21 | 22 | QString readmeUrl() 23 | { 24 | if constexpr (std::string_view(APP_VERSION).find('-') == std::string_view::npos) { 25 | return QStringLiteral("https://github.com/" APP_AUTHOR "/" PROJECT_NAME "/blob/v" APP_VERSION "/README.md"); 26 | } else { 27 | return QStringLiteral("https://github.com/" APP_AUTHOR "/" PROJECT_NAME "/blob/master/README.md"); 28 | } 29 | } 30 | 31 | } // namespace QtGui 32 | -------------------------------------------------------------------------------- /syncthingwidgets/misc/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef SYNCTHINGWIDGETS_UTILS_H 2 | #define SYNCTHINGWIDGETS_UTILS_H 3 | 4 | #include "../global.h" 5 | 6 | #include 7 | 8 | QT_FORWARD_DECLARE_CLASS(QString) 9 | 10 | namespace Data { 11 | class SyncthingConnection; 12 | } 13 | 14 | namespace QtGui { 15 | 16 | SYNCTHINGWIDGETS_EXPORT void handleRelevantControlsChanged(bool visible, int tabIndex, Data::SyncthingConnection &connection); 17 | SYNCTHINGWIDGETS_EXPORT QString readmeUrl(); 18 | 19 | } // namespace QtGui 20 | 21 | #endif // SYNCTHINGWIDGETS_UTILS_H 22 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/document-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/edit-paste.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/edit-undo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/go-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/go-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/list-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/list-remove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/process-stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/tools-wizard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/actions/view-refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/app/syncthingtray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/icons/hicolor/scalable/apps/internet-web-browser.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /syncthingwidgets/resources/syncthingwidgetsicons.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/hicolor/scalable/actions/view-refresh.svg 4 | icons/hicolor/scalable/apps/internet-web-browser.svg 5 | icons/hicolor/scalable/apps/preferences-other.svg 6 | icons/hicolor/scalable/apps/system-help.svg 7 | icons/hicolor/scalable/apps/system-run.svg 8 | icons/hicolor/scalable/actions/process-stop.svg 9 | icons/hicolor/scalable/actions/list-add.svg 10 | icons/hicolor/scalable/actions/list-remove.svg 11 | icons/hicolor/scalable/actions/edit-paste.svg 12 | icons/hicolor/scalable/app/syncthingtray.svg 13 | icons/hicolor/scalable/actions/edit-undo.svg 14 | icons/hicolor/scalable/actions/document-open.svg 15 | icons/hicolor/scalable/actions/go-down.svg 16 | icons/hicolor/scalable/actions/go-up.svg 17 | icons/hicolor/scalable/actions/tools-wizard.svg 18 | 19 | 20 | -------------------------------------------------------------------------------- /syncthingwidgets/settings/applywizardpage.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | QtGui::ApplyWizardPage 4 | 5 | 6 | General 7 | 8 | 9 | 10 | :/icons/hicolor/scalable/apps/internet-web-browser.svg:/icons/hicolor/scalable/apps/internet-web-browser.svg 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /syncthingwidgets/settings/wizardenums.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_WIZARD_ENUMS_H 2 | #define SETTINGS_WIZARD_ENUMS_H 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace QtGui { 9 | 10 | enum class MainConfiguration : quint64 { 11 | None, 12 | CurrentlyRunning, 13 | LauncherExternal, 14 | LauncherBuiltIn, 15 | SystemdUserUnit, 16 | SystemdSystemUnit, 17 | }; 18 | 19 | enum class ExtraConfiguration : quint64 { 20 | None, 21 | SystemdIntegration = (1 << 0), 22 | }; 23 | 24 | } // namespace QtGui 25 | 26 | CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(QtGui, QtGui::ExtraConfiguration) 27 | 28 | #endif // SETTINGS_WIZARD_H 29 | -------------------------------------------------------------------------------- /syncthingwidgets/webview/webviewdefs.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template webviewdefs.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGWIDGETS_WEB_VIEW_DEFINES 5 | #define SYNCTHINGWIDGETS_WEB_VIEW_DEFINES 6 | 7 | #include 8 | 9 | #if defined(SYNCTHINGWIDGETS_USE_WEBENGINE) 10 | # define SYNCTHINGWIDGETS_WEB_VIEW QWebEngineView 11 | # define SYNCTHINGWIDGETS_WEB_PAGE QWebEnginePage 12 | #elif defined(SYNCTHINGWIDGETS_USE_WEBKIT) 13 | # define SYNCTHINGWIDGETS_WEB_VIEW QWebView 14 | # define SYNCTHINGWIDGETS_WEB_PAGE QWebPage 15 | # define SYNCTHINGWIDGETS_WEB_FRAME QWebFrame 16 | #elif !defined(SYNCTHINGWIDGETS_NO_WEBVIEW) 17 | # error "No definition for web view provider present." 18 | #endif 19 | 20 | #ifdef SYNCTHINGWIDGETS_WEB_VIEW 21 | QT_FORWARD_DECLARE_CLASS(SYNCTHINGWIDGETS_WEB_VIEW) 22 | #endif 23 | #ifdef SYNCTHINGWIDGETS_WEB_PAGE 24 | QT_FORWARD_DECLARE_CLASS(SYNCTHINGWIDGETS_WEB_PAGE) 25 | #endif 26 | #ifdef SYNCTHINGWIDGETS_WEB_FRAME 27 | QT_FORWARD_DECLARE_CLASS(SYNCTHINGWIDGETS_WEB_FRAME) 28 | #endif 29 | 30 | #endif // SYNCTHINGWIDGETS_WEB_VIEW_DEFINES 31 | -------------------------------------------------------------------------------- /syncthingwidgets/webview/webviewincludes.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template webviewincludes.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGWIDGETS_WEB_VIEW_INCLUDES 5 | #define SYNCTHINGWIDGETS_WEB_VIEW_INCLUDES 6 | 7 | #include 8 | 9 | #if defined(SYNCTHINGWIDGETS_USE_WEBENGINE) 10 | # include 11 | # include 12 | # include 13 | #elif defined(SYNCTHINGWIDGETS_USE_WEBKIT) 14 | # include 15 | # include 16 | # include 17 | #elif !defined(SYNCTHINGWIDGETS_NO_WEBVIEW) 18 | # error "No definition for web view provider present." 19 | #endif 20 | 21 | #endif // SYNCTHINGWIDGETS_WEB_VIEW_INCLUDES 22 | -------------------------------------------------------------------------------- /syncthingwidgets/webview/webviewinterceptor.cpp: -------------------------------------------------------------------------------- 1 | #ifdef SYNCTHINGWIDGETS_USE_WEBENGINE 2 | #include "./webviewinterceptor.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace QtGui { 10 | 11 | void WebViewInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) 12 | { 13 | info.setHttpHeader(QByteArrayLiteral("X-API-Key"), m_settings.apiKey); 14 | } 15 | 16 | } // namespace QtGui 17 | 18 | #endif // SYNCTHINGWIDGETS_USE_WEBENGINE 19 | -------------------------------------------------------------------------------- /syncthingwidgets/webview/webviewinterceptor.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBVIEW_INTERCEPTOR_H 2 | #define WEBVIEW_INTERCEPTOR_H 3 | #ifdef SYNCTHINGWIDGETS_USE_WEBENGINE 4 | 5 | #include 6 | 7 | namespace Data { 8 | struct SyncthingConnectionSettings; 9 | } 10 | 11 | namespace QtGui { 12 | 13 | class WebViewInterceptor : public QWebEngineUrlRequestInterceptor { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit WebViewInterceptor(const Data::SyncthingConnectionSettings &settings, QObject *parent = nullptr); 18 | 19 | virtual void interceptRequest(QWebEngineUrlRequestInfo &info) override; 20 | 21 | private: 22 | const Data::SyncthingConnectionSettings &m_settings; 23 | }; 24 | 25 | inline WebViewInterceptor::WebViewInterceptor(const Data::SyncthingConnectionSettings &settings, QObject *parent) 26 | : QWebEngineUrlRequestInterceptor(parent) 27 | , m_settings(settings) 28 | { 29 | } 30 | 31 | } // namespace QtGui 32 | 33 | #endif // SYNCTHINGWIDGETS_USE_WEBENGINE 34 | #endif // WEBVIEW_INTERCEPTOR_H 35 | -------------------------------------------------------------------------------- /testhelper/global.h: -------------------------------------------------------------------------------- 1 | // Created via CMake from template global.h.in 2 | // WARNING! Any changes to this file will be overwritten by the next CMake run! 3 | 4 | #ifndef SYNCTHINGTESTHELPER_GLOBAL 5 | #define SYNCTHINGTESTHELPER_GLOBAL 6 | 7 | #include "syncthingtesthelper-definitions.h" 8 | #include 9 | 10 | #ifdef SYNCTHINGTESTHELPER_STATIC 11 | #define SYNCTHINGTESTHELPER_EXPORT 12 | #define SYNCTHINGTESTHELPER_IMPORT 13 | #else 14 | #define SYNCTHINGTESTHELPER_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT 15 | #define SYNCTHINGTESTHELPER_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT 16 | #endif 17 | 18 | /*! 19 | * \def SYNCTHINGTESTHELPER_EXPORT 20 | * \brief Marks the symbol to be exported by the syncthingtesthelper library. 21 | */ 22 | 23 | /*! 24 | * \def SYNCTHINGTESTHELPER_IMPORT 25 | * \brief Marks the symbol to be imported from the syncthingtesthelper library. 26 | */ 27 | 28 | #endif // SYNCTHINGTESTHELPER_GLOBAL 29 | -------------------------------------------------------------------------------- /testhelper/helper.cpp: -------------------------------------------------------------------------------- 1 | #include "./helper.h" 2 | 3 | namespace CppUtilities { 4 | 5 | /*! 6 | * \brief Specifies the factor for multiplying all timeouts passed to functions declared in helper.h. 7 | * 8 | * The default factor is 1.0. 9 | */ 10 | double timeoutFactor = 1.0; 11 | } // namespace CppUtilities 12 | -------------------------------------------------------------------------------- /testhelper/tests/manualtesting.cpp: -------------------------------------------------------------------------------- 1 | #include "../syncthingtestinstance.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace std; 8 | using namespace CppUtilities; 9 | 10 | /*! 11 | * \brief Launches a Syncthing test instance for manual testing. 12 | */ 13 | int main(int argc, char **argv) 14 | { 15 | TestApplication testApp(argc, argv); 16 | if (!testApp) { 17 | return -1; 18 | } 19 | 20 | SyncthingTestInstance testInstance; 21 | auto &syncthingProcess(testInstance.syncthingProcess()); 22 | //syncthingProcess.setProcessChannelMode(QProcess::ForwardedChannels); 23 | QObject::connect(&syncthingProcess, static_cast(&Data::SyncthingProcess::finished), 24 | &QCoreApplication::exit); 25 | testInstance.start(); 26 | 27 | const int res = testInstance.application().exec(); 28 | testInstance.stop(); 29 | return res; 30 | } 31 | -------------------------------------------------------------------------------- /tray/android/res/drawable-hdpi/ic_stat_notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/drawable-hdpi/ic_stat_notify.png -------------------------------------------------------------------------------- /tray/android/res/drawable-mdpi/ic_stat_notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/drawable-mdpi/ic_stat_notify.png -------------------------------------------------------------------------------- /tray/android/res/drawable-xhdpi/ic_stat_notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/drawable-xhdpi/ic_stat_notify.png -------------------------------------------------------------------------------- /tray/android/res/drawable-xxhdpi/ic_stat_notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/drawable-xxhdpi/ic_stat_notify.png -------------------------------------------------------------------------------- /tray/android/res/drawable-xxxhdpi/ic_stat_notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/drawable-xxxhdpi/ic_stat_notify.png -------------------------------------------------------------------------------- /tray/android/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/drawable/icon.png -------------------------------------------------------------------------------- /tray/android/res/drawable/splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tray/android/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tray/android/res/mipmap/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/mipmap/ic_launcher_background.png -------------------------------------------------------------------------------- /tray/android/res/mipmap/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/android/res/mipmap/ic_launcher_foreground.png -------------------------------------------------------------------------------- /tray/android/res/values/apptheme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /tray/android/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tray/android/signalhandler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #define ST_LIB_LOG_TAG "SyncthingLibrary" 7 | #define ST_LIB_LOG_INF(...) __android_log_print(ANDROID_LOG_INFO, ST_LIB_LOG_TAG, __VA_ARGS__) 8 | 9 | static void handleSigsys(int signum, siginfo_t *info, void *context) 10 | { 11 | // ignore the signal to prevent the app from crashing 12 | ST_LIB_LOG_INF("SIGSYS signal received and ignored"); 13 | } 14 | 15 | static void initSigsysHandler() 16 | { 17 | struct sigaction sa; 18 | sa.sa_flags = SA_SIGINFO; 19 | sa.sa_sigaction = handleSigsys; 20 | sigemptyset(&sa.sa_mask); 21 | if (sigaction(SIGSYS, &sa, nullptr) == -1) { 22 | ST_LIB_LOG_INF("Failed to set up SIGSYS handler"); 23 | } else { 24 | ST_LIB_LOG_INF("SIGSYS handler set up successfully"); 25 | } 26 | } 27 | 28 | extern "C" { 29 | 30 | JNIEXPORT void JNICALL 31 | Java_io_github_martchus_syncthingtray_Activity_initSigsysHandler(JNIEnv *env, jobject thiz) 32 | { 33 | initSigsysHandler(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /tray/android/src/io/github/martchus/syncthingtray/Application.java: -------------------------------------------------------------------------------- 1 | package io.github.martchus.syncthingtray; 2 | 3 | import android.os.StrictMode; 4 | 5 | import org.qtproject.qt.android.bindings.QtApplication; 6 | 7 | public class Application extends QtApplication { 8 | 9 | @Override 10 | public void onCreate() { 11 | super.onCreate(); 12 | 13 | // set VM policy to avoid crash when sending folder URI to file manager 14 | StrictMode.VmPolicy vmPolicy = new StrictMode.VmPolicy.Builder() 15 | .detectAll() 16 | .penaltyLog() 17 | .build(); 18 | StrictMode.setVmPolicy(vmPolicy); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tray/android/src/io/github/martchus/syncthingtray/SyncthingServiceBinder.java: -------------------------------------------------------------------------------- 1 | package io.github.martchus.syncthingtray; 2 | 3 | import android.os.Binder; 4 | 5 | public class SyncthingServiceBinder extends Binder { 6 | private final SyncthingService m_service; 7 | 8 | public SyncthingServiceBinder(SyncthingService service) { 9 | m_service = service; 10 | } 11 | 12 | public SyncthingService getService() { 13 | return m_service; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tray/application/singleinstance.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLEINSTANCE_H 2 | #define SINGLEINSTANCE_H 3 | 4 | #include 5 | 6 | QT_FORWARD_DECLARE_CLASS(QLocalServer) 7 | 8 | namespace Data { 9 | struct SyncthingDir; 10 | } 11 | 12 | namespace QtGui { 13 | 14 | class SingleInstance : public QObject { 15 | Q_OBJECT 16 | public: 17 | explicit SingleInstance( 18 | int argc, const char *const *argv, bool skipSingleInstanceBehavior = false, bool skipPassing = false, QObject *parent = nullptr); 19 | 20 | Q_SIGNALS: 21 | void newInstance(int argc, const char *const *argv); 22 | 23 | public: 24 | static const QString &applicationId(); 25 | static bool passArgsToRunningInstance(int argc, const char *const *argv, const QString &appId, bool waitUntilGone = false); 26 | 27 | private Q_SLOTS: 28 | void handleNewConnection(); 29 | void readArgs(); 30 | 31 | private: 32 | QLocalServer *m_server; 33 | }; 34 | } // namespace QtGui 35 | 36 | #endif // SINGLEINSTANCE_H 37 | -------------------------------------------------------------------------------- /tray/cmake/templates/bash-completion.sh.in: -------------------------------------------------------------------------------- 1 | _@META_TARGET_NAME@() 2 | { 3 | export COMP_LINE=${COMP_WORDS[@]} # for Syncthing's own completion which is invoked via `complete -C ...` 4 | reply=$(@TARGET_EXECUTABLE@ --bash-completion-for "$((COMP_CWORD - 1))" "${COMP_WORDS[@]:1}") 5 | if [[ $reply =~ COMPREPLY=.* ]]; then 6 | eval "$reply" # for own completions which output `COMPREPLY=(...)` 7 | else 8 | COMPREPLY=($reply) # for Syncthing's own completion which outputs just the values 9 | fi 10 | return 0; 11 | } 12 | complete -F _@META_TARGET_NAME@ @META_TARGET_NAME@ 13 | -------------------------------------------------------------------------------- /tray/gui/devbuttonsitemdelegate.cpp: -------------------------------------------------------------------------------- 1 | #include "./devbuttonsitemdelegate.h" 2 | 3 | #include "./helper.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace Data; 18 | 19 | namespace QtGui { 20 | 21 | DevButtonsItemDelegate::DevButtonsItemDelegate(QObject *parent) 22 | : QStyledItemDelegate(parent) 23 | { 24 | } 25 | 26 | void DevButtonsItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 27 | { 28 | auto opt = option; 29 | initStyleOption(&opt, index); 30 | opt.viewItemPosition = QStyleOptionViewItem::OnlyOne; 31 | 32 | if (index.parent().isValid()) { 33 | drawField(this, painter, opt, index, SyncthingDeviceModel::DeviceDetail); 34 | } else { 35 | drawIdAndStatus(this, painter, opt, index, SyncthingDeviceModel::DeviceStatusString, SyncthingDeviceModel::DeviceStatusColor, 36 | listItemIconsSize(0) + listItemSpacing); 37 | 38 | // draw button 39 | if (index.data(SyncthingDeviceModel::IsThisDevice).toBool()) { 40 | return; 41 | } 42 | const int buttonY = option.rect.y() + centerObj(option.rect.height(), listItemIconSize); 43 | QtForkAwesome::Renderer::global().render( 44 | index.data(SyncthingDeviceModel::DevicePaused).toBool() ? QtForkAwesome::Icon::Play : QtForkAwesome::Icon::Pause, painter, 45 | QRect(option.rect.right() - listItemIconsSize(0), buttonY, listItemIconSize, listItemIconSize), 46 | QGuiApplication::palette().color(QPalette::Text)); 47 | } 48 | } 49 | } // namespace QtGui 50 | -------------------------------------------------------------------------------- /tray/gui/devbuttonsitemdelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef DEVBUTTONSITEMDELEGATE_H 2 | #define DEVBUTTONSITEMDELEGATE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace QtGui { 8 | 9 | class DevButtonsItemDelegate : public QStyledItemDelegate { 10 | Q_OBJECT 11 | public: 12 | explicit DevButtonsItemDelegate(QObject *parent); 13 | 14 | void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; 15 | }; 16 | } // namespace QtGui 17 | 18 | #endif // DEVBUTTONSITEMDELEGATE_H 19 | -------------------------------------------------------------------------------- /tray/gui/devview.h: -------------------------------------------------------------------------------- 1 | #ifndef DEVVIEW_H 2 | #define DEVVIEW_H 3 | 4 | #include "./helper.h" 5 | 6 | namespace Data { 7 | struct SyncthingDev; 8 | class SyncthingDeviceModel; 9 | class SyncthingSortFilterModel; 10 | } // namespace Data 11 | 12 | namespace QtGui { 13 | 14 | class DevView : public BasicTreeView { 15 | Q_OBJECT 16 | public: 17 | using ModelType = Data::SyncthingDeviceModel; 18 | using SortFilterModelType = Data::SyncthingSortFilterModel; 19 | 20 | explicit DevView(QWidget *parent = nullptr); 21 | 22 | Q_SIGNALS: 23 | void pauseResumeDev(const Data::SyncthingDev &dev); 24 | 25 | protected: 26 | void mouseReleaseEvent(QMouseEvent *event) override; 27 | 28 | private Q_SLOTS: 29 | void showContextMenu(const QPoint &position); 30 | }; 31 | } // namespace QtGui 32 | 33 | #endif // DEVVIEW_H 34 | -------------------------------------------------------------------------------- /tray/gui/dirbuttonsitemdelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef DIRBUTTONSITEMDELEGATE_H 2 | #define DIRBUTTONSITEMDELEGATE_H 3 | 4 | #include 5 | 6 | namespace QtGui { 7 | 8 | class DirButtonsItemDelegate : public QStyledItemDelegate { 9 | Q_OBJECT 10 | public: 11 | explicit DirButtonsItemDelegate(QObject *parent); 12 | 13 | void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; 14 | }; 15 | } // namespace QtGui 16 | 17 | #endif // DIRBUTTONSITEMDELEGATE_H 18 | -------------------------------------------------------------------------------- /tray/gui/dirview.h: -------------------------------------------------------------------------------- 1 | #ifndef DIRVIEW_H 2 | #define DIRVIEW_H 3 | 4 | #include "./helper.h" 5 | 6 | namespace Data { 7 | struct SyncthingDir; 8 | class SyncthingDirectoryModel; 9 | class SyncthingSortFilterModel; 10 | } // namespace Data 11 | 12 | namespace QtGui { 13 | 14 | class DirView : public BasicTreeView { 15 | Q_OBJECT 16 | public: 17 | using ModelType = Data::SyncthingDirectoryModel; 18 | using SortFilterModelType = Data::SyncthingSortFilterModel; 19 | 20 | explicit DirView(QWidget *parent = nullptr); 21 | 22 | Q_SIGNALS: 23 | void openDir(const Data::SyncthingDir &dir); 24 | void scanDir(const Data::SyncthingDir &dir); 25 | void pauseResumeDir(const Data::SyncthingDir &dir); 26 | void browseRemoteFiles(const Data::SyncthingDir &dir); 27 | void showIgnorePatterns(const Data::SyncthingDir &dir); 28 | 29 | protected: 30 | void mouseReleaseEvent(QMouseEvent *event) override; 31 | 32 | private Q_SLOTS: 33 | void showContextMenu(const QPoint &position); 34 | }; 35 | } // namespace QtGui 36 | 37 | #endif // DIRVIEW_H 38 | -------------------------------------------------------------------------------- /tray/gui/downloaditemdelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNLOADITEMDELEGATE_H 2 | #define DOWNLOADITEMDELEGATE_H 3 | 4 | #include 5 | #include 6 | 7 | namespace QtGui { 8 | 9 | class DownloadItemDelegate : public QStyledItemDelegate { 10 | Q_OBJECT 11 | public: 12 | explicit DownloadItemDelegate(QObject *parent); 13 | 14 | void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; 15 | QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; 16 | }; 17 | } // namespace QtGui 18 | 19 | #endif // DOWNLOADITEMDELEGATE_H 20 | -------------------------------------------------------------------------------- /tray/gui/downloadview.h: -------------------------------------------------------------------------------- 1 | #ifndef DOWNLOADVIEW_H 2 | #define DOWNLOADVIEW_H 3 | 4 | #include "./helper.h" 5 | 6 | namespace Data { 7 | struct SyncthingItemDownloadProgress; 8 | struct SyncthingDir; 9 | class SyncthingDownloadModel; 10 | } // namespace Data 11 | 12 | namespace QtGui { 13 | 14 | class DownloadView : public BasicTreeView { 15 | Q_OBJECT 16 | public: 17 | using ModelType = Data::SyncthingDownloadModel; 18 | using SortFilterModelType = void; 19 | 20 | explicit DownloadView(QWidget *parent = nullptr); 21 | 22 | Q_SIGNALS: 23 | void openDir(const Data::SyncthingDir &dir); 24 | void openItemDir(const Data::SyncthingItemDownloadProgress &dir); 25 | 26 | protected: 27 | void mouseReleaseEvent(QMouseEvent *event) override; 28 | 29 | private Q_SLOTS: 30 | void showContextMenu(const QPoint &position); 31 | 32 | private: 33 | void emitOpenDir(QPair info); 34 | }; 35 | } // namespace QtGui 36 | 37 | #endif // DOWNLOADVIEW_H 38 | -------------------------------------------------------------------------------- /tray/gui/qml/AdvancedDevConfigPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | 3 | import Main 4 | 5 | AdvancedConfigPage { 6 | title: qsTr("Advanced config of device \"%1\"").arg(devName) 7 | entryName: "device" 8 | entriesKey: "devices" 9 | isEntry: (device) => device.deviceID === devId 10 | configCategory: "config-option-device" 11 | required property string devName 12 | required property string devId 13 | function makeNewConfig() { 14 | const config = App.connection.rawConfig?.defaults?.device ?? {}; 15 | 16 | // add device ID and name as default values for deviceID/name 17 | if (devId.length > 0) { 18 | config.deviceID = devId; 19 | } 20 | if (devName.length > 0) { 21 | config.name = devName; 22 | } 23 | 24 | isNew = true; 25 | return config; 26 | } 27 | function updateIdentification() { 28 | const id = configObject.deviceID ?? ""; 29 | const name = configObject.name ?? ""; 30 | devName = name.length > 0 ? name : id; 31 | if (!configObjectExists) { 32 | devId = id; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tray/gui/qml/AdvancedDirConfigPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | 3 | import Main 4 | 5 | AdvancedConfigPage { 6 | title: qsTr("Advanced config of folder \"%1\"").arg(dirName) 7 | entryName: "folder" 8 | entriesKey: "folders" 9 | isEntry: (folder) => folder.id === dirId 10 | configCategory: "config-option-folder" 11 | required property string dirName 12 | required property string dirId 13 | function makeNewConfig() { 14 | const config = App.connection.rawConfig?.defaults?.folder ?? {}; 15 | 16 | // for now, give user simply always the chance to edit ignore patterns before syncing 17 | config.paused = true; 18 | 19 | // add dirId/dirName as default values for id/label 20 | if (dirId.length > 0) { 21 | config.id = dirId; 22 | } 23 | if (dirName.length > 0) { 24 | config.label = dirName; 25 | } 26 | 27 | isNew = true; 28 | return config; 29 | } 30 | function updateIdentification() { 31 | const id = configObject.id ?? ""; 32 | const label = configObject.label ?? ""; 33 | dirName = label.length > 0 ? label : id; 34 | if (!configObjectExists) { 35 | dirId = id; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tray/gui/qml/ArrayElementButtons.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | RowLayout { 8 | visible: rowData.isArray 9 | IconOnlyButton { 10 | text: qsTr("Move down") 11 | icon.source: App.faUrlBase + "angle-down" 12 | onClicked: page.swapObjects(rowData, 1) 13 | } 14 | IconOnlyButton { 15 | text: qsTr("Move up") 16 | icon.source: App.faUrlBase + "angle-up" 17 | onClicked: page.swapObjects(rowData, -1) 18 | } 19 | IconOnlyButton { 20 | id: menuButton 21 | text: qsTr("More options") 22 | icon.source: App.faUrlBase + "ellipsis-v" 23 | onClicked: menu.popup(menuButton, menuButton.width / 2 - menu.width, menuButton.height / 2) 24 | Menu { 25 | id: menu 26 | popupType: App.nativePopups ? Popup.Native : Popup.Item 27 | MenuItem { 28 | text: qsTr("Remove") 29 | Layout.preferredWidth: 36 30 | Layout.preferredHeight: 36 31 | icon.width: App.iconSize 32 | icon.height: App.iconSize 33 | icon.source: App.faUrlBase + "minus" 34 | onClicked: page.removeObjects(rowData, 1) 35 | } 36 | MenuItem { 37 | text: qsTr("Insert before") 38 | Layout.preferredWidth: 36 39 | Layout.preferredHeight: 36 40 | icon.width: App.iconSize 41 | icon.height: App.iconSize 42 | icon.source: App.faUrlBase + "plus" 43 | onClicked: page.showNewValueDialog(rowData.index) 44 | } 45 | } 46 | } 47 | required property var page 48 | required property var rowData 49 | } 50 | -------------------------------------------------------------------------------- /tray/gui/qml/CloseDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | Dialog { 8 | id: closeDialog 9 | Material.primary: Material.LightBlue 10 | Material.accent: Material.LightBlue 11 | parent: Overlay.overlay 12 | anchors.centerIn: Overlay.overlay 13 | popupType: App.nativePopups ? Popup.Native : Popup.Item 14 | width: Math.min(popupType === Popup.Item ? parent.width - 20 : implicitWidth, 800) 15 | standardButtons: Dialog.NoButton 16 | modal: true 17 | title: meta.title 18 | contentItem: Label { 19 | Layout.fillWidth: true 20 | text: qsTr("Do you want to shutdown Syncthing and quit the app? You can also just quit the app and keep Syncthing running in the background.") 21 | wrapMode: Text.WordWrap 22 | } 23 | onAccepted: closeRequested() 24 | footer: DialogButtonBox { 25 | Button { 26 | text: qsTr("Cancel") 27 | flat: true 28 | DialogButtonBox.buttonRole: DialogButtonBox.RejectRole 29 | } 30 | Button { 31 | text: qsTr("Shutdown") 32 | flat: true 33 | DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole 34 | } 35 | Button { 36 | text: qsTr("Background") 37 | flat: true 38 | onClicked: { 39 | closeDialog.close(); 40 | App.minimize(); 41 | } 42 | DialogButtonBox.buttonRole: DialogButtonBox.InvalidRole 43 | } 44 | } 45 | required property Meta meta 46 | signal closeRequested 47 | } 48 | -------------------------------------------------------------------------------- /tray/gui/qml/CopyPasteButtons.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | RowLayout { 8 | IconOnlyButton { 9 | text: qsTr("Copy") 10 | icon.source: App.faUrlBase + "files-o" 11 | onClicked: App.copyText(edit[textProperty]) 12 | } 13 | IconOnlyButton { 14 | text: qsTr("Paste") 15 | enabled: edit.enabled 16 | icon.source: App.faUrlBase + "clipboard" 17 | onClicked: edit[textProperty] = App.getClipboardText() 18 | } 19 | required property Item edit 20 | property string textProperty: "text" 21 | } 22 | -------------------------------------------------------------------------------- /tray/gui/qml/CustomDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | import Main 5 | 6 | Dialog { 7 | parent: Overlay.overlay 8 | anchors.centerIn: Overlay.overlay 9 | popupType: App.nativePopups ? Popup.Native : Popup.Item 10 | width: Math.min(popupType === Popup.Item ? parent.width - 20 : implicitWidth, 800) 11 | standardButtons: Dialog.Yes | Dialog.No 12 | modal: true 13 | } 14 | -------------------------------------------------------------------------------- /tray/gui/qml/CustomFlickable.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | Flickable { 5 | id: flickable 6 | boundsMovement: Flickable.StopAtBounds 7 | boundsBehavior: Flickable.DragAndOvershootBounds 8 | transformOrigin: flickable.verticalOvershoot >= 0 ? Item.Top : Item.Bottom 9 | transform: Scale { 10 | origin.y: flickable.verticalOvershoot > 0 ? flickable.height : 0 11 | yScale: 1 + Math.log(Math.abs(flickable.verticalOvershoot) + 1) * 0.01 12 | } 13 | ScrollIndicator.vertical: ScrollIndicator { } 14 | } 15 | -------------------------------------------------------------------------------- /tray/gui/qml/CustomListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | ListView { 5 | id: listView 6 | activeFocusOnTab: true 7 | keyNavigationEnabled: true 8 | boundsMovement: Flickable.StopAtBounds 9 | boundsBehavior: Flickable.DragAndOvershootBounds 10 | transformOrigin: listView.verticalOvershoot >= 0 ? Item.Top : Item.Bottom 11 | transform: Scale { 12 | origin.y: listView.verticalOvershoot > 0 ? listView.height : 0 13 | yScale: 1 + Math.log(Math.abs(listView.verticalOvershoot) + 1) * 0.01 14 | } 15 | ScrollIndicator.vertical: ScrollIndicator { } 16 | } 17 | -------------------------------------------------------------------------------- /tray/gui/qml/CustomToolButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | import Main 5 | 6 | ToolButton { 7 | icon.width: App.iconSize 8 | icon.height: App.iconSize 9 | display: AbstractButton.IconOnly 10 | onPressAndHold: App.performHapticFeedback() 11 | ToolTip.text: text 12 | ToolTip.visible: hovered || pressed 13 | ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval 14 | } 15 | -------------------------------------------------------------------------------- /tray/gui/qml/DevDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | ExpandableDelegate { 8 | id: mainDelegateModel 9 | delegate: ExpandableItemDelegate { 10 | id: devDelegate 11 | mainView: mainDelegateModel.mainView 12 | actions: [ 13 | Action { 14 | text: modelData.paused ? qsTr("Resume") : qsTr("Pause") 15 | enabled: !modelData.isThisDevice 16 | icon.source: App.faUrlBase + (modelData.paused ? "play" : "pause") 17 | onTriggered: App.connection[modelData.paused ? "resumeDevice" : "pauseDevice"]([modelData.devId]) 18 | } 19 | ] 20 | extraActions: [ 21 | Action { 22 | text: qsTr("Edit") 23 | icon.source: App.faUrlBase + "pencil" 24 | onTriggered: mainView.stackView.push("DevConfigPage.qml", {devName: modelData.name, devId: modelData.devId, stackView: mainView.stackView}, StackView.PushTransition) 25 | }, 26 | Action { 27 | text: qsTr("Out of Sync items") 28 | icon.source: App.faUrlBase + "exchange" 29 | enabled: !modelData.paused && (modelData.neededItemsCount > 0) 30 | onTriggered: mainView.stackView.push("OutOfSyncDirs.qml", {devLabel: modelData.name, devId: modelData.devId, dirIndex: modelData.index, devFilterModel: mainDelegateModel.model, stackView: mainView.stackView}, StackView.PushTransition) 31 | }, 32 | Action { 33 | text: qsTr("Advanced config") 34 | icon.source: App.faUrlBase + "cogs" 35 | onTriggered: mainView.stackView.push("AdvancedDevConfigPage.qml", {devName: modelData.name, devId: modelData.devId, stackView: mainView.stackView}, StackView.PushTransition) 36 | } 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tray/gui/qml/DevListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | ExpandableListView { 6 | id: mainView 7 | model: DevDelegate { 8 | mainView: mainView 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tray/gui/qml/DevsPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | StackView { 8 | id: stackView 9 | Layout.fillWidth: true 10 | Layout.fillHeight: true 11 | initialItem: Page { 12 | id: page 13 | title: qsTr("Devices") 14 | Layout.fillWidth: true 15 | Layout.fillHeight: true 16 | DevListView { 17 | id: devsListView 18 | mainModel: App.sortFilterDevModel 19 | stackView: stackView 20 | } 21 | property list actions: [ 22 | Action { 23 | text: qsTr("Add device") 24 | icon.source: App.faUrlBase + "plus" 25 | onTriggered: page.add() 26 | } 27 | ] 28 | property list extraActions: [ 29 | Action { 30 | text: qsTr("Pause all") 31 | icon.source: App.faUrlBase + "pause" 32 | onTriggered: App.connection.pauseAllDevs() 33 | }, 34 | Action { 35 | text: qsTr("Resume all") 36 | icon.source: App.faUrlBase + "play" 37 | onTriggered: App.connection.resumeAllDevs() 38 | } 39 | ] 40 | property alias model: devsListView.mainModel 41 | function add(deviceId = "", deviceName = "") { 42 | stackView.push("DevConfigPage.qml", {devId: deviceId, devName: deviceName, stackView: stackView}, StackView.PushTransition); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tray/gui/qml/DirErrorsPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | Page { 8 | title: qsTr("Errors of folder \"%1\"").arg(dirName) 9 | Component.onCompleted: App.loadDirErrors(dirId, listView) 10 | ScrollView { 11 | anchors.fill: parent 12 | CustomListView { 13 | id: listView 14 | width: parent.width 15 | enabled: false 16 | delegate: ItemDelegate { 17 | width: listView.width 18 | contentItem: ColumnLayout { 19 | spacing: 0 20 | Label { 21 | Layout.fillWidth: true 22 | text: modelData.path 23 | wrapMode: Text.WrapAnywhere 24 | font.weight: Font.Medium 25 | } 26 | Label { 27 | Layout.fillWidth: true 28 | text: modelData.message 29 | wrapMode: Text.WrapAnywhere 30 | font.weight: Font.Light 31 | } 32 | } 33 | required property var modelData 34 | } 35 | } 36 | } 37 | BusyIndicator { 38 | anchors.centerIn: parent 39 | running: !listView.enabled 40 | } 41 | 42 | required property string dirName 43 | required property string dirId 44 | property list actions 45 | } 46 | -------------------------------------------------------------------------------- /tray/gui/qml/DirListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | ExpandableListView { 6 | id: mainView 7 | model: DirDelegate { 8 | mainView: mainView 9 | } 10 | CustomDialog { 11 | id: confirmDirActionDlg 12 | title: isOverrideAction ? qsTr("Override changes on remote devices") : qsTr("Revert local changes") 13 | standardButtons: Dialog.Ok | Dialog.Cancel 14 | Material.primary: Material.Red 15 | Material.accent: Material.primary 16 | contentItem: Label { 17 | text: confirmDirActionDlg.isOverrideAction ? qsTr("Do you really want to override changes on remote devices within folder \"%1\"? This will mark the local version as the latest version causing changes on all remote devices to be overridden with the version from this device.").arg(confirmDirActionDlg.dirLabel) 18 | : qsTr("Do you really want to revert the local changes on this device within folder \"%1\"? This will undo all local changes on this device.").arg(confirmDirActionDlg.dirLabel) 19 | elide: Label.ElideRight 20 | wrapMode: Text.WordWrap 21 | } 22 | onAccepted: App.invokeDirAction(confirmDirActionDlg.dirId, confirmDirActionDlg.action) 23 | property string dirId 24 | property string dirLabel 25 | property string action 26 | readonly property bool isOverrideAction: action === "override" 27 | } 28 | function confirmDirAction(dirId, dirLabel, action) { 29 | confirmDirActionDlg.dirId = dirId; 30 | confirmDirActionDlg.dirLabel = dirLabel; 31 | confirmDirActionDlg.action = action; 32 | confirmDirActionDlg.open(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tray/gui/qml/DirsPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | StackView { 8 | id: stackView 9 | Layout.fillWidth: true 10 | Layout.fillHeight: true 11 | initialItem: Page { 12 | id: page 13 | title: qsTr("Folders") 14 | Layout.fillWidth: true 15 | Layout.fillHeight: true 16 | DirListView { 17 | id: dirsListView 18 | mainModel: App.sortFilterDirModel 19 | stackView: stackView 20 | } 21 | property list actions: [ 22 | Action { 23 | text: qsTr("Add folder") 24 | icon.source: App.faUrlBase + "plus" 25 | onTriggered: page.add() 26 | } 27 | ] 28 | property list extraActions: [ 29 | Action { 30 | text: qsTr("Pause all") 31 | icon.source: App.faUrlBase + "pause" 32 | onTriggered: App.connection.pauseAllDirs() 33 | }, 34 | Action { 35 | text: qsTr("Resume all") 36 | icon.source: App.faUrlBase + "play" 37 | onTriggered: App.connection.resumeAllDirs() 38 | }, 39 | Action { 40 | text: qsTr("Rescan all") 41 | icon.source: App.faUrlBase + "refresh" 42 | onTriggered: App.connection.rescanAllDirs() 43 | } 44 | ] 45 | property alias model: dirsListView.mainModel 46 | function add(dirId = "", dirName = "", shareWithDeviceIds = []) { 47 | stackView.push("DirConfigPage.qml", {dirId: dirId, dirName: dirName, shareWithDeviceIds: shareWithDeviceIds ?? [], stackView: stackView}, StackView.PushTransition); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tray/gui/qml/DiscardChangesDialog.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | CustomDialog { 8 | id: discardChangesDialog 9 | Material.primary: Material.LightBlue 10 | Material.accent: Material.LightBlue 11 | title: meta.title 12 | contentItem: Label { 13 | Layout.fillWidth: true 14 | text: qsTr("Do you really want to go back without applying changes?") 15 | wrapMode: Text.WordWrap 16 | } 17 | onAccepted: pageStack.pop(true) 18 | required property Meta meta 19 | required property PageStack pageStack 20 | } 21 | -------------------------------------------------------------------------------- /tray/gui/qml/ErrorsDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | ItemDelegate { 8 | width: listView.width 9 | onPressAndHold: App.copyText(`${modelData.when}\n${modelData.url}: ${modelData.message}`) 10 | contentItem: GridLayout { 11 | columns: 2 12 | columnSpacing: 10 13 | ForkAwesomeIcon { 14 | iconName: "calendar" 15 | } 16 | Label { 17 | Layout.fillWidth: true 18 | text: modelData.when 19 | elide: Text.ElideRight 20 | font.weight: Font.Light 21 | } 22 | ForkAwesomeIcon { 23 | iconName: "exclamation-triangle" 24 | } 25 | Label { 26 | Layout.fillWidth: true 27 | text: modelData.message 28 | wrapMode: Text.WordWrap 29 | font.weight: Font.Light 30 | } 31 | } 32 | required property var modelData 33 | } 34 | -------------------------------------------------------------------------------- /tray/gui/qml/ErrorsPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | Page { 8 | title: qsTr("Notifications/errors") 9 | Component.onCompleted: App.loadErrors(listView) 10 | actions: [ 11 | Action { 12 | text: qsTr("Clear") 13 | icon.source: App.faUrlBase + "trash" 14 | onTriggered: App.connection.requestClearingErrors() 15 | } 16 | ] 17 | CustomListView { 18 | id: listView 19 | anchors.fill: parent 20 | delegate: ErrorsDelegate {} 21 | } 22 | 23 | required property list actions 24 | } 25 | -------------------------------------------------------------------------------- /tray/gui/qml/ExpandableDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | DelegateModel { 5 | id: mainDelegateModel 6 | model: mainView.mainModel 7 | delegate: ExpandableItemDelegate { 8 | mainView: mainDelegateModel.mainView 9 | } 10 | 11 | required property ListView mainView 12 | property StackView stackView 13 | } 14 | -------------------------------------------------------------------------------- /tray/gui/qml/ExpandableListView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | CustomListView { 5 | id: mainView 6 | anchors.fill: parent 7 | model: ExpandableDelegate { 8 | mainView: mainView 9 | } 10 | required property QtObject mainModel 11 | property StackView stackView 12 | } 13 | -------------------------------------------------------------------------------- /tray/gui/qml/ForkAwesomeIcon.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | 4 | import Main 5 | 6 | Image { 7 | Layout.preferredWidth: size 8 | Layout.preferredHeight: size 9 | Layout.maximumWidth: size 10 | source: `${App.faUrlBase}${iconName}::${App.darkmodeEnabled}` 11 | width: size 12 | height: size 13 | property int size: 16 14 | required property string iconName 15 | } 16 | -------------------------------------------------------------------------------- /tray/gui/qml/HelpButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | import QtQuick.Layouts 4 | 5 | import Main 6 | 7 | IconOnlyButton { 8 | id: helpButton 9 | visible: modelData.helpUrl?.length > 0 || configCategory.length > 0 10 | text: qsTr("Open help") 11 | icon.source: App.faUrlBase + "question" 12 | onClicked: helpButton.desc.length > 0 ? helpDlg.open() : helpButton.openSyncthingDocs() 13 | 14 | CustomDialog { 15 | id: helpDlg 16 | title: modelData.label ?? helpButton.key 17 | standardButtons: Dialog.NoButton 18 | contentItem: Label { 19 | text: helpButton.desc 20 | wrapMode: Text.WordWrap 21 | } 22 | footer: DialogButtonBox { 23 | Button { 24 | text: qsTr("Close") 25 | flat: true 26 | DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole 27 | } 28 | Button { 29 | text: qsTr("Details") 30 | flat: true 31 | DialogButtonBox.buttonRole: DialogButtonBox.HelpRole 32 | } 33 | } 34 | onHelpRequested: helpButton.openSyncthingDocs() 35 | } 36 | 37 | property string key: modelData.key 38 | property string desc: modelData.desc 39 | property string url: modelData.helpUrl ?? `https://docs.syncthing.net/users/config#${helpButton.configCategory}.${helpButton.key.toLowerCase()}` 40 | property string configCategory 41 | 42 | function openSyncthingDocs() { 43 | Qt.openUrlExternally(helpButton.url); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tray/gui/qml/IconOnlyButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | RoundButton { 6 | Layout.preferredWidth: 36 7 | Layout.preferredHeight: 36 8 | display: AbstractButton.IconOnly 9 | hoverEnabled: true 10 | ToolTip.text: text 11 | ToolTip.visible: pressed 12 | ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval 13 | icon.width: 20 14 | icon.height: 20 15 | } 16 | -------------------------------------------------------------------------------- /tray/gui/qml/IgnorePatternPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | Page { 8 | id: page 9 | title: qsTr("Ignore patterns of \"%1\"").arg(dirName) 10 | Component.onCompleted: App.loadIgnorePatterns(dirId, textArea) 11 | actions: [ 12 | Action { 13 | text: qsTr("Edit externally") 14 | icon.source: App.faUrlBase + "external-link" 15 | enabled: App.connection.isLocal 16 | onTriggered: App.openIgnorePatterns(page.dirId) 17 | }, 18 | Action { 19 | text: qsTr("Save") 20 | icon.source: App.faUrlBase + "floppy-o" 21 | onTriggered: App.saveIgnorePatterns(page.dirId, textArea) 22 | } 23 | ] 24 | ScrollView { 25 | anchors.fill: parent 26 | TextArea { 27 | id: textArea 28 | width: parent.width 29 | enabled: false 30 | } 31 | } 32 | BusyIndicator { 33 | anchors.centerIn: parent 34 | running: !textArea.enabled 35 | } 36 | 37 | required property string dirName 38 | required property string dirId 39 | required property list actions 40 | } 41 | -------------------------------------------------------------------------------- /tray/gui/qml/InternalErrorsPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | Page { 8 | title: qsTr("Log of Syncthing API errors") 9 | Layout.fillWidth: true 10 | Layout.fillHeight: true 11 | actions: [ 12 | Action { 13 | text: qsTr("Clear") 14 | icon.source: App.faUrlBase + "trash" 15 | onTriggered: { 16 | App.clearInternalErrors(); 17 | listView.model = App.internalErrors(); 18 | } 19 | } 20 | ] 21 | CustomListView { 22 | id: listView 23 | anchors.fill: parent 24 | model: App.internalErrors() 25 | delegate: ErrorsDelegate {} 26 | } 27 | Connections { 28 | target: App 29 | function onInternalError(error) { 30 | listView.model.push(error) 31 | } 32 | } 33 | property list actions 34 | } 35 | -------------------------------------------------------------------------------- /tray/gui/qml/LoadingPane.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | Pane { 8 | height: visible ? implicitHeight : 0 9 | contentItem: RowLayout { 10 | Item { 11 | Layout.fillWidth: true 12 | } 13 | BusyIndicator { 14 | Layout.preferredWidth: App.iconSize * 2 15 | Layout.preferredHeight: Layout.preferredWidth 16 | } 17 | Label { 18 | text: qsTr("Loading …") 19 | elide: Text.ElideRight 20 | font.weight: Font.Light 21 | } 22 | Item { 23 | Layout.fillWidth: true 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tray/gui/qml/LogPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | import Main 5 | 6 | Page { 7 | title: qsTr("Syncthing log") 8 | Component.onCompleted: App.showLog(textArea) 9 | ScrollView { 10 | anchors.fill: parent 11 | TextArea { 12 | id: textArea 13 | width: parent.width 14 | readOnly: true 15 | wrapMode: TextEdit.WrapAnywhere 16 | } 17 | } 18 | property list actions: [ 19 | Action { 20 | text: qsTr("Copy") 21 | icon.source: App.faUrlBase + "files-o" 22 | onTriggered: App.copyText(textArea.text) 23 | }, 24 | Action { 25 | text: qsTr("Clear") 26 | icon.source: App.faUrlBase + "undo" 27 | onTriggered: { 28 | App.clearLog(); 29 | textArea.clear(); 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /tray/gui/qml/MainTabBar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | import Qt.labs.qmlmodels 5 | 6 | import Main 7 | 8 | TabBar { 9 | visible: drawer.interactive 10 | currentIndex: Math.min(pageStack.currentIndex, 4) 11 | MainTabButton { 12 | text: qsTr("Start") 13 | iconName: "home" 14 | tabIndex: 0 15 | } 16 | MainTabButton { 17 | text: qsTr("Folders") 18 | iconName: "folder" 19 | tabIndex: 1 20 | } 21 | MainTabButton { 22 | text: qsTr("Devices") 23 | iconName: "sitemap" 24 | tabIndex: 2 25 | } 26 | MainTabButton { 27 | text: qsTr("Recent changes") 28 | iconName: "history" 29 | tabIndex: 3 30 | } 31 | MainTabButton { 32 | text: qsTr("More") 33 | iconName: "cog" 34 | tabIndex: 5 35 | } 36 | required property LeftDrawer drawer 37 | required property PageStack pageStack 38 | } 39 | -------------------------------------------------------------------------------- /tray/gui/qml/MainTabButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | import Main 5 | 6 | TabButton { 7 | display: parent.width > 400 ? AbstractButton.TextUnderIcon : AbstractButton.IconOnly 8 | font.pointSize: 7 9 | icon.source: App.faUrlBase + iconName 10 | icon.width: App.iconSize 11 | icon.height: App.iconSize 12 | onClicked: pageStack.setCurrentIndex(tabIndex) 13 | onPressAndHold: App.performHapticFeedback() 14 | ToolTip.visible: hovered || pressed 15 | ToolTip.text: text 16 | ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval 17 | required property string iconName 18 | required property int tabIndex 19 | } 20 | -------------------------------------------------------------------------------- /tray/gui/qml/MenuItemInstantiator.qml: -------------------------------------------------------------------------------- 1 | import QtQml 2 | import QtQuick 3 | import QtQuick.Controls.Material 4 | 5 | Instantiator { 6 | onObjectAdded: (index, object) => menu.insertItem(index, object) 7 | onObjectRemoved: (index, object) => menu.removeItem(object) 8 | delegate: MenuItem { 9 | required property Action modelData 10 | text: modelData.text 11 | enabled: modelData.enabled 12 | visible: enabled 13 | height: visible ? implicitHeight : 0 14 | icon.source: modelData.icon.source 15 | icon.width: App.iconSize 16 | icon.height: App.iconSize 17 | onTriggered: modelData?.trigger() 18 | } 19 | required property Menu menu 20 | } 21 | -------------------------------------------------------------------------------- /tray/gui/qml/Meta.qml: -------------------------------------------------------------------------------- 1 | import QtQml 2 | 3 | QtObject { 4 | readonly property string title: qsTr("Syncthing") 5 | } 6 | -------------------------------------------------------------------------------- /tray/gui/qml/Notifications.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | 3 | import Main 4 | 5 | QtObject { 6 | readonly property Connections appConnections: Connections { 7 | target: App 8 | function onError(message) { 9 | showNotifiction(message); 10 | } 11 | function onInfo(message) { 12 | showNotifiction(message); 13 | } 14 | function onInternalError(error) { 15 | showNotifiction(error.message); 16 | } 17 | function onTextShared(text) { 18 | if (text.match(/^[0-9A-z]{7}(-[0-9A-z]{7}){7}$/)) { 19 | pageStack.addDevice(text); 20 | } else { 21 | showNotifiction(qsTr("Not a valid device ID.")); 22 | } 23 | } 24 | function onNewDeviceTriggered(devId) { 25 | pageStack.addDevice(devId); 26 | } 27 | function onNewDirTriggered(devId, dirId, dirLabel) { 28 | pageStack.addDir(dirId, dirLabel, [devId]); 29 | } 30 | } 31 | readonly property Connections connectionConnections: Connections { 32 | target: App.connection 33 | function onNewConfigTriggered() { 34 | showNotifiction(qsTr("Configuration changed")); 35 | } 36 | } 37 | readonly property Connections notifierConnections: Connections { 38 | target: App.notifier 39 | function onDisconnected() { 40 | showNotifiction(qsTr("UI disconnected from Syncthing backend")); 41 | } 42 | } 43 | 44 | signal notification 45 | function showNotifiction(message) { 46 | return App.showToast(message) || notification(message); 47 | } 48 | 49 | required property PageStack pageStack 50 | } 51 | -------------------------------------------------------------------------------- /tray/gui/qml/OutOfSyncDirs.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | import Main 6 | 7 | Page { 8 | id: page 9 | title: qsTr("Out of Sync folders on %1").arg(devLabel) 10 | Component.onCompleted: page.loadDirs() 11 | actions: [ 12 | Action { 13 | text: qsTr("Refresh") 14 | icon.source: App.faUrlBase + "refresh" 15 | onTriggered: page.loadDirs() 16 | } 17 | ] 18 | contentItem: CustomListView { 19 | id: outOfSyncDirsListView 20 | delegate: ItemDelegate { 21 | width: outOfSyncDirsListView.width 22 | text: qsTr("%1: %2 items needed, ~ %3").arg(modelData.dirName).arg(modelData.items).arg(App.formatDataSize(modelData.bytes)) 23 | icon.source: App.faUrlBase + "folder-o" 24 | onClicked: page.stackView.push("NeededPage.qml", {devLabel: page.devLabel, devId: page.devId, dirLabel: modelData.dirName, dirId: modelData.dirId}, StackView.PushTransition) 25 | required property var modelData 26 | } 27 | } 28 | required property string devId 29 | required property string devLabel 30 | required property int dirIndex 31 | required property var devFilterModel 32 | required property var stackView 33 | required property list actions 34 | function loadDirs() { 35 | outOfSyncDirsListView.model = App.computeDirsNeedingItems(devFilterModel.index(dirIndex, 0)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tray/gui/qml/Statistics.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls.Material 4 | 5 | ItemDelegate { 6 | id: itemDelegate 7 | Layout.fillWidth: true 8 | ToolTip.visible: pressed 9 | ToolTip.text: qsTr("%1 files, %2 dirs, ~ %3").arg(stats.files).arg(stats.dirs).arg(stats.bytesAsString) 10 | ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval 11 | hoverEnabled: true 12 | contentItem: RowLayout { 13 | spacing: 15 14 | ForkAwesomeIcon { 15 | id: icon 16 | Layout.alignment: Qt.AlignHCenter | Qt.AlignTop 17 | } 18 | ColumnLayout { 19 | Layout.fillWidth: true 20 | Label { 21 | id: label 22 | Layout.fillWidth: true 23 | font.weight: Font.Medium 24 | elide: Text.ElideRight 25 | } 26 | GridLayout { 27 | Layout.fillWidth: true 28 | columns: itemDelegate.width > 300 ? 6 : 2 29 | ForkAwesomeIcon { 30 | iconName: "file-o" 31 | } 32 | Label { 33 | text: stats.files 34 | font.weight: Font.Light 35 | } 36 | ForkAwesomeIcon { 37 | iconName: "folder-o" 38 | } 39 | Label { 40 | text: stats.dirs 41 | font.weight: Font.Light 42 | } 43 | ForkAwesomeIcon { 44 | iconName: "hdd-o" 45 | } 46 | Label { 47 | text: stats.bytesAsString 48 | font.weight: Font.Light 49 | } 50 | } 51 | } 52 | } 53 | required property var stats 54 | property alias iconName: icon.iconName 55 | property alias labelText: label.text 56 | } 57 | -------------------------------------------------------------------------------- /tray/gui/qml/StatisticsPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls.Material 3 | 4 | import Main 5 | 6 | ObjectConfigPage { 7 | id: page 8 | title: qsTr("Statistics") 9 | isLoading: true 10 | Component.onCompleted: page.loadStatistics() 11 | actions: [ 12 | Action { 13 | text: qsTr("Refresh") 14 | icon.source: App.faUrlBase + "refresh" 15 | onTriggered: page.loadStatistics() 16 | } 17 | ] 18 | readOnly: true 19 | specialEntries: [ 20 | {key: "platform", label: qsTr("Platform")}, 21 | {key: "longVersion", label: qsTr("Syncthing version")}, 22 | {key: "memoryUsageMiB", label: qsTr("Memory usage in MiB")}, 23 | {key: "natType", label: qsTr("NAT type")}, 24 | {key: "stConfigDir", label: qsTr("Syncthing config directory")}, 25 | {key: "stDataDir", label: qsTr("Syncthing data directory")}, 26 | {key: "stDbSize", label: qsTr("Syncthing database size")}, 27 | {key: "extFilesDir", label: qsTr("External files directory")}, 28 | {key: "extStoragePaths", label: qsTr("External storage paths")}, 29 | ] 30 | function loadStatistics() { 31 | page.isLoading = true; 32 | App.loadStatistics((res, error) => { 33 | // delete unwanted or empty statistics 34 | delete res.version; 35 | Object.entries(res).forEach((entry) => { 36 | const value = entry[1]; 37 | const type = typeof value; 38 | if (type === "array" || (type !== "object" && value.toString() === "")) { 39 | delete res[entry[0]]; 40 | } 41 | }); 42 | 43 | page.isLoading = false; 44 | page.configObject = res; 45 | page.model.loadEntries(); 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tray/gui/qml/Theming.qml: -------------------------------------------------------------------------------- 1 | import QtQml 2 | import QtQuick.Controls.Material 3 | 4 | import Main 5 | 6 | QtObject { 7 | Material.theme: App.darkmodeEnabled ? Material.Dark : Material.Light 8 | Material.primary: pageStack.currentPage.isDangerous ? Material.Red : Material.LightBlue 9 | Material.accent: Material.primary 10 | Material.onForegroundChanged: App.setPalette(Material.foreground, Material.background) 11 | 12 | // propagate palette of Qt Quick Controls 2 style to regular QPalette of QGuiApplication for icon rendering 13 | Component.onCompleted: App.setPalette(Material.foreground, Material.background) 14 | 15 | readonly property var font: App.font 16 | required property PageStack pageStack 17 | } 18 | -------------------------------------------------------------------------------- /tray/gui/qml/WebViewPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls 4 | 5 | import Main 6 | import WebViewItem 7 | 8 | Page { 9 | id: webViewPage 10 | title: qsTr("Web-based UI") 11 | Layout.fillWidth: true 12 | Layout.fillHeight: true 13 | WebViewItem { 14 | id: webViewItem 15 | anchors.fill: parent 16 | url: webViewPage.active ? App.connection.syncthingUrlWithCredentials : "about:blank" 17 | } 18 | property alias actions: webViewItem.actions 19 | property bool active: false 20 | } 21 | -------------------------------------------------------------------------------- /tray/gui/qml/webview-none/WebViewItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls 3 | 4 | import Main 5 | 6 | Label { 7 | id: webViewItem 8 | anchors.fill: parent 9 | text: qsTr("The app has not been built with web view support so this page is not available.") 10 | horizontalAlignment: Text.AlignHCenter 11 | verticalAlignment: Text.AlignVCenter 12 | wrapMode: Text.WordWrap 13 | property url url 14 | property list actions: [ 15 | Action { 16 | text: qsTr("Open in web browser") 17 | icon.source: App.faUrlBase + "external-link" 18 | onTriggered: Qt.openUrlExternally(webViewItem.url) 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tray/gui/qml/webview-webview/WebViewItem.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Layouts 3 | import QtQuick.Controls 4 | import QtWebView 5 | 6 | import Main 7 | 8 | ColumnLayout { 9 | id: webViewItem 10 | WebView { 11 | id: webView 12 | Layout.fillWidth: true 13 | Layout.fillHeight: true 14 | onLoadingChanged: (request) => { 15 | errorLabel.visible = request.errorString.length > 0; 16 | errorLabel.text = request.errorString; 17 | } 18 | BusyIndicator { 19 | anchors.centerIn: parent 20 | running: webView.loading 21 | } 22 | Label { 23 | id: errorLabel 24 | anchors.centerIn: parent 25 | visible: false 26 | } 27 | } 28 | property alias url: webView.url 29 | property list actions: [ 30 | Action { 31 | text: qsTr("Refresh") 32 | icon.source: App.faUrlBase + "refresh" 33 | onTriggered: webView.reload() 34 | }, 35 | Action { 36 | text: qsTr("Open in web browser") 37 | icon.source: App.faUrlBase + "external-link" 38 | onTriggered: Qt.openUrlExternally(webViewItem.url) 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /tray/gui/quick/scenegraph/managedtexturenode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011 Marco Martin 3 | * SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez 4 | * SPDX-FileCopyrightText: 2020 Carson Black 5 | * SPDX-FileCopyrightText: 2024 Martchus 6 | * 7 | * SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | 10 | #pragma once 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace QtGui { 18 | 19 | class ManagedTextureNode : public QSGSimpleTextureNode { 20 | Q_DISABLE_COPY(ManagedTextureNode) 21 | public: 22 | ManagedTextureNode(); 23 | 24 | void setTexture(std::shared_ptr texture); 25 | 26 | private: 27 | std::shared_ptr m_texture; 28 | }; 29 | 30 | typedef QHash>> TexturesCache; 31 | 32 | struct ImageTexturesCachePrivate { 33 | TexturesCache cache; 34 | }; 35 | 36 | class ImageTexturesCache { 37 | public: 38 | ImageTexturesCache(); 39 | ~ImageTexturesCache(); 40 | 41 | /** 42 | * @returns the texture for a given @p window and @p image. 43 | * 44 | * If an @p image id is the same as one already provided before, we won't create 45 | * a new texture and return a shared pointer to the existing texture. 46 | */ 47 | std::shared_ptr loadTexture(QQuickWindow *window, const QImage &image, QQuickWindow::CreateTextureOptions options); 48 | 49 | std::shared_ptr loadTexture(QQuickWindow *window, const QImage &image); 50 | 51 | private: 52 | std::unique_ptr d; 53 | }; 54 | 55 | } // namespace QtGui 56 | -------------------------------------------------------------------------------- /tray/resources/Info.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${MACOSX_BUNDLE_EXECUTABLE_NAME} 9 | CFBundleGetInfoString 10 | ${MACOSX_BUNDLE_INFO_STRING} 11 | CFBundleIconFile 12 | ${MACOSX_BUNDLE_ICON_FILE} 13 | CFBundleIdentifier 14 | ${MACOSX_BUNDLE_GUI_IDENTIFIER} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleLongVersionString 18 | ${MACOSX_BUNDLE_LONG_VERSION_STRING} 19 | CFBundleName 20 | ${MACOSX_BUNDLE_BUNDLE_NAME} 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | ${MACOSX_BUNDLE_SHORT_VERSION_STRING} 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | ${MACOSX_BUNDLE_BUNDLE_VERSION} 29 | CSResourcesFileMapped 30 | 31 | NSHumanReadableCopyright 32 | ${MACOSX_BUNDLE_COPYRIGHT} 33 | LSUIElement 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tray/resources/android-svg/ic_launcher_background.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tray/resources/android-svg/ic_launcher_foreground.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tray/resources/body.appdata.xml: -------------------------------------------------------------------------------- 1 | 2 |

3 | Syncthing Tray complements the normal web-based UI of Syncthing itself providing the following features: 4 |

5 |
    6 |
  • A tray icon showing the overall status of Syncthing
  • 7 |
  • A tray menu showing more detailed status information that also allows to quickly perform common actions such as triggering a rescan
  • 8 |
  • Notifications for certain events
  • 9 |
  • Quick access to the normal web-based UI of Syncthing
  • 10 |
  • Integration with systemd and launcher for Syncthing
  • 11 |
12 |

13 | Note that this is the desktop environment independent version. The Plasmoid/KDE-integrations might be installed via a different 14 | distribution-specific package (but cannot be provided as Flatpak for technical reasons). 15 |

16 |
17 | 18 | 19 | @META_APP_NAME@ under Openbox with Tint2 panel 20 | @META_APP_URL_RAW@/tray/resources/screenshots/tint2-dark.png 21 | 22 | 23 | Web view, dark theme 24 | @META_APP_URL_RAW@/tray/resources/screenshots/webview-dark.png 25 | 26 | 27 | Settings dialog 28 | @META_APP_URL_RAW@/tray/resources/screenshots/settings.png 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tray/resources/dark-palette.ini: -------------------------------------------------------------------------------- 1 | [General] 2 | palette=@Variant(\0\0\0\x44\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xffTTTTTT\0\0\x1\x1\xff\xff\x46\x46\x46\x46\x46\x46\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0\x1\x1\xff\xff%z%z%z\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff!!!!!!\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xff\0\0\0\0\0\0\0\0\x1\x1\xff\xff\0\0xx\xd7\xd7\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\0\0\0\0\xff\xff\0\0\x1\x1\xff\xff\xff\xff\0\0\xff\xff\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xffTTTTTT\0\0\x1\x1\xff\xff\x46\x46\x46\x46\x46\x46\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0\x1\x1\xff\xff%z%z%z\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xff\0\0\0\0\0\0\0\0\x1\x1\xff\xff\0\0xx\xd7\xd7\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\0\0\0\0\xff\xff\0\0\x1\x1\xff\xff\xff\xff\0\0\xff\xff\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xffTTTTTT\0\0\x1\x1\xff\xff\x46\x46\x46\x46\x46\x46\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0\x1\x1\xff\xff%z%z%z\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff\xff\xff\xff\xff\xff\xff\0\0\x1\x1\xff\xff!!!!!!\0\0\x1\x1\xff\xff\x38\x38\x38\x38\x38\x38\0\0\x1\x1\xff\xff\0\0\0\0\0\0\0\0\x1\x1\xff\xff\xf0\xf0\xf0\xf0\xf0\xf0\0\0\x1\x1\xff\xff\0\0\0\0\0\0\0\0\x1\x1\xff\xff\0\0\0\0\xff\xff\0\0\x1\x1\xff\xff\xff\xff\0\0\xff\xff\0\0\x1\x1\xff\xff\x1c\x1c\x1c\x1c\x1c\x1c\0\0) 3 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/256x256/apps/syncthingtray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/icons/hicolor/256x256/apps/syncthingtray.png -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/application-menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/appointment-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/edit-copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/edit-delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/folder-sync.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/help-about.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/media-playback-pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/media-playback-start.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/network-connect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/window-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/actions/window-pin.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/apps/syncthingtray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/emblems/8/emblem-error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/emblems/8/emblem-warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/places/folder-download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/places/folder-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/places/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/icons/hicolor/scalable/places/network-workgroup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /tray/resources/screenshots/custom-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/custom-icons.png -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/dark-advanced.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/dark-advanced.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/dark-devices.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/dark-devices.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/dark-file-tree-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/dark-file-tree-1.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/dark-file-tree-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/dark-file-tree-2.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/dark-filtering.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/dark-filtering.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/folder-config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/folder-config.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/folders.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/folders.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/import.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/import.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/run-config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/run-config.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/mobile/startpage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/mobile/startpage.jpg -------------------------------------------------------------------------------- /tray/resources/screenshots/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/settings.png -------------------------------------------------------------------------------- /tray/resources/screenshots/tint2-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/tint2-dark.png -------------------------------------------------------------------------------- /tray/resources/screenshots/webview-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/webview-dark.png -------------------------------------------------------------------------------- /tray/resources/screenshots/webview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/webview.png -------------------------------------------------------------------------------- /tray/resources/screenshots/windows-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Martchus/syncthingtray/7fc29c413a1f345393e5170bbdb75a59aa65c92e/tray/resources/screenshots/windows-11.png -------------------------------------------------------------------------------- /tray/resources/syncthingtrayicons.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/hicolor/scalable/actions/application-menu.svg 4 | icons/hicolor/scalable/actions/edit-copy.svg 5 | icons/hicolor/scalable/actions/folder-sync.svg 6 | icons/hicolor/scalable/actions/help-about.svg 7 | icons/hicolor/scalable/actions/media-playback-pause.svg 8 | icons/hicolor/scalable/actions/media-playback-start.svg 9 | icons/hicolor/scalable/actions/window-close.svg 10 | icons/hicolor/scalable/apps/help-about.svg 11 | icons/hicolor/scalable/places/folder-open.svg 12 | icons/hicolor/scalable/places/folder.svg 13 | icons/hicolor/scalable/places/network-workgroup.svg 14 | icons/hicolor/scalable/actions/network-connect.svg 15 | icons/hicolor/scalable/places/folder-download.svg 16 | icons/hicolor/scalable/emblems/8/emblem-error.svg 17 | icons/hicolor/scalable/emblems/8/emblem-warning.svg 18 | icons/hicolor/scalable/actions/appointment-new.svg 19 | icons/hicolor/scalable/actions/download.svg 20 | icons/hicolor/scalable/actions/window-pin.svg 21 | icons/hicolor/scalable/actions/edit-delete.svg 22 | 23 | 24 | -------------------------------------------------------------------------------- /tray/testfiles/syncthing-testinstance.service: -------------------------------------------------------------------------------- 1 | # Unit file for testing systemd integration with a test instance of Syncthing 2 | # 1. Copy to ~/.config/systemd/user/syncthing-testinstance.service for using it. 3 | # 2. Set HOME=/tmp/syncthingtest when launching Syncthing Tray itself to be in 4 | # accordance with this unit file. 5 | # 3. Set SYNCTHINGTRAY_SYSTEMD_USER_UNIT=syncthing-testinstance.service so the 6 | # wizard will make use of this unit instead of the normal one. 7 | 8 | [Unit] 9 | Description=Test instance of Syncthing with HOME under /tmp/syncthingtest 10 | Documentation=man:syncthing(1) 11 | StartLimitIntervalSec=60 12 | StartLimitBurst=4 13 | 14 | [Service] 15 | ExecStart=/usr/bin/syncthing serve --no-browser --no-restart --logflags=0 16 | Restart=on-failure 17 | RestartSec=1 18 | SuccessExitStatus=3 4 19 | RestartForceExitStatus=3 4 20 | Environment=HOME=/tmp/syncthingtest 21 | 22 | # Hardening 23 | SystemCallArchitectures=native 24 | MemoryDenyWriteExecute=true 25 | NoNewPrivileges=true 26 | 27 | # Elevated permissions to sync ownership (disabled by default), 28 | # see https://docs.syncthing.net/advanced/folder-sync-ownership 29 | #AmbientCapabilities=CAP_CHOWN CAP_FOWNER 30 | 31 | [Install] 32 | WantedBy=default.target 33 | --------------------------------------------------------------------------------