Skip to content

Commit

Permalink
fix(registry,ui,util): order docsets case-insensitively (fixes #244)
Browse files Browse the repository at this point in the history
  • Loading branch information
trollixx committed Oct 24, 2018
1 parent d34d7b5 commit 0a33df2
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 33 deletions.
73 changes: 47 additions & 26 deletions src/libs/registry/listmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include "docsetregistry.h"
#include "itemdatarole.h"

#include <iterator>

using namespace Zeal::Registry;

ListModel::ListModel(DocsetRegistry *docsetRegistry, QObject *parent) :
Expand All @@ -36,15 +38,16 @@ ListModel::ListModel(DocsetRegistry *docsetRegistry, QObject *parent) :
connect(m_docsetRegistry, &DocsetRegistry::docsetLoaded, this, &ListModel::addDocset);
connect(m_docsetRegistry, &DocsetRegistry::docsetAboutToBeUnloaded, this, &ListModel::removeDocset);

for (const QString &name : m_docsetRegistry->names())
for (const QString &name : m_docsetRegistry->names()) {
addDocset(name);
}
}

ListModel::~ListModel()
{
for (DocsetItem *item : m_docsetItems) {
qDeleteAll(item->groups);
delete item;
for (auto &kv : m_docsetItems) {
qDeleteAll(kv.second->groups);
delete kv.second;
}
}

Expand All @@ -57,7 +60,7 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole:
switch (indexLevel(index)) {
case Level::DocsetLevel:
return m_docsetRegistry->docset(index.row())->icon();
return itemInRow(index.row())->docset->icon();
case Level::GroupLevel: {
DocsetItem *docsetItem = static_cast<DocsetItem *>(index.internalPointer());
const QString symbolType = docsetItem->groups.at(index.row())->symbolType;
Expand All @@ -73,7 +76,7 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
case Qt::DisplayRole:
switch (indexLevel(index)) {
case Level::DocsetLevel:
return m_docsetRegistry->docset(index.row())->title();
return itemInRow(index.row())->docset->title();
case Level::GroupLevel: {
DocsetItem *docsetItem = static_cast<DocsetItem *>(index.internalPointer());
const QString symbolType = docsetItem->groups.at(index.row())->symbolType;
Expand All @@ -91,7 +94,7 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
case ItemDataRole::UrlRole:
switch (indexLevel(index)) {
case Level::DocsetLevel:
return m_docsetRegistry->docset(index.row())->indexFileUrl();
return itemInRow(index.row())->docset->indexFileUrl();
case Level::SymbolLevel: {
GroupItem *groupItem = static_cast<GroupItem *>(index.internalPointer());
auto it = groupItem->docsetItem->docset->symbols(groupItem->symbolType).cbegin() + index.row();
Expand All @@ -103,11 +106,11 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
case ItemDataRole::DocsetNameRole:
if (index.parent().isValid())
return QVariant();
return m_docsetRegistry->docset(index.row())->name();
return itemInRow(index.row())->docset->name();
case ItemDataRole::UpdateAvailableRole:
if (index.parent().isValid())
return QVariant();
return m_docsetRegistry->docset(index.row())->hasUpdate;
return itemInRow(index.row())->docset->hasUpdate;
default:
return QVariant();
}
Expand All @@ -121,10 +124,8 @@ QModelIndex ListModel::index(int row, int column, const QModelIndex &parent) con
switch (indexLevel(parent)) {
case Level::RootLevel:
return createIndex(row, column);
case Level::DocsetLevel: {
auto it = m_docsetItems.begin() + parent.row();
return createIndex(row, column, static_cast<void *>(it.value()));
}
case Level::DocsetLevel:
return createIndex(row, column, static_cast<void *>(itemInRow(parent.row())));
case Level::GroupLevel: {
DocsetItem *docsetItem = static_cast<DocsetItem *>(parent.internalPointer());
return createIndex(row, column, docsetItem->groups.at(parent.row()));
Expand All @@ -139,7 +140,18 @@ QModelIndex ListModel::parent(const QModelIndex &child) const
switch (indexLevel(child)) {
case Level::GroupLevel: {
DocsetItem *item = static_cast<DocsetItem *>(child.internalPointer());
return createIndex(m_docsetItems.keys().indexOf(item->docset->name()), 0);

auto it = std::find_if(m_docsetItems.cbegin(), m_docsetItems.cend(), [item](const auto &pair) {
return pair.second == item;
});

if (it == m_docsetItems.cend()) {
// TODO: Report error, this should never happen.
return QModelIndex();
}

const int row = std::distance(m_docsetItems.begin(), it);
return createIndex(row, 0);
}
case SymbolLevel: {
GroupItem *item = static_cast<GroupItem *>(child.internalPointer());
Expand All @@ -163,9 +175,9 @@ int ListModel::rowCount(const QModelIndex &parent) const

switch (indexLevel(parent)) {
case Level::RootLevel:
return m_docsetRegistry->count();
return static_cast<int>(m_docsetItems.size());
case Level::DocsetLevel:
return m_docsetRegistry->docset(parent.row())->symbolCounts().count();
return itemInRow(parent.row())->docset->symbolCounts().count();
case Level::GroupLevel: {
DocsetItem *docsetItem = static_cast<DocsetItem *>(parent.internalPointer());
return docsetItem->docset->symbolCount(docsetItem->groups.at(parent.row())->symbolType);
Expand All @@ -177,8 +189,8 @@ int ListModel::rowCount(const QModelIndex &parent) const

void ListModel::addDocset(const QString &name)
{
const int index = m_docsetRegistry->names().indexOf(name);
beginInsertRows(QModelIndex(), index, index);
const int row = std::distance(m_docsetItems.begin(), m_docsetItems.upper_bound(name));
beginInsertRows(QModelIndex(), row, row);

DocsetItem *docsetItem = new DocsetItem();
docsetItem->docset = m_docsetRegistry->docset(name);
Expand All @@ -190,23 +202,25 @@ void ListModel::addDocset(const QString &name)
docsetItem->groups.append(groupItem);
}

m_docsetItems.insert(name, docsetItem);
m_docsetItems.insert({name, docsetItem});

endInsertRows();
}

void ListModel::removeDocset(const QString &name)
{
const int index = m_docsetItems.keys().indexOf(name);
// TODO: Investigate why this can happen (see #420)
if (index == -1)
auto it = m_docsetItems.find(name);
if (it == m_docsetItems.cend()) {
// TODO: Investigate why this can happen (see #420)
return;
}

beginRemoveRows(QModelIndex(), index, index);
const int row = std::distance(m_docsetItems.begin(), it);
beginRemoveRows(QModelIndex(), row, row);

DocsetItem *docsetItem = m_docsetItems.take(name);
qDeleteAll(docsetItem->groups);
delete docsetItem;
qDeleteAll(it->second->groups);
delete it->second;
m_docsetItems.erase(it);

endRemoveRows();
}
Expand All @@ -230,3 +244,10 @@ ListModel::Level ListModel::indexLevel(const QModelIndex &index)
else
return Level::SymbolLevel;
}

ListModel::DocsetItem *ListModel::itemInRow(int row) const
{
auto it = m_docsetItems.cbegin();
std::advance(it, row);
return it->second;
}
7 changes: 5 additions & 2 deletions src/libs/registry/listmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
#ifndef LISTMODEL_H
#define LISTMODEL_H

#include <util/caseinsensitivemap.h>

#include <QAbstractItemModel>
#include <QMap>

namespace Zeal {
namespace Registry {
Expand Down Expand Up @@ -76,7 +77,9 @@ private slots:
QList<GroupItem *> groups;
};

QMap<QString, DocsetItem *> m_docsetItems;
inline DocsetItem *itemInRow(int row) const;

Util::CaseInsensitiveMap<DocsetItem *> m_docsetItems;
};

} // namespace Registry
Expand Down
10 changes: 6 additions & 4 deletions src/libs/ui/docsetsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ void DocsetsDialog::extractionCompleted(const QString &filePath)
const QString docsetPath = dataDir.filePath(docsetName + QLatin1String(".docset"));

// Write metadata about docset
Registry::DocsetMetadata metadata = m_availableDocsets.contains(docsetName)
Registry::DocsetMetadata metadata = m_availableDocsets.count(docsetName)
? m_availableDocsets[docsetName] : m_userFeeds[docsetName];
metadata.save(docsetPath, metadata.latestVersion());

Expand Down Expand Up @@ -745,11 +745,13 @@ void DocsetsDialog::processDocsetList(const QJsonArray &list)
QJsonObject docsetJson = v.toObject();

Registry::DocsetMetadata metadata(docsetJson);
m_availableDocsets.insert(metadata.name(), metadata);
m_availableDocsets.insert({metadata.name(), metadata});
}

// TODO: Move into dedicated method
for (const Registry::DocsetMetadata &metadata : m_availableDocsets) {
for (const auto &kv : m_availableDocsets) {
const auto &metadata = kv.second;

QListWidgetItem *listItem
= new QListWidgetItem(metadata.icon(), metadata.title(), ui->availableDocsetList);
listItem->setData(Registry::ItemDataRole::DocsetNameRole, metadata.name());
Expand Down Expand Up @@ -777,7 +779,7 @@ void DocsetsDialog::downloadDashDocset(const QModelIndex &index)
{
const QString name = index.data(Registry::ItemDataRole::DocsetNameRole).toString();

if (!m_availableDocsets.contains(name) && !m_userFeeds.contains(name))
if (m_availableDocsets.count(name) == 0 && !m_userFeeds.contains(name))
return;

QUrl url;
Expand Down
3 changes: 2 additions & 1 deletion src/libs/ui/docsetsdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define ZEAL_WIDGETUI_DOCSETSDIALOG_H

#include <registry/docsetmetadata.h>
#include <util/caseinsensitivemap.h>

#include <QDialog>
#include <QHash>
Expand Down Expand Up @@ -96,7 +97,7 @@ private slots:
qint64 m_combinedReceived = 0;

// TODO: Create a special model
QMap<QString, Registry::DocsetMetadata> m_availableDocsets;
Util::CaseInsensitiveMap<Registry::DocsetMetadata> m_availableDocsets;
QMap<QString, Registry::DocsetMetadata> m_userFeeds;

QHash<QString, QTemporaryFile *> m_tmpFiles;
Expand Down
1 change: 1 addition & 0 deletions src/libs/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
set(CMAKE_AUTOMOC OFF)

add_library(Util
caseinsensitivemap.h
plist.cpp
sqlitedatabase.cpp
version.cpp
Expand Down
45 changes: 45 additions & 0 deletions src/libs/util/caseinsensitivemap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/****************************************************************************
**
** Copyright (C) 2018 Oleg Shparber
** Contact: https://go.zealdocs.org/l/contact
**
** This file is part of Zeal.
**
** Zeal is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Zeal is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Zeal. If not, see <https://www.gnu.org/licenses/>.
**
****************************************************************************/

#ifndef ZEAL_UTIL_CASEINSENSITIVEMAP_H
#define ZEAL_UTIL_CASEINSENSITIVEMAP_H

#include <QString>

#include <map>

namespace Zeal {
namespace Util {

struct CaseInsensitiveStringComparator {
bool operator()(const QString &lhs, const QString &rhs) const {
return QString::compare(lhs, rhs, Qt::CaseInsensitive) < 0;
}
};

template <typename T>
using CaseInsensitiveMap = std::map<QString, T, CaseInsensitiveStringComparator>;

} // namespace Util
} // namespace Zeal

#endif // ZEAL_UTIL_CASEINSENSITIVEMAP_H

0 comments on commit 0a33df2

Please sign in to comment.