PrismLauncher/api/logic/InstanceList.cpp

348 lines
8.4 KiB
C++
Raw Normal View History

2018-02-10 23:40:01 +00:00
/* Copyright 2013-2018 MultiMC Contributors
2013-01-14 23:42:38 +00:00
*
* 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 00:13:40 +01:00
*
2013-01-14 23:42:38 +00:00
* 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 18:15:22 +00:00
#include <QDir>
#include <QSet>
2013-02-19 18:15:22 +00:00
#include <QFile>
2013-03-18 22:00:46 +00:00
#include <QThread>
#include <QTextStream>
#include <QXmlStreamReader>
#include <QDebug>
2013-02-19 18:15:22 +00:00
2015-02-09 00:51:14 +00:00
#include "InstanceList.h"
#include "BaseInstance.h"
2016-10-02 23:55:54 +01:00
#include "FolderInstanceProvider.h"
2013-02-19 18:15:22 +00:00
InstanceList::InstanceList(QObject *parent)
2018-07-15 13:51:05 +01:00
: QAbstractListModel(parent)
2013-02-19 18:15:22 +00:00
{
2018-07-15 13:51:05 +01:00
resumeWatch();
2013-02-19 18:15:22 +00:00
}
InstanceList::~InstanceList()
{
}
2013-10-06 00:13:40 +01:00
int InstanceList::rowCount(const QModelIndex &parent) const
{
2018-07-15 13:51:05 +01:00
Q_UNUSED(parent);
return m_instances.count();
}
2013-10-06 00:13:40 +01:00
QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
{
2018-07-15 13:51:05 +01:00
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 00:13:40 +01:00
QVariant InstanceList::data(const QModelIndex &index, int role) const
{
2018-07-15 13:51:05 +01:00
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 19:11:05 +01:00
{
return pdata->id();
}
2018-07-15 13:51:05 +01:00
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 00:13:40 +01:00
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
{
2018-07-15 13:51:05 +01:00
Qt::ItemFlags f;
if (index.isValid())
{
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
}
return f;
}
QStringList InstanceList::getGroups()
{
2018-07-15 13:51:05 +01:00
return m_groups.toList();
}
void InstanceList::deleteGroup(const QString& name)
{
2018-07-15 13:51:05 +01:00
for(auto & instance: m_instances)
{
auto instGroupName = instance->group();
if(instGroupName == name)
{
instance->setGroupPost(QString());
}
}
}
2016-10-02 23:55:54 +01:00
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
{
2018-07-15 13:51:05 +01:00
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-02 23:55:54 +01:00
}
2016-10-02 23:55:54 +01:00
InstanceList::InstListError InstanceList::loadList(bool complete)
{
2018-07-15 13:51:05 +01:00
auto existingIds = getIdMapping(m_instances);
2018-07-15 13:51:05 +01:00
QList<InstancePtr> newList;
2018-07-15 13:51:05 +01:00
auto processIds = [&](BaseInstanceProvider * provider, QList<InstanceId> ids)
{
for(auto & id: ids)
{
if(existingIds.contains(id))
{
auto instPair = existingIds[id];
/*
auto & instPtr = instPair.first;
auto & instIdx = instPair.second;
*/
existingIds.remove(id);
qDebug() << "Should keep and soft-reload" << id;
}
else
{
InstancePtr instPtr = provider->loadInstance(id);
if(instPtr)
{
newList.append(instPtr);
}
}
}
};
if(complete)
{
for(auto & item: m_providers)
{
processIds(item.get(), item->discoverInstances());
}
}
else
{
for (auto & item: m_updatedProviders)
{
processIds(item, item->discoverInstances());
}
}
2016-10-02 23:55:54 +01:00
2018-07-15 13:51:05 +01:00
// 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;
if(!complete && !m_updatedProviders.contains(instPtr->provider()))
{
continue;
}
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);
}
m_updatedProviders.clear();
return NoError;
}
void InstanceList::saveNow()
{
2018-07-15 13:51:05 +01:00
for(auto & item: m_instances)
{
item->saveNow();
}
}
2016-10-02 23:55:54 +01:00
void InstanceList::add(const QList<InstancePtr> &t)
2013-01-14 23:42:38 +00:00
{
2018-07-15 13:51:05 +01:00
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-02 23:55:54 +01:00
}
2013-10-06 00:13:40 +01:00
2016-10-02 23:55:54 +01:00
void InstanceList::resumeWatch()
{
2018-07-15 13:51:05 +01:00
if(m_watchLevel > 0)
{
qWarning() << "Bad suspend level resume in instance list";
return;
}
m_watchLevel++;
if(m_watchLevel > 0 && !m_updatedProviders.isEmpty())
{
loadList();
}
}
2016-10-02 23:55:54 +01:00
void InstanceList::suspendWatch()
{
2018-07-15 13:51:05 +01:00
m_watchLevel --;
2016-10-02 23:55:54 +01:00
}
2013-10-06 00:13:40 +01:00
2016-10-02 23:55:54 +01:00
void InstanceList::providerUpdated()
{
2018-07-15 13:51:05 +01:00
auto provider = dynamic_cast<BaseInstanceProvider *>(QObject::sender());
if(!provider)
{
qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
return;
}
m_updatedProviders.insert(provider);
if(m_watchLevel == 1)
{
loadList();
}
2013-10-06 00:13:40 +01:00
}
2016-10-02 23:55:54 +01:00
void InstanceList::groupsPublished(QSet<QString> newGroups)
{
2018-07-15 13:51:05 +01:00
m_groups.unite(newGroups);
}
2013-03-18 22:00:46 +00:00
2016-10-02 23:55:54 +01:00
void InstanceList::addInstanceProvider(BaseInstanceProvider* provider)
2013-03-18 22:00:46 +00:00
{
2018-07-15 13:51:05 +01:00
connect(provider, &BaseInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
connect(provider, &BaseInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
m_providers.append(provider);
2013-03-18 22:00:46 +00:00
}
InstancePtr InstanceList::getInstanceById(QString instId) const
2013-03-18 22:00:46 +00:00
{
2018-07-15 13:51:05 +01:00
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 13:51:05 +01:00
return index(getInstIndex(getInstanceById(id).get()));
}
int InstanceList::getInstIndex(BaseInstance *inst) const
{
2018-07-15 13:51:05 +01:00
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 00:13:40 +01:00
void InstanceList::propertiesChanged(BaseInstance *inst)
{
2018-07-15 13:51:05 +01:00
int i = getInstIndex(inst);
if (i != -1)
{
emit dataChanged(index(i), index(i));
}
}