add-proj
This commit is contained in:
parent
3547a2c84a
commit
c6d3483d1e
|
|
@ -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>>());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#include "storypresent.h"
|
||||
|
||||
StoryPresent::StoryPresent(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
{
|
||||
}
|
||||
|
||||
StoryPresent::~StoryPresent()
|
||||
{}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets/QMainWindow>
|
||||
|
||||
class StoryPresent : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StoryPresent(QWidget* parent = nullptr);
|
||||
~StoryPresent();
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<RCC>
|
||||
<qresource prefix="StoryPresent">
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue