diff --git a/3rdparty/qredisclient b/3rdparty/qredisclient index d2349db21..a28f13f38 160000 --- a/3rdparty/qredisclient +++ b/3rdparty/qredisclient @@ -1 +1 @@ -Subproject commit d2349db21b62efd1bcf8e3e1f170c00624aef322 +Subproject commit a28f13f380717a89f21c6de41aea66ea5f3da4a5 diff --git a/src/app/app.cpp b/src/app/app.cpp index 881314b19..a86bbf455 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -206,14 +206,24 @@ void Application::initUpdater() void Application::installTranslator() { - QString locale = QLocale::system().uiLanguages().first().replace( "-", "_" ); + QSettings settings; + QString preferredLocale = settings.value("app/locale", "system").toString(); + + QString locale; + + if (preferredLocale == "system") { + settings.setValue("app/locale", "system"); + locale = QLocale::system().uiLanguages().first().replace( "-", "_" ); - qDebug() << QLocale::system().uiLanguages(); + qDebug() << QLocale::system().uiLanguages(); - if (locale.isEmpty() || locale == "C") - locale = "en_US"; + if (locale.isEmpty() || locale == "C") + locale = "en_US"; - qDebug() << "Detected locale:" << locale; + qDebug() << "Detected locale:" << locale; + } else { + locale = preferredLocale; + } QTranslator* translator = new QTranslator((QObject *)this); if (translator->load( QString( ":/translations/rdm_" ) + locale )) diff --git a/src/app/models/treeoperations.cpp b/src/app/models/treeoperations.cpp index 53be7adf1..79fd6821f 100644 --- a/src/app/models/treeoperations.cpp +++ b/src/app/models/treeoperations.cpp @@ -34,22 +34,25 @@ void TreeOperations::getDatabases(std::functiongetKeyspaceInfo(); - //detect all databases - RedisClient::Response scanningResp; - int dbIndex = (availableDatabeses.size() == 0)? 0 : availableDatabeses.lastKey() + 1; - - while (true) { - try { - scanningResp = m_connection->commandSync("select", QString::number(dbIndex)); - } catch (const RedisClient::Connection::Exception& e) { - throw ConnectionsTree::Operations::Exception(QObject::tr("Connection error: ") + QString(e.what())); + if (m_connection->mode() != RedisClient::Connection::Mode::Cluster) { + //detect all databases + RedisClient::Response scanningResp; + int dbIndex = (availableDatabeses.size() == 0)? 0 : availableDatabeses.lastKey() + 1; + + while (true) { + try { + scanningResp = m_connection->commandSync("select", QString::number(dbIndex)); + } catch (const RedisClient::Connection::Exception& e) { + throw ConnectionsTree::Operations::Exception(QObject::tr("Connection error: ") + QString(e.what())); + } + + if (!scanningResp.isOkMessage()) + break; + + availableDatabeses.insert(dbIndex, 0); + ++dbIndex; } - if (!scanningResp.isOkMessage()) - break; - - availableDatabeses.insert(dbIndex, 0); - ++dbIndex; } emit m_manager.openServerStats(m_connection); @@ -63,7 +66,11 @@ void TreeOperations::getDatabaseKeys(uint dbIndex, QString filter, QString keyPattern = filter.isEmpty() ? static_cast(m_connection->getConfig()).keysPattern() : filter; try { - m_connection->getDatabaseKeys(callback, keyPattern, dbIndex); + if (m_connection->mode() == RedisClient::Connection::Mode::Cluster) { + m_connection->getClusterKeys(callback, keyPattern); + } else { + m_connection->getDatabaseKeys(callback, keyPattern, dbIndex); + } } catch (const RedisClient::Connection::Exception& error) { callback(RedisClient::Connection::RawKeysList(), QString(QObject::tr("Cannot load keys: %1")).arg(error.what())); } @@ -153,3 +160,14 @@ void TreeOperations::flushDb(int dbIndex, std::function ca throw ConnectionsTree::Operations::Exception(QObject::tr("FlushDB error: ") + QString(e.what())); } } + +QString TreeOperations::mode() +{ + if (m_connection->mode() == RedisClient::Connection::Mode::Cluster) { + return QString("cluster"); + } else if (m_connection->mode() == RedisClient::Connection::Mode::Sentinel) { + return QString("sentinel"); + } else { + return QString("standalone"); + } +} diff --git a/src/app/models/treeoperations.h b/src/app/models/treeoperations.h index 72b085b5b..c16e93914 100644 --- a/src/app/models/treeoperations.h +++ b/src/app/models/treeoperations.h @@ -40,6 +40,8 @@ class TreeOperations : public QObject, public ConnectionsTree::Operations virtual void flushDb(int dbIndex, std::function callback) override; + virtual QString mode() override; + private: QSharedPointer m_connection; ConnectionsManager& m_manager; diff --git a/src/modules/connections-tree/items/abstractnamespaceitem.cpp b/src/modules/connections-tree/items/abstractnamespaceitem.cpp index b62be9281..d0f4e1a8b 100644 --- a/src/modules/connections-tree/items/abstractnamespaceitem.cpp +++ b/src/modules/connections-tree/items/abstractnamespaceitem.cpp @@ -170,7 +170,7 @@ void AbstractNamespaceItem::renderChilds() QSharedPointer self = getSelf().toStrongRef(); if (!self) { - qDebug() << "Cannot render keys: invalid parent item"; + qDebug() << "Cannot render keys: invalid parent item"; return; } diff --git a/src/modules/connections-tree/items/databaseitem.h b/src/modules/connections-tree/items/databaseitem.h index 510063a51..4e8d6c84c 100644 --- a/src/modules/connections-tree/items/databaseitem.h +++ b/src/modules/connections-tree/items/databaseitem.h @@ -24,7 +24,9 @@ class DatabaseItem : public QObject, public AbstractNamespaceItem QString getIconUrl() const override; - QString getType() const override { return "database"; } + QString getType() const override { return "database"; } + + int itemDepth() const override { return 1; } bool isLocked() const override; diff --git a/src/modules/connections-tree/items/keyitem.h b/src/modules/connections-tree/items/keyitem.h index e4b46d7c8..f172c57fe 100644 --- a/src/modules/connections-tree/items/keyitem.h +++ b/src/modules/connections-tree/items/keyitem.h @@ -22,6 +22,8 @@ class KeyItem : public TreeItem QString getType() const override { return "key"; } + int itemDepth() const override { return m_fullPath.count(m_operations->getNamespaceSeparator().toUtf8()) + 2; } + QList> getAllChilds() const override; bool supportChildItems() const override; diff --git a/src/modules/connections-tree/items/namespaceitem.cpp b/src/modules/connections-tree/items/namespaceitem.cpp index fc2eb9cc5..f3cb4df55 100644 --- a/src/modules/connections-tree/items/namespaceitem.cpp +++ b/src/modules/connections-tree/items/namespaceitem.cpp @@ -14,15 +14,21 @@ NamespaceItem::NamespaceItem(const QByteArray &fullPath, const KeysTreeRenderer::RenderingSettigns& settings) : AbstractNamespaceItem(model, parent, operations, settings), m_fullPath(fullPath), - m_removed(false) + m_removed(false), + m_rendering(false) { m_displayName = m_fullPath.mid(m_fullPath.lastIndexOf(settings.nsSeparator) + 1); m_eventHandlers.insert("click", [this]() { - if (m_childItems.size() != 0) - return; + if (m_childItems.size() == 0) { + m_rendering = true; + m_model.itemChanged(getSelf()); - renderChilds(); + renderChilds(); + + m_rendering = false; + m_model.itemChanged(getSelf()); + } }); m_eventHandlers.insert("delete", [this]() { @@ -42,6 +48,7 @@ QByteArray NamespaceItem::getName() const QString NamespaceItem::getIconUrl() const { + if (m_rendering) return QString("qrc:/images/wait.svg"); return QString("qrc:/images/namespace.svg"); } diff --git a/src/modules/connections-tree/items/namespaceitem.h b/src/modules/connections-tree/items/namespaceitem.h index 1e3c61387..dad561e1e 100644 --- a/src/modules/connections-tree/items/namespaceitem.h +++ b/src/modules/connections-tree/items/namespaceitem.h @@ -24,6 +24,8 @@ class NamespaceItem : public QObject, public AbstractNamespaceItem QString getType() const override { return "namespace"; } + int itemDepth() const override { return m_fullPath.count(m_renderingSettings.nsSeparator.toUtf8()) + 2; } + bool isLocked() const override; bool isEnabled() const override; @@ -36,5 +38,6 @@ class NamespaceItem : public QObject, public AbstractNamespaceItem QByteArray m_fullPath; QByteArray m_displayName; bool m_removed; + bool m_rendering; }; } diff --git a/src/modules/connections-tree/items/serveritem.cpp b/src/modules/connections-tree/items/serveritem.cpp index 470c43d0a..de22e8a17 100644 --- a/src/modules/connections-tree/items/serveritem.cpp +++ b/src/modules/connections-tree/items/serveritem.cpp @@ -26,7 +26,7 @@ ServerItem::ServerItem(const QString& name, QSharedPointer operation if (isDatabaseListLoaded()) return; - load(); + load(); }); m_eventHandlers.insert("console", [this]() { @@ -35,17 +35,18 @@ ServerItem::ServerItem(const QString& name, QSharedPointer operation m_eventHandlers.insert("reload", [this]() { reload(); + emit m_model.itemChanged(getSelf()); }); m_eventHandlers.insert("unload", [this]() { - unload(); + unload(); }); m_eventHandlers.insert("edit", [this]() { confirmAction(nullptr, tr("Value and Console tabs related to this " "connection will be closed. Do you want to continue?"), [this]() { - unload(); + unload(); emit editActionRequested(); }); }); @@ -53,7 +54,7 @@ ServerItem::ServerItem(const QString& name, QSharedPointer operation m_eventHandlers.insert("delete", [this]() { confirmAction(nullptr, tr("Do you really want delete connection?"), [this]() { - unload(); + unload(); emit deleteActionRequested(); }); }); @@ -71,8 +72,16 @@ QString ServerItem::getDisplayName() const QString ServerItem::getIconUrl() const { if (m_locked) return QString("qrc:/images/wait.svg"); - if (isDatabaseListLoaded()) return QString("qrc:/images/server.svg"); - return QString("qrc:/images/server.svg"); //offline + if (isDatabaseListLoaded()) { + if (m_operations->mode() == "cluster") { + return QString("qrc:/images/cluster.svg"); + } else if (m_operations->mode() == "sentinel") { + return QString("qrc:/images/sentinel.svg"); + } else { + return QString("qrc:/images/server.svg"); + } + } + return QString("qrc:/images/server_offline.svg"); } QList > ServerItem::getAllChilds() const diff --git a/src/modules/connections-tree/items/serveritem.h b/src/modules/connections-tree/items/serveritem.h index 699579fb6..c3d2e45b2 100644 --- a/src/modules/connections-tree/items/serveritem.h +++ b/src/modules/connections-tree/items/serveritem.h @@ -24,6 +24,8 @@ class ServerItem : public QObject, public TreeItem QString getType() const override { return "server"; } + int itemDepth() const override { return 0; } + QList> getAllChilds() const override; uint childCount(bool recursive = false) const override; diff --git a/src/modules/connections-tree/items/treeitem.h b/src/modules/connections-tree/items/treeitem.h index 426e40f47..e2e975a53 100644 --- a/src/modules/connections-tree/items/treeitem.h +++ b/src/modules/connections-tree/items/treeitem.h @@ -30,7 +30,9 @@ class TreeItem { virtual QString getIconUrl() const = 0; - virtual QString getType() const = 0; + virtual QString getType() const = 0; + + virtual int itemDepth() const = 0; virtual QList> getAllChilds() const = 0; diff --git a/src/modules/connections-tree/model.cpp b/src/modules/connections-tree/model.cpp index d401aa02a..e18b990de 100644 --- a/src/modules/connections-tree/model.cpp +++ b/src/modules/connections-tree/model.cpp @@ -33,6 +33,7 @@ QVariant Model::data(const QModelIndex &index, int role) const case itemType: return item->getType(); case itemOriginalName: return item->getName(); case itemIsInitiallyExpanded: return item->isExpanded(); + case itemDepth: return item->itemDepth(); } return QVariant(); @@ -44,6 +45,7 @@ QHash Model::roleNames() const roles[itemName] = "name"; roles[itemType] = "type"; roles[itemIsInitiallyExpanded] = "expanded"; + roles[Qt::DecorationRole] = "icon"; return roles; } @@ -108,12 +110,25 @@ int Model::rowCount(const QModelIndex &parent) const if (!parentItem) return m_treeItems.size(); - if (parent.column() > 0) - return 0; - return parentItem->childCount(); } +bool Model::hasChildren(const QModelIndex &parent) +{ + const TreeItem* parentItem = getItemFromIndex(parent); + + if (!parentItem) + return m_treeItems.size() > 0; + + if (parentItem->getType() == "key") + return false; + + if (parentItem->getType() == "namespace" || parentItem->getType() == "server") + return true; + + return parentItem->childCount() > 0; +} + QModelIndex Model::getIndexFromItem(QWeakPointer item) { if (item && item.toStrongRef()) { @@ -146,7 +161,7 @@ void Model::onItemChanged(QWeakPointer item) auto index = getIndexFromItem(item); - if (!index.isValid() || item.toStrongRef()->childCount() == 0) + if (!index.isValid()) return; emit dataChanged(index, index); @@ -167,6 +182,8 @@ void Model::onItemChildsLoaded(QWeakPointer item) emit beginInsertRows(index, 0, treeItem->childCount() - 1); emit endInsertRows(); + emit dataChanged(index, index); + if (treeItem->getType() == "database") { emit expand(index); @@ -176,7 +193,7 @@ void Model::onItemChildsLoaded(QWeakPointer item) } else { qDebug() << "Namespace reopening is disabled in settings"; m_expanded.clear(); - } + } } else if (treeItem->getType() == "server" || treeItem->getType() == "namespace") { emit expand(index); } @@ -210,9 +227,9 @@ void Model::onExpandItem(QWeakPointer item) } -QVariant Model::getItemIcon(const QModelIndex &index) +QVariant Model::getItemDepth(const QModelIndex &index) { - return data(index, Qt::DecorationRole); + return data(index, itemDepth); } QVariant Model::getItemType(const QModelIndex &index) diff --git a/src/modules/connections-tree/model.h b/src/modules/connections-tree/model.h index 9fc126b89..45950f8d5 100644 --- a/src/modules/connections-tree/model.h +++ b/src/modules/connections-tree/model.h @@ -21,7 +21,8 @@ namespace ConnectionsTree { itemOriginalName, itemType, itemFullPath, - itemIsInitiallyExpanded + itemIsInitiallyExpanded, + itemDepth }; public: @@ -39,6 +40,8 @@ namespace ConnectionsTree { int rowCount(const QModelIndex & parent = QModelIndex()) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()); + inline int columnCount(const QModelIndex & parent = QModelIndex()) const { Q_UNUSED(parent); @@ -94,7 +97,7 @@ namespace ConnectionsTree { void onExpandItem(QWeakPointer item); public slots: - QVariant getItemIcon(const QModelIndex &index); + QVariant getItemDepth(const QModelIndex &index); QVariant getItemType(const QModelIndex &index); diff --git a/src/modules/connections-tree/operations.h b/src/modules/connections-tree/operations.h index d8b23b876..7010641df 100644 --- a/src/modules/connections-tree/operations.h +++ b/src/modules/connections-tree/operations.h @@ -60,6 +60,8 @@ namespace ConnectionsTree { virtual void flushDb(int dbIndex, std::function callback) = 0; + virtual QString mode() = 0; + virtual ~Operations() {} }; diff --git a/src/modules/console/consolemodel.cpp b/src/modules/console/consolemodel.cpp index 23e4dc715..a6d361fcb 100644 --- a/src/modules/console/consolemodel.cpp +++ b/src/modules/console/consolemodel.cpp @@ -21,8 +21,13 @@ void Model::init() return; } - emit addOutput(QObject::tr("Connected.\n"), "complete"); - emit changePrompt(QString("%1:0>").arg(m_connection->getConfig().name()), true); + if (m_connection->mode() == RedisClient::Connection::Mode::Cluster) { + emit addOutput(QObject::tr("Connected to cluster.\n"), "complete"); + } else { + emit addOutput(QObject::tr("Connected.\n"), "complete"); + } + + updatePrompt(true); } QString Model::getName() const @@ -49,16 +54,31 @@ void Model::executeCommand(const QString & cmd) return; } - if (command.isSelectCommand()) + if (command.isSelectCommand() || m_connection->mode() == RedisClient::Connection::Mode::Cluster) { m_current_db = command.getPartAsString(1).toInt(); + updatePrompt(false); + } + QVariant value = result.getValue(); + emit addOutput(RedisClient::Response::valueToHumanReadString(value), "complete"); +} + +void Model::updatePrompt(bool showPrompt) +{ + if (m_connection->mode() == RedisClient::Connection::Mode::Cluster) { + emit changePrompt( + QString("%1(%2:%3)>") + .arg(m_connection->getConfig().name()) + .arg(m_connection->getConfig().host()) + .arg(m_connection->getConfig().port()), + showPrompt + ); + } else { emit changePrompt( QString("%1:%2>") .arg(m_connection->getConfig().name()) - .arg(command.getPartAsString(1)), - false + .arg(m_current_db), + showPrompt ); } - QVariant value = result.getValue(); - emit addOutput(RedisClient::Response::valueToHumanReadString(value), "complete"); } diff --git a/src/modules/console/consolemodel.h b/src/modules/console/consolemodel.h index 06754c514..5fd119f28 100644 --- a/src/modules/console/consolemodel.h +++ b/src/modules/console/consolemodel.h @@ -24,5 +24,8 @@ namespace Console { private: int m_current_db; + + private: + void updatePrompt(bool showPrompt); }; } diff --git a/src/modules/server-stats/serverstatsmodel.cpp b/src/modules/server-stats/serverstatsmodel.cpp index 798dc417e..e51452a7a 100644 --- a/src/modules/server-stats/serverstatsmodel.cpp +++ b/src/modules/server-stats/serverstatsmodel.cpp @@ -12,9 +12,12 @@ ServerStats::Model::Model(QSharedPointer connection) 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(); + if (!err.isEmpty()) { + emit error(QObject::tr("Cannot update server info tab. Error: %0").arg(err)); + return; + } + m_serverInfo = RedisClient::ServerInfo::fromString(r.toRawString()).parsed.toVariantMap(); emit serverInfoChanged(); }); diff --git a/src/qml/GlobalSettings.qml b/src/qml/GlobalSettings.qml index c18d6dc7e..6562064e0 100644 --- a/src/qml/GlobalSettings.qml +++ b/src/qml/GlobalSettings.qml @@ -18,6 +18,23 @@ Dialog { anchors.fill: parent anchors.margins: 20 + Text { + text: qsTr("General") + font.pixelSize: 20 + } + + ComboboxOption { + id: appLang + + Layout.fillWidth: true + Layout.preferredHeight: 40 + + model: ["system", "en_US", "zh_CN", "zh_TW"] + value: "system" + label: qsTr("Language") + description: qsTr("Application restart is needed to apply this setting.") + } + Text { text: qsTr("Appearance") font.pixelSize: 20 @@ -31,7 +48,7 @@ Dialog { value: "Open Sans" model: Qt.fontFamilies() - label: "Font" + label: qsTr("Font") description: "" } @@ -126,5 +143,6 @@ Dialog { property alias liveUpdateInterval: liveUpdateInterval.value property alias appFont: appFont.value property alias appFontSize: appFontSize.value + property alias locale: appLang.value } } diff --git a/src/qml/connections-tree/BetterTreeView.qml b/src/qml/connections-tree/BetterTreeView.qml index bca63736f..36a097796 100644 --- a/src/qml/connections-tree/BetterTreeView.qml +++ b/src/qml/connections-tree/BetterTreeView.qml @@ -13,122 +13,115 @@ TreeView { TableViewColumn { title: "item" - role: "name" - width: root.width - } - - selectionMode: SelectionMode.SingleSelection - - selection: ItemSelectionModel { - id: connectionTreeSelectionModel - model: connectionsManager + role: "icon" + width: 25 + delegate: Item { + Image { + anchors.centerIn: parent + sourceSize.width: 25 + sourceSize.height: 25 + source: styleData.value + cache: true + asynchronous: true + } + } } - model: connectionsManager - - rowDelegate: Rectangle { - height: 28 - color: styleData.selected ? "#e2e2e2" : "white" //sysPalette.highlight + TableViewColumn { + id: itemColumn + title: "item" + role: "name" + width: root.width - 20 } itemDelegate: Item { - id: itemRoot - - Loader { - id: itemIcon - width: 21 - height: 21 - asynchronous: true - - anchors {left: itemRoot.left; verticalCenter: itemRoot.verticalCenter; } - - sourceComponent: Component { - Image { - anchors.centerIn: parent - - sourceSize.width: 25 - sourceSize.height: 25 - - source: { - if (!connectionsManager || !styleData.index) - return "" - - var icon = connectionsManager.getItemIcon(styleData.index) - - if (icon != undefined) { - return icon - } else { - return "" - } - } - } - } - } + id: itemRoot Item { - id: itemText - anchors {left: itemIcon.right; top: itemRoot.top; bottom: itemRoot.bottom; leftMargin: 5 } + id: wrapper + height: 30 + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 10 + Text { + anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - //color: styleData.selected ? sysPalette.highlightedText : styleData.textColor - elide: styleData.elideMode + //elide: styleData.elideMode text: styleData.value + anchors.leftMargin: { + var itemDepth = connectionsManager.getItemDepth(styleData.index) + return itemDepth * 10 + 15 + } } - } - Loader { - id: menuLoader - anchors {right: itemRoot.right; top: itemRoot.top; bottom: itemRoot.bottom; } - anchors.rightMargin: 20 - height: parent.height - visible: styleData.selected - asynchronous: true - - source: { - if (!styleData.selected - || !connectionsManager - || !styleData.index) - return "" - - var type = connectionsManager.getItemType(styleData.index) - - if (type != undefined) { - return "./menu/" + type + ".qml" - } else { - return "" + Loader { + id: menuLoader + anchors {right: wrapper.right; top: wrapper.top; bottom: wrapper.bottom; } + anchors.rightMargin: 20 + height: parent.height + visible: styleData.selected + asynchronous: true + + source: { + if (!styleData.selected + || !connectionsManager + || !styleData.index) + return "" + + var type = connectionsManager.getItemType(styleData.index) + + if (type != undefined) { + return "./menu/" + type + ".qml" + } else { + return "" + } } } - } - MouseArea { - anchors.left: itemIcon.left - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: parent.bottom + MouseArea { + anchors.fill: parent - acceptedButtons: Qt.RightButton | Qt.MiddleButton + acceptedButtons: Qt.RightButton | Qt.MiddleButton - onClicked: { - console.log("Catch event to item") + onClicked: { + console.log("Catch event to item") - if(mouse.button == Qt.RightButton) { - mouse.accepted = true - connectionTreeSelectionModel.setCurrentIndex(styleData.index, 1) - connectionsManager.sendEvent(styleData.index, "right-click") - return - } + if(mouse.button == Qt.RightButton) { + mouse.accepted = true + connectionTreeSelectionModel.setCurrentIndex(styleData.index, 1) + connectionsManager.sendEvent(styleData.index, "right-click") + return + } - if (mouse.button == Qt.MiddleButton) { - mouse.accepted = true - connectionsManager.sendEvent(styleData.index, "mid-click") - return + if (mouse.button == Qt.MiddleButton) { + mouse.accepted = true + connectionsManager.sendEvent(styleData.index, "mid-click") + return + } } } + + focus: true + Keys.forwardTo: menuLoader.item ? [menuLoader.item] : [] + } + } + + selectionMode: SelectionMode.SingleSelection - focus: true - Keys.forwardTo: menuLoader.item ? [menuLoader.item] : [] + selection: ItemSelectionModel { + id: connectionTreeSelectionModel + model: connectionsManager + } + + model: connectionsManager + + rowDelegate: Rectangle { + height: 30 + color: styleData.selected ? "#e2e2e2" : "white" //sysPalette.highlight } onClicked: { @@ -144,8 +137,12 @@ TreeView { Connections { target: connectionsManager; onExpand: { - if (!root.isExpanded(index)) { + if (!root.isExpanded(index)) { root.expand(index) + + // Hack to prevent rendering issues + root.__listView.contentY = root.__listView.contentY + 20 + root.__listView.forceLayout() } } } diff --git a/src/qml/console/Consoles.qml b/src/qml/console/Consoles.qml index 2ec841023..72dfe4020 100644 --- a/src/qml/console/Consoles.qml +++ b/src/qml/console/Consoles.qml @@ -23,6 +23,8 @@ Repeater { QConsole { id: redisConsole + property var model: consoleModel.getValue(tabIndex) + Connections { target: consoleModel ? consoleModel.getValue(tabIndex) : null @@ -36,14 +38,16 @@ Repeater { } onExecCommand: { - consoleModel.getValue(tabIndex).executeCommand(command) + if (model) + model.executeCommand(command) } Timer { id: initTimer onTriggered: { - consoleModel.getValue(tabIndex).init() + if (model) + model.init() } } diff --git a/src/qml/console/QConsole.qml b/src/qml/console/QConsole.qml index 8ec896a1e..7bfa88945 100644 --- a/src/qml/console/QConsole.qml +++ b/src/qml/console/QConsole.qml @@ -13,7 +13,7 @@ Rectangle { property alias busy: textArea.readOnly property string initText: - "RDM Redis Console | Unsupported commands: DUMP, RESTORE, AUTH
" + + "RDM Redis Console
" + "Connecting ..." @@ -40,7 +40,7 @@ Rectangle { if (type == "error") { textArea.append("" + text + '') } else { - textArea.append("" + text + '') + textArea.append("
" + text + '
') } if (type == "complete" || type == "error") { diff --git a/src/qml/settings/ComboboxOption.qml b/src/qml/settings/ComboboxOption.qml index 8e16d1361..ef335cc3b 100644 --- a/src/qml/settings/ComboboxOption.qml +++ b/src/qml/settings/ComboboxOption.qml @@ -28,7 +28,7 @@ Item { } Text { - color: "#cccccc" + color: "grey" text: root.description } } @@ -48,6 +48,12 @@ Item { currentIndex = val.find(root.value) } } + + Component.onCompleted: { + if (model) { + currentIndex = val.find(root.value) + } + } } } } diff --git a/src/resources/images.qrc b/src/resources/images.qrc index ce26d3b9c..25eb31545 100644 --- a/src/resources/images.qrc +++ b/src/resources/images.qrc @@ -17,13 +17,13 @@ images/export.svg images/editdb.svg images/delete.svg - redis.ico - images/loader.gif + redis.ico images/ua.svg images/filter.svg images/chat.svg images/ga.png images/server.svg + images/server_offline.svg images/offline.svg images/settings.svg images/console.svg @@ -32,5 +32,7 @@ images/live_update.svg images/live_update_disable.svg images/copy.svg + images/cluster.svg + images/sentinel.svg diff --git a/src/resources/images/cluster.svg b/src/resources/images/cluster.svg new file mode 100644 index 000000000..0e51f7729 --- /dev/null +++ b/src/resources/images/cluster.svg @@ -0,0 +1,147 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/resources/images/db.svg b/src/resources/images/db.svg index 8e15913f2..532001702 100644 --- a/src/resources/images/db.svg +++ b/src/resources/images/db.svg @@ -1 +1,90 @@ - \ No newline at end of file + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/resources/images/editdb.svg b/src/resources/images/editdb.svg index f7e6a699f..f2985d145 100644 --- a/src/resources/images/editdb.svg +++ b/src/resources/images/editdb.svg @@ -1,9 +1,84 @@ - - - - - - - - - \ No newline at end of file + + + +image/svg+xml \ No newline at end of file diff --git a/src/resources/images/export.svg b/src/resources/images/export.svg index 65f77a259..30c0c41e2 100644 --- a/src/resources/images/export.svg +++ b/src/resources/images/export.svg @@ -1,7 +1,78 @@ - - - - - - - \ No newline at end of file + + + +image/svg+xml \ No newline at end of file diff --git a/src/resources/images/import.svg b/src/resources/images/import.svg index 74ade4669..877f55b04 100644 --- a/src/resources/images/import.svg +++ b/src/resources/images/import.svg @@ -1,7 +1,120 @@ - - - - - - - \ No newline at end of file + + + +image/svg+xml \ No newline at end of file diff --git a/src/resources/images/loader.gif b/src/resources/images/loader.gif deleted file mode 100644 index 1560b646c..000000000 Binary files a/src/resources/images/loader.gif and /dev/null differ diff --git a/src/resources/images/sentinel.svg b/src/resources/images/sentinel.svg new file mode 100644 index 000000000..93413501b --- /dev/null +++ b/src/resources/images/sentinel.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/resources/images/server.svg b/src/resources/images/server.svg index 8bb7ff6f1..1c6313ff4 100644 --- a/src/resources/images/server.svg +++ b/src/resources/images/server.svg @@ -1,17 +1,89 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file + + + +image/svg+xml \ No newline at end of file diff --git a/src/resources/images/server_offline.svg b/src/resources/images/server_offline.svg new file mode 100644 index 000000000..8d7ccdff0 --- /dev/null +++ b/src/resources/images/server_offline.svg @@ -0,0 +1,89 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/resources/images/settings.svg b/src/resources/images/settings.svg index 20e6c93be..be05c5273 100644 --- a/src/resources/images/settings.svg +++ b/src/resources/images/settings.svg @@ -1,4 +1,52 @@ - - - - \ No newline at end of file + + + +image/svg+xml \ No newline at end of file diff --git a/src/resources/translations/rdm.qm b/src/resources/translations/rdm.qm new file mode 100644 index 000000000..be651eede --- /dev/null +++ b/src/resources/translations/rdm.qm @@ -0,0 +1 @@ + - Live update was disabled due to exceeded keys limit. Please specify more accurate filter or change limit in settings. + Live update was disabled due to exceeded keys limit. Please specify filter more carrfully or change limit in settings. ConnectionsTree::ServerItem - - All value and console tabs related to thisconnection will be closed. Do you want to continue? + + Value and Console tabs related to this connection will be closed. Do you want to continue? - + Do you really want delete connection? @@ -412,52 +412,72 @@ + General + + + + + Language + + + + + Application restart is needed to apply this setting. + + + + Appearance - + + Font + + + + 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 @@ -496,17 +516,17 @@ - + 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 @@ -537,18 +557,13 @@ - + Connection error: - Value with same key already exist - - - - - Partial data loaded from server + Value with the same key already exist @@ -577,28 +592,28 @@ - + Cannot load keys: %1 - - + + Cannot remove key: %1 - + Delete key error: - + FlushDB error: - + Cannot load databases: @@ -606,29 +621,40 @@ - + Connection error. Check network connection - + Invalid Connection. Check connection settings. - + + Connected to cluster. + + + + + Connected. - + Connection error: - + + Cannot update server info tab. Error: %0 + + + + Server %0 @@ -678,6 +704,7 @@ + Data was loaded from server partially. @@ -749,22 +776,22 @@ - + Mb - + Server Info - + Property - + Value @@ -822,6 +849,11 @@ The row is the last one in the key. After removing it key will be deleted. + + + Do you really want to remove this row? + + Search on page... @@ -859,11 +891,6 @@ Delete row - - - Do you relly want to remove this row? - - app diff --git a/src/resources/translations/rdm_zh_CN.qm b/src/resources/translations/rdm_zh_CN.qm index 5b0161f03..8bb23244b 100644 Binary files a/src/resources/translations/rdm_zh_CN.qm 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 index 164772e9f..7483e86bc 100644 --- a/src/resources/translations/rdm_zh_CN.ts +++ b/src/resources/translations/rdm_zh_CN.ts @@ -1,4 +1,6 @@ - + + + AddKeyDialog @@ -384,19 +386,19 @@ - Live update was disabled due to exceeded keys limit. Please specify more accurate filter or change limit in settings. + Live update was disabled due to exceeded keys limit. Please specify filter more carrfully or change limit in settings. 由于超出加载键数量限制,在线更新功能已经关闭。请设置更精确的筛查条件或更改加载限制设定。 ConnectionsTree::ServerItem - - All value and console tabs related to thisconnection will be closed. Do you want to continue? + + Value and Console tabs related to this connection will be closed. Do you want to continue? 所有与该连接相关的键值对话框和命令操作对话框都将被关闭,确定要继续吗? - + Do you really want delete connection? 确定要删除连接? @@ -410,52 +412,72 @@ + General + + + + + Language + + + + + Application restart is needed to apply this setting. + + + + Appearance 界面 - + + Font + + + + 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 @@ -494,17 +516,17 @@ 键错误 - + Settings directory is not writable 设置保存文件夹没有写入权限 - + RDM can't save connections file to settings directory. Please change file permissions or restart RDM as administrator. RDM 不能保存设置文件。请更改文件写入权限或者以管理员模式启动 RDM。 - + Please download new version of Redis Desktop Manager: %1 请下载新版本的 Redis Desktop Manager: %1 @@ -535,20 +557,15 @@ - + Connection error: 连接错误: - Value with same key already exist + Value with the same key already exist 相同键已经存在 - - - Partial data loaded from server - 已从服务器加载部分数据 - Cannot load key %1, connection error occurred: %2 @@ -575,28 +592,28 @@ 0.9.0 版本以下的 Redis Desktop Manager 不支持老版本的 Redis 服务器 (<2.8)。请使用 0.8.8 的 Redis Desktop Manager 或者升级您的 Redis 服务器。 - + Cannot load keys: %1 无法加载键:%1 - - + + Cannot remove key: %1 无法删除键:%1 - + Delete key error: 删除键错误: - + FlushDB error: 清空库错误: - + Cannot load databases: @@ -606,30 +623,41 @@ - + Connection error. Check network connection 连接错误,请检查网络。 - + Invalid Connection. Check connection settings. 无效连接,请检查连接设置。 - + + Connected to cluster. + + + + + Connected. 已连接。 - + Connection error: 连接错误: - + + Cannot update server info tab. Error: %0 + + + + Server %0 服务器 %0 @@ -679,6 +707,7 @@ 此行数据已经更改,无法删除。请重载后重试。 + Data was loaded from server partially. 部分数据已经从服务器加载。 @@ -750,22 +779,22 @@ 内存占用 - + Mb Mb - + Server Info 服务器信息 - + Property 属性 - + Value 键值 @@ -823,6 +852,11 @@ The row is the last one in the key. After removing it key will be deleted. 此行数据是该键最后一行数据。删除此行数据,该键将会被删除。 + + + Do you really want to remove this row? + 确定要删除该行数据吗? + Search on page... @@ -860,11 +894,6 @@ Delete row 删除行 - - - Do you relly want to remove this row? - 确定要删除该行数据吗? - app @@ -879,4 +908,4 @@ 无法连接 Redis 服务器 - \ No newline at end of file + diff --git a/src/resources/translations/rdm_zh_TW.qm b/src/resources/translations/rdm_zh_TW.qm index be651eede..3a08dd519 100644 Binary files a/src/resources/translations/rdm_zh_TW.qm and b/src/resources/translations/rdm_zh_TW.qm differ diff --git a/src/resources/translations/rdm_zh_TW.ts b/src/resources/translations/rdm_zh_TW.ts index 241b8aaf5..6fb108982 100644 --- a/src/resources/translations/rdm_zh_TW.ts +++ b/src/resources/translations/rdm_zh_TW.ts @@ -6,32 +6,32 @@ Key: - + 鍵: Type: - + 類型: Value: - + 鍵值: Save - + 保存 Cancel - + 取消 Error - + 錯誤 @@ -39,24 +39,24 @@ Connect to Redis Server - + 連接 Redis 伺服器 Import Connections - + 導入連接 Export Connections - + 匯出連接 Settings - + 設置 @@ -64,67 +64,67 @@ Delete keys - + 刪除鍵 Copy keys - + 複製鍵 Redis Server: - + Redis 伺服器: Database number: - + 資料庫編號: Key pattern: - + 鍵名運算式: Destination Redis Server: - + 目標 Redis 伺服器: Destination Redis Server Database Index: - + 目標資料庫編號: Affected keys: - + 受影響的鍵: Bulk Operation finished. - + 批量操作完成。 Delete Keys - + 刪除鍵 Cancel - + 取消 Confirmation - + 確認 Do you really want to perform bulk operation? - + 確認要執行批量操作? @@ -132,218 +132,218 @@ New Connection Settings - + 新連接設置 Edit Connection Settings - %1 - + 編輯連接設置 - %1 Connection Settings - + 連接設置 Main Settings - + 通用設置 Name: - + 名稱: Connection Name - + 連接名稱 Address: - + 地址: redis-server host - + Redis 伺服器地址 Auth: - + 驗證: (Optional) redis-server authentication password - + (可選) Redis 伺服器驗證密碼 Security - + 安全設置 None - + SSL - + SSL Public Key: - + 公開金鑰: (Optional) Public Key in PEM format - + (可選) PEM 格式公開金鑰 Select public key in PEM format - + 選擇 PEM 格式公開金鑰 Private Key: - + 私密金鑰: (Optional) Private Key in PEM format - + (可選) PEM 格式私密金鑰 Select private key in PEM format - + 選擇 PEM 格式私密金鑰 Authority: - + 授權: (Optional) Authority in PEM format - + (可選) PEM 格式授權 Select authority file in PEM format - + 選擇 PEM 格式授權檔 SSH Tunnel - + SSH 通道 SSH Address: - + SSH 地址: Remote Host with SSH server - + SSH 遠端伺服器地址 SSH User: - + SSH 用戶: Valid SSH User Name - + 有效的 SSH 用戶名 Private Key - + 私密金鑰 Path to Private Key in PEM format - + PEM 格式私密金鑰路徑 Password - + 密碼 SSH User Password - + SSH 使用者密碼 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 - + 取消 @@ -351,56 +351,56 @@ 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. - + Live update was disabled due to exceeded keys limit. Please specify filter more carrfully or change limit in settings. + 由於超出載入鍵數量限制,同步更新功能已經關閉。請設置更精確的篩查條件或更改載入限制設定。 ConnectionsTree::ServerItem - - All value and console tabs related to thisconnection will be closed. Do you want to continue? - + + Value and Console tabs related to this connection will be closed. Do you want to continue? + 所有與該連接相關的鍵值對話方塊和命令操作對話方塊都將被關閉,確定要繼續嗎? - + Do you really want delete connection? - + 確定要刪除連接? @@ -408,58 +408,78 @@ Settings - + 設置 - Appearance + General - - Font Size + + Language - - in pixels + + Application restart is needed to apply this setting. + + + Appearance + 介面 + - Connections Tree + Font - + + 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 - + 確定 @@ -467,12 +487,12 @@ Page - + Set Page - + 設置頁碼 @@ -480,7 +500,7 @@ Show password - + 顯示密碼 @@ -488,27 +508,27 @@ 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. - + RDM 不能保存設置檔。請更改檔寫入許可權或者以管理員模式啟動 RDM。 - + Please download new version of Redis Desktop Manager: %1 - + 請下載新版本的 Redis Desktop Manager: %1 @@ -520,7 +540,7 @@ Invalid row - + 無效行 @@ -537,160 +557,170 @@ - + Connection error: - + 連接錯誤: - Value with same key already exist - - - - - Partial data loaded from server - + Value with the same key already exist + 相同鍵已經存在 Cannot load key %1, connection error occurred: %2 - + 無法載入鍵 %1,連接發生錯誤:%2 Cannot load key %1 because it doesn't exist in database. Please reload connection tree and try again. - + 無法載入鍵 %1,資料庫中不存在該鍵,請重載連接樹後重試。 Cannot load TTL for key %1, connection error occurred: %2 - + 無法載入鍵 %1 的 TTL 值,連接發生錯誤: %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. - + 0.9.0 版本以下的 Redis Desktop Manager 不支援老版本的 Redis 伺服器 (<2.8)。請使用 0.8.8 的 Redis Desktop Manager 或者升級您的 Redis 伺服器。 - + Cannot load keys: %1 - + 無法載入鍵:%1 - - + + Cannot remove key: %1 - + 無法刪除鍵:%1 - + Delete key error: - + 刪除鍵錯誤: - + FlushDB error: - + 清空庫錯誤: - + Cannot load databases: - + 無法載入資料庫: + + - + Connection error. Check network connection - + 連接錯誤,請檢查網路。 - + Invalid Connection. Check connection settings. + 無效連接,請檢查連接設置。 + + + + Connected to cluster. + - + Connected. - + 已連接。 + - + Connection error: + 連接錯誤: + + + + Cannot update server info tab. Error: %0 - + Server %0 - + 伺服器 %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: - + 無法設置鍵的 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 - + 批量操作錯誤:%1 Cannot load key value: %1 - + 無法載入鍵值:%1 @@ -698,22 +728,22 @@ Explore Redis Desktop Manager - + Redis Desktop Manager Before using Redis Desktop Manager (RDM) take a look on the %1 - + 在使用 Redis Desktop Manager (RDM) 之前,您可以先看看 %1 Quick Start Guide - + 快速入門指南 OK - + 知道了 @@ -721,52 +751,52 @@ Redis Version - + Redis 版本 Used memory - + 已使用記憶體 Clients - + 連接數 Commands Processed - + 已執行命令 Uptime - + 執行時間 Memory Usage - + 記憶體佔用 - + Mb - + Mb - + Server Info - + 伺服器資訊 - + Property - + 屬性 - + Value - + @@ -774,95 +804,95 @@ TTL: - + TTL: New name: - + 新名稱: Delete - + 刪除 Delete key - + 刪除鍵 Do you really want to delete this key? - + 確定要刪除該鍵? Reload Value - + 重載鍵值 Set TTL - + 設置 TTL Set key TTL - + 設置鍵的 TTL New TTL: - + 新的 TTL: The row is the last one in the key. After removing it key will be deleted. - + 此行資料是該鍵最後一行資料。刪除此行資料,該鍵將會被刪除。 + + + + Do you really want to remove this row? + 確定要刪除該行資料嗎? Search on page... - + 頁面搜索... Save - + 保存 Nothing to save - + 不需要保存 Value was updated! - + 鍵值已經更新! Save value - + 保存鍵值 Add Row - + 插入行 Delete row - - - - - Do you relly want to remove this row? - + 刪除行 @@ -870,12 +900,12 @@ Successful connection to redis-server - + 連接成功! Can't connect to redis-server - + 無法連接! diff --git a/tests/unit_tests/testcases/connections-tree/mocks/itemoperationsmock.h b/tests/unit_tests/testcases/connections-tree/mocks/itemoperationsmock.h index 443579703..7d79c8d08 100644 --- a/tests/unit_tests/testcases/connections-tree/mocks/itemoperationsmock.h +++ b/tests/unit_tests/testcases/connections-tree/mocks/itemoperationsmock.h @@ -48,6 +48,8 @@ class ItemOperationsMock : public ConnectionsTree::Operations { virtual void flushDb(int, std::function) override {} + virtual QString mode() { return QString("fake"); } + protected: bool m_positive_mode; };