/* Copyright 2013 MultiMC Contributors * * 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 * * 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. */ #include "instancemodel.h" #include #include #include #include #include #include #include #include #include "data/stdinstance.h" #include "util/pathutils.h" #define GROUP_FILE_FORMAT_VERSION 1 InstanceModel::InstanceModel( QObject* parent ) : QAbstractItemModel() { } InstanceModel::~InstanceModel() { saveGroupInfo(); for(int i = 0; i < groups.size(); i++) { delete groups[i]; } } void InstanceModel::addInstance( InstanceBase* inst, const QString& groupName ) { auto group = getGroupByName(groupName); group->addInstance(inst); } void InstanceGroup::addInstance ( InstanceBase* inst ) { instances.append(inst); inst->setGroup(this); // TODO: notify model. } void InstanceModel::initialLoad(QString dir) { groupFileName = dir + "/instgroups.json"; implicitGroup = new InstanceGroup("Ungrouped", this); groups.append(implicitGroup); // temporary map from instance ID to group name QMap groupMap; if (QFileInfo(groupFileName).exists()) { QFile groupFile(groupFileName); if (!groupFile.open(QIODevice::ReadOnly)) { // An error occurred. Ignore it. qDebug("Failed to read instance group file."); goto groupParseFail; } QTextStream in(&groupFile); QString jsonStr = in.readAll(); groupFile.close(); QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { qWarning(QString("Failed to parse instance group file: %1 at offset %2"). arg(error.errorString(), QString::number(error.offset)).toUtf8()); goto groupParseFail; } if (!jsonDoc.isObject()) { qWarning("Invalid group file. Root entry should be an object."); goto groupParseFail; } QJsonObject rootObj = jsonDoc.object(); // Make sure the format version matches. if (rootObj.value("formatVersion").toVariant().toInt() == GROUP_FILE_FORMAT_VERSION) { // Get the group list. if (!rootObj.value("groups").isObject()) { qWarning("Invalid group list JSON: 'groups' should be an object."); goto groupParseFail; } // Iterate through the list. QJsonObject groupList = rootObj.value("groups").toObject(); for (QJsonObject::iterator iter = groupList.begin(); iter != groupList.end(); iter++) { QString groupName = iter.key(); // If not an object, complain and skip to the next one. if (!iter.value().isObject()) { qWarning(QString("Group '%1' in the group list should " "be an object.").arg(groupName).toUtf8()); continue; } QJsonObject groupObj = iter.value().toObject(); // Create the group object. InstanceGroup *group = new InstanceGroup(groupName, this); groups.push_back(group); // If 'hidden' isn't a bool value, just assume it's false. if (groupObj.value("hidden").isBool() && groupObj.value("hidden").toBool()) { group->setHidden(groupObj.value("hidden").toBool()); } if (!groupObj.value("instances").isArray()) { qWarning(QString("Group '%1' in the group list is invalid. " "It should contain an array " "called 'instances'.").arg(groupName).toUtf8()); continue; } // Iterate through the list of instances in the group. QJsonArray instancesArray = groupObj.value("instances").toArray(); for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) { groupMap[(*iter2).toString()] = groupName; } } } } groupParseFail: qDebug("Loading instances"); QDir instDir(dir); QDirIterator iter(instDir); while (iter.hasNext()) { QString subDir = iter.next(); if (QFileInfo(PathCombine(subDir, "instance.cfg")).exists()) { // TODO Differentiate between different instance types. InstanceBase* inst = new StdInstance(subDir); QString instID = inst->getInstID(); auto iter = groupMap.find(instID); if(iter != groupMap.end()) { addInstance(inst,iter.value()); } else { addInstance(inst); } } } } int InstanceModel::columnCount ( const QModelIndex& parent ) const { // for now... return 1; } QVariant InstanceModel::data ( const QModelIndex& index, int role ) const { if (!index.isValid()) return QVariant(); if (role != Qt::DisplayRole) return QVariant(); InstanceModelItem *item = static_cast(index.internalPointer()); return item->data(index.column()); } QModelIndex InstanceModel::index ( int row, int column, const QModelIndex& parent ) const { if (!hasIndex(row, column, parent)) return QModelIndex(); InstanceModelItem *parentItem; if (!parent.isValid()) parentItem = (InstanceModelItem *) this; else parentItem = static_cast(parent.internalPointer()); InstanceModelItem *childItem = parentItem->getChild(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex InstanceModel::parent ( const QModelIndex& index ) const { if (!index.isValid()) return QModelIndex(); InstanceModelItem *childItem = static_cast(index.internalPointer()); InstanceModelItem *parentItem = childItem->getParent(); if (parentItem == this) return QModelIndex(); return createIndex(parentItem->getRow(), 0, parentItem); } int InstanceModel::rowCount ( const QModelIndex& parent ) const { InstanceModelItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = (InstanceModelItem*) this; else parentItem = static_cast(parent.internalPointer()); return parentItem->numChildren(); } bool InstanceModel::saveGroupInfo() const { /* using namespace boost::property_tree; ptree pt; pt.put("formatVersion", GROUP_FILE_FORMAT_VERSION); try { typedef QMap > GroupListMap; GroupListMap groupLists; for (auto iter = instances.begin(); iter != instances.end(); iter++) { InstanceGroup *group = getInstanceGroup(*iter); if (group != nullptr) groupLists[group].push_back(*iter); } ptree groupsPtree; for (auto iter = groupLists.begin(); iter != groupLists.end(); iter++) { auto group = iter.key(); auto & gList = iter.value(); ptree groupTree; groupTree.put("hidden", group->isHidden()); ptree instList; for (auto iter2 = gList.begin(); iter2 != gList.end(); iter2++) { std::string instID((*iter2)->getInstID().toUtf8()); instList.push_back(std::make_pair("", ptree(instID))); } groupTree.put_child("instances", instList); groupsPtree.push_back(std::make_pair(std::string(group->getName().toUtf8()), groupTree)); } pt.put_child("groups", groupsPtree); write_json(groupFile.toStdString(), pt); } catch (json_parser_error e) { // wxLogError(_("Failed to read group list.\nJSON parser error at line %i: %s"), // e.line(), wxStr(e.message()).c_str()); return false; } catch (ptree_error e) { // wxLogError(_("Failed to save group list. Unknown ptree error.")); return false; } return true; */ return false; } void InstanceModel::setInstanceGroup ( InstanceBase* inst, const QString& groupName ) { /* InstanceGroup *prevGroup = getInstanceGroup(inst); if (prevGroup != nullptr) { groupsMap.remove(inst); } if (!groupName.isEmpty()) { InstanceGroup *newGroup = nullptr; for (auto iter = root->groups.begin(); iter != root->groups.end(); iter++) { if ((*iter)->getName() == groupName) { newGroup = *iter; } } if (newGroup == nullptr) { newGroup = new InstanceGroup(groupName, this); root->groups.push_back(newGroup); } groupsMap[inst] = newGroup; } // TODO: propagate change, reflect in model, etc. //InstanceGroupChanged(inst); */ } InstanceGroup* InstanceModel::getGroupByName ( const QString& name ) const { for (auto iter = groups.begin(); iter != groups.end(); iter++) { if ((*iter)->getName() == name) return *iter; } return nullptr; } /* void InstanceModel::setGroupFile ( QString filename ) { groupFile = filename; }*/ int InstanceModel::numChildren() const { return groups.count(); } InstanceModelItem* InstanceModel::getChild ( int index ) const { return groups[index]; } QVariant InstanceModel::data ( int role ) const { switch(role) { case Qt::DisplayRole: return "name"; } return QVariant(); } InstanceGroup::InstanceGroup(const QString& name, InstanceModel *parent) { this->name = name; this->model = parent; this->hidden = false; } InstanceGroup::~InstanceGroup() { for(int i = 0; i < instances.size(); i++) { delete instances[i]; } } QString InstanceGroup::getName() const { return name; } void InstanceGroup::setName(const QString& name) { this->name = name; //TODO: propagate change } InstanceModelItem* InstanceGroup::getParent() const { return model; } bool InstanceGroup::isHidden() const { return hidden; } void InstanceGroup::setHidden(bool hidden) { this->hidden = hidden; //TODO: propagate change } int InstanceGroup::getRow() const { return model->getIndexOf( this); } InstanceModelItem* InstanceGroup::getChild ( int index ) const { return instances[index]; } int InstanceGroup::numChildren() const { return instances.size(); } QVariant InstanceGroup::data ( int role ) const { switch(role) { case Qt::DisplayRole: return name; default: return QVariant(); } }