QtNovelUI/libProjectManager/xmlprojectmanager.cpp

422 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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)
: reason_store(reason), title_store(title) {}
private:
QString reason_store;
QString title_store;
// exception interface
public:
virtual const char *what() const override{return reason_store.toLocal8Bit();}
// ParseException interface
public:
virtual QString reason() const override{ return reason_store;}
virtual QString title() const override{return title_store;}
};
XMLProjectManager::XMLProjectManager(QObject *parent)
: QObject(parent),
project_config(new XMLConfig(this)),
project_structure(new QStandardItemModel(this)),
open_status(false){ }
XMLProjectManager::~XMLProjectManager(){
delete project_config;
}
bool XMLProjectManager::isOpen() const
{
return open_status;
}
/*
* <?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)
{
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("项目文件格式错误:%1row%2col%3").arg(err).arg(row).arg(col));
// 构建项目组织树
auto root_elm = doc.documentElement();
auto name = root_elm.attribute("name");
auto pnode = new ProjectNode(NodeType::GROUP, 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 &project_dir, const QString &name)
{
// 确定目标项目文件
QDir root(project_dir);
QDir::setCurrent(root.path());
auto project_file = root.filePath(name + ".nsf");
filepath_store = project_file;
QFileInfo info(project_file);
if(info.exists())
throw new Impl_ProjectException("新建错误", "指定路径的项目文件已经存在,无法构建新项目");
// 构建项目表示树
auto project_node = new ProjectNode(NodeType::GROUP, name);
project_node->setFile(".project_config.xml");
project_structure->appendRow(project_node);
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<ProjectNode*>(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::projectName() const
{
return project_structure->item(0)->text();
}
Configration *XMLProjectManager::configraions() const
{
return this->project_config;
}
QStandardItemModel *XMLProjectManager::model() const
{
return project_structure;
}
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"){
node = new ProjectNode(NodeType::FILE, xnode.attribute("name"));
node->setFile(xnode.attribute("path"));
}
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");
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);
}
}
int XMLProjectManager::createFile(const QList<QString> &path, const QString &name, const QString &suffix)
{
auto pnode = static_cast<ProjectNode*>(project_structure->item(0));
auto group = node_follows(pnode, path);
if(group == nullptr)
return -1;
if(group->nodeType() == NodeType::FILE)
return -2;
for(auto idx=0; idx<group->rowCount(); ++idx)
if(group->child(idx)->text() == name)
return -3;
auto root = QFileInfo(filepath_store).dir();
QRandomGenerator xgen(QDateTime::currentDateTime().offsetFromUtc());
QString filename = "";
do{
QString number = QString("%1").arg(xgen.generate());
filename = QString("contentfile_%1.%2").arg(number, suffix);
}while(QFileInfo::exists(root.absoluteFilePath(filename)));
QFile tfile(root.absoluteFilePath(filename));
if(!tfile.open(QIODevice::Text|QIODevice::WriteOnly))
return -3;
tfile.close();
auto filenode = new ProjectNode(NodeType::FILE, name);
filenode->setFile(filename);
group->appendRow(filenode);
return 0;
}
bool XMLProjectManager::deletePath(const QList<QString> &path)
{
auto root_node = static_cast<ProjectNode*>(project_structure->item(0));
auto xnode = node_follows(root_node, path);
if(xnode == nullptr)
return false;
xnode->parent()->removeRow(xnode->row());
return true;
}
QList<std::tuple<QFileInfo, QString> > XMLProjectManager::filesWithEnds(const QString &suffix) const
{
auto root_project = project_structure->item(0);
return nodes_search(static_cast<ProjectNode*>(root_project), suffix);
}
QStandardItem *XMLProjectManager::newPath(const QList<QString> &path)
{
auto pnode = project_structure->item(0);
return groups_rebuild(static_cast<ProjectNode*>(pnode), path);
}
QFileInfo XMLProjectManager::queryWith(const QList<QString> &path)
{
auto root = project_structure->item(0);
auto item = node_follows(static_cast<ProjectNode*>(root), path);
if(item == nullptr || item->nodeType() == NodeType::GROUP)
return QFileInfo();
return QFileInfo(QFileInfo(filepath_store).dir().filePath(item->file()));
}
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;
}
QList<std::tuple<QFileInfo, QString> > XMLProjectManager::nodes_search(ProjectNode *pnode, const QString &suffix) const
{
auto root_dir = QFileInfo(filepath_store).dir();
QList<std::tuple<QFileInfo, QString>> infos_return;
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)
infos_return << std::make_tuple(xinfo, item->text());
}
else{
auto elist = nodes_search(item, suffix);
infos_return.append(elist);
}
}
return infos_return;
}
ProjectNode* XMLProjectManager::groups_rebuild(ProjectNode *pnode, const QList<QString> &path_remains)
{
// 查找对匹配结果
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));
return item;
}
}
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;
}
// 完成线 =======================================================================================================
bool XMLProjectManager::moveTo(const QList<QString> &item_path, const QList<QString> &target_group, int index)
{
auto root = project_structure->item(0);
auto file = node_follows(static_cast<ProjectNode*>(root), item_path);
if(file == nullptr || file->nodeType() == NodeType::GROUP)
return false;
auto group = node_follows(static_cast<ProjectNode*>(root), target_group);
if(group->rowCount() <= index)
group->insertRow(index, file);
else
group->appendRow(file);
return true;
}
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;}