Initial import
|
@ -0,0 +1,44 @@
|
|||
# C++ objects and libs
|
||||
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.a
|
||||
*.la
|
||||
*.lai
|
||||
*.so
|
||||
*.dll
|
||||
*.dylib
|
||||
|
||||
# Qt-es
|
||||
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.pro.user
|
||||
*.txt.user
|
||||
*.pro.user.*
|
||||
*.qbs.user
|
||||
*.qbs.user.*
|
||||
*.moc
|
||||
moc_*.cpp
|
||||
qrc_*.cpp
|
||||
ui_*.h
|
||||
Makefile*
|
||||
*-build-*
|
||||
|
||||
# QtCreator
|
||||
|
||||
*.autosave
|
||||
|
||||
#QtCtreator Qml
|
||||
*.qmlproject.user
|
||||
*.qmlproject.user.*
|
||||
|
||||
# Dolphin
|
||||
.directory
|
||||
|
||||
# documentation
|
||||
/doc
|
||||
|
||||
# tests
|
||||
testfiles/output.*
|
|
@ -0,0 +1,102 @@
|
|||
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
|
||||
|
||||
# metadata
|
||||
set(META_PROJECT_NAME syncthingtray)
|
||||
set(META_PROJECT_TYPE application)
|
||||
set(META_APP_NAME "Syncthing Tray")
|
||||
set(META_APP_AUTHOR "Martchus")
|
||||
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
||||
set(META_APP_DESCRIPTION "Tray application for Syncthing")
|
||||
set(META_APP_CATEGORIES "Utility;Network;")
|
||||
set(META_GUI_OPTIONAL false)
|
||||
set(META_VERSION_MAJOR 0)
|
||||
set(META_VERSION_MINOR 0)
|
||||
set(META_VERSION_PATCH 1)
|
||||
set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH})
|
||||
|
||||
# add project files
|
||||
set(HEADER_FILES
|
||||
data/syncthingconnection.h
|
||||
data/syncthingdirectorymodel.h
|
||||
data/syncthingdevicemodel.h
|
||||
)
|
||||
set(SRC_FILES
|
||||
data/syncthingconnection.cpp
|
||||
data/syncthingdirectorymodel.cpp
|
||||
data/syncthingdevicemodel.cpp
|
||||
)
|
||||
|
||||
set(WIDGETS_HEADER_FILES
|
||||
application/settings.h
|
||||
gui/tray.h
|
||||
gui/settingsdialog.h
|
||||
gui/webpage.h
|
||||
gui/webviewdialog.h
|
||||
gui/webviewprovider.h
|
||||
gui/dirbuttonsitemdelegate.h
|
||||
gui/dirview.h
|
||||
gui/devview.h
|
||||
)
|
||||
set(WIDGETS_SRC_FILES
|
||||
application/main.cpp
|
||||
application/settings.cpp
|
||||
gui/tray.cpp
|
||||
gui/settingsdialog.cpp
|
||||
gui/webpage.cpp
|
||||
gui/webviewdialog.cpp
|
||||
gui/dirbuttonsitemdelegate.cpp
|
||||
gui/dirview.cpp
|
||||
gui/devview.cpp
|
||||
resources/icons.qrc
|
||||
)
|
||||
set(WIDGETS_UI_FILES
|
||||
gui/traywidget.ui
|
||||
gui/connectionoptionpage.ui
|
||||
gui/notificationsoptionpage.ui
|
||||
gui/launcheroptionpage.ui
|
||||
gui/webviewoptionpage.ui
|
||||
)
|
||||
|
||||
#set(QUICK_HEADER_FILES
|
||||
#)
|
||||
#set(QUICK_SRC_FILES
|
||||
#)
|
||||
|
||||
set(TS_FILES
|
||||
translations/${META_PROJECT_NAME}_de_DE.ts
|
||||
translations/${META_PROJECT_NAME}_en_US.ts
|
||||
)
|
||||
|
||||
set(ICON_FILES
|
||||
resources/icons/hicolor/scalable/apps/${META_PROJECT_NAME}.svg
|
||||
)
|
||||
|
||||
set(DOC_FILES
|
||||
README.md
|
||||
)
|
||||
|
||||
# find c++utilities
|
||||
find_package(c++utilities 4.0.0 REQUIRED)
|
||||
use_cpp_utilities()
|
||||
include(BasicConfig)
|
||||
|
||||
# find qtutilities
|
||||
if(WIDGETS_GUI OR QUICK_GUI)
|
||||
find_package(qtutilities 5.0.0 REQUIRED)
|
||||
use_qt_utilities()
|
||||
endif()
|
||||
|
||||
list(APPEND ADDITIONAL_QT_MODULES Network Svg)
|
||||
|
||||
# include modules to apply configuration
|
||||
include(BasicConfig)
|
||||
include(QtGuiConfig)
|
||||
include(QtConfig)
|
||||
include(WindowsResources)
|
||||
include(WebViewProviderConfig)
|
||||
include(AppTarget)
|
||||
include(ShellCompletion)
|
||||
include(ConfigHeader)
|
||||
|
||||
# create desktop file using previously defined meta data
|
||||
add_desktop_file()
|
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
|
@ -0,0 +1,29 @@
|
|||
# Syncthing Tray
|
||||
|
||||
Qt 5-based tray application for [Syncthing](https://github.com/syncthing/syncthing)
|
||||
|
||||
* Still under development
|
||||
* Designed to work under any desktop environment with tray icon support
|
||||
* Doesn't require desktop environment specific libraries
|
||||
* Provides quick access to most frequently used features but does not intend to replace the official web UI
|
||||
* Shows Syncthing notifications
|
||||
* Provides quick access to the official web UI
|
||||
* Utilizes either Qt WebKit or Qt WebEngine
|
||||
* Can be built without web view support as well (then the web UI is opened in the regular browser)
|
||||
|
||||
## Screenshots
|
||||
### Openbox/Tint2
|
||||
![Openbox/Tint2](/resources/screenshots/1.png?raw=true "Under Openbox with Tint2")
|
||||
|
||||
## Download / binary repository
|
||||
I will provide packages for Arch Linux and Windows when releasing. For more information checkout my
|
||||
[website](http://martchus.no-ip.biz/website/page.php?name=programming).
|
||||
|
||||
## Build instructions
|
||||
The application depends on [c++utilities](https://github.com/Martchus/cpp-utilities) and [qtutilities](https://github.com/Martchus/qtutilities) and is built the same way as these libaries. For basic instructions checkout the README file of [c++utilities](https://github.com/Martchus/cpp-utilities).
|
||||
|
||||
The following Qt 5 modules are requried: core network gui network widgets webenginewidgets/webkitwidgets
|
||||
|
||||
#### Select Qt modules for WebView
|
||||
* If Qt WebKitWidgets is installed on the system, the tray will link against it. Otherwise it will link against Qt WebEngineWidgets.
|
||||
* To force usage of Qt WebKit/Qt WebEngine or to disable both add `-DWEBVIEW_PROVIDER=webkit/webengine/none` to the CMake arguments.
|
|
@ -0,0 +1,68 @@
|
|||
#include "./settings.h"
|
||||
#include "../gui/tray.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <c++utilities/application/argumentparser.h>
|
||||
#include <c++utilities/application/commandlineutils.h>
|
||||
#include <c++utilities/application/failure.h>
|
||||
|
||||
#include <qtutilities/resources/qtconfigarguments.h>
|
||||
#include <qtutilities/resources/resources.h>
|
||||
#include <qtutilities/settingsdialog/qtsettings.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace ApplicationUtilities;
|
||||
using namespace QtGui;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SET_APPLICATION_INFO;
|
||||
// setup argument parser
|
||||
ArgumentParser parser;
|
||||
HelpArgument helpArg(parser);
|
||||
// Qt configuration arguments
|
||||
QT_CONFIG_ARGUMENTS qtConfigArgs;
|
||||
parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &helpArg});
|
||||
try {
|
||||
parser.parseArgs(argc, argv);
|
||||
if(qtConfigArgs.qtWidgetsGuiArg().isPresent()) {
|
||||
SET_QT_APPLICATION_INFO;
|
||||
QApplication application(argc, argv);
|
||||
Settings::restore();
|
||||
Settings::qtSettings().apply();
|
||||
qtConfigArgs.applySettings(true);
|
||||
LOAD_QT_TRANSLATIONS;
|
||||
QtUtilitiesResources::init();
|
||||
int res;
|
||||
#ifndef QT_NO_SYSTEMTRAYICON
|
||||
if(QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
application.setQuitOnLastWindowClosed(false);
|
||||
TrayIcon trayIcon;
|
||||
trayIcon.show();
|
||||
res = application.exec();
|
||||
} else {
|
||||
QMessageBox::critical(nullptr, QApplication::applicationName(), QApplication::translate("main", "The system tray is (currently) not available."));
|
||||
res = -1;
|
||||
}
|
||||
#else
|
||||
QMessageBox::critical(nullptr, QApplication::applicationName(), QApplication::translate("main", "The Qt libraries have not been built with tray icon support."));
|
||||
res = -2;
|
||||
#endif
|
||||
Settings::save();
|
||||
QtUtilitiesResources::cleanup();
|
||||
return res;
|
||||
}
|
||||
} catch(const Failure &ex) {
|
||||
CMD_UTILS_START_CONSOLE;
|
||||
cout << "Unable to parse arguments. " << ex.what() << "\nSee --help for available commands." << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
#include "./settings.h"
|
||||
#include <qtutilities/settingsdialog/qtsettings.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
|
||||
using namespace Media;
|
||||
|
||||
namespace Settings {
|
||||
|
||||
// tray
|
||||
QString &syncthingUrl()
|
||||
{
|
||||
static QString v;
|
||||
return v;
|
||||
}
|
||||
bool &authEnabled()
|
||||
{
|
||||
static bool v = false;
|
||||
return v;
|
||||
}
|
||||
QString &userName()
|
||||
{
|
||||
static QString v;
|
||||
return v;
|
||||
}
|
||||
QString &password()
|
||||
{
|
||||
static QString v;
|
||||
return v;
|
||||
}
|
||||
QByteArray &apiKey()
|
||||
{
|
||||
static QByteArray v;
|
||||
return v;
|
||||
}
|
||||
bool ¬ifyOnDisconnect()
|
||||
{
|
||||
static bool v = true;
|
||||
return v;
|
||||
}
|
||||
bool ¬ifyOnErrors()
|
||||
{
|
||||
static bool v = true;
|
||||
return v;
|
||||
}
|
||||
bool ¬ifyOnSyncComplete()
|
||||
{
|
||||
static bool v = true;
|
||||
return v;
|
||||
}
|
||||
bool &showSyncthingNotifications()
|
||||
{
|
||||
static bool v = true;
|
||||
return v;
|
||||
}
|
||||
bool &launchSynchting()
|
||||
{
|
||||
static bool v = false;
|
||||
return v;
|
||||
}
|
||||
QString &syncthingCommand()
|
||||
{
|
||||
static QString v;
|
||||
return v;
|
||||
}
|
||||
|
||||
// web view
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
bool &webViewDisabled()
|
||||
{
|
||||
static bool v = false;
|
||||
return v;
|
||||
}
|
||||
double &webViewZoomFactor()
|
||||
{
|
||||
static double v = 1.0;
|
||||
return v;
|
||||
}
|
||||
QByteArray &webViewGeometry()
|
||||
{
|
||||
static QByteArray v;
|
||||
return v;
|
||||
}
|
||||
bool &webViewKeepRunning()
|
||||
{
|
||||
static bool v = true;
|
||||
return v;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Qt settings
|
||||
Dialogs::QtSettings &qtSettings()
|
||||
{
|
||||
static Dialogs::QtSettings v;
|
||||
return v;
|
||||
}
|
||||
|
||||
void restore()
|
||||
{
|
||||
QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName());
|
||||
|
||||
settings.beginGroup(QStringLiteral("tray"));
|
||||
syncthingUrl() = settings.value(QStringLiteral("syncthingUrl"), QStringLiteral("http://localhost:8080/")).toString();
|
||||
authEnabled() = settings.value(QStringLiteral("authEnabled"), false).toBool();
|
||||
userName() = settings.value(QStringLiteral("userName")).toString();
|
||||
password() = settings.value(QStringLiteral("password")).toString();
|
||||
apiKey() = settings.value(QStringLiteral("apiKey")).toByteArray();
|
||||
notifyOnDisconnect() = settings.value(QStringLiteral("notifyOnDisconnect"), true).toBool();
|
||||
notifyOnErrors() = settings.value(QStringLiteral("notifyOnErrors"), true).toBool();
|
||||
notifyOnSyncComplete() = settings.value(QStringLiteral("notifyOnSyncComplete"), true).toBool();
|
||||
notifyOnSyncComplete() = settings.value(QStringLiteral("showSyncthingNotifications"), true).toBool();
|
||||
launchSynchting() = settings.value(QStringLiteral("launchSynchting"), false).toBool();
|
||||
syncthingCommand() = settings.value(QStringLiteral("syncthingCommand"), QStringLiteral("syncthing")).toString();
|
||||
settings.endGroup();
|
||||
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
settings.beginGroup(QStringLiteral("webview"));
|
||||
webViewDisabled() = settings.value(QStringLiteral("isabled"), false).toBool();
|
||||
webViewZoomFactor() = settings.value(QStringLiteral("zoomFactor"), 1.0).toDouble();
|
||||
webViewGeometry() = settings.value(QStringLiteral("geometry")).toByteArray();
|
||||
webViewKeepRunning() = settings.value(QStringLiteral("keepRunning"), true).toBool();
|
||||
settings.endGroup();
|
||||
#endif
|
||||
|
||||
qtSettings().restore(settings);
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName());
|
||||
|
||||
settings.beginGroup(QStringLiteral("tray"));
|
||||
settings.setValue(QStringLiteral("syncthingUrl"), syncthingUrl());
|
||||
settings.setValue(QStringLiteral("authEnabled"), authEnabled());
|
||||
settings.setValue(QStringLiteral("userName"), userName());
|
||||
settings.setValue(QStringLiteral("password"), password());
|
||||
settings.setValue(QStringLiteral("apiKey"), apiKey());
|
||||
settings.setValue(QStringLiteral("notifyOnDisconnect"), notifyOnDisconnect());
|
||||
settings.setValue(QStringLiteral("notifyOnErrors"), notifyOnErrors());
|
||||
settings.setValue(QStringLiteral("notifyOnSyncComplete"), notifyOnSyncComplete());
|
||||
settings.setValue(QStringLiteral("showSyncthingNotifications"), showSyncthingNotifications());
|
||||
settings.setValue(QStringLiteral("launchSynchting"), launchSynchting());
|
||||
settings.setValue(QStringLiteral("syncthingCommand"), syncthingCommand());
|
||||
settings.endGroup();
|
||||
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
settings.beginGroup(QStringLiteral("webview"));
|
||||
settings.setValue(QStringLiteral("disabled"), webViewDisabled());
|
||||
settings.setValue(QStringLiteral("zoomFactor"), webViewZoomFactor());
|
||||
settings.setValue(QStringLiteral("geometry"), webViewGeometry());
|
||||
settings.setValue(QStringLiteral("keepRunning"), webViewKeepRunning());
|
||||
settings.endGroup();
|
||||
#endif
|
||||
|
||||
qtSettings().save(settings);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <c++utilities/conversion/types.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QByteArray)
|
||||
QT_FORWARD_DECLARE_CLASS(QString)
|
||||
|
||||
namespace Media {
|
||||
enum class TagUsage;
|
||||
enum class ElementPosition;
|
||||
}
|
||||
|
||||
namespace Dialogs {
|
||||
class QtSettings;
|
||||
}
|
||||
|
||||
namespace Settings {
|
||||
|
||||
QString &syncthingUrl();
|
||||
bool &authEnabled();
|
||||
QString &userName();
|
||||
QString &password();
|
||||
QByteArray &apiKey();
|
||||
|
||||
bool ¬ifyOnDisconnect();
|
||||
bool ¬ifyOnErrors();
|
||||
bool ¬ifyOnSyncComplete();
|
||||
bool &showSyncthingNotifications();
|
||||
|
||||
bool &launchSynchting();
|
||||
QString &syncthingCommand();
|
||||
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
bool &webViewDisabled();
|
||||
double &webViewZoomFactor();
|
||||
QByteArray &webViewGeometry();
|
||||
bool &webViewKeepRunning();
|
||||
#endif
|
||||
|
||||
// Qt settings
|
||||
Dialogs::QtSettings &qtSettings();
|
||||
|
||||
void restore();
|
||||
void save();
|
||||
|
||||
}
|
||||
|
||||
#endif // SETTINGS_H
|
|
@ -0,0 +1,614 @@
|
|||
#include "./syncthingconnection.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
#include <QPixmap>
|
||||
#include <QAuthenticator>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Data {
|
||||
|
||||
/*!
|
||||
* \brief Returns the QNetworkAccessManager instance used by SyncthingConnection instances.
|
||||
*/
|
||||
QNetworkAccessManager &networkAccessManager()
|
||||
{
|
||||
static QNetworkAccessManager networkAccessManager;
|
||||
return networkAccessManager;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \class SyncthingConnection
|
||||
* \brief The SyncthingConnection class allows Qt applications to access Syncthing.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Constructs a new instance ready to connect. To establish the connection, call connect().
|
||||
*/
|
||||
SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByteArray &apiKey, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_syncthingUrl(syncthingUrl),
|
||||
m_apiKey(apiKey),
|
||||
m_status(SyncthingStatus::Disconnected),
|
||||
m_keepPolling(false),
|
||||
m_reconnecting(false),
|
||||
m_configReply(nullptr),
|
||||
m_statusReply(nullptr),
|
||||
m_eventsReply(nullptr),
|
||||
m_unreadNotifications(false)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief Destroys the instance. Ongoing requests are aborted.
|
||||
*/
|
||||
SyncthingConnection::~SyncthingConnection()
|
||||
{
|
||||
disconnect();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the string representation of the current status().
|
||||
*/
|
||||
QString SyncthingConnection::statusText() const
|
||||
{
|
||||
switch(m_status) {
|
||||
case SyncthingStatus::Disconnected:
|
||||
return tr("disconnected");
|
||||
case SyncthingStatus::Default:
|
||||
return tr("connected");
|
||||
case SyncthingStatus::NotificationsAvailable:
|
||||
return tr("connected, notifications available");
|
||||
case SyncthingStatus::Paused:
|
||||
return tr("connected, paused");
|
||||
case SyncthingStatus::Synchronizing:
|
||||
return tr("connected, synchronizing");
|
||||
default:
|
||||
return tr("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Connects asynchronously to Syncthing. Does nothing if already connected.
|
||||
*/
|
||||
void SyncthingConnection::connect()
|
||||
{
|
||||
if(!isConnected()) {
|
||||
m_reconnecting = false;
|
||||
requestConfig();
|
||||
requestStatus();
|
||||
m_lastEventId = 0;
|
||||
requestEvents();
|
||||
m_keepPolling = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Disconnects. Does nothing if not connected.
|
||||
*/
|
||||
void SyncthingConnection::disconnect()
|
||||
{
|
||||
m_reconnecting = false;
|
||||
if(m_configReply) {
|
||||
m_configReply->abort();
|
||||
}
|
||||
if(m_statusReply) {
|
||||
m_statusReply->abort();
|
||||
}
|
||||
if(m_eventsReply) {
|
||||
m_eventsReply->abort();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Disconnects if connected, then (re-)connects asynchronously.
|
||||
*/
|
||||
void SyncthingConnection::reconnect()
|
||||
{
|
||||
if(isConnected()) {
|
||||
m_reconnecting = true;
|
||||
if(m_configReply) {
|
||||
m_configReply->abort();
|
||||
}
|
||||
if(m_statusReply) {
|
||||
m_statusReply->abort();
|
||||
}
|
||||
if(m_eventsReply) {
|
||||
m_eventsReply->abort();
|
||||
}
|
||||
} else {
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingConnection::pause(const QString &dev)
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("device"), dev);
|
||||
QObject::connect(postData(QStringLiteral("system/pause"), query), &QNetworkReply::finished, this, &SyncthingConnection::readPauseResume);
|
||||
}
|
||||
|
||||
void SyncthingConnection::pauseAllDevs()
|
||||
{
|
||||
for(const SyncthingDev &dev : m_devs) {
|
||||
pause(dev.id);
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingConnection::resume(const QString &dev)
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("device"), dev);
|
||||
QObject::connect(postData(QStringLiteral("system/resume"), query), &QNetworkReply::finished, this, &SyncthingConnection::readPauseResume);
|
||||
}
|
||||
|
||||
void SyncthingConnection::resumeAllDevs()
|
||||
{
|
||||
for(const SyncthingDev &dev : m_devs) {
|
||||
resume(dev.id);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests rescanning the directory with the specified ID.
|
||||
*
|
||||
* The signal error() is emitted when the request was not successful.
|
||||
*/
|
||||
void SyncthingConnection::rescan(const QString &dir)
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("folder"), dir);
|
||||
QObject::connect(postData(QStringLiteral("db/scan"), query), &QNetworkReply::finished, this, &SyncthingConnection::readRescan);
|
||||
}
|
||||
|
||||
void SyncthingConnection::rescanAllDirs()
|
||||
{
|
||||
for(const SyncthingDir &dir : m_dirs) {
|
||||
rescan(dir.id);
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingConnection::notificationsRead()
|
||||
{
|
||||
m_unreadNotifications = false;
|
||||
setStatus(status());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Prepares a request for the specified \a path and \a query.
|
||||
*/
|
||||
QNetworkRequest SyncthingConnection::prepareRequest(const QString &path, const QUrlQuery &query, bool rest)
|
||||
{
|
||||
QUrl url(m_syncthingUrl);
|
||||
url.setPath(rest ? (url.path() % QStringLiteral("/rest/") % path) : (url.path() + path));
|
||||
url.setUserName(user());
|
||||
url.setPassword(password());
|
||||
url.setQuery(query);
|
||||
QNetworkRequest request(url);
|
||||
request.setRawHeader("X-API-Key", m_apiKey);
|
||||
return request;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests asynchronously data using the rest API.
|
||||
*/
|
||||
inline QNetworkReply *SyncthingConnection::requestData(const QString &path, const QUrlQuery &query, bool rest)
|
||||
{
|
||||
return networkAccessManager().get(prepareRequest(path, query, rest));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Posts asynchronously data using the rest API.
|
||||
*/
|
||||
inline QNetworkReply *SyncthingConnection::postData(const QString &path, const QUrlQuery &query, const QByteArray &data)
|
||||
{
|
||||
return networkAccessManager().post(prepareRequest(path, query), data);
|
||||
}
|
||||
|
||||
SyncthingDir *SyncthingConnection::findDirInfo(const QString &dir, int &row)
|
||||
{
|
||||
for(SyncthingDir &d : m_dirs) {
|
||||
if(d.id == dir) {
|
||||
return &d;
|
||||
}
|
||||
++row;
|
||||
}
|
||||
return nullptr; // TODO: dir is unknown, trigger refreshing the config
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests the Syncthing configuration asynchronously.
|
||||
*
|
||||
* The signal newConfig() is emitted on success; otherwise error() is emitted.
|
||||
*/
|
||||
void SyncthingConnection::requestConfig()
|
||||
{
|
||||
QObject::connect(m_configReply = requestData(QStringLiteral("system/config"), QUrlQuery()), &QNetworkReply::finished, this, &SyncthingConnection::readConfig);
|
||||
}
|
||||
|
||||
void SyncthingConnection::requestStatus()
|
||||
{
|
||||
QObject::connect(m_statusReply = requestData(QStringLiteral("system/status"), QUrlQuery()), &QNetworkReply::finished, this, &SyncthingConnection::readStatus);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests the Syncthing events (since the last successful call) asynchronously.
|
||||
*
|
||||
* The signal newEvents() is emitted on success; otherwise error() is emitted.
|
||||
*/
|
||||
void SyncthingConnection::requestEvents()
|
||||
{
|
||||
QUrlQuery query;
|
||||
if(m_lastEventId) {
|
||||
query.addQueryItem(QStringLiteral("since"), QString::number(m_lastEventId));
|
||||
}
|
||||
QObject::connect(m_eventsReply = requestData(QStringLiteral("events"), query), &QNetworkReply::finished, this, &SyncthingConnection::readEvents);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Requests a QR code for the specified \a text.
|
||||
*
|
||||
* The specified \a callback is called on success; otherwise error() is emitted.
|
||||
*/
|
||||
void SyncthingConnection::requestQrCode(const QString &text, std::function<void(const QPixmap &)> callback)
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(QStringLiteral("text"), text);
|
||||
QNetworkReply *reply = requestData(QStringLiteral("/qr/"), query, false);
|
||||
QObject::connect(reply, &QNetworkReply::finished, [this, reply, callback] {
|
||||
reply->deleteLater();
|
||||
QPixmap pixmap;
|
||||
switch(reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
pixmap.loadFromData(reply->readAll());
|
||||
callback(pixmap);
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request QR-Code: ") + reply->errorString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SyncthingConnection::requestLog(std::function<void (const std::vector<SyncthingLogEntry> &)> callback)
|
||||
{
|
||||
QNetworkReply *reply = requestData(QStringLiteral("system/log"), QUrlQuery());
|
||||
QObject::connect(reply, &QNetworkReply::finished, [this, reply, callback] {
|
||||
reply->deleteLater();
|
||||
switch(reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
if(jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonArray log(replyDoc.object().value(QStringLiteral("messages")).toArray());
|
||||
vector<SyncthingLogEntry> logEntries;
|
||||
logEntries.reserve(log.size());
|
||||
for(const QJsonValue &logVal : log) {
|
||||
const QJsonObject logObj(logVal.toObject());
|
||||
SyncthingLogEntry entry;
|
||||
entry.when = logObj.value(QStringLiteral("when")).toString();
|
||||
entry.message = logObj.value(QStringLiteral("message")).toString();
|
||||
logEntries.emplace_back(move(entry));
|
||||
}
|
||||
callback(logEntries);
|
||||
} else {
|
||||
emit error(tr("Unable to parse Syncthing log: ") + jsonError.errorString());
|
||||
}
|
||||
break;
|
||||
} default:
|
||||
emit error(tr("Unable to request system log: ") + reply->errorString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads results of requestConfig().
|
||||
*/
|
||||
void SyncthingConnection::readConfig()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if(reply == m_configReply) {
|
||||
m_configReply = nullptr;
|
||||
}
|
||||
|
||||
switch(reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
if(jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonObject replyObj = replyDoc.object();
|
||||
emit newConfig(replyObj);
|
||||
readDirs(replyObj.value(QStringLiteral("folders")).toArray());
|
||||
readDevs(replyObj.value(QStringLiteral("devices")).toArray());
|
||||
} else {
|
||||
emit error(tr("Unable to parse Syncthing config: ") + jsonError.errorString());
|
||||
}
|
||||
} case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing config: ") + reply->errorString());
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingConnection::readDirs(const QJsonArray &dirs)
|
||||
{
|
||||
m_dirs.clear();
|
||||
m_dirs.reserve(dirs.size());
|
||||
for(const QJsonValue &dirVal : dirs) {
|
||||
const QJsonObject dirObj(dirVal.toObject());
|
||||
SyncthingDir dirItem;
|
||||
dirItem.id = dirObj.value(QStringLiteral("id")).toString();
|
||||
if(!dirItem.id.isEmpty()) { // ignore dirs with empty id
|
||||
dirItem.label = dirObj.value(QStringLiteral("label")).toString();
|
||||
dirItem.path = dirObj.value(QStringLiteral("path")).toString();
|
||||
for(const QJsonValue &dev : dirObj.value(QStringLiteral("devices")).toArray()) {
|
||||
const QString devId = dev.toObject().value(QStringLiteral("deviceID")).toString();
|
||||
if(!devId.isEmpty()) {
|
||||
dirItem.devices << devId;
|
||||
}
|
||||
}
|
||||
dirItem.readOnly = dirObj.value(QStringLiteral("readOnly")).toBool(false);
|
||||
dirItem.rescanInterval = dirObj.value(QStringLiteral("rescanIntervalS")).toInt(-1);
|
||||
dirItem.ignorePermissions = dirObj.value(QStringLiteral("ignorePerms")).toBool(false);
|
||||
dirItem.autoNormalize = dirObj.value(QStringLiteral("autoNormalize")).toBool(false);
|
||||
dirItem.minDiskFreePercentage = dirObj.value(QStringLiteral("minDiskFreePct")).toInt(-1);
|
||||
dirItem.status = DirStatus::Unknown;
|
||||
dirItem.progressPercentage = 0;
|
||||
m_dirs.emplace_back(move(dirItem));
|
||||
}
|
||||
}
|
||||
emit newDirs(m_dirs);
|
||||
}
|
||||
|
||||
void SyncthingConnection::readDevs(const QJsonArray &devs)
|
||||
{
|
||||
m_devs.clear();
|
||||
m_devs.reserve(devs.size());
|
||||
for(const QJsonValue &devVal: devs) {
|
||||
const QJsonObject devObj(devVal.toObject());
|
||||
SyncthingDev devItem;
|
||||
devItem.id = devObj.value(QStringLiteral("deviceID")).toString();
|
||||
if(!devItem.id.isEmpty()) { // ignore dirs with empty id
|
||||
devItem.name = devObj.value(QStringLiteral("name")).toString();
|
||||
for(const QJsonValue &addrVal : devObj.value(QStringLiteral("addresses")).toArray()) {
|
||||
devItem.addresses << addrVal.toString();
|
||||
}
|
||||
devItem.compression = devObj.value(QStringLiteral("compression")).toString();
|
||||
devItem.certName = devObj.value(QStringLiteral("certName")).toString();
|
||||
devItem.introducer = devObj.value(QStringLiteral("introducer")).toBool(false);
|
||||
devItem.status = DevStatus::Unknown;
|
||||
devItem.progressPercentage = 0;
|
||||
m_devs.push_back(move(devItem));
|
||||
}
|
||||
}
|
||||
emit newDevices(m_devs);
|
||||
}
|
||||
|
||||
void SyncthingConnection::readStatus()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if(reply == m_statusReply) {
|
||||
m_statusReply = nullptr;
|
||||
}
|
||||
|
||||
switch(reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
if(jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonObject replyObj = replyDoc.object();
|
||||
const QString myId(replyObj.value(QStringLiteral("myID")).toString());
|
||||
if(myId != m_myId) {
|
||||
emit configDirChanged(m_myId = myId);
|
||||
}
|
||||
// other values are currently not interesting
|
||||
} else {
|
||||
emit error(tr("Unable to parse Syncthing config: ") + jsonError.errorString());
|
||||
}
|
||||
} case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing config: ") + reply->errorString());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads results of requestEvents().
|
||||
*/
|
||||
void SyncthingConnection::readEvents()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if(reply == m_eventsReply) {
|
||||
m_eventsReply = nullptr;
|
||||
}
|
||||
|
||||
switch(reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
if(jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonArray replyArray = replyDoc.array();
|
||||
emit newEvents(replyArray);
|
||||
// search the array for interesting events
|
||||
for(const QJsonValue &eventVal : replyArray) {
|
||||
const QJsonObject event = eventVal.toObject();
|
||||
m_lastEventId = event.value(QStringLiteral("id")).toInt(m_lastEventId);
|
||||
const QString eventType = event.value(QStringLiteral("type")).toString();
|
||||
const QJsonObject eventData = event.value(QStringLiteral("data")).toObject();
|
||||
if(eventType == QLatin1String("Starting")) {
|
||||
readStartingEvent(eventData);
|
||||
} else if(eventType == QLatin1String("StateChanged")) {
|
||||
readStatusChangedEvent(eventData);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
emit error(tr("Unable to parse Syncthing events: ") + jsonError.errorString());
|
||||
setStatus(SyncthingStatus::Disconnected);
|
||||
return;
|
||||
}
|
||||
} case QNetworkReply::TimeoutError:
|
||||
// no new events available, keep polling
|
||||
break;
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
// intended disconnect, not an error
|
||||
if(m_reconnecting) {
|
||||
// if reconnection flag is set, instantly etstablish a new connection ...
|
||||
m_reconnecting = false;
|
||||
requestConfig();
|
||||
requestStatus();
|
||||
m_lastEventId = 0;
|
||||
requestEvents();
|
||||
m_keepPolling = true;
|
||||
} else {
|
||||
// ... otherwise keep disconnected
|
||||
setStatus(SyncthingStatus::Disconnected);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing events: ") + reply->errorString());
|
||||
setStatus(SyncthingStatus::Disconnected);
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_keepPolling) {
|
||||
requestEvents();
|
||||
// TODO: need to change the status somewhere else
|
||||
setStatus(SyncthingStatus::Default);
|
||||
} else {
|
||||
setStatus(SyncthingStatus::Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads results of requestEvents().
|
||||
*/
|
||||
void SyncthingConnection::readStartingEvent(const QJsonObject &event)
|
||||
{
|
||||
QString strValue = event.value(QStringLiteral("home")).toString();
|
||||
if(strValue != m_configDir) {
|
||||
emit configDirChanged(m_configDir = strValue);
|
||||
}
|
||||
strValue = event.value(QStringLiteral("myID")).toString();
|
||||
if(strValue != m_myId) {
|
||||
emit configDirChanged(m_myId = strValue);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads results of requestEvents().
|
||||
*/
|
||||
void SyncthingConnection::readStatusChangedEvent(const QJsonObject &event)
|
||||
{
|
||||
const QString dir(event.value(QStringLiteral("folder")).toString());
|
||||
if(!dir.isEmpty()) {
|
||||
// dir status changed
|
||||
int row;
|
||||
if(SyncthingDir *dirInfo = findDirInfo(dir, row)) {
|
||||
const QString statusStr(event.value(QStringLiteral("to")).toString());
|
||||
DirStatus status;
|
||||
if(statusStr == QLatin1String("idle")) {
|
||||
status = DirStatus::Idle;
|
||||
} else if(statusStr == QLatin1String("scanning")) {
|
||||
status = DirStatus::Scanning;
|
||||
} else if(statusStr == QLatin1String("syncing")) {
|
||||
status = DirStatus::Synchronizing;
|
||||
} else if(statusStr == QLatin1String("error")) {
|
||||
status = DirStatus::OutOfSync;
|
||||
} else {
|
||||
status = DirStatus::Unknown;
|
||||
}
|
||||
if(dirInfo->status != status) {
|
||||
dirInfo->status = status;
|
||||
emit dirStatusChanged(*dirInfo, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads results of rescan().
|
||||
*/
|
||||
void SyncthingConnection::readRescan()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
switch(reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request rescan: ") + reply->errorString());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Reads results of pause().
|
||||
*/
|
||||
void SyncthingConnection::readPauseResume()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
switch(reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request pause/resume: ") + reply->errorString());
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the connection status. Ensures statusChanged() is emitted.
|
||||
* \param status Specifies the status; should be either SyncthingStatus::Disconnected or SyncthingStatus::Default.
|
||||
* \remarks If \a status is not SyncthingStatus::Disconnected the best status for the current connection is determined automatically.
|
||||
*/
|
||||
void SyncthingConnection::setStatus(SyncthingStatus status)
|
||||
{
|
||||
if(m_status != status) {
|
||||
switch(m_status = status) {
|
||||
case SyncthingStatus::Disconnected:
|
||||
break;
|
||||
default:
|
||||
if(m_unreadNotifications) {
|
||||
m_status = SyncthingStatus::NotificationsAvailable;
|
||||
} else {
|
||||
// check whether at least one directory is synchronizing
|
||||
bool synchronizing = false;
|
||||
for(const SyncthingDir &dir : m_dirs) {
|
||||
if(dir.status == DirStatus::Synchronizing) {
|
||||
synchronizing = true;
|
||||
}
|
||||
}
|
||||
if(synchronizing) {
|
||||
m_status = SyncthingStatus::Synchronizing;
|
||||
} else {
|
||||
// check whether at least one device is paused
|
||||
bool paused = false;
|
||||
for(const SyncthingDev &dev : m_devs) {
|
||||
if(dev.status == DevStatus::Paused) {
|
||||
paused = true;
|
||||
}
|
||||
}
|
||||
if(paused) {
|
||||
m_status = SyncthingStatus::Paused;
|
||||
} else {
|
||||
m_status = SyncthingStatus::Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
emit statusChanged(m_status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
#ifndef SYNCTHINGCONNECTION_H
|
||||
#define SYNCTHINGCONNECTION_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager)
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkRequest)
|
||||
QT_FORWARD_DECLARE_CLASS(QUrlQuery)
|
||||
QT_FORWARD_DECLARE_CLASS(QJsonObject)
|
||||
QT_FORWARD_DECLARE_CLASS(QJsonArray)
|
||||
|
||||
namespace Data {
|
||||
|
||||
QNetworkAccessManager &networkAccessManager();
|
||||
|
||||
enum class SyncthingStatus
|
||||
{
|
||||
Disconnected,
|
||||
Default,
|
||||
NotificationsAvailable,
|
||||
Paused,
|
||||
Synchronizing
|
||||
};
|
||||
|
||||
enum class DirStatus
|
||||
{
|
||||
Unknown,
|
||||
Idle,
|
||||
Scanning,
|
||||
Synchronizing,
|
||||
Paused,
|
||||
OutOfSync
|
||||
};
|
||||
|
||||
struct SyncthingDir
|
||||
{
|
||||
QString id;
|
||||
QString label;
|
||||
QString path;
|
||||
QStringList devices;
|
||||
bool readOnly;
|
||||
bool ignorePermissions;
|
||||
bool autoNormalize;
|
||||
int rescanInterval;
|
||||
int minDiskFreePercentage;
|
||||
DirStatus status;
|
||||
int progressPercentage;
|
||||
};
|
||||
|
||||
enum class DevStatus
|
||||
{
|
||||
Unknown,
|
||||
Disconnected,
|
||||
Idle,
|
||||
Synchronizing,
|
||||
Paused,
|
||||
OutOfSync
|
||||
};
|
||||
|
||||
struct SyncthingDev
|
||||
{
|
||||
QString id;
|
||||
QString name;
|
||||
QStringList addresses;
|
||||
QString compression;
|
||||
QString certName;
|
||||
DevStatus status;
|
||||
int progressPercentage;
|
||||
bool introducer;
|
||||
};
|
||||
|
||||
struct SyncthingLogEntry
|
||||
{
|
||||
QString when;
|
||||
QString message;
|
||||
};
|
||||
|
||||
class SyncthingConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString syncthingUrl READ syncthingUrl WRITE setSyncthingUrl)
|
||||
Q_PROPERTY(QByteArray apiKey READ apiKey WRITE setApiKey)
|
||||
Q_PROPERTY(SyncthingStatus status READ status NOTIFY statusChanged)
|
||||
Q_PROPERTY(QString configDir READ configDir NOTIFY configDirChanged)
|
||||
Q_PROPERTY(QString myId READ myId NOTIFY myIdChanged)
|
||||
|
||||
public:
|
||||
explicit SyncthingConnection(const QString &syncthingUrl = QStringLiteral("http://localhost:8080"), const QByteArray &apiKey = QByteArray(), QObject *parent = nullptr);
|
||||
~SyncthingConnection();
|
||||
|
||||
const QString &syncthingUrl() const;
|
||||
void setSyncthingUrl(const QString &url);
|
||||
const QByteArray &apiKey() const;
|
||||
void setApiKey(const QByteArray &apiKey);
|
||||
const QString &user() const;
|
||||
const QString &password() const;
|
||||
void setCredentials(const QString &user, const QString &password);
|
||||
SyncthingStatus status() const;
|
||||
QString statusText() const;
|
||||
bool isConnected() const;
|
||||
const QString &configDir() const;
|
||||
const QString &myId() const;
|
||||
const std::vector<SyncthingDir> &dirInfo() const;
|
||||
const std::vector<SyncthingDev> &devInfo() const;
|
||||
void requestQrCode(const QString &text, std::function<void (const QPixmap &)> callback);
|
||||
void requestLog(std::function<void (const std::vector<SyncthingLogEntry> &)> callback);
|
||||
|
||||
public Q_SLOTS:
|
||||
void connect();
|
||||
void disconnect();
|
||||
void reconnect();
|
||||
void pause(const QString &dev);
|
||||
void pauseAllDevs();
|
||||
void resume(const QString &dev);
|
||||
void resumeAllDevs();
|
||||
void rescan(const QString &dir);
|
||||
void rescanAllDirs();
|
||||
void notificationsRead();
|
||||
|
||||
Q_SIGNALS:
|
||||
/*!
|
||||
* \brief Indicates new configuration (dirs, devs, ...) is available.
|
||||
* \remarks
|
||||
* - Configuration is requested automatically when connecting.
|
||||
* - Previous directories (and directory info objects!) are invalidated.
|
||||
* - Previous devices (and device info objects!) are invalidated.
|
||||
*/
|
||||
void newConfig(const QJsonObject &config);
|
||||
|
||||
/*!
|
||||
* \brief Indicates new directories are available.
|
||||
* \remarks Always emitted after newConfig() as soon as new directory info objects become available.
|
||||
*/
|
||||
void newDirs(const std::vector<SyncthingDir> &dirs);
|
||||
|
||||
/*!
|
||||
* \brief Indicates new devices are available.
|
||||
* \remarks Always emitted after newConfig() as soon as new device info objects become available.
|
||||
*/
|
||||
void newDevices(const std::vector<SyncthingDev> &devs);
|
||||
|
||||
/*!
|
||||
* \brief Indicates new events (dir status changed, ...) are available.
|
||||
* \remarks New events are automatically polled when connected.
|
||||
*/
|
||||
void newEvents(const QJsonArray &events);
|
||||
|
||||
/*!
|
||||
* \brief Indicates the status of the specified \a dir changed.
|
||||
*/
|
||||
void dirStatusChanged(const SyncthingDir &dir, int index);
|
||||
|
||||
void newNotification(const QString &message);
|
||||
|
||||
/*!
|
||||
* \brief Indicates a request (for configuration, events, ...) failed.
|
||||
*/
|
||||
void error(const QString &errorMessage);
|
||||
|
||||
/*!
|
||||
* \brief Indicates the status of the connection changed.
|
||||
*/
|
||||
void statusChanged(SyncthingStatus newStatus);
|
||||
|
||||
/*!
|
||||
* \brief Indicates the Syncthing home/configuration directory changed.
|
||||
*/
|
||||
void configDirChanged(const QString &newConfigDir);
|
||||
|
||||
/*!
|
||||
* \brief Indicates ID of the own Syncthing device changed.
|
||||
*/
|
||||
void myIdChanged(const QString &myNewId);
|
||||
|
||||
private Q_SLOTS:
|
||||
void requestConfig();
|
||||
void requestStatus();
|
||||
void requestEvents();
|
||||
|
||||
void readConfig();
|
||||
void readDirs(const QJsonArray &dirs);
|
||||
void readDevs(const QJsonArray &devs);
|
||||
|
||||
void readStatus();
|
||||
|
||||
void readEvents();
|
||||
void readStartingEvent(const QJsonObject &event);
|
||||
void readStatusChangedEvent(const QJsonObject &event);
|
||||
void readRescan();
|
||||
void readPauseResume();
|
||||
|
||||
void setStatus(SyncthingStatus status);
|
||||
|
||||
private:
|
||||
QNetworkRequest prepareRequest(const QString &path, const QUrlQuery &query, bool rest = true);
|
||||
QNetworkReply *requestData(const QString &path, const QUrlQuery &query, bool rest = true);
|
||||
QNetworkReply *postData(const QString &path, const QUrlQuery &query, const QByteArray &data = QByteArray());
|
||||
SyncthingDir *findDirInfo(const QString &dir, int &row);
|
||||
|
||||
QString m_syncthingUrl;
|
||||
QByteArray m_apiKey;
|
||||
QString m_user;
|
||||
QString m_password;
|
||||
SyncthingStatus m_status;
|
||||
bool m_keepPolling;
|
||||
bool m_reconnecting;
|
||||
int m_lastEventId;
|
||||
QString m_configDir;
|
||||
QString m_myId;
|
||||
QNetworkReply *m_configReply;
|
||||
QNetworkReply *m_statusReply;
|
||||
QNetworkReply *m_eventsReply;
|
||||
bool m_unreadNotifications;
|
||||
std::vector<SyncthingDir> m_dirs;
|
||||
std::vector<SyncthingDev> m_devs;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Returns the URL used to connect to Syncthing.
|
||||
*/
|
||||
inline const QString &SyncthingConnection::syncthingUrl() const
|
||||
{
|
||||
return m_syncthingUrl;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the URL used to connect to Syncthing.
|
||||
*/
|
||||
inline void SyncthingConnection::setSyncthingUrl(const QString &url)
|
||||
{
|
||||
m_syncthingUrl = url;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the API key used to connect to Syncthing.
|
||||
*/
|
||||
inline const QByteArray &SyncthingConnection::apiKey() const
|
||||
{
|
||||
return m_apiKey;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Sets the API key used to connect to Syncthing.
|
||||
*/
|
||||
inline void SyncthingConnection::setApiKey(const QByteArray &apiKey)
|
||||
{
|
||||
m_apiKey = apiKey;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the user name which has been set using setCredentials().
|
||||
*/
|
||||
inline const QString &SyncthingConnection::user() const
|
||||
{
|
||||
return m_user;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the password which has been set using setCredentials().
|
||||
*/
|
||||
inline const QString &SyncthingConnection::password() const
|
||||
{
|
||||
return m_password;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Provides credentials used for HTTP authentication.
|
||||
*/
|
||||
inline void SyncthingConnection::setCredentials(const QString &user, const QString &password)
|
||||
{
|
||||
m_user = user, m_password = password;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the connection status.
|
||||
*/
|
||||
inline SyncthingStatus SyncthingConnection::status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns whether the connection has been established.
|
||||
*/
|
||||
inline bool SyncthingConnection::isConnected() const
|
||||
{
|
||||
return m_status != SyncthingStatus::Disconnected;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the Syncthing home/configuration directory.
|
||||
*/
|
||||
inline const QString &SyncthingConnection::configDir() const
|
||||
{
|
||||
return m_configDir;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the ID of the own Syncthing device.
|
||||
*/
|
||||
inline const QString &SyncthingConnection::myId() const
|
||||
{
|
||||
return m_myId;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns all available directory info.
|
||||
* \remarks The returned object container object is persistent. However, the contained
|
||||
* info objects are invalidated when the newConfig() signal is emitted.
|
||||
*/
|
||||
inline const std::vector<SyncthingDir> &SyncthingConnection::dirInfo() const
|
||||
{
|
||||
return m_dirs;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns all available device info.
|
||||
* \remarks The returned object container object is persistent. However, the contained
|
||||
* info objects are invalidated when the newConfig() signal is emitted.
|
||||
*/
|
||||
inline const std::vector<SyncthingDev> &SyncthingConnection::devInfo() const
|
||||
{
|
||||
return m_devs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // SYNCTHINGCONNECTION_H
|
|
@ -0,0 +1,195 @@
|
|||
#include "./syncthingdevicemodel.h"
|
||||
#include "./syncthingconnection.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
SyncthingDeviceModel::SyncthingDeviceModel(SyncthingConnection &connection, QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
m_connection(connection),
|
||||
m_devs(connection.devInfo()),
|
||||
m_unknownIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-disconnected.svg"))),
|
||||
m_idleIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-ok.svg"))),
|
||||
m_syncIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-sync.svg"))),
|
||||
m_errorIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-error.svg"))),
|
||||
m_pausedIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-pause.svg"))),
|
||||
m_otherIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-default.svg")))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the device info for the spcified \a index. The returned object is not persistent.
|
||||
*/
|
||||
const SyncthingDev *SyncthingDeviceModel::devInfo(const QModelIndex &index) const
|
||||
{
|
||||
return (index.parent().isValid() ? devInfo(index.parent()) : (index.row() < m_devs.size() ? &m_devs[index.row()] : nullptr));
|
||||
}
|
||||
|
||||
QModelIndex SyncthingDeviceModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid()) {
|
||||
// top-level: all dev IDs
|
||||
if(row < rowCount(parent)) {
|
||||
return createIndex(row, column, -1);
|
||||
}
|
||||
} else if(!parent.parent().isValid()) {
|
||||
// dev-level: dev attributes
|
||||
if(row < rowCount(parent)) {
|
||||
return createIndex(row, column, parent.row());
|
||||
}
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex SyncthingDeviceModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
return child.internalId() != static_cast<quintptr>(-1) ? index(child.internalId(), 0, QModelIndex()) : QModelIndex();
|
||||
}
|
||||
|
||||
QVariant SyncthingDeviceModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch(orientation) {
|
||||
case Qt::Horizontal:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
switch(section) {
|
||||
case 0: return tr("ID");
|
||||
case 1: return tr("Status");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant SyncthingDeviceModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if(index.isValid()) {
|
||||
if(index.parent().isValid()) {
|
||||
// dir attributes
|
||||
if(index.parent().row() < m_devs.size()) {
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0: // attribute names
|
||||
switch(index.row()) {
|
||||
case 0: return tr("ID");
|
||||
case 1: return tr("Addresses");
|
||||
case 2: return tr("Compression");
|
||||
case 3: return tr("Certificate");
|
||||
case 4: return tr("Introducer");
|
||||
}
|
||||
break;
|
||||
case 1: // attribute values
|
||||
const SyncthingDev &dev = m_devs[index.parent().row()];
|
||||
switch(index.row()) {
|
||||
case 0: return dev.id;
|
||||
case 1: return dev.addresses.join(QStringLiteral(", "));
|
||||
case 2: return dev.compression;
|
||||
case 3: return dev.certName.isEmpty() ? tr("none") : dev.certName;
|
||||
case 4: return dev.introducer ? tr("yes") : tr("no");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
} else if(index.row() < m_devs.size()) {
|
||||
// dir IDs and status
|
||||
const SyncthingDev &dev = m_devs[index.row()];
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0: return dev.name.isEmpty() ? dev.id : dev.name;
|
||||
case 1:
|
||||
switch(dev.status) {
|
||||
case DevStatus::Unknown: return tr("Unknown status");
|
||||
case DevStatus::Idle: return tr("Idle");
|
||||
case DevStatus::Disconnected: return tr("Idle");
|
||||
case DevStatus::Synchronizing: return dev.progressPercentage > 0 ? tr("Synchronizing (%1 %)").arg(dev.progressPercentage) : tr("Synchronizing");
|
||||
case DevStatus::Paused: return tr("Paused");
|
||||
case DevStatus::OutOfSync: return tr("Out of sync");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::DecorationRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
switch(dev.status) {
|
||||
case DevStatus::Unknown:
|
||||
case DevStatus::Disconnected: return m_unknownIcon;
|
||||
case DevStatus::Idle: return m_idleIcon;
|
||||
case DevStatus::Synchronizing: return m_syncIcon;
|
||||
case DevStatus::Paused: return m_pausedIcon;
|
||||
case DevStatus::OutOfSync: return m_errorIcon;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::TextAlignmentRole:
|
||||
switch(index.column()) {
|
||||
case 0: break;
|
||||
case 1: return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);
|
||||
}
|
||||
break;
|
||||
case Qt::ForegroundRole:
|
||||
switch(index.column()) {
|
||||
case 0: break;
|
||||
case 1:
|
||||
switch(dev.status) {
|
||||
case DevStatus::Unknown: break;
|
||||
case DevStatus::Disconnected: break;
|
||||
case DevStatus::Idle: return QColor(Qt::darkGreen);
|
||||
case DevStatus::Synchronizing: return QColor(Qt::blue);
|
||||
case DevStatus::Paused: break;
|
||||
case DevStatus::OutOfSync: return QColor(Qt::red);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool SyncthingDeviceModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int SyncthingDeviceModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid()) {
|
||||
return m_devs.size();
|
||||
} else if(!parent.parent().isValid()) {
|
||||
return 5;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int SyncthingDeviceModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid()) {
|
||||
return 2; // name/id, status
|
||||
} else if(!parent.parent().isValid()) {
|
||||
return 2; // field name and value
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Data
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef DATA_SYNCTHINGDEVICEMODEL_H
|
||||
#define DATA_SYNCTHINGDEVICEMODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QIcon>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Data {
|
||||
|
||||
class SyncthingConnection;
|
||||
struct SyncthingDev;
|
||||
|
||||
class SyncthingDeviceModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SyncthingDeviceModel(SyncthingConnection &connection, QObject *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
|
||||
QModelIndex parent(const QModelIndex &child) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
int columnCount(const QModelIndex &parent) const;
|
||||
const SyncthingDev *devInfo(const QModelIndex &index) const;
|
||||
|
||||
private:
|
||||
Data::SyncthingConnection &m_connection;
|
||||
const std::vector<SyncthingDev> &m_devs;
|
||||
const QIcon m_unknownIcon;
|
||||
const QIcon m_idleIcon;
|
||||
const QIcon m_syncIcon;
|
||||
const QIcon m_errorIcon;
|
||||
const QIcon m_pausedIcon;
|
||||
const QIcon m_otherIcon;
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
|
||||
#endif // DATA_SYNCTHINGDEVICEMODEL_H
|
|
@ -0,0 +1,215 @@
|
|||
#include "./syncthingdirectorymodel.h"
|
||||
#include "./syncthingconnection.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
SyncthingDirectoryModel::SyncthingDirectoryModel(SyncthingConnection &connection, QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
m_connection(connection),
|
||||
m_dirs(connection.dirInfo()),
|
||||
m_unknownIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-disconnected.svg"))),
|
||||
m_idleIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-ok.svg"))),
|
||||
m_syncIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-sync.svg"))),
|
||||
m_errorIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-error.svg"))),
|
||||
m_pausedIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-pause.svg"))),
|
||||
m_otherIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-default.svg")))
|
||||
{
|
||||
connect(&m_connection, &SyncthingConnection::newConfig, this, &SyncthingDirectoryModel::newConfig);
|
||||
connect(&m_connection, &SyncthingConnection::newDirs, this, &SyncthingDirectoryModel::newDirs);
|
||||
connect(&m_connection, &SyncthingConnection::dirStatusChanged, this, &SyncthingDirectoryModel::dirStatusChanged);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the directory info for the spcified \a index. The returned object is not persistent.
|
||||
*/
|
||||
const SyncthingDir *SyncthingDirectoryModel::dirInfo(const QModelIndex &index) const
|
||||
{
|
||||
return (index.parent().isValid() ? dirInfo(index.parent()) : (index.row() < m_dirs.size() ? &m_dirs[index.row()] : nullptr));
|
||||
}
|
||||
|
||||
QModelIndex SyncthingDirectoryModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid()) {
|
||||
// top-level: all dir labels/IDs
|
||||
if(row < rowCount(parent)) {
|
||||
return createIndex(row, column, -1);
|
||||
}
|
||||
} else if(!parent.parent().isValid()) {
|
||||
// dir-level: dir attributes
|
||||
if(row < rowCount(parent)) {
|
||||
return createIndex(row, column, parent.row());
|
||||
}
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex SyncthingDirectoryModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
return child.internalId() != static_cast<quintptr>(-1) ? index(child.internalId(), 0, QModelIndex()) : QModelIndex();
|
||||
}
|
||||
|
||||
QVariant SyncthingDirectoryModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
switch(orientation) {
|
||||
case Qt::Horizontal:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
switch(section) {
|
||||
case 0: return tr("ID");
|
||||
case 1: return tr("Status");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant SyncthingDirectoryModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if(index.isValid()) {
|
||||
if(index.parent().isValid()) {
|
||||
// dir attributes
|
||||
if(index.parent().row() < m_dirs.size()) {
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0: // attribute names
|
||||
switch(index.row()) {
|
||||
case 0: return tr("ID");
|
||||
case 1: return tr("Path");
|
||||
case 2: return tr("Devices");
|
||||
case 3: return tr("Read-only");
|
||||
case 4: return tr("Rescan interval");
|
||||
}
|
||||
break;
|
||||
case 1: // attribute values
|
||||
const SyncthingDir &dir = m_dirs[index.parent().row()];
|
||||
switch(index.row()) {
|
||||
case 0: return dir.id;
|
||||
case 1: return dir.path;
|
||||
case 2: return dir.devices.join(QStringLiteral(", "));
|
||||
case 3: return dir.readOnly ? tr("yes") : tr("no");
|
||||
case 4: return QStringLiteral("%1 s").arg(dir.rescanInterval);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
} else if(index.row() < m_dirs.size()) {
|
||||
// dir IDs and status
|
||||
const SyncthingDir &dir = m_dirs[index.row()];
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch(index.column()) {
|
||||
case 0: return dir.label.isEmpty() ? dir.id : dir.label;
|
||||
case 1:
|
||||
switch(dir.status) {
|
||||
case DirStatus::Unknown: return tr("Unknown status");
|
||||
case DirStatus::Idle: return tr("Idle");
|
||||
case DirStatus::Scanning: return dir.progressPercentage > 0 ? tr("Scanning (%1 %)").arg(dir.progressPercentage) : tr("Scanning");
|
||||
case DirStatus::Synchronizing: return dir.progressPercentage > 0 ? tr("Synchronizing (%1 %)").arg(dir.progressPercentage) : tr("Synchronizing");
|
||||
case DirStatus::Paused: return tr("Paused");
|
||||
case DirStatus::OutOfSync: return tr("Out of sync");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::DecorationRole:
|
||||
switch(index.column()) {
|
||||
case 0:
|
||||
switch(dir.status) {
|
||||
case DirStatus::Unknown: return m_unknownIcon;
|
||||
case DirStatus::Idle: return m_idleIcon;
|
||||
case DirStatus::Scanning: return m_otherIcon;
|
||||
case DirStatus::Synchronizing: return m_syncIcon;
|
||||
case DirStatus::Paused: return m_pausedIcon;
|
||||
case DirStatus::OutOfSync: return m_errorIcon;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Qt::TextAlignmentRole:
|
||||
switch(index.column()) {
|
||||
case 0: break;
|
||||
case 1: return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);
|
||||
}
|
||||
break;
|
||||
case Qt::ForegroundRole:
|
||||
switch(index.column()) {
|
||||
case 0: break;
|
||||
case 1:
|
||||
switch(dir.status) {
|
||||
case DirStatus::Unknown: break;
|
||||
case DirStatus::Idle: return QColor(Qt::darkGreen);
|
||||
case DirStatus::Scanning: return QColor(Qt::blue);
|
||||
case DirStatus::Synchronizing: return QColor(Qt::blue);
|
||||
case DirStatus::Paused: break;
|
||||
case DirStatus::OutOfSync: return QColor(Qt::red);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool SyncthingDirectoryModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int SyncthingDirectoryModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid()) {
|
||||
return m_dirs.size();
|
||||
} else if(!parent.parent().isValid()) {
|
||||
return 5;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int SyncthingDirectoryModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid()) {
|
||||
return 2; // label/ID, status/buttons
|
||||
} else if(!parent.parent().isValid()) {
|
||||
return 2; // field name and value
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingDirectoryModel::newConfig()
|
||||
{
|
||||
beginResetModel();
|
||||
}
|
||||
|
||||
void SyncthingDirectoryModel::newDirs()
|
||||
{
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void SyncthingDirectoryModel::dirStatusChanged(const SyncthingDir &, int index)
|
||||
{
|
||||
const QModelIndex modelIndex1(this->index(index, 0, QModelIndex()));
|
||||
emit dataChanged(modelIndex1, modelIndex1, QVector<int>() << Qt::DecorationRole);
|
||||
const QModelIndex modelIndex2(this->index(index, 1, QModelIndex()));
|
||||
emit dataChanged(modelIndex2, modelIndex2, QVector<int>() << Qt::DisplayRole << Qt::ForegroundRole);
|
||||
}
|
||||
|
||||
} // namespace Data
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef DATA_SYNCTHINGDIRECTORYMODEL_H
|
||||
#define DATA_SYNCTHINGDIRECTORYMODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QIcon>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Data {
|
||||
|
||||
class SyncthingConnection;
|
||||
struct SyncthingDir;
|
||||
|
||||
class SyncthingDirectoryModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SyncthingDirectoryModel(SyncthingConnection &connection, QObject *parent = nullptr);
|
||||
|
||||
public Q_SLOTS:
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
|
||||
QModelIndex parent(const QModelIndex &child) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
int columnCount(const QModelIndex &parent) const;
|
||||
const SyncthingDir *dirInfo(const QModelIndex &index) const;
|
||||
|
||||
private slots:
|
||||
void newConfig();
|
||||
void newDirs();
|
||||
void dirStatusChanged(const SyncthingDir &, int index);
|
||||
|
||||
private:
|
||||
Data::SyncthingConnection &m_connection;
|
||||
const std::vector<SyncthingDir> &m_dirs;
|
||||
const QIcon m_unknownIcon;
|
||||
const QIcon m_idleIcon;
|
||||
const QIcon m_syncIcon;
|
||||
const QIcon m_errorIcon;
|
||||
const QIcon m_pausedIcon;
|
||||
const QIcon m_otherIcon;
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
|
||||
#endif // DATA_SYNCTHINGDIRECTORYMODEL_H
|
|
@ -0,0 +1,192 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtGui::ConnectionOptionPage</class>
|
||||
<widget class="QWidget" name="QtGui::ConnectionOptionPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>433</width>
|
||||
<height>267</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Connection</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="urlLabel">
|
||||
<property name="text">
|
||||
<string>Syncthing URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Widgets::ClearLineEdit" name="urlLineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="authLabel">
|
||||
<property name="text">
|
||||
<string>Authentication</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="authCheckBox"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="userNameLabel">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>User</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="passwordLabel">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Widgets::ClearLineEdit" name="userNameLineEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="passwordLineEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="text">
|
||||
<string>disconnected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="statusTextLabel">
|
||||
<property name="text">
|
||||
<string>Status</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<widget class="QPushButton" name="connectPushButton">
|
||||
<property name="text">
|
||||
<string>Apply connection settings and try to reconnect</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="Widgets::ClearLineEdit" name="apiKeyLineEdit"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="apiKeyLabel">
|
||||
<property name="text">
|
||||
<string>API key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Widgets::ClearLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header location="global">qtutilities/widgets/clearlineedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>authCheckBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>userNameLabel</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>authCheckBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>userNameLineEdit</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>authCheckBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>passwordLabel</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>authCheckBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>passwordLineEdit</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>20</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -0,0 +1,48 @@
|
|||
#include "./devview.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMouseEvent>
|
||||
#include <QMenu>
|
||||
#include <QCursor>
|
||||
#include <QGuiApplication>
|
||||
#include <QClipboard>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
DevView::DevView(QWidget *parent) :
|
||||
QTreeView(parent)
|
||||
{
|
||||
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
header()->hide();
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &DevView::customContextMenuRequested, this, &DevView::showContextMenu);
|
||||
}
|
||||
|
||||
void DevView::showContextMenu()
|
||||
{
|
||||
if(selectionModel() && selectionModel()->selectedRows(0).size() == 1) {
|
||||
QMenu menu;
|
||||
connect(menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), tr("Copy")), &QAction::triggered, this, &DevView::copySelectedItem);
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
void DevView::copySelectedItem()
|
||||
{
|
||||
if(selectionModel() && selectionModel()->selectedRows(0).size() == 1) {
|
||||
const QModelIndex selectedIndex = selectionModel()->selectedRows(0).at(0);
|
||||
QString text;
|
||||
if(selectedIndex.parent().isValid()) {
|
||||
// dev attribute
|
||||
text = model()->data(model()->index(selectedIndex.row(), 1, selectedIndex.parent())).toString();
|
||||
} else {
|
||||
// dev name/id
|
||||
text = model()->data(selectedIndex).toString();
|
||||
}
|
||||
if(!text.isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef DEVVIEW_H
|
||||
#define DEVVIEW_H
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class DevView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DevView(QWidget *parent = nullptr);
|
||||
|
||||
private Q_SLOTS:
|
||||
void showContextMenu();
|
||||
void copySelectedItem();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DEVVIEW_H
|
|
@ -0,0 +1,60 @@
|
|||
#include "./dirbuttonsitemdelegate.h"
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QApplication>
|
||||
#include <QStyle>
|
||||
#include <QTextOption>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include <QBrush>
|
||||
#include <QPalette>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
inline int centerObj(int avail, int size)
|
||||
{
|
||||
return (avail - size) / 2;
|
||||
}
|
||||
|
||||
DirButtonsItemDelegate::DirButtonsItemDelegate(QObject* parent) :
|
||||
QStyledItemDelegate(parent),
|
||||
m_refreshIcon(QIcon::fromTheme(QStringLiteral("view-refresh")).pixmap(QSize(16, 16))),
|
||||
m_folderIcon(QIcon::fromTheme(QStringLiteral("folder-open")).pixmap(QSize(16, 16)))
|
||||
{}
|
||||
|
||||
void DirButtonsItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
// use the customization only on top-level rows
|
||||
if(index.parent().isValid()) {
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
} else {
|
||||
// init style options to use drawControl(), except for the text
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
opt.text.clear();
|
||||
opt.features = QStyleOptionViewItem::None;
|
||||
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
|
||||
|
||||
// draw text
|
||||
QRectF textRect = option.rect;
|
||||
textRect.setWidth(textRect.width() - 38);
|
||||
QTextOption textOption;
|
||||
textOption.setAlignment(opt.displayAlignment);
|
||||
painter->setFont(opt.font);
|
||||
painter->setPen(opt.palette.color(QPalette::Text));
|
||||
painter->drawText(textRect, displayText(index.data(Qt::DisplayRole), option.locale), textOption);
|
||||
|
||||
// draw buttons
|
||||
const int buttonY = option.rect.y() + centerObj(option.rect.height(), 16);
|
||||
painter->drawPixmap(option.rect.right() - 34, buttonY, 16, 16, m_refreshIcon);
|
||||
painter->drawPixmap(option.rect.right() - 16, buttonY, 16, 16, m_folderIcon);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//QSize DirButtonsItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
//{
|
||||
// return QSize(16, 16);
|
||||
//}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef DIRBUTTONSITEMDELEGATE_H
|
||||
#define DIRBUTTONSITEMDELEGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QPixmap>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class DirButtonsItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DirButtonsItemDelegate(QObject *parent);
|
||||
|
||||
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const;
|
||||
//QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
private:
|
||||
const QPixmap m_refreshIcon;
|
||||
const QPixmap m_folderIcon;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DIRBUTTONSITEMDELEGATE_H
|
|
@ -0,0 +1,67 @@
|
|||
#include "./dirview.h"
|
||||
#include "./dirbuttonsitemdelegate.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMouseEvent>
|
||||
#include <QMenu>
|
||||
#include <QCursor>
|
||||
#include <QGuiApplication>
|
||||
#include <QClipboard>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
DirView::DirView(QWidget *parent) :
|
||||
QTreeView(parent)
|
||||
{
|
||||
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
header()->hide();
|
||||
setItemDelegateForColumn(1, new DirButtonsItemDelegate(this));
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &DirView::customContextMenuRequested, this, &DirView::showContextMenu);
|
||||
}
|
||||
|
||||
void DirView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
QTreeView::mouseReleaseEvent(event);
|
||||
const QPoint pos(event->pos());
|
||||
const QModelIndex clickedIndex(indexAt(event->pos()));
|
||||
if(clickedIndex.isValid() && clickedIndex.column() == 1 && !clickedIndex.parent().isValid()) {
|
||||
const QRect itemRect(visualRect(clickedIndex));
|
||||
if(pos.x() > itemRect.right() - 34) {
|
||||
if(pos.x() > itemRect.right() - 17) {
|
||||
emit openDir(clickedIndex);
|
||||
} else {
|
||||
emit scanDir(clickedIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DirView::showContextMenu()
|
||||
{
|
||||
if(selectionModel() && selectionModel()->selectedRows(0).size() == 1) {
|
||||
QMenu menu;
|
||||
connect(menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), tr("Copy")), &QAction::triggered, this, &DirView::copySelectedItem);
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
void DirView::copySelectedItem()
|
||||
{
|
||||
if(selectionModel() && selectionModel()->selectedRows(0).size() == 1) {
|
||||
const QModelIndex selectedIndex = selectionModel()->selectedRows(0).at(0);
|
||||
QString text;
|
||||
if(selectedIndex.parent().isValid()) {
|
||||
// dev attribute
|
||||
text = model()->data(model()->index(selectedIndex.row(), 1, selectedIndex.parent())).toString();
|
||||
} else {
|
||||
// dev label/id
|
||||
text = model()->data(selectedIndex).toString();
|
||||
}
|
||||
if(!text.isEmpty()) {
|
||||
QGuiApplication::clipboard()->setText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef DIRVIEW_H
|
||||
#define DIRVIEW_H
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class DirView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DirView(QWidget *parent = nullptr);
|
||||
|
||||
Q_SIGNALS:
|
||||
void openDir(const QModelIndex &index);
|
||||
void scanDir(const QModelIndex &index);
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void showContextMenu();
|
||||
void copySelectedItem();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DIRVIEW_H
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtGui::LauncherOptionPage</class>
|
||||
<widget class="QWidget" name="QtGui::LauncherOptionPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>229</width>
|
||||
<height>164</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Launcher</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtGui::NotificationsOptionPage</class>
|
||||
<widget class="QWidget" name="QtGui::NotificationsOptionPage">
|
||||
<property name="windowTitle">
|
||||
<string>Notifications</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="notifyOnDisconnectCheckBox">
|
||||
<property name="text">
|
||||
<string>Notify on disconnect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="notifyOnErrorsCheckBox">
|
||||
<property name="text">
|
||||
<string>Notify on errors</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="notifyOnSyncCompleteCheckBox">
|
||||
<property name="text">
|
||||
<string>Notify on sync complete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showSyncthingNotificationsCheckBox">
|
||||
<property name="text">
|
||||
<string>Show Syncthing notifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,214 @@
|
|||
#include "./settingsdialog.h"
|
||||
|
||||
#include "../application/settings.h"
|
||||
#include "../data/syncthingconnection.h"
|
||||
|
||||
#include "ui_connectionoptionpage.h"
|
||||
#include "ui_notificationsoptionpage.h"
|
||||
#include "ui_launcheroptionpage.h"
|
||||
#include "ui_webviewoptionpage.h"
|
||||
|
||||
#include <tagparser/mediafileinfo.h>
|
||||
#include <tagparser/backuphelper.h>
|
||||
|
||||
#include <qtutilities/settingsdialog/optioncategory.h>
|
||||
#include <qtutilities/settingsdialog/optioncategorymodel.h>
|
||||
#include <qtutilities/settingsdialog/qtsettings.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
using namespace Settings;
|
||||
using namespace Dialogs;
|
||||
using namespace Data;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
// ConnectionOptionPage
|
||||
ConnectionOptionPage::ConnectionOptionPage(Data::SyncthingConnection *connection, QWidget *parentWidget) :
|
||||
ConnectionOptionPageBase(parentWidget),
|
||||
m_connection(connection)
|
||||
{}
|
||||
|
||||
ConnectionOptionPage::~ConnectionOptionPage()
|
||||
{}
|
||||
|
||||
QWidget *ConnectionOptionPage::setupWidget()
|
||||
{
|
||||
auto *w = ConnectionOptionPageBase::setupWidget();
|
||||
updateConnectionStatus();
|
||||
QObject::connect(m_connection, &SyncthingConnection::statusChanged, bind(&ConnectionOptionPage::updateConnectionStatus, this));
|
||||
QObject::connect(ui()->connectPushButton, &QPushButton::clicked, bind(&ConnectionOptionPage::applyAndReconnect, this));
|
||||
return w;
|
||||
}
|
||||
|
||||
void ConnectionOptionPage::updateConnectionStatus()
|
||||
{
|
||||
ui()->statusLabel->setText(m_connection->statusText());
|
||||
}
|
||||
|
||||
bool ConnectionOptionPage::apply()
|
||||
{
|
||||
if(hasBeenShown()) {
|
||||
syncthingUrl() = ui()->urlLineEdit->text();
|
||||
authEnabled() = ui()->authCheckBox->isChecked();
|
||||
userName() = ui()->userNameLineEdit->text();
|
||||
password() = ui()->passwordLineEdit->text();
|
||||
apiKey() = ui()->apiKeyLineEdit->text().toUtf8();
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectionOptionPage::reset()
|
||||
{
|
||||
if(hasBeenShown()) {
|
||||
ui()->urlLineEdit->setText(syncthingUrl());
|
||||
ui()->authCheckBox->setChecked(authEnabled());
|
||||
ui()->userNameLineEdit->setText(userName());
|
||||
ui()->passwordLineEdit->setText(password());
|
||||
ui()->apiKeyLineEdit->setText(apiKey());
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionOptionPage::applyAndReconnect()
|
||||
{
|
||||
apply();
|
||||
m_connection->setSyncthingUrl(Settings::syncthingUrl());
|
||||
m_connection->setApiKey(Settings::apiKey());
|
||||
if(Settings::authEnabled()) {
|
||||
m_connection->setCredentials(Settings::userName(), Settings::password());
|
||||
} else {
|
||||
m_connection->setCredentials(QString(), QString());
|
||||
}
|
||||
m_connection->reconnect();
|
||||
}
|
||||
|
||||
// NotificationsOptionPage
|
||||
NotificationsOptionPage::NotificationsOptionPage(QWidget *parentWidget) :
|
||||
NotificationsOptionPageBase(parentWidget)
|
||||
{}
|
||||
|
||||
NotificationsOptionPage::~NotificationsOptionPage()
|
||||
{}
|
||||
|
||||
bool NotificationsOptionPage::apply()
|
||||
{
|
||||
if(hasBeenShown()) {
|
||||
notifyOnDisconnect() = ui()->notifyOnDisconnectCheckBox->isChecked();
|
||||
notifyOnErrors() = ui()->notifyOnErrorsCheckBox->isChecked();
|
||||
notifyOnSyncComplete() = ui()->notifyOnSyncCompleteCheckBox->isChecked();
|
||||
showSyncthingNotifications() = ui()->showSyncthingNotificationsCheckBox->isChecked();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NotificationsOptionPage::reset()
|
||||
{
|
||||
if(hasBeenShown()) {
|
||||
ui()->notifyOnDisconnectCheckBox->setChecked(notifyOnDisconnect());
|
||||
ui()->notifyOnErrorsCheckBox->setChecked(notifyOnErrors());
|
||||
ui()->notifyOnSyncCompleteCheckBox->setChecked(notifyOnSyncComplete());
|
||||
ui()->showSyncthingNotificationsCheckBox->setChecked(showSyncthingNotifications());
|
||||
}
|
||||
}
|
||||
|
||||
// LauncherOptionPage
|
||||
LauncherOptionPage::LauncherOptionPage(QWidget *parentWidget) :
|
||||
LauncherOptionPageBase(parentWidget)
|
||||
{}
|
||||
|
||||
LauncherOptionPage::~LauncherOptionPage()
|
||||
{}
|
||||
|
||||
bool LauncherOptionPage::apply()
|
||||
{
|
||||
if(hasBeenShown()) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LauncherOptionPage::reset()
|
||||
{
|
||||
if(hasBeenShown()) {
|
||||
}
|
||||
}
|
||||
|
||||
// WebViewOptionPage
|
||||
WebViewOptionPage::WebViewOptionPage(QWidget *parentWidget) :
|
||||
WebViewOptionPageBase(parentWidget)
|
||||
{}
|
||||
|
||||
WebViewOptionPage::~WebViewOptionPage()
|
||||
{}
|
||||
|
||||
#if !defined(SYNCTHINGTRAY_USE_WEBENGINE) && !defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
QWidget *WebViewOptionPage::setupWidget()
|
||||
{
|
||||
auto *label = new QLabel;
|
||||
label->setWindowTitle(QCoreApplication::translate("QtGui::WebViewOptionPage", "General"));
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
label->setText(QCoreApplication::translate("QtGui::WebViewOptionPage", "Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine.\nThe Web UI will be opened in the default web browser instead."));
|
||||
return label;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WebViewOptionPage::apply()
|
||||
{
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
if(hasBeenShown()) {
|
||||
webViewDisabled() = ui()->disableCheckBox->isChecked();
|
||||
webViewZoomFactor() = ui()->zoomDoubleSpinBox->value();
|
||||
webViewKeepRunning() = ui()->keepRunningCheckBox->isChecked();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebViewOptionPage::reset()
|
||||
{
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
if(hasBeenShown()) {
|
||||
ui()->disableCheckBox->setChecked(webViewDisabled());
|
||||
ui()->zoomDoubleSpinBox->setValue(webViewZoomFactor());
|
||||
ui()->keepRunningCheckBox->setChecked(webViewKeepRunning());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SettingsDialog::SettingsDialog(Data::SyncthingConnection *connection, QWidget *parent) :
|
||||
Dialogs::SettingsDialog(parent)
|
||||
{
|
||||
// setup categories
|
||||
QList<Dialogs::OptionCategory *> categories;
|
||||
Dialogs::OptionCategory *category;
|
||||
|
||||
category = new OptionCategory(this);
|
||||
category->setDisplayName(tr("Tray"));
|
||||
category->assignPages(QList<Dialogs::OptionPage *>()
|
||||
<< new ConnectionOptionPage(connection) << new NotificationsOptionPage
|
||||
<< new LauncherOptionPage);
|
||||
category->setIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
|
||||
categories << category;
|
||||
|
||||
category = new OptionCategory(this);
|
||||
category->setDisplayName(tr("Web view"));
|
||||
category->assignPages(QList<Dialogs::OptionPage *>() << new WebViewOptionPage);
|
||||
category->setIcon(QIcon::fromTheme(QStringLiteral("internet-web-browser"), QIcon(QStringLiteral(":/icons/hicolor/scalable/app/"))));
|
||||
categories << category;
|
||||
|
||||
categories << Settings::qtSettings().category();
|
||||
|
||||
categoryModel()->setCategories(categories);
|
||||
|
||||
setMinimumSize(800, 450);
|
||||
setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-other")));
|
||||
|
||||
// some settings could be applied without restarting the application, good idea?
|
||||
//connect(this, &Dialogs::SettingsDialog::applied, bind(&Dialogs::QtSettings::apply, &Settings::qtSettings()));
|
||||
}
|
||||
|
||||
SettingsDialog::~SettingsDialog()
|
||||
{}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef SETTINGS_DIALOG_H
|
||||
#define SETTINGS_DIALOG_H
|
||||
|
||||
#include <qtutilities/settingsdialog/settingsdialog.h>
|
||||
#include <qtutilities/settingsdialog/optionpage.h>
|
||||
#include <qtutilities/settingsdialog/qtsettings.h>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Settings {
|
||||
class KnownFieldModel;
|
||||
class TargetLevelModel;
|
||||
}
|
||||
|
||||
namespace Data {
|
||||
class SyncthingConnection;
|
||||
}
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(ConnectionOptionPage)
|
||||
public:
|
||||
ConnectionOptionPage(Data::SyncthingConnection *connection, QWidget *parentWidget = nullptr);
|
||||
private:
|
||||
DECLARE_SETUP_WIDGETS
|
||||
void updateConnectionStatus();
|
||||
void applyAndReconnect();
|
||||
Data::SyncthingConnection *m_connection;
|
||||
END_DECLARE_OPTION_PAGE
|
||||
|
||||
DECLARE_UI_FILE_BASED_OPTION_PAGE(NotificationsOptionPage)
|
||||
|
||||
DECLARE_UI_FILE_BASED_OPTION_PAGE(LauncherOptionPage)
|
||||
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
DECLARE_UI_FILE_BASED_OPTION_PAGE(WebViewOptionPage)
|
||||
#else
|
||||
DECLARE_OPTION_PAGE(WebViewOptionPage)
|
||||
#endif
|
||||
|
||||
class SettingsDialog : public Dialogs::SettingsDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsDialog(Data::SyncthingConnection *connection, QWidget *parent = nullptr);
|
||||
~SettingsDialog();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SETTINGS_DIALOG_H
|
|
@ -0,0 +1,455 @@
|
|||
#include "./tray.h"
|
||||
#include "./settingsdialog.h"
|
||||
#include "./webviewdialog.h"
|
||||
#include "./dirbuttonsitemdelegate.h"
|
||||
|
||||
#include "../application/settings.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include "ui_traywidget.h"
|
||||
|
||||
#include <qtutilities/resources/qtconfigarguments.h>
|
||||
#include <qtutilities/resources/resources.h>
|
||||
#include <qtutilities/resources/importplugin.h>
|
||||
#include <qtutilities/settingsdialog/qtsettings.h>
|
||||
#include <qtutilities/aboutdialog/aboutdialog.h>
|
||||
#include <qtutilities/misc/dialogutils.h>
|
||||
#include <qtutilities/misc/desktoputils.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSvgRenderer>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QMenu>
|
||||
#include <QDesktopServices>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QCursor>
|
||||
#include <QDesktopWidget>
|
||||
#include <QLabel>
|
||||
#include <QClipboard>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
#include <QTextBrowser>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace ApplicationUtilities;
|
||||
using namespace Dialogs;
|
||||
using namespace Data;
|
||||
using namespace std;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
/*!
|
||||
* \brief Instantiates a new tray widget.
|
||||
*/
|
||||
TrayWidget::TrayWidget(TrayMenu *parent) :
|
||||
QWidget(parent),
|
||||
m_menu(parent),
|
||||
m_ui(new Ui::TrayWidget),
|
||||
m_settingsDlg(nullptr),
|
||||
m_aboutDlg(nullptr),
|
||||
#ifndef SYNCTHINGTRAY_NO_WEBVIEW
|
||||
m_webViewDlg(nullptr),
|
||||
#endif
|
||||
m_dirModel(m_connection),
|
||||
m_devModel(m_connection)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
// setup model and view
|
||||
m_ui->dirsTreeView->setModel(&m_dirModel);
|
||||
m_ui->devsTreeView->setModel(&m_devModel);
|
||||
|
||||
// apply settings, this also establishes the connection to Syncthing
|
||||
applySettings();
|
||||
|
||||
// setup sync-all button
|
||||
auto *cornerFrame = new QFrame(this);
|
||||
cornerFrame->setFrameStyle(QFrame::StyledPanel), cornerFrame->setFrameShadow(QFrame::Sunken);
|
||||
auto *cornerFrameLayout = new QHBoxLayout(cornerFrame);
|
||||
cornerFrameLayout->setSpacing(0), cornerFrameLayout->setMargin(0);
|
||||
cornerFrameLayout->addStretch();
|
||||
cornerFrame->setLayout(cornerFrameLayout);
|
||||
auto *viewIdButton = new QPushButton(cornerFrame);
|
||||
viewIdButton->setToolTip(tr("View own device ID"));
|
||||
viewIdButton->setIcon(QIcon::fromTheme(QStringLiteral("view-barcode")));
|
||||
viewIdButton->setFlat(true);
|
||||
connect(viewIdButton, &QPushButton::clicked, this, &TrayWidget::showOwnDeviceId);
|
||||
cornerFrameLayout->addWidget(viewIdButton);
|
||||
auto *showLogButton = new QPushButton(cornerFrame);
|
||||
showLogButton->setToolTip(tr("Show Syncthing log"));
|
||||
showLogButton->setIcon(QIcon::fromTheme(QStringLiteral("text-plain")));
|
||||
showLogButton->setFlat(true);
|
||||
connect(showLogButton, &QPushButton::clicked, this, &TrayWidget::showLog);
|
||||
cornerFrameLayout->addWidget(showLogButton);
|
||||
auto *scanAllButton = new QPushButton(cornerFrame);
|
||||
scanAllButton->setToolTip(tr("Rescan all directories"));
|
||||
scanAllButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-sync")));
|
||||
scanAllButton->setFlat(true);
|
||||
connect(scanAllButton, &QPushButton::clicked, &m_connection, &SyncthingConnection::rescanAllDirs);
|
||||
cornerFrameLayout->addWidget(scanAllButton);
|
||||
m_ui->tabWidget->setCornerWidget(cornerFrame, Qt::BottomRightCorner);
|
||||
|
||||
// connect signals and slots
|
||||
connect(m_ui->statusPushButton, &QPushButton::clicked, this, &TrayWidget::handleStatusButtonClicked);
|
||||
connect(m_ui->closePushButton, &QPushButton::clicked, &QApplication::quit);
|
||||
connect(m_ui->aboutPushButton, &QPushButton::clicked, this, &TrayWidget::showAboutDialog);
|
||||
connect(m_ui->webUiPushButton, &QPushButton::clicked, this, &TrayWidget::showWebUi);
|
||||
connect(m_ui->settingsPushButton, &QPushButton::clicked, this, &TrayWidget::showSettingsDialog);
|
||||
connect(&m_connection, &SyncthingConnection::statusChanged, this, &TrayWidget::updateStatusButton);
|
||||
connect(m_ui->dirsTreeView, &DirView::openDir, this, &TrayWidget::openDir);
|
||||
connect(m_ui->dirsTreeView, &DirView::scanDir, this, &TrayWidget::scanDir);
|
||||
}
|
||||
|
||||
TrayWidget::~TrayWidget()
|
||||
{}
|
||||
|
||||
void TrayWidget::showSettingsDialog()
|
||||
{
|
||||
if(!m_settingsDlg) {
|
||||
m_settingsDlg = new SettingsDialog(&m_connection, this);
|
||||
m_settingsDlg->setWindowTitle(tr("Settings - Syncthing tray"));
|
||||
connect(m_settingsDlg, &SettingsDialog::applied, this, &TrayWidget::applySettings);
|
||||
#ifndef SYNCTHINGTRAY_NO_WEBVIEW
|
||||
if(m_webViewDlg) {
|
||||
connect(m_settingsDlg, &SettingsDialog::applied, m_webViewDlg, &WebViewDialog::applySettings);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
m_settingsDlg->show();
|
||||
centerWidget(m_settingsDlg);
|
||||
if(m_menu) {
|
||||
m_menu->close();
|
||||
}
|
||||
m_settingsDlg->activateWindow();
|
||||
}
|
||||
|
||||
void TrayWidget::showAboutDialog()
|
||||
{
|
||||
if(!m_aboutDlg) {
|
||||
m_aboutDlg = new AboutDialog(this, tr("Tray application for Syncthing"), QImage(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
|
||||
m_aboutDlg->setWindowTitle(tr("About - Syncthing Tray"));
|
||||
m_aboutDlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
|
||||
}
|
||||
m_aboutDlg->show();
|
||||
centerWidget(m_aboutDlg);
|
||||
if(m_menu) {
|
||||
m_menu->close();
|
||||
}
|
||||
m_aboutDlg->activateWindow();
|
||||
}
|
||||
|
||||
void TrayWidget::showWebUi()
|
||||
{
|
||||
#ifndef SYNCTHINGTRAY_NO_WEBVIEW
|
||||
if(Settings::webViewDisabled()) {
|
||||
#endif
|
||||
QDesktopServices::openUrl(Settings::syncthingUrl());
|
||||
#ifndef SYNCTHINGTRAY_NO_WEBVIEW
|
||||
} else {
|
||||
if(!m_webViewDlg) {
|
||||
m_webViewDlg = new WebViewDialog(this);
|
||||
connect(m_webViewDlg, &WebViewDialog::destroyed, this, &TrayWidget::handleWebViewDeleted);
|
||||
if(m_settingsDlg) {
|
||||
connect(m_settingsDlg, &SettingsDialog::applied, m_webViewDlg, &WebViewDialog::applySettings);
|
||||
}
|
||||
}
|
||||
m_webViewDlg->show();
|
||||
if(m_menu) {
|
||||
m_menu->close();
|
||||
}
|
||||
m_webViewDlg->activateWindow();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TrayWidget::showOwnDeviceId()
|
||||
{
|
||||
auto *dlg = new QDialog(this);
|
||||
dlg->setWindowTitle(tr("Own device ID - Syncthing Tray"));
|
||||
dlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dlg->setBackgroundRole(QPalette::Background);
|
||||
auto *layout = new QVBoxLayout(dlg);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
auto *pixmapLabel = new QLabel(dlg);
|
||||
pixmapLabel->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(pixmapLabel);
|
||||
auto *textLabel = new QLabel(dlg);
|
||||
textLabel->setText(m_connection.myId().isEmpty() ? tr("device ID is unknown") : m_connection.myId());
|
||||
QFont defaultFont = textLabel->font();
|
||||
defaultFont.setBold(true);
|
||||
defaultFont.setPointSize(defaultFont.pointSize() + 2);
|
||||
textLabel->setFont(defaultFont);
|
||||
textLabel->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(textLabel);
|
||||
auto *copyPushButton = new QPushButton(dlg);
|
||||
copyPushButton->setText(tr("Copy to clipboard"));
|
||||
connect(copyPushButton, &QPushButton::clicked, bind(&QClipboard::setText, QGuiApplication::clipboard(), m_connection.myId(), QClipboard::Clipboard));
|
||||
layout->addWidget(copyPushButton);
|
||||
m_connection.requestQrCode(m_connection.myId(), bind(&QLabel::setPixmap, pixmapLabel, placeholders::_1));
|
||||
dlg->setLayout(layout);
|
||||
dlg->show();
|
||||
centerWidget(dlg);
|
||||
if(m_menu) {
|
||||
m_menu->close();
|
||||
}
|
||||
dlg->activateWindow();
|
||||
}
|
||||
|
||||
void TrayWidget::showLog()
|
||||
{
|
||||
auto *dlg = new QDialog(this);
|
||||
dlg->setWindowTitle(tr("Log - Syncthing"));
|
||||
dlg->setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
auto *layout = new QVBoxLayout(dlg);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
auto *browser = new QTextBrowser(dlg);
|
||||
m_connection.requestLog([browser] (const std::vector<SyncthingLogEntry> &entries) {
|
||||
for(const SyncthingLogEntry &entry : entries) {
|
||||
browser->append(entry.when % QChar(':') % QChar(' ') % QChar('\n') % entry.message % QChar('\n'));
|
||||
}
|
||||
});
|
||||
layout->addWidget(browser);
|
||||
dlg->setLayout(layout);
|
||||
dlg->show();
|
||||
dlg->resize(600, 500);
|
||||
centerWidget(dlg);
|
||||
if(m_menu) {
|
||||
m_menu->close();
|
||||
}
|
||||
dlg->activateWindow();
|
||||
}
|
||||
|
||||
void TrayWidget::updateStatusButton(SyncthingStatus status)
|
||||
{
|
||||
switch(status) {
|
||||
case SyncthingStatus::Disconnected:
|
||||
m_ui->statusPushButton->setText(tr("Connect"));
|
||||
m_ui->statusPushButton->setToolTip(tr("Not connected to Syncthing, click to connect"));
|
||||
m_ui->statusPushButton->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
|
||||
break;
|
||||
case SyncthingStatus::Default:
|
||||
case SyncthingStatus::NotificationsAvailable:
|
||||
case SyncthingStatus::Synchronizing:
|
||||
m_ui->statusPushButton->setText(tr("Pause"));
|
||||
m_ui->statusPushButton->setToolTip(tr("Syncthing is doing its job, click to pause"));
|
||||
m_ui->statusPushButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause")));
|
||||
break;
|
||||
case SyncthingStatus::Paused:
|
||||
m_ui->statusPushButton->setText(tr("Continue"));
|
||||
m_ui->statusPushButton->setToolTip(tr("Syncthing is suspended, click to continue"));
|
||||
m_ui->statusPushButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TrayWidget::applySettings()
|
||||
{
|
||||
m_connection.setSyncthingUrl(Settings::syncthingUrl());
|
||||
m_connection.setApiKey(Settings::apiKey());
|
||||
if(Settings::authEnabled()) {
|
||||
m_connection.setCredentials(Settings::userName(), Settings::password());
|
||||
} else {
|
||||
m_connection.setCredentials(QString(), QString());
|
||||
}
|
||||
m_connection.reconnect();
|
||||
}
|
||||
|
||||
void TrayWidget::openDir(const QModelIndex &index)
|
||||
{
|
||||
if(const SyncthingDir *dir = m_dirModel.dirInfo(index)) {
|
||||
if(QDir(dir->path).exists()) {
|
||||
DesktopUtils::openLocalFileOrDir(dir->path);
|
||||
} else {
|
||||
QMessageBox::warning(this, QCoreApplication::applicationName(), tr("The directly <i>%1</i> does not exist on the local machine.").arg(dir->path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrayWidget::scanDir(const QModelIndex &index)
|
||||
{
|
||||
if(const SyncthingDir *dir = m_dirModel.dirInfo(index)) {
|
||||
m_connection.rescan(dir->id);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayWidget::handleStatusButtonClicked()
|
||||
{
|
||||
switch(m_connection.status()) {
|
||||
case SyncthingStatus::Disconnected:
|
||||
m_connection.connect();
|
||||
break;
|
||||
case SyncthingStatus::Default:
|
||||
case SyncthingStatus::NotificationsAvailable:
|
||||
case SyncthingStatus::Synchronizing:
|
||||
m_connection.pauseAllDevs();
|
||||
break;
|
||||
case SyncthingStatus::Paused:
|
||||
m_connection.resumeAllDevs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TrayWidget::handleWebViewDeleted()
|
||||
{
|
||||
m_webViewDlg = nullptr;
|
||||
}
|
||||
|
||||
TrayMenu::TrayMenu(QWidget *parent) :
|
||||
QMenu(parent)
|
||||
{
|
||||
auto *menuLayout = new QHBoxLayout;
|
||||
menuLayout->setMargin(0), menuLayout->setSpacing(0);
|
||||
menuLayout->addWidget(m_trayWidget = new TrayWidget(this));
|
||||
setLayout(menuLayout);
|
||||
setPlatformMenu(nullptr);
|
||||
}
|
||||
|
||||
QSize TrayMenu::sizeHint() const
|
||||
{
|
||||
return QSize(350, 300);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Instantiates a new tray icon.
|
||||
*/
|
||||
TrayIcon::TrayIcon(QObject *parent) :
|
||||
QSystemTrayIcon(parent),
|
||||
m_size(QSize(128, 128)),
|
||||
m_statusIconDisconnected(QIcon(renderSvgImage(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-disconnected.svg")))),
|
||||
m_statusIconDefault(QIcon(renderSvgImage(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-default.svg")))),
|
||||
m_statusIconNotify(QIcon(renderSvgImage(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-notify.svg")))),
|
||||
m_statusIconPause(QIcon(renderSvgImage(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-pause.svg")))),
|
||||
m_statusIconSync(QIcon(renderSvgImage(QStringLiteral(":/icons/hicolor/scalable/status/syncthing-sync.svg")))),
|
||||
m_status(SyncthingStatus::Disconnected)
|
||||
{
|
||||
// set context menu
|
||||
connect(m_contextMenu.addAction(QIcon::fromTheme(QStringLiteral("internet-web-browser")), tr("Web UI")), &QAction::triggered, m_trayMenu.widget(), &TrayWidget::showWebUi);
|
||||
connect(m_contextMenu.addAction(QIcon::fromTheme(QStringLiteral("preferences-other")), tr("Settings")), &QAction::triggered, m_trayMenu.widget(), &TrayWidget::showSettingsDialog);
|
||||
connect(m_contextMenu.addAction(QIcon::fromTheme(QStringLiteral("help-about")), tr("About")), &QAction::triggered, m_trayMenu.widget(), &TrayWidget::showAboutDialog);
|
||||
m_contextMenu.addSeparator();
|
||||
connect(m_contextMenu.addAction(QIcon::fromTheme(QStringLiteral("window-close")), tr("Close")), &QAction::triggered, &QCoreApplication::quit);
|
||||
setContextMenu(&m_contextMenu);
|
||||
|
||||
// set initial status
|
||||
updateStatusIconAndText(SyncthingStatus::Disconnected);
|
||||
|
||||
// connect signals and slots
|
||||
SyncthingConnection *connection = &(m_trayMenu.widget()->connection());
|
||||
connect(this, &TrayIcon::activated, this, &TrayIcon::handleActivated);
|
||||
connect(connection, &SyncthingConnection::error, this, &TrayIcon::showSyncthingError);
|
||||
connect(connection, &SyncthingConnection::newNotification, this, &TrayIcon::showSyncthingNotification);
|
||||
connect(connection, &SyncthingConnection::statusChanged, this, &TrayIcon::updateStatusIconAndText);
|
||||
}
|
||||
|
||||
void TrayIcon::handleActivated(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
switch(reason) {
|
||||
case QSystemTrayIcon::Context:
|
||||
// can't catch that event on Plasma 5 anyways
|
||||
break;
|
||||
case QSystemTrayIcon::Trigger:
|
||||
// either show web UI or context menu
|
||||
if(false) {
|
||||
m_trayMenu.widget()->showWebUi();
|
||||
} else {
|
||||
// when showing the menu manually
|
||||
// move the menu to the closest of the currently available screen
|
||||
// this implies that the tray icon is located near the edge of the screen; otherwise this behavior makes no sense
|
||||
const QPoint cursorPos(QCursor::pos());
|
||||
const QRect availableGeometry(QApplication::desktop()->availableGeometry(cursorPos));
|
||||
Qt::Alignment alignment = 0;
|
||||
alignment |= (cursorPos.x() - availableGeometry.left() < availableGeometry.right() - cursorPos.x() ? Qt::AlignLeft : Qt::AlignRight);
|
||||
alignment |= (cursorPos.y() - availableGeometry.top() < availableGeometry.bottom() - cursorPos.y() ? Qt::AlignTop : Qt::AlignBottom);
|
||||
m_trayMenu.setGeometry(
|
||||
QStyle::alignedRect(
|
||||
Qt::LeftToRight,
|
||||
alignment,
|
||||
m_trayMenu.sizeHint(),
|
||||
availableGeometry
|
||||
)
|
||||
);
|
||||
m_trayMenu.show();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIcon::showSyncthingError(const QString &errorMsg)
|
||||
{
|
||||
if(Settings::notifyOnErrors()) {
|
||||
showMessage(tr("Syncthing error"), errorMsg, QSystemTrayIcon::Critical);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIcon::showSyncthingNotification(const QString &message)
|
||||
{
|
||||
if(Settings::showSyncthingNotifications()) {
|
||||
showMessage(tr("Syncthing notification"), message, QSystemTrayIcon::Information);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIcon::updateStatusIconAndText(SyncthingStatus status)
|
||||
{
|
||||
switch(status) {
|
||||
case SyncthingStatus::Disconnected:
|
||||
setIcon(m_statusIconDisconnected);
|
||||
setToolTip(tr("Not connected to Syncthing"));
|
||||
if(Settings::notifyOnDisconnect()) {
|
||||
showMessage(QCoreApplication::applicationName(), tr("Disconnected from Syncthing"), QSystemTrayIcon::Warning);
|
||||
}
|
||||
break;
|
||||
case SyncthingStatus::Default:
|
||||
setIcon(m_statusIconDefault);
|
||||
setToolTip(tr("Syncthing is running"));
|
||||
break;
|
||||
case SyncthingStatus::NotificationsAvailable:
|
||||
setIcon(m_statusIconNotify);
|
||||
setToolTip(tr("Notifications available"));
|
||||
break;
|
||||
case SyncthingStatus::Paused:
|
||||
setIcon(m_statusIconPause);
|
||||
setToolTip(tr("Syncthing has been suspended"));
|
||||
break;
|
||||
case SyncthingStatus::Synchronizing:
|
||||
setIcon(m_statusIconSync);
|
||||
setToolTip(tr("Synchronization is ongoing"));
|
||||
break;
|
||||
}
|
||||
switch(status) {
|
||||
case SyncthingStatus::Disconnected:
|
||||
case SyncthingStatus::Synchronizing:
|
||||
break;
|
||||
default:
|
||||
if(m_status == SyncthingStatus::Synchronizing && Settings::notifyOnSyncComplete()) {
|
||||
showMessage(QCoreApplication::applicationName(), tr("Synchronization complete"), QSystemTrayIcon::Information);
|
||||
}
|
||||
}
|
||||
|
||||
m_status = status;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Renders an SVG image to a QPixmap.
|
||||
* \remarks If instantiating QIcon directly from SVG image the icon is not displayed under Plasma 5. It would work
|
||||
* with Tint2, tough.
|
||||
*/
|
||||
QPixmap TrayIcon::renderSvgImage(const QString &path)
|
||||
{
|
||||
QSvgRenderer renderer(path);
|
||||
QPixmap pm(m_size);
|
||||
pm.fill(QColor(Qt::transparent));
|
||||
QPainter painter(&pm);
|
||||
renderer.render(&painter, pm.rect());
|
||||
return pm;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
#ifndef TRAY_H
|
||||
#define TRAY_H
|
||||
|
||||
#include "./webviewprovider.h"
|
||||
#include "../data/syncthingconnection.h"
|
||||
#include "../data/syncthingdirectorymodel.h"
|
||||
#include "../data/syncthingdevicemodel.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QMenu>
|
||||
|
||||
#include <memory>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QString)
|
||||
QT_FORWARD_DECLARE_CLASS(QPixmap)
|
||||
|
||||
namespace ApplicationUtilities {
|
||||
class QtConfigArguments;
|
||||
}
|
||||
namespace Dialogs {
|
||||
class AboutDialog;
|
||||
}
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class WebViewDialog;
|
||||
class SettingsDialog;
|
||||
class TrayMenu;
|
||||
namespace Ui {
|
||||
class TrayWidget;
|
||||
}
|
||||
|
||||
class TrayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TrayWidget(TrayMenu *parent = nullptr);
|
||||
~TrayWidget();
|
||||
|
||||
Data::SyncthingConnection &connection();
|
||||
|
||||
public slots:
|
||||
void showSettingsDialog();
|
||||
void showAboutDialog();
|
||||
void showWebUi();
|
||||
void showOwnDeviceId();
|
||||
void showLog();
|
||||
|
||||
private slots:
|
||||
void updateStatusButton(Data::SyncthingStatus status);
|
||||
void applySettings();
|
||||
void openDir(const QModelIndex &index);
|
||||
void scanDir(const QModelIndex &index);
|
||||
void handleStatusButtonClicked();
|
||||
void handleWebViewDeleted();
|
||||
|
||||
private:
|
||||
TrayMenu *m_menu;
|
||||
std::unique_ptr<Ui::TrayWidget> m_ui;
|
||||
SettingsDialog *m_settingsDlg;
|
||||
Dialogs::AboutDialog *m_aboutDlg;
|
||||
#ifndef SYNCTHINGTRAY_NO_WEBVIEW
|
||||
WebViewDialog *m_webViewDlg;
|
||||
#endif
|
||||
Data::SyncthingConnection m_connection;
|
||||
Data::SyncthingDirectoryModel m_dirModel;
|
||||
Data::SyncthingDeviceModel m_devModel;
|
||||
};
|
||||
|
||||
inline Data::SyncthingConnection &TrayWidget::connection()
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
class TrayMenu : public QMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TrayMenu(QWidget *parent = nullptr);
|
||||
|
||||
QSize sizeHint() const;
|
||||
TrayWidget *widget();
|
||||
|
||||
private:
|
||||
TrayWidget *m_trayWidget;
|
||||
};
|
||||
|
||||
inline TrayWidget *TrayMenu::widget()
|
||||
{
|
||||
return m_trayWidget;
|
||||
}
|
||||
|
||||
class TrayIcon : public QSystemTrayIcon
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TrayIcon(QObject *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void handleActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
void showSyncthingError(const QString &errorMsg);
|
||||
void showSyncthingNotification(const QString &message);
|
||||
void updateStatusIconAndText(Data::SyncthingStatus status);
|
||||
|
||||
private:
|
||||
QPixmap renderSvgImage(const QString &path);
|
||||
|
||||
const QSize m_size;
|
||||
const QIcon m_statusIconDisconnected;
|
||||
const QIcon m_statusIconDefault;
|
||||
const QIcon m_statusIconNotify;
|
||||
const QIcon m_statusIconPause;
|
||||
const QIcon m_statusIconSync;
|
||||
TrayMenu m_trayMenu;
|
||||
QMenu m_contextMenu;
|
||||
Data::SyncthingStatus m_status;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TRAY_H
|
|
@ -0,0 +1,248 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtGui::TrayWidget</class>
|
||||
<widget class="QWidget" name="QtGui::TrayWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="buttonsFrame">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closePushButton">
|
||||
<property name="toolTip">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="window-close">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="aboutPushButton">
|
||||
<property name="toolTip">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="help-about">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="statusPushButton">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="view-refresh">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="settingsPushButton">
|
||||
<property name="toolTip">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="preferences-other">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="webUiPushButton">
|
||||
<property name="toolTip">
|
||||
<string>Web UI</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="internet-web-browser">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::South</enum>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Triangular</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="dirsTab">
|
||||
<attribute name="icon">
|
||||
<iconset theme="folder">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Directories</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="dirsTabVerticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QtGui::DirView" name="dirsTreeView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="devsTab">
|
||||
<attribute name="icon">
|
||||
<iconset theme="network-server">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Devices</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="devsTabVerticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QtGui::DevView" name="devsTreeView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QtGui::DirView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>./gui/dirview.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QtGui::DevView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>./gui/devview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,88 @@
|
|||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
#include "./webpage.h"
|
||||
|
||||
#include "../application/settings.h"
|
||||
#include "../data/syncthingconnection.h"
|
||||
|
||||
#include "resources/config.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QAuthenticator>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE)
|
||||
# include <QWebEngineSettings>
|
||||
# include <QWebEngineView>
|
||||
#elif defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
# include <QWebSettings>
|
||||
# include <QWebView>
|
||||
# include <QWebFrame>
|
||||
#endif
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
WebPage::WebPage(WEB_VIEW_PROVIDER *view) :
|
||||
WEB_PAGE_PROVIDER(view),
|
||||
m_view(view)
|
||||
{
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE)
|
||||
settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
|
||||
connect(this, &WebPage::authenticationRequired, this, static_cast<void(WebPage::*)(const QUrl &, QAuthenticator *)>(&WebPage::supplyCredentials));
|
||||
#else
|
||||
settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
|
||||
setNetworkAccessManager(&Data::networkAccessManager());
|
||||
connect(&Data::networkAccessManager(), &QNetworkAccessManager::authenticationRequired, this, static_cast<void(WebPage::*)(QNetworkReply *, QAuthenticator *)>(&WebPage::supplyCredentials));
|
||||
#endif
|
||||
if(!m_view) {
|
||||
// delegate to external browser if no view is assigned
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE)
|
||||
connect(this, &WebPage::urlChanged, this, &WebPage::delegateToExternalBrowser);
|
||||
#else
|
||||
connect(this->mainFrame(), &QWebFrame::urlChanged, this, &WebPage::delegateToExternalBrowser);
|
||||
#endif
|
||||
m_view = new WEB_VIEW_PROVIDER;
|
||||
m_view->setPage(this);
|
||||
}
|
||||
}
|
||||
|
||||
WEB_PAGE_PROVIDER *WebPage::createWindow(WEB_PAGE_PROVIDER::WebWindowType type)
|
||||
{
|
||||
return new WebPage;
|
||||
}
|
||||
|
||||
void WebPage::delegateToExternalBrowser(const QUrl &url)
|
||||
{
|
||||
openUrlExternal(url);
|
||||
// this page and the associated view are useless
|
||||
m_view->deleteLater();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void WebPage::supplyCredentials(const QUrl &requestUrl, QAuthenticator *authenticator)
|
||||
{
|
||||
Q_UNUSED(requestUrl)
|
||||
supplyCredentials(authenticator);
|
||||
}
|
||||
|
||||
void WebPage::supplyCredentials(QNetworkReply *reply, QAuthenticator *authenticator)
|
||||
{
|
||||
Q_UNUSED(reply)
|
||||
supplyCredentials(authenticator);
|
||||
}
|
||||
|
||||
void WebPage::supplyCredentials(QAuthenticator *authenticator)
|
||||
{
|
||||
if(Settings::authEnabled()) {
|
||||
authenticator->setUser(Settings::userName());
|
||||
authenticator->setPassword(Settings::password());
|
||||
}
|
||||
}
|
||||
|
||||
void WebPage::openUrlExternal(const QUrl &url)
|
||||
{
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef WEBPAGE_H
|
||||
#define WEBPAGE_H
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
|
||||
#include "./webviewprovider.h"
|
||||
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE)
|
||||
# include <QWebEnginePage>
|
||||
#elif defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
# include <QWebPage>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(WEB_VIEW_PROVIDER)
|
||||
QT_FORWARD_DECLARE_CLASS(QAuthenticator)
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class WebPage : public WEB_PAGE_PROVIDER
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebPage(WEB_VIEW_PROVIDER *view = nullptr);
|
||||
|
||||
public slots:
|
||||
void openUrlExternal(const QUrl &url);
|
||||
|
||||
protected:
|
||||
WEB_PAGE_PROVIDER *createWindow(WebWindowType type);
|
||||
|
||||
private slots:
|
||||
void delegateToExternalBrowser(const QUrl &url);
|
||||
void supplyCredentials(const QUrl &requestUrl, QAuthenticator *authenticator);
|
||||
void supplyCredentials(QNetworkReply *reply, QAuthenticator *authenticator);
|
||||
void supplyCredentials(QAuthenticator *authenticator);
|
||||
|
||||
private:
|
||||
WEB_VIEW_PROVIDER *m_view;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // defined(SYNCTHINGTRAY_USE_WEBENGINE) || defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
#endif // WEBPAGE_H
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef SYNCTHINGTRAY_NO_WEBVIEW
|
||||
#include "./webviewdialog.h"
|
||||
#include "./webpage.h"
|
||||
|
||||
#include "../application/settings.h"
|
||||
#include "../data/syncthingconnection.h"
|
||||
|
||||
#include <qtutilities/misc/dialogutils.h>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QCloseEvent>
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE)
|
||||
# include <QWebEngineView>
|
||||
#elif defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
# include <QWebView>
|
||||
#endif
|
||||
|
||||
using namespace Dialogs;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
WebViewDialog::WebViewDialog(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
m_view(new WEB_VIEW_PROVIDER(this))
|
||||
{
|
||||
setWindowTitle(tr("Syncthing"));
|
||||
setWindowIcon(QIcon(QStringLiteral(":/icons/hicolor/scalable/app/syncthingtray.svg")));
|
||||
setCentralWidget(m_view);
|
||||
|
||||
m_view->setPage(new WebPage(m_view));
|
||||
|
||||
applySettings();
|
||||
|
||||
if(Settings::webViewGeometry().isEmpty()) {
|
||||
resize(1200, 800);
|
||||
centerWidget(this);
|
||||
} else {
|
||||
restoreGeometry(Settings::webViewGeometry());
|
||||
}
|
||||
}
|
||||
|
||||
QtGui::WebViewDialog::~WebViewDialog()
|
||||
{
|
||||
Settings::webViewGeometry() = saveGeometry();
|
||||
}
|
||||
|
||||
void QtGui::WebViewDialog::applySettings()
|
||||
{
|
||||
m_view->setUrl(Settings::syncthingUrl());
|
||||
m_view->setZoomFactor(Settings::webViewZoomFactor());
|
||||
}
|
||||
|
||||
void QtGui::WebViewDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if(!Settings::webViewKeepRunning()) {
|
||||
deleteLater();
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef WEBVIEW_DIALOG_H
|
||||
#define WEBVIEW_DIALOG_H
|
||||
#ifndef SYNCTHINGTRAY_NO_WEBVIEW
|
||||
|
||||
#include "./webviewprovider.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(WEB_VIEW_PROVIDER)
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class WebViewDialog : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WebViewDialog(QWidget *parent = nullptr);
|
||||
~WebViewDialog();
|
||||
|
||||
public slots:
|
||||
void applySettings();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
private:
|
||||
WEB_VIEW_PROVIDER *m_view;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // WEBVIEW_DIALOG_H
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QtGui::WebViewOptionPage</class>
|
||||
<widget class="QWidget" name="QtGui::WebViewOptionPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>356</width>
|
||||
<height>98</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="usageLabel">
|
||||
<property name="text">
|
||||
<string>Usage</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="disableCheckBox">
|
||||
<property name="text">
|
||||
<string>Disable web view (open regular web browser instead)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="zoomLabel">
|
||||
<property name="text">
|
||||
<string>Zoom factor</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="zoomDoubleSpinBox">
|
||||
<property name="buttonSymbols">
|
||||
<enum>QAbstractSpinBox::PlusMinus</enum>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>5.990000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.200000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="keepRunningLabel">
|
||||
<property name="text">
|
||||
<string>Hiding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="keepRunningCheckBox">
|
||||
<property name="text">
|
||||
<string>Keep web view running when currently not shown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef WEB_VIEW_PROVIDER
|
||||
|
||||
#if defined(SYNCTHINGTRAY_USE_WEBENGINE)
|
||||
# define WEB_VIEW_PROVIDER QWebEngineView
|
||||
# define WEB_PAGE_PROVIDER QWebEnginePage
|
||||
#elif defined(SYNCTHINGTRAY_USE_WEBKIT)
|
||||
# define WEB_VIEW_PROVIDER QWebView
|
||||
# define WEB_PAGE_PROVIDER QWebPage
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/hicolor/scalable/app/syncthingtray.svg</file>
|
||||
<file>icons/hicolor/scalable/status/syncthing-default.svg</file>
|
||||
<file>icons/hicolor/scalable/status/syncthing-notify.svg</file>
|
||||
<file>icons/hicolor/scalable/status/syncthing-pause.svg</file>
|
||||
<file>icons/hicolor/scalable/status/syncthing-sync.svg</file>
|
||||
<file>icons/hicolor/scalable/status/syncthing-disconnected.svg</file>
|
||||
<file>icons/hicolor/scalable/status/syncthing-ok.svg</file>
|
||||
<file>icons/hicolor/scalable/status/syncthing-error.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 117.3 117.3" enable-background="new 0 0 117.3 117.3" xml:space="preserve">
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="58.666" y1="117.332" x2="58.666" y2="0">
|
||||
<stop offset="0" style="stop-color:#0882C8"/>
|
||||
<stop offset="1" style="stop-color:#26B6DB"/>
|
||||
</linearGradient>
|
||||
<circle fill="url(#SVGID_1_)" cx="58.7" cy="58.7" r="58.7"/>
|
||||
<g>
|
||||
<circle fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" cx="58.7" cy="58.5" r="43.7"/>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M94.7,47.8c4.7,1.6,9.8-0.9,11.4-5.6c1.6-4.7-0.9-9.8-5.6-11.4c-4.7-1.6-9.8,0.9-11.4,5.6
|
||||
C87.5,41.1,90,46.2,94.7,47.8z"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="97.6" y1="39.4" x2="67.5" y2="64.4"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M77.6,91c-0.4,4.9,3.2,9.3,8.2,9.8c5,0.4,9.3-3.2,9.8-8.2c0.4-4.9-3.2-9.3-8.2-9.8
|
||||
C82.4,82.4,78,86,77.6,91z"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="86.5" y1="91.8" x2="67.5" y2="64.4"/>
|
||||
</g>
|
||||
<path fill="#FFFFFF" d="M60,69.3c2.7,4.2,8.3,5.4,12.4,2.7c4.2-2.7,5.4-8.3,2.7-12.4c-2.7-4.2-8.3-5.4-12.4-2.7
|
||||
C58.5,59.5,57.3,65.1,60,69.3z"/>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M21.2,61.4c-4.3-2.5-9.8-1.1-12.3,3.1c-2.5,4.3-1.1,9.8,3.1,12.3c4.3,2.5,9.8,1.1,12.3-3.1
|
||||
C26.8,69.5,25.4,64,21.2,61.4z"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="10" x1="16.6" y1="69.1" x2="67.5" y2="64.4"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="8" y1="0" x2="8" y2="16">
|
||||
<stop offset="0" style="stop-color:#26B6DB"/>
|
||||
<stop offset="1" style="stop-color:#0882C8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="syncthing-logo">
|
||||
<circle id="outer" cx="8" cy="8" r="8" style="fill:url(#grad)"/>
|
||||
<circle id="inner" cx="8" cy="7.9727402" r="5.9557071" style="fill:none;stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-l" x1="9.1993189" y1="8.776825" x2="2.262351" y2="9.4173737" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-tr" x1="9.1993189" y1="8.776825" x2="13.301533" y2="5.3696747" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-br" x1="9.1993189" y1="8.776825" x2="11.788756" y2="12.51107" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<circle id="node-c" cx="9.1993189" cy="8.776825" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-l" cx="2.262351" cy="9.4173737" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-tr" cx="13.301533" cy="5.3696747" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-br" cx="11.788756" cy="12.51107" r="1.22" style="fill:#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="8" y1="0" x2="8" y2="16">
|
||||
<stop offset="0" style="stop-color:#888888"/>
|
||||
<stop offset="1" style="stop-color:#5E6061"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="syncthing-logo">
|
||||
<circle id="outer" cx="8" cy="8" r="8" style="fill:url(#grad)"/>
|
||||
<circle id="inner" cx="8" cy="7.9727402" r="5.9557071" style="fill:none;stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-l" x1="9.1993189" y1="8.776825" x2="2.262351" y2="9.4173737" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-tr" x1="9.1993189" y1="8.776825" x2="13.301533" y2="5.3696747" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-br" x1="9.1993189" y1="8.776825" x2="11.788756" y2="12.51107" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<circle id="node-c" cx="9.1993189" cy="8.776825" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-l" cx="2.262351" cy="9.4173737" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-tr" cx="13.301533" cy="5.3696747" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-br" cx="11.788756" cy="12.51107" r="1.22" style="fill:#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="8" y1="0" x2="8" y2="16">
|
||||
<stop offset="0" style="stop-color:#DB3C26"/>
|
||||
<stop offset="1" style="stop-color:#C80828"/>
|
||||
</linearGradient>
|
||||
<mask id="bitemask" maskUnits="userSpaceOnUse">
|
||||
<g>
|
||||
<rect id="mask-bg" x="0" y="0" width="16" height="16" style="fill:#ffffff"/>
|
||||
<circle id="mask-subtract" cx="11.5" cy="11.5" r="5.5" style="fill:#000000"/>
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<g id="syncthing-logo" mask="url(#bitemask)">
|
||||
<circle id="outer" cx="8" cy="8" r="8" style="fill:url(#grad)"/>
|
||||
<circle id="inner" cx="8" cy="7.9727402" r="5.9557071" style="fill:none;stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-l" x1="9.1993189" y1="8.776825" x2="2.262351" y2="9.4173737" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-tr" x1="9.1993189" y1="8.776825" x2="13.301533" y2="5.3696747" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-br" x1="9.1993189" y1="8.776825" x2="11.788756" y2="12.51107" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<circle id="node-c" cx="9.1993189" cy="8.776825" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-l" cx="2.262351" cy="9.4173737" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-tr" cx="13.301533" cy="5.3696747" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-br" cx="11.788756" cy="12.51107" r="1.22" style="fill:#ffffff"/>
|
||||
</g>
|
||||
<circle id="bubble" cx="11.5" cy="11.5" r="4.5" style="fill:#000000"/>
|
||||
<g id="exclaim">
|
||||
<rect id="exclaim-top" x="11" y="9" width="1" height="3" style="fill:#ffffff"/>
|
||||
<rect id="exclaim-bottom" x="11" y="13" width="1" height="1" style="fill:#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="8" y1="0" x2="8" y2="16">
|
||||
<stop offset="0" style="stop-color:#26B6DB"/>
|
||||
<stop offset="1" style="stop-color:#0882C8"/>
|
||||
</linearGradient>
|
||||
<mask id="bitemask" maskUnits="userSpaceOnUse">
|
||||
<g>
|
||||
<rect id="mask-bg" x="0" y="0" width="16" height="16" style="fill:#ffffff"/>
|
||||
<circle id="mask-subtract" cx="11.5" cy="11.5" r="5.5" style="fill:#000000"/>
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<g id="syncthing-logo" mask="url(#bitemask)">
|
||||
<circle id="outer" cx="8" cy="8" r="8" style="fill:url(#grad)"/>
|
||||
<circle id="inner" cx="8" cy="7.9727402" r="5.9557071" style="fill:none;stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-l" x1="9.1993189" y1="8.776825" x2="2.262351" y2="9.4173737" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-tr" x1="9.1993189" y1="8.776825" x2="13.301533" y2="5.3696747" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-br" x1="9.1993189" y1="8.776825" x2="11.788756" y2="12.51107" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<circle id="node-c" cx="9.1993189" cy="8.776825" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-l" cx="2.262351" cy="9.4173737" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-tr" cx="13.301533" cy="5.3696747" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-br" cx="11.788756" cy="12.51107" r="1.22" style="fill:#ffffff"/>
|
||||
</g>
|
||||
<circle id="bubble" cx="11.5" cy="11.5" r="4.5" style="fill:#000000"/>
|
||||
<g id="exclaim">
|
||||
<rect id="exclaim-top" x="11" y="9" width="1" height="3" style="fill:#ffffff"/>
|
||||
<rect id="exclaim-bottom" x="11" y="13" width="1" height="1" style="fill:#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="8" y1="0" x2="8" y2="16">
|
||||
<stop offset="0" style="stop-color:#26DBA4"/>
|
||||
<stop offset="1" style="stop-color:#08C86F"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="syncthing-logo">
|
||||
<circle id="outer" cx="8" cy="8" r="8" style="fill:url(#grad)"/>
|
||||
<circle id="inner" cx="8" cy="7.9727402" r="5.9557071" style="fill:none;stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-l" x1="9.1993189" y1="8.776825" x2="2.262351" y2="9.4173737" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-tr" x1="9.1993189" y1="8.776825" x2="13.301533" y2="5.3696747" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-br" x1="9.1993189" y1="8.776825" x2="11.788756" y2="12.51107" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<circle id="node-c" cx="9.1993189" cy="8.776825" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-l" cx="2.262351" cy="9.4173737" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-tr" cx="13.301533" cy="5.3696747" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-br" cx="11.788756" cy="12.51107" r="1.22" style="fill:#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="8" y1="0" x2="8" y2="16">
|
||||
<stop offset="0" style="stop-color:#26B6DB"/>
|
||||
<stop offset="1" style="stop-color:#0882C8"/>
|
||||
</linearGradient>
|
||||
<mask id="bitemask" maskUnits="userSpaceOnUse">
|
||||
<g>
|
||||
<rect id="mask-bg" x="0" y="0" width="16" height="16" style="fill:#ffffff"/>
|
||||
<circle id="mask-subtract" cx="11.5" cy="11.5" r="5.5" style="fill:#000000"/>
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<g id="syncthing-logo" mask="url(#bitemask)">
|
||||
<circle id="outer" cx="8" cy="8" r="8" style="fill:url(#grad)"/>
|
||||
<circle id="inner" cx="8" cy="7.9727402" r="5.9557071" style="fill:none;stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-l" x1="9.1993189" y1="8.776825" x2="2.262351" y2="9.4173737" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-tr" x1="9.1993189" y1="8.776825" x2="13.301533" y2="5.3696747" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-br" x1="9.1993189" y1="8.776825" x2="11.788756" y2="12.51107" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<circle id="node-c" cx="9.1993189" cy="8.776825" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-l" cx="2.262351" cy="9.4173737" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-tr" cx="13.301533" cy="5.3696747" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-br" cx="11.788756" cy="12.51107" r="1.22" style="fill:#ffffff"/>
|
||||
</g>
|
||||
<circle id="bubble" cx="11.5" cy="11.5" r="4.5" style="fill:#000000"/>
|
||||
<g id="pause">
|
||||
<rect id="pause-leftbar" x="10" y="9" width="1" height="5" style="fill:#ffffff"/>
|
||||
<rect id="pause-rightbar" x="12" y="9" width="1" height="5" style="fill:#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs>
|
||||
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="8" y1="0" x2="8" y2="16">
|
||||
<stop offset="0" style="stop-color:#26B6DB"/>
|
||||
<stop offset="1" style="stop-color:#0882C8"/>
|
||||
</linearGradient>
|
||||
<mask id="bitemask" maskUnits="userSpaceOnUse">
|
||||
<g>
|
||||
<rect id="mask-bg" x="0" y="0" width="16" height="16" style="fill:#ffffff"/>
|
||||
<circle id="mask-subtract" cx="11.5" cy="11.5" r="5.5" style="fill:#000000"/>
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<g id="syncthing-logo" mask="url(#bitemask)">
|
||||
<circle id="outer" cx="8" cy="8" r="8" style="fill:url(#grad)"/>
|
||||
<circle id="inner" cx="8" cy="7.9727402" r="5.9557071" style="fill:none;stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-l" x1="9.1993189" y1="8.776825" x2="2.262351" y2="9.4173737" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-tr" x1="9.1993189" y1="8.776825" x2="13.301533" y2="5.3696747" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<line id="arm-br" x1="9.1993189" y1="8.776825" x2="11.788756" y2="12.51107" style="stroke:#ffffff;stroke-width:0.81771719"/>
|
||||
<circle id="node-c" cx="9.1993189" cy="8.776825" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-l" cx="2.262351" cy="9.4173737" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-tr" cx="13.301533" cy="5.3696747" r="1.22" style="fill:#ffffff"/>
|
||||
<circle id="node-br" cx="11.788756" cy="12.51107" r="1.22" style="fill:#ffffff"/>
|
||||
</g>
|
||||
<circle id="bubble" cx="11.5" cy="11.5" r="4.5" style="fill:#000000"/>
|
||||
<g id="arrows" transform="rotate(0 11.5 11.5)">
|
||||
<path id="arrow-left" d="m 11.5,14 0,-1 c -1.5,0 -1.5,0 -1.5,-2 l 1,0 -1.5,-2 -1.5,2 1,0 c 0,3 0,3 2.5,3 z" style="fill:#ffffff" />
|
||||
<path id="arrow-right" d="m 11.5,9 0,1 c 1.5,0 1.5,0 1.5,2 l -1,0 1.5,2 1.5,-2 -1,0 C 14,9 14,9 11.5,9 Z" style="fill:#ffffff" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 116 KiB |
|
@ -0,0 +1,614 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="de_DE">
|
||||
<context>
|
||||
<name>Data::SyncthingConnection</name>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="65"/>
|
||||
<source>disconnected</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="67"/>
|
||||
<source>connected</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="69"/>
|
||||
<source>connected, notifications available</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="71"/>
|
||||
<source>connected, paused</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="73"/>
|
||||
<source>connected, synchronizing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="75"/>
|
||||
<source>unknown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="302"/>
|
||||
<source>Unable to parse Syncthing log: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="306"/>
|
||||
<source>Unable to request system log: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="332"/>
|
||||
<location filename="../data/syncthingconnection.cpp" line="415"/>
|
||||
<source>Unable to parse Syncthing config: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="337"/>
|
||||
<location filename="../data/syncthingconnection.cpp" line="420"/>
|
||||
<source>Unable to request Syncthing config: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="455"/>
|
||||
<source>Unable to parse Syncthing events: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="478"/>
|
||||
<source>Unable to request Syncthing events: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="550"/>
|
||||
<source>Unable to request rescan: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="566"/>
|
||||
<source>Unable to request pause/resume: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="275"/>
|
||||
<source>Unable to request QR-Code: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Data::SyncthingDeviceModel</name>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="56"/>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="82"/>
|
||||
<source>ID</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="57"/>
|
||||
<source>Status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="83"/>
|
||||
<source>Addresses</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="84"/>
|
||||
<source>Compression</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="85"/>
|
||||
<source>Certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="86"/>
|
||||
<source>Introducer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="95"/>
|
||||
<source>none</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="96"/>
|
||||
<source>yes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="96"/>
|
||||
<source>no</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="115"/>
|
||||
<source>Unknown status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="116"/>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="117"/>
|
||||
<source>Idle</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="118"/>
|
||||
<source>Synchronizing (%1 %)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="118"/>
|
||||
<source>Synchronizing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="119"/>
|
||||
<source>Paused</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="120"/>
|
||||
<source>Out of sync</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Data::SyncthingDirectoryModel</name>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="58"/>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="84"/>
|
||||
<source>ID</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="59"/>
|
||||
<source>Status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="85"/>
|
||||
<source>Path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="86"/>
|
||||
<source>Devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="88"/>
|
||||
<source>Rescan interval</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/>
|
||||
<source>yes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/>
|
||||
<source>no</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="118"/>
|
||||
<source>Idle</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/>
|
||||
<source>Scanning (%1 %)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/>
|
||||
<source>Synchronizing (%1 %)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="122"/>
|
||||
<source>Out of sync</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/>
|
||||
<source>Scanning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="87"/>
|
||||
<source>Read-only</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="117"/>
|
||||
<source>Unknown status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/>
|
||||
<source>Synchronizing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="121"/>
|
||||
<source>Paused</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::ConnectionOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="14"/>
|
||||
<source>Connection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="20"/>
|
||||
<source>Syncthing URL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="30"/>
|
||||
<source>Authentication</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="43"/>
|
||||
<source>User</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="84"/>
|
||||
<source>disconnected</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="91"/>
|
||||
<source>Status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="98"/>
|
||||
<source>Apply connection settings and try to reconnect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="112"/>
|
||||
<source>API key</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="53"/>
|
||||
<source>Password</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::DevView</name>
|
||||
<message>
|
||||
<location filename="../gui/devview.cpp" line="25"/>
|
||||
<source>Copy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::DirView</name>
|
||||
<message>
|
||||
<location filename="../gui/dirview.cpp" line="44"/>
|
||||
<source>Copy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::LauncherOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/launcheroptionpage.ui" line="14"/>
|
||||
<source>Launcher</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::NotificationsOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="6"/>
|
||||
<source>Notifications</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="12"/>
|
||||
<source>Notify on disconnect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="19"/>
|
||||
<source>Notify on errors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="26"/>
|
||||
<source>Notify on sync complete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="33"/>
|
||||
<source>Show Syncthing notifications</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::SettingsDialog</name>
|
||||
<message>
|
||||
<location filename="../gui/settingsdialog.cpp" line="187"/>
|
||||
<source>Tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/settingsdialog.cpp" line="195"/>
|
||||
<source>Web view</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::TrayIcon</name>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="333"/>
|
||||
<source>Web UI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="334"/>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="335"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="337"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="389"/>
|
||||
<source>Syncthing error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="396"/>
|
||||
<source>Syncthing notification</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="405"/>
|
||||
<source>Not connected to Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="407"/>
|
||||
<source>Disconnected from Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="412"/>
|
||||
<source>Syncthing is running</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="416"/>
|
||||
<source>Notifications available</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="420"/>
|
||||
<source>Syncthing has been suspended</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="424"/>
|
||||
<source>Synchronization is ongoing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="433"/>
|
||||
<source>Synchronization complete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::TrayWidget</name>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="14"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="62"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="103"/>
|
||||
<location filename="../gui/tray.cpp" line="233"/>
|
||||
<source>Connect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="178"/>
|
||||
<source>Directories</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="207"/>
|
||||
<source>Devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="76"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="130"/>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="144"/>
|
||||
<source>Web UI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="78"/>
|
||||
<source>View own device ID</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="115"/>
|
||||
<source>Settings - Syncthing tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="134"/>
|
||||
<source>Tray application for Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="90"/>
|
||||
<source>Rescan all directories</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="84"/>
|
||||
<source>Show Syncthing log</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="135"/>
|
||||
<source>About - Syncthing Tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="173"/>
|
||||
<source>Own device ID - Syncthing Tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="183"/>
|
||||
<source>device ID is unknown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="191"/>
|
||||
<source>Copy to clipboard</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="207"/>
|
||||
<source>Log - Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="234"/>
|
||||
<source>Not connected to Syncthing, click to connect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="240"/>
|
||||
<source>Pause</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="241"/>
|
||||
<source>Syncthing is doing its job, click to pause</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="245"/>
|
||||
<source>Continue</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="246"/>
|
||||
<source>Syncthing is suspended, click to continue</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="270"/>
|
||||
<source>The directly <i>%1</i> does not exist on the local machine.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::WebViewDialog</name>
|
||||
<message>
|
||||
<location filename="../gui/webviewdialog.cpp" line="26"/>
|
||||
<source>Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::WebViewOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="14"/>
|
||||
<location filename="../gui/settingsdialog.cpp" line="149"/>
|
||||
<source>General</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="20"/>
|
||||
<source>Usage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="27"/>
|
||||
<source>Disable web view (open regular web browser instead)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="64"/>
|
||||
<source>Keep web view running when currently not shown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="34"/>
|
||||
<source>Zoom factor</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="57"/>
|
||||
<source>Hiding</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/settingsdialog.cpp" line="151"/>
|
||||
<source>Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine.
|
||||
The Web UI will be opened in the default web browser instead.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>main</name>
|
||||
<message>
|
||||
<location filename="../application/main.cpp" line="50"/>
|
||||
<source>The system tray is (currently) not available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../application/main.cpp" line="54"/>
|
||||
<source>The Qt libraries have not been built with tray icon support.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
|
@ -0,0 +1,614 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="en_US">
|
||||
<context>
|
||||
<name>Data::SyncthingConnection</name>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="65"/>
|
||||
<source>disconnected</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="67"/>
|
||||
<source>connected</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="69"/>
|
||||
<source>connected, notifications available</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="71"/>
|
||||
<source>connected, paused</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="73"/>
|
||||
<source>connected, synchronizing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="75"/>
|
||||
<source>unknown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="302"/>
|
||||
<source>Unable to parse Syncthing log: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="306"/>
|
||||
<source>Unable to request system log: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="332"/>
|
||||
<location filename="../data/syncthingconnection.cpp" line="415"/>
|
||||
<source>Unable to parse Syncthing config: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="337"/>
|
||||
<location filename="../data/syncthingconnection.cpp" line="420"/>
|
||||
<source>Unable to request Syncthing config: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="455"/>
|
||||
<source>Unable to parse Syncthing events: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="478"/>
|
||||
<source>Unable to request Syncthing events: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="550"/>
|
||||
<source>Unable to request rescan: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="566"/>
|
||||
<source>Unable to request pause/resume: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingconnection.cpp" line="275"/>
|
||||
<source>Unable to request QR-Code: </source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Data::SyncthingDeviceModel</name>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="56"/>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="82"/>
|
||||
<source>ID</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="57"/>
|
||||
<source>Status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="83"/>
|
||||
<source>Addresses</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="84"/>
|
||||
<source>Compression</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="85"/>
|
||||
<source>Certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="86"/>
|
||||
<source>Introducer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="95"/>
|
||||
<source>none</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="96"/>
|
||||
<source>yes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="96"/>
|
||||
<source>no</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="115"/>
|
||||
<source>Unknown status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="116"/>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="117"/>
|
||||
<source>Idle</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="118"/>
|
||||
<source>Synchronizing (%1 %)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="118"/>
|
||||
<source>Synchronizing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="119"/>
|
||||
<source>Paused</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdevicemodel.cpp" line="120"/>
|
||||
<source>Out of sync</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Data::SyncthingDirectoryModel</name>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="58"/>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="84"/>
|
||||
<source>ID</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="59"/>
|
||||
<source>Status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="85"/>
|
||||
<source>Path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="86"/>
|
||||
<source>Devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="88"/>
|
||||
<source>Rescan interval</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/>
|
||||
<source>yes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="97"/>
|
||||
<source>no</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="118"/>
|
||||
<source>Idle</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/>
|
||||
<source>Scanning (%1 %)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/>
|
||||
<source>Synchronizing (%1 %)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="122"/>
|
||||
<source>Out of sync</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="119"/>
|
||||
<source>Scanning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="87"/>
|
||||
<source>Read-only</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="117"/>
|
||||
<source>Unknown status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="120"/>
|
||||
<source>Synchronizing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../data/syncthingdirectorymodel.cpp" line="121"/>
|
||||
<source>Paused</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::ConnectionOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="14"/>
|
||||
<source>Connection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="20"/>
|
||||
<source>Syncthing URL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="30"/>
|
||||
<source>Authentication</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="43"/>
|
||||
<source>User</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="84"/>
|
||||
<source>disconnected</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="91"/>
|
||||
<source>Status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="98"/>
|
||||
<source>Apply connection settings and try to reconnect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="112"/>
|
||||
<source>API key</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/connectionoptionpage.ui" line="53"/>
|
||||
<source>Password</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::DevView</name>
|
||||
<message>
|
||||
<location filename="../gui/devview.cpp" line="25"/>
|
||||
<source>Copy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::DirView</name>
|
||||
<message>
|
||||
<location filename="../gui/dirview.cpp" line="44"/>
|
||||
<source>Copy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::LauncherOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/launcheroptionpage.ui" line="14"/>
|
||||
<source>Launcher</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::NotificationsOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="6"/>
|
||||
<source>Notifications</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="12"/>
|
||||
<source>Notify on disconnect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="19"/>
|
||||
<source>Notify on errors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="26"/>
|
||||
<source>Notify on sync complete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/notificationsoptionpage.ui" line="33"/>
|
||||
<source>Show Syncthing notifications</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::SettingsDialog</name>
|
||||
<message>
|
||||
<location filename="../gui/settingsdialog.cpp" line="187"/>
|
||||
<source>Tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/settingsdialog.cpp" line="195"/>
|
||||
<source>Web view</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::TrayIcon</name>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="333"/>
|
||||
<source>Web UI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="334"/>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="335"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="337"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="389"/>
|
||||
<source>Syncthing error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="396"/>
|
||||
<source>Syncthing notification</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="405"/>
|
||||
<source>Not connected to Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="407"/>
|
||||
<source>Disconnected from Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="412"/>
|
||||
<source>Syncthing is running</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="416"/>
|
||||
<source>Notifications available</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="420"/>
|
||||
<source>Syncthing has been suspended</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="424"/>
|
||||
<source>Synchronization is ongoing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="433"/>
|
||||
<source>Synchronization complete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::TrayWidget</name>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="14"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="62"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="103"/>
|
||||
<location filename="../gui/tray.cpp" line="233"/>
|
||||
<source>Connect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="178"/>
|
||||
<source>Directories</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="207"/>
|
||||
<source>Devices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="76"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="130"/>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/traywidget.ui" line="144"/>
|
||||
<source>Web UI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="78"/>
|
||||
<source>View own device ID</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="115"/>
|
||||
<source>Settings - Syncthing tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="134"/>
|
||||
<source>Tray application for Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="90"/>
|
||||
<source>Rescan all directories</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="84"/>
|
||||
<source>Show Syncthing log</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="135"/>
|
||||
<source>About - Syncthing Tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="173"/>
|
||||
<source>Own device ID - Syncthing Tray</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="183"/>
|
||||
<source>device ID is unknown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="191"/>
|
||||
<source>Copy to clipboard</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="207"/>
|
||||
<source>Log - Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="234"/>
|
||||
<source>Not connected to Syncthing, click to connect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="240"/>
|
||||
<source>Pause</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="241"/>
|
||||
<source>Syncthing is doing its job, click to pause</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="245"/>
|
||||
<source>Continue</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="246"/>
|
||||
<source>Syncthing is suspended, click to continue</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/tray.cpp" line="270"/>
|
||||
<source>The directly <i>%1</i> does not exist on the local machine.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::WebViewDialog</name>
|
||||
<message>
|
||||
<location filename="../gui/webviewdialog.cpp" line="26"/>
|
||||
<source>Syncthing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtGui::WebViewOptionPage</name>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="14"/>
|
||||
<location filename="../gui/settingsdialog.cpp" line="149"/>
|
||||
<source>General</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="20"/>
|
||||
<source>Usage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="27"/>
|
||||
<source>Disable web view (open regular web browser instead)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="64"/>
|
||||
<source>Keep web view running when currently not shown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="34"/>
|
||||
<source>Zoom factor</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/webviewoptionpage.ui" line="57"/>
|
||||
<source>Hiding</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui/settingsdialog.cpp" line="151"/>
|
||||
<source>Syncthing Tray has not been built with vieb view support utilizing either Qt WebKit or Qt WebEngine.
|
||||
The Web UI will be opened in the default web browser instead.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>main</name>
|
||||
<message>
|
||||
<location filename="../application/main.cpp" line="50"/>
|
||||
<source>The system tray is (currently) not available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../application/main.cpp" line="54"/>
|
||||
<source>The Qt libraries have not been built with tray icon support.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|