diff --git a/.travis.yml b/.travis.yml index c6c8b0077..d2509ccb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,10 @@ script: - ./../bin/tests/tests -platform minimal -txt - ./../bin/tests/qml_tests -platform minimal -txt - cd ./../src - - qmake && make -s -j 2 + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then + qmake && make -s -j 2 + ; + fi after_success: - cd ./../tests - sudo pip install cpp-coveralls diff --git a/3rdparty/qredisclient b/3rdparty/qredisclient index 27959f8d3..d2349db21 160000 --- a/3rdparty/qredisclient +++ b/3rdparty/qredisclient @@ -1 +1 @@ -Subproject commit 27959f8d368418ede7a8e7c6adf8803aced9fea3 +Subproject commit d2349db21b62efd1bcf8e3e1f170c00624aef322 diff --git a/src/app/app.cpp b/src/app/app.cpp index 21f4586e7..881314b19 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -12,6 +12,7 @@ #include "logger.h" #include "qmlutils.h" +#include "common/tabviewmodel.h" #include "models/connectionconf.h" #include "models/configmanager.h" #include "models/connectionsmanager.h" @@ -20,7 +21,8 @@ #include "modules/value-editor/valueviewmodel.h" #include "modules/value-editor/viewmodel.h" #include "modules/value-editor/sortfilterproxymodel.h" -#include "modules/console/consoleviewmodel.h" +#include "modules/console/consolemodel.h" +#include "modules/server-stats/serverstatsmodel.h" #include "modules/bulk-operations/bulkoperationsmanager.h" @@ -47,16 +49,22 @@ Application::Application(int &argc, char **argv) initAppAnalytics(); initRedisClient(); initUpdater(); + installTranslator(); } void Application::initModels() { initConnectionsManager(); - m_consoleModel = QSharedPointer(new Console::ViewModel()); + m_consoleModel = QSharedPointer(new TabViewModel(getTabModelFactory())); connect(m_connections.data(), &ConnectionsManager::openConsole, - m_consoleModel.data(), &Console::ViewModel::openConsole); + m_consoleModel.data(), &TabViewModel::openTab); + + m_serverStatsModel = QSharedPointer(new TabViewModel(getTabModelFactory())); + + connect(m_connections.data(), &ConnectionsManager::openServerStats, + m_serverStatsModel.data(), &TabViewModel::openTab); } void Application::initAppInfo() @@ -70,8 +78,16 @@ void Application::initAppInfo() void Application::initAppFonts() { QSettings settings; - QString appFont = settings.value("app/appFont", "Open Sans").toString(); - int appFontSize = settings.value("app/appFontSize", 11).toInt(); +#ifdef Q_OS_MAC + QString defaultFontName("Helvetica Neue"); + int defaultFontSize = 12; +#else + QString defaultFontName("Open Sans"); + int defaultFontSize = 11; +#endif + + QString appFont = settings.value("app/appFont", defaultFontName).toString(); + int appFontSize = settings.value("app/appFontSize", defaultFontSize).toInt(); if (appFont == "Open Sans") { int result = QFontDatabase::addApplicationFont("://fonts/OpenSans.ttc"); @@ -109,6 +125,7 @@ void Application::registerQmlRootObjects() m_engine.rootContext()->setContextProperty("viewModel", m_keyValues.data()); // TODO: Remove legacy name usage in qml m_engine.rootContext()->setContextProperty("valuesModel", m_keyValues.data()); m_engine.rootContext()->setContextProperty("consoleModel", m_consoleModel.data()); + m_engine.rootContext()->setContextProperty("serverStatsModel", m_serverStatsModel.data()); m_engine.rootContext()->setContextProperty("appLogger", m_logger); m_engine.rootContext()->setContextProperty("bulkOperations", m_bulkOperations.data()); } @@ -151,10 +168,9 @@ void Application::initConnectionsManager() if (config.isNull()) { QMessageBox::critical(nullptr, - "Settings directory is not writable", - QString("Program can't save connections file to settings dir." - "Please change permissions or restart this program " - " with administrative privileges") + QObject::tr("Settings directory is not writable"), + QString(QObject::tr("RDM can't save connections file to settings directory. " + "Please change file permissions or restart RDM as administrator.")) ); throw std::runtime_error("invalid connections config"); @@ -188,8 +204,29 @@ void Application::initUpdater() connect(m_updater.data(), SIGNAL(updateUrlRetrived(QString &)), this, SLOT(OnNewUpdateAvailable(QString &))); } +void Application::installTranslator() +{ + QString locale = QLocale::system().uiLanguages().first().replace( "-", "_" ); + + qDebug() << QLocale::system().uiLanguages(); + + if (locale.isEmpty() || locale == "C") + locale = "en_US"; + + qDebug() << "Detected locale:" << locale; + + QTranslator* translator = new QTranslator((QObject *)this); + if (translator->load( QString( ":/translations/rdm_" ) + locale )) + { + qDebug() << "Load translations file for locale:" << locale; + QCoreApplication::installTranslator( translator ); + } else { + delete translator; + } +} + void Application::OnNewUpdateAvailable(QString &url) { QMessageBox::information(nullptr, "New update available", - QString("Please download new version of Redis Desktop Manager: %1").arg(url)); + QString(QObject::tr("Please download new version of Redis Desktop Manager: %1")).arg(url)); } diff --git a/src/app/app.h b/src/app/app.h index c2afb3d39..a8a63f671 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -14,8 +14,8 @@ class QmlUtils; class ConnectionsManager; class Updater; class LogHandler; +class TabViewModel; namespace ValueEditor { class ViewModel; } -namespace Console { class ViewModel; } namespace BulkOperations { class Manager; } @@ -37,6 +37,7 @@ class Application : public QApplication void initLog(); void initConnectionsManager(); void initUpdater(); + void installTranslator(); private slots: void OnNewUpdateAvailable(QString &url); @@ -48,6 +49,7 @@ private slots: QSharedPointer m_updater; QSharedPointer m_keyValues; QSharedPointer m_bulkOperations; - QSharedPointer m_consoleModel; + QSharedPointer m_consoleModel; + QSharedPointer m_serverStatsModel; LogHandler* m_logger; }; diff --git a/src/app/models/connectionsmanager.h b/src/app/models/connectionsmanager.h index e5c1c6198..f2934f281 100644 --- a/src/app/models/connectionsmanager.h +++ b/src/app/models/connectionsmanager.h @@ -49,6 +49,7 @@ class ConnectionsManager : public ConnectionsTree::Model, public BulkOperations: void openConsole(QSharedPointer connection); + void openServerStats(QSharedPointer connection); // Proxy-signals from TreeOperationsModel void openValueTab(QSharedPointer connection, diff --git a/src/app/models/key-models/hashkey.cpp b/src/app/models/key-models/hashkey.cpp index cc9a16956..b56b0ef3f 100644 --- a/src/app/models/key-models/hashkey.cpp +++ b/src/app/models/key-models/hashkey.cpp @@ -1,5 +1,6 @@ #include "hashkey.h" #include +#include HashKeyModel::HashKeyModel(QSharedPointer connection, QByteArray fullPath, int dbIndex, long long ttl) @@ -47,7 +48,7 @@ QVariant HashKeyModel::getData(int rowIndex, int dataRole) void HashKeyModel::updateRow(int rowIndex, const QVariantMap &row) { if (!isRowLoaded(rowIndex) || !isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); QPair cachedRow = m_rowsCache[rowIndex]; @@ -70,7 +71,7 @@ void HashKeyModel::updateRow(int rowIndex, const QVariantMap &row) void HashKeyModel::addRow(const QVariantMap &row) { if (!isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); setHashRow(row["key"].toByteArray(), row["value"].toByteArray(), false); m_rowCount++; @@ -101,12 +102,12 @@ void HashKeyModel::setHashRow(const QByteArray &hashKey, const QByteArray &hashV result = m_connection->commandSync({(updateIfNotExist)? "HSET" : "HSETNX", m_keyFullPath, hashKey, hashValue}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } if (updateIfNotExist == false && result.getValue().toInt() == 0) - throw Exception("Value with same key already exist"); + throw Exception(QObject::tr("Value with the same key already exist")); } void HashKeyModel::deleteHashRow(const QByteArray &hashKey) @@ -114,7 +115,7 @@ void HashKeyModel::deleteHashRow(const QByteArray &hashKey) try { m_connection->commandSync({"HDEL", m_keyFullPath, hashKey}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } } @@ -130,7 +131,7 @@ void HashKeyModel::addLoadedRowsToCache(const QVariantList &rows, int rowStart) ++item; if (item == rows.end()) - throw Exception("Partial data loaded from server"); + throw Exception(QObject::tr("Data was loaded from server partially.")); value.second = item->toByteArray(); result.push_back(value); diff --git a/src/app/models/key-models/keyfactory.cpp b/src/app/models/key-models/keyfactory.cpp index b935c9c97..89684d4a6 100644 --- a/src/app/models/key-models/keyfactory.cpp +++ b/src/app/models/key-models/keyfactory.cpp @@ -7,6 +7,7 @@ #include "sortedsetkey.h" #include "hashkey.h" #include "listkey.h" +#include KeyFactory::KeyFactory() {} @@ -23,7 +24,7 @@ void KeyFactory::loadKey(QSharedPointer connection, QSharedPointer result; if (resp.isErrorMessage() || resp.getType() != RedisClient::Response::Type::Status) { - QString msg("Cannot load key %1, connection error occurred: %2"); + QString msg(QObject::tr("Cannot load key %1, connection error occurred: %2")); callback(result, msg.arg(printableString(keyFullPath)).arg(resp.toRawString())); return; } @@ -31,8 +32,8 @@ void KeyFactory::loadKey(QSharedPointer connection, QString type = resp.getValue().toString(); if (type == "none") { - QString msg("Cannot load key %1 because it doesn't exist in database." - " Please reload connection tree and try again."); + QString msg(QObject::tr("Cannot load key %1 because it doesn't exist in database." + " Please reload connection tree and try again.")); callback(result, msg.arg(printableString(keyFullPath))); return; } @@ -42,7 +43,7 @@ void KeyFactory::loadKey(QSharedPointer connection, try { ttlResult = connection->commandSync({"ttl", keyFullPath}, dbIndex); } catch (const RedisClient::Connection::Exception& e) { - QString msg("Cannot load TTL for key %1, connection error occurred: %2"); + QString msg(QObject::tr("Cannot load TTL for key %1, connection error occurred: %2")); callback(result, msg.arg(printableString(keyFullPath)).arg(QString(e.what()))); return; } @@ -62,7 +63,7 @@ void KeyFactory::loadKey(QSharedPointer connection, try { connection->runCommand(typeCmd); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Cannot retrive type of the key: " + QString(e.what())); + throw Exception(QObject::tr("Cannot retrive type of the key: ") + QString(e.what())); } } diff --git a/src/app/models/key-models/listkey.cpp b/src/app/models/key-models/listkey.cpp index de9c3aabe..d110db5f8 100644 --- a/src/app/models/key-models/listkey.cpp +++ b/src/app/models/key-models/listkey.cpp @@ -16,11 +16,10 @@ QString ListKeyModel::getType() void ListKeyModel::updateRow(int rowIndex, const QVariantMap &row) { if (!isRowLoaded(rowIndex) || !isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); if (isActualPositionChanged(rowIndex)) - throw Exception("Can't delete row from list, because row already has changed." - " Reload values and try again."); + throw Exception(QObject::tr("The row has been changed and can't be updated now. Reload and try again.")); QByteArray newRow(row["value"].toByteArray()); setListRow(rowIndex, newRow); @@ -30,7 +29,7 @@ void ListKeyModel::updateRow(int rowIndex, const QVariantMap &row) void ListKeyModel::addRow(const QVariantMap &row) { if (!isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); addListRow(row["value"].toByteArray()); m_rowCount++; @@ -42,8 +41,7 @@ void ListKeyModel::removeRow(int i) return; if (isActualPositionChanged(i)) - throw Exception("Can't delete row from list, because row already has changed." - " Reload values and try again."); + throw Exception(QObject::tr("The row has been changed and can't be deleted now. Reload and try again.")); // Replace value by system string QString customSystemValue("---VALUE_REMOVED_BY_RDM---"); @@ -74,7 +72,7 @@ bool ListKeyModel::isActualPositionChanged(int row) result = m_connection->commandSync({"LRANGE", m_keyFullPath, QString::number(row).toLatin1(), QString::number(row).toLatin1()}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } QVariantList currentState = result.getValue().toList(); @@ -87,7 +85,7 @@ void ListKeyModel::addListRow(const QByteArray &value) try { m_connection->commandSync({"LPUSH", m_keyFullPath, value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } } @@ -97,7 +95,7 @@ void ListKeyModel::setListRow(int pos, const QByteArray &value) m_connection->commandSync({"LSET", m_keyFullPath, QString::number(pos).toLatin1(), value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } } @@ -107,6 +105,6 @@ void ListKeyModel::deleteListRow(int count, const QByteArray &value) m_connection->commandSync({"LREM", m_keyFullPath, QString::number(count).toLatin1(), value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } } diff --git a/src/app/models/key-models/setkey.cpp b/src/app/models/key-models/setkey.cpp index b1b75e408..737ba31e2 100644 --- a/src/app/models/key-models/setkey.cpp +++ b/src/app/models/key-models/setkey.cpp @@ -16,7 +16,7 @@ QString SetKeyModel::getType() void SetKeyModel::updateRow(int rowIndex, const QVariantMap &row) { if (!isRowLoaded(rowIndex) || !isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); QByteArray cachedRow = m_rowsCache[rowIndex]; QByteArray newRow(row["value"].toByteArray()); @@ -29,7 +29,7 @@ void SetKeyModel::updateRow(int rowIndex, const QVariantMap &row) void SetKeyModel::addRow(const QVariantMap &row) { if (!isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); addSetRow(row["value"].toByteArray()); m_rowCount++; @@ -54,7 +54,7 @@ void SetKeyModel::addSetRow(const QByteArray &value) try { m_connection->commandSync({"SADD", m_keyFullPath, value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } } @@ -63,6 +63,6 @@ RedisClient::Response SetKeyModel::deleteSetRow(const QByteArray &value) try { return m_connection->commandSync({"SREM", m_keyFullPath, value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } } diff --git a/src/app/models/key-models/sortedsetkey.cpp b/src/app/models/key-models/sortedsetkey.cpp index 7116f715f..98a8e0861 100644 --- a/src/app/models/key-models/sortedsetkey.cpp +++ b/src/app/models/key-models/sortedsetkey.cpp @@ -47,7 +47,7 @@ QVariant SortedSetKeyModel::getData(int rowIndex, int dataRole) void SortedSetKeyModel::updateRow(int rowIndex, const QVariantMap &row) { if (!isRowLoaded(rowIndex) || !isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); QPair cachedRow = m_rowsCache[rowIndex]; @@ -69,7 +69,7 @@ void SortedSetKeyModel::updateRow(int rowIndex, const QVariantMap &row) void SortedSetKeyModel::addRow(const QVariantMap &row) { if (!isRowValid(row)) - throw Exception("Invalid row"); + throw Exception(QObject::tr("Invalid row")); QPair cachedRow( row["value"].toByteArray(), @@ -91,7 +91,7 @@ void SortedSetKeyModel::removeRow(int i) try { m_connection->commandSync({"ZREM", m_keyFullPath, value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } m_rowCount--; @@ -106,7 +106,7 @@ bool SortedSetKeyModel::addSortedSetRow(const QByteArray &value, QByteArray scor result = m_connection->commandSync( {"ZADD", m_keyFullPath, score, value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } return result.getValue().toInt() == 1; @@ -117,7 +117,7 @@ void SortedSetKeyModel::deleteSortedSetRow(const QByteArray &value) try { m_connection->commandSync({"ZREM", m_keyFullPath, value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } } @@ -133,7 +133,7 @@ void SortedSetKeyModel::addLoadedRowsToCache(const QVariantList &rows, int rowSt ++item; if (item == rows.end()) - throw Exception("Partial data loaded from server"); + throw Exception(QObject::tr("Data was loaded from server partially.")); value.second = item->toByteArray(); result.push_back(value); diff --git a/src/app/models/key-models/stringkey.cpp b/src/app/models/key-models/stringkey.cpp index 0023f7ec8..41951f066 100644 --- a/src/app/models/key-models/stringkey.cpp +++ b/src/app/models/key-models/stringkey.cpp @@ -52,7 +52,7 @@ void StringKeyModel::updateRow(int rowIndex, const QVariantMap &row) try { result = m_connection->commandSync({"SET", m_keyFullPath, value}, m_dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw Exception("Connection error: " + QString(e.what())); + throw Exception(QObject::tr("Connection error: ") + QString(e.what())); } if (result.isOkMessage()) { @@ -84,7 +84,7 @@ void StringKeyModel::loadRows(unsigned long, unsigned long, std::functionconnect(true); } catch (const RedisClient::Connection::Exception& e) { - throw ConnectionsTree::Operations::Exception("Connection error: " + QString(e.what())); + throw ConnectionsTree::Operations::Exception(QObject::tr("Connection error: ") + QString(e.what())); } } if (m_connection->getServerVersion() < 2.8) - throw ConnectionsTree::Operations::Exception("RedisDesktopManager >= 0.9.0 doesn't support old " - "redis-servers (< 2.8). Please use RedisDesktopManager 0.8.8 or upgrade your redis-server."); + throw ConnectionsTree::Operations::Exception(QObject::tr("RedisDesktopManager >= 0.9.0 doesn't support old versions of " + "redis-server (< 2.8). Please use RedisDesktopManager 0.8.8 or upgrade your redis-server.")); RedisClient::DatabaseList availableDatabeses = m_connection->getKeyspaceInfo(); @@ -42,7 +42,7 @@ void TreeOperations::getDatabases(std::functioncommandSync("select", QString::number(dbIndex)); } catch (const RedisClient::Connection::Exception& e) { - throw ConnectionsTree::Operations::Exception("Connection error: " + QString(e.what())); + throw ConnectionsTree::Operations::Exception(QObject::tr("Connection error: ") + QString(e.what())); } if (!scanningResp.isOkMessage()) @@ -52,17 +52,20 @@ void TreeOperations::getDatabases(std::function callback) +void TreeOperations::getDatabaseKeys(uint dbIndex, QString filter, + std::function callback) { QString keyPattern = filter.isEmpty() ? static_cast(m_connection->getConfig()).keysPattern() : filter; try { m_connection->getDatabaseKeys(callback, keyPattern, dbIndex); } catch (const RedisClient::Connection::Exception& error) { - callback(RedisClient::Connection::RawKeysList(), QString("Cannot load keys: %1").arg(error.what())); + callback(RedisClient::Connection::RawKeysList(), QString(QObject::tr("Cannot load keys: %1")).arg(error.what())); } } @@ -102,7 +105,7 @@ void TreeOperations::deleteDbKey(ConnectionsTree::KeyItem& key, std::functioncommand({"DEL", key.getFullPath()}, this, cmdCallback, key.getDbIndex()); } catch (const RedisClient::Connection::Exception& e) { - throw ConnectionsTree::Operations::Exception("Delete key error: " + QString(e.what())); + throw ConnectionsTree::Operations::Exception(QObject::tr("Delete key error: ") + QString(e.what())); } } @@ -138,7 +141,7 @@ void TreeOperations::flushDb(int dbIndex, std::function ca RedisClient::Command::Callback cmdCallback = [this, callback](const RedisClient::Response&, const QString& error) { if (!error.isEmpty()) { - callback(QString("Cannot remove key: %1").arg(error)); + callback(QString(QObject::tr("Cannot remove key: %1")).arg(error)); return; } callback(QString()); @@ -147,6 +150,6 @@ void TreeOperations::flushDb(int dbIndex, std::function ca try { m_connection->command({"FLUSHDB"}, this, cmdCallback, dbIndex); } catch (const RedisClient::Connection::Exception& e) { - throw ConnectionsTree::Operations::Exception("FlushDB error: " + QString(e.what())); + throw ConnectionsTree::Operations::Exception(QObject::tr("FlushDB error: ") + QString(e.what())); } } diff --git a/src/app/qmlutils.cpp b/src/app/qmlutils.cpp index c574986da..01360f5fe 100644 --- a/src/app/qmlutils.cpp +++ b/src/app/qmlutils.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include bool QmlUtils::isBinaryString(const QVariant &value) { @@ -89,3 +91,38 @@ void QmlUtils::copyToClipboard(const QString &text) cb->clear(); cb->setText(text); } + +QtCharts::QDateTimeAxis* findDateTimeAxis(QtCharts::QXYSeries *series) +{ + using namespace QtCharts; + + QList axes = series->attachedAxes(); + + QDateTimeAxis* ax = nullptr; + + for (QAbstractAxis* axis : axes) { + if (axis->type() == QAbstractAxis::AxisTypeDateTime) { + ax = qobject_cast(axis); + return ax; + } + } + + return ax; +} + +void QmlUtils::addNewValueToDynamicChart(QtCharts::QXYSeries *series, double value) +{ + using namespace QtCharts; + + QDateTimeAxis* ax = findDateTimeAxis(series); + + if (series->count() == 0 && ax) { + ax->setMin(QDateTime::currentDateTime()); + } + + series->append(QDateTime::currentDateTime().toMSecsSinceEpoch(), value); + + if (series->attachedAxes().size() > 0 && ax) { + ax->setMax(QDateTime::currentDateTime()); + } +} diff --git a/src/app/qmlutils.h b/src/app/qmlutils.h index 133cc38be..cb7eb1207 100644 --- a/src/app/qmlutils.h +++ b/src/app/qmlutils.h @@ -3,6 +3,7 @@ #include #include #include +#include class QmlUtils : public QObject { @@ -17,4 +18,5 @@ class QmlUtils : public QObject Q_INVOKABLE QVariant toUtf(const QVariant &value); Q_INVOKABLE QString getPathFromUrl(const QUrl &url); Q_INVOKABLE void copyToClipboard(const QString &text); + Q_INVOKABLE void addNewValueToDynamicChart(QtCharts::QXYSeries* series, double value); }; diff --git a/src/configure b/src/configure index 61e42477b..30e77ddeb 100755 --- a/src/configure +++ b/src/configure @@ -48,7 +48,7 @@ if [ "$os_VENDOR" == "Ubuntu" ]; then sudo apt-get update -y sudo apt-get install qt${RDM_QT_VERSION:0:2}base qt${RDM_QT_VERSION:0:2}imageformats qt${RDM_QT_VERSION:0:2}tools \ - qt${RDM_QT_VERSION:0:2}declarative qt${RDM_QT_VERSION:0:2}quickcontrols -y + qt${RDM_QT_VERSION:0:2}declarative qt${RDM_QT_VERSION:0:2}quickcontrols qt${RDM_QT_VERSION:0:2}charts-no-lgpl qt${RDM_QT_VERSION:0:2}datavis-no-lgpl qt${RDM_QT_VERSION:0:2}svg -y fi print_title "Check deps" @@ -80,7 +80,7 @@ elif [ "$os_VENDOR" == "LinuxMint" ]; then sudo apt-get update -y sudo apt-get install qt${RDM_QT_VERSION:0:2}base qt${RDM_QT_VERSION:0:2}imageformats qt${RDM_QT_VERSION:0:2}tools \ - qt${RDM_QT_VERSION:0:2}declarative qt${RDM_QT_VERSION:0:2}quickcontrols -y + qt${RDM_QT_VERSION:0:2}declarative qt${RDM_QT_VERSION:0:2}quickcontrols qt${RDM_QT_VERSION:0:2}charts-no-lgpl qt${RDM_QT_VERSION:0:2}datavis-no-lgpl qt${RDM_QT_VERSION:0:2}svg -y fi print_title "Check deps" diff --git a/src/modules/bulk-operations/bulkoperationsmanager.cpp b/src/modules/bulk-operations/bulkoperationsmanager.cpp index 61831f1cb..7015840bd 100644 --- a/src/modules/bulk-operations/bulkoperationsmanager.cpp +++ b/src/modules/bulk-operations/bulkoperationsmanager.cpp @@ -46,7 +46,7 @@ void BulkOperations::Manager::runOperation(int, int) } if (r.isErrorMessage()) { - emit error(QString("Bulk operation error: %1").arg(r.getValue().toString())); + emit error(QString(QObject::tr("Bulk operation error: %1")).arg(r.getValue().toString())); return; } diff --git a/src/modules/common/tabmodel.cpp b/src/modules/common/tabmodel.cpp new file mode 100644 index 000000000..8fa931057 --- /dev/null +++ b/src/modules/common/tabmodel.cpp @@ -0,0 +1,14 @@ +#include "tabmodel.h" + + +TabModel::TabModel(QSharedPointer connection) +{ + // Clone connection + RedisClient::ConnectionConfig config = connection->getConfig(); + m_connection = QSharedPointer(new RedisClient::Connection(config)); +} + +QSharedPointer TabModel::getConnection() const +{ + return m_connection; +} diff --git a/src/modules/common/tabmodel.h b/src/modules/common/tabmodel.h new file mode 100644 index 000000000..feef39ae3 --- /dev/null +++ b/src/modules/common/tabmodel.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +class TabModel : public QObject +{ + Q_OBJECT + + public: + TabModel(QSharedPointer connection); + + virtual ~TabModel() {} + + virtual QString getName() const = 0; + + Q_INVOKABLE virtual void init() = 0; + + virtual QSharedPointer getConnection() const; + + protected: + QSharedPointer m_connection; +}; diff --git a/src/modules/common/tabviewmodel.cpp b/src/modules/common/tabviewmodel.cpp new file mode 100644 index 000000000..01cc00541 --- /dev/null +++ b/src/modules/common/tabviewmodel.cpp @@ -0,0 +1,95 @@ +#include "tabviewmodel.h" + +TabViewModel::TabViewModel(const ModelFactory& modelFactory) + : m_currentTabIndex(0), m_modelFactory(modelFactory) +{ +} + +QModelIndex TabViewModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(column); + return createIndex(row, 0); +} + +int TabViewModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_models.count(); +} + +QVariant TabViewModel::data(const QModelIndex &index, int role) const +{ + if (!isIndexValid(index)) + return QVariant(); + + QSharedPointer model = m_models.at(index.row()); + + if (model.isNull()) + return QVariant(); + + switch (role) { + case tabIndex: return index.row(); + case tabName: return model->getName(); + } + + return QVariant(); +} + +QHash TabViewModel::roleNames() const +{ + QHash roles; + roles[tabIndex] = "tabIndex"; + roles[tabName] = "tabName"; + return roles; +} + +void TabViewModel::closeTab(int i) +{ + if (!isIndexValid(index(i, 0))) + return; + + beginRemoveRows(QModelIndex(), i, i); + m_models.removeAt(i); + endRemoveRows(); +} + +void TabViewModel::setCurrentTab(int i) +{ + m_currentTabIndex = i; +} + +QObject *TabViewModel::getValue(int i) +{ + if (!isIndexValid(index(i, 0))) + return nullptr; + + return qobject_cast(m_models.at(i).data()); +} + +void TabViewModel::openTab(QSharedPointer connection) +{ + beginInsertRows(QModelIndex(), m_models.count(), m_models.count()); + m_models.append(m_modelFactory(connection)); + setCurrentTab(m_models.size() - 1); + emit changeCurrentTab(m_models.size() - 1); + endInsertRows(); +} + +void TabViewModel::closeAllTabsWithConnection(QSharedPointer connection) +{ + for (int index = 0; 0 <= index && index < m_models.size(); index++) { + auto model = m_models.at(index); + + if (model->getConnection() == connection) { + beginRemoveRows(QModelIndex(), index, index); + m_models.removeAt(index); + endRemoveRows(); + index--; + } + } +} + +bool TabViewModel::isIndexValid(const QModelIndex &index) const +{ + return 0 <= index.row() && index.row() < rowCount(); +} diff --git a/src/modules/common/tabviewmodel.h b/src/modules/common/tabviewmodel.h new file mode 100644 index 000000000..95791639d --- /dev/null +++ b/src/modules/common/tabviewmodel.h @@ -0,0 +1,58 @@ +#pragma once +#include +#include +#include "tabmodel.h" + +class TabViewModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles { + tabName = Qt::UserRole + 1, + tabIndex, + }; + + typedef std::function(QSharedPointer)> ModelFactory; + +public: + TabViewModel(const ModelFactory& modelFactory); + + QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const; + + int rowCount(const QModelIndex& parent = QModelIndex()) const; + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + + QHash roleNames() const override; + +public: // methods exported to QML + Q_INVOKABLE void closeTab(int i); + + Q_INVOKABLE void setCurrentTab(int i); + + Q_INVOKABLE QObject* getValue(int i); + +signals: + void changeCurrentTab(int i); + +public slots: + void openTab(QSharedPointer connection); + + void closeAllTabsWithConnection(QSharedPointer connection); + +private: + QList> m_models; + int m_currentTabIndex; + ModelFactory m_modelFactory; + + bool isIndexValid(const QModelIndex &index) const; + +}; + +template TabViewModel::ModelFactory getTabModelFactory() +{ + return TabViewModel::ModelFactory([](QSharedPointer c) { + return QSharedPointer(new T(c)); + }); +} diff --git a/src/modules/connections-tree/items/databaseitem.cpp b/src/modules/connections-tree/items/databaseitem.cpp index eeb50ea68..4c1ea2eca 100644 --- a/src/modules/connections-tree/items/databaseitem.cpp +++ b/src/modules/connections-tree/items/databaseitem.cpp @@ -40,7 +40,7 @@ DatabaseItem::DatabaseItem(unsigned int index, int keysCount, m_operations->openNewKeyDialog(m_index, [this]() { confirmAction(nullptr, - tr("Key was added. Do you want to reload keys in the selected database?"), + tr("Key was added. Do you want to reload keys in selected database?"), [this]() { reload(); m_keysCount++; }, tr("Key was added")); }); }); @@ -247,7 +247,7 @@ void DatabaseItem::liveUpdate() emit m_model.itemChanged(getSelf()); QMessageBox::warning(nullptr, tr("Live update was disabled"), tr("Live update was disabled due to exceeded keys limit. " - "Please specify more accurate filter or change limit in settings.")); + "Please specify filter more carrfully or change limit in settings.")); } else { clear(false); renderChilds(); diff --git a/src/modules/connections-tree/items/serveritem.cpp b/src/modules/connections-tree/items/serveritem.cpp index fc48c76b4..470c43d0a 100644 --- a/src/modules/connections-tree/items/serveritem.cpp +++ b/src/modules/connections-tree/items/serveritem.cpp @@ -42,7 +42,7 @@ ServerItem::ServerItem(const QString& name, QSharedPointer operation }); m_eventHandlers.insert("edit", [this]() { - confirmAction(nullptr, tr("All value and console tabs related to this" + confirmAction(nullptr, tr("Value and Console tabs related to this " "connection will be closed. Do you want to continue?"), [this]() { unload(); @@ -155,7 +155,7 @@ void ServerItem::load() m_operations->getDatabases(callback); } catch (const ConnectionsTree::Operations::Exception& e) { m_locked = false; - emit m_model.error("Cannot load databases:\n\n" + QString(e.what())); + emit m_model.error(QObject::tr("Cannot load databases:\n\n") + QString(e.what())); } } diff --git a/src/modules/console/consolemodel.cpp b/src/modules/console/consolemodel.cpp index 1110a5a3e..23e4dc715 100644 --- a/src/modules/console/consolemodel.cpp +++ b/src/modules/console/consolemodel.cpp @@ -4,11 +4,8 @@ using namespace Console; Model::Model(QSharedPointer connection) - : m_current_db(0) -{ - // Clone connection - RedisClient::ConnectionConfig config = connection->getConfig(); - m_connection = QSharedPointer(new RedisClient::Connection(config)); + : TabModel(connection), m_current_db(0) +{ } void Model::init() @@ -16,28 +13,23 @@ void Model::init() try { if (!m_connection->connect()) { - emit addOutput("Connection error. Check network connection", "error"); + emit addOutput(QObject::tr("Connection error. Check network connection"), "error"); return; } } catch (RedisClient::Connection::Exception&) { - emit addOutput("Invalid Connection. Check connection settings.", "error"); + emit addOutput(QObject::tr("Invalid Connection. Check connection settings."), "error"); return; } - emit addOutput("Connected.\n", "complete"); + emit addOutput(QObject::tr("Connected.\n"), "complete"); emit changePrompt(QString("%1:0>").arg(m_connection->getConfig().name()), true); } -QString Model::getName() +QString Model::getName() const { return m_connection->getConfig().name(); } -QSharedPointer Model::getConnection() -{ - return m_connection; -} - void Model::executeCommand(const QString & cmd) { if (cmd == "segfault") { //crash @@ -53,7 +45,7 @@ void Model::executeCommand(const QString & cmd) try { result = m_connection->commandSync(command); } catch (Connection::Exception& e) { - emit addOutput(QString("Connection error:") + QString(e.what()), "error"); + emit addOutput(QString(QObject::tr("Connection error:")) + QString(e.what()), "error"); return; } diff --git a/src/modules/console/consolemodel.h b/src/modules/console/consolemodel.h index 360c04f3a..06754c514 100644 --- a/src/modules/console/consolemodel.h +++ b/src/modules/console/consolemodel.h @@ -1,28 +1,19 @@ #pragma once - -#include -#include #include "exception.h" - -namespace RedisClient { class Connection; } - -#pragma once - -#include +#include "common/tabviewmodel.h" namespace Console { - class Model : public QObject - { + class Model : public TabModel + { Q_OBJECT ADD_EXCEPTION - - public: + public: Model(QSharedPointer connection); Q_INVOKABLE void init(); - QString getName(); - QSharedPointer getConnection(); + + QString getName() const override; public slots: void executeCommand(const QString&); @@ -31,8 +22,7 @@ namespace Console { void changePrompt(const QString &text, bool showPrompt); void addOutput(const QString &text, QString resultType); - private: - QSharedPointer m_connection; + private: int m_current_db; }; } diff --git a/src/modules/console/consoleviewmodel.cpp b/src/modules/console/consoleviewmodel.cpp deleted file mode 100644 index d694f0f51..000000000 --- a/src/modules/console/consoleviewmodel.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "consoleviewmodel.h" -#include -#include -#include -#include -#include - -Console::ViewModel::ViewModel() - : m_currentTabIndex(0) -{ -} - -void Console::ViewModel::openConsole(QSharedPointer connection) -{ - beginInsertRows(QModelIndex(), m_models.count(), m_models.count()); - - QSharedPointer model(new Model(connection)); - m_models.append(model); - setCurrentTab(m_models.size() - 1); - emit changeCurrentTab(m_models.size() - 1); - endInsertRows(); -} - -void Console::ViewModel::closeAllTabsWithConnection(QSharedPointer connection) -{ - for (int index = 0; 0 <= index && index < m_models.size(); index++) { - auto model = m_models.at(index); - - if (model->getConnection() == connection) { - beginRemoveRows(QModelIndex(), index, index); - m_models.removeAt(index); - endRemoveRows(); - index--; - } - } -} - -QModelIndex Console::ViewModel::index(int row, int, const QModelIndex&) const -{ - return createIndex(row, 0); -} - -int Console::ViewModel::rowCount(const QModelIndex&) const -{ - return m_models.count(); -} - -QVariant Console::ViewModel::data(const QModelIndex &index, int role) const -{ - if (!isIndexValid(index)) - return QVariant(); - - QSharedPointer model = m_models.at(index.row()); - - if (model.isNull()) - return QVariant(); - - switch (role) { - case consoleIndex: return index.row(); - case consoleName: return model->getName(); - } - - return QVariant(); -} - -QHash Console::ViewModel::roleNames() const -{ - QHash roles; - roles[consoleIndex] = "consoleIndex"; - roles[consoleName] = "consoleName"; - return roles; -} - -void Console::ViewModel::closeTab(int i) -{ - if (!isIndexValid(index(i, 0))) - return; - - beginRemoveRows(QModelIndex(), i, i); - m_models.removeAt(i); - endRemoveRows(); -} - -void Console::ViewModel::setCurrentTab(int i) -{ - m_currentTabIndex = i; -} - -QObject* Console::ViewModel::getValue(int i) -{ - if (!isIndexValid(index(i, 0))) - return nullptr; - - return qobject_cast(m_models.at(i).data()); -} - -bool Console::ViewModel::isIndexValid(const QModelIndex &index) const -{ - return 0 <= index.row() && index.row() < rowCount(); -} diff --git a/src/modules/console/consoleviewmodel.h b/src/modules/console/consoleviewmodel.h deleted file mode 100644 index eac76290b..000000000 --- a/src/modules/console/consoleviewmodel.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include "consolemodel.h" - -namespace Console { - -class ViewModel : public QAbstractListModel -{ - Q_OBJECT - -public: - enum Roles { - consoleName = Qt::UserRole + 1, - consoleIndex, - }; - -public: - ViewModel(); - - QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const; - int rowCount(const QModelIndex& parent = QModelIndex()) const; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - QHash roleNames() const override; - -public: // methods exported to QML - Q_INVOKABLE void closeTab(int i); - Q_INVOKABLE void setCurrentTab(int i); - Q_INVOKABLE QObject* getValue(int i); - -signals: - void changeCurrentTab(int i); - -public slots: - void openConsole(QSharedPointer connection); - void closeAllTabsWithConnection(QSharedPointer connection); - -private: - QList> m_models; - int m_currentTabIndex; - - bool isIndexValid(const QModelIndex &index) const; -}; - -} diff --git a/src/modules/server-stats/serverstatsmodel.cpp b/src/modules/server-stats/serverstatsmodel.cpp new file mode 100644 index 000000000..798dc417e --- /dev/null +++ b/src/modules/server-stats/serverstatsmodel.cpp @@ -0,0 +1,51 @@ +#include "serverstatsmodel.h" + + +ServerStats::Model::Model(QSharedPointer connection) + : TabModel(connection) +{ + m_updateTimer.setInterval(5000); + m_updateTimer.setSingleShot(false); + + QObject::connect(&m_updateTimer, &QTimer::timeout, this, [this]{ + + QList rawCmd {"INFO", "all"}; + m_connection->command(rawCmd, this, [this](RedisClient::Response r, QString err) { + + // TODO: emit error + m_serverInfo = RedisClient::ServerInfo::fromString(r.toRawString()).parsed.toVariantMap(); + + emit serverInfoChanged(); + }); + + }); +} + +void ServerStats::Model::init() +{ + try { + if (!m_connection->connect()) + { + emit error(QObject::tr("Connection error. Check network connection")); + return; + } + + m_updateTimer.start(); + + } catch (RedisClient::Connection::Exception&) { + emit error(QObject::tr("Invalid Connection. Check connection settings.")); + return; + } + + emit initialized(); +} + +QString ServerStats::Model::getName() const +{ + return QString(QObject::tr("Server %0")).arg(m_connection->getConfig().name()); +} + +QVariantMap ServerStats::Model::serverInfo() +{ + return m_serverInfo; +} diff --git a/src/modules/server-stats/serverstatsmodel.h b/src/modules/server-stats/serverstatsmodel.h new file mode 100644 index 000000000..b008b73e0 --- /dev/null +++ b/src/modules/server-stats/serverstatsmodel.h @@ -0,0 +1,34 @@ +#pragma once +#include "exception.h" +#include "common/tabviewmodel.h" + +namespace ServerStats { + + class Model : public TabModel + { + Q_OBJECT + ADD_EXCEPTION + + Q_PROPERTY(QVariantMap serverInfo READ serverInfo NOTIFY serverInfoChanged) + + public: + Model(QSharedPointer connection); + + Q_INVOKABLE void init() override; + + QString getName() const override; + + QVariantMap serverInfo(); + + signals: + void error(const QString& error); + + void initialized(); + + void serverInfoChanged(); + + private: + QTimer m_updateTimer; + QVariantMap m_serverInfo; + }; +} diff --git a/src/modules/value-editor/valueviewmodel.cpp b/src/modules/value-editor/valueviewmodel.cpp index 07d65d158..25611e924 100644 --- a/src/modules/value-editor/valueviewmodel.cpp +++ b/src/modules/value-editor/valueviewmodel.cpp @@ -81,7 +81,7 @@ void ValueEditor::ValueViewModel::loadRows(int start, int count) return; } - QString msg = QString("Cannot load key value: %1"); + QString msg = QString(QObject::tr("Cannot load key value: %1")); try { // NOTE(u_glide): Do so for proper rendering of QML table diff --git a/src/modules/value-editor/viewmodel.cpp b/src/modules/value-editor/viewmodel.cpp index c6d5278e8..16a51dd78 100644 --- a/src/modules/value-editor/viewmodel.cpp +++ b/src/modules/value-editor/viewmodel.cpp @@ -18,9 +18,8 @@ void ValueEditor::ViewModel::openTab(QSharedPointer con m_keyFactory->loadKey(connection, key.getFullPath(), key.getDbIndex(), [this, inNewTab, &key](QSharedPointer keyModel, const QString& error) { - if (keyModel.isNull() || !error.isEmpty()) { - QString msg("Cannot open value tab:\n%1"); - emit keyError(-1, msg.arg(error)); + if (keyModel.isNull() || !error.isEmpty()) { + emit keyError(-1, QString("%1:\n%2").arg(QObject::tr("Cannot open value tab")).arg(error)); return; } @@ -37,7 +36,7 @@ void ValueEditor::ViewModel::openTab(QSharedPointer con }); // TODO: add empty key model for loading } catch (...) { - emit keyError(-1, "Connection error. Can't open value tab. "); + emit keyError(-1, QObject::tr("Connection error. Can't open value tab. ")); } } @@ -138,7 +137,7 @@ void ValueEditor::ViewModel::addKey(QString keyName, QString keyType, m_newKeyRequest = NewKeyRequest(); } catch (const Model::Exception& e) { if (jsCallback.isCallable()) - jsCallback.call(QJSValueList { "Can't add new key: " + QString(e.what()) }); + jsCallback.call(QJSValueList { QObject::tr("Can't add new key: ") + QString(e.what()) }); } } @@ -153,7 +152,7 @@ void ValueEditor::ViewModel::renameKey(int i, const QString& newKeyName) value->setKeyName(printableStringToBinary(newKeyName)); emit dataChanged(index(i, 0), index(i, 0)); } catch (const Model::Exception& e) { - emit keyError(i, "Can't rename key: " + QString(e.what())); + emit keyError(i, QObject::tr("Can't rename key: ") + QString(e.what())); } } @@ -167,7 +166,7 @@ void ValueEditor::ViewModel::removeKey(int i) try { value->removeKey(); } catch (const Model::Exception& e) { - emit keyError(i, "Can't remove key: " + QString(e.what())); + emit keyError(i, QObject::tr("Can't remove key: ") + QString(e.what())); } } @@ -182,7 +181,7 @@ void ValueEditor::ViewModel::setTTL(int i, const QString& newTTL) value->setTTL(newTTL.toLong()); emit dataChanged(index(i, 0), index(i, 0)); } catch (const Model::Exception& e) { - emit keyError(i, "Can't set key ttl: " + QString(e.what())); + emit keyError(i, QObject::tr("Can't set key ttl: ") + QString(e.what())); } } @@ -196,7 +195,7 @@ void ValueEditor::ViewModel::closeTab(int i) m_valueModels.removeAt(i); endRemoveRows(); } catch (const Model::Exception& e) { - emit keyError(i, "Can't remove key: " + QString(e.what())); + emit keyError(i, QObject::tr("Can't close key tab: ") + QString(e.what())); } } diff --git a/src/qml/AppToolBar.qml b/src/qml/AppToolBar.qml index 54be0fa81..272b21dbe 100644 --- a/src/qml/AppToolBar.qml +++ b/src/qml/AppToolBar.qml @@ -11,7 +11,7 @@ ToolBar { anchors.fill: parent Button { iconSource: "qrc:/images/add.svg" - text: "Connect to Redis Server" + text: qsTr("Connect to Redis Server") Layout.preferredWidth: 230 onClicked: { @@ -22,14 +22,14 @@ ToolBar { ToolButton { iconSource: "qrc:/images/import.svg" - text: "Import Connections" + text: qsTr("Import Connections") tooltip: text onClicked: importConnectionsDialog.open() FileDialog { id: importConnectionsDialog - title: "Import Connections" + title: qsTr("Import Connections") nameFilters: ["RDM Connections (*.xml *.json)"] selectExisting: true onAccepted: connectionsManager.importConnections(qmlUtils.getPathFromUrl(fileUrl)) @@ -38,14 +38,14 @@ ToolBar { ToolButton { iconSource: "qrc:/images/export.svg" - text: "Export Connections" + text: qsTr("Export Connections") tooltip: text onClicked: exportConnectionsDialog.open() FileDialog { id: exportConnectionsDialog - title: "Import Connections" + title: qsTr("Import Connections") nameFilters: ["RDM Connections (*.json)"] selectExisting: false onAccepted: connectionsManager.saveConnectionsConfigToFile(qmlUtils.getPathFromUrl(fileUrl)) @@ -58,7 +58,7 @@ ToolBar { Button { iconSource: "qrc:/images/settings.svg" - text: "Settings" + text: qsTr("Settings") onClicked: { settingsDialog.open() diff --git a/src/qml/ConnectionSettignsDialog.qml b/src/qml/ConnectionSettignsDialog.qml index 95e9d1cdf..7e04e1975 100644 --- a/src/qml/ConnectionSettignsDialog.qml +++ b/src/qml/ConnectionSettignsDialog.qml @@ -7,7 +7,7 @@ import "./common" Dialog { id: root - title: !settings || !settings.name ? "New Connection Settings" : "Edit Connection Settings - " + settings.name + title: !settings || !settings.name ? qsTr("New Connection Settings") : qsTr("Edit Connection Settings - %1").arg(settings.name) property var settings property string quickStartGuideUrl: "http://docs.redisdesktop.com/en/latest/quick-start/" @@ -113,36 +113,36 @@ Dialog { Tab { id: mainTab - title: "Connection Settings" + title: qsTr("Connection Settings") ColumnLayout { anchors.fill: parent anchors.margins: 10 GroupBox { - title: "Main Settings" + title: qsTr("Main Settings") Layout.fillWidth: true GridLayout { anchors.fill: parent columns: 2 - Label { text: "Name:" } + Label { text: qsTr("Name:") } TextField { id: connectionName Layout.fillWidth: true - placeholderText: "Connection Name" + placeholderText: qsTr("Connection Name") text: root.settings ? root.settings.name : "" Component.onCompleted: root.items.push(connectionName) onTextChanged: root.settings.name = text } - Label { text: "Address:" } + Label { text: qsTr("Address:") } AddressInput { id: connectionAddress - placeholderText: "redis-server host" + placeholderText: qsTr("redis-server host") host: root.settings ? root.settings.host : "127.0.0.1" port: root.settings ? root.settings.port : 6379 Component.onCompleted: root.items.push(connectionAddress) @@ -150,12 +150,12 @@ Dialog { onPortChanged: if (root.settings) root.settings.port = port } - Label { text: "Auth:" } + Label { text: qsTr("Auth:") } PasswordInput { id: connectionAuth Layout.fillWidth: true - placeholderText: "(Optional) redis-server authentication password" + placeholderText: qsTr("(Optional) redis-server authentication password") text: root.settings ? root.settings.auth : "" onTextChanged: root.settings.auth = text } @@ -163,7 +163,7 @@ Dialog { } GroupBox { - title: "Security" + title: qsTr("Security") Layout.columnSpan: 2 Layout.fillWidth: true @@ -175,7 +175,7 @@ Dialog { columns: 2 RadioButton { - text: "None" + text: qsTr("None") checked: root.settings ? !root.settings.sslEnabled && !root.settings.useSshTunnel() : true exclusiveGroup: connectionSecurityExGroup Layout.columnSpan: 2 @@ -184,7 +184,7 @@ Dialog { RadioButton { id: sslRadioButton Layout.columnSpan: 2 - text: "SSL" + text: qsTr("SSL") exclusiveGroup: connectionSecurityExGroup checked: root.settings ? root.settings.sslEnabled : false Component.onCompleted: root.sslEnabled = Qt.binding(function() { return sslRadioButton.checked }) @@ -207,38 +207,38 @@ Dialog { columns: 2 Layout.fillWidth: true - Label { text: "Public Key:" } + Label { text: qsTr("Public Key:") } FilePathInput { id: sslLocalCertPath Layout.fillWidth: true - placeholderText: "(Optional) Public Key in PEM format" + placeholderText: qsTr("(Optional) Public Key in PEM format") nameFilters: [ "Public Key in PEM format (*.pem *.crt)" ] - title: "Select public key in PEM format" + title: qsTr("Select public key in PEM format") path: root.settings ? root.settings.sslLocalCertPath : "" onPathChanged: root.settings.sslLocalCertPath = path } - Label { text: "Private Key:" } + Label { text: qsTr("Private Key:") } FilePathInput { id: sslPrivateKeyPath Layout.fillWidth: true - placeholderText: "(Optional) Private Key in PEM format" + placeholderText: qsTr("(Optional) Private Key in PEM format") nameFilters: [ "Private Key in PEM format (*.pem *.key)" ] - title: "Select private key in PEM format" + title: qsTr("Select private key in PEM format") path: root.settings ? root.settings.sslPrivateKeyPath : "" onPathChanged: root.settings.sslPrivateKeyPath = path } - Label { text: "Authority:" } + Label { text: qsTr("Authority:") } FilePathInput { id: sslCaCertPath Layout.fillWidth: true - placeholderText: "(Optional) Authority in PEM format" + placeholderText: qsTr("(Optional) Authority in PEM format") nameFilters: [ "Authority file in PEM format (*.pem *.crt)" ] - title: "Select authority file in PEM format" + title: qsTr("Select authority file in PEM format") path: root.settings ? root.settings.sslCaCertPath : "" onPathChanged: root.settings.sslCaCertPath = path } @@ -247,7 +247,7 @@ Dialog { RadioButton { id: sshRadioButton Layout.columnSpan: 2 - text: "SSH Tunnel" + text: qsTr("SSH Tunnel") exclusiveGroup: connectionSecurityExGroup checked: root.settings ? root.settings.useSshTunnel() : false Component.onCompleted: root.sshEnabled = Qt.binding(function() { return sshRadioButton.checked }) @@ -271,11 +271,11 @@ Dialog { columns: 2 Layout.fillWidth: true - Label { text: "SSH Address:" } + Label { text: qsTr("SSH Address:") } AddressInput { id: sshAddress - placeholderText: "Remote Host with SSH server" + placeholderText: qsTr("Remote Host with SSH server") port: root.settings ? root.settings.sshPort : 22 host: root.settings ? root.settings.sshHost : "" Component.onCompleted: root.sshItems.push(sshAddress) @@ -283,19 +283,19 @@ Dialog { onPortChanged: root.settings.sshPort = port } - Label { text: "SSH User:" } + Label { text: qsTr("SSH User:") } TextField { id: sshUser Layout.fillWidth: true - placeholderText: "Valid SSH User Name" + placeholderText: qsTr("Valid SSH User Name") text: root.settings ? root.settings.sshUser : "" Component.onCompleted: root.sshItems.push(sshUser) onTextChanged: root.settings.sshUser = text } GroupBox { - title: "Private Key" + title: qsTr("Private Key") checkable: true checked: root.settings ? root.settings.sshPrivateKey : false @@ -305,16 +305,16 @@ Dialog { FilePathInput { id: sshPrivateKey anchors.fill: parent - placeholderText: "Path to Private Key in PEM format" + placeholderText: qsTr("Path to Private Key in PEM format") nameFilters: [ "Private key in PEM format (*)" ] - title: "Select private key in PEM format" + title: qsTr("Select private key in PEM format") path: root.settings ? root.settings.sshPrivateKey : "" onPathChanged: root.settings.sshPrivateKey = path } } GroupBox { - title: "Password" + title: qsTr("Password") checkable: true checked: root.settings ? root.settings.sshPassword : true @@ -324,7 +324,7 @@ Dialog { PasswordInput { id: sshPassword anchors.fill: parent - placeholderText: "SSH User Password" + placeholderText: qsTr("SSH User Password") text: root.settings ? root.settings.sshPassword : "" onTextChanged: root.settings.sshPassword = text } @@ -336,7 +336,7 @@ Dialog { } Tab { - title: "Advanced Settings" + title: qsTr("Advanced Settings") GridLayout { anchors.fill: parent @@ -344,44 +344,43 @@ Dialog { columns: 2 - Label { text: "Keys glob-style pattern:" } + Label { text: qsTr("Keys glob-style pattern:") } TextField { id: keysPattern Layout.fillWidth: true - placeholderText: "Pattern which defines loaded keys from redis-server" + placeholderText: qsTr("Pattern which defines loaded keys from redis-server") text: root.settings ? root.settings.keysPattern : "*" Component.onCompleted: root.items.push(keysPattern) onTextChanged: root.settings.keysPattern = text } - Label { text: "Namespace Separator:" } + Label { text: qsTr("Namespace Separator:") } TextField { id: namespaceSeparator Layout.fillWidth: true - placeholderText: "Separator used for namespace extraction from keys" + placeholderText: qsTr("Separator used for namespace extraction from keys") text: root.settings ? root.settings.namespaceSeparator : ":" onTextChanged: root.settings.namespaceSeparator = text } - Label { text: "Connection Timeout (sec):"} + Label { text: qsTr("Connection Timeout (sec):") } SpinBox { id: executeTimeout Layout.fillWidth: true minimumValue: 30 maximumValue: 100000 - value: { - console.log("Execution timeout:", root.settings.executeTimeout) + value: { return root.settings ? (root.settings.executeTimeout / 1000.0) : 60 } onValueChanged: root.settings.executeTimeout = value * 1000 } - Label { text: "Execution Timeout (sec):"} + Label { text: qsTr("Execution Timeout (sec):")} SpinBox { id: connectionTimeout @@ -411,7 +410,7 @@ Dialog { RowLayout { anchors.centerIn: parent Image {source: "qrc:/images/alert.svg"} - Text { text: "Invalid settings detected!"} + Text { text: qsTr("Invalid settings detected!")} } } @@ -420,13 +419,13 @@ Dialog { Button { iconSource: "qrc:/images/offline.svg" - text: "Test Connection" + text: qsTr("Test Connection") onClicked: root.testConnection(root.settings) } ToolButton { iconSource: "qrc:/images/help.svg" - text: "Quick Start Guide" + text: qsTr("Quick Start Guide") tooltip: text onClicked: Qt.openUrlExternally(root.quickStartGuideUrl) } @@ -434,7 +433,7 @@ Dialog { Item { Layout.fillWidth: true } Button { - text: "OK" + text: qsTr("OK") onClicked: { if (root.validate()) { root.saveConnection(root.settings) @@ -446,7 +445,7 @@ Dialog { } Button { - text: "Cancel" + text: qsTr("Cancel") onClicked: root.close() } } diff --git a/src/qml/GlobalSettings.qml b/src/qml/GlobalSettings.qml index 259e545c9..c18d6dc7e 100644 --- a/src/qml/GlobalSettings.qml +++ b/src/qml/GlobalSettings.qml @@ -8,7 +8,7 @@ import "./settings" Dialog { id: root - title: "Settings" + title: qsTr("Settings") contentItem: Item { implicitWidth: 800 @@ -19,7 +19,7 @@ Dialog { anchors.margins: 20 Text { - text: "Appearance" + text: qsTr("Appearance") font.pixelSize: 20 } @@ -43,12 +43,12 @@ Dialog { model: ["8", "9", "10", "11", "12"] value: "10" - label: "Font Size" - description: "in pixels" + label: qsTr("Font Size") + description: qsTr("in pixels") } Text { - text: "Connections Tree" + text: qsTr("Connections Tree") font.pixelSize: 20 } @@ -59,8 +59,8 @@ Dialog { Layout.preferredHeight: 40 value: true - label: "Reopen namespaces on reload" - description: "(Disable to improve treeview performance)" + label: qsTr("Reopen namespaces on reload") + description: qsTr("(Disable to improve treeview performance)") } BoolOption { @@ -70,8 +70,8 @@ Dialog { Layout.preferredHeight: 40 value: true - label: "Enable key sorting in tree" - description: "(Disable to improve treeview performance)" + label: qsTr("Enable key sorting in tree") + description: qsTr("(Disable to improve treeview performance)") } IntOption { @@ -83,7 +83,7 @@ Dialog { min: 100 max: 100000 value: 1000 - label: "Live update maximum allowed keys" + label: qsTr("Live update maximum allowed keys") description: "" } @@ -96,7 +96,7 @@ Dialog { min: 3 max: 100000 value: 10 - label: "Live update interval (in seconds)" + label: qsTr("Live update interval (in seconds)") description: "" } @@ -109,7 +109,7 @@ Dialog { Item { Layout.fillWidth: true; } Button { - text: "OK" + text: qsTr("OK") onClicked: root.close() } } diff --git a/src/qml/QuickStartDialog.qml b/src/qml/QuickStartDialog.qml index ff1716da5..9ed973292 100644 --- a/src/qml/QuickStartDialog.qml +++ b/src/qml/QuickStartDialog.qml @@ -6,7 +6,7 @@ import "./common" Dialog { id: root - title: "Explore Redis Desktop Manager" + title: qsTr("Explore Redis Desktop Manager") contentItem: Item { implicitWidth: msgLayout.implicitWidth + 50 @@ -24,9 +24,8 @@ Dialog { RichTextWithLinks { - html: "

Before using Redis Desktop Manager (RDM) take a look on the " - + "Quick Start Guide" - + "

" + html: "

" + qsTr("Before using Redis Desktop Manager (RDM) take a look on the %1").arg( + "" + qsTr("Quick Start Guide")+ "") + "

" } } @@ -35,7 +34,7 @@ Dialog { Item { Layout.fillWidth: true; } Button { - text: "Ok" + text: qsTr("OK") onClicked: root.close() } } diff --git a/src/qml/WelcomeTab.qml b/src/qml/WelcomeTab.qml index c378d3a80..e50ca70cc 100644 --- a/src/qml/WelcomeTab.qml +++ b/src/qml/WelcomeTab.qml @@ -26,7 +26,7 @@ BetterTab { ColumnLayout { RichTextWithLinks { html: 'Redis Desktop Manager'} RichTextWithLinks { html: ' Version ' + Qt.application.version +'     ' - + 'Developed by - Igor Malinovskiy in ' + + 'Developed by - Igor Malinovskiy in ' + '  Ukraine'} } } @@ -43,7 +43,7 @@ BetterTab { } RichTextWithLinks { Layout.fillWidth: true - html: ' Wiki' + html: ' Documentation' } RichTextWithLinks { Layout.fillWidth: true @@ -75,8 +75,8 @@ BetterTab { html: '' + ' Redis Desktop Manager uses Google Analytics to track which features you are using. ' + '
 This data helps me to develop features that you actually need :)' - + '
 RDM doesn\'t send any sensitive information or data from your databases.' - + ' More >' + + '
 RDM doesn\'t send any sensitive information or data from your databases.' + + ' More >' + '
'} Rectangle { color: "#cccccc"; Layout.preferredHeight: 1; Layout.fillWidth: true } diff --git a/src/qml/app.qml b/src/qml/app.qml index 2fb32ff92..74b17f7d8 100644 --- a/src/qml/app.qml +++ b/src/qml/app.qml @@ -11,6 +11,7 @@ import "./common" import "./value-editor" import "./connections-tree" import "./console" +import "./server-info" import "./bulk-operations" ApplicationWindow { @@ -67,9 +68,9 @@ ApplicationWindow { onTestConnection: { if (connectionsManager.testConnectionSettings(settings)) { - notification.showMsg("Successful connection to redis-server") + notification.showMsg(qsTr("Successful connection to redis-server")) } else { - notification.showError("Can't connect to redis-server") + notification.showError(qsTr("Can't connect to redis-server")) } } @@ -161,12 +162,30 @@ ApplicationWindow { } WelcomeTab { + id: welcomeTab clip: true objectName: "rdm_qml_welcome_tab" property bool not_mapped: true onClose: tabs.removeTab(index) + + function closeIfOpened() { + var welcomeTab = tabs.getTab(0) + + if (welcomeTab && welcomeTab.not_mapped) + tabs.removeTab(0) + } + } + + ServerInfoTabs { + model: serverStatsModel + } + + Connections { + target: serverStatsModel + + onRowsInserted: welcomeTab.closeIfOpened() } ValueTabs { @@ -188,13 +207,7 @@ ApplicationWindow { notification.showError(error) } - onCloseWelcomeTab: { - var welcomeTab = tabs.getTab(0) - - if (welcomeTab && welcomeTab.not_mapped) - tabs.removeTab(0) - } - + onRowsInserted: welcomeTab.closeIfOpened() onNewKeyDialog: addNewKeyDialog.open() } } diff --git a/src/qml/bulk-operations/BulkOperationsDialog.qml b/src/qml/bulk-operations/BulkOperationsDialog.qml index 40c632f4a..d796ef24e 100644 --- a/src/qml/bulk-operations/BulkOperationsDialog.qml +++ b/src/qml/bulk-operations/BulkOperationsDialog.qml @@ -51,12 +51,12 @@ Dialog { states: [ State { name: "delete_keys" - PropertyChanges { target: operationLabel; text: "Delete keys" } + PropertyChanges { target: operationLabel; text: qsTr("Delete keys") } PropertyChanges { target: targetConnectionSettings; visible: false } }, State { name: "copy_keys" - PropertyChanges { target: operationLabel; text: "Copy keys" } + PropertyChanges { target: operationLabel; text: qsTr("Copy keys") } PropertyChanges { target: targetConnectionSettings; visible: true } } ] @@ -84,7 +84,7 @@ Dialog { columns: 2 Label { - text: "Redis Server:" + text: qsTr("Redis Server:") } Label { @@ -92,7 +92,7 @@ Dialog { } Label { - text: "Database number:" + text: qsTr("Database number:") } Label { @@ -100,7 +100,7 @@ Dialog { } Label { - text: "Key pattern:" + text: qsTr("Key pattern:") } Label { @@ -116,7 +116,7 @@ Dialog { columns: 2 Label { - text: "Destination Redis Server:" + text: qsTr("Destination Redis Server:") } ComboBox { @@ -124,7 +124,7 @@ Dialog { } Label { - text: "Destination Redis Server Database Index:" + text: qsTr("Destination Redis Server Database Index:") } ComboBox { @@ -143,7 +143,7 @@ Dialog { Layout.fillHeight: true Text { - text: "Affected keys:" + text: qsTr("Affected keys:") } Rectangle { id: listContainer @@ -181,7 +181,7 @@ Dialog { onOperationFinished: { affectedKeysListView.model = [] uiBlocker.visible = false - bulkSuccessNotification.text = "Bulk Operation finished." + bulkSuccessNotification.text = qsTr("Bulk Operation finished.") bulkSuccessNotification.open() } @@ -204,12 +204,12 @@ Dialog { Item { Layout.fillWidth: true; } Button { - text: "Delete Keys" + text: qsTr("Delete Keys") onClicked: bulkConfirmation.open() } Button { - text: "Cancel" + text: qsTr("Cancel") onClicked: root.close() } } @@ -263,8 +263,8 @@ Dialog { MessageDialog { id: bulkConfirmation - title: "Confirmation" - text: "Do you really want to perform bulk operation?" + title: qsTr("Confirmation") + text: qsTr("Do you really want to perform bulk operation?") onYes: { bulkOperations.runOperation() } diff --git a/src/qml/common/BetterTabView.qml b/src/qml/common/BetterTabView.qml index 790234daa..1d0c561c3 100644 --- a/src/qml/common/BetterTabView.qml +++ b/src/qml/common/BetterTabView.qml @@ -32,7 +32,7 @@ TabView { AnimatedImage { source: { - var icon = root.getTab(styleData.index).icon + var icon = root.getTab(styleData.index) !== undefined ? root.getTab(styleData.index).icon : "" if (icon && icon.indexOf(".gif") > -1) { visible = true @@ -49,7 +49,7 @@ TabView { Image { source: { - var icon = root.getTab(styleData.index).icon + var icon = root.getTab(styleData.index) !== undefined ? root.getTab(styleData.index).icon : "" if (icon && icon.indexOf(".gif") == -1) { visible = true @@ -72,12 +72,12 @@ TabView { } Item { - visible: root.getTab(styleData.index) && !root.getTab(styleData.index).closable + visible: root.getTab(styleData.index) !== undefined && !root.getTab(styleData.index).closable Layout.preferredWidth: 3 } ImageButton { - visible: root.getTab(styleData.index) && root.getTab(styleData.index).closable + visible: root.getTab(styleData.index) !== undefined && root.getTab(styleData.index).closable Layout.preferredWidth: 18 Layout.preferredHeight: 18 diff --git a/src/qml/common/PasswordInput.qml b/src/qml/common/PasswordInput.qml index a19332957..deed4a30e 100644 --- a/src/qml/common/PasswordInput.qml +++ b/src/qml/common/PasswordInput.qml @@ -17,6 +17,6 @@ RowLayout { CheckBox { id: passwordMask - text: "Show password" + text: qsTr("Show password") } } diff --git a/src/qml/connections-tree/menu/database.qml b/src/qml/connections-tree/menu/database.qml index d3f94ad59..f45fbc0be 100644 --- a/src/qml/connections-tree/menu/database.qml +++ b/src/qml/connections-tree/menu/database.qml @@ -136,7 +136,7 @@ RowLayout { TextField { id: filterText - placeholderText: "Enter Filter" + placeholderText: qsTr("Enter Filter") text: { if (!connectionsManager) diff --git a/src/qml/console/Consoles.qml b/src/qml/console/Consoles.qml index 5bcd60816..2ec841023 100644 --- a/src/qml/console/Consoles.qml +++ b/src/qml/console/Consoles.qml @@ -13,18 +13,18 @@ Repeater { BetterTab { id: tab - title: consoleName + title: tabName icon: "qrc:/images/console.png" onClose: { - consoleModel.closeTab(consoleIndex) + consoleModel.closeTab(tabIndex) } QConsole { id: redisConsole Connections { - target: consoleModel ? consoleModel.getValue(consoleIndex) : null + target: consoleModel ? consoleModel.getValue(tabIndex) : null onChangePrompt: { redisConsole.setPrompt(text, showPrompt) @@ -36,20 +36,20 @@ Repeater { } onExecCommand: { - consoleModel.getValue(consoleIndex).executeCommand(command) + consoleModel.getValue(tabIndex).executeCommand(command) } Timer { id: initTimer onTriggered: { - consoleModel.getValue(consoleIndex).init() + consoleModel.getValue(tabIndex).init() } } Component.onCompleted: { tab.icon = Qt.binding(function() { - return redisConsole.busy ? "qrc:/images/loader.gif" : "qrc:/images/console.png" + return redisConsole.busy ? "qrc:/images/loader.gif" : "qrc:/images/console.svg" }) initTimer.start() } diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc index ae1f82820..2c75ba492 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -42,5 +42,6 @@ bulk-operations/BulkOperationsDialog.qml value-editor/editors/formatters/json-tools.js settings/ComboboxOption.qml + server-info/ServerInfoTabs.qml diff --git a/src/qml/server-info/ServerInfoTabs.qml b/src/qml/server-info/ServerInfoTabs.qml new file mode 100644 index 000000000..7124a1d1c --- /dev/null +++ b/src/qml/server-info/ServerInfoTabs.qml @@ -0,0 +1,282 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Dialogs 1.2 +import QtQuick.Window 2.2 +import QtCharts 2.0 +import MeasurementProtocol 1.0 +import "./../common" + + +Repeater { + id: root + + BetterTab { + id: tab + title: tabName + icon: "qrc:/images/console.svg" + + property var model + + onClose: { + serverStatsModel.closeTab(tabIndex) + } + + Rectangle { + anchors.fill: parent + + color: "white" + + ColumnLayout { + + anchors.fill: parent + anchors.margins: 20 + + GridLayout { + Layout.fillWidth: true + + rows: 2 + flow: GridLayout.TopToBottom + + Text { + Layout.preferredWidth: tab.width / 5 + + text: qsTr("Redis Version") + font.pointSize: 15 + color: "grey" + } + + Label { id: redisVersionLabel; text: "N/A"; font.pointSize: 18 } + + Text { + Layout.preferredWidth: tab.width / 5 + + text: qsTr("Used memory") + font.pointSize: 15 + color: "grey" + } + + Label { id: usedMemoryLabel; text: "N/A"; font.pointSize: 18 } + + Text { + Layout.preferredWidth: tab.width / 5 + + text: qsTr("Clients") + font.pointSize: 15 + color: "grey" + } + + Label { id: connectedClientsLabel; text: "N/A"; font.pointSize: 18 } + + Text { + Layout.preferredWidth: tab.width / 5 + + text: qsTr("Commands Processed") + font.pointSize: 15 + color: "grey" + wrapMode: Text.WordWrap + } + + Label { id: totalCommandsProcessedLabel; text: "N/A"; font.pointSize: 18 } + + + Text { + Layout.preferredWidth: tab.width / 5 + + text: qsTr("Uptime") + font.pointSize: 15 + color: "grey" + } + + Label { id: uptimeLabel; text: "N/A"; font.pointSize: 18 } + + + Connections { + target: tab.model? tab.model : null + + onServerInfoChanged: { + usedMemoryLabel.text = tab.model.serverInfo["memory"]["used_memory_human"] + redisVersionLabel.text = tab.model.serverInfo["server"]["redis_version"] + connectedClientsLabel.text = tab.model.serverInfo["clients"]["connected_clients"] + totalCommandsProcessedLabel.text = tab.model.serverInfo["stats"]["total_commands_processed"] + uptimeLabel.text = tab.model.serverInfo["server"]["uptime_in_days"] + " days" + } + } + } + + RowLayout { + Layout.fillHeight: true + Layout.fillWidth: true + + ChartView { + id: view + + Layout.fillHeight: true + Layout.preferredWidth: tab.width * 0.5 + + title: qsTr("Memory Usage") + antialiasing: true + + DateTimeAxis { + id: axisX + min: new Date() + format: "HH:mm:ss" + } + + ValueAxis { + id: axisY + min: 0 + titleText: qsTr("Mb") + } + + function toMsecsSinceEpoch(date) { + var msecs = date.getTime(); + return msecs; + } + + SplineSeries { + id: used_memory_series + name: "used_memory" + axisX: axisX + axisY: axisY + } + + SplineSeries { + id: used_memory_rss_series + name: "used_memory_rss" + axisX: axisX + axisY: axisY + } + + SplineSeries { + id: used_memory_lua_series + name: "used_memory_lua" + axisX: axisX + axisY: axisY + } + + SplineSeries { + id: used_memory_peak_series + name: "used_memory_peak" + axisX: axisX + axisY: axisY + } + } + + TabView { + Layout.fillHeight: true + Layout.minimumWidth: tab.width * 0.4 + + Tab { + title: qsTr("Server Info") + + TableView { + id: serverInfoListView + + Layout.fillHeight: true + Layout.minimumWidth: 400 + + model: tab.model && tab.model.serverInfo ? tab.model.serverInfo["server"] : null + + TableViewColumn { + role: "name" + title: qsTr("Property") + width: 200 + } + + TableViewColumn { + role: "value" + title: qsTr("Value") + width: 200 + } + + Connections { + target: tab.model? tab.model : null + + onServerInfoChanged: { + // fill tab with info + var serverInfo = [] + + for (var key in tab.model.serverInfo["server"]) + { + var property = {"name": key, "value": tab.model.serverInfo["server"][key]} + serverInfo.push(property) + } + + serverInfoListView.model = serverInfo + } + } + } + } +// TODO: Add UI for slowlog (view/reset) and clients (view/kill) +// Tab { +// title: "Slowlog" +// } + +// Tab { +// title: "Clients" +// } + } + + Connections { + target: tab.model? tab.model : null + + onServerInfoChanged: { + var getValue = function (name) { + return parseFloat(tab.model.serverInfo["memory"][name] ) / (1024 * 1024) + } + + qmlUtils.addNewValueToDynamicChart(used_memory_series, getValue("used_memory")) + qmlUtils.addNewValueToDynamicChart(used_memory_rss_series, getValue("used_memory_rss")) + qmlUtils.addNewValueToDynamicChart(used_memory_lua_series, getValue("used_memory_lua")) + qmlUtils.addNewValueToDynamicChart(used_memory_peak_series, getValue("used_memory_peak")) + + axisY.max = getValue("used_memory_peak") + 100 + } + } + } + } + + + Timer { + id: initTimer + + onTriggered: { + tab.model = serverStatsModel.getValue(tabIndex) + tab.model.init() + } + } + + Component.onCompleted: { + initTimer.start() + uiBlocker.visible = true + } + + Connections { + target: tab.model ? tab.model : null + + onInitialized: { + uiBlocker.visible = false + tab.icon = "qrc:/images/console.svg" + } + } + + Rectangle { + id: uiBlocker + visible: false + anchors.fill: parent + color: Qt.rgba(0, 0, 0, 0.1) + + Item { + anchors.fill: parent + BusyIndicator { anchors.centerIn: parent; running: true } + } + + MouseArea { + anchors.fill: parent + } + } + } + } +} diff --git a/src/qml/settings/BoolOption.qml b/src/qml/settings/BoolOption.qml index 0fc128161..33eb09c3c 100644 --- a/src/qml/settings/BoolOption.qml +++ b/src/qml/settings/BoolOption.qml @@ -27,7 +27,7 @@ Item { } Text { - color: "#cccccc" + color: "grey" text: root.description } } diff --git a/src/qml/value-editor/AddKeyDialog.qml b/src/qml/value-editor/AddKeyDialog.qml index c7441f6eb..63eedbc5f 100644 --- a/src/qml/value-editor/AddKeyDialog.qml +++ b/src/qml/value-editor/AddKeyDialog.qml @@ -23,20 +23,20 @@ Dialog { anchors.fill: parent anchors.margins: 5 - Text { text: "Key:" } + Text { text: qsTr("Key:") } TextField { id: newKeyName Layout.fillWidth: true } - Text { text: "Type:" } + Text { text: qsTr("Type:") } ComboBox { id: typeSelector model: Editor.getSupportedKeyTypes() Layout.fillWidth: true } - Text { text: "Value:" } + Text { text: qsTr("Value:") } Loader { id: valueAddEditor Layout.fillWidth: true @@ -54,7 +54,7 @@ Dialog { Layout.minimumHeight: 40 Item { Layout.fillWidth: true} Button { - text: "Save" + text: qsTr("Save") onClicked: { if (!valueAddEditor.item) @@ -86,7 +86,7 @@ Dialog { } Button { - text: "Cancel" + text: qsTr("Cancel") onClicked: root.close() } } @@ -96,7 +96,7 @@ Dialog { MessageDialog { id: addError - title: "Error" + title: qsTr("Error") text: "" visible: false modality: Qt.ApplicationModal diff --git a/src/qml/value-editor/Pagination.qml b/src/qml/value-editor/Pagination.qml index adce61ebc..f22ea281c 100644 --- a/src/qml/value-editor/Pagination.qml +++ b/src/qml/value-editor/Pagination.qml @@ -7,7 +7,7 @@ ColumnLayout { Layout.fillWidth: true Text { - text: "Page " + text: qsTr("Page") + " " wrapMode: Text.WrapAnywhere } @@ -29,7 +29,7 @@ ColumnLayout { Button { Layout.maximumWidth: 200 Layout.fillWidth: true - text: "Set Page" + text: qsTr("Set Page") onClicked: table.goToPage(pageField.text) } diff --git a/src/qml/value-editor/ValueTabs.qml b/src/qml/value-editor/ValueTabs.qml index bce3adb0e..d2a06f1fd 100644 --- a/src/qml/value-editor/ValueTabs.qml +++ b/src/qml/value-editor/ValueTabs.qml @@ -125,7 +125,7 @@ Repeater { Item { visible: showValueNavigation; Layout.preferredWidth: 5} Text { visible: showValueNavigation; text: "Size: "+ valuesCount } Item { Layout.preferredWidth: 5} - Text { text: "TTL:"; font.bold: true } + Text { text: qsTr("TTL:"); font.bold: true } Text { text: keyTtl} Item { Layout.preferredWidth: 5} @@ -143,7 +143,7 @@ Repeater { implicitHeight: 100 width: 500 - Text { text: "New name:" } + Text { text: qsTr("New name:") } TextField { id: newKeyName; Layout.fillWidth: true; @@ -172,13 +172,13 @@ Repeater { } Button { - text: "Delete" + text: qsTr("Delete") iconSource: "qrc:/images/delete.svg" MessageDialog { id: deleteConfirmation - title: "Delete key" - text: "Do you really want to delete this key?" + title: qsTr("Delete key") + text: qsTr("Do you really want to delete this key?") onYes: { console.log("remove key") viewModel.removeKey(keyTab.tabIndex) @@ -197,16 +197,16 @@ Repeater { } Button { - text: "Reload Value" + text: qsTr("Reload Value") action: reLoadAction visible: !showValueNavigation } Button { - text: "Set TTL" + text: qsTr("Set TTL") Dialog { id: setTTLConfirmation - title: "Set key TTL" + title: qsTr("Set key TTL") width: 520 @@ -215,7 +215,7 @@ Repeater { implicitHeight: 100 width: 500 - Text { text: "New TTL:" } + Text { text: qsTr("New TTL:") } TextField { id: newTTL; Layout.fillWidth: true; @@ -419,7 +419,7 @@ Repeater { Button { Layout.preferredWidth: 195 - text: "Add row"; + text: qsTr("Add Row"); iconSource: "qrc:/images/add.svg" onClicked: { addRowDialog.open() @@ -429,7 +429,7 @@ Repeater { Dialog { id: addRowDialog - title: "Add Row" + title: qsTr("Add Row") width: 550 height: 400 @@ -473,16 +473,15 @@ Repeater { Button { Layout.preferredWidth: 195 - text: "Delete row" + text: qsTr("Delete row") iconSource: "qrc:/images/delete.svg" enabled: table.currentRow != -1 onClicked: { if (keyTab.keyModel.totalRowCount() == 1) { - deleteRowConfirmation.text = "This is last row in this key, " + - "if you remove this - key will be removed!" + deleteRowConfirmation.text = qsTr("The row is the last one in the key. After removing it key will be deleted.") } else { - deleteRowConfirmation.text = "Do you really want to remove this row?" + deleteRowConfirmation.text = qsTr("Do you really want to remove this row?") } var rowIndex = table.model.getOriginalRowIndex(table.currentRow) @@ -496,7 +495,7 @@ Repeater { MessageDialog { id: deleteRowConfirmation - title: "Delete row" + title: qsTr("Delete row") text: "" onYes: { console.log("remove row in key") @@ -513,7 +512,7 @@ Repeater { Button { Layout.preferredWidth: 195 - text: "Reload Value" + text: qsTr("Reload Value") iconSource: "qrc:/images/refresh.svg" action: reLoadAction @@ -534,7 +533,7 @@ Repeater { id: searchField Layout.preferredWidth: 195 - placeholderText: "Search on page..." + placeholderText: qsTr("Search on page...") Component.onCompleted: { table.searchField = searchField @@ -626,11 +625,11 @@ Repeater { Layout.minimumHeight: 40 Item { Layout.fillWidth: true} Button { - text: "Save" + text: qsTr("Save") onClicked: { if (!valueEditor.item || !valueEditor.item.isValueChanged()) { - savingConfirmation.text = "Nothing to save" + savingConfirmation.text = qsTr("Nothing to save") savingConfirmation.open() return } @@ -640,7 +639,7 @@ Repeater { console.log(value, value["value"]) keyTab.keyModel.updateRow(valueEditor.currentRow, value) - savingConfirmation.text = "Value was updated!" + savingConfirmation.text = qsTr("Value was updated!") savingConfirmation.open() } @@ -648,7 +647,7 @@ Repeater { MessageDialog { id: savingConfirmation - title: "Save value" + title: qsTr("Save value") text: "" visible: false modality: Qt.ApplicationModal diff --git a/src/qml/value-editor/editors/MultilineEditor.qml b/src/qml/value-editor/editors/MultilineEditor.qml index a0af9c50c..24ccdf507 100644 --- a/src/qml/value-editor/editors/MultilineEditor.qml +++ b/src/qml/value-editor/editors/MultilineEditor.qml @@ -43,8 +43,8 @@ ColumnLayout Layout.fillWidth: true Text { text: root.fieldLabel } - Text { id: binaryFlag; text: "[Binary]"; visible: false; color: "green"; } - Text { id: compressedFlag; text: "[GZIP compressed]"; visible: false; color: "red"; } // TBD + Text { id: binaryFlag; text: qsTr("[Binary]"); visible: false; color: "green"; } + Text { id: compressedFlag; text: qsTr("[GZIP compressed]"); visible: false; color: "red"; } // TBD Item { Layout.fillWidth: true } Text { text: "View as:" } diff --git a/src/qml/value-editor/editors/SortedSetItemEditor.qml b/src/qml/value-editor/editors/SortedSetItemEditor.qml index b0a440f4c..f9a4c8a4f 100644 --- a/src/qml/value-editor/editors/SortedSetItemEditor.qml +++ b/src/qml/value-editor/editors/SortedSetItemEditor.qml @@ -12,7 +12,7 @@ AbstractEditor { Text { Layout.fillWidth: true - text: "Score:" + text: qsTr("Score:") } TextField { @@ -24,7 +24,7 @@ AbstractEditor { text: "" enabled: originalValue != "" || root.state !== "edit" property var originalValue: "" - placeholderText: "Score" + placeholderText: qsTr("Score") validator: DoubleValidator { locale: "C"; notation: DoubleValidator.StandardNotation } // force point as decimal separator } diff --git a/src/rdm.pro b/src/rdm.pro index a868ae124..1e8cfcd8e 100644 --- a/src/rdm.pro +++ b/src/rdm.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui network concurrent widgets quick quickwidgets +QT += core gui network concurrent widgets quick quickwidgets charts TARGET = rdm TEMPLATE = app @@ -29,6 +29,8 @@ SOURCES += \ $$PWD/modules/crashhandler/*.cpp \ $$PWD/modules/updater/*.cpp \ $$PWD/modules/bulk-operations/*.cpp \ + $$PWD/modules/common/*.cpp \ + $$PWD/modules/server-stats/*.cpp \ HEADERS += \ $$PWD/app/app.h \ @@ -44,6 +46,8 @@ HEADERS += \ $$PWD/modules/updater/*.h \ $$PWD/modules/*.h \ $$PWD/modules/bulk-operations/*.h \ + $$PWD/modules/common/*.h \ + $$PWD/modules/server-stats/*.h \ exists( $$PWD/version.h ) { HEADERS += $$PWD/version.h @@ -123,8 +127,30 @@ RESOURCES += \ $$PWD/resources/images.qrc \ $$PWD/resources/fonts.qrc \ $$PWD/qml/qml.qrc \ + $$PWD/resources/tr.qrc \ OTHER_FILES += \ qt.conf \ Info.plist \ qml\*.qml \ + + +lupdate_only{ + SOURCES += \ + $$PWD/qml/*.qml \ + $$PWD/qml/value-editor/*.qml \ + $$PWD/qml/settings/*.qml \ + $$PWD/qml/server-info/*.qml \ + $$PWD/qml/console/*.qml \ + $$PWD/qml/connections-tree/*.qml \ + $$PWD/qml/common/*.qml \ + $$PWD/qml/bulk-operations/*.qml \ +} + + +TRANSLATIONS = \ + $$PWD/resources/translations/rdm.ts \ + $$PWD/resources/translations/rdm_zh_CN.ts \ + $$PWD/resources/translations/rdm_zh_TW.ts + +CODECFORSRC = UTF-8 diff --git a/src/resources/tr.qrc b/src/resources/tr.qrc new file mode 100644 index 000000000..217167a6f --- /dev/null +++ b/src/resources/tr.qrc @@ -0,0 +1,6 @@ + + + translations/rdm_zh_CN.qm + translations/rdm_zh_TW.qm + + diff --git a/src/resources/translations/rdm.ts b/src/resources/translations/rdm.ts new file mode 100644 index 000000000..0399fa090 --- /dev/null +++ b/src/resources/translations/rdm.ts @@ -0,0 +1,881 @@ + + + + + AddKeyDialog + + + Key: + + + + + Type: + + + + + Value: + + + + + Save + + + + + Cancel + + + + + Error + + + + + AppToolBar + + + Connect to Redis Server + + + + + + + Import Connections + + + + + Export Connections + + + + + Settings + + + + + BulkOperationsDialog + + + Delete keys + + + + + Copy keys + + + + + Redis Server: + + + + + Database number: + + + + + Key pattern: + + + + + Destination Redis Server: + + + + + Destination Redis Server Database Index: + + + + + Affected keys: + + + + + Bulk Operation finished. + + + + + Delete Keys + + + + + Cancel + + + + + Confirmation + + + + + Do you really want to perform bulk operation? + + + + + ConnectionSettignsDialog + + + New Connection Settings + + + + + Edit Connection Settings - %1 + + + + + Connection Settings + + + + + Main Settings + + + + + Name: + + + + + Connection Name + + + + + Address: + + + + + redis-server host + + + + + Auth: + + + + + (Optional) redis-server authentication password + + + + + Security + + + + + None + + + + + SSL + + + + + Public Key: + + + + + (Optional) Public Key in PEM format + + + + + Select public key in PEM format + + + + + Private Key: + + + + + (Optional) Private Key in PEM format + + + + + + Select private key in PEM format + + + + + Authority: + + + + + (Optional) Authority in PEM format + + + + + Select authority file in PEM format + + + + + SSH Tunnel + + + + + SSH Address: + + + + + Remote Host with SSH server + + + + + SSH User: + + + + + Valid SSH User Name + + + + + Private Key + + + + + Path to Private Key in PEM format + + + + + Password + + + + + SSH User Password + + + + + Advanced Settings + + + + + Keys glob-style pattern: + + + + + Pattern which defines loaded keys from redis-server + + + + + Namespace Separator: + + + + + Separator used for namespace extraction from keys + + + + + Connection Timeout (sec): + + + + + Execution Timeout (sec): + + + + + Invalid settings detected! + + + + + Test Connection + + + + + Quick Start Guide + + + + + OK + + + + + Cancel + + + + + ConnectionsTree::DatabaseItem + + + Key was added. Do you want to reload keys in selected database? + + + + + Key was added + + + + + Another operation is currently in progress + + + + + Please wait until another operation will be finised. + + + + + Do you really want to remove all keys from this database? + + + + + + Keys error + + + + + Live update was disabled + + + + + Live update was disabled due to exceeded keys limit. Please specify more accurate filter or change limit in settings. + + + + + ConnectionsTree::ServerItem + + + All value and console tabs related to thisconnection will be closed. Do you want to continue? + + + + + Do you really want delete connection? + + + + + GlobalSettings + + + Settings + + + + + Appearance + + + + + Font Size + + + + + in pixels + + + + + Connections Tree + + + + + Reopen namespaces on reload + + + + + + (Disable to improve treeview performance) + + + + + Enable key sorting in tree + + + + + Live update maximum allowed keys + + + + + Live update interval (in seconds) + + + + + OK + + + + + Pagination + + + Page + + + + + Set Page + + + + + PasswordInput + + + Show password + + + + + QObject + + + Do you really want to delete this key? + + + + + Key error + + + + + Settings directory is not writable + + + + + RDM can't save connections file to settings directory. Please change file permissions or restart RDM as administrator. + + + + + Please download new version of Redis Desktop Manager: %1 + + + + + + + + + + + + Invalid row + + + + + + + + + + + + + + + + + + + Connection error: + + + + + Value with same key already exist + + + + + Partial data loaded from server + + + + + Cannot load key %1, connection error occurred: %2 + + + + + Cannot load key %1 because it doesn't exist in database. Please reload connection tree and try again. + + + + + Cannot load TTL for key %1, connection error occurred: %2 + + + + + Cannot retrive type of the key: + + + + + RedisDesktopManager >= 0.9.0 doesn't support old versions of redis-server (< 2.8). Please use RedisDesktopManager 0.8.8 or upgrade your redis-server. + + + + + Cannot load keys: %1 + + + + + + Cannot remove key: %1 + + + + + Delete key error: + + + + + FlushDB error: + + + + + Cannot load databases: + + + + + + + + Connection error. Check network connection + + + + + + Invalid Connection. Check connection settings. + + + + + Connected. + + + + + + Connection error: + + + + + Server %0 + + + + + Cannot open value tab + + + + + Connection error. Can't open value tab. + + + + + Can't add new key: + + + + + Can't rename key: + + + + + Can't remove key: + + + + + Can't set key ttl: + + + + + Can't close key tab: + + + + + The row has been changed and can't be updated now. Reload and try again. + + + + + The row has been changed and can't be deleted now. Reload and try again. + + + + + Data was loaded from server partially. + + + + + Bulk operation error: %1 + + + + + Cannot load key value: %1 + + + + + QuickStartDialog + + + Explore Redis Desktop Manager + + + + + Before using Redis Desktop Manager (RDM) take a look on the %1 + + + + + Quick Start Guide + + + + + OK + + + + + ServerInfoTabs + + + Redis Version + + + + + Used memory + + + + + Clients + + + + + Commands Processed + + + + + Uptime + + + + + Memory Usage + + + + + Mb + + + + + Server Info + + + + + Property + + + + + Value + + + + + ValueTabs + + + TTL: + + + + + New name: + + + + + Delete + + + + + Delete key + + + + + Do you really want to delete this key? + + + + + + Reload Value + + + + + Set TTL + + + + + Set key TTL + + + + + New TTL: + + + + + The row is the last one in the key. After removing it key will be deleted. + + + + + Search on page... + + + + + Save + + + + + Nothing to save + + + + + Value was updated! + + + + + Save value + + + + + + Add Row + + + + + + Delete row + + + + + Do you relly want to remove this row? + + + + + app + + + Successful connection to redis-server + + + + + Can't connect to redis-server + + + + diff --git a/src/resources/translations/rdm_zh_CN.qm b/src/resources/translations/rdm_zh_CN.qm new file mode 100644 index 000000000..85827cc1b Binary files /dev/null and b/src/resources/translations/rdm_zh_CN.qm differ diff --git a/src/resources/translations/rdm_zh_CN.ts b/src/resources/translations/rdm_zh_CN.ts new file mode 100644 index 000000000..1fc53f93d --- /dev/null +++ b/src/resources/translations/rdm_zh_CN.ts @@ -0,0 +1,881 @@ + + + + + AddKeyDialog + + + Key: + + + + + Type: + + + + + Value: + + + + + Save + + + + + Cancel + + + + + Error + + + + + AppToolBar + + + Connect to Redis Server + + + + + + + Import Connections + + + + + Export Connections + + + + + Settings + 设置 + + + + BulkOperationsDialog + + + Delete keys + + + + + Copy keys + + + + + Redis Server: + + + + + Database number: + + + + + Key pattern: + + + + + Destination Redis Server: + + + + + Destination Redis Server Database Index: + + + + + Affected keys: + + + + + Bulk Operation finished. + + + + + Delete Keys + + + + + Cancel + + + + + Confirmation + + + + + Do you really want to perform bulk operation? + + + + + ConnectionSettignsDialog + + + New Connection Settings + + + + + Edit Connection Settings - %1 + + + + + Connection Settings + + + + + Main Settings + + + + + Name: + + + + + Connection Name + + + + + Address: + + + + + redis-server host + + + + + Auth: + + + + + (Optional) redis-server authentication password + + + + + Security + + + + + None + + + + + SSL + + + + + Public Key: + + + + + (Optional) Public Key in PEM format + + + + + Select public key in PEM format + + + + + Private Key: + + + + + (Optional) Private Key in PEM format + + + + + + Select private key in PEM format + + + + + Authority: + + + + + (Optional) Authority in PEM format + + + + + Select authority file in PEM format + + + + + SSH Tunnel + + + + + SSH Address: + + + + + Remote Host with SSH server + + + + + SSH User: + + + + + Valid SSH User Name + + + + + Private Key + + + + + Path to Private Key in PEM format + + + + + Password + + + + + SSH User Password + + + + + Advanced Settings + + + + + Keys glob-style pattern: + + + + + Pattern which defines loaded keys from redis-server + + + + + Namespace Separator: + + + + + Separator used for namespace extraction from keys + + + + + Connection Timeout (sec): + + + + + Execution Timeout (sec): + + + + + Invalid settings detected! + + + + + Test Connection + + + + + Quick Start Guide + + + + + OK + + + + + Cancel + + + + + ConnectionsTree::DatabaseItem + + + Key was added. Do you want to reload keys in selected database? + + + + + Key was added + + + + + Another operation is currently in progress + + + + + Please wait until another operation will be finised. + + + + + Do you really want to remove all keys from this database? + + + + + + Keys error + + + + + Live update was disabled + + + + + Live update was disabled due to exceeded keys limit. Please specify more accurate filter or change limit in settings. + + + + + ConnectionsTree::ServerItem + + + All value and console tabs related to thisconnection will be closed. Do you want to continue? + + + + + Do you really want delete connection? + + + + + GlobalSettings + + + Settings + 设置 + + + + Appearance + + + + + Font Size + + + + + in pixels + + + + + Connections Tree + + + + + Reopen namespaces on reload + + + + + + (Disable to improve treeview performance) + + + + + Enable key sorting in tree + + + + + Live update maximum allowed keys + + + + + Live update interval (in seconds) + + + + + OK + + + + + Pagination + + + Page + + + + + Set Page + + + + + PasswordInput + + + Show password + + + + + QObject + + + Do you really want to delete this key? + + + + + Key error + + + + + Settings directory is not writable + + + + + RDM can't save connections file to settings directory. Please change file permissions or restart RDM as administrator. + + + + + Please download new version of Redis Desktop Manager: %1 + + + + + + + + + + + + Invalid row + + + + + + + + + + + + + + + + + + + Connection error: + + + + + Value with same key already exist + + + + + Partial data loaded from server + + + + + Cannot load key %1, connection error occurred: %2 + + + + + Cannot load key %1 because it doesn't exist in database. Please reload connection tree and try again. + + + + + Cannot load TTL for key %1, connection error occurred: %2 + + + + + Cannot retrive type of the key: + + + + + RedisDesktopManager >= 0.9.0 doesn't support old versions of redis-server (< 2.8). Please use RedisDesktopManager 0.8.8 or upgrade your redis-server. + + + + + Cannot load keys: %1 + + + + + + Cannot remove key: %1 + + + + + Delete key error: + + + + + FlushDB error: + + + + + Cannot load databases: + + + + + + + + Connection error. Check network connection + + + + + + Invalid Connection. Check connection settings. + + + + + Connected. + + + + + + Connection error: + + + + + Server %0 + + + + + Cannot open value tab + + + + + Connection error. Can't open value tab. + + + + + Can't add new key: + + + + + Can't rename key: + + + + + Can't remove key: + + + + + Can't set key ttl: + + + + + Can't close key tab: + + + + + The row has been changed and can't be updated now. Reload and try again. + + + + + The row has been changed and can't be deleted now. Reload and try again. + + + + + Data was loaded from server partially. + + + + + Bulk operation error: %1 + + + + + Cannot load key value: %1 + + + + + QuickStartDialog + + + Explore Redis Desktop Manager + + + + + Before using Redis Desktop Manager (RDM) take a look on the %1 + + + + + Quick Start Guide + + + + + OK + + + + + ServerInfoTabs + + + Redis Version + + + + + Used memory + + + + + Clients + + + + + Commands Processed + + + + + Uptime + + + + + Memory Usage + + + + + Mb + + + + + Server Info + + + + + Property + + + + + Value + + + + + ValueTabs + + + TTL: + + + + + New name: + + + + + Delete + + + + + Delete key + + + + + Do you really want to delete this key? + + + + + + Reload Value + + + + + Set TTL + + + + + Set key TTL + + + + + New TTL: + + + + + The row is the last one in the key. After removing it key will be deleted. + + + + + Search on page... + + + + + Save + + + + + Nothing to save + + + + + Value was updated! + + + + + Save value + + + + + + Add Row + + + + + + Delete row + + + + + Do you relly want to remove this row? + + + + + app + + + Successful connection to redis-server + + + + + Can't connect to redis-server + + + + diff --git a/src/resources/translations/rdm_zh_TW.qm b/src/resources/translations/rdm_zh_TW.qm new file mode 100644 index 000000000..be651eede --- /dev/null +++ b/src/resources/translations/rdm_zh_TW.qm @@ -0,0 +1 @@ +<¸dÊÍ!¿`¡½Ý \ No newline at end of file diff --git a/src/resources/translations/rdm_zh_TW.ts b/src/resources/translations/rdm_zh_TW.ts new file mode 100644 index 000000000..241b8aaf5 --- /dev/null +++ b/src/resources/translations/rdm_zh_TW.ts @@ -0,0 +1,881 @@ + + + + + AddKeyDialog + + + Key: + + + + + Type: + + + + + Value: + + + + + Save + + + + + Cancel + + + + + Error + + + + + AppToolBar + + + Connect to Redis Server + + + + + + + Import Connections + + + + + Export Connections + + + + + Settings + + + + + BulkOperationsDialog + + + Delete keys + + + + + Copy keys + + + + + Redis Server: + + + + + Database number: + + + + + Key pattern: + + + + + Destination Redis Server: + + + + + Destination Redis Server Database Index: + + + + + Affected keys: + + + + + Bulk Operation finished. + + + + + Delete Keys + + + + + Cancel + + + + + Confirmation + + + + + Do you really want to perform bulk operation? + + + + + ConnectionSettignsDialog + + + New Connection Settings + + + + + Edit Connection Settings - %1 + + + + + Connection Settings + + + + + Main Settings + + + + + Name: + + + + + Connection Name + + + + + Address: + + + + + redis-server host + + + + + Auth: + + + + + (Optional) redis-server authentication password + + + + + Security + + + + + None + + + + + SSL + + + + + Public Key: + + + + + (Optional) Public Key in PEM format + + + + + Select public key in PEM format + + + + + Private Key: + + + + + (Optional) Private Key in PEM format + + + + + + Select private key in PEM format + + + + + Authority: + + + + + (Optional) Authority in PEM format + + + + + Select authority file in PEM format + + + + + SSH Tunnel + + + + + SSH Address: + + + + + Remote Host with SSH server + + + + + SSH User: + + + + + Valid SSH User Name + + + + + Private Key + + + + + Path to Private Key in PEM format + + + + + Password + + + + + SSH User Password + + + + + Advanced Settings + + + + + Keys glob-style pattern: + + + + + Pattern which defines loaded keys from redis-server + + + + + Namespace Separator: + + + + + Separator used for namespace extraction from keys + + + + + Connection Timeout (sec): + + + + + Execution Timeout (sec): + + + + + Invalid settings detected! + + + + + Test Connection + + + + + Quick Start Guide + + + + + OK + + + + + Cancel + + + + + ConnectionsTree::DatabaseItem + + + Key was added. Do you want to reload keys in selected database? + + + + + Key was added + + + + + Another operation is currently in progress + + + + + Please wait until another operation will be finised. + + + + + Do you really want to remove all keys from this database? + + + + + + Keys error + + + + + Live update was disabled + + + + + Live update was disabled due to exceeded keys limit. Please specify more accurate filter or change limit in settings. + + + + + ConnectionsTree::ServerItem + + + All value and console tabs related to thisconnection will be closed. Do you want to continue? + + + + + Do you really want delete connection? + + + + + GlobalSettings + + + Settings + + + + + Appearance + + + + + Font Size + + + + + in pixels + + + + + Connections Tree + + + + + Reopen namespaces on reload + + + + + + (Disable to improve treeview performance) + + + + + Enable key sorting in tree + + + + + Live update maximum allowed keys + + + + + Live update interval (in seconds) + + + + + OK + + + + + Pagination + + + Page + + + + + Set Page + + + + + PasswordInput + + + Show password + + + + + QObject + + + Do you really want to delete this key? + + + + + Key error + + + + + Settings directory is not writable + + + + + RDM can't save connections file to settings directory. Please change file permissions or restart RDM as administrator. + + + + + Please download new version of Redis Desktop Manager: %1 + + + + + + + + + + + + Invalid row + + + + + + + + + + + + + + + + + + + Connection error: + + + + + Value with same key already exist + + + + + Partial data loaded from server + + + + + Cannot load key %1, connection error occurred: %2 + + + + + Cannot load key %1 because it doesn't exist in database. Please reload connection tree and try again. + + + + + Cannot load TTL for key %1, connection error occurred: %2 + + + + + Cannot retrive type of the key: + + + + + RedisDesktopManager >= 0.9.0 doesn't support old versions of redis-server (< 2.8). Please use RedisDesktopManager 0.8.8 or upgrade your redis-server. + + + + + Cannot load keys: %1 + + + + + + Cannot remove key: %1 + + + + + Delete key error: + + + + + FlushDB error: + + + + + Cannot load databases: + + + + + + + + Connection error. Check network connection + + + + + + Invalid Connection. Check connection settings. + + + + + Connected. + + + + + + Connection error: + + + + + Server %0 + + + + + Cannot open value tab + + + + + Connection error. Can't open value tab. + + + + + Can't add new key: + + + + + Can't rename key: + + + + + Can't remove key: + + + + + Can't set key ttl: + + + + + Can't close key tab: + + + + + The row has been changed and can't be updated now. Reload and try again. + + + + + The row has been changed and can't be deleted now. Reload and try again. + + + + + Data was loaded from server partially. + + + + + Bulk operation error: %1 + + + + + Cannot load key value: %1 + + + + + QuickStartDialog + + + Explore Redis Desktop Manager + + + + + Before using Redis Desktop Manager (RDM) take a look on the %1 + + + + + Quick Start Guide + + + + + OK + + + + + ServerInfoTabs + + + Redis Version + + + + + Used memory + + + + + Clients + + + + + Commands Processed + + + + + Uptime + + + + + Memory Usage + + + + + Mb + + + + + Server Info + + + + + Property + + + + + Value + + + + + ValueTabs + + + TTL: + + + + + New name: + + + + + Delete + + + + + Delete key + + + + + Do you really want to delete this key? + + + + + + Reload Value + + + + + Set TTL + + + + + Set key TTL + + + + + New TTL: + + + + + The row is the last one in the key. After removing it key will be deleted. + + + + + Search on page... + + + + + Save + + + + + Nothing to save + + + + + Value was updated! + + + + + Save value + + + + + + Add Row + + + + + + Delete row + + + + + Do you relly want to remove this row? + + + + + app + + + Successful connection to redis-server + + + + + Can't connect to redis-server + + + + diff --git a/tests/unit_tests/unit_tests.pro b/tests/unit_tests/unit_tests.pro index 6af258c60..1778e8ae9 100644 --- a/tests/unit_tests/unit_tests.pro +++ b/tests/unit_tests/unit_tests.pro @@ -10,11 +10,13 @@ PROJECT_ROOT = $$PWD/../..// SRC_DIR = $$PROJECT_ROOT/src// HEADERS += \ - $$PROJECT_ROOT/3rdparty/qredisclient/tests/unit_tests/basetestcase.h \ + $$PROJECT_ROOT/3rdparty/qredisclient/tests/unit_tests/basetestcase.h \ $$PROJECT_ROOT/3rdparty/qredisclient/tests/unit_tests/mocks/*.h \ + $$PROJECT_ROOT/src/modules/common/*.h \ SOURCES += \ $$PROJECT_ROOT/3rdparty/qredisclient/tests/unit_tests/basetestcase.cpp \ + $$PROJECT_ROOT/src/modules/common/*.cpp \ $$PWD/main.cpp \