#include "mainwindow.h" #include "srcedit_defaulttext.h" #include "srcedit_storyboard.h" #include "srcedit_storychain.h" #include "srcedit_storyunit.h" #include "srcedit_storyvolume.h" #include "xapp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "command_list.h" using namespace Project; using namespace MakeTools; using namespace Parse::Result; using namespace Components; using namespace Core; using namespace Tools; using namespace CommandList; using namespace DataModel; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), app_core(new AppCore(this)), sync_kernel(new StatusSyncCore(this)), split_first(new QSplitter(Qt::Horizontal, this)), split_second(new QSplitter(Qt::Vertical, this)), split_third(new QSplitter(Qt::Horizontal, this)), project_manager(new XMLProjectManager(this)), left_funcs(new PresentContainer(QTabWidget::West, this)), right_funcs(new PresentContainer(QTabWidget::West, this)), center_funcs(new PresentContainer(QTabWidget::North, this)), bottom_funcs(new PresentContainer(QTabWidget::North, this)), docs_container(new DocumentsManager(app_core, project_manager, this)), fragments_model(new FragmentsOrderviewModel(app_core)), boards_model(new StoryboardsPresentModel(app_core)), chains_model(new StorychainsPresentModel(app_core)), concepts_model(new StoryconceptsPresentModel(app_core)), units_model(new StoryunitsPresentModel(app_core)), welcome_list(new WelcomePanel()), errors_present(new MessagesPresent(app_core->getMakeCore(), this)), project_present(new ProjectPresent(XApp::disp_core, docs_container, this)), fragments_order(new FragmentsOrderPresent(XApp::disp_core, fragments_model, this)), boards_view(new StoryboardsPresent(XApp::disp_core, boards_model, this)), chains_view(new StorychainsPresent(XApp::disp_core, chains_model, this)), concept_view(new StoryconceptsPresent(XApp::disp_core, concepts_model, this)), units_view(new StoryunitsPresent(XApp::disp_core, units_model, this)) { QApplication::instance()->installEventFilter(this); initial_commandlist(XApp::disp_core); XApp::disp_core->registerMember(this->app_core); this->app_core->setCurrentProject(project_manager); XApp::disp_core->registerMember(docs_container); XApp::disp_core->registerMember(fragments_model); XApp::disp_core->registerMember(boards_model); XApp::disp_core->registerMember(chains_model); XApp::disp_core->registerMember(concepts_model); XApp::disp_core->registerMember(units_model); setMinimumSize(1000, 600); setWindowTitle("提线木偶"); project_present->widget()->setVisible(false); chains_view->setVisible(false); units_view->setVisible(false); errors_present->setVisible(false); boards_view->setVisible(false); concept_view->setVisible(false); fragments_order->setVisible(false); auto mbar = menuBar(); initial_menubar(mbar); setCentralWidget(this->split_first); this->split_first->addWidget(left_funcs->hostWidget()); this->split_first->addWidget(this->split_second); this->split_second->addWidget(this->split_third); this->split_second->addWidget(this->bottom_funcs->hostWidget()); this->split_third->addWidget(this->center_funcs->hostWidget()); this->split_third->addWidget(this->right_funcs->hostWidget()); connect(static_cast(left_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport); connect(static_cast(right_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport); connect(static_cast(bottom_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport); connect(static_cast(center_funcs), &PresentContainer::presentTransTo, this, &MainWindow::accept_view_transport); } MainWindow::~MainWindow() {} void MainWindow::initial_menubar(QMenuBar *mbar) { // 项目菜单树 auto project = mbar->addMenu("项目"); auto opnp = project->addAction("打开项目", [this]() { auto file = QFileDialog::getOpenFileName(this, "打开项目", QString(), "小说项目(*.nsf)"); if (file == "") return; XApp::disp_core->postCommand(OpenProject(file)); build_internal(true); }); sync_kernel->actionEnableSync(opnp, [this]() -> bool { return !project_manager->isOpenning(); }); auto newp = project->addAction("新建项目", [this]() { auto name = QInputDialog::getText(this, "输入项目名称", "项目名称"); if (name == "") return; auto dir_path = QFileDialog::getExistingDirectory(this, "指定项目存储路径"); if (dir_path == "") return; XApp::disp_core->postCommand(NewProject(QDir(dir_path), name)); }); sync_kernel->actionEnableSync(newp, [this]() -> bool { return !project_manager->isOpenning(); }); auto clsp = project->addAction("关闭项目", [this]() { XApp::disp_core->postCommand(CloseProject()); this->refresh_views(); }); sync_kernel->actionEnableSync(clsp, [this]() -> bool { return project_manager->isOpenning(); }); project->addSeparator(); auto pnew = project->addAction("新建路径", [this]() { auto packages = QInputDialog::getText(this, "输入包路径名称/PackA/PackB/PackC", "包路径:"); if (packages != "") XApp::disp_core->postCommand(NewPackage(packages)); }); sync_kernel->actionEnableSync(pnew, [this]() -> bool { return project_manager->isOpenning(); }); auto _xnew = project->addMenu("新建文件"); _xnew->setEnabled(project_manager->isOpenning()); auto types = docs_container->fileTypes(); for (auto &t : types) { _xnew->addAction(t, [this, &t]() { auto name = QInputDialog::getText(this, "输入名称", "名称:"); if (name == "") return; auto idx = project_present->currentIndex(); if (!idx.isValid()) idx = project_manager->model()->item(0)->index(); auto group_path = docs_container->converter(idx); XApp::disp_core->postCommand(NewFile(group_path, name, t)); }); } sync_kernel->widgetEnableSync(_xnew, [this]() -> bool { return project_manager->isOpenning(); }); project->addSeparator(); auto sav = project->addAction("保存全部", [this]() { XApp::disp_core->postCommand(SaveAll()); }); sav->setShortcut(QKeySequence::StandardKey::Save); sync_kernel->actionEnableSync(sav, [this]() -> bool { return project_manager->isOpenning(); }); project->addSeparator(); project->addAction("退出", [this]() { XApp::disp_core->postCommand(SaveAll()); QApplication::exit(0); }); // 编辑菜单 auto edit = mbar->addMenu("编辑"); edit->addAction("撤销一步"); edit->addAction("重做一步"); edit->addSeparator(); edit->addAction("查找关键词"); edit->addAction("替换关键词"); // 视图菜单 auto view = mbar->addMenu("视图"); auto area = view->addMenu("区域管理"); auto xtool = new QToolBar(this); xtool->setVisible(false); addToolBar(Qt::ToolBarArea::TopToolBarArea, xtool); auto act_tool = area->addAction("工具栏"); sync_kernel->actionCheckSync(act_tool, [xtool]() -> bool { return xtool->isVisible(); }); connect(act_tool, &QAction::triggered, [xtool](bool v) { xtool->setVisible(v); }); auto xstatus = statusBar(); xstatus->setVisible(false); xstatus->addWidget(new QLabel("文本消息", this)); auto act_status = area->addAction("状态栏"); sync_kernel->actionCheckSync(act_status, [xstatus]() -> bool { return xstatus->isVisible(); }); connect(act_status, &QAction::triggered, [xstatus](bool v) { xstatus->setVisible(v); }); auto act_left = area->addAction("左侧功能区"); sync_kernel->actionCheckSync(act_left, [this]() -> bool { return this->left_funcs->visibleState(); }); connect(act_left, &QAction::triggered, [this](bool v) { this->left_funcs->setVisibleState(v); }); auto act_right = area->addAction("右侧功能区"); sync_kernel->actionCheckSync(act_right, [this]() -> bool { return this->right_funcs->visibleState(); }); connect(act_right, &QAction::triggered, [this](bool v) { this->right_funcs->setVisibleState(v); }); auto act_bottom = area->addAction("底部功能区"); sync_kernel->actionCheckSync(act_bottom, [this]() -> bool { return this->bottom_funcs->visibleState(); }); connect(act_bottom, &QAction::triggered, [this](bool v) { this->bottom_funcs->setVisibleState(v); }); view->addSeparator(); auto change = view->addMenu("内容切换"); change->addAction("序列视图"); change->addAction("编辑视图"); view->addSeparator(); auto func = view->addMenu("功能视图"); sync_kernel->widgetEnableSync(func, [this]() { return this->project_manager->isOpenning(); }); // 项目管理 { auto project_v = func->addAction("项目管理", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, project_present, v)); }); sync_kernel->actionCheckSync(project_v, [this]() -> bool { return this->project_present->isVisible(); }); } func->addAction("引用统计"); func->addAction("脉络分蘖"); // 编译信息 { auto msgs = func->addAction("编译输出", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, this->errors_present, true)); XApp::disp_core->postCommand(Build(false)); }); sync_kernel->actionCheckSync(msgs, [this]() -> bool { return bottom_funcs->contains(errors_present); }); } // 脉络统计 { auto chain_v = func->addAction("脉络统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, this->chains_view, v)); }); chain_v->setCheckable(true); sync_kernel->registerTrigger([this]() -> bool { return right_funcs->contains(chains_view); }, [chain_v](bool v) { if (v != chain_v->isChecked()) chain_v->setChecked(v); }); } // 单元统计 { auto unit_v = func->addAction("单元统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, this->units_view, v)); }); unit_v->setCheckable(true); sync_kernel->registerTrigger([this]() -> bool { return right_funcs->contains(units_view); }, [unit_v](bool v) { if (v != unit_v->isChecked()) unit_v->setChecked(v); }); } // 故事统计 { auto board_v = func->addAction("故事统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, boards_view, v)); }); board_v->setCheckable(true); sync_kernel->registerTrigger([this]() -> bool { return right_funcs->contains(boards_view); }, [board_v](bool v) { if (v != board_v->isChecked()) board_v->setChecked(v); }); } // 概念统计 { auto concept_v = func->addAction("概念统计", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, concept_view, v)); }); concept_v->setCheckable(true); sync_kernel->registerTrigger([this]() -> bool { return right_funcs->contains(concept_view); }, [concept_v](bool v) { if (v != concept_v->isChecked()) concept_v->setChecked(v); }); } // 情节序列 { auto fragments_v = func->addAction("情节序列", [this](bool v) { XApp::disp_core->postCommand(CompVisible(this, fragments_order, v)); }); fragments_v->setCheckable(true); sync_kernel->registerTrigger([this]() -> bool { return right_funcs->contains(fragments_order); }, [fragments_v](bool v) { if (v != fragments_v->isChecked()) fragments_v->setChecked(v); }); } // 工具菜单 auto tool = mbar->addMenu("工具"); auto word = tool->addMenu("敏感词检测"); word->addAction("工具配置"); word->addAction("敏感词编辑"); auto vcs = tool->addMenu("版本管理"); vcs->addAction("工具配置"); vcs->addAction("启用"); auto exp = tool->addMenu("导入导出"); exp->addAction("导入WsOutlines"); exp->addSeparator(); exp->addAction("导出TXT大纲"); exp->addAction("导出Web大纲"); exp->addAction("导出WsOutlines"); exp->addSeparator(); exp->addAction("导出TXT内容"); auto build = tool->addAction("编译", [this]() { this->build_internal(); }); sync_kernel->actionEnableSync(build, [this]() -> bool { return project_manager->isOpenning(); }); // 窗口菜单 auto window = mbar->addMenu("窗口"); window->addAction("新建窗口"); window->addAction("关闭窗口"); window->addSeparator(); window->addAction("布局管理"); window->addMenu("布局切换"); window->addMenu("窗口切换"); // 系统 auto sys = mbar->addMenu("系统"); sys->addAction("软件激活"); sys->addAction("系统信息"); sys->addSeparator(); sys->addAction("关于……"); } void MainWindow::initial_commandlist(Schedule::CommandsDispatcher *host) { host->registerCommand(new Build(true)); host->registerCommand(new CloseProject()); host->registerCommand(new CompVisible(this, welcome_list, true)); host->registerCommand(new CompVisible(this, errors_present, true)); host->registerCommand(new CompVisible(this, project_present, true)); host->registerCommand(new CompVisible(this, fragments_order, true)); host->registerCommand(new CompVisible(this, boards_view, true)); host->registerCommand(new CompVisible(this, chains_view, true)); host->registerCommand(new CompVisible(this, concept_view, true)); host->registerCommand(new CompVisible(this, units_view, true)); host->registerCommand(new NewFile(Route(), "", "")); host->registerCommand(new NewPackage("")); host->registerCommand(new NewProject(QDir(), "")); host->registerCommand(new OpenFile(Route())); host->registerCommand(new OpenProject("")); host->registerCommand(new SaveAll()); host->registerCommand(new StoryboardDetailShow(QList())); host->registerCommand(new StoryboardJumpTo(QList())); host->registerCommand(new StorychainDetailShow(QList())); host->registerCommand(new StorychainJumpTo(QList())); host->registerCommand(new StoryconceptDetailShow(QList())); host->registerCommand(new StoryconceptJumpTo(QList())); host->registerCommand(new StoryunitDetailShow(QList())); host->registerCommand(new StoryunitJumpTo(QList())); } void MainWindow::refresh_views() { errors_present->refresh(); chains_model->refreshTree(); units_model->refreshTree(); boards_model->refresh(); concepts_model->refreshTree(); fragments_model->refreshTable(); } void MainWindow::build_internal(bool all_from_disk) { XApp::disp_core->postCommand(Build(all_from_disk)); refresh_views(); } void MainWindow::splitter_layout_save(const QList &path_type, const QList &size) { auto convert = [](QList sizes) -> QStringList { QStringList size_strs; for (auto i : sizes) size_strs << QString("%1").arg(i); return size_strs; }; app_core->globalConfig()->setList(path_type, convert(size)); } #define LEFT_SIDE "left" #define RIGHT_SIDE "right" #define BOTTOM_SIDE "bottom" #define CENTER_SIDE "center" void MainWindow::append(PresentBase *ins, const Route &unique) { auto cfg = project_manager->configraions(); auto pos_query = QList() << "unique-record" << "split-area" << "index-hash" << "store-point"; auto hash_pos = cfg->getMap(pos_query); auto unique_key = unique.links().join("/"); auto pos = hash_pos[unique_key]; if (pos == "") { pos = CENTER_SIDE; hash_pos[unique_key] = pos; cfg->setMap(pos_query, hash_pos); } if (pos == LEFT_SIDE) { left_funcs->append(ins, unique); } else if (pos == RIGHT_SIDE) { right_funcs->append(ins, unique); } else if (pos == BOTTOM_SIDE) { bottom_funcs->append(ins, unique); } else if (pos == CENTER_SIDE) { center_funcs->append(ins, unique); } } void MainWindow::accept_view_transport(const Core::Route &key, Components::GroupType type) { auto cfg = project_manager->configraions(); auto pos_query = QList() << "unique-record" << "split-area" << "index-hash" << "store-point"; auto hash_pos = cfg->getMap(pos_query); auto unique_key = key.links().join("/"); if (type == GroupType::LEFT) hash_pos[unique_key] = LEFT_SIDE; else if (type == GroupType::RIGHT) hash_pos[unique_key] = RIGHT_SIDE; else if (type == GroupType::BOTTOM) hash_pos[unique_key] = BOTTOM_SIDE; else hash_pos[unique_key] = CENTER_SIDE; cfg->setMap(pos_query, hash_pos); auto xins = remove(key); append(xins, key); } bool MainWindow::active(const Route &unique) { PresentHost *array[4] = {left_funcs, right_funcs, bottom_funcs, center_funcs}; for (auto &it : array) it->active(unique); return true; } PresentBase *MainWindow::remove(const Route &unique) { PresentHost *array[4] = {left_funcs, right_funcs, bottom_funcs, center_funcs}; for (auto &it : array) { auto ins = it->remove(unique); if (ins) return ins; } } bool MainWindow::contains(MakeTools::PresentBase *ins) const { return false; } QWidget *MainWindow::hostWidget() const { return (QWidget *)this; } void MainWindow::setVisibleState(bool state) { QMainWindow::setVisible(state); } bool MainWindow::visibleState() const { return QMainWindow::isVisible(); } void MainWindow::hasBeenAccepted(const Core::Route &key) { auto cfg = project_manager->configraions(); auto pos_query = QList() << "unique-record" << "split-area" << "index-hash" << "store-point"; auto hash_pos = cfg->getMap(pos_query); auto unique_key = key.links().join("/"); if (left_funcs->active(key)) { hash_pos[unique_key] = LEFT_SIDE; } else if (right_funcs->active(key)) { hash_pos[unique_key] = RIGHT_SIDE; } else if (bottom_funcs->active(key)) { hash_pos[unique_key] = BOTTOM_SIDE; } else { hash_pos[unique_key] = CENTER_SIDE; } cfg->setMap(pos_query, hash_pos); docs_container->hasBeenAccepted(key); } void MainWindow::hasBeenRemoved(const Core::Route &key) { docs_container->hasBeenRemoved(key); } void MainWindow::hasBeenClosed(const Core::Route &key) { docs_container->hasBeenClosed(key); } void MainWindow::closeEvent(QCloseEvent *event) { // 关闭事件 if (project_manager->isOpenning()) { XApp::disp_core->postCommand(CloseProject()); } QMainWindow::closeEvent(event); } bool MainWindow::eventFilter(QObject *watched, QEvent *event) { auto ev = event->type(); switch (ev) { case QEvent::Type::MouseButtonPress: case QEvent::Type::MouseButtonRelease: case QEvent::Type::Wheel: case QEvent::Type::MouseMove: case QEvent::Type::KeyPress: case QEvent::Type::KeyRelease: sync_kernel->sync(); break; default: break; } return QMainWindow::eventFilter(watched, event); }