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>
|
2013-08-25 21:48:41 +01:00
|
|
|
#include <QSet>
|
2013-02-19 18:15:22 +00:00
|
|
|
#include <QFile>
|
2013-03-18 22:00:46 +00:00
|
|
|
#include <QThread>
|
2013-03-18 22:35:54 +00:00
|
|
|
#include <QTextStream>
|
2013-12-20 13:47:26 +00:00
|
|
|
#include <QXmlStreamReader>
|
2015-02-04 20:10:10 +00:00
|
|
|
#include <QDebug>
|
2013-02-19 18:15:22 +00:00
|
|
|
|
2015-02-09 00:51:14 +00:00
|
|
|
#include "InstanceList.h"
|
|
|
|
#include "BaseInstance.h"
|
2015-02-04 20:10:10 +00:00
|
|
|
|
2016-10-02 23:55:54 +01:00
|
|
|
#include "FolderInstanceProvider.h"
|
2018-07-22 23:49:26 +01:00
|
|
|
#include "FileSystem.h"
|
2013-02-19 18:15:22 +00:00
|
|
|
|
2017-12-05 08:52:04 +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
|
|
|
}
|
|
|
|
|
2013-08-25 21:48:41 +01:00
|
|
|
InstanceList::~InstanceList()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-10-06 00:13:40 +01:00
|
|
|
int InstanceList::rowCount(const QModelIndex &parent) const
|
2013-08-26 05:30:11 +01:00
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
Q_UNUSED(parent);
|
|
|
|
return m_instances.count();
|
2013-08-26 05:30:11 +01:00
|
|
|
}
|
|
|
|
|
2013-10-06 00:13:40 +01:00
|
|
|
QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
|
2013-08-26 05:30:11 +01:00
|
|
|
{
|
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-08-26 05:30:11 +01:00
|
|
|
}
|
|
|
|
|
2013-10-06 00:13:40 +01:00
|
|
|
QVariant InstanceList::data(const QModelIndex &index, int role) const
|
2013-08-26 05:30:11 +01:00
|
|
|
{
|
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-08-26 05:30:11 +01:00
|
|
|
}
|
|
|
|
|
2013-10-06 00:13:40 +01:00
|
|
|
Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
|
2013-08-26 05:30:11 +01:00
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
Qt::ItemFlags f;
|
|
|
|
if (index.isValid())
|
|
|
|
{
|
|
|
|
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
|
|
|
}
|
|
|
|
return f;
|
2013-08-26 05:30:11 +01:00
|
|
|
}
|
2013-08-25 21:48:41 +01:00
|
|
|
|
2013-12-15 17:10:51 +00:00
|
|
|
QStringList InstanceList::getGroups()
|
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
return m_groups.toList();
|
2013-12-15 17:10:51 +00:00
|
|
|
}
|
|
|
|
|
2016-04-11 00:30:50 +01:00
|
|
|
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-04-11 00:30:50 +01:00
|
|
|
}
|
|
|
|
|
2018-07-22 23:49:26 +01:00
|
|
|
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-02 23:55:54 +01:00
|
|
|
static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
|
2013-08-25 21:48:41 +01:00
|
|
|
{
|
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-04-11 00:30:50 +01:00
|
|
|
|
2018-07-23 23:11:24 +01:00
|
|
|
InstanceList::InstListError InstanceList::loadList()
|
2016-10-02 23:55:54 +01:00
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
auto existingIds = getIdMapping(m_instances);
|
2013-12-15 17:10:51 +00:00
|
|
|
|
2018-07-15 13:51:05 +01:00
|
|
|
QList<InstancePtr> newList;
|
2013-12-15 17:10:51 +00:00
|
|
|
|
2018-07-23 23:11:24 +01:00
|
|
|
for(auto & id: m_provider->discoverInstances())
|
2018-07-15 13:51:05 +01:00
|
|
|
{
|
2018-07-23 23:11:24 +01:00
|
|
|
if(existingIds.contains(id))
|
2018-07-15 13:51:05 +01:00
|
|
|
{
|
2018-07-23 23:11:24 +01:00
|
|
|
auto instPair = existingIds[id];
|
|
|
|
existingIds.remove(id);
|
|
|
|
qDebug() << "Should keep and soft-reload" << id;
|
2018-07-15 13:51:05 +01:00
|
|
|
}
|
2018-07-23 23:11:24 +01:00
|
|
|
else
|
2018-07-15 13:51:05 +01:00
|
|
|
{
|
2018-07-23 23:11:24 +01:00
|
|
|
InstancePtr instPtr = m_provider->loadInstance(id);
|
|
|
|
if(instPtr)
|
|
|
|
{
|
|
|
|
newList.append(instPtr);
|
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
}
|
|
|
|
}
|
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;
|
|
|
|
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-23 23:11:24 +01:00
|
|
|
m_dirty = false;
|
2018-07-15 13:51:05 +01:00
|
|
|
return NoError;
|
2013-08-25 21:48:41 +01:00
|
|
|
}
|
|
|
|
|
2017-12-03 17:36:28 +00:00
|
|
|
void InstanceList::saveNow()
|
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
for(auto & item: m_instances)
|
|
|
|
{
|
|
|
|
item->saveNow();
|
|
|
|
}
|
2017-12-03 17:36:28 +00:00
|
|
|
}
|
|
|
|
|
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++;
|
2018-07-23 23:11:24 +01:00
|
|
|
if(m_watchLevel > 0 && m_dirty)
|
2018-07-15 13:51:05 +01:00
|
|
|
{
|
|
|
|
loadList();
|
|
|
|
}
|
2013-07-28 07:40:15 +01:00
|
|
|
}
|
|
|
|
|
2016-10-02 23:55:54 +01:00
|
|
|
void InstanceList::suspendWatch()
|
2013-07-28 07:40:15 +01:00
|
|
|
{
|
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-23 23:11:24 +01:00
|
|
|
auto provider = dynamic_cast<FolderInstanceProvider *>(QObject::sender());
|
2018-07-15 13:51:05 +01:00
|
|
|
if(!provider)
|
|
|
|
{
|
|
|
|
qWarning() << "InstanceList::providerUpdated triggered by a non-provider";
|
|
|
|
return;
|
|
|
|
}
|
2018-07-23 23:11:24 +01:00
|
|
|
m_dirty = true;
|
2018-07-15 13:51:05 +01:00
|
|
|
if(m_watchLevel == 1)
|
|
|
|
{
|
|
|
|
loadList();
|
|
|
|
}
|
2013-10-06 00:13:40 +01:00
|
|
|
}
|
2013-10-28 19:55:12 +00:00
|
|
|
|
2016-10-02 23:55:54 +01:00
|
|
|
void InstanceList::groupsPublished(QSet<QString> newGroups)
|
2013-10-28 19:55:12 +00:00
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
m_groups.unite(newGroups);
|
2013-10-28 19:55:12 +00:00
|
|
|
}
|
2013-03-18 22:00:46 +00:00
|
|
|
|
2018-07-23 23:11:24 +01:00
|
|
|
void InstanceList::addInstanceProvider(FolderInstanceProvider* provider)
|
2013-03-18 22:00:46 +00:00
|
|
|
{
|
2018-07-23 23:11:24 +01:00
|
|
|
connect(provider, &FolderInstanceProvider::instancesChanged, this, &InstanceList::providerUpdated);
|
|
|
|
connect(provider, &FolderInstanceProvider::groupsChanged, this, &InstanceList::groupsPublished);
|
|
|
|
m_provider = provider;
|
2013-03-18 22:00:46 +00:00
|
|
|
}
|
|
|
|
|
2013-11-27 14:00:44 +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();
|
2013-03-19 05:24:34 +00:00
|
|
|
}
|
|
|
|
|
2013-11-27 14:00:44 +00:00
|
|
|
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
|
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
return index(getInstIndex(getInstanceById(id).get()));
|
2013-11-27 14:00:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int InstanceList::getInstIndex(BaseInstance *inst) const
|
2013-03-19 05:24:34 +00:00
|
|
|
{
|
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-05-03 20:41:37 +01:00
|
|
|
}
|
2013-08-26 05:30:11 +01:00
|
|
|
|
2013-10-06 00:13:40 +01:00
|
|
|
void InstanceList::propertiesChanged(BaseInstance *inst)
|
2013-08-26 05:30:11 +01:00
|
|
|
{
|
2018-07-15 13:51:05 +01:00
|
|
|
int i = getInstIndex(inst);
|
|
|
|
if (i != -1)
|
|
|
|
{
|
|
|
|
emit dataChanged(index(i), index(i));
|
|
|
|
}
|
2013-08-26 05:30:11 +01:00
|
|
|
}
|