QtNovelUI/libProjectManager/xmlprojectmanager.cpp

528 lines
16 KiB
C++
Raw Normal View History

2022-11-17 08:26:05 +00:00
#include "xmlprojectmanager.h"
#include <xmlconfig.h>
#include <QDir>
#include <QRandomGenerator>
#include <QDateTime>
#include <QTextStream>
#include <QCoreApplication>
#include <QIcon>
using namespace Project;
using namespace Config;
class Impl_ProjectException : public ProjectException
{
public:
Impl_ProjectException(const QString& title, const QString& reason)
2023-02-26 14:44:00 +00:00
{
temp_string = QString("%1%2").arg(title, reason);
title_length = title.length();
}
private:
2023-02-26 14:44:00 +00:00
int title_length;
QString temp_string;
// exception interface
public:
2023-02-26 14:44:00 +00:00
virtual const char *what() const override{
return temp_string.toLocal8Bit();
}
// ParseException interface
public:
2023-02-26 14:44:00 +00:00
virtual QString reason() const override{return temp_string.mid(title_length+1);}
virtual QString title() const override{ return temp_string.mid(0, title_length);}
};
2022-11-17 08:26:05 +00:00
XMLProjectManager::XMLProjectManager(QObject *parent)
: QObject(parent),
2022-11-17 08:26:05 +00:00
project_config(new XMLConfig(this)),
project_structure(new QStandardItemModel(this)),
2023-02-26 14:44:00 +00:00
open_status(false)
{
}
2022-11-17 08:26:05 +00:00
XMLProjectManager::~XMLProjectManager(){
delete project_config;
}
2023-02-26 14:44:00 +00:00
QStandardItemModel *XMLProjectManager::model() const
{
return this->project_structure;
}
QString XMLProjectManager::suffix() const
{
return "nsf";
}
2023-08-12 13:01:01 +00:00
bool XMLProjectManager::isOpenning() const noexcept { return open_status; }
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
bool XMLProjectManager::isModified() const noexcept
{
return true;
}
2022-11-17 08:26:05 +00:00
/*
* <?xml version='1.0'?>
* <project name="project-name" config="xml-path">
* <package name="name" path="filepath">
* <file name="name" path="filepath"/>
* <package name="name2" path="filepath" />
* </package>
* </project>
*/
void XMLProjectManager::openProject(const QString &project_file)
2022-11-17 08:26:05 +00:00
{
filepath_store = project_file;
2022-11-17 08:26:05 +00:00
QFileInfo info(project_file);
if(!info.exists())
throw new Impl_ProjectException("打开失败", "指定的项目文件不存在:"+project_file);
2022-11-17 08:26:05 +00:00
// 获取项目文件内容
QFile project_in(project_file);
if(!project_in.open(QIODevice::ReadOnly|QIODevice::Text))
throw new Impl_ProjectException("打开失败", "指定的项目文件无法打开:" + project_file);
2022-11-17 08:26:05 +00:00
// 载入文件内容
QDomDocument doc;
QString err; int row, col;
if(!doc.setContent(&project_in, false, &err, &row, &col))
throw new Impl_ProjectException("打开失败", QString("项目文件格式错误:%1row%2col%3").arg(err).arg(row).arg(col));
2022-11-17 08:26:05 +00:00
// 构建项目组织树
auto root_elm = doc.documentElement();
auto name = root_elm.attribute("name");
2023-02-26 14:44:00 +00:00
auto pnode = new ProjectNode(NodeType::ROOT, name);
2022-11-17 08:26:05 +00:00
pnode->setFile(root_elm.attribute("config"));
project_structure->appendRow(pnode);
2022-11-17 08:26:05 +00:00
// 载入项目配置
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"));
}
2023-08-12 13:01:01 +00:00
void XMLProjectManager::newProject(const QString &path_holder, const QString &name) {
2022-11-17 08:26:05 +00:00
// 确定目标项目文件
2023-08-12 13:01:01 +00:00
filepath_store = path_holder;
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
QFileInfo info(filepath_store);
2022-11-17 08:26:05 +00:00
if(info.exists())
throw new Impl_ProjectException("新建错误", "指定路径的项目文件已经存在,无法构建新项目");
2022-11-17 08:26:05 +00:00
// 构建项目表示树
2023-02-26 14:44:00 +00:00
auto project_node = new ProjectNode(NodeType::ROOT, name);
2022-11-17 08:26:05 +00:00
project_node->setFile(".project_config.xml");
project_structure->appendRow(project_node);
2023-08-12 13:01:01 +00:00
QDir root = info.absoluteDir();
2022-11-17 08:26:05 +00:00
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();
2022-11-17 08:26:05 +00:00
}
void XMLProjectManager::save()
{
project_config->save();
QDomDocument doc;
auto pnode = static_cast<ProjectNode*>(project_structure->item(0));
2022-11-17 08:26:05 +00:00
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);
2022-11-17 08:26:05 +00:00
if(!records.open(QIODevice::WriteOnly|QIODevice::Text))
throw new Impl_ProjectException("保存错误", "保存过程中,指定项目文件无法打开保存:" + filepath_store);
2022-11-17 08:26:05 +00:00
QTextStream txout(&records);
doc.save(txout, 4);
txout.flush();
2022-11-17 08:26:05 +00:00
}
2023-08-12 13:01:01 +00:00
QString XMLProjectManager::name() const { return project_structure->item(0)->text(); }
2022-11-17 08:26:05 +00:00
2023-08-12 13:01:01 +00:00
void XMLProjectManager::resetName(const QString &name) { this->project_structure->item(0)->setText(name); }
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
Configration *XMLProjectManager::configraions() const
2022-11-17 08:26:05 +00:00
{
2023-02-26 14:44:00 +00:00
return this->project_config;
2022-11-17 08:26:05 +00:00
}
2023-08-12 13:01:01 +00:00
QDir XMLProjectManager::directory() const
{
return QFileInfo(filepath_store).absoluteDir();
}
2023-08-12 13:01:01 +00:00
FilesOperate *XMLProjectManager::operateAccess() const { return const_cast<XMLProjectManager *>(this); }
FilesQuery *XMLProjectManager::queryAccess() const { return const_cast<XMLProjectManager *>(this); }
2022-11-17 08:26:05 +00:00
void XMLProjectManager::structure_parser(QDomElement struct_elm, ProjectNode *pnode)
{
auto children = struct_elm.childNodes();
for(auto idx=0; idx<children.count(); ++idx) {
auto node = children.at(idx);
if(node.isElement()){
auto xnode = node.toElement();
ProjectNode *node = nullptr;
if(xnode.tagName()=="package"){
node = new ProjectNode(NodeType::GROUP, xnode.attribute("name"));
}
else if(xnode.tagName() == "file"){
2022-11-17 08:26:05 +00:00
node = new ProjectNode(NodeType::FILE, xnode.attribute("name"));
node->setFile(xnode.attribute("path"));
2023-02-26 14:44:00 +00:00
all_files_managed << node->file();
2022-11-17 08:26:05 +00:00
}
pnode->appendRow(node);
structure_parser(xnode, node);
}
}
}
void XMLProjectManager::structure_severlize(ProjectNode *pnode, QDomElement &elm)
{
auto doc = elm.ownerDocument();
for(auto idx=0; idx<pnode->rowCount(); ++idx){
auto item = static_cast<ProjectNode*>(pnode->child(idx));
QDomElement e = doc.createElement("package");
2022-11-17 08:26:05 +00:00
e.setAttribute("name", item->text());
if(item->nodeType() == NodeType::FILE){
e.setTagName("file");
e.setAttribute("path", item->file());
}
elm.appendChild(e);
2022-11-17 08:26:05 +00:00
structure_severlize(item, e);
}
}
2023-08-12 13:01:01 +00:00
QModelIndex XMLProjectManager::appendFile(const QModelIndex &package_path, const QString &name, const QString &path_on_disk) {
2023-02-26 14:44:00 +00:00
if(!package_path.isValid())
throw new Impl_ProjectException("参数错误", "指定的package_path无效");
2022-11-29 03:47:12 +00:00
2023-02-26 14:44:00 +00:00
auto group = static_cast<ProjectNode*>(project_structure->itemFromIndex(package_path));
2022-11-29 03:47:12 +00:00
if(group->nodeType() == NodeType::FILE)
2023-02-26 14:44:00 +00:00
throw new Impl_ProjectException("参数错误", "传入了文件逻辑路径,而不是包路径");
2022-11-29 03:47:12 +00:00
2023-02-26 14:44:00 +00:00
for(auto idx=0; idx<group->rowCount(); ++idx){
auto child = static_cast<ProjectNode*>(group->child(idx));
if(child->text() == name)
throw new Impl_ProjectException("参数错误", "传入了重复的节点名称:" + name);
}
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
if(all_files_managed.contains(path_on_disk))
throw new Impl_ProjectException("参数错误", "指定路径文件已经纳入项目管理:"+path_on_disk);
2022-11-17 08:26:05 +00:00
auto filenode = new ProjectNode(NodeType::FILE, name);
2023-02-26 14:44:00 +00:00
filenode->setFile(path_on_disk);
all_files_managed << path_on_disk;
2022-11-17 08:26:05 +00:00
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; idx<parent->rowCount(); ++idx)
if(item != parent->child(idx) && item->text() == parent->child(idx)->text())
throw new Impl_ProjectException("参数错误", "传入了重复的节点名称:" + name);
item->setText(name);
2023-02-26 14:44:00 +00:00
}
2022-11-17 08:26:05 +00:00
QList<QModelIndex> XMLProjectManager::filesGather(const QModelIndex &node) const
2023-02-26 14:44:00 +00:00
{
QList<QModelIndex> rets;
2023-02-26 14:44:00 +00:00
if(!node .isValid())
return rets;
auto item = static_cast<ProjectNode*>(project_structure->itemFromIndex(node));
switch (item->nodeType()) {
case NodeType::ROOT:
case NodeType::GROUP:
for(auto idx=0; idx<item->rowCount(); ++idx)
rets.append(filesGather(item->child(idx)->index()));
break;
case NodeType::FILE:
rets.append(item->index());
2023-02-26 14:44:00 +00:00
break;
}
return rets;
2022-11-17 08:26:05 +00:00
}
2023-08-12 13:01:01 +00:00
void XMLProjectManager::deleteT(const QModelIndex &node_path) {
2023-02-26 14:44:00 +00:00
if(!node_path.isValid())
throw new Impl_ProjectException("参数错误", "指定的节点路径无效");
auto xnode = static_cast<ProjectNode*>(project_structure->itemFromIndex(node_path));
if(xnode->nodeType() == NodeType::ROOT)
throw new Impl_ProjectException("参数错误", "不允许删除项目节点");
2022-11-17 08:26:05 +00:00
xnode->parent()->removeRow(xnode->row());
}
2023-02-26 14:44:00 +00:00
QList<std::tuple<QString, QFileInfo> > XMLProjectManager::filesWithEnds(const QString &suffix) const
2022-11-17 08:26:05 +00:00
{
auto root_project = project_structure->item(0);
2022-11-17 08:26:05 +00:00
return nodes_search(static_cast<ProjectNode*>(root_project), suffix);
}
2023-02-26 14:44:00 +00:00
QModelIndex XMLProjectManager::newPackage(const QList<QString> &path)
2022-11-17 08:26:05 +00:00
{
auto pnode = project_structure->item(0);
2022-11-17 08:26:05 +00:00
return groups_rebuild(static_cast<ProjectNode*>(pnode), path);
}
2023-02-26 14:44:00 +00:00
QStringList XMLProjectManager::packagePath(const QModelIndex &path) const
{
auto item = static_cast<ProjectNode*>(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)
2022-11-17 08:26:05 +00:00
{
2023-02-26 14:44:00 +00:00
if(!path.isValid())
throw new Impl_ProjectException("参数错误", "指定的路径参数无效");
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
auto item = static_cast<ProjectNode*>(project_structure->itemFromIndex(path));
if(item->nodeType() == NodeType::GROUP){
return QFileInfo(QFileInfo(filepath_store).canonicalPath());
}
2022-11-17 08:26:05 +00:00
return QFileInfo(QFileInfo(filepath_store).dir().filePath(item->file()));
2022-11-17 08:26:05 +00:00
}
QModelIndex XMLProjectManager::queryIndex(const QFileInfo &file)
{
auto xinfo = query_index(file, project_structure->item(0)->index());
if(xinfo.isValid())
return xinfo;
return QModelIndex();
}
2022-11-17 08:26:05 +00:00
ProjectNode *XMLProjectManager::node_follows(ProjectNode *pnode, const QList<QString> &path_remains)
{
if(path_remains.size()==0)
return nullptr;
for(auto idx=0; idx<pnode->rowCount(); ++idx){
auto node = static_cast<ProjectNode*>(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;
}
2023-02-26 14:44:00 +00:00
QList<std::tuple<QString, QFileInfo>> XMLProjectManager::nodes_search(ProjectNode *pnode, const QString &suffix) const
2022-11-17 08:26:05 +00:00
{
2023-02-26 14:44:00 +00:00
QDir root_dir = QFileInfo(this->filepath_store).absoluteDir();
QList<std::tuple<QString, QFileInfo>> infos_return;
2022-11-17 08:26:05 +00:00
for(auto idx=0; idx<pnode->rowCount(); ++idx){
auto item = static_cast<ProjectNode*>(pnode->child(idx));
if(item->nodeType() == NodeType::FILE){
auto file_path = root_dir.filePath(item->file());
QFileInfo xinfo(file_path);
if(xinfo.suffix() == suffix)
2023-02-26 14:44:00 +00:00
infos_return << std::make_tuple(item->text(), xinfo);
2022-11-17 08:26:05 +00:00
}
else{
auto elist = nodes_search(item, suffix);
infos_return.append(elist);
}
}
return infos_return;
}
2023-02-26 14:44:00 +00:00
QModelIndex XMLProjectManager::groups_rebuild(ProjectNode *pnode, const QList<QString> &path_remains)
2022-11-17 08:26:05 +00:00
{
// 查找对匹配结果
for(auto idx=0; idx<pnode->rowCount(); ++idx){
auto item = static_cast<ProjectNode*>(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));
2023-02-26 14:44:00 +00:00
return item->index();
2022-11-17 08:26:05 +00:00
}
}
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));
2023-02-26 14:44:00 +00:00
return item->index();
2022-11-17 08:26:05 +00:00
}
QModelIndex XMLProjectManager::query_index(const QFileInfo &file, const QModelIndex &base)
{
if(!base.isValid())
return QModelIndex();
auto item = static_cast<ProjectNode*>(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; idx<item->rowCount(); ++idx)
{
auto idx_m = query_index(file, item->child(idx)->index());
if(idx_m.isValid())
return idx_m;
}
}
return QModelIndex();
}
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
void XMLProjectManager::moveTo(const QModelIndex &item_path, const QModelIndex &target_group, int index)
{
if(!item_path.isValid())
throw new Impl_ProjectException("参数错误", "传入的源文件路径无效");
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
if(!target_group.isValid())
throw new Impl_ProjectException("参数错误", "传入的目标包路径无效");
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
auto file = static_cast<ProjectNode*>(project_structure->itemFromIndex(item_path));
if(file->nodeType() == NodeType::GROUP)
throw new Impl_ProjectException("参数错误","传入的源文件节点路径无效");
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
auto group = static_cast<ProjectNode*>(project_structure->itemFromIndex(target_group));
if(group->nodeType() != NodeType::GROUP)
throw new Impl_ProjectException("参数错误", "传入的目标包路径无效");
2022-11-17 08:26:05 +00:00
2023-02-26 14:44:00 +00:00
if(index >= 0 && index < group->rowCount())
2022-11-17 08:26:05 +00:00
group->insertRow(index, file);
else
group->appendRow(file);
}
2023-02-26 14:44:00 +00:00
// 完成线 =======================================================================================================
2022-11-17 08:26:05 +00:00
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"));
2022-11-17 08:26:05 +00:00
}
QString ProjectNode::file() const
{return filename;}