This commit is contained in:
codeboss 2024-09-24 18:43:10 +08:00
parent 3547a2c84a
commit c6d3483d1e
22 changed files with 2884 additions and 55 deletions

View File

@ -1,63 +1,72 @@
#include "argsparser.h"
using namespace args_parse;
using namespace std;
ArgvPackImpl::ArgvPackImpl(const QString& means, ParamType t) : means_store(means), type_store(t) {}
ArgvPackImpl::ArgvPackImpl(const QString& means, ParamType t) : means_store(means), type_store(t) { }
// ͨ¹ý ArgvPack ¼Ì³Ð
ParamType ArgvPackImpl::paramType() const { return type_store; }
ParamType ArgvPackImpl::paramType() const {
return type_store;
}
QString ArgvPackImpl::means() const { return means_store; }
QString ArgvPackImpl::means() const {
return means_store;
}
void ArgvPackImpl::setValue(const QString& v) {
this->value_store = v;
}
QString ArgvPackImpl::value() const { return value_store; }
QString ArgvPackImpl::value() const {
return value_store;
}
FloatArgvPack::FloatArgvPack(const QString& key, const QString& means, bool optional) :
FloatArgvImpl(key, means, optional) {}
FloatArgvImpl(key, means, optional) { }
QString FloatArgvImpl::bindKey() const { return key_name; }
QString FloatArgvImpl::bindKey() const {
return key_name;
}
bool FloatArgvImpl::optional() const {
return optional_value;
}
int FloatArgvPack::matchLenth() const
{
int FloatArgvPack::matchLenth() const {
return 2;
}
bool FloatArgvPack::parse(const QList<QString> args, int start)
{
bool FloatArgvPack::parse(const QList<QString> args, int start) {
auto args_t = args[start];
auto args_v = args[start + 1];
if(args_t == bindKey())
if (args_t == bindKey())
setValue(args_v);
return args_t == bindKey();
}
IndexParam::IndexParam(const QString& means) : ArgvPackImpl(means, ParamType::IndexParam) {}
IndexParam::IndexParam(const QString& means) : ArgvPackImpl(means, ParamType::IndexParam) { }
int args_parse::IndexParam::matchLenth() const
{
int IndexParam::matchLenth() const {
return 1;
}
bool args_parse::IndexParam::parse(const QList<QString> args, int start)
{
bool IndexParam::parse(const QList<QString> args, int start) {
setValue(args[start]);
return true;
}
FloatArgvImpl::FloatArgvImpl(const QString& key, const QString& means, bool optional)
: ArgvPackImpl(means, ParamType::FloatParam), key_name(key), optional_value(optional) {}
: ArgvPackImpl(means, ParamType::FloatParam), key_name(key), optional_value(optional) { }
FloatOption::FloatOption(const QString &key, const QString &means, bool opt)
: FloatArgvImpl(key, means, opt) { setValue(u8"0"); }
FloatOption::FloatOption(const QString& key, const QString& means, bool opt)
: FloatArgvImpl(key, means, opt) {
setValue(u8"0");
}
int FloatOption::matchLenth() const { return 1; }
int FloatOption::matchLenth() const {
return 1;
}
bool FloatOption::parse(const QList<QString> args, int start) {
auto args_t = args[start];
@ -68,68 +77,88 @@ bool FloatOption::parse(const QList<QString> args, int start) {
namespace args_parse {
class MatchMode {
private:
QList<std::shared_ptr<ArgvPack>> args_mode;
QList<shared_ptr<ArgvPack>> args_mode;
int code_store;
public:
explicit MatchMode(const QList<std::shared_ptr<ArgvPack>> mode, int mode_code) :args_mode(mode), code_store(mode_code) {}
explicit MatchMode(const QList<shared_ptr<ArgvPack>> mode, int mode_code)
:args_mode(mode), code_store(mode_code) { }
int modeCode()const {return code_store;}
QList<std::shared_ptr<ArgvPack>> result() const {return args_mode;}
bool parse(const QList<QString>& args, int argv_start, int parse_index) {
if(argv_start >= args.size())
/**
* @brief
* @return
*/
int modeCode()const {
return code_store;
}
/**
* @brief
* @return
*/
QList<shared_ptr<ArgvPack>> result() const {
return args_mode;
}
/**
* @brief
*/
bool parse(const QList<QString>& args, int argv_index, int parse_index) {
if (argv_index >= args.size())
return true;
// 获取模式匹配单元
auto parse_unit = args_mode[parse_index];
switch (parse_unit->paramType()) {
case ParamType::IndexParam: {
parse_unit->parse(args, argv_start);
return parse(args, argv_start + parse_unit->matchLenth(), parse_index + 1);
}break;
case ParamType::FloatParam: {
QList<std::shared_ptr<FloatArgvImpl>> float_parsers;
case ParamType::IndexParam:// 固定位置索引匹配
{
parse_unit->parse(args, argv_index);
// 继续匹配下一个为止
return parse(args, argv_index + parse_unit->matchLenth(), parse_index + 1);
}break;
case ParamType::FloatParam:// 浮动参数匹配
{
QList<shared_ptr<FloatArgvImpl>> float_parsers;
for (auto& unit : args_mode) {
if (unit->paramType() == ParamType::FloatParam)
float_parsers.append(std::dynamic_pointer_cast<FloatArgvImpl>(unit));
}
for (auto& unit : args_mode) {
if (unit->paramType() == ParamType::FloatParam)
float_parsers << dynamic_pointer_cast<FloatArgvImpl>(unit);
}
for (auto& unit : float_parsers) {
if(unit->matchLenth() + argv_start > args.size())
continue;
for (auto& unit : float_parsers) {
if (unit->matchLenth() + argv_index > args.size())
continue;
auto result = unit->parse(args, argv_start);
if (result)
if (parse(args, argv_start + unit->matchLenth(), parse_index + 1))
return true;
else if (!result && unit->optional())
if (parse(args, argv_start, parse_index + 1))
return true;
}
}break;
auto result = unit->parse(args, argv_index);
if (result)
if (parse(args, argv_index + unit->matchLenth(), parse_index + 1))
return true;
else if (!result && unit->optional())
if (parse(args, argv_index, parse_index + 1))
return true;
}
}break;
}
return false;
}
};
}
void args_parse::ArgsParser::loadMode(int mode_code, const QList<std::shared_ptr<ArgvPack>>& args_model)
{
this->match_modes.append(std::make_shared<MatchMode>(args_model, mode_code));
void ArgsParser::loadMode(int mode_code, const QList<shared_ptr<ArgvPack>>& args_model) {
this->match_modes.append(make_shared<MatchMode>(args_model, mode_code));
}
std::tuple<int, QList<std::shared_ptr<ArgvPack>>> args_parse::ArgsParser::parse(int argc, char* argv[]){
tuple<int, QList<shared_ptr<ArgvPack>>> ArgsParser::parse(int argc, char* argv[]) {
// 聚合参数数组
QList<QString> args_list;
for (int idx = 0; idx < argc; idx++) {
auto argv_str = QString::fromLocal8Bit(argv[idx]);
args_list.append(argv_str);
}
// 枚举模式匹配
for (auto& minst : this->match_modes) {
if (minst->parse(args_list, 0, 0))
return std::tuple<int, QList<std::shared_ptr<ArgvPack>>>(minst->modeCode(), minst->result());
return tuple<int, QList<shared_ptr<ArgvPack>>>(minst->modeCode(), minst->result());
}
return std::tuple<int, QList<std::shared_ptr<ArgvPack>>>(0, QList<std::shared_ptr<ArgvPack>>());
return tuple<int, QList<shared_ptr<ArgvPack>>>(0, QList<shared_ptr<ArgvPack>>());
}

View File

@ -0,0 +1,82 @@
# Visual Studio 生成了具有 C++ 设置的 .editorconfig 文件。
root = true
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
# Visual C++ 代码样式设置
cpp_generate_documentation_comments = xml
# Visual C++ 格式设置
cpp_indent_braces = false
cpp_indent_multi_line_relative_to = statement_begin
cpp_indent_within_parentheses = indent
cpp_indent_preserve_within_parentheses = true
cpp_indent_case_contents = true
cpp_indent_case_labels = false
cpp_indent_case_contents_when_block = true
cpp_indent_lambda_braces_when_parameter = false
cpp_indent_goto_labels = one_left
cpp_indent_preprocessor = leftmost_column
cpp_indent_access_specifiers = false
cpp_indent_namespace_contents = true
cpp_indent_preserve_comments = false
cpp_new_line_before_open_brace_namespace = same_line
cpp_new_line_before_open_brace_type = same_line
cpp_new_line_before_open_brace_function = ignore
cpp_new_line_before_open_brace_block = same_line
cpp_new_line_before_open_brace_lambda = ignore
cpp_new_line_scope_braces_on_separate_lines = true
cpp_new_line_close_brace_same_line_empty_type = true
cpp_new_line_close_brace_same_line_empty_function = true
cpp_new_line_before_catch = true
cpp_new_line_before_else = true
cpp_new_line_before_while_in_do_while = false
cpp_space_before_function_open_parenthesis = remove
cpp_space_within_parameter_list_parentheses = false
cpp_space_between_empty_parameter_list_parentheses = false
cpp_space_after_keywords_in_control_flow_statements = true
cpp_space_within_control_flow_statement_parentheses = false
cpp_space_before_lambda_open_parenthesis = false
cpp_space_within_cast_parentheses = false
cpp_space_after_cast_close_parenthesis = true
cpp_space_within_expression_parentheses = false
cpp_space_before_block_open_brace = true
cpp_space_between_empty_braces = true
cpp_space_before_initializer_list_open_brace = false
cpp_space_within_initializer_list_braces = true
cpp_space_preserve_in_initializer_list = true
cpp_space_before_open_square_bracket = false
cpp_space_within_square_brackets = false
cpp_space_before_empty_square_brackets = false
cpp_space_between_empty_square_brackets = false
cpp_space_group_square_brackets = true
cpp_space_within_lambda_brackets = false
cpp_space_between_empty_lambda_brackets = false
cpp_space_before_comma = false
cpp_space_after_comma = true
cpp_space_remove_around_member_operators = true
cpp_space_before_inheritance_colon = true
cpp_space_before_constructor_colon = true
cpp_space_remove_before_semicolon = true
cpp_space_after_semicolon = true
cpp_space_remove_around_unary_operator = true
cpp_space_around_binary_operator = insert
cpp_space_around_assignment_operator = insert
cpp_space_pointer_reference_alignment = left
cpp_space_around_ternary_operator = insert
cpp_use_unreal_engine_macro_formatting = true
cpp_wrap_preserve_blocks = never
# Visual C++ 包含清理设置
cpp_include_cleanup_add_missing_error_tag_type = suggestion
cpp_include_cleanup_remove_unused_error_tag_type = dimmed
cpp_include_cleanup_optimize_unused_error_tag_type = suggestion
cpp_include_cleanup_sort_after_edits = false
cpp_sort_includes_error_tag_type = none
cpp_sort_includes_priority_case_sensitive = false
cpp_sort_includes_priority_style = quoted
cpp_includes_style = default
cpp_includes_use_forward_slash = true

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{48DA8516-26EA-4D59-8913-7EF28E3F87C3}</ProjectGuid>
<Keyword>QtVS_v304</Keyword>
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0</WindowsTargetPlatformVersion>
<QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')">
<Import Project="$(QtMsBuild)\qt_defaults.props" />
</ImportGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings">
<QtInstall>5.12.11_msvc2017_64</QtInstall>
<QtModules>core;xml;gui;widgets</QtModules>
<QtBuildConfig>debug</QtBuildConfig>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings">
<QtInstall>5.12.11_msvc2017_64</QtInstall>
<QtModules>core;xml;gui;widgets</QtModules>
<QtBuildConfig>release</QtBuildConfig>
</PropertyGroup>
<Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
<Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
</Target>
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(QtMsBuild)\Qt.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<TargetName>storym</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<TargetName>StoryManage</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration">
<ClCompile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration">
<ClCompile>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="dag_layout.cpp" />
<ClCompile Include="dag_present.cpp" />
<ClCompile Include="data_type.cpp" />
<ClCompile Include="storyline_compare.cpp" />
<ClCompile Include="view_present.cpp" />
<ClCompile Include="xast_parse.cpp" />
<QtRcc Include="storypresent.qrc" />
<QtMoc Include="storypresent.h" />
<ClCompile Include="storypresent.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="dag_layout.h" />
<QtMoc Include="dag_present.h" />
<ClInclude Include="data_type.h" />
<ClInclude Include="storyline_compare.h" />
<ClInclude Include="view_present.h" />
<ClInclude Include="xast_parse.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
<Import Project="$(QtMsBuild)\qt.targets" />
</ImportGroup>
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Form Files">
<UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier>
<Extensions>ui</Extensions>
</Filter>
<Filter Include="Translation Files">
<UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier>
<Extensions>ts</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<QtRcc Include="storypresent.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
<QtMoc Include="storypresent.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClCompile Include="storypresent.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="xast_parse.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dag_layout.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="data_type.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dag_present.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="view_present.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="storyline_compare.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="xast_parse.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dag_layout.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="data_type.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="view_present.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="storyline_compare.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtMoc Include="dag_present.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommandArguments>--graph --path D:\CustomNovel</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<QtTouchProperty>
</QtTouchProperty>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<QtTouchProperty>
</QtTouchProperty>
</PropertyGroup>
</Project>

301
StoryPresent/dag_layout.cpp Normal file
View File

@ -0,0 +1,301 @@
#include "dag_layout.h"
using namespace dags;
using namespace graph_data;
DAGLayerHelper::DAGLayerHelper(const Node& bind)
: bind_node(bind) {
}
Node DAGLayerHelper::bindPoint() const {
return this->bind_node;
}
int& dags::DAGLayerHelper::inputCount() {
return this->input_count;
}
int dags::DAGLayerHelper::layerValue() const {
return this->layer_v;
}
void dags::DAGLayerHelper::setLayerValue(int v) {
this->layer_v = v;
}
void DAGLayerHelper::nextAppend(std::shared_ptr<DAGLayerHelper> inst) {
if (this->next_points.contains(inst))
return;
this->next_points.append(inst);
inst->input_count++;
}
QList<std::shared_ptr<DAGLayerHelper>> DAGLayerHelper::nextNodes() const {
return this->next_points;
}
DAGOrderHelper::DAGOrderHelper(std::shared_ptr<DAGLayerHelper> bind) : layer_bind(bind) {
this->layer_number = bind->layerValue();
}
DAGOrderHelper::DAGOrderHelper(std::shared_ptr<DAGLayerHelper> f, std::shared_ptr<DAGLayerHelper> t)
: relate_bind(f), towards_to(t) {
}
bool DAGOrderHelper::isFakeNode() const {
return !this->layer_bind;
}
std::shared_ptr<DAGLayerHelper> dags::DAGOrderHelper::layerNode() const {
return this->layer_bind;
}
std::shared_ptr<DAGLayerHelper> dags::DAGOrderHelper::relateNode() const {
return this->relate_bind;
}
std::shared_ptr<DAGLayerHelper> dags::DAGOrderHelper::towardsNode() const {
return this->towards_to;
}
int dags::DAGOrderHelper::layerNumber() const {
return this->layer_number;
}
void dags::DAGOrderHelper::setLayerNumber(int v) {
this->layer_number = v;
}
double dags::DAGOrderHelper::sortNumber() const {
return this->sort_number;
}
void dags::DAGOrderHelper::setSortNumber(double v) {
this->sort_number = v;
}
QList<std::shared_ptr<DAGOrderHelper>> DAGOrderHelper::getUpstreamNodes() const {
return this->__prev_layer_nodes;
}
void DAGOrderHelper::appendUpstreamNode(std::shared_ptr<DAGOrderHelper> inst) {
this->__prev_layer_nodes.append(inst);
}
void DAGGraph::rebuildFromEdges(const QList<Arrow>& arrow_list) {
for (auto& arr : arrow_list) {
auto start = arr.startPoint();
std::shared_ptr<DAGLayerHelper> start_helper;
if (this->graph_inst.contains(start.name())) {
start_helper = this->graph_inst[start.name()];
}
else {
start_helper = std::make_shared<DAGLayerHelper>(start);
this->graph_inst[start.name()] = start_helper;
}
auto end = arr.endPoint();
std::shared_ptr< DAGLayerHelper> end_helper;
if (this->graph_inst.contains(end.name())) {
end_helper = this->graph_inst[end.name()];
}
else {
end_helper = std::make_shared<DAGLayerHelper>(end);
this->graph_inst[end.name()] = end_helper;
}
start_helper->nextAppend(end_helper);
}
}
QList<std::shared_ptr<DAGOrderHelper>> dags::DAGGraph::nodeWithLayout() const {
return this->node_with_layout;
}
int dags::DAGGraph::maxLayerCount() const {
return this->max_layer_count;
}
std::shared_ptr<DAGLayerHelper> DAGGraph::spawns_peak(QList<std::shared_ptr<DAGLayerHelper>>& ref_set) {
for (auto inst : ref_set) {
if (!inst->inputCount()) {
for (auto it_nxt : inst->nextNodes()) {
it_nxt->inputCount()--;
if (!ref_set.contains(it_nxt))
ref_set << it_nxt;
}
ref_set.removeAll(inst);
this->graph_inst.remove(inst->bindPoint().name());
return inst;
}
}
for (auto inst : this->graph_inst.values()) {
if (!inst->inputCount()) {
if (ref_set.contains(inst))
ref_set.removeAll(inst);
for (auto it_nxt : inst->nextNodes()) {
it_nxt->inputCount()--;
if (!ref_set.contains(it_nxt))
ref_set << it_nxt;
}
this->graph_inst.remove(inst->bindPoint().name());
return inst;
}
}
if (this->graph_inst.size()) {
throw "在有向无环图中发现环形回路。";
}
return std::shared_ptr<DAGLayerHelper>();
}
void DAGGraph::graph_recovery(QList<std::shared_ptr<DAGLayerHelper>> sort_seqs) {
for (auto it : sort_seqs) {
it->inputCount() = 0;
}
for (auto it : sort_seqs) {
for (auto nxt : it->nextNodes()) {
nxt->inputCount()++;
}
}
this->graph_inst.clear();
for (auto it : sort_seqs) {
this->graph_inst[it->bindPoint().name()] = it;
}
}
int DAGGraph::node_layering(std::shared_ptr<DAGLayerHelper> inst, int layer_current) {
auto max_remains = layer_current;
if (!layer_current || inst->layerValue() < layer_current) {
inst->setLayerValue(layer_current);
for (auto fork : inst->nextNodes()) {
max_remains = std::max(this->node_layering(fork, inst->layerValue() + 1), max_remains);
}
}
return max_remains + 1;
}
int DAGGraph::node_layering_adj(std::shared_ptr<DAGLayerHelper> inst) {
if (inst->inputCount() > 1)
return inst->layerValue() - 1;
if (!inst->nextNodes().size())
return inst->layerValue() - 1;
auto layer_number = INT_MAX;
for (auto cinst : inst->nextNodes()) {
layer_number = std::min(layer_number, this->node_layering_adj(cinst));
}
inst->setLayerValue(layer_number);
return inst->layerValue() - 1;
}
QList<std::shared_ptr<DAGOrderHelper>> DAGGraph::tidy_graph_nodes() {
QHash<QString, std::shared_ptr<DAGOrderHelper>> nodes_temp;
for (auto node : this->graph_inst.values()) {
nodes_temp[node->bindPoint().name()] = std::make_shared<DAGOrderHelper>(node);
}
QList<std::shared_ptr<DAGOrderHelper>> temp_array;
temp_array.append(nodes_temp.values());
for (auto node : this->graph_inst.values()) {
for (auto next : node->nextNodes()) {
auto node_links = QList<std::shared_ptr<DAGOrderHelper>>{
nodes_temp[node->bindPoint().name()]
};
for (auto layer_index = node->layerValue() + 1; layer_index < next->layerValue(); ++layer_index) {
node_links.append(std::make_shared<DAGOrderHelper>(node, next));
node_links.last()->setLayerNumber(layer_index);
}
node_links.append(nodes_temp[next->bindPoint().name()]);
for (auto idx = 1; idx < node_links.size(); ++idx) {
auto start_point = node_links[idx - 1];
auto end_point = node_links[idx];
end_point->appendUpstreamNode(start_point);
}
temp_array.append(node_links.mid(1, node_links.size() - 2));
}
}
return temp_array;
}
void DAGGraph::graph_layer_nodes_sort(int layer_index, QList<std::shared_ptr<DAGOrderHelper>> nodes) {
QList<std::shared_ptr<DAGOrderHelper>> target_nodes_within_layer;
for (auto n : nodes)
if (n->layerNumber() == layer_index) {
target_nodes_within_layer.append(n);
}
if (target_nodes_within_layer.size()) {
if (!layer_index) {
for (auto idx = 0; idx < target_nodes_within_layer.size(); ++idx) {
target_nodes_within_layer[idx]->setSortNumber(idx + 1);
}
}
else if (layer_index > 0) {
for (auto target_node : target_nodes_within_layer) {
QList<float> prev_sorts;
auto upstream_list = target_node->getUpstreamNodes();
std::transform(upstream_list.begin(), upstream_list.end(),
std::back_inserter(prev_sorts), [](std::shared_ptr<DAGOrderHelper> inst) {
return inst->sortNumber();
});
if (prev_sorts.size()) {
target_node->setSortNumber(std::accumulate(prev_sorts.begin(), prev_sorts.end(), 0) / prev_sorts.size());
}
}
std::sort(target_nodes_within_layer.begin(), target_nodes_within_layer.end(),
[](std::shared_ptr<DAGOrderHelper> a, std::shared_ptr<DAGOrderHelper> b) {
return a->sortNumber() < b->sortNumber();
});
for (auto idx = 0; idx < target_nodes_within_layer.size(); ++idx) {
auto target_item = target_nodes_within_layer[idx];
target_item->setSortNumber(idx + 1);
}
}
this->graph_layer_nodes_sort(layer_index + 1, nodes);
}
}
void dags::DAGGraph::graphLayout() {
QList<std::shared_ptr<DAGLayerHelper>> sort_seqs;
QList<std::shared_ptr<DAGLayerHelper>> refs;
while (1) {
auto peaks = this->spawns_peak(refs);
if (!peaks)
break;
sort_seqs.append(peaks);
}
this->graph_recovery(sort_seqs);
for (auto item : sort_seqs) {
if (!item->inputCount()) {
this->max_layer_count = std::max(this->max_layer_count, this->node_layering(item, 0));
}
}
for (auto item : sort_seqs) {
if (!item->inputCount()) {
this->node_layering_adj(item);
}
}
auto tidy_nodes = this->tidy_graph_nodes();
this->graph_layer_nodes_sort(0, tidy_nodes);
this->node_with_layout = tidy_nodes;
}

92
StoryPresent/dag_layout.h Normal file
View File

@ -0,0 +1,92 @@
#pragma once
#include "data_type.h"
#include <QString>
#include <memory>
#include <QHash>
namespace dags {
/// <summary>
/// 节点分层辅助数据
/// </summary>
class DAGLayerHelper {
private:
graph_data::Node bind_node;
int input_count = 0;
QList<std::shared_ptr<DAGLayerHelper>> next_points;
int layer_v = 0;
public:
explicit DAGLayerHelper(const graph_data::Node& bind);
graph_data::Node bindPoint() const;
int& inputCount();
int layerValue() const;
void setLayerValue(int v);
void nextAppend(std::shared_ptr<DAGLayerHelper> inst);
QList<std::shared_ptr<DAGLayerHelper>> nextNodes() const;
};
/// <summary>
/// 节点排序辅助节点
/// </summary>
class DAGOrderHelper {
private:
std::shared_ptr<DAGLayerHelper> layer_bind = nullptr;
std::shared_ptr<DAGLayerHelper> relate_bind = nullptr;
std::shared_ptr<DAGLayerHelper> towards_to = nullptr;
int layer_number = 0;
double sort_number = 0;
QList<std::shared_ptr<DAGOrderHelper>> __prev_layer_nodes;
public:
/// <summary>
/// 构建一个可视图形节点
/// </summary>
/// <param name="bind">图形节点</param>
DAGOrderHelper(std::shared_ptr<DAGLayerHelper> bind);
/// <summary>
/// 构建一个辅助的不可视图形节点
/// </summary>
/// <param name="f">起始节点</param>
/// <param name="t">终止节点</param>
DAGOrderHelper(std::shared_ptr<DAGLayerHelper> f, std::shared_ptr<DAGLayerHelper> t);
bool isFakeNode() const;
std::shared_ptr<DAGLayerHelper> layerNode() const;
std::shared_ptr<DAGLayerHelper> relateNode() const;
std::shared_ptr<DAGLayerHelper> towardsNode() const;
int layerNumber() const;
void setLayerNumber(int v);
double sortNumber() const;
void setSortNumber(double v);
QList<std::shared_ptr<DAGOrderHelper>> getUpstreamNodes() const;
void appendUpstreamNode(std::shared_ptr<DAGOrderHelper> inst);
};
class DAGGraph {
private:
QHash<QString, std::shared_ptr<DAGLayerHelper>> graph_inst;
QList<std::shared_ptr<DAGOrderHelper>> node_with_layout;
int max_layer_count = 0;
public:
void rebuildFromEdges(const QList<graph_data::Arrow>& arrow_list);
QList<std::shared_ptr<DAGOrderHelper>> nodeWithLayout() const;
int maxLayerCount() const;
void graphLayout();
private:
std::shared_ptr<DAGLayerHelper> spawns_peak(QList<std::shared_ptr<DAGLayerHelper>>& ref_set);
void graph_recovery(QList<std::shared_ptr<DAGLayerHelper>> sort_seqs);
int node_layering(std::shared_ptr<DAGLayerHelper> inst, int layer_current);
int node_layering_adj(std::shared_ptr<DAGLayerHelper> inst);
QList<std::shared_ptr<DAGOrderHelper>> tidy_graph_nodes();
void graph_layer_nodes_sort(int layer_index, QList<std::shared_ptr<DAGOrderHelper>> nodes);
};
}

View File

@ -0,0 +1,344 @@
#include "dag_present.h"
using namespace dags;
ActivePresentNode::ActivePresentNode(const QString name, PrsnType type, QFont font)
: __GraphNodeBase(GraphNodeType::ActivePresentNode),
node_type(type), node_name(name), measure_base(font) {}
QString ActivePresentNode::nodeName() const {
return this->node_name;
}
QRectF ActivePresentNode::boundingRect() const {
auto rect = this->measure_base.boundingRect(this->node_name);
return rect += QMarginsF(0, 0, 10, 10);
}
void ActivePresentNode::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto outline = this->boundingRect();
painter->save();
if (this->node_type == PrsnType::NormalNode)
painter->fillRect(outline, Qt::gray);
else
painter->fillRect(outline, Qt::green);
if (this->isHighlighted()) {
painter->setPen(Qt::red);
}
painter->drawRect(outline);
painter->drawText(outline - QMarginsF(5, 5, 5, 5), this->node_name);
painter->restore();
}
PenetrateNode::PenetrateNode(double width, const QString& towards, const QString& to)
: __GraphNodeBase(GraphNodeType::PenetrateNode),
width_store(width), from_name(towards), to_name(to) {
}
void PenetrateNode::resizeWidth(double width) {
this->width_store = width;
this->update();
}
QString PenetrateNode::nodeFrom() const {
return from_name;
}
QString PenetrateNode::nodeTo() const {
return to_name;
}
QRectF PenetrateNode::boundingRect() const {
return QRectF(0, 0, this->width_store, 8);
}
void PenetrateNode::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto outline = this->boundingRect();
painter->save();
if (this->isHighlighted())
painter->fillRect(QRectF(0, -2, outline.width(), 4), Qt::red);
else
painter->fillRect(QRectF(0, -2, outline.width(), 4), Qt::black);
painter->restore();
}
TransitionCurve::TransitionCurve(IGraphNode* start, IGraphNode* end, double prev_layer_width)
: __GraphNodeBase(GraphNodeType::TransitionCurve),
start_node(start), end_node(end), prev_layer_w(prev_layer_width) {}
void TransitionCurve::layoutRefresh() {
auto orect = this->start_node->boundingRect();
auto erect = this->end_node->boundingRect();
auto xpos = this->start_node->pos().x() + this->start_node->boundingRect().width();
auto width_value = this->end_node->pos().x() - this->start_node->pos().x() - orect.width();
auto ypos = std::min(this->start_node->pos().y(), this->end_node->pos().y());
auto bottom_y = std::max(this->start_node->pos().y() + orect.height(), this->end_node->pos().y() + erect.height());
this->outline = QRectF(0, 0, width_value, bottom_y - ypos);
this->setPos(xpos, ypos);
}
QString TransitionCurve::nodeFrom() const {
if (start_node->nodeType() == GraphNodeType::ActivePresentNode)
return static_cast<ActivePresentNode*>(start_node)->nodeName();
else
return static_cast<PenetrateNode*>(start_node)->nodeFrom();
}
QString TransitionCurve::nodeTo() const {
if (end_node->nodeType() == GraphNodeType::ActivePresentNode)
return static_cast<ActivePresentNode*>(end_node)->nodeName();
else
return static_cast<PenetrateNode*>(end_node)->nodeTo();
}
QRectF TransitionCurve::boundingRect() const {
return this->outline;
}
void TransitionCurve::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto outline = this->boundingRect();
auto start_rect = this->start_node->boundingRect();
auto end_rect = this->end_node->boundingRect();
auto aj_start_pos = this->start_node->pos() + QPointF(start_rect.width(), 0);
auto aj_end_pos = this->end_node->pos();
auto line_span = this->prev_layer_w - start_rect.width();
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
auto start_pos = aj_start_pos - QGraphicsItem::pos();
auto end_pos = aj_end_pos - QGraphicsItem::pos();
auto line_epos = start_pos + QPointF(line_span, 0);
auto control_pos0 = line_epos + QPointF((outline.width() - line_span) / 3, 0);
auto control_pos1 = end_pos - QPointF((outline.width() - line_span) / 3, 0);
auto npen = QPen(Qt::black);
if (this->isHighlighted())
npen = QPen(Qt::red);
npen.setWidthF(4);
painter->setPen(npen);
painter->drawLine(start_pos, line_epos);
auto path0 = QPainterPath();
path0.moveTo(line_epos);
path0.cubicTo(control_pos0, control_pos1, end_pos);
painter->drawPath(path0);
painter->restore();
}
DAGActiveView::DAGActiveView(QWidget* parent)
: QGraphicsView(parent) {
this->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
this->setScene(&this->scene_bind);
auto f = this->font();
f.setPixelSize(25);
this->setFont(f);
}
QList<IGraphNode*> DAGActiveView::layer_nodes_construction(
const QHash<IGraphNode*, std::shared_ptr<DAGOrderHelper>>& prev_layer_helper,
const QList<std::shared_ptr<DAGOrderHelper>>& total_datas, int layer_idx, double prev_layer_end) {
// 挑选当前层次节点
QList<std::shared_ptr<DAGOrderHelper>> current_layer_datas;
std::copy_if(total_datas.begin(), total_datas.end(), std::back_inserter(current_layer_datas),
[&](std::shared_ptr<DAGOrderHelper> ins) { return ins->layerNumber() == layer_idx; });
std::sort(current_layer_datas.begin(), current_layer_datas.end(),
[](std::shared_ptr<DAGOrderHelper>a, std::shared_ptr<DAGOrderHelper> b) {
return a->sortNumber() < b->sortNumber();
});
QHash<IGraphNode*, std::shared_ptr<DAGOrderHelper>> current_nodes_helper;
// 当前层次无节点
if (!current_layer_datas.size())
return current_nodes_helper.keys();
// 构建当前层次图形节点
double ypos_acc = 0;
for (auto& data_node : current_layer_datas) {
if (data_node->isFakeNode()) {
auto from = data_node->relateNode()->bindPoint().name();
auto to = data_node->towardsNode()->bindPoint().name();
auto curr_gnode = new PenetrateNode(20, from, to);
curr_gnode->setPos(prev_layer_end, ypos_acc);
ypos_acc += curr_gnode->boundingRect().height();
current_nodes_helper[curr_gnode] = data_node;
}
else {
auto node_type_vx = PrsnType::NormalNode;
if (!data_node->layerNode()->inputCount()) {
node_type_vx = PrsnType::StartNode;
}
auto curr_gnode = new ActivePresentNode(data_node->layerNode()->bindPoint().name(), node_type_vx, this->font());
curr_gnode->setPos(prev_layer_end, ypos_acc);
ypos_acc += curr_gnode->boundingRect().height();
current_nodes_helper[curr_gnode] = data_node;
}
ypos_acc += this->node_span;
}
// 对其当前层次节点宽度
double curr_layer_width = 0;
for (auto n : current_nodes_helper.keys()) {
curr_layer_width = std::max(curr_layer_width, n->boundingRect().width());
}
for (auto n : current_nodes_helper.keys()) {
if (n->nodeType() == GraphNodeType::PenetrateNode) {
static_cast<PenetrateNode*>(n)->resizeWidth(curr_layer_width);
}
}
auto next_nodes = this->layer_nodes_construction(current_nodes_helper,
total_datas, layer_idx + 1, prev_layer_end + curr_layer_width + this->layer_span);
// 构建层次连线
if (prev_layer_helper.size()) {
double prev_layer_width = 0;
for (auto n : prev_layer_helper.keys()) {
prev_layer_width = std::max(prev_layer_width, n->boundingRect().width());
}
for (auto curr_gnode : current_nodes_helper.keys()) {
auto curr_data = current_nodes_helper[curr_gnode];
auto upstream_nodes = curr_data->getUpstreamNodes();
for (auto prev_gnode : prev_layer_helper.keys()) {
auto prev_data = prev_layer_helper[prev_gnode];
if (upstream_nodes.contains(prev_data)) {
auto line_cmbn = new TransitionCurve(prev_gnode, curr_gnode, prev_layer_width);
line_cmbn->layoutRefresh();
next_nodes << line_cmbn;
}
}
}
}
next_nodes.append(current_nodes_helper.keys());
return next_nodes;
}
void DAGActiveView::updateWithEdges(QList<graph_data::Arrow> arrows) {
DAGGraph tools;
tools.rebuildFromEdges(arrows);
tools.graphLayout();
auto total_nodes = tools.nodeWithLayout();
auto gnodelist = this->layer_nodes_construction(QHash<IGraphNode*, std::shared_ptr<DAGOrderHelper>>(), total_nodes);
for (auto& gnode : gnodelist) {
this->scene_bind.addItem(dynamic_cast<QGraphicsItem*>(gnode));
total_graph_nodes << gnode;
}
}
void DAGActiveView::highlightGraphLink(const QList<graph_data::Arrow> arrows) {
for (auto& n : this->highlight_nodelist)
n->highlight(false);
this->highlight_nodelist.clear();
QList<QString> _color_path;
for (auto& a : arrows) {
_color_path << a.endPoint().name();
_color_path << a.startPoint().name();
}
auto set = _color_path.toSet();
//高亮关键路径点
for (auto& node : this->total_graph_nodes) {
if (node->nodeType() == GraphNodeType::ActivePresentNode) {
auto x_node = static_cast<ActivePresentNode*>(node);
if (set.contains(x_node->nodeName())) {
x_node->highlight(true);
this->highlight_nodelist << x_node;
}
}
}
for (auto idx = 0; idx < arrows.size(); ++idx) {
auto start_name = arrows.at(idx).startPoint().name();
auto end_name = arrows.at(idx).endPoint().name();
for (auto gnode : this->total_graph_nodes) {
switch (gnode->nodeType()) {
// 高亮占位节点
case GraphNodeType::PenetrateNode:
{
auto pinst = dynamic_cast<PenetrateNode*>(gnode);
if (start_name == pinst->nodeFrom() && end_name == pinst->nodeTo()) {
gnode->highlight(true);
this->highlight_nodelist << gnode;
}
} break;
case GraphNodeType::TransitionCurve:
{
auto pinst = dynamic_cast<TransitionCurve*>(gnode);
if (start_name == pinst->nodeFrom() && end_name == pinst->nodeTo()) {
gnode->highlight(true);
this->highlight_nodelist << gnode;
}
} break;
default: break;
}
}
}
this->scene_bind.update();
this->update();
}
void DAGActiveView::mousePressEvent(QMouseEvent* ev) {
QGraphicsView::mousePressEvent(ev);
if (ev->buttons().testFlag(Qt::LeftButton)) {
auto gitems = this->items(ev->pos());
if (!gitems.size())
return;
for (auto& gitem : gitems) {
auto type_item = dynamic_cast<IGraphNode*>(gitem);
if (type_item->nodeType() == GraphNodeType::ActivePresentNode) {
auto node = static_cast<ActivePresentNode*>(type_item);
emit this->nodeClicked(ev->pos(), QList<QString>() << node->nodeName());
break;
}
}
}
}
__GraphNodeBase::__GraphNodeBase(GraphNodeType t)
:_node_type(t), _highlight_mark(false) {}
GraphNodeType __GraphNodeBase::nodeType() const {
return _node_type;
}
void __GraphNodeBase::highlight(bool mark) {
_highlight_mark = mark;
this->update();
}
bool __GraphNodeBase::isHighlighted() const {
return _highlight_mark;
}
QPointF __GraphNodeBase::pos() const {
return QGraphicsItem::pos();
}
void __GraphNodeBase::setPos(qreal x, qreal y) {
QGraphicsItem::setPos(x, y);
}

123
StoryPresent/dag_present.h Normal file
View File

@ -0,0 +1,123 @@
#pragma once
#include <QRectF>
#include <QGraphicsItem>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <memory>
#include "data_type.h"
#include "dag_layout.h"
namespace dags {
enum class GraphNodeType {
ActivePresentNode,
PenetrateNode,
TransitionCurve,
};
class IGraphNode {
public:
virtual GraphNodeType nodeType() const = 0;
virtual void highlight(bool mark) = 0;
virtual bool isHighlighted() const = 0;
virtual QRectF boundingRect() const = 0;
virtual QPointF pos() const = 0;
virtual void setPos(qreal x, qreal y) = 0;
};
class __GraphNodeBase : public QGraphicsItem, public IGraphNode {
private:
GraphNodeType _node_type;
bool _highlight_mark = false;
public:
__GraphNodeBase(GraphNodeType t);
// Inherited via IGraphNode
GraphNodeType nodeType() const override;
void highlight(bool mark) override;
bool isHighlighted() const override;
QPointF pos() const override;
void setPos(qreal x, qreal y) override;
};
enum class PrsnType {
StartNode,
NormalNode,
};
class ActivePresentNode : public __GraphNodeBase {
private:
PrsnType node_type = PrsnType::NormalNode;
QString node_name;
QFontMetricsF measure_base;
public:
ActivePresentNode(const QString name, PrsnType type, QFont font);
QString nodeName() const;
// 通过 QGraphicsItem 继承
QRectF boundingRect() const override;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
};
class PenetrateNode : public __GraphNodeBase {
private:
double width_store = 20;
QString from_name, to_name;
public:
PenetrateNode(double width, const QString &from, const QString &to);
void resizeWidth(double width);
QString nodeFrom() const;
QString nodeTo() const;
// 通过 QGraphicsItem 继承
QRectF boundingRect() const override;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
};
class TransitionCurve : public __GraphNodeBase {
private:
IGraphNode* start_node, * end_node;
double prev_layer_w = 0;
QRectF outline;
public:
TransitionCurve(IGraphNode* start, IGraphNode* end, double prev_layer_width);
void layoutRefresh();
QString nodeFrom() const;
QString nodeTo() const;
// 通过 QGraphicsItem 继承
QRectF boundingRect() const override;
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
};
class DAGActiveView : public QGraphicsView {
Q_OBJECT
private:
double layer_span = 200;
double node_span = 10;
QGraphicsScene scene_bind;
QList<IGraphNode*> highlight_nodelist;
QList<IGraphNode*> total_graph_nodes;
signals:
void nodeClicked(const QPointF &pos, const QList<QString> &node_name);
public:
DAGActiveView(QWidget* parent = nullptr);
QList<IGraphNode*> layer_nodes_construction(
const QHash<IGraphNode*, std::shared_ptr<dags::DAGOrderHelper>>& prev_layer,
const QList<std::shared_ptr<dags::DAGOrderHelper>>& total_datas,
int layer_idx = 0, double prev_layer_end = 0);
void updateWithEdges(QList<graph_data::Arrow> arrows);
void highlightGraphLink(const QList<graph_data::Arrow> color_path);
virtual void mousePressEvent(QMouseEvent *ev) override;
};
};

View File

@ -0,0 +1,30 @@
#include "data_type.h"
using namespace graph_data;
Node::Node(const QString& name) : _point_name(name) { }
QString Node::name() const {
return _point_name;
}
Line::Line(const Node& _p0, const Node& _p1) :_p0(_p0), _p1(_p1) {}
Line::Line(const QString& n0, const QString& n1) :
Line(Node(n0), Node(n1)) {}
QList<Node> Line::points() const {
return QList<Node>{this->_p0, this->_p1};
}
Arrow::Arrow(const Node& s, const Node& e) : Line(s, e) {}
Arrow::Arrow(const QString& s, const QString& e) : Line(s, e) {}
Node Arrow::startPoint() const {
return this->points()[0];
}
Node Arrow::endPoint() const {
return this->points()[1];
}

44
StoryPresent/data_type.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <QString>
#include <QList>
namespace graph_data {
/// <summary>
/// 图形节点
/// </summary>
class Node {
private:
QString _point_name;
public:
explicit Node(const QString& name);
QString name() const;
};
/// <summary>
/// 图形边
/// </summary>
class Line {
private:
Node _p0, _p1;
public:
Line(const Node& _p0, const Node& _p1);
Line(const QString& n0, const QString& n1);
QList<Node> points() const;
};
/// <summary>
/// 带方向的边
/// </summary>
class Arrow : public Line {
public:
Arrow(const Node& s, const Node& e);
Arrow(const QString& s, const QString& e);
Node startPoint() const;
Node endPoint() const;
};
}

17
StoryPresent/main.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "storypresent.h"
#include "dag_layout.h"
#include "xast_parse.h"
#include "dag_present.h"
#include "view_present.h"
#include <QDebug>
#include <QtWidgets/QApplication>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
for (auto idx = 0; idx < argc; ++idx) {
qDebug() << argv[idx];
}
return a.exec();
}

View File

@ -0,0 +1,200 @@
#include "storyline_compare.h"
using namespace compare;
using namespace xast_parse;
using namespace std;
Compare::Compare(const QHash<QString, shared_ptr<StoryDefine>>& graph)
: _graph_base(graph) {}
const QHash<QString, std::shared_ptr<xast_parse::StoryDefine>>& compare::Compare::graphBind() const
{
return _graph_base;
}
QList<shared_ptr<FragmentDefine>>
Compare::changeCompare(Type _type, const QHash<QString, shared_ptr<StoryDefine>>& g_old) const {
QList<shared_ptr<FragmentDefine>> values;
for (auto& type : _graph_base.keys()) {
if (g_old.contains(type)) {
auto story_new = _graph_base[type];
auto story_old = g_old[type];
auto story_new_temp = story_new->firstChild();
QList<QString> fragmets_define;
while (story_new_temp) {
if (story_new_temp->type() == SliceType::FragmentDefines) {
auto fragm = std::dynamic_pointer_cast<FragmentDefine>(story_new_temp);
fragmets_define << fragm->name();
}
story_new_temp = story_new_temp->nextSlice();
}
for (auto fragm : fragmets_define) {
auto fragm_this = std::dynamic_pointer_cast<FragmentDefine>(story_new->getFragment(fragm));
auto fragm_peer = std::dynamic_pointer_cast<FragmentDefine>(story_old->getFragment(fragm));
if (fragm_peer) {
if (_type == Type::FragmentAlter) {
if (fragment_check(fragm_this, fragm_peer))
values << fragm_this;
}
else {
if (upstream_check(fragm_this, fragm_peer))
values << fragm_this;
}
}
}
}
}
return values;
}
bool Compare::fragment_check(std::shared_ptr<FragmentDefine> a, std::shared_ptr<FragmentDefine> b) const
{
auto texts_a = a->getTextNode();
auto texts_b = b->getTextNode();
if (texts_a->getLines() != texts_b->getLines())
return true;
auto slices_a = a->referSlices();
auto slices_b = b->referSlices();
if (slices_a.size() != slices_b.size())
return true;
auto get_label = [](std::shared_ptr<IElementSlice> v) -> QString {
switch (v->type()) {
case SliceType::StoryDefines:
{
auto cast_it = std::dynamic_pointer_cast<StoryDefine>(v);
return "s:" + cast_it->name();
}break;
default:
{
auto cast_it = std::dynamic_pointer_cast<ArticleDefine>(v);
return "a:" + cast_it->name();
}break;
}
};
std::sort(slices_a.begin(), slices_a.end(), [&](std::shared_ptr<IElementSlice>a, std::shared_ptr<IElementSlice>b) {
auto parent_a = a->parentSlice();
auto parent_b = b->parentSlice();
auto label_a = get_label(parent_a.lock());
auto label_b = get_label(parent_b.lock());
return label_a < label_b;
});
std::sort(slices_b.begin(), slices_b.end(), [&](std::shared_ptr<IElementSlice>a, std::shared_ptr<IElementSlice>b) {
auto parent_a = a->parentSlice();
auto parent_b = b->parentSlice();
auto label_a = get_label(parent_a.lock());
auto label_b = get_label(parent_b.lock());
return label_a < label_b;
});
for (auto idx = 0; idx < slices_a.size(); ++idx) {
auto refer_a = std::dynamic_pointer_cast<FragmentRefer>(slices_a.at(idx));
auto refer_b = std::dynamic_pointer_cast<FragmentRefer>(slices_b.at(idx));
auto desc_a = refer_a->getTextNode()->getLines();
auto desc_b = refer_b->getTextNode()->getLines();
if (desc_a != desc_b)
return true;
}
return false;
}
bool Compare::upstream_check(std::shared_ptr<IElementSlice> a, std::shared_ptr<IElementSlice> b) const
{
auto prev_slice_a = a->prevSlice().lock();
auto prev_slice_b = b->prevSlice().lock();
if (!prev_slice_a && prev_slice_b)
return true;
if (prev_slice_a && !prev_slice_b)
return true;
if (prev_slice_a && prev_slice_b) {
if (prev_slice_a->type() != prev_slice_b->type())
return true;
QString desc_a, desc_b;
switch (prev_slice_a->type()) {
case SliceType::TextPragraph:
{
desc_a = std::dynamic_pointer_cast<TextParagraph>(prev_slice_a)->getLines();
desc_b = std::dynamic_pointer_cast<TextParagraph>(prev_slice_b)->getLines();
}break;
case SliceType::FragmentRefers:
{
desc_a = std::dynamic_pointer_cast<FragmentRefer>(prev_slice_a)->getTextNode()->getLines();
desc_b = std::dynamic_pointer_cast<FragmentRefer>(prev_slice_b)->getTextNode()->getLines();
}break;
default:
{
desc_a = std::dynamic_pointer_cast<FragmentDefine>(prev_slice_a)->getTextNode()->getLines();
desc_b = std::dynamic_pointer_cast<FragmentDefine>(prev_slice_b)->getTextNode()->getLines();
}
break;
}
if (desc_a != desc_b)
return true;
}
if (a->type() == SliceType::FragmentDefines) {
auto slices_a = std::dynamic_pointer_cast<FragmentDefine>(a)->referSlices();
auto slices_b = std::dynamic_pointer_cast<FragmentDefine>(b)->referSlices();
if(slices_a.size() != slices_b.size())
return true;
auto get_label = [](std::shared_ptr<IElementSlice> v) -> QString {
switch (v->type()) {
case SliceType::StoryDefines:
{
auto cast_it = std::dynamic_pointer_cast<StoryDefine>(v);
return "s:" + cast_it->name();
}break;
default:
{
auto cast_it = std::dynamic_pointer_cast<ArticleDefine>(v);
return "a:" + cast_it->name();
}break;
}
};
std::sort(slices_a.begin(), slices_a.end(), [&](std::shared_ptr<IElementSlice>a, std::shared_ptr<IElementSlice>b) {
auto parent_a = a->parentSlice();
auto parent_b = b->parentSlice();
auto label_a = get_label(parent_a.lock());
auto label_b = get_label(parent_b.lock());
return label_a < label_b;
});
std::sort(slices_b.begin(), slices_b.end(), [&](std::shared_ptr<IElementSlice>a, std::shared_ptr<IElementSlice>b) {
auto parent_a = a->parentSlice();
auto parent_b = b->parentSlice();
auto label_a = get_label(parent_a.lock());
auto label_b = get_label(parent_b.lock());
return label_a < label_b;
});
for (auto idx = 0; idx < slices_a.size(); ++idx) {
auto refer_a = std::dynamic_pointer_cast<FragmentRefer>(slices_a.at(idx));
auto refer_b = std::dynamic_pointer_cast<FragmentRefer>(slices_b.at(idx));
if(upstream_check(refer_a, refer_b))
return true;
}
}
return false;
}

View File

@ -0,0 +1,41 @@
#pragma once
#include "xast_parse.h"
#include <QHash>
namespace compare {
enum class Type {
UpstreamAlter,
FragmentAlter
};
class Compare {
private:
QHash<QString, std::shared_ptr<xast_parse::StoryDefine>> _graph_base;
public:
explicit Compare(const QHash<QString, std::shared_ptr<xast_parse::StoryDefine>>& graph);
const QHash<QString, std::shared_ptr<xast_parse::StoryDefine>> &graphBind() const;
QList<std::shared_ptr<xast_parse::FragmentDefine>> changeCompare(Type type,
const QHash<QString, std::shared_ptr<xast_parse::StoryDefine>>& g_old) const;
private:
/*
* @brief
* @param a
* @param b
* @return true-
*/
bool fragment_check(std::shared_ptr<xast_parse::FragmentDefine> a, std::shared_ptr<xast_parse::FragmentDefine> b) const;
/*
* @brief
* @param a
* @param b
* @return true-
*/
bool upstream_check(std::shared_ptr<xast_parse::IElementSlice> a, std::shared_ptr<xast_parse::IElementSlice> b) const;
};
}

View File

@ -0,0 +1,9 @@
#include "storypresent.h"
StoryPresent::StoryPresent(QWidget *parent)
: QMainWindow(parent)
{
}
StoryPresent::~StoryPresent()
{}

View File

@ -0,0 +1,12 @@
#pragma once
#include <QtWidgets/QMainWindow>
class StoryPresent : public QMainWindow {
Q_OBJECT
public:
StoryPresent(QWidget* parent = nullptr);
~StoryPresent();
};

View File

@ -0,0 +1,4 @@
<RCC>
<qresource prefix="StoryPresent">
</qresource>
</RCC>

View File

@ -0,0 +1,323 @@
#include "view_present.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSplitter>
#include <QDebug>
using namespace views;
using namespace xast_parse;
using namespace std;
using namespace compare;
StoryChangePresent::StoryChangePresent(
const QHash<QString, shared_ptr<StoryDefine>>& g_base,
const QHash<QString, shared_ptr<StoryDefine>>& g_old,
QWidget* parent) :
QWidget(parent),
base_compare(Compare(g_base)), g_old(g_old),
_type_appoint(new QToolBox(this)),
_fragment_summary(new QTableView(this)),
_upstream_summary(new QTableView(this)),
_change_model(new QStandardItemModel(this)),
_story_appoint(new QComboBox(this)),
_edit_splitter(new QSplitter(this)),
_define_view(new QListView(this)),
_refer_view(new QListView(this)),
_defn_fragment(new StorylineModel),
_curr_refslice(new StorylineModel) {
_type_appoint->layout()->setMargin(1);
_type_appoint->layout()->setSpacing(1);
auto _base_layout = new QVBoxLayout(this);
_base_layout->setMargin(0);
_base_layout->setSpacing(0);
// 功能区域左右分割
auto area_splitter = new QSplitter(this);
_base_layout->addWidget(area_splitter);
area_splitter->addWidget(_type_appoint);
// 编辑区域
auto edit_area = new QWidget(this);
area_splitter->addWidget(edit_area);
auto edit_layout = new QVBoxLayout(edit_area);
edit_layout->setMargin(0);
edit_layout->addWidget(_story_appoint);
edit_layout->addWidget(_edit_splitter, 1);
_edit_splitter->addWidget(_refer_view);
_edit_splitter->addWidget(_define_view);
// ============================
_type_appoint->addItem(_upstream_summary, u8"上游修改");
_type_appoint->addItem(_fragment_summary, u8"情节修改");
auto font_default = this->font();
font_default.setPointSize(16);
this->setFont(font_default);
_define_view->setModel(_defn_fragment);
_refer_view->setModel(_curr_refslice);
_fragment_summary->setModel(_change_model);
_upstream_summary->setModel(_change_model);
// ===========================
connect(_type_appoint, &QToolBox::currentChanged, this, &StoryChangePresent::type_change);
type_change(0);
_define_view->setWordWrap(true);
_define_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
_define_view->setItemDelegate(new StorylineDelegate(_define_view));
_refer_view->setWordWrap(true);
_refer_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
_refer_view->setItemDelegate(new StorylineDelegate(_refer_view));
}
Compare& StoryChangePresent::compareTool() {
return this->base_compare;
}
void StoryChangePresent::type_change(int idx) {
disconnect(_story_appoint, QOverload<const QString&>::of(&QComboBox::currentIndexChanged), nullptr, nullptr);
disconnect(_upstream_summary, &QTableView::pressed, nullptr, nullptr);
disconnect(_fragment_summary, &QTableView::pressed, nullptr, nullptr);
_story_appoint->clear();
_story_appoint->setEnabled(false);
_defn_fragment->loadCurrentSlice(nullptr);
_curr_refslice->loadCurrentSlice(nullptr);
_change_model->clear();
QList<shared_ptr<FragmentDefine>> fragms;
if (!idx) {
fragms = base_compare.changeCompare(Type::UpstreamAlter, g_old);
}
else {
fragms = base_compare.changeCompare(Type::FragmentAlter, g_old);
}
_change_model->setHorizontalHeaderLabels(QStringList() << u8"故事名称" << u8"情节名称");
for (auto fragm : fragms) {
auto story = dynamic_pointer_cast<StoryDefine>(fragm->parentSlice().lock());
QList<QStandardItem*> row;
row << new QStandardItem(story->name());
row << new QStandardItem(fragm->name());
_change_model->appendRow(row);
}
_upstream_summary->resizeColumnsToContents();
_fragment_summary->resizeColumnsToContents();
connect(_upstream_summary, &QTableView::pressed, this, &StoryChangePresent::fragment_selected);
connect(_fragment_summary, &QTableView::pressed, this, &StoryChangePresent::fragment_selected);
}
pair<QString, qlonglong> combine_with(const QList<QString>& sections) {
if (sections.isEmpty())
return pair<QString, qlonglong>();
auto head_rst = combine_with(sections.mid(0, sections.size() - 1));
auto target_str = sections.last();
auto final_rst = make_pair(
head_rst.first + "/" + target_str,
head_rst.second << 8 + target_str.size()
);
return final_rst;
}
void StoryChangePresent::fragment_selected(const QModelIndex& item) {
if (!item.isValid())
return;
disconnect(_story_appoint, QOverload<const QString&>::of(&QComboBox::currentIndexChanged), nullptr, nullptr);
_story_appoint->clear();
_story_appoint->setEnabled(true);
auto story_index = item.sibling(item.row(), 0);
auto fragm_index = item.sibling(item.row(), 1);
auto story_item = this->_change_model->itemFromIndex(story_index);
auto fragm_item = this->_change_model->itemFromIndex(fragm_index);
auto story_inst = this->base_compare.graphBind()[story_item->text()];
auto fragm_slice = story_inst->getFragment(fragm_item->text());
auto fragm_def = dynamic_pointer_cast<FragmentDefine>(fragm_slice);
_defn_fragment->loadCurrentSlice(fragm_def);
connect(_story_appoint, QOverload<const QString&>::of(&QComboBox::currentIndexChanged),
this, &StoryChangePresent::storyline_selected);
for (auto refer : fragm_def->referSlices()) {
auto def_v = combine_with(refer->nameSet());
_story_appoint->addItem(def_v.first, def_v.second);
}
}
void StoryChangePresent::storyline_selected(const QString& line) {
auto def_fragm = dynamic_pointer_cast<FragmentDefine>(_defn_fragment->currentSlice());
auto flag = _story_appoint->currentData();
for (auto slice : def_fragm->referSlices()) {
auto defv = combine_with(slice->nameSet());
if (flag == defv.second && line == defv.first) {
_curr_refslice->loadCurrentSlice(slice);
break;
}
}
}
void StorylineModel::loadCurrentSlice(shared_ptr<IElementSlice> target_node) {
this->beginResetModel();
this->_current_slice = target_node;
_nodes_temp.clear();
if (this->_current_slice) {
auto pslice = this->_current_slice->parentSlice().lock();
auto conn = dynamic_pointer_cast<ICollection>(pslice);
auto temp_node = conn->firstChild();
while (temp_node) {
_nodes_temp << temp_node;
temp_node = temp_node->nextSlice();
}
}
this->endResetModel();
}
shared_ptr<IElementSlice> StorylineModel::currentSlice() {
return this->_current_slice;
}
QModelIndex StorylineModel::index(int row, int column, const QModelIndex& parent) const {
return createIndex(row, column);
}
QModelIndex StorylineModel::parent(const QModelIndex& child) const {
return QModelIndex();
}
int StorylineModel::rowCount(const QModelIndex& parent) const {
return _nodes_temp.size();
}
int StorylineModel::columnCount(const QModelIndex& parent) const {
return 1;
}
QVariant StorylineModel::data(const QModelIndex& index, int role) const {
switch (role) {
case Qt::EditRole:
case Qt::DisplayRole:
{
auto curr_node = _nodes_temp.at(index.row());
switch (curr_node->type()) {
case SliceType::TextPragraph:
{
auto node = dynamic_pointer_cast<TextParagraph>(curr_node);
return node->getLines();
}
case SliceType::FragmentDefines:
{
auto node = dynamic_pointer_cast<FragmentDefine>(curr_node);
return node->getTextNode()->getLines();
}
case SliceType::FragmentRefers:
{
auto node = dynamic_pointer_cast<FragmentRefer>(curr_node);
return node->getTextNode()->getLines();
}
}
}
case Qt::BackgroundRole:
{
auto curr_node = _nodes_temp.at(index.row());
if (curr_node == _current_slice) {
return QColor(Qt::lightGray);
}
return QColor(Qt::white);
}
default:
return QVariant();
}
}
Qt::ItemFlags StorylineModel::flags(const QModelIndex& idx) const {
auto flags = QAbstractItemModel::flags(idx);
auto curr_node = _nodes_temp.at(idx.row());
if (curr_node == _current_slice)
return flags | Qt::ItemIsEditable;
return flags;
}
bool StorylineModel::setData(const QModelIndex& index, const QVariant& value, int role) {
if (role == Qt::EditRole) {
auto curr_node = _nodes_temp.at(index.row());
switch (curr_node->type()) {
case SliceType::TextPragraph:
{
auto tnode = dynamic_pointer_cast<TextParagraph>(curr_node);
tnode->setLines(value.toString());
}break;
case SliceType::FragmentDefines:
{
auto defnode = dynamic_pointer_cast<FragmentDefine>(curr_node);
defnode->getTextNode()->setLines(value.toString());
}break;
case SliceType::FragmentRefers:
{
auto refnode = dynamic_pointer_cast<FragmentRefer>(curr_node);
refnode->getTextNode()->setLines(value.toString());
}break;
default:
return false;
}
this->dataChanged(index, index, QVector<int>() << Qt::EditRole << Qt::DisplayRole);
return true;
}
else {
return QAbstractItemModel::setData(index, value, role);
}
}
#include <QTextEdit>
StorylineDelegate::StorylineDelegate(QAbstractItemView* pwidget)
:QStyledItemDelegate(pwidget), bind_view(pwidget) { }
QWidget* StorylineDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const {
return new QTextEdit(bind_view);
}
void StorylineDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
static_cast<QTextEdit*>(editor)->setText(index.data(Qt::EditRole).toString());
}
void StorylineDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const {
auto ned = static_cast<QTextEdit*>(editor);
model->setData(index, ned->toPlainText(), Qt::EditRole);
}
void StorylineDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const {
editor->setGeometry(option.rect);
}
QSize StorylineDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
return QStyledItemDelegate::sizeHint(option, index) + QSize(0, 20);
}
#include <QMenu>
#include <QMenuBar>
#include <QFileDialog>
CompareWindow::CompareWindow(QFileInfo filebase, QFileInfo fileold)
:_graph_base(XAST_Parser(filebase.canonicalFilePath())),
_graph_old(XAST_Parser(fileold.canonicalFilePath())),
_cmp_widget(new StoryChangePresent(_graph_base.storyGraph(), _graph_old.storyGraph())) {
auto mbar = menuBar();
auto file = mbar->addMenu(u8"文件");
file->addAction(u8"另存为", [=]() {
auto target = QFileDialog::getExistingDirectory(this, "获取目标文件夹", QDir::homePath());
if (target == u8"")
return;
this->_graph_base.output(QDir(target));
});
this->setCentralWidget(_cmp_widget);
}

View File

@ -0,0 +1,93 @@
#pragma once
#include <QWidget>
#include <QToolBox>
#include <QSplitter>
#include <QTableView>
#include <QStandardItemModel>
#include <QListView>
#include <QComboBox>
#include <QPushButton>
#include <QStyledItemDelegate>
#include <QMainWindow>
#include "xast_parse.h"
#include "storyline_compare.h"
namespace views {
class StorylineModel : public QAbstractItemModel {
private:
QList<std::shared_ptr<xast_parse::IElementSlice>> _nodes_temp;
std::shared_ptr<xast_parse::IElementSlice> _current_slice = nullptr;
public:
void loadCurrentSlice(std::shared_ptr<xast_parse::IElementSlice> target_slice);
std::shared_ptr<xast_parse::IElementSlice> currentSlice();
// Inherited via QAbstractItemModel
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& child) const override;
virtual int rowCount(const QModelIndex& parent) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual Qt::ItemFlags flags(const QModelIndex& idx) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
};
class StorylineDelegate : public QStyledItemDelegate {
private:
QWidget* const bind_view;
public:
explicit StorylineDelegate(QAbstractItemView* pwidget);
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
virtual void setEditorData(QWidget* editor, const QModelIndex& index) const override;
virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};
class StoryChangePresent : public QWidget {
public:
StoryChangePresent(
const QHash<QString, std::shared_ptr<xast_parse::StoryDefine>>& g_base,
const QHash<QString, std::shared_ptr<xast_parse::StoryDefine>>& g_old,
QWidget* parent = nullptr);
compare::Compare& compareTool();
private:
compare::Compare base_compare;
QHash<QString, std::shared_ptr<xast_parse::StoryDefine>> g_old;
QToolBox* const _type_appoint;
QTableView* const _fragment_summary; // 本节点修改汇总
QTableView* const _upstream_summary; // 上游节点修改汇总
QStandardItemModel* const _change_model;
QComboBox* const _story_appoint;
QSplitter* const _edit_splitter;
QListView* const _define_view;
QListView* const _refer_view;
StorylineModel* const _defn_fragment;
StorylineModel* const _curr_refslice;
void type_change(int idx);
void fragment_selected(const QModelIndex& item);
void storyline_selected(const QString& line);
};
class CompareWindow : public QMainWindow {
private:
xast_parse::XAST_Parser _graph_base, _graph_old;
StoryChangePresent *const _cmp_widget;
public:
CompareWindow(QFileInfo file_base, QFileInfo file_old);
};
}

605
StoryPresent/xast_parse.cpp Normal file
View File

@ -0,0 +1,605 @@
#include "xast_parse.h"
using namespace xast_parse;
using namespace std;
__SiblingImpl::__SiblingImpl(SliceType t, shared_ptr<IElementSlice> pnode)
: t_store(t), parent_store(pnode) { }
SliceType __SiblingImpl::type() const {
return t_store;
}
weak_ptr<IElementSlice> __SiblingImpl::parentSlice() const {
return this->parent_store;
}
void __SiblingImpl::setPrev(shared_ptr<IElementSlice> inst) {
this->prev_store = inst;
}
weak_ptr<IElementSlice> __SiblingImpl::prevSlice() const {
return this->prev_store;
}
void __SiblingImpl::setNext(shared_ptr<IElementSlice> next) {
if (this->next_store) {
this->next_store->setPrev(next);
next->setNext(this->next_store);
}
next->setPrev(this->shared_from_this());
this->next_store = next;
}
shared_ptr<IElementSlice> __SiblingImpl::nextSlice() const {
return this->next_store;
}
uint __SiblingImpl::index() const {
uint v_index = 0;
auto temp_node = this->prevSlice();
while (temp_node.lock()) {
v_index++;
temp_node = temp_node.lock()->prevSlice();
}
return v_index;
}
TextParagraph::TextParagraph(shared_ptr<IElementSlice> parent)
: __SiblingImpl(SliceType::TextPragraph, parent) { }
void TextParagraph::setLines(const QString& contents) {
this->lines = contents.split('\n');
}
QString TextParagraph::getLines() const {
return lines.join('\n');
}
void TextParagraph::addLine(const QString& line) {
this->lines << line;
}
QList<QString> TextParagraph::nameSet() const {
return parentSlice().lock()->nameSet() << "$$";
}
StoryDefine::StoryDefine(const QString& name, uint sort, const QString& path)
:__CollectionElement(SliceType::StoryDefines, nullptr),
name_store(name), sort_store(sort), file_path(path) { }
QString StoryDefine::name() const {
return this->name_store;
}
QString xast_parse::StoryDefine::bindPath() const
{
return file_path;
}
shared_ptr<IElementSlice> StoryDefine::getFragment(const QString& name) {
auto temp_node = this->firstChild();
while (temp_node) {
if (temp_node->type() == SliceType::FragmentDefines) {
if (dynamic_pointer_cast<FragmentDefine>(temp_node)->name() == name)
return temp_node;
}
temp_node = temp_node->nextSlice();
}
return nullptr;
}
QList<QString> StoryDefine::nameSet() const {
return QList<QString>() << name();
}
uint StoryDefine::index() const {
return this->sort_store;
}
FragmentDefine::FragmentDefine(const QString& name, shared_ptr<StoryDefine> pnode)
: __DesElement(SliceType::FragmentDefines, static_pointer_cast<IElementSlice>(pnode)), name_store(name) { }
QString FragmentDefine::name() const {
return name_store;
}
QList<shared_ptr<IElementSlice>> FragmentDefine::referSlices() const {
return this->refs_list;
}
void FragmentDefine::appendRefer(shared_ptr<IElementSlice> slice) {
if (this->refs_list.contains(slice))
return;
this->refs_list << slice;
}
QList<QString> FragmentDefine::nameSet() const {
return parentSlice().lock()->nameSet() << name();
}
__CollectionElement::__CollectionElement(SliceType t, shared_ptr<IElementSlice> pnode) :__SiblingImpl(t, pnode) { }
shared_ptr<IElementSlice> __CollectionElement::firstChild() const {
return this->first_head;
}
void __CollectionElement::setFirstChild(shared_ptr<IElementSlice> inst) {
this->first_head = inst;
}
__DesElement::__DesElement(SliceType t, shared_ptr<IElementSlice> pnode)
: __SiblingImpl(t, pnode) { }
shared_ptr<TextParagraph> __DesElement::getTextNode() const {
if (!text_store)
const_cast<__DesElement*>(this)->text_store = make_shared<TextParagraph>(
const_pointer_cast<__SiblingImpl>(this->shared_from_this()));
return text_store;
}
FragmentRefer::FragmentRefer(const QString& story, const QString& fragm, shared_ptr<IElementSlice> pnode)
: __DesElement(SliceType::FragmentRefers, pnode), story_refers(story), fragment_refers(fragm) { }
QString FragmentRefer::storyRefer() const {
return this->story_refers;
}
QString FragmentRefer::fragmentRefer() const {
return this->fragment_refers;
}
weak_ptr<FragmentDefine> FragmentRefer::referTarget() const {
return this->refer_targets;
}
void FragmentRefer::setReferTowards(shared_ptr<FragmentDefine> target) {
this->refer_targets = target;
}
QList<QString> FragmentRefer::nameSet() const {
return parentSlice().lock()->nameSet() << "@" + storyRefer() << fragmentRefer();
}
ArticleDefine::ArticleDefine(const QString& name, shared_ptr<IElementSlice> pnode)
: __CollectionElement(SliceType::ArticleDefines, pnode), name_store(name) { }
QString ArticleDefine::name() const {
return name_store;
}
QList<QString> ArticleDefine::nameSet() const {
return parentSlice().lock()->nameSet() << name();
}
VolumeDefine::VolumeDefine(const QString& name, const QString& path)
: __CollectionElement(SliceType::VolumeDefines, nullptr), name_store(name), file_path(path) { }
QString VolumeDefine::name() const {
return name_store;
}
QString VolumeDefine::bindPath() const {
return file_path;
}
QList<QString> VolumeDefine::getArticleNames() const {
QList<QString> names;
auto first_elm = this->firstChild();
while (first_elm) {
if (first_elm->type() == SliceType::ArticleDefines) {
names << static_pointer_cast<ArticleDefine>(first_elm)->name();
}
first_elm = first_elm->nextSlice();
}
return names;
}
shared_ptr<ArticleDefine> VolumeDefine::getArticleWith(const QString& name) const {
auto temp_elm = this->firstChild();
while (temp_elm) {
if (temp_elm->type() == SliceType::ArticleDefines) {
auto conv_elm = static_pointer_cast<ArticleDefine>(temp_elm);
if (conv_elm->name() == name) {
return conv_elm;
}
}
temp_elm = temp_elm->nextSlice();
}
return nullptr;
}
QList<QString> VolumeDefine::nameSet() const {
return QList<QString>() << name();
}
XAST_Parser::XAST_Parser(const QString& ast_path) {
QFile ast_file(ast_path);
if (!ast_file.open(QIODevice::Text | QIODevice::ReadOnly))
throw new exception("无法打开指定文件:" + ast_path.toLocal8Bit());
this->dom_tree.setContent(&ast_file);
auto ranks = this->dom_tree.elementsByTagName(u8"rank");
for (auto idx = 0; idx < ranks.size(); ++idx) {
auto re = ranks.at(idx).toElement();
auto path = re.attribute(u8"doc-path");
auto rankv = re.attribute(u8"rank");
auto inst = std::make_shared<RankDecs>(path, rankv);
this->rank_coll.append(inst);
}
auto nodes = this->dom_tree.elementsByTagName(u8"story");
for (auto idx = 0; idx < nodes.size(); ++idx) {
auto nd = nodes.at(idx);
auto sinst = this->init_story_define(nd.toElement());
story_dict[sinst->name()] = sinst;
}
nodes = this->dom_tree.elementsByTagName(u8"volume");
for (auto idx = 0; idx < nodes.size(); ++idx) {
auto nd = nodes.at(idx);
auto vinst = this->init_volume_define(nd.toElement());
volume_dict[vinst->name()] = vinst;
}
for (auto sit : story_dict.values()) {
this->fragments_plait(story_dict, sit);
}
for (auto vit : volume_dict.values()) {
this->fragments_plait(story_dict, vit);
}
ast_file.close();
}
QList<shared_ptr<RankDecs>> XAST_Parser::rankList() const {
return rank_coll;
}
QHash<QString, shared_ptr<StoryDefine>> XAST_Parser::storyGraph() const {
return story_dict;
}
QHash<QString, shared_ptr<VolumeDefine>> XAST_Parser::volumeGraph() const {
return volume_dict;
}
void xast_parse::XAST_Parser::output(const QDir& dir) {
for (auto& r : this->rankList()) {
this->write_rank(r, dir);
}
for (auto& s : this->storyGraph()) {
this->write_story(s, dir);
}
for (auto& v : this->volumeGraph()) {
this->write_volume(v, dir);
}
}
shared_ptr<StoryDefine> XAST_Parser::init_story_define(const QDomElement& story_e) {
auto s_name = story_e.attribute(u8"name");
auto s_sort = story_e.attribute(u8"sort").toUInt();
auto s_path = story_e.attribute(u8"file-path");
auto inst = make_shared<StoryDefine>(s_name, s_sort, s_path);
QList<QDomElement> elms_list;
auto temp_elm = story_e.firstChildElement();
while (!temp_elm.isNull()) {
if (temp_elm.tagName() != u8"tokens")
elms_list << temp_elm;
temp_elm = temp_elm.nextSiblingElement();
}
shared_ptr<IElementSlice> temporary_ptr = nullptr;
while (elms_list.size()) {
shared_ptr<IElementSlice> current_ptr = nullptr;
auto text_count = this->text_sections_count(elms_list);
if (text_count) {
current_ptr = this->text_paragraph_build(inst, elms_list, text_count);
}
else if (elms_list.at(0).tagName() == u8"fragment") {
current_ptr = this->fragment_define_build(inst, elms_list.at(0));
elms_list.takeFirst();
}
else if (elms_list.at(0).tagName() == u8"refer") {
current_ptr = this->fragment_refer_build(inst, elms_list.at(0));
elms_list.takeFirst();
}
if (!temporary_ptr) {
inst->setFirstChild(current_ptr);
}
else {
temporary_ptr->setNext(current_ptr);
}
temporary_ptr = current_ptr;
}
return inst;
}
uint XAST_Parser::text_sections_count(const QList<QDomElement>& elms) {
uint count = 0;
for (auto enode : elms) {
if (enode.tagName() != u8"text-section")
break;
count++;
}
return count;
}
shared_ptr<TextParagraph> XAST_Parser::text_paragraph_build(shared_ptr<__CollectionElement> pnode, QList<QDomElement>& elms, uint count) {
auto inst = make_shared<TextParagraph>(pnode);
for (uint idx = 0; idx < count; ++idx) {
auto telm = elms.takeAt(0);
auto line = telm.attribute(u8"text");
inst->addLine(line);
}
return inst;
}
shared_ptr<FragmentDefine> XAST_Parser::fragment_define_build(shared_ptr<StoryDefine> pnode, const QDomElement& e) {
auto fname = e.attribute(u8"name");
auto fragm = make_shared<FragmentDefine>(fname, pnode);
auto temp_elm = e.firstChildElement();
while (!temp_elm.isNull()) {
if (temp_elm.tagName() == u8"text-section") {
auto line = temp_elm.attribute(u8"text");
fragm->getTextNode()->addLine(line);
}
temp_elm = temp_elm.nextSiblingElement();
}
return fragm;
}
shared_ptr<FragmentRefer> XAST_Parser::fragment_refer_build(shared_ptr<__CollectionElement> pnode, const QDomElement& e) {
auto story_ref = e.attribute(u8"story");
auto fragm_ref = e.attribute(u8"fragment");
auto fragmemt_refi = make_shared<FragmentRefer>(story_ref, fragm_ref, pnode);
auto temp_node = e.firstChildElement();
while (!temp_node.isNull()) {
if (temp_node.tagName() == u8"text-section") {
auto line = temp_node.attribute(u8"text");
fragmemt_refi->getTextNode()->addLine(line);
}
temp_node = temp_node.nextSiblingElement();
}
return fragmemt_refi;
}
void XAST_Parser::fragments_plait(QHash<QString, shared_ptr<StoryDefine>> story_map, shared_ptr<ICollection> reflist) {
auto slice = reflist->firstChild();
while (slice) {
if (slice->type() == SliceType::FragmentRefers) {
auto refer_inst = static_pointer_cast<FragmentRefer>(slice);
auto story_target = story_map[refer_inst->storyRefer()];
auto fragm_target = story_target->getFragment(refer_inst->fragmentRefer());
auto fragm_cast = static_pointer_cast<FragmentDefine>(fragm_target);
fragm_cast->appendRefer(refer_inst);
refer_inst->setReferTowards(fragm_cast);
}
switch (slice->type()) {
case SliceType::StoryDefines:
case SliceType::VolumeDefines:
case SliceType::ArticleDefines:
this->fragments_plait(story_map, static_pointer_cast<__CollectionElement>(slice));
break;
default:
break;
}
slice = slice->nextSlice();
}
}
shared_ptr<VolumeDefine> XAST_Parser::init_volume_define(const QDomElement& volume_e) {
auto v_name = volume_e.attribute(u8"name");
auto v_path = volume_e.attribute(u8"file-path");
auto inst = make_shared<VolumeDefine>(v_name, v_path);
QList<QDomElement> node_list;
auto temp_node = volume_e.firstChildElement();
while (!temp_node.isNull()) {
if (temp_node.tagName() != u8"tokens")
node_list << temp_node;
temp_node = temp_node.nextSiblingElement();
}
shared_ptr<IElementSlice> temporary_ptr = nullptr;
while (node_list.size()) {
shared_ptr<IElementSlice> current_ptr = nullptr;
auto text_count = this->text_sections_count(node_list);
if (text_count) {
current_ptr = this->text_paragraph_build(inst, node_list, text_count);
}
else if (node_list.at(0).tagName() == u8"article") {
current_ptr = this->init_article_define(inst, node_list.at(0));
node_list.takeFirst();
}
if (!temporary_ptr) {
inst->setFirstChild(current_ptr);
}
else {
temporary_ptr->setNext(current_ptr);
}
temporary_ptr = current_ptr;
}
return inst;
}
shared_ptr<ArticleDefine> XAST_Parser::init_article_define(shared_ptr<VolumeDefine> pnode, const QDomElement& article_e) {
auto a_name = article_e.attribute(u8"name");
auto inst = make_shared<ArticleDefine>(a_name, pnode);
QList<QDomElement> node_list;
auto temp_node = article_e.firstChildElement();
while (!temp_node.isNull()) {
if (temp_node.tagName() != u8"tokens")
node_list << temp_node;
temp_node = temp_node.nextSiblingElement();
}
shared_ptr<IElementSlice> temporary_ptr = nullptr;
while (node_list.size()) {
shared_ptr<IElementSlice> current_ptr = nullptr;
auto text_count = this->text_sections_count(node_list);
if (text_count) {
current_ptr = this->text_paragraph_build(inst, node_list, text_count);
}
else if (node_list.at(0).tagName() == u8"refer") {
current_ptr = this->fragment_refer_build(inst, node_list.at(0));
node_list.takeFirst();
}
if (!temporary_ptr) {
inst->setFirstChild(current_ptr);
}
else {
temporary_ptr->setNext(current_ptr);
}
temporary_ptr = current_ptr;
}
return inst;
}
#include <QMessageBox>
#include <QCoreApplication>
#include <QTextStream>
void xast_parse::XAST_Parser::write_rank(std::shared_ptr<RankDecs> inst, const QDir& odir) {
auto fname = inst->bindPath();
QFile target_file(odir.filePath(fname));
if (!target_file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(nullptr, "文件操作", odir.filePath(fname) + "无法打开!");
QCoreApplication::exit(0);
return;
}
QTextStream o(&target_file);
o.setCodec("UTF-8");
o << QString(u8"#排序 ") << inst->rank() << endl;
}
void xast_parse::XAST_Parser::write_story(std::shared_ptr<StoryDefine> inst, const QDir& odir)
{
auto fname = inst->bindPath();
QFile target_file(odir.filePath(fname));
if (!target_file.open(QIODevice::Append | QIODevice::Text)) {
QMessageBox::critical(nullptr, "文件操作", odir.filePath(fname) + "无法打开!");
QCoreApplication::exit(0);
return;
}
QTextStream o(&target_file);
o.setCodec("UTF-8");
o << QString(u8"{故事 ") << inst->name() << "\n";
auto temp = inst->firstChild();
while (temp) {
switch (temp->type()) {
case SliceType::TextPragraph:
this->write_text(1, std::dynamic_pointer_cast<TextParagraph>(temp), o);
break;
case SliceType::FragmentDefines:
this->write_fragmdef(1, std::dynamic_pointer_cast<FragmentDefine>(temp), o);
break;
case SliceType::FragmentRefers:
this->write_fragmref(1, std::dynamic_pointer_cast<FragmentRefer>(temp), o);
break;
default:
break;
}
temp = temp->nextSlice();
}
o << QString(u8"}\n");
}
void xast_parse::XAST_Parser::write_text(int dep, std::shared_ptr<TextParagraph> inst, QTextStream& out)
{
auto text = inst->getLines();
text.replace("\n", "\n" + QString(dep * 2, ' '));
out << QString(dep * 2, ' ') << text << "\n";
}
void xast_parse::XAST_Parser::write_fragmdef(int dep, std::shared_ptr<FragmentDefine> inst, QTextStream& out)
{
out << QString(dep * 2, ' ') << QString(u8"{情节 %1\n").arg(inst->name());
this->write_text(dep + 1, inst->getTextNode(), out);
out << QString(dep * 2, ' ') << QString(u8"}\n");
}
void xast_parse::XAST_Parser::write_fragmref(int dep, std::shared_ptr<FragmentRefer> inst, QTextStream& out)
{
out << QString(dep * 2, ' ') << QString(u8"{@情节 %1&%2\n").arg(inst->fragmentRefer(), inst->storyRefer());
this->write_text(dep + 1, inst->getTextNode(), out);
out << QString(dep * 2, ' ') << QString(u8"}\n");
}
void xast_parse::XAST_Parser::write_volume(std::shared_ptr<VolumeDefine> inst, const QDir& odir)
{
auto fname = inst->bindPath();
QFile target_file(odir.filePath(fname));
if (!target_file.open(QIODevice::Append | QIODevice::Text)) {
QMessageBox::critical(nullptr, "文件操作", odir.filePath(fname) + "无法打开!");
QCoreApplication::exit(0);
return;
}
QTextStream o(&target_file);
o.setCodec("UTF-8");
o << QString(u8"{分卷 %1\n").arg(inst->name());
auto temp = inst->firstChild();
while (temp) {
switch (temp->type()) {
case SliceType::TextPragraph:
this->write_text(1, std::dynamic_pointer_cast<TextParagraph>(temp), o);
break;
case SliceType::ArticleDefines:
this->write_article(1, std::dynamic_pointer_cast<ArticleDefine>(temp), o);
break;
default:
break;
}
temp = temp->nextSlice();
}
o << QString(u8"}\n");
}
void xast_parse::XAST_Parser::write_article(int dep, std::shared_ptr<ArticleDefine> inst, QTextStream& out)
{
out << QString(u8"%1{章节 %2\n").arg(QString(dep * 2, ' '), inst->name());
auto temp = inst->firstChild();
while (temp) {
switch (temp->type()) {
case SliceType::TextPragraph:
this->write_text(dep + 1, std::dynamic_pointer_cast<TextParagraph>(temp), out);
break;
case SliceType::FragmentRefers:
this->write_fragmref(dep + 1, std::dynamic_pointer_cast<FragmentRefer>(temp), out);
break;
default:
break;
}
temp = temp->nextSlice();
}
out << QString(u8"%1}\n").arg(QString(dep * 2, ' '));
}
RankDecs::RankDecs(const QString& path, const QString& rank)
:fpath(path), rank_v(rank) { }
QString RankDecs::bindPath() const {
return fpath;
}
QString RankDecs::rank() const {
return rank_v;
}

256
StoryPresent/xast_parse.h Normal file
View File

@ -0,0 +1,256 @@
#pragma once
#include <QString>
#include <QStringList>
#include <QDomDocument>
#include <QHash>
#include <QDir>
#include <QFile>
#include <memory>
#include <exception>
namespace xast_parse {
enum class SliceType {
StoryDefines, // 故事线定义
TextPragraph, // 文字段落
FragmentDefines, // 情节定义
FragmentRefers, // 情节引用
VolumeDefines, // 卷宗定义
ArticleDefines, // 章节定义
};
/// <summary>
/// 故事节点切片
/// </summary>
class IElementSlice {
public:
virtual SliceType type() const = 0;
virtual std::weak_ptr<IElementSlice> parentSlice() const = 0;
virtual QList<QString> nameSet() const = 0;
virtual void setPrev(std::shared_ptr<IElementSlice> inst) = 0;
virtual std::weak_ptr<IElementSlice> prevSlice() const = 0;
virtual void setNext(std::shared_ptr<IElementSlice> next) = 0;
virtual std::shared_ptr<IElementSlice> nextSlice() const = 0;
virtual uint index() const = 0;
};
/// <summary>
/// 可以包含子节点的故事节点
/// </summary>
class ICollection {
public:
virtual std::shared_ptr<IElementSlice> firstChild() const = 0;
virtual void setFirstChild(std::shared_ptr<IElementSlice> inst) = 0;
};
class __SiblingImpl : public IElementSlice, public std::enable_shared_from_this<__SiblingImpl> {
private:
SliceType t_store;
std::weak_ptr<IElementSlice> parent_store;
std::weak_ptr<IElementSlice> prev_store;
std::shared_ptr<IElementSlice> next_store;
public:
__SiblingImpl(SliceType t, std::shared_ptr<IElementSlice> pnode);
virtual SliceType type() const;
virtual std::weak_ptr<IElementSlice> parentSlice() const;
virtual void setPrev(std::shared_ptr<IElementSlice> inst);
virtual std::weak_ptr<IElementSlice> prevSlice() const;
virtual void setNext(std::shared_ptr<IElementSlice> next);
virtual std::shared_ptr<IElementSlice> nextSlice() const;
virtual uint index() const;
};
class __CollectionElement : public __SiblingImpl, public ICollection {
private:
std::shared_ptr<IElementSlice> first_head;
public:
__CollectionElement(SliceType t, std::shared_ptr<IElementSlice> pnode);
// 通过 ICollection 继承
std::shared_ptr<IElementSlice> firstChild() const override;
void setFirstChild(std::shared_ptr<IElementSlice> inst) override;
};
/// <summary>
/// 连续文本段落
/// </summary>
class TextParagraph : public __SiblingImpl {
private:
QStringList lines;
public:
TextParagraph(std::shared_ptr<IElementSlice> parent);
virtual ~TextParagraph() = default;
virtual void setLines(const QString& contents);
virtual QString getLines() const;
virtual void addLine(const QString& line);
// IElementSlice
virtual QList<QString> nameSet() const override;
};
class __DesElement : public __SiblingImpl {
private:
std::shared_ptr<TextParagraph> text_store;
public:
__DesElement(SliceType t, std::shared_ptr<IElementSlice> pnode);
virtual std::shared_ptr<TextParagraph> getTextNode() const;
};
/// <summary>
/// 故事定义节点
/// </summary>
class StoryDefine : public __CollectionElement {
private:
QString name_store, file_path;
uint sort_store;
public:
StoryDefine(const QString& name, uint sort, const QString &path);
virtual ~StoryDefine() = default;
virtual QString name() const;
virtual QString bindPath() const;
virtual uint index() const;
virtual std::shared_ptr<IElementSlice> getFragment(const QString& name);
// IElementSlice
virtual QList<QString> nameSet() const override;
};
/// <summary>
/// 情节定义节点
/// </summary>
class FragmentDefine : public __DesElement {
private:
QString name_store;
QList<std::shared_ptr<IElementSlice>> refs_list;
public:
FragmentDefine(const QString& name, std::shared_ptr<StoryDefine> pnode);
virtual ~FragmentDefine() = default;
virtual QString name() const;
virtual QList<std::shared_ptr<IElementSlice>> referSlices() const;
virtual void appendRefer(std::shared_ptr<IElementSlice> slice);
// IElementSlice
virtual QList<QString> nameSet() const override;
};
/// <summary>
/// 情节引用节点
/// </summary>
class FragmentRefer : public __DesElement {
private:
QString story_refers, fragment_refers;
std::weak_ptr<FragmentDefine> refer_targets;
public:
FragmentRefer(const QString& story, const QString& fragm, std::shared_ptr<IElementSlice> pnode);
virtual ~FragmentRefer() = default;
virtual QString storyRefer() const;
virtual QString fragmentRefer() const;
virtual std::weak_ptr<FragmentDefine> referTarget() const;
virtual void setReferTowards(std::shared_ptr<FragmentDefine> target);
// IElementSlice
virtual QList<QString> nameSet() const override;
};
/// <summary>
/// 章节定义节点
/// </summary>
class ArticleDefine : public __CollectionElement {
private:
QString name_store;
public:
ArticleDefine(const QString& name, std::shared_ptr<IElementSlice> pnode);
virtual ~ArticleDefine() = default;
virtual QString name() const;
// IElementSlice
virtual QList<QString> nameSet() const override;
};
/// <summary>
/// 卷宗定义节点
/// </summary>
class VolumeDefine : public __CollectionElement {
private:
QString name_store, file_path;
public:
VolumeDefine(const QString &name, const QString &path);
virtual ~VolumeDefine() = default;
virtual QString name() const;
virtual QString bindPath() const;
virtual QList<QString> getArticleNames() const;
virtual std::shared_ptr<ArticleDefine> getArticleWith(const QString& name) const;
// IElementSlice
virtual QList<QString> nameSet() const override;
};
class RankDecs {
private:
QString fpath, rank_v;
public:
RankDecs(const QString &path, const QString &rank);
QString bindPath() const;
QString rank() const;
};
class XAST_Parser {
private:
QDomDocument dom_tree;
QList<std::shared_ptr<RankDecs>> rank_coll;
QHash<QString, std::shared_ptr<StoryDefine>> story_dict;
QHash<QString, std::shared_ptr<VolumeDefine>> volume_dict;
public:
XAST_Parser(const QString& ast_path);
QList<std::shared_ptr<RankDecs>> rankList() const;
QHash<QString, std::shared_ptr<StoryDefine>> storyGraph() const;
QHash<QString, std::shared_ptr<VolumeDefine>> volumeGraph() const;
void output(const QDir &dir);
private:
std::shared_ptr<StoryDefine> init_story_define(const QDomElement& story_e);
uint text_sections_count(const QList<QDomElement>& elms);
std::shared_ptr<TextParagraph> text_paragraph_build(std::shared_ptr<__CollectionElement> pnode, QList<QDomElement>& elms, uint count);
std::shared_ptr<FragmentDefine> fragment_define_build(std::shared_ptr<StoryDefine> pnode, const QDomElement& e);
std::shared_ptr<FragmentRefer> fragment_refer_build(std::shared_ptr<__CollectionElement> pnode, const QDomElement& e);
void fragments_plait(QHash<QString, std::shared_ptr<StoryDefine>> story_map, std::shared_ptr<ICollection> reflist);
std::shared_ptr<VolumeDefine> init_volume_define(const QDomElement& volume_e);
std::shared_ptr<ArticleDefine> init_article_define(std::shared_ptr<VolumeDefine> pnode, const QDomElement& article_e);
void write_rank(std::shared_ptr<RankDecs> inst, const QDir &odir);
void write_story(std::shared_ptr<StoryDefine> inst, const QDir &odir);
void write_text(int dep, std::shared_ptr<TextParagraph> inst, QTextStream &out);
void write_fragmdef(int dep, std::shared_ptr<FragmentDefine> inst, QTextStream& out);
void write_fragmref(int dep, std::shared_ptr<FragmentRefer> inst, QTextStream& out);
void write_volume(std::shared_ptr<VolumeDefine> inst, const QDir &odir);
void write_article(int dep, std::shared_ptr<ArticleDefine> inst, QTextStream &out);
};
}

View File

@ -31,6 +31,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ArgsParser", "ArgsParser\ArgsParser.vcxproj", "{1FF80476-26C9-42FB-BFF6-D587C4941964}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StoryPresent", "StoryPresent\StoryPresent.vcxproj", "{48DA8516-26EA-4D59-8913-7EF28E3F87C3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -57,6 +59,10 @@ Global
{1FF80476-26C9-42FB-BFF6-D587C4941964}.Debug|x64.Build.0 = Debug|x64
{1FF80476-26C9-42FB-BFF6-D587C4941964}.Release|x64.ActiveCfg = Release|x64
{1FF80476-26C9-42FB-BFF6-D587C4941964}.Release|x64.Build.0 = Release|x64
{48DA8516-26EA-4D59-8913-7EF28E3F87C3}.Debug|x64.ActiveCfg = Debug|x64
{48DA8516-26EA-4D59-8913-7EF28E3F87C3}.Debug|x64.Build.0 = Debug|x64
{48DA8516-26EA-4D59-8913-7EF28E3F87C3}.Release|x64.ActiveCfg = Release|x64
{48DA8516-26EA-4D59-8913-7EF28E3F87C3}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE