pollymc/api/logic/InstanceList.cpp

343 lines
8.2 KiB
C++
Raw Normal View History

2018-02-11 05:10:01 +05:30
/* Copyright 2013-2018 MultiMC Contributors
2013-01-15 05:12:38 +05:30
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
2013-10-06 04:43:40 +05:30
*
2013-01-15 05:12:38 +05:30
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2013-02-19 23:45:22 +05:30
#include <QDir>
#include <QSet>
2013-02-19 23:45:22 +05:30
#include <QFile>
2013-03-19 03:30:46 +05:30
#include <QThread>
#include <QTextStream>
#include <QXmlStreamReader>
#include <QDebug>
2013-02-19 23:45:22 +05:30
2015-02-09 06:21:14 +05:30
#include "InstanceList.h"
#include "BaseInstance.h"
2016-10-03 04:25:54 +05:30
#include "FolderInstanceProvider.h"
#include "FileSystem.h"
2013-02-19 23:45:22 +05:30
InstanceList::InstanceList(QObject *parent)
2018-07-15 18:21:05 +05:30
: QAbstractListModel(parent)
2013-02-19 23:45:22 +05:30
{
2018-07-15 18:21:05 +05:30
resumeWatch();
2013-02-19 23:45:22 +05:30
}
InstanceList::~InstanceList()
{
}
2013-10-06 04:43:40 +05:30
int InstanceList::rowCount(const QModelIndex &parent) const
{
2018-07-15 18:21:05 +05:30
Q_UNUSED(parent);
return m_instances.count();
}
2013-10-06 04:43:40 +05:30
QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
{
2018-07-15 18:21:05 +05:30
Q_UNUSED(parent);
if (row < 0 || row >= m_instances.size())
return QModelIndex();
return createIndex(row, column, (void *)m_instances.at(row).get());
}
2013-10-06 04:43:40 +05:30
QVariant InstanceList::data(const QModelIndex &index, int role) const
{
2018-07-15 18:21:05 +05:30
if (!index.isValid())
{
return QVariant();
}
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
switch (role)
{
case InstancePointerRole:
{
QVariant v = qVariantFromValue((void *)pdata);
return v;
}
case InstanceIDRole:
2014-03-30 23:41:05 +05:30
{
return pdata->id();
}
2018-07-15 18:21:05 +05:30
case Qt::DisplayRole:
{
return pdata->name();
}
case Qt::ToolTipRole:
{
return pdata->instanceRoot();
}
case Qt::DecorationRole:
{
return pdata->iconKey();
}
// HACK: see GroupView.h in gui!
case GroupRole:
{
return pdata->group();
}
default:
break;
}
return QVariant();
}
2013-10-06 04:43:40 +05:30
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
{
2018-07-15 18:21:05 +05:30
Qt::ItemFlags f;
if (index.isValid())
{
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
}
return f;
}
QStringList InstanceList::getGroups()
{
2018-07-15 18:21:05 +05:30
return m_groups.toList();
}
void InstanceList::deleteGroup(const QString& name)
{
2018-07-15 18:21:05 +05:30
for(auto & instance: m_instances)
{
auto instGroupName = instance->group();
if(instGroupName == name)
{
instance->setGroupPost(QString());
}
}
}
void InstanceList::deleteInstance(const InstanceId& id)
{
auto inst = getInstanceById(id);
if(!inst)
{
qDebug() << "Cannot delete instance" << id << " No such instance is present.";
return;
}
qDebug() << "Will delete instance" << id;
if(!FS::deletePath(inst->instanceRoot()))
{
qWarning() << "Deletion of instance" << id << "has not been completely successful ...";
return;
}
qDebug() << "Instance" << id << "has been deleted by MultiMC.";
}
2016-10-03 04:25:54 +05:30
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
{
2018-07-15 18:21:05 +05:30
QMap<InstanceId, InstanceLocator> out;
int i = 0;
for(auto & item: list)
{
auto id = item->id();
if(out.contains(id))
{
qWarning() << "Duplicate ID" << id << "in instance list";
}
out[id] = std::make_pair(item, i);
i++;
}
return out;
2016-10-03 04:25:54 +05:30
}
2018-07-24 03:41:24 +05:30
InstanceList::InstListError InstanceList::loadList()
2016-10-03 04:25:54 +05:30
{
2018-07-15 18:21:05 +05:30
auto existingIds = getIdMapping(m_instances);
2018-07-15 18:21:05 +05:30
QList<InstancePtr> newList;
2018-07-24 03:41:24 +05:30
for(auto & id: m_provider->discoverInstances())
2018-07-15 18:21:05 +05:30
{
2018-07-24 03:41:24 +05:30
if(existingIds.contains(id))
2018-07-15 18:21:05 +05:30
{
2018-07-24 03:41:24 +05:30
auto instPair = existingIds[id];
existingIds.remove(id);
qDebug() << "Should keep and soft-reload" << id;
2018-07-15 18:21:05 +05:30
}
2018-07-24 03:41:24 +05:30
else
2018-07-15 18:21:05 +05:30
{
2018-07-24 03:41:24 +05:30
InstancePtr instPtr = m_provider->loadInstance(id);
if(instPtr)
{
newList.append(instPtr);
}
2018-07-15 18:21:05 +05:30
}
}
2016-10-03 04:25:54 +05:30
2018-07-15 18:21:05 +05:30
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
if(!existingIds.isEmpty())
{
// get the list of removed instances and sort it by their original index, from last to first
auto deadList = existingIds.values();
auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
{
return a.second > b.second;
};
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
// remove the contiguous ranges of rows
int front_bookmark = -1;
int back_bookmark = -1;
int currentItem = -1;
auto removeNow = [&]()
{
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
endRemoveRows();
front_bookmark = -1;
back_bookmark = currentItem;
};
for(auto & removedItem: deadList)
{
auto instPtr = removedItem.first;
instPtr->invalidate();
currentItem = removedItem.second;
if(back_bookmark == -1)
{
// no bookmark yet
back_bookmark = currentItem;
}
else if(currentItem == front_bookmark - 1)
{
// part of contiguous sequence, continue
}
else
{
// seam between previous and current item
removeNow();
}
front_bookmark = currentItem;
}
if(back_bookmark != -1)
{
removeNow();
}
}
if(newList.size())
{
add(newList);
}
2018-07-24 03:41:24 +05:30
m_dirty = false;
2018-07-15 18:21:05 +05:30
return NoError;
}
void InstanceList::saveNow()
{
2018-07-15 18:21:05 +05:30
for(auto & item: m_instances)
{
item->saveNow();
}
}
2016-10-03 04:25:54 +05:30
void InstanceList::add(const QList<InstancePtr> &t)
2013-01-15 05:12:38 +05:30
{
2018-07-15 18:21:05 +05:30
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
m_instances.append(t);
for(auto & ptr : t)
{
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
}
endInsertRows();
2016-10-03 04:25:54 +05:30
}
2013-10-06 04:43:40 +05:30
2016-10-03 04:25:54 +05:30
void InstanceList::resumeWatch()
{
2018-07-15 18:21:05 +05:30
if(m_watchLevel > 0)
{
qWarning() << "Bad suspend level resume in instance list";
return;
}
m_watchLevel++;
2018-07-24 03:41:24 +05:30
if(m_watchLevel > 0 && m_dirty)
2018-07-15 18:21:05 +05:30
{
loadList();
}
}
2016-10-03 04:25:54 +05:30
void InstanceList::suspendWatch()
{
2018-07-15 18:21:05 +05:30
m_watchLevel --;
2016-10-03 04:25:54 +05:30
}
2013-10-06 04:43:40 +05:30
2016-10-03 04:25:54 +05:30
void InstanceList::providerUpdated()
{
2018-07-24 03:41:24 +05:30
auto provider = dynamic_cast<FolderInstanceProvider *>(QObject::sender());
2018-07-15 18:21:05 +05:30
if(!provider)
{
qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
return;
}
2018-07-24 03:41:24 +05:30
m_dirty = true;
2018-07-15 18:21:05 +05:30
if(m_watchLevel == 1)
{
loadList();
}
2013-10-06 04:43:40 +05:30
}
2016-10-03 04:25:54 +05:30
void InstanceList::groupsPublished(QSet<QString> newGroups)
{
2018-07-15 18:21:05 +05:30
m_groups.unite(newGroups);
}
2013-03-19 03:30:46 +05:30
2018-07-24 03:41:24 +05:30
void InstanceList::addInstanceProvider(FolderInstanceProvider* provider)
2013-03-19 03:30:46 +05:30
{
2018-07-24 03:41:24 +05:30
connect(provider, &FolderInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
connect(provider, &FolderInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
m_provider = provider;
2013-03-19 03:30:46 +05:30
}
InstancePtr InstanceList::getInstanceById(QString instId) const
2013-03-19 03:30:46 +05:30
{
2018-07-15 18:21:05 +05:30
if(instId.isEmpty())
return InstancePtr();
for(auto & inst: m_instances)
{
if (inst->id() == instId)
{
return inst;
}
}
return InstancePtr();
}
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
{
2018-07-15 18:21:05 +05:30
return index(getInstIndex(getInstanceById(id).get()));
}
int InstanceList::getInstIndex(BaseInstance *inst) const
{
2018-07-15 18:21:05 +05:30
int count = m_instances.count();
for (int i = 0; i < count; i++)
{
if (inst == m_instances[i].get())
{
return i;
}
}
return -1;
}
2013-10-06 04:43:40 +05:30
void InstanceList::propertiesChanged(BaseInstance *inst)
{
2018-07-15 18:21:05 +05:30
int i = getInstIndex(inst);
if (i != -1)
{
emit dataChanged(index(i), index(i));
}
}