#include "xmlprojectmanager.h" #include #include #include #include #include #include #include using namespace Project; using namespace Config; class Impl_ProjectException : public ProjectException { public: Impl_ProjectException(const QString& title, const QString& reason) { temp_string = QString("%1:%2").arg(title, reason); title_length = title.length(); } private: int title_length; QString temp_string; // exception interface public: virtual const char *what() const override{ return temp_string.toLocal8Bit(); } // ParseException interface public: virtual QString reason() const override{return temp_string.mid(title_length+1);} virtual QString title() const override{ return temp_string.mid(0, title_length);} }; XMLProjectManager::XMLProjectManager(QObject *parent) : QObject(parent), project_config(new XMLConfig(this)), project_structure(new QStandardItemModel(this)), open_status(false) { } XMLProjectManager::~XMLProjectManager(){ delete project_config; } QStandardItemModel *XMLProjectManager::model() const { return this->project_structure; } QString XMLProjectManager::suffix() const { return "nsf"; } bool XMLProjectManager::isOpenning() const noexcept { return open_status; } bool XMLProjectManager::isModified() const noexcept { return true; } /* * * * * * * * */ void XMLProjectManager::openProject(const QString &project_file) { filepath_store = project_file; QFileInfo info(project_file); if(!info.exists()) throw new Impl_ProjectException("打开失败", "指定的项目文件不存在:"+project_file); // 获取项目文件内容 QFile project_in(project_file); if(!project_in.open(QIODevice::ReadOnly|QIODevice::Text)) throw new Impl_ProjectException("打开失败", "指定的项目文件无法打开:" + project_file); // 载入文件内容 QDomDocument doc; QString err; int row, col; if(!doc.setContent(&project_in, false, &err, &row, &col)) throw new Impl_ProjectException("打开失败", QString("项目文件格式错误:%1,row:%2,col:%3").arg(err).arg(row).arg(col)); // 构建项目组织树 auto root_elm = doc.documentElement(); auto name = root_elm.attribute("name"); auto pnode = new ProjectNode(NodeType::ROOT, name); pnode->setFile(root_elm.attribute("config")); project_structure->appendRow(pnode); // 载入项目配置 auto config = root_elm.attribute("config"); auto config_path = info.dir().filePath(config); project_config->loadFile(config_path); structure_parser(root_elm, pnode); open_status = true; pnode->setIcon(QIcon(":/icons/toplevel.png")); } void XMLProjectManager::newProject(const QString &path_holder, const QString &name) { // 确定目标项目文件 filepath_store = path_holder; QFileInfo info(filepath_store); if(info.exists()) throw new Impl_ProjectException("新建错误", "指定路径的项目文件已经存在,无法构建新项目"); // 构建项目表示树 auto project_node = new ProjectNode(NodeType::ROOT, name); project_node->setFile(".project_config.xml"); project_structure->appendRow(project_node); QDir root = info.absoluteDir(); project_config->loadFile(root.filePath(project_node->file())); // 写出到磁盘 save(); open_status = true; project_node->setIcon(QIcon(":/icons/toplevel.png")); } void XMLProjectManager::closeProject() { open_status = false; project_structure->clear(); } void XMLProjectManager::save() { project_config->save(); QDomDocument doc; auto pnode = static_cast(project_structure->item(0)); doc.appendChild(doc.createProcessingInstruction("xml", "version='1.0'")); auto project_elm = doc.createElement("project"); project_elm.setAttribute("name", pnode->text()); project_elm.setAttribute("config", pnode->file()); doc.appendChild(project_elm); structure_severlize(pnode, project_elm); QFile records(filepath_store); if(!records.open(QIODevice::WriteOnly|QIODevice::Text)) throw new Impl_ProjectException("保存错误", "保存过程中,指定项目文件无法打开保存:" + filepath_store); QTextStream txout(&records); doc.save(txout, 4); txout.flush(); } QString XMLProjectManager::name() const { return project_structure->item(0)->text(); } void XMLProjectManager::resetName(const QString &name) { this->project_structure->item(0)->setText(name); } Configration *XMLProjectManager::configraions() const { return this->project_config; } QDir XMLProjectManager::directory() const { return QFileInfo(filepath_store).absoluteDir(); } FilesOperate *XMLProjectManager::operateAccess() const { return const_cast(this); } FilesQuery *XMLProjectManager::queryAccess() const { return const_cast(this); } void XMLProjectManager::structure_parser(QDomElement struct_elm, ProjectNode *pnode) { auto children = struct_elm.childNodes(); for(auto idx=0; idxsetFile(xnode.attribute("path")); all_files_managed << node->file(); } pnode->appendRow(node); structure_parser(xnode, node); } } } void XMLProjectManager::structure_severlize(ProjectNode *pnode, QDomElement &elm) { auto doc = elm.ownerDocument(); for(auto idx=0; idxrowCount(); ++idx){ auto item = static_cast(pnode->child(idx)); QDomElement e = doc.createElement("package"); e.setAttribute("name", item->text()); if(item->nodeType() == NodeType::FILE){ e.setTagName("file"); e.setAttribute("path", item->file()); } elm.appendChild(e); structure_severlize(item, e); } } QModelIndex XMLProjectManager::appendFile(const QModelIndex &package_path, const QString &name, const QString &path_on_disk) { if(!package_path.isValid()) throw new Impl_ProjectException("参数错误", "指定的package_path无效"); auto group = static_cast(project_structure->itemFromIndex(package_path)); if(group->nodeType() == NodeType::FILE) throw new Impl_ProjectException("参数错误", "传入了文件逻辑路径,而不是包路径"); for(auto idx=0; idxrowCount(); ++idx){ auto child = static_cast(group->child(idx)); if(child->text() == name) throw new Impl_ProjectException("参数错误", "传入了重复的节点名称:" + name); } if(all_files_managed.contains(path_on_disk)) throw new Impl_ProjectException("参数错误", "指定路径文件已经纳入项目管理:"+path_on_disk); auto filenode = new ProjectNode(NodeType::FILE, name); filenode->setFile(path_on_disk); all_files_managed << path_on_disk; group->appendRow(filenode); return filenode->index(); } void XMLProjectManager::rename(const QModelIndex &node, const QString &name) { auto item = this->model()->itemFromIndex(node); auto parent = item->parent(); for(auto idx=0; idxrowCount(); ++idx) if(item != parent->child(idx) && item->text() == parent->child(idx)->text()) throw new Impl_ProjectException("参数错误", "传入了重复的节点名称:" + name); item->setText(name); } QList XMLProjectManager::filesGather(const QModelIndex &node) const { QList rets; if(!node .isValid()) return rets; auto item = static_cast(project_structure->itemFromIndex(node)); switch (item->nodeType()) { case NodeType::ROOT: case NodeType::GROUP: for(auto idx=0; idxrowCount(); ++idx) rets.append(filesGather(item->child(idx)->index())); break; case NodeType::FILE: rets.append(item->index()); break; } return rets; } void XMLProjectManager::deleteT(const QModelIndex &node_path) { if(!node_path.isValid()) throw new Impl_ProjectException("参数错误", "指定的节点路径无效"); auto xnode = static_cast(project_structure->itemFromIndex(node_path)); if(xnode->nodeType() == NodeType::ROOT) throw new Impl_ProjectException("参数错误", "不允许删除项目节点"); xnode->parent()->removeRow(xnode->row()); } QList > XMLProjectManager::filesWithEnds(const QString &suffix) const { auto root_project = project_structure->item(0); return nodes_search(static_cast(root_project), suffix); } QModelIndex XMLProjectManager::newPackage(const QList &path) { auto pnode = project_structure->item(0); return groups_rebuild(static_cast(pnode), path); } QStringList XMLProjectManager::packagePath(const QModelIndex &path) const { auto item = static_cast(project_structure->itemFromIndex(path)); auto project_node = project_structure->item(0); auto query_path = [project_node](QStandardItem *it) -> QStringList{ QStringList values; while (it != project_node) { values.insert(0, it->text()); it = it->parent(); } return values; }; if(item->nodeType() == NodeType::GROUP) return query_path(item); else if(item->nodeType() == NodeType::FILE) return query_path(item->parent()); else return QStringList(); } QFileInfo XMLProjectManager::queryInfo(const QModelIndex &path) { if(!path.isValid()) throw new Impl_ProjectException("参数错误", "指定的路径参数无效"); auto item = static_cast(project_structure->itemFromIndex(path)); if(item->nodeType() == NodeType::GROUP){ return QFileInfo(QFileInfo(filepath_store).canonicalPath()); } return QFileInfo(QFileInfo(filepath_store).dir().filePath(item->file())); } QModelIndex XMLProjectManager::queryIndex(const QFileInfo &file) { auto xinfo = query_index(file, project_structure->item(0)->index()); if(xinfo.isValid()) return xinfo; return QModelIndex(); } ProjectNode *XMLProjectManager::node_follows(ProjectNode *pnode, const QList &path_remains) { if(path_remains.size()==0) return nullptr; for(auto idx=0; idxrowCount(); ++idx){ auto node = static_cast(pnode->child(idx)); if(node->text() == path_remains[0]){ if(path_remains.size() == 1) return node; else return node_follows(node, path_remains.mid(1)); } } return nullptr; } QList> XMLProjectManager::nodes_search(ProjectNode *pnode, const QString &suffix) const { QDir root_dir = QFileInfo(this->filepath_store).absoluteDir(); QList> infos_return; for(auto idx=0; idxrowCount(); ++idx){ auto item = static_cast(pnode->child(idx)); if(item->nodeType() == NodeType::FILE){ auto file_path = root_dir.filePath(item->file()); QFileInfo xinfo(file_path); if(xinfo.suffix() == suffix) infos_return << std::make_tuple(item->text(), xinfo); } else{ auto elist = nodes_search(item, suffix); infos_return.append(elist); } } return infos_return; } QModelIndex XMLProjectManager::groups_rebuild(ProjectNode *pnode, const QList &path_remains) { // 查找对匹配结果 for(auto idx=0; idxrowCount(); ++idx){ auto item = static_cast(pnode->child(idx)); if(item->text() == path_remains[0] && item->nodeType() == NodeType::GROUP){ if(path_remains.size() > 1) return groups_rebuild(item, path_remains.mid(1)); return item->index(); } } auto item = new ProjectNode(NodeType::GROUP, path_remains[0]); pnode->appendRow(item); if(path_remains.size() > 1) return groups_rebuild(item, path_remains.mid(1)); return item->index(); } QModelIndex XMLProjectManager::query_index(const QFileInfo &file, const QModelIndex &base) { if(!base.isValid()) return QModelIndex(); auto item = static_cast(project_structure->itemFromIndex(base)); if(item->nodeType() == NodeType::FILE) { auto dir = QFileInfo(filepath_store).absoluteDir(); auto target_info = QFileInfo(dir.filePath(item->file())); if(target_info == file) return item->index(); } else { for(auto idx=0; idxrowCount(); ++idx) { auto idx_m = query_index(file, item->child(idx)->index()); if(idx_m.isValid()) return idx_m; } } return QModelIndex(); } void XMLProjectManager::moveTo(const QModelIndex &item_path, const QModelIndex &target_group, int index) { if(!item_path.isValid()) throw new Impl_ProjectException("参数错误", "传入的源文件路径无效"); if(!target_group.isValid()) throw new Impl_ProjectException("参数错误", "传入的目标包路径无效"); auto file = static_cast(project_structure->itemFromIndex(item_path)); if(file->nodeType() == NodeType::GROUP) throw new Impl_ProjectException("参数错误","传入的源文件节点路径无效"); auto group = static_cast(project_structure->itemFromIndex(target_group)); if(group->nodeType() != NodeType::GROUP) throw new Impl_ProjectException("参数错误", "传入的目标包路径无效"); if(index >= 0 && index < group->rowCount()) group->insertRow(index, file); else group->appendRow(file); } // 完成线 ======================================================================================================= ProjectNode::ProjectNode(NodeType t, const QString &name) : QStandardItem(name),type_indi(t) { if(t == NodeType::FILE) { setIcon(QIcon(":/icons/文件.png")); } else { setIcon(QIcon(":/icons/集合.png")); } setEditable(false); } NodeType ProjectNode::nodeType() const {return type_indi;} void ProjectNode::setFile(const QString &name) { this->filename = name; if(name.endsWith("storychain")) setIcon(QIcon(":/icons/脉络.png")); if(name.endsWith("storyunit")) setIcon(QIcon(":/icons/单元.png")); if(name.endsWith("storyboard")) setIcon(QIcon(":/icons/故事.png")); if(name.endsWith("storyvolume")) setIcon(QIcon(":/icons/叙述.png")); if(name.endsWith("txt")) setIcon(QIcon(":/icons/文件.png")); if(name.endsWith("storyconcept")) setIcon(QIcon(":/icons/概念.png")); } QString ProjectNode::file() const {return filename;}