Files
sqlitebrowser/src/RemoteModel.cpp
Martin Kleusberg 0449406450 Fix warning
2018-11-15 14:16:05 +01:00

324 lines
9.5 KiB
C++

#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QImage>
#include "RemoteModel.h"
#include "RemoteDatabase.h"
RemoteModelItem::RemoteModelItem(RemoteModelItem* parent) :
m_parent(parent),
m_fetchedDirectoryList(false)
{
}
RemoteModelItem::~RemoteModelItem()
{
qDeleteAll(m_children);
}
QVariant RemoteModelItem::value(RemoteModelColumns column) const
{
return m_values[column];
}
void RemoteModelItem::setValue(RemoteModelColumns column, QVariant value)
{
m_values[column] = value;
}
void RemoteModelItem::appendChild(RemoteModelItem *item)
{
m_children.append(item);
}
RemoteModelItem* RemoteModelItem::child(int row) const
{
return m_children.value(row);
}
RemoteModelItem* RemoteModelItem::parent() const
{
return m_parent;
}
int RemoteModelItem::childCount() const
{
return m_children.count();
}
int RemoteModelItem::row() const
{
if(m_parent)
return m_parent->m_children.indexOf(const_cast<RemoteModelItem*>(this));
return 0;
}
bool RemoteModelItem::fetchedDirectoryList() const
{
return m_fetchedDirectoryList;
}
void RemoteModelItem::setFetchedDirectoryList(bool fetched)
{
m_fetchedDirectoryList = fetched;
}
QList<RemoteModelItem*> RemoteModelItem::loadArray(const QJsonValue& value, RemoteModelItem* parent)
{
QList<RemoteModelItem*> items;
// Loop through all directory items
QJsonArray array = value.toArray();
for(int i=0;i<array.size();i++)
{
// Create a new model item with the specified parent
RemoteModelItem* item = new RemoteModelItem(parent);
// Save all relevant values. If one of the values isn't set in the JSON document, an empty string
// will be stored
item->setValue(RemoteModelColumnName, array.at(i).toObject().value("name"));
item->setValue(RemoteModelColumnType, array.at(i).toObject().value("type"));
item->setValue(RemoteModelColumnUrl, array.at(i).toObject().value("url"));
item->setValue(RemoteModelColumnCommitId, array.at(i).toObject().value("commit_id"));
item->setValue(RemoteModelColumnSize, array.at(i).toObject().value("size"));
item->setValue(RemoteModelColumnLastModified, array.at(i).toObject().value("last_modified"));
items.push_back(item);
}
return items;
}
RemoteModel::RemoteModel(QObject* parent, RemoteDatabase& remote) :
QAbstractItemModel(parent),
rootItem(new RemoteModelItem()),
remoteDatabase(remote)
{
// Initialise list of column names
headerList << tr("Name") << tr("Commit") << tr("Last modified") << tr("Size");
// Set up signals
connect(&remoteDatabase, &RemoteDatabase::gotDirList, this, &RemoteModel::parseDirectoryListing);
}
RemoteModel::~RemoteModel()
{
delete rootItem;
}
void RemoteModel::setNewRootDir(const QString& url, const QString& cert)
{
// Get user name from client cert
currentUserName = remoteDatabase.getInfoFromClientCert(cert, RemoteDatabase::CertInfoUser);
// Save settings
currentRootDirectory = url;
currentClientCert = cert;
// Fetch root directory and put the reply data under the root item
remoteDatabase.fetch(currentRootDirectory, RemoteDatabase::RequestTypeDirectory, currentClientCert, QModelIndex());
}
void RemoteModel::parseDirectoryListing(const QString& json, const QVariant& userdata)
{
// Load new JSON root document assuming it's an array
QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
if(doc.isNull() || !doc.isArray())
return;
QJsonArray array = doc.array();
// Get model index to store the new data under
QModelIndex parent = userdata.toModelIndex();
RemoteModelItem* parentItem = const_cast<RemoteModelItem*>(modelIndexToItem(parent));
// An invalid model index indicates that this is a new root item. This means the old one needs to be entirely deleted first.
if(!parent.isValid())
{
// Clear root item
beginResetModel();
delete rootItem;
rootItem = new RemoteModelItem();
endResetModel();
// Set parent model index and parent item to the new values
parent = QModelIndex();
parentItem = rootItem;
}
// Insert data
beginInsertRows(parent, 0, array.size());
QList<RemoteModelItem*> items = RemoteModelItem::loadArray(QJsonValue(array), parentItem);
for(RemoteModelItem* item : items)
parentItem->appendChild(item);
endInsertRows();
// Emit directory listing parsed signal
emit directoryListingParsed(parent);
}
QModelIndex RemoteModel::index(int row, int column, const QModelIndex& parent) const
{
if(!hasIndex(row, column, parent))
return QModelIndex();
const RemoteModelItem* parentItem = modelIndexToItem(parent);
RemoteModelItem* childItem = parentItem->child(row);
if(childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex RemoteModel::parent(const QModelIndex& index) const
{
if(!index.isValid())
return QModelIndex();
const RemoteModelItem* childItem = modelIndexToItem(index);
RemoteModelItem* parentItem = childItem->parent();
if(parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
QVariant RemoteModel::data(const QModelIndex& index, int role) const
{
// Don't return data for invalid indices
if(!index.isValid())
return QVariant();
// Type of item
const RemoteModelItem* item = modelIndexToItem(index);
QString type = item->value(RemoteModelColumnType).toString();
// Decoration role? Only for first column!
if(role == Qt::DecorationRole && index.column() == 0)
{
// Use different icons depending on item type
if(type == "folder" && index.parent() == QModelIndex() && item->value(RemoteModelColumnName) == currentUserName)
return QImage(":/icons/folder_user");
else if(type == "folder")
return QImage(":/icons/folder");
else if(type == "database")
return QImage(":/icons/database");
} else if(role == Qt::DisplayRole) {
// Display role?
// Return different value depending on column
switch(index.column())
{
case 0:
{
return item->value(RemoteModelColumnName);
}
case 1:
{
if(type == "folder")
return QVariant();
return item->value(RemoteModelColumnCommitId);
}
case 2:
{
return item->value(RemoteModelColumnLastModified);
}
case 3:
{
// Folders don't have a size
if(type == "folder")
return QVariant();
// Convert size to human readable format
float size = item->value(RemoteModelColumnSize).toLongLong();
QStringList list;
list << "KiB" << "MiB" << "GiB" << "TiB";
QStringListIterator it(list);
QString unit(tr("bytes"));
while(size >= 1024.0f && it.hasNext())
{
unit = it.next();
size /= 1024.0f;
}
return QString().setNum(size, 'f', 2).remove(".00") + QString(" ") + unit;
}
}
}
return QVariant();
}
QVariant RemoteModel::headerData(int section, Qt::Orientation orientation, int role) const
{
// Call default implementation for vertical headers and for non-display roles
if(role != Qt::DisplayRole || orientation != Qt::Horizontal)
return QAbstractItemModel::headerData(section, orientation, role);
// Return header string depending on column
return headerList.at(section);
}
int RemoteModel::rowCount(const QModelIndex& parent) const
{
if(parent.column() > 0)
return 0;
const RemoteModelItem* parentItem = modelIndexToItem(parent);
return parentItem->childCount();
}
int RemoteModel::columnCount(const QModelIndex& /*parent*/) const
{
return headerList.size();
}
bool RemoteModel::hasChildren(const QModelIndex& parent) const
{
if(!parent.isValid())
return true;
// If the item actually has children or is of type "folder" (and may have no children yet), we say that it actually has children
const RemoteModelItem* item = modelIndexToItem(parent);
return item->childCount() || item->value(RemoteModelColumnType) == "folder";
}
bool RemoteModel::canFetchMore(const QModelIndex& parent) const
{
if(!parent.isValid())
return false;
// If the item is of type "folder" and we haven't tried fetching a directory listing yet, we indicate that there might be more data to load
const RemoteModelItem* item = modelIndexToItem(parent);
return item->value(RemoteModelColumnType) == "folder" && !item->fetchedDirectoryList();
}
void RemoteModel::fetchMore(const QModelIndex& parent)
{
// Can we even fetch more data?
if(!canFetchMore(parent))
return;
// Get parent item
RemoteModelItem* item = static_cast<RemoteModelItem*>(parent.internalPointer());
// Fetch item URL
item->setFetchedDirectoryList(true);
remoteDatabase.fetch(item->value(RemoteModelColumnUrl).toString(), RemoteDatabase::RequestTypeDirectory, currentClientCert, parent);
}
const QString& RemoteModel::currentClientCertificate() const
{
return currentClientCert;
}
const RemoteModelItem* RemoteModel::modelIndexToItem(const QModelIndex& idx) const
{
if(!idx.isValid())
return rootItem;
else
return static_cast<const RemoteModelItem*>(idx.internalPointer());
}