diff --git a/client/client.pro b/client/client.pro index e56f69fa..1c027a04 100644 --- a/client/client.pro +++ b/client/client.pro @@ -31,6 +31,7 @@ HEADERS += \ ui/Controls/SlidingStackedWidget.h \ ui/mainwindow.h \ ui/qautostart.h \ + ui/server_widget.h \ utils.h \ vpnconnection.h \ protocols/vpnprotocol.h \ @@ -52,12 +53,14 @@ SOURCES += \ ui/Controls/SlidingStackedWidget.cpp \ ui/mainwindow.cpp \ ui/qautostart.cpp \ + ui/server_widget.cpp \ utils.cpp \ vpnconnection.cpp \ protocols/vpnprotocol.cpp \ protocols/openvpnprotocol.cpp \ -FORMS += ui/mainwindow.ui +FORMS += ui/mainwindow.ui \ + ui/server_widget.ui RESOURCES += \ resources.qrc diff --git a/client/configurators/openvpn_configurator.cpp b/client/configurators/openvpn_configurator.cpp index b9e0c0d2..89cf2c6d 100644 --- a/client/configurators/openvpn_configurator.cpp +++ b/client/configurators/openvpn_configurator.cpp @@ -215,7 +215,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia if (proto == Protocol::OpenVpn) config.replace("$PROTO", "udp"); - else if (proto == Protocol::ShadowSocks) { + else if (proto == Protocol::ShadowSocksOverOpenVpn) { config.replace("$PROTO", "tcp"); config.replace("$LOCAL_PROXY_PORT", QString::number(amnezia::protocols::shadowsocks::ssContainerPort())); } diff --git a/client/core/defs.h b/client/core/defs.h index d90947b4..9b6f85b8 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -1,25 +1,48 @@ #ifndef DEFS_H #define DEFS_H +#include #include namespace amnezia { +Q_NAMESPACE enum class Protocol { Any, OpenVpn, - ShadowSocks, + ShadowSocksOverOpenVpn, OpenVpnOverCloak, WireGuard }; +Q_ENUM_NS(Protocol) + +inline Protocol protoFromString(QString proto){ + auto&& metaEnum = QMetaEnum::fromType(); + return static_cast(metaEnum.keyToValue(proto.toStdString().c_str())); +} + +inline QString protoToString(Protocol proto){ + return QVariant::fromValue(proto).toString(); +} + enum class DockerContainer { None, OpenVpn, - ShadowSocks, + ShadowSocksOverOpenVpn, OpenVpnOverCloak, WireGuard }; +Q_ENUM_NS(DockerContainer) + +inline DockerContainer containerFromString(QString container){ + auto&& metaEnum = QMetaEnum::fromType(); + return static_cast(metaEnum.keyToValue(container.toStdString().c_str())); +} + +inline QString containerToString(DockerContainer container){ + return QVariant::fromValue(container).toString(); +} static DockerContainer containerForProto(Protocol proto) { @@ -28,7 +51,7 @@ static DockerContainer containerForProto(Protocol proto) switch (proto) { case Protocol::OpenVpn: return DockerContainer::OpenVpn; case Protocol::OpenVpnOverCloak: return DockerContainer::OpenVpnOverCloak; - case Protocol::ShadowSocks: return DockerContainer::ShadowSocks; + case Protocol::ShadowSocksOverOpenVpn: return DockerContainer::ShadowSocksOverOpenVpn; case Protocol::WireGuard: return DockerContainer::WireGuard; case Protocol::Any: return DockerContainer::None; } @@ -91,10 +114,10 @@ enum ErrorCode namespace config { // config keys -static QString key_openvpn_config_data() { return "openvpn_config_data"; } -static QString key_openvpn_config_path() { return "openvpn_config_path"; } -static QString key_shadowsocks_config_data() { return "shadowsocks_config_data"; } -static QString key_cloak_config_data() { return "cloak_config_data"; } +const char key_openvpn_config_data[] = "openvpn_config_data"; +const char key_openvpn_config_path[] = "openvpn_config_path"; +const char key_shadowsocks_config_data[] = "shadowsocks_config_data"; +const char key_cloak_config_data[] = "cloak_config_data"; } diff --git a/client/core/scripts_registry.cpp b/client/core/scripts_registry.cpp index 5496ecb8..dc01a44e 100644 --- a/client/core/scripts_registry.cpp +++ b/client/core/scripts_registry.cpp @@ -9,7 +9,7 @@ QString amnezia::scriptFolder(amnezia::Protocol proto) switch (proto) { case Protocol::OpenVpn: return QLatin1String("openvpn"); case Protocol::OpenVpnOverCloak: return QLatin1String("openvpn_cloak"); - case Protocol::ShadowSocks: return QLatin1String("openvpn_shadowsocks"); + case Protocol::ShadowSocksOverOpenVpn: return QLatin1String("openvpn_shadowsocks"); case Protocol::WireGuard: return QLatin1String("wireguard"); default: return ""; } diff --git a/client/core/server_defs.cpp b/client/core/server_defs.cpp index eea22a5a..a2f64133 100644 --- a/client/core/server_defs.cpp +++ b/client/core/server_defs.cpp @@ -5,7 +5,7 @@ QString amnezia::server::getContainerName(amnezia::DockerContainer container) switch (container) { case(DockerContainer::OpenVpn): return "amnezia-openvpn"; case(DockerContainer::OpenVpnOverCloak): return "amnezia-openvpn-cloak"; - case(DockerContainer::ShadowSocks): return "amnezia-shadowsocks"; + case(DockerContainer::ShadowSocksOverOpenVpn): return "amnezia-shadowsocks"; default: return ""; } } diff --git a/client/core/server_defs.h b/client/core/server_defs.h index 2475771b..99865a02 100644 --- a/client/core/server_defs.h +++ b/client/core/server_defs.h @@ -9,9 +9,9 @@ namespace server { QString getContainerName(amnezia::DockerContainer container); QString getDockerfileFolder(amnezia::DockerContainer container); -static QString vpnDefaultSubnetIp() { return "10.8.0.0"; } -static QString vpnDefaultSubnetMask() { return "255.255.255.0"; } -static QString vpnDefaultSubnetMaskVal() { return "24"; } +const char vpnDefaultSubnetIp[] = "10.8.0.0"; +const char vpnDefaultSubnetMask[] = "255.255.255.0"; +const char vpnDefaultSubnetMaskVal[] = "24"; } } diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index befff7b8..ce4f8dd9 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -392,15 +392,15 @@ ErrorCode ServerController::removeServer(const ServerCredentials &credentials, P if (e) { return e; } - return removeServer(credentials, Protocol::ShadowSocks); + return removeServer(credentials, Protocol::ShadowSocksOverOpenVpn); } else if (proto == Protocol::OpenVpn) { scriptFileName = ":/server_scripts/remove_container.sh"; container = DockerContainer::OpenVpn; } - else if (proto == Protocol::ShadowSocks) { + else if (proto == Protocol::ShadowSocksOverOpenVpn) { scriptFileName = ":/server_scripts/remove_container.sh"; - container = DockerContainer::ShadowSocks; + container = DockerContainer::ShadowSocksOverOpenVpn; } else return ErrorCode::NotImplementedError; @@ -427,7 +427,7 @@ ErrorCode ServerController::setupServer(const ServerCredentials &credentials, Pr return ErrorCode::NoError; //return setupOpenVpnServer(credentials); } - else if (proto == Protocol::ShadowSocks) { + else if (proto == Protocol::ShadowSocksOverOpenVpn) { return setupShadowSocksServer(credentials); } else if (proto == Protocol::Any) { @@ -586,9 +586,9 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential { Vars vars; - vars.append(qMakePair("$VPN_SUBNET_IP", amnezia::server::vpnDefaultSubnetIp())); - vars.append(qMakePair("$VPN_SUBNET_MASK_VAL", amnezia::server::vpnDefaultSubnetMaskVal())); - vars.append(qMakePair("$VPN_SUBNET_MASK", amnezia::server::vpnDefaultSubnetMask())); + vars.append(qMakePair("$VPN_SUBNET_IP", amnezia::server::vpnDefaultSubnetIp)); + vars.append(qMakePair("$VPN_SUBNET_MASK_VAL", amnezia::server::vpnDefaultSubnetMaskVal)); + vars.append(qMakePair("$VPN_SUBNET_MASK", amnezia::server::vpnDefaultSubnetMask)); vars.append(qMakePair("$CONTAINER_NAME", amnezia::server::getContainerName(container))); vars.append(qMakePair("$DOCKERFILE_FOLDER", "/opt/amnezia/" + amnezia::server::getContainerName(container))); @@ -610,7 +610,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential vars.append(qMakePair("$SERVER_PORT", amnezia::protocols::cloak::ckDefaultPort())); vars.append(qMakePair("$FAKE_WEB_SITE_ADDRESS", amnezia::protocols::cloak::ckDefaultRedirSite())); } - else if (container == DockerContainer::ShadowSocks) { + else if (container == DockerContainer::ShadowSocksOverOpenVpn) { vars.append(qMakePair("$SERVER_PORT", "6789")); } diff --git a/client/defines.h b/client/defines.h index 4f244d90..e106f003 100644 --- a/client/defines.h +++ b/client/defines.h @@ -4,6 +4,6 @@ #define APPLICATION_NAME "AmneziaVPN" #define SERVICE_NAME "AmneziaVPN-service" #define ORGANIZATION_NAME "AmneziaVPN.ORG" -#define APP_VERSION "1.0.0.0" +#define APP_VERSION "1.6.0.0" #endif // DEFINES_H diff --git a/client/images/check.png b/client/images/check.png new file mode 100644 index 00000000..558bed5d Binary files /dev/null and b/client/images/check.png differ diff --git a/client/images/plus.png b/client/images/plus.png new file mode 100644 index 00000000..40a238e0 Binary files /dev/null and b/client/images/plus.png differ diff --git a/client/images/settings.png b/client/images/settings.png index 60127b5c..a64c6116 100644 Binary files a/client/images/settings.png and b/client/images/settings.png differ diff --git a/client/images/settings_grey.png b/client/images/settings_grey.png new file mode 100644 index 00000000..60127b5c Binary files /dev/null and b/client/images/settings_grey.png differ diff --git a/client/images/uncheck.png b/client/images/uncheck.png new file mode 100644 index 00000000..233843c5 Binary files /dev/null and b/client/images/uncheck.png differ diff --git a/client/protocols/openvpnovercloakprotocol.cpp b/client/protocols/openvpnovercloakprotocol.cpp index 314706d6..3338b0ac 100644 --- a/client/protocols/openvpnovercloakprotocol.cpp +++ b/client/protocols/openvpnovercloakprotocol.cpp @@ -101,5 +101,5 @@ QString OpenVpnOverCloakProtocol::cloakExecPath() void OpenVpnOverCloakProtocol::readCloakConfiguration(const QJsonObject &configuration) { - m_cloakConfig = configuration.value(config::key_cloak_config_data()).toObject(); + m_cloakConfig = configuration.value(config::key_cloak_config_data).toObject(); } diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp index c3fddebe..ca673170 100644 --- a/client/protocols/openvpnprotocol.cpp +++ b/client/protocols/openvpnprotocol.cpp @@ -68,16 +68,16 @@ void OpenVpnProtocol::killOpenVpnProcess() void OpenVpnProtocol::readOpenVpnConfiguration(const QJsonObject &configuration) { - if (configuration.contains(config::key_openvpn_config_data())) { + if (configuration.contains(config::key_openvpn_config_data)) { m_configFile.open(); - m_configFile.write(configuration.value(config::key_openvpn_config_data()).toString().toUtf8()); + m_configFile.write(configuration.value(config::key_openvpn_config_data).toString().toUtf8()); m_configFile.close(); m_configFileName = m_configFile.fileName(); qDebug().noquote() << QString("Set config data") << m_configFileName; } - else if (configuration.contains(config::key_openvpn_config_path())) { - m_configFileName = configuration.value(config::key_openvpn_config_path()).toString(); + else if (configuration.contains(config::key_openvpn_config_path)) { + m_configFileName = configuration.value(config::key_openvpn_config_path).toString(); QFileInfo file(m_configFileName); if (file.fileName().isEmpty()) { diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp index 83f66009..74e0ec4d 100644 --- a/client/protocols/shadowsocksvpnprotocol.cpp +++ b/client/protocols/shadowsocksvpnprotocol.cpp @@ -110,5 +110,5 @@ QJsonObject ShadowSocksVpnProtocol::genShadowSocksConfig(const ServerCredentials void ShadowSocksVpnProtocol::readShadowSocksConfiguration(const QJsonObject &configuration) { - m_shadowSocksConfig = configuration.value(config::key_shadowsocks_config_data()).toObject(); + m_shadowSocksConfig = configuration.value(config::key_shadowsocks_config_data).toObject(); } diff --git a/client/protocols/shadowsocksvpnprotocol.h b/client/protocols/shadowsocksvpnprotocol.h index 86ad40b3..a7545a58 100644 --- a/client/protocols/shadowsocksvpnprotocol.h +++ b/client/protocols/shadowsocksvpnprotocol.h @@ -13,7 +13,7 @@ public: ErrorCode start() override; void stop() override; - static QJsonObject genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto = Protocol::ShadowSocks); + static QJsonObject genShadowSocksConfig(const ServerCredentials &credentials, Protocol proto = Protocol::ShadowSocksOverOpenVpn); protected: void readShadowSocksConfiguration(const QJsonObject &configuration); diff --git a/client/resources.qrc b/client/resources.qrc index 18045ed4..6e87c044 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -48,5 +48,9 @@ server_scripts/install_docker.sh server_scripts/build_container.sh server_scripts/prepare_host.sh + images/check.png + images/uncheck.png + images/settings_grey.png + images/plus.png diff --git a/client/settings.cpp b/client/settings.cpp index a7e12278..27a9c928 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -1,33 +1,116 @@ - #include "defines.h" #include "settings.h" +#include + Settings::Settings(QObject* parent) : QObject(parent), m_settings (ORGANIZATION_NAME, APPLICATION_NAME, this) { + // Import old settings + if (serversCount() == 0) { + QString user = m_settings.value("Server/userName").toString(); + QString password = m_settings.value("Server/password").toString(); + QString serverName = m_settings.value("Server/serverName").toString(); + int port = m_settings.value("Server/serverPort").toInt(); + + if (!user.isEmpty() && !password.isEmpty() && !serverName.isEmpty()){ + QJsonObject server; + server.insert(userNameString, user); + server.insert(passwordString, password); + server.insert(hostNameString, serverName); + server.insert(portString, port); + server.insert(descriptionString, tr("Server #1")); + + addServer(server); + } + } +} + +int Settings::serversCount() const +{ + return serversArray().size(); +} + +QJsonObject Settings::server(int index) const +{ + const QJsonArray &servers = serversArray(); + if (index >= servers.size()) return QJsonObject(); + + return servers.at(index).toObject(); +} + +void Settings::addServer(const QJsonObject &server) +{ + QJsonArray servers = serversArray(); + servers.append(server); + setServersArray(servers); +} + +void Settings::removeServer(int index) +{ + QJsonArray servers = serversArray(); + if (index >= servers.size()) return; + + servers.removeAt(index); + setServersArray(servers); +} + +bool Settings::editServer(int index, const QJsonObject &server) +{ + QJsonArray servers = serversArray(); + if (index >= servers.size()) return false; + + servers.replace(index, server); + setServersArray(servers); + return true; +} + +void Settings::setDefaultContainer(int serverIndex, DockerContainer container) +{ + QJsonObject s = server(serverIndex); + s.insert(defaultContainerString, containerToString(container)); + editServer(serverIndex, s); +} + +DockerContainer Settings::defaultContainer(int serverIndex) const +{ + return containerFromString(defaultContainerName(serverIndex)); +} + +QString Settings::defaultContainerName(int serverIndex) const +{ + QString name = server(serverIndex).value(defaultContainerString).toString(); + if (name.isEmpty()) { + return containerToString(DockerContainer::OpenVpnOverCloak); + } + else return name; } bool Settings::haveAuthData() const { - return (!serverName().isEmpty() && !userName().isEmpty() && !password().isEmpty()); + ServerCredentials cred = defaultServerCredentials(); + + return (!cred.hostName.isEmpty() && !cred.userName.isEmpty() && !cred.password.isEmpty()); } -void Settings::setServerCredentials(const ServerCredentials &credentials) -{ - setServerName(credentials.hostName); - setServerPort(credentials.port); - setUserName(credentials.userName); - setPassword(credentials.password); -} +//void Settings::setServerCredentials(const ServerCredentials &credentials) +//{ +// setServerName(credentials.hostName); +// setServerPort(credentials.port); +// setUserName(credentials.userName); +// setPassword(credentials.password); +//} -ServerCredentials Settings::serverCredentials() +ServerCredentials Settings::defaultServerCredentials() const { + const QJsonObject &s = defaultServer(); + ServerCredentials credentials; - credentials.hostName = serverName(); - credentials.userName = userName(); - credentials.password = password(); - credentials.port = serverPort(); + credentials.hostName = s.value(hostNameString).toString(); + credentials.userName = s.value(userNameString).toString(); + credentials.password = s.value(passwordString).toString(); + credentials.port = s.value(portString).toInt(); return credentials; } diff --git a/client/settings.h b/client/settings.h index a6dc1b66..4fe5b183 100644 --- a/client/settings.h +++ b/client/settings.h @@ -5,6 +5,10 @@ #include #include +#include +#include +#include + #include "core/defs.h" using namespace amnezia; @@ -18,22 +22,43 @@ class Settings : public QObject public: explicit Settings(QObject* parent = nullptr); - QString userName() const { return m_settings.value("Server/userName", QString()).toString(); } - void setUserName(const QString& login) { m_settings.setValue("Server/userName", login); } - QString password() const { return m_settings.value("Server/password", QString()).toString(); } - void setPassword(const QString& password) { m_settings.setValue("Server/password", password); } +// QString userName() const { return m_settings.value("Server/userName", QString()).toString(); } +// void setUserName(const QString& login) { m_settings.setValue("Server/userName", login); } - QString serverName() const { return m_settings.value("Server/serverName", QString()).toString(); } - void setServerName(const QString& serverName) { m_settings.setValue("Server/serverName", serverName); } +// QString password() const { return m_settings.value("Server/password", QString()).toString(); } +// void setPassword(const QString& password) { m_settings.setValue("Server/password", password); } - int serverPort() const { return m_settings.value("Server/serverPort", 22).toInt(); } - void setServerPort(int serverPort = 22) { m_settings.setValue("Server/serverPort", serverPort); } +// QString serverName() const { return m_settings.value("Server/serverName", QString()).toString(); } +// void setServerName(const QString& serverName) { m_settings.setValue("Server/serverName", serverName); } + +// int serverPort() const { return m_settings.value("Server/serverPort", 22).toInt(); } +// void setServerPort(int serverPort = 22) { m_settings.setValue("Server/serverPort", serverPort); } + + ServerCredentials defaultServerCredentials() const; + //void setServerCredentials(const ServerCredentials &credentials); + + QJsonArray serversArray() const {return QJsonDocument::fromJson(m_settings.value("Servers/serversList").toByteArray()).array(); } + void setServersArray(const QJsonArray &servers) { m_settings.setValue("Servers/serversList", QJsonDocument(servers).toJson()); } + + // Servers section + int serversCount() const; + QJsonObject server(int index) const; + void addServer(const QJsonObject &server); + void removeServer(int index); + bool editServer(int index, const QJsonObject &server); + + int defaultServerIndex() const { return m_settings.value("Servers/defaultServerIndex", 0).toInt(); } + void setDefaultServer(int index) { m_settings.setValue("Servers/defaultServerIndex", index); } + QJsonObject defaultServer() const { return server(defaultServerIndex()); } + + void setDefaultContainer(int serverIndex, DockerContainer container ); + DockerContainer defaultContainer(int serverIndex) const; + QString defaultContainerName(int serverIndex) const; - ServerCredentials serverCredentials(); - void setServerCredentials(const ServerCredentials &credentials); bool haveAuthData() const; + // App settings section bool isAutoConnect() const { return m_settings.value("Conf/autoConnect", QString()).toBool(); } void setAutoConnect(bool enabled) { m_settings.setValue("Conf/autoConnect", enabled); } @@ -48,8 +73,8 @@ public: QStringList customIps() { return m_settings.value("Conf/customIps").toStringList(); } void setCustomIps(const QStringList &customIps) { m_settings.setValue("Conf/customIps", customIps); } - QString primaryDns() const { return m_settings.value("Conf/primaryDns", cloudFlareNs1()).toString(); } - QString secondaryDns() const { return m_settings.value("Conf/secondaryDns", cloudFlareNs2()).toString(); } + QString primaryDns() const { return m_settings.value("Conf/primaryDns", cloudFlareNs1).toString(); } + QString secondaryDns() const { return m_settings.value("Conf/secondaryDns", cloudFlareNs2).toString(); } //QString primaryDns() const { return m_primaryDns; } void setPrimaryDns(const QString &primaryDns) { m_settings.setValue("Conf/primaryDns", primaryDns); } @@ -57,14 +82,29 @@ public: //QString secondaryDns() const { return m_secondaryDns; } void setSecondaryDns(const QString &secondaryDns) { m_settings.setValue("Conf/secondaryDns", secondaryDns); } - QString cloudFlareNs1() const { return "1.1.1.1"; } - QString cloudFlareNs2() const { return "1.0.0.1"; } + static constexpr char cloudFlareNs1[] = "1.1.1.1"; + static constexpr char cloudFlareNs2[] = "1.0.0.1"; + + static constexpr char openNicNs5[] = "94.103.153.176"; + static constexpr char openNicNs13[] = "144.76.103.143"; + + +public: + // Json strings + static constexpr char hostNameString[] = "hostName"; + static constexpr char userNameString[] = "userName"; + static constexpr char passwordString[] = "password"; + static constexpr char portString[] = "port"; + static constexpr char descriptionString[] = "description"; + + static constexpr char defaultContainerString[] = "defaultContainer"; - QString openNicNs5() const { return "94.103.153.176"; } - QString openNicNs13() const { return "144.76.103.143"; } private: QSettings m_settings; + + + }; #endif // SETTINGS_H diff --git a/client/ui/Controls/SlidingStackedWidget.cpp b/client/ui/Controls/SlidingStackedWidget.cpp index 104de522..ee370fe9 100644 --- a/client/ui/Controls/SlidingStackedWidget.cpp +++ b/client/ui/Controls/SlidingStackedWidget.cpp @@ -1,5 +1,7 @@ #include "SlidingStackedWidget.h" +#include + SlidingStackedWidget::SlidingStackedWidget(QWidget *parent) : QStackedWidget(parent) { @@ -28,6 +30,14 @@ SlidingStackedWidget::SlidingStackedWidget(QWidget *parent) m_wrap = false; m_pnow = QPoint(0,0); m_active = false; + + animnow = new QPropertyAnimation(); + animnext = new QPropertyAnimation(); + + animgroup = new QParallelAnimationGroup; + + animgroup->addAnimation(animnow); + animgroup->addAnimation(animnext); } SlidingStackedWidget::~SlidingStackedWidget() { @@ -90,6 +100,21 @@ void SlidingStackedWidget::slideInWidget(QWidget *widget, SlidingStackedWidget:: #endif } +bool SlidingStackedWidget::isAnimationRunning() +{ + return animgroup->state() == QAnimationGroup::Running; +} + +void SlidingStackedWidget::waitForAnimation() +{ + if (!isAnimationRunning()) return; + + qDebug() << "Wait for stacked widget animation"; + QEventLoop l; + connect(animgroup, &QParallelAnimationGroup::finished, &l, &QEventLoop::quit); + l.exec(); +} + void SlidingStackedWidget::slideInWgtImpl(QWidget * newwidget, enum t_direction direction) { if (m_active) { return; @@ -149,22 +174,28 @@ void SlidingStackedWidget::slideInWgtImpl(QWidget * newwidget, enum t_direction widget(next)->raise(); // animate both, the now and next widget to the side, using animation framework - QPropertyAnimation *animnow = new QPropertyAnimation(widget(now), "pos"); + //QPropertyAnimation *animnow = new QPropertyAnimation(widget(now), "pos"); + animnow->setTargetObject(widget(now)); + animnow->setPropertyName("pos"); + animnow->setDuration(m_speed); animnow->setEasingCurve(m_animationtype); animnow->setStartValue(QPoint(pnow.x(), pnow.y())); animnow->setEndValue(QPoint(offsetx + pnow.x(), offsety + pnow.y())); - QPropertyAnimation *animnext = new QPropertyAnimation(widget(next), "pos"); + //QPropertyAnimation *animnext = new QPropertyAnimation(widget(next), "pos"); + animnext->setTargetObject(widget(next)); + animnext->setPropertyName("pos"); + animnext->setDuration(m_speed); animnext->setEasingCurve(m_animationtype); animnext->setStartValue(QPoint(-offsetx + pnext.x(), offsety + pnext.y())); animnext->setEndValue(QPoint(pnext.x(), pnext.y())); - QParallelAnimationGroup *animgroup = new QParallelAnimationGroup; +// QParallelAnimationGroup *animgroup = new QParallelAnimationGroup; - animgroup->addAnimation(animnow); - animgroup->addAnimation(animnext); +// animgroup->addAnimation(animnow); +// animgroup->addAnimation(animnext); QObject::connect(animgroup, SIGNAL(finished()),this,SLOT(animationDoneSlot())); m_next = next; diff --git a/client/ui/Controls/SlidingStackedWidget.h b/client/ui/Controls/SlidingStackedWidget.h index 979e8f6a..f0798306 100644 --- a/client/ui/Controls/SlidingStackedWidget.h +++ b/client/ui/Controls/SlidingStackedWidget.h @@ -47,6 +47,8 @@ public slots: void slideInIdx(int idx, enum t_direction direction = AUTOMATIC); void slideInWidget(QWidget *widget, enum t_direction direction = AUTOMATIC); + bool isAnimationRunning(); + void waitForAnimation(); signals: // this is used for internal purposes in the class engine void animationFinished(void); @@ -71,6 +73,10 @@ protected: bool m_active; QList blockedPageList; + + QPropertyAnimation *animnow; + QPropertyAnimation *animnext; + QParallelAnimationGroup *animgroup; }; #endif // SLIDINGSTACKEDWIDGET_H diff --git a/client/ui/mainwindow.cpp b/client/ui/mainwindow.cpp index 43637108..23d6350f 100644 --- a/client/ui/mainwindow.cpp +++ b/client/ui/mainwindow.cpp @@ -25,6 +25,8 @@ #include "ui_mainwindow.h" #include "utils.h" #include "vpnconnection.h" +#include "ui/server_widget.h" +#include "ui_server_widget.h" #ifdef Q_OS_MAC #include "ui/macos_util.h" @@ -43,8 +45,10 @@ MainWindow::MainWindow(QWidget *parent) : setupTray(); setupUiConnections(); + setupProtocolsPage(); ui->label_error_text->clear(); + installEventFilter(this); ui->widget_tittlebar->installEventFilter(this); ui->stackedWidget_main->setSpeed(200); @@ -67,11 +71,10 @@ MainWindow::MainWindow(QWidget *parent) : } // Post initialization + goToPage(Page::Start, true, false); if (m_settings.haveAuthData()) { goToPage(Page::Vpn, true, false); - } else { - goToPage(Page::Start, true, false); } connect(ui->lineEdit_sites_add_custom, &QLineEdit::returnPressed, [&](){ @@ -137,6 +140,7 @@ MainWindow::~MainWindow() void MainWindow::goToPage(Page page, bool reset, bool slide) { + qDebug() << "goToPage" << page; if (reset) { if (page == Page::NewServer) { ui->label_new_server_wait_info->hide(); @@ -148,13 +152,13 @@ void MainWindow::goToPage(Page page, bool reset, bool slide) if (page == Page::ServerSettings) { ui->label_server_settings_wait_info->hide(); ui->label_server_settings_wait_info->clear(); - ui->label_server_settings_server->setText(QString("%1@%2:%3") - .arg(m_settings.userName()) - .arg(m_settings.serverName()) - .arg(m_settings.serverPort())); +// ui->label_server_settings_server->setText(QString("%1@%2:%3") +// .arg(m_settings.userName()) +// .arg(m_settings.serverName()) +// .arg(m_settings.serverPort())); } if (page == Page::ShareConnection) { - QJsonObject ssConfig = ShadowSocksVpnProtocol::genShadowSocksConfig(m_settings.serverCredentials()); + QJsonObject ssConfig = ShadowSocksVpnProtocol::genShadowSocksConfig(m_settings.defaultServerCredentials()); QString ssString = QString("%1:%2@%3:%4") .arg(ssConfig.value("method").toString()) @@ -171,14 +175,29 @@ void MainWindow::goToPage(Page page, bool reset, bool slide) ui->label_share_ss_method->setText(ssConfig.value("method").toString()); ui->label_share_ss_password->setText(ssConfig.value("password").toString()); } + if (page == Page::ServerSettings) { + updateSettings(); + } + if (page == Page::Start) { + ui->pushButton_back_from_start->setVisible(!pagesStack.isEmpty()); + } ui->pushButton_new_server_connect_key->setChecked(false); } if (slide) - ui->stackedWidget_main->slideInWidget(getPageWidget(page)); + ui->stackedWidget_main->slideInWidget(getPageWidget(page), SlidingStackedWidget::RIGHT2LEFT); else ui->stackedWidget_main->setCurrentWidget(getPageWidget(page)); + + pagesStack.push(page); +} + +void MainWindow::closePage() +{ + Page prev = pagesStack.pop(); + qDebug() << "closePage" << prev << "Set page" << pagesStack.top(); + ui->stackedWidget_main->slideInWidget(getPageWidget(pagesStack.top()), SlidingStackedWidget::LEFT2RIGHT); } QWidget *MainWindow::getPageWidget(MainWindow::Page page) @@ -191,8 +210,13 @@ QWidget *MainWindow::getPageWidget(MainWindow::Page page) case(Page::AppSettings): return ui->page_app_settings; case(Page::NetworkSettings): return ui->page_network_settings; case(Page::ServerSettings): return ui->page_server_settings; + case(Page::ServerVpnProtocols): return ui->page_server_protocols; + case(Page::ServersList): return ui->page_servers; case(Page::ShareConnection): return ui->page_share_connection; case(Page::Sites): return ui->page_sites; + case(Page::OpenVpnSettings): return ui->page_proto_openvpn; + case(Page::ShadowSocksSettings): return ui->page_proto_shadowsocks; + case(Page::CloakSettings): return ui->page_proto_cloak; } return nullptr; } @@ -224,6 +248,14 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) return false; } } + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape && ! ui->stackedWidget_main->isAnimationRunning() ) { + if (currentPage() != Page::Vpn && currentPage() != Page::Start) { + closePage(); + } + } + } return QMainWindow::eventFilter(obj, event); } @@ -236,6 +268,8 @@ void MainWindow::keyPressEvent(QKeyEvent *event) QMessageBox::warning(this, APPLICATION_NAME, tr("Cannot open logs folder!")); } break; + case Qt::Key_Q: + qApp->quit(); default: ; } @@ -326,7 +360,7 @@ void MainWindow::onPushButtonNewServerConnectWithNewData(bool) ui->label_new_server_wait_info); if (ok) { - m_settings.setServerCredentials(serverCredentials); + //m_settings.setServerCredentials(serverCredentials); goToPage(Page::Vpn); qApp->processEvents(); @@ -358,7 +392,7 @@ void MainWindow::onPushButtonNewServerConnectWithExistingCode(bool) return; } - m_settings.setServerCredentials(credentials); + //m_settings.setServerCredentials(credentials); goToPage(Page::Vpn); } @@ -425,7 +459,7 @@ bool MainWindow::installServer(ServerCredentials credentials, void MainWindow::onPushButtonReinstallServer(bool) { onDisconnect(); - installServer(m_settings.serverCredentials(), + installServer(m_settings.defaultServerCredentials(), ui->page_server_settings, ui->progressBar_server_settings_reinstall, ui->pushButton_server_settings_reinstall, @@ -436,7 +470,7 @@ void MainWindow::onPushButtonClearServer(bool) { onDisconnect(); - ErrorCode e = ServerController::removeServer(m_settings.serverCredentials(), Protocol::Any); + ErrorCode e = ServerController::removeServer(m_settings.defaultServerCredentials(), Protocol::Any); if (e) { QMessageBox::warning(this, APPLICATION_NAME, tr("Error occurred while configuring server.") + "\n" + @@ -455,10 +489,10 @@ void MainWindow::onPushButtonForgetServer(bool) { onDisconnect(); - m_settings.setUserName(""); - m_settings.setPassword(""); - m_settings.setServerName(""); - m_settings.setServerPort(); +// m_settings.setUserName(""); +// m_settings.setPassword(""); +// m_settings.setServerName(""); +// m_settings.setServerPort(); goToPage(Page::Start); } @@ -580,6 +614,8 @@ void MainWindow::setTrayIcon(const QString &iconPath) MainWindow::Page MainWindow::currentPage() { + ui->stackedWidget_main->waitForAnimation(); + QWidget *currentPage = ui->stackedWidget_main->currentWidget(); QMetaEnum e = QMetaEnum::fromType(); @@ -616,6 +652,8 @@ void MainWindow::setupUiConnections() connect(ui->pushButton_app_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::AppSettings); }); connect(ui->pushButton_network_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::NetworkSettings); }); connect(ui->pushButton_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); }); + connect(ui->pushButton_server_settings_protocols, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerVpnProtocols); }); + connect(ui->pushButton_servers_list, &QPushButton::clicked, this, [this](){ goToPage(Page::ServersList); }); connect(ui->pushButton_share_connection, &QPushButton::clicked, this, [this](){ goToPage(Page::ShareConnection); updateShareCode(); @@ -631,13 +669,35 @@ void MainWindow::setupUiConnections() }); - connect(ui->pushButton_back_from_sites, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); }); - connect(ui->pushButton_back_from_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); }); - connect(ui->pushButton_back_from_new_server, &QPushButton::clicked, this, [this](){ goToPage(Page::Start); }); - connect(ui->pushButton_back_from_app_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); - connect(ui->pushButton_back_from_network_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); - connect(ui->pushButton_back_from_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); - connect(ui->pushButton_back_from_share, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); +// connect(ui->pushButton_back_from_sites, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); }); +// connect(ui->pushButton_back_from_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::Vpn); }); +// connect(ui->pushButton_back_from_new_server, &QPushButton::clicked, this, [this](){ goToPage(Page::Start); }); +// connect(ui->pushButton_back_from_app_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); +// connect(ui->pushButton_back_from_network_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); +// connect(ui->pushButton_back_from_server_settings, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); +// connect(ui->pushButton_back_from_servers, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); +// connect(ui->pushButton_back_from_share, &QPushButton::clicked, this, [this](){ goToPage(Page::GeneralSettings); }); +// connect(ui->pushButton_back_from_server_vpn_protocols, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); }); + +// connect(ui->pushButton_back_from_server_vpn_protocols, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); }); +// connect(ui->pushButton_back_from_server_vpn_protocols, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); }); +// connect(ui->pushButton_back_from_server_vpn_protocols, &QPushButton::clicked, this, [this](){ goToPage(Page::ServerSettings); }); + + connect(ui->pushButton_back_from_sites, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_settings, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_start, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_new_server, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_app_settings, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_network_settings, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_server_settings, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_servers, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_share, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_server_vpn_protocols, &QPushButton::clicked, this, [this](){ closePage(); }); + + connect(ui->pushButton_back_from_openvpn_settings, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_cloak_settings, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_back_from_shadowsocks_settings, &QPushButton::clicked, this, [this](){ closePage(); }); + connect(ui->pushButton_sites_add_custom, &QPushButton::clicked, this, [this](){ onPushButtonAddCustomSitesClicked(); }); @@ -659,11 +719,11 @@ void MainWindow::setupUiConnections() }); connect(ui->pushButton_network_settings_resetdns1, &QPushButton::clicked, this, [this](){ - m_settings.setPrimaryDns(m_settings.cloudFlareNs1()); + m_settings.setPrimaryDns(m_settings.cloudFlareNs1); updateSettings(); }); connect(ui->pushButton_network_settings_resetdns2, &QPushButton::clicked, this, [this](){ - m_settings.setSecondaryDns(m_settings.cloudFlareNs2()); + m_settings.setSecondaryDns(m_settings.cloudFlareNs2); updateSettings(); }); @@ -689,6 +749,36 @@ void MainWindow::setupUiConnections() QDesktopServices::openUrl(QUrl("https://github.com/amnezia-vpn/desktop-client/releases")); }); + connect(ui->pushButton_servers_add_new, &QPushButton::clicked, this, [this](){ goToPage(Page::Start); }); +} + +void MainWindow::setupProtocolsPage() +{ + connect(ui->pushButton_proto_openvpn_cont_openvpn_config, &QPushButton::clicked, this, [this](){ + goToPage(Page::OpenVpnSettings); + }); + connect(ui->pushButton_proto_ss_openvpn_cont_openvpn_config, &QPushButton::clicked, this, [this](){ + goToPage(Page::OpenVpnSettings); + }); + connect(ui->pushButton_proto_cloak_openvpn_cont_openvpn_config, &QPushButton::clicked, this, [this](){ + goToPage(Page::OpenVpnSettings); + }); + + connect(ui->pushButton_proto_cloak_openvpn_cont_default, &QPushButton::clicked, this, [this](){ + m_settings.setDefaultContainer(selectedServerIndex, DockerContainer::OpenVpnOverCloak); + updateSettings(); + }); + + connect(ui->pushButton_proto_ss_openvpn_cont_default, &QPushButton::clicked, this, [this](){ + m_settings.setDefaultContainer(selectedServerIndex, DockerContainer::ShadowSocksOverOpenVpn); + updateSettings(); + }); + + connect(ui->pushButton_proto_openvpn_cont_default, &QPushButton::clicked, this, [this](){ + m_settings.setDefaultContainer(selectedServerIndex, DockerContainer::OpenVpn); + updateSettings(); + }); + } void MainWindow::setTrayState(VpnProtocol::ConnectionState state) @@ -753,7 +843,7 @@ void MainWindow::onConnect() qApp->processEvents(); // TODO: Call connectToVpn with restricted server account - ServerCredentials credentials = m_settings.serverCredentials(); + ServerCredentials credentials = m_settings.defaultServerCredentials(); ErrorCode errorCode = m_vpnConnection->connectToVpn(credentials); if (errorCode) { @@ -864,18 +954,36 @@ void MainWindow::updateSettings() for(const QString &site : m_settings.customSites()) { makeSitesListItem(ui->listWidget_sites, site); } + + ui->listWidget_servers->clear(); + const QJsonArray &servers = m_settings.serversArray(); + int defaultServer = m_settings.defaultServerIndex(); + + for(int i = 0; i < servers.size(); i++) { + makeServersListItem(ui->listWidget_servers, servers.at(i).toObject(), i == defaultServer, i); + } + + QJsonObject selectedServer = m_settings.server(selectedServerIndex); + QString selectedContainerName = m_settings.defaultContainerName(selectedServerIndex); + + ui->label_server_settings_current_vpn_protocol->setText(tr("Protocol: ") + selectedContainerName); + + qDebug() << "DefaultContainer(selectedServerIndex)" << selectedServerIndex << m_settings.defaultContainer(selectedServerIndex); + ui->pushButton_proto_cloak_openvpn_cont_default->setChecked(m_settings.defaultContainer(selectedServerIndex) == DockerContainer::OpenVpnOverCloak); + ui->pushButton_proto_ss_openvpn_cont_default->setChecked(m_settings.defaultContainer(selectedServerIndex) == DockerContainer::ShadowSocksOverOpenVpn); + ui->pushButton_proto_openvpn_cont_default->setChecked(m_settings.defaultContainer(selectedServerIndex) == DockerContainer::OpenVpn); } void MainWindow::updateShareCode() { - QJsonObject o; - o.insert("h", m_settings.serverName()); - o.insert("p", m_settings.serverPort()); - o.insert("u", m_settings.userName()); - o.insert("w", m_settings.password()); +// QJsonObject o; +// o.insert("h", m_settings.serverName()); +// o.insert("p", m_settings.serverPort()); +// o.insert("u", m_settings.userName()); +// o.insert("w", m_settings.password()); - QByteArray ba = QJsonDocument(o).toJson().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - ui->textEdit_sharing_code->setText(QString("vpn://%1").arg(QString(ba))); +// QByteArray ba = QJsonDocument(o).toJson().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); +// ui->textEdit_sharing_code->setText(QString("vpn://%1").arg(QString(ba))); //qDebug() << "Share code" << QJsonDocument(o).toJson(); } @@ -909,6 +1017,34 @@ void MainWindow::makeSitesListItem(QListWidget *listWidget, const QString &addre widget->setStyleSheet(styleSheet()); } +void MainWindow::makeServersListItem(QListWidget *listWidget, const QJsonObject &server, bool isDefault, int index) +{ + QSize size(310, 70); + ServerWidget* widget = new ServerWidget(server, isDefault); + widget->resize(size); + + connect(widget->ui->pushButton_default, &QPushButton::clicked, this, [this, index](){ + m_settings.setDefaultServer(index); + updateSettings(); + }); + + connect(widget->ui->pushButton_share, &QPushButton::clicked, this, [this, index](){ + goToPage(Page::ShareConnection); + // update share page + }); + + connect(widget->ui->pushButton_settings, &QPushButton::clicked, this, [this, index](){ + selectedServerIndex = index; + goToPage(Page::ServerSettings); + }); + + QListWidgetItem* item = new QListWidgetItem(listWidget); + item->setSizeHint(size); + listWidget->setItemWidget(item, widget); + + widget->setStyleSheet(styleSheet()); +} + void MainWindow::updateQRCodeImage(const QString &text, QLabel *label) { int levelIndex = 1; diff --git a/client/ui/mainwindow.h b/client/ui/mainwindow.h index f8d67ccc..45c56a51 100644 --- a/client/ui/mainwindow.h +++ b/client/ui/mainwindow.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -39,7 +40,9 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); - enum Page {Start, NewServer, Vpn, GeneralSettings, AppSettings, NetworkSettings, ServerSettings, ShareConnection, Sites}; + enum Page {Start, NewServer, Vpn, GeneralSettings, AppSettings, NetworkSettings, + ServerSettings, ServerVpnProtocols, ServersList, ShareConnection, Sites, + OpenVpnSettings, ShadowSocksSettings, CloakSettings}; Q_ENUM(Page) private slots: @@ -69,6 +72,8 @@ private slots: private: void goToPage(Page page, bool reset = true, bool slide = true); + void closePage(); + QWidget *getPageWidget(Page page); Page currentPage(); @@ -78,10 +83,13 @@ private: void setTrayIcon(const QString &iconPath); void setupUiConnections(); + void setupProtocolsPage(); + void updateSettings(); void updateShareCode(); void makeSitesListItem(QListWidget* listWidget, const QString &address); + void makeServersListItem(QListWidget* listWidget, const QJsonObject &server, bool isDefault, int index); void updateQRCodeImage(const QString &text, QLabel *label); private: @@ -111,6 +119,10 @@ private: const QString ConnectedTrayIconName = "active.png"; const QString DisconnectedTrayIconName = "default.png"; const QString ErrorTrayIconName = "error.png"; + + + QStack pagesStack; + int selectedServerIndex = -1; // server index to use when proto settings page opened }; #endif // MAINWINDOW_H diff --git a/client/ui/mainwindow.ui b/client/ui/mainwindow.ui index ebe00ae3..1764970b 100644 --- a/client/ui/mainwindow.ui +++ b/client/ui/mainwindow.ui @@ -163,7 +163,26 @@ QScrollBar::down-arrow:vertical { /* arrow to scroll down with */ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background-color: black; -} +} + +/* +QGroupBox { + border: 1px solid lightgray; + border-radius: 2px; + margin-top: 1ex; +} + +QGroupBox::title { + font-size: 16px; + font-style: normal; + font-weight: normal; + color: #181922; + + subcontrol-origin: margin; + subcontrol-position: top left; + padding: 0 3px; +} +*/ @@ -263,14 +282,14 @@ QPushButton:hover { - 5 + 1 10 - 40 + 50 361 71 @@ -430,6 +449,38 @@ border-radius: 4px; Set up your own server + + + + 10 + 10 + 26 + 20 + + + + PointingHandCursor + + + QPushButton { + image: url(:/images/arrow_right.png); + image-position: left; + text-align: left; + /*font: 17pt "Ancient";*/ + + padding: 1px; + image: url(:/images/arrow_left.png); +} +QPushButton:hover { + padding: 0px; +} + + + + + + + @@ -1028,7 +1079,7 @@ background: #282932; PointingHandCursor - image: url(:/images/settings.png); + image: url(:/images/settings_grey.png); background: transparent @@ -1198,34 +1249,31 @@ color: #181922; - -QListView { + QListView { outline: 0; background: transparent; border: none; - gridline-color: darkgray; } QListView::item { - padding-left: 5px; + padding-left: 5px; border: none; -color: #181922; - + color: #181922; } QListView::item:disabled { - padding-left: 5px; + padding-left: 5px; border: none; -color: #181922; + color: #181922; } QListView::item:selected { border: none; -background: rgba(167, 167, 167, 0.1); -color: #181922; + background: rgba(167, 167, 167, 0.1); + color: #181922; } @@ -1772,6 +1820,218 @@ background-repeat: no-repeat; Network settings + + + + 30 + 300 + 330 + 30 + + + + PointingHandCursor + + + Reinstall server, clear server + + + font-family: Lato; +font-style: normal; +font-weight: bold; +font-size: 20px; +line-height: 25px; +Text-align:left; +padding-left: 30px; + + +/* black */ +color: #100A44; + +background-image: url(:/images/server_settings.png); +background-repeat: no-repeat; + background-position: left center; + + + Servers + + + + + + 10 + 340 + 360 + 10 + + + + image: url(:/images/line.png); + + + + + + + + + + + + + + 20 + 90 + 340 + 501 + + + + QWidget { + margin: 0px; + padding: 0px; +} + +QPushButton:hover { + image: url(:/images/close.png); + image-position: right center; +} + +QListView { + outline: 0; + background: transparent; + border: none; + gridline-color: darkgray; + show-decoration-selected: 1; +} + +QListView::item +{ + padding-left: 5px; + color: #181922; + border: none; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #FAFBFE, stop: 1 #ECEEFF); +} + +QListView::item:disabled +{ + padding-left: 5px; + border: none; + color: #181922; +} + +QListView::item:selected { + border: none; + background: rgba(167, 167, 167, 0.1); + color: #181922; +} + +QListView::item:selected:!active { + background: transparent; + border: none; +} + +QListView::item:selected:active { + background: transparent; + border: none; +} + +QListView::item:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #FAFBFE, stop: 1 #DCDEDF); +} + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + + + + 10 + 10 + 26 + 20 + + + + PointingHandCursor + + + QPushButton { + image: url(:/images/arrow_right.png); + image-position: left; + text-align: left; + /*font: 17pt "Ancient";*/ + + padding: 1px; + image: url(:/images/arrow_left.png); +} +QPushButton:hover { + padding: 0px; +} + + + + + + + + + + + 120 + 40 + 111 + 40 + + + + font-family: Lato; +font-style: normal; +font-weight: bold; +font-size: 20px; +line-height: 25px; +color: #100A44; + + + + Servers + + + Qt::AlignCenter + + + + + + 230 + 49 + 24 + 24 + + + + PointingHandCursor + + + QPushButton { + image: url(:/images/plus.png); + padding:1px; +} +QPushButton:hover { + padding:0px; +} + + + + + + @@ -1926,8 +2186,8 @@ background: #211966; - 20 - 20 + 10 + 10 26 20 @@ -2139,7 +2399,7 @@ padding:0px; 40 - 490 + 530 301 41 @@ -2155,7 +2415,7 @@ padding:0px; 40 - 150 + 350 300 40 @@ -2221,7 +2481,7 @@ QPushButton:hover { 20 - 40 + 30 340 40 @@ -2236,7 +2496,7 @@ color: #100A44; - Your configured server + Server settings Qt::AlignCenter @@ -2249,7 +2509,7 @@ color: #100A44; 40 - 150 + 350 300 40 @@ -2309,7 +2569,7 @@ border-radius: 4px 0px 0px 4px; 40 - 210 + 410 300 40 @@ -2343,7 +2603,7 @@ background: #211966; 40 - 270 + 470 300 40 @@ -2377,9 +2637,9 @@ background: #211966; 20 - 80 + 120 341 - 41 + 31 @@ -2387,8 +2647,7 @@ background: #211966; font-family: Lato; font-style: normal; font-weight: normal; -font-size: 24px; -color: #333333; +font-size: 20px; } @@ -2401,8 +2660,94 @@ color: #333333; Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + 70 + 80 + 251 + 31 + + + + QLineEdit { +border: none; +outline: none; +border-bottom: 1px solid lightgrey; +font-size: 18px; +font-weight: bold; +} + + + + Qt::AlignCenter + + + false + + + + + + 40 + 220 + 300 + 40 + + + + PointingHandCursor + + + QPushButton { +color:rgb(212, 212, 212); +border-radius: 4px; + +font-family: Lato; +font-style: normal; +font-weight: normal; +font-size: 16px; +line-height: 21px; + +background: #100A44; +border-radius: 4px; +} +QPushButton:hover { +background: #211966; +} + + + VPN protocols + + + + + + 20 + 150 + 341 + 31 + + + + QLabel { +font-family: Lato; +font-style: normal; +font-weight: normal; +font-size: 20px; +} + + + VPN Protocol: + + + Qt::AlignCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + label_server_settings_wait_info - pushButton_back_from_server_settings label_16 progressBar_server_settings_reinstall label_17 @@ -2410,6 +2755,663 @@ color: #333333; pushButton_server_settings_clear pushButton_server_settings_forget label_server_settings_server + lineEdit_server_settings_description + pushButton_server_settings_protocols + pushButton_back_from_server_settings + label_server_settings_current_vpn_protocol + + + + + + + + + 20 + 440 + 340 + 121 + + + + QWidget { + margin: 0px; + padding: 0px; +} + +QPushButton:hover { + image: url(:/images/close.png); + image-position: right center; +} + +QListView { + outline: 0; + background: transparent; + border: none; + gridline-color: darkgray; + show-decoration-selected: 1; +} + +QListView::item +{ + padding-left: 5px; + color: #181922; + border: none; + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #FAFBFE, stop: 1 #ECEEFF); +} + +QListView::item:disabled +{ + padding-left: 5px; + border: none; + color: #181922; +} + +QListView::item:selected { + border: none; + background: rgba(167, 167, 167, 0.1); + color: #181922; +} + +QListView::item:selected:!active { + background: transparent; + border: none; +} + +QListView::item:selected:active { + background: transparent; + border: none; +} + +QListView::item:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #FAFBFE, stop: 1 #DCDEDF); +} + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + + + + 20 + 30 + 340 + 40 + + + + font-family: Lato; +font-style: normal; +font-weight: bold; +font-size: 20px; +line-height: 25px; +color: #100A44; + + + + Protocols + + + Qt::AlignCenter + + + true + + + + + + 10 + 10 + 26 + 20 + + + + PointingHandCursor + + + QPushButton { + image: url(:/images/arrow_right.png); + image-position: left; + text-align: left; + /*font: 17pt "Ancient";*/ + + padding: 1px; + image: url(:/images/arrow_left.png); +} +QPushButton:hover { + padding: 0px; +} + + + + + + + + + + + 0 + 70 + 381 + 511 + + + + QWidget { +background: transparent; +} +QPushButton { + text-align: left; + background-repeat:no-repeat; + background-position:left top; + + background-image: url(:/images/settings.png); + padding-left: 30px; + min-height: 24px; +} +QFrame { +border: 1px solid lightgrey; +border-radius: 2px; +} +QFrame#scrollArea_server_protocols { +border: none; +} +QLabel { +border: none; +} + + + true + + + + + 0 + 0 + 381 + 511 + + + + + 19 + + + + + + 0 + 100 + + + + + + + + + + + + Cloak container + + + + + + + + 36 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + background: transparent; +padding: 0px; +image: url(:/images/connect_button_connected.png); +margin: 0px; + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + background: transparent; +image: url(:/images/share.png); +padding: 0px; +margin: 0px; + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + QPushButton { + background: transparent; + image: url(:/images/check.png); + padding: 0px; + margin: 0px; +} +QPushButton:checked { + image: url(:/images/check.png); +} +QPushButton:!checked { + image: url(:/images/uncheck.png); +} + + + + + + + + true + + + false + + + + + + + + + + + + PointingHandCursor + + + OpenVPN settings + + + + + + + PointingHandCursor + + + ShadowSocks settings + + + + + + + PointingHandCursor + + + Cloak settings + + + + + + + + + + + + + + 0 + 100 + + + + + + + + + ShadowSocks container + + + + + + + + 36 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + background: transparent; +padding: 0px; +image: url(:/images/connect_button_disconnected.png); +margin: 0px; + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + background: transparent; +image: url(:/images/share.png); +padding: 0px; +margin: 0px; + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + QPushButton { + background: transparent; + image: url(:/images/check.png); + padding: 0px; + margin: 0px; +} +QPushButton:checked { + image: url(:/images/check.png); +} +QPushButton:!checked { + image: url(:/images/uncheck.png); +} + + + + + + + true + + + false + + + + + + + + + + + + PointingHandCursor + + + OpenVPN settings + + + + + + + PointingHandCursor + + + ShadowSocks settings + + + + + + + + + + + + + + 0 + 100 + + + + + + + + + + + + OpenVPN container + + + + + + + + 36 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + background: transparent; +padding: 0px; +image: url(:/images/connect_button_connected.png); +margin: 0px; + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + background: transparent; +image: url(:/images/share.png); +padding: 0px; +margin: 0px; + + + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + PointingHandCursor + + + QPushButton { + background: transparent; + image: url(:/images/check.png); + padding: 0px; + margin: 0px; +} +QPushButton:checked { + image: url(:/images/check.png); +} +QPushButton:!checked { + image: url(:/images/uncheck.png); +} + + + + + + + true + + + false + + + + + + + + + + + + PointingHandCursor + + + OpenVPN settings + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + @@ -2659,8 +3661,8 @@ background: #282932; 0 0 - 100 - 30 + 360 + 500 @@ -2880,6 +3882,357 @@ color: #15CDCB; + + + + + 20 + 30 + 340 + 40 + + + + font-family: Lato; +font-style: normal; +font-weight: bold; +font-size: 20px; +line-height: 25px; +color: #100A44; + + + + OpenVPN Settings + + + Qt::AlignCenter + + + true + + + + + + 10 + 10 + 26 + 20 + + + + PointingHandCursor + + + QPushButton { + image: url(:/images/arrow_right.png); + image-position: left; + text-align: left; + /*font: 17pt "Ancient";*/ + + padding: 1px; + image: url(:/images/arrow_left.png); +} +QPushButton:hover { + padding: 0px; +} + + + + + + + + + + + 30 + 95 + 321 + 31 + + + + + + + + + + + + true + + + + 30 + 70 + 291 + 21 + + + + VPN Addresses Subnet + + + true + + + + + + 30 + 150 + 321 + 91 + + + + QGroupBox{ + border: 1px solid lightgray; + border-radius: 2px; + margin-top: 7ex; +} + +QGroupBox::title { + font-size: 16px; + font-style: normal; + font-weight: normal; + color: #181922; + + subcontrol-origin: margin; + subcontrol-position: top left; +} + + + Network protocol + + + + + 10 + 30 + 171 + 19 + + + + UDP + + + + + + 10 + 60 + 171 + 19 + + + + TCP + + + + + + + 30 + 290 + 151 + 31 + + + + + + true + + + + 30 + 260 + 151 + 21 + + + + Cipher + + + true + + + + + + 200 + 290 + 151 + 31 + + + + + + true + + + + 200 + 260 + 151 + 21 + + + + Hash + + + true + + + + + + 30 + 350 + 321 + 21 + + + + Block DNS requests outside of VPN + + + false + + + + + + + + 20 + 30 + 340 + 40 + + + + font-family: Lato; +font-style: normal; +font-weight: bold; +font-size: 20px; +line-height: 25px; +color: #100A44; + + + + OpenVPN Settings + + + Qt::AlignCenter + + + true + + + + + + 10 + 10 + 26 + 20 + + + + PointingHandCursor + + + QPushButton { + image: url(:/images/arrow_right.png); + image-position: left; + text-align: left; + /*font: 17pt "Ancient";*/ + + padding: 1px; + image: url(:/images/arrow_left.png); +} +QPushButton:hover { + padding: 0px; +} + + + + + + + + + + + + + 20 + 30 + 340 + 40 + + + + font-family: Lato; +font-style: normal; +font-weight: bold; +font-size: 20px; +line-height: 25px; +color: #100A44; + + + + OpenVPN Settings + + + Qt::AlignCenter + + + true + + + + + + 10 + 10 + 26 + 20 + + + + PointingHandCursor + + + QPushButton { + image: url(:/images/arrow_right.png); + image-position: left; + text-align: left; + /*font: 17pt "Ancient";*/ + + padding: 1px; + image: url(:/images/arrow_left.png); +} +QPushButton:hover { + padding: 0px; +} + + + + + + + + diff --git a/client/ui/server_widget.cpp b/client/ui/server_widget.cpp new file mode 100644 index 00000000..4c2f445f --- /dev/null +++ b/client/ui/server_widget.cpp @@ -0,0 +1,30 @@ +#include "server_widget.h" +#include "ui_server_widget.h" + +#include "settings.h" + +ServerWidget::ServerWidget(const QJsonObject &server, bool isDefault, QWidget *parent) : + QWidget(parent), + ui(new Ui::ServerWidget) +{ + ui->setupUi(this); + QString desc = server.value(Settings::descriptionString).toString(); + QString address = server.value(Settings::hostNameString).toString(); + + ui->label_address->setText(address); + + if (desc.isEmpty()) { + ui->label_description->setText(address); + } + else { + ui->label_description->setText(desc); + } + + ui->pushButton_default->setChecked(isDefault); + ui->pushButton_default->setDisabled(isDefault); +} + +ServerWidget::~ServerWidget() +{ + delete ui; +} diff --git a/client/ui/server_widget.h b/client/ui/server_widget.h new file mode 100644 index 00000000..dd529979 --- /dev/null +++ b/client/ui/server_widget.h @@ -0,0 +1,22 @@ +#ifndef SERVER_WIDGET_H +#define SERVER_WIDGET_H + +#include +#include + +namespace Ui { +class ServerWidget; +} + +class ServerWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ServerWidget(const QJsonObject &server, bool isDefault, QWidget *parent = nullptr); + ~ServerWidget(); + Ui::ServerWidget *ui; + +}; + +#endif // SERVER_WIDGET_H diff --git a/client/ui/server_widget.ui b/client/ui/server_widget.ui new file mode 100644 index 00000000..69779f9a --- /dev/null +++ b/client/ui/server_widget.ui @@ -0,0 +1,167 @@ + + + ServerWidget + + + + 0 + 0 + 325 + 70 + + + + Form + + + + + + + + 10 + 10 + 181 + 21 + + + + QLabel { + font-size: 16px; + + font-style: normal; + font-weight: bold; + color: #181922; +} + + + Description + + + + + + 20 + 40 + 141 + 16 + + + + Address + + + + + + 300 + 25 + 24 + 24 + + + + PointingHandCursor + + + Set as default + + + QPushButton:checked { + image: url(:/images/check.png); +} +QPushButton:!checked { + image: url(:/images/uncheck.png); +} + + + + + + true + + + + + + 260 + 25 + 24 + 24 + + + + PointingHandCursor + + + Share connection + + + image: url(:/images/share.png); + + + + + + true + + + + + + 212 + 25 + 32 + 24 + + + + PointingHandCursor + + + Connection + + + QPushButton:checked { + image: url(:/images/connect_button_connected.png); +} +QPushButton:!checked { + image: url(:/images/connect_button_disconnected.png); +} + + + + + + true + + + + + + 174 + 25 + 24 + 24 + + + + PointingHandCursor + + + Server settings + + + image: url(:/images/settings.png); + + + + + + true + + + + + + diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 155e9b6b..bd847d3c 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -83,9 +83,9 @@ ErrorCode VpnConnection::lastError() const ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credentials, Protocol protocol) { ErrorCode errorCode = ErrorCode::NoError; - if (protocol == Protocol::OpenVpn || protocol == Protocol::ShadowSocks || protocol == Protocol::OpenVpnOverCloak) { + if (protocol == Protocol::OpenVpn || protocol == Protocol::ShadowSocksOverOpenVpn || protocol == Protocol::OpenVpnOverCloak) { QString openVpnConfigData = OpenVpnConfigurator::genOpenVpnConfig(credentials, protocol, &errorCode); - m_vpnConfiguration.insert(config::key_openvpn_config_data(), openVpnConfigData); + m_vpnConfiguration.insert(config::key_openvpn_config_data, openVpnConfigData); if (errorCode) { return errorCode; } @@ -101,14 +101,14 @@ ErrorCode VpnConnection::createVpnConfiguration(const ServerCredentials &credent } } - if (protocol == Protocol::ShadowSocks) { + if (protocol == Protocol::ShadowSocksOverOpenVpn) { QJsonObject ssConfigData = ShadowSocksVpnProtocol::genShadowSocksConfig(credentials); - m_vpnConfiguration.insert(config::key_shadowsocks_config_data(), ssConfigData); + m_vpnConfiguration.insert(config::key_shadowsocks_config_data, ssConfigData); } if (protocol == Protocol::OpenVpnOverCloak) { QJsonObject cloakConfigData = CloakConfigurator::genCloakConfig(credentials, Protocol::OpenVpnOverCloak, &errorCode); - m_vpnConfiguration.insert(config::key_cloak_config_data(), cloakConfigData); + m_vpnConfiguration.insert(config::key_cloak_config_data, cloakConfigData); } //qDebug().noquote() << "VPN config" << QJsonDocument(m_vpnConfiguration).toJson(); @@ -152,8 +152,8 @@ ErrorCode VpnConnection::connectToVpn(const ServerCredentials &credentials, Prot return e; } } - else if (protocol == Protocol::ShadowSocks) { - ErrorCode e = createVpnConfiguration(credentials, Protocol::ShadowSocks); + else if (protocol == Protocol::ShadowSocksOverOpenVpn) { + ErrorCode e = createVpnConfiguration(credentials, Protocol::ShadowSocksOverOpenVpn); if (e) { emit connectionStateChanged(VpnProtocol::ConnectionState::Error); return e;