From 484935af39e7af41c22442ec15d7f425f83843d1 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Mon, 1 Apr 2024 22:17:04 +0900 Subject: [PATCH] fix undo/redo --- GUI.cpp | 340 ++++++++++++++++++++++++------------------- XG_PatternDialog.hpp | 24 --- XG_UndoBuffer.cpp | 3 + XG_UndoBuffer.hpp | 27 +++- 4 files changed, 222 insertions(+), 172 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index a66fea8..cd479e4 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -2789,6 +2789,40 @@ void __fastcall XgFitZoom(HWND hwnd) XgUpdateStatusBar(hwnd); } +// 結果を表示する。 +void __fastcall XgShowResults(HWND hwnd, BOOL bOK) +{ + WCHAR sz[MAX_PATH]; + if (bOK) { + // 成功メッセージを表示する。 + if (xg_bAutoSave && PathFileExistsW(xg_strFileName.c_str())) { + StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_MADEPROBLEM2), + PathFindFileNameW(xg_strFileName.c_str()), + DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, + DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); + } else { + StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_MADEPROBLEM), + DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, + DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); + } + XgCenterMessageBoxW(hwnd, sz, XgLoadStringDx2(IDS_RESULTS), MB_ICONINFORMATION); + // ヒントを表示する。 + XgShowHints(hwnd); + } else if (xg_bCancelled) { + // キャンセルされた。 + StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_CANCELLED), + DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, + DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); + XgCenterMessageBoxW(hwnd, sz, XgLoadStringDx2(IDS_RESULTS), MB_ICONINFORMATION); + } else { + // 失敗メッセージを表示する。 + StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_CANTMAKEPROBLEM), + DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, + DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); + XgCenterMessageBoxW(hwnd, sz, XgLoadStringDx2(IDS_RESULTS), MB_ICONERROR); + } +} + // 自動的にPAT.txtから選んで問題を作成する。 TRIVALUE XgGenerateFromPat(HWND hwnd) { @@ -2830,97 +2864,42 @@ TRIVALUE XgGenerateFromPat(HWND hwnd) size_t iPat = g() % patterns.size(); #endif + // 元に戻す情報を取得。 + auto sa1 = std::make_shared(); + sa1->Get(); + // コピー&貼り付け。 auto& pat = patterns[iPat]; - XgPasteBoard(xg_hMainWnd, pat.text); + XgPasteBoard(hwnd, pat.text); XgUpdateImage(hwnd); + // 元に戻す情報を設定。 + auto sa2 = std::make_shared(); + sa2->Get(); + xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); + // 解を求める(黒マス追加なし)。結果を表示しない。 XgOnSolve_NoAddBlackNoResults(hwnd); xg_bShowAnswer = xg_bShowAnswerOnGenerate; XgUpdateImage(hwnd); - // 成功か失敗。 - return xg_bSolved ? TV_POSITIVE : TV_NEGATIVE; -} - -// 問題の作成。 -bool __fastcall XgOnGenerate(HWND hwnd) -{ - INT_PTR nID; - ::EnableWindow(xg_hwndInputPalette, FALSE); - { - // [問題の作成]ダイアログ。 - XG_GenDialog dialog; - nID = static_cast(dialog.DoModal(hwnd)); - } - ::EnableWindow(xg_hwndInputPalette, TRUE); - if (nID != IDOK) { - return false; - } - - if (xg_bChoosePAT) { // 複数でなく、自動的にPAT.txtから選択するか? - auto tri_value = XgGenerateFromPat(hwnd); - if (tri_value == TV_NEGATIVE) - return false; // 失敗。 - if (tri_value == TV_POSITIVE) - return true; // 成功。 - // さもなくばパターンを生成。 - } - - xg_bSolvingEmpty = true; - xg_bNoAddBlack = false; - xg_nNumberGenerated = 0; - s_bOutOfDiskSpace = false; - xg_strHeader.clear(); - xg_strNotes.clear(); - xg_strFileName.clear(); - // ナンクロモードをリセットする。 - xg_bNumCroMode = false; - xg_mapNumCro1.clear(); - xg_mapNumCro2.clear(); - - // ズームを実際のウィンドウに合わせる。 - XgFitZoom(hwnd); - // ステータスバーを更新する。 - XgUpdateStatusBar(hwnd); + if (xg_bSolved) { + // 番号とヒントを付ける。 + xg_solution.DoNumberingNoCheck(); + XgUpdateHints(); - // 辞書を読み込む。 - XgLoadDictFile(xg_dict_name.c_str()); - XgSetInputModeFromDict(hwnd); - // 計算時間を求めるために、開始時間をセットする。 - xg_dwlTick0 = ::GetTickCount64(); - // キャンセルダイアログを表示し、実行を開始する。 - ::EnableWindow(xg_hwndInputPalette, FALSE); - { - if (xg_bSmartResolution && xg_nRows >= 7 && xg_nCols >= 7) { - XG_CancelSmartSolveDialog dialog; - nID = dialog.DoModal(hwnd); - } else if (xg_nRules & (RULE_POINTSYMMETRY | RULE_LINESYMMETRYV | RULE_LINESYMMETRYH)) { - XG_CancelSmartSolveDialog dialog; - nID = dialog.DoModal(hwnd); - } else { - XG_CancelSolveDialog dialog; - nID = dialog.DoModal(hwnd); - } - // 生成成功のときはxg_nNumberGeneratedを増やす。 - if (nID == IDOK && xg_bSolved) { - ++xg_nNumberGenerated; - } - } - ::EnableWindow(xg_hwndInputPalette, TRUE); + // 元に戻す情報を設定。 + auto sa3 = std::make_shared(); + sa3->Get(); + xg_ubUndoBuffer.Commit(UC_SETALL, sa2, sa3); - // 初期化する。 - xg_bShowAnswer = xg_bShowAnswerOnGenerate; - if (xg_bSmartResolution && xg_bCancelled) { - xg_xword.clear(); + // 結果を表示する。 + XgShowResults(hwnd, TRUE); } - // ズームを全体に合わせる。 - XgFitZoom(hwnd); - - return true; + // 成功か失敗。 + return xg_bSolved ? TV_POSITIVE : TV_NEGATIVE; } // 黒マスパターンを連続生成する。 @@ -3088,40 +3067,6 @@ bool __fastcall XgOnGenerateBlacks(HWND hwnd, bool sym) return true; } -// 結果を表示する。 -void __fastcall XgShowResults(HWND hwnd) -{ - WCHAR sz[MAX_PATH]; - if (xg_bSolved) { - // 成功メッセージを表示する。 - if (xg_bAutoSave && PathFileExistsW(xg_strFileName.c_str())) { - StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_MADEPROBLEM2), - PathFindFileNameW(xg_strFileName.c_str()), - DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, - DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); - } else { - StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_MADEPROBLEM), - DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, - DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); - } - XgCenterMessageBoxW(hwnd, sz, XgLoadStringDx2(IDS_RESULTS), MB_ICONINFORMATION); - // ヒントを表示する。 - XgShowHints(hwnd); - } else if (xg_bCancelled) { - // キャンセルされた。 - StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_CANCELLED), - DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, - DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); - XgCenterMessageBoxW(hwnd, sz, XgLoadStringDx2(IDS_RESULTS), MB_ICONINFORMATION); - } else { - // 失敗メッセージを表示する。 - StringCchPrintf(sz, _countof(sz), XgLoadStringDx1(IDS_CANTMAKEPROBLEM), - DWORD(xg_dwlTick2 - xg_dwlTick0) / 1000, - DWORD(xg_dwlTick2 - xg_dwlTick0) / 100 % 10); - XgCenterMessageBoxW(hwnd, sz, XgLoadStringDx2(IDS_RESULTS), MB_ICONERROR); - } -} - // 連番保存。 BOOL __fastcall XgNumberingSave(HWND hwnd) { @@ -3227,13 +3172,17 @@ bool __fastcall XgOnSolve_AddBlack(HWND hwnd) XgMarkUpdate(); XgUpdateImage(hwnd, 0, 0); + // 番号とヒントを付ける。 + xg_solution.DoNumberingNoCheck(); + XgUpdateHints(); + // 自動保存なら保存する。 if (xg_bAutoSave) { XgNumberingSave(hwnd); } // 結果を表示する。 - XgShowResults(hwnd); + XgShowResults(hwnd, TRUE); } else { // 解なし。表示を更新する。 xg_bShowAnswer = false; @@ -3342,7 +3291,7 @@ bool __fastcall XgOnSolve_NoAddBlack(HWND hwnd) } // メッセージを表示する。 - XgShowResults(hwnd); + XgShowResults(hwnd, TRUE); } else { // 解なし。表示を更新する。 xg_bShowAnswer = false; @@ -4988,7 +4937,32 @@ void __fastcall XgOnlineDict(HWND hwnd, BOOL bVert) void __fastcall XgOpenPatterns(HWND hwnd) { XG_PatternDialog dialog; - dialog.DoModal(hwnd); + if (dialog.DoModal(hwnd) != IDOK) + return; // キャンセルされた。 + + // ズームを実際のウィンドウに合わせる。 + XgFitZoom(hwnd); + + // ステータスバーを更新する。 + XgUpdateStatusBar(GetParent(hwnd)); + + // 元に戻す情報を取得する。 + auto sa1 = std::make_shared(); + sa1->Get(); + + // 解を求める(黒マス追加なし)。 + XgOnSolve_NoAddBlack(hwnd); + + // 元に戻す情報を設定する。 + auto sa2 = std::make_shared(); + sa2->Get(); + xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); + + // 表示を更新する。 + XgUpdateImage(hwnd, 0, 0); + + // ツールバーのUIを更新する。 + XgUpdateToolBarUI(hwnd); } // 「黒マスルールの説明.txt」を開く。 @@ -5250,10 +5224,6 @@ void XgGenerateFromWordList(HWND hwnd) CharLowerBuffW(&word[0], (DWORD)word.size()); } XG_WordListDialog::s_str_word_list = mstr_join(XG_WordListDialog::s_words, L" "); - // 「元に戻す」情報を設定する。 - auto sa2 = std::make_shared(); - sa2->Get(); - xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); // ズームを実際のウィンドウに合わせる。 XgFitZoom(hwnd); @@ -5262,13 +5232,18 @@ void XgGenerateFromWordList(HWND hwnd) xg_solution.DoNumberingNoCheck(); XgUpdateHints(); + // 「元に戻す」情報を設定する。 + auto sa2 = std::make_shared(); + sa2->Get(); + xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); + // 自動保存なら保存する。 if (xg_bAutoSave) { XgNumberingSave(hwnd); } // 成功メッセージ。 - XgShowResults(hwnd); + XgShowResults(hwnd, TRUE); // クリア。 XG_WordListDialog::s_words.clear(); @@ -5485,43 +5460,116 @@ void __fastcall XgDrawCaret(HDC hdc) // 問題を生成する。 void __fastcall XgGenerate(HWND hwnd) { - bool flag = false; - // 元に戻す情報を取得する。 + auto sa0 = std::make_shared(); + sa0->Get(); + // [問題の作成]ダイアログ。 + INT_PTR nID; + ::EnableWindow(xg_hwndInputPalette, FALSE); + { + XG_GenDialog dialog; + nID = dialog.DoModal(hwnd); + } + ::EnableWindow(xg_hwndInputPalette, TRUE); + if (nID != IDOK) + return; // キャンセルされた。 + auto sa1 = std::make_shared(); sa1->Get(); + xg_ubUndoBuffer.Commit(UC_SETALL, sa0, sa1); + + if (xg_bChoosePAT) { // 自動的にPAT.txtから選択するか? + auto tri_value = XgGenerateFromPat(hwnd); // PAT.txtから生成。 + if (tri_value == TV_NEGATIVE) + return; // 失敗。 + if (tri_value == TV_POSITIVE) + return; // 成功。 + // さもなくばパターンを生成。 + } + // 候補ウィンドウを破棄する。 XgDestroyCandsWnd(); // ヒントウィンドウを破棄する。 XgDestroyHintsWnd(); // 二重マス単語の候補と配置を破棄する。 ::DestroyWindow(xg_hMarkingDlg); - // 問題の作成。 - if (XgOnGenerate(hwnd)) { - flag = true; - // イメージを更新する。 - XgSetCaretPos(); - XgMarkUpdate(); - XgUpdateImage(hwnd); - // 番号とヒントを付ける。 - xg_solution.DoNumberingNoCheck(); - XgUpdateHints(); - // 元に戻す情報を確定。 - auto sa2 = std::make_shared(); - sa2->Get(); - xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); - // 自動で保存なら保存する。 - if (xg_bAutoSave) { - XgNumberingSave(hwnd); + + xg_bSolvingEmpty = true; + xg_bNoAddBlack = false; + xg_nNumberGenerated = 0; + s_bOutOfDiskSpace = false; + xg_strHeader.clear(); + xg_strNotes.clear(); + xg_strFileName.clear(); + // ナンクロモードをリセットする。 + xg_bNumCroMode = false; + xg_mapNumCro1.clear(); + xg_mapNumCro2.clear(); + + // ズームを実際のウィンドウに合わせる。 + XgFitZoom(hwnd); + // ステータスバーを更新する。 + XgUpdateStatusBar(hwnd); + + // 辞書を読み込む。 + XgLoadDictFile(xg_dict_name.c_str()); + XgSetInputModeFromDict(hwnd); + + // 計算時間を求めるために、開始時間をセットする。 + xg_dwlTick0 = ::GetTickCount64(); + + // キャンセルダイアログを表示し、実行を開始する。 + ::EnableWindow(xg_hwndInputPalette, FALSE); + { + if (xg_bSmartResolution) { + XG_CancelSmartSolveDialog dialog; + nID = dialog.DoModal(hwnd); + } else if (xg_nRules & (RULE_POINTSYMMETRY | RULE_LINESYMMETRYV | RULE_LINESYMMETRYH)) { + XG_CancelSmartSolveDialog dialog; + nID = dialog.DoModal(hwnd); + } else { + XG_CancelSolveDialog dialog; + nID = dialog.DoModal(hwnd); + } + // 生成成功のときはxg_nNumberGeneratedを増やす。 + if (nID == IDOK && xg_bSolved) { + ++xg_nNumberGenerated; } } - if (!flag) { - sa1->Apply(); + ::EnableWindow(xg_hwndInputPalette, TRUE); + + if (!xg_bSolved) { + // 結果を表示する。 + XgShowResults(hwnd, FALSE); + return; } + + // 番号とヒントを付ける。 + xg_solution.DoNumberingNoCheck(); + XgUpdateHints(); + + // ズームを全体に合わせる。 + XgFitZoom(hwnd); + + // 答えを表示するかどうか。 + xg_bShowAnswer = xg_bShowAnswerOnGenerate; + + // 「元に戻す」情報を確定する。 + auto sa2 = std::make_shared(); + sa2->Get(); + xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); + + // 自動で保存なら保存する。 + if (xg_bAutoSave) { + XgNumberingSave(hwnd); + } + // イメージを更新する。 XgSetCaretPos(); XgMarkUpdate(); + XgUpdateImage(hwnd); + // 結果を表示する。 - XgShowResults(hwnd); + XgShowResults(hwnd, TRUE); } // 次のペインまたは前のペインに移動する。 @@ -6045,14 +6093,12 @@ void __fastcall MainWnd_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT /*codeNo // イメージを更新する。 XgUpdateImage(hwnd, x, y); - auto sa2 = std::make_shared(); - { - // 解を求める(黒マス追加なし)。 - XgOnSolve_NoAddBlack(hwnd); - } - sa2->Get(); + // 解を求める(黒マス追加なし)。 + XgOnSolve_NoAddBlack(hwnd); // 元に戻す情報を設定する。 + auto sa2 = std::make_shared(); + sa2->Get(); xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); } bUpdateImage = TRUE; diff --git a/XG_PatternDialog.hpp b/XG_PatternDialog.hpp index 253bb99..66f2d42 100644 --- a/XG_PatternDialog.hpp +++ b/XG_PatternDialog.hpp @@ -238,30 +238,6 @@ class XG_PatternDialog : public XG_Dialog // 答えを表示するか? xg_bShowAnswerOnGenerate = (IsDlgButtonChecked(hwnd, chx1) == BST_CHECKED); - - // ズームを実際のウィンドウに合わせる。 - XgFitZoom(GetParent(hwnd)); - // ステータスバーを更新する。 - XgUpdateStatusBar(GetParent(hwnd)); - - { - auto sa1 = std::make_shared(); - auto sa2 = std::make_shared(); - sa1->Get(); - { - // 解を求める(黒マス追加なし)。 - XgOnSolve_NoAddBlack(xg_hMainWnd); - } - sa2->Get(); - // 元に戻す情報を設定する。 - xg_ubUndoBuffer.Commit(UC_SETALL, sa1, sa2); - } - - // 表示を更新する。 - XgUpdateImage(xg_hMainWnd, 0, 0); - - // ツールバーのUIを更新する。 - XgUpdateToolBarUI(xg_hMainWnd); } // ダウンロードする。 diff --git a/XG_UndoBuffer.cpp b/XG_UndoBuffer.cpp index 2a0131d..af39e1f 100644 --- a/XG_UndoBuffer.cpp +++ b/XG_UndoBuffer.cpp @@ -176,6 +176,9 @@ static constexpr int c_max_size = 25; void XG_UndoBuffer::Commit(UINT id, shared_ptr before, shared_ptr after) { + assert(id == before->m_cmdId); + assert(id == after->m_cmdId); + if (!m_enabled) return; diff --git a/XG_UndoBuffer.hpp b/XG_UndoBuffer.hpp index e19c9a3..cd155fd 100644 --- a/XG_UndoBuffer.hpp +++ b/XG_UndoBuffer.hpp @@ -22,8 +22,11 @@ enum UNDOABLE_COMMAND_ID : UINT { ////////////////////////////////////////////////////////////////////////////// struct XG_UndoData { - XG_UndoData() noexcept + UNDOABLE_COMMAND_ID m_cmdId; + + XG_UndoData(UNDOABLE_COMMAND_ID cmdId) noexcept { + m_cmdId = cmdId; } virtual ~XG_UndoData() { @@ -39,6 +42,8 @@ struct XG_UndoData_SetAt : XG_UndoData { XG_Pos pos; WCHAR ch; + XG_UndoData_SetAt() : XG_UndoData(UC_SETAT) { } + ~XG_UndoData_SetAt() override { } @@ -51,6 +56,8 @@ struct XG_UndoData_MarksUpdated : XG_UndoData { std::vector vMarks; XGStringW strMarked; + XG_UndoData_MarksUpdated() : XG_UndoData(UC_MARKS_UPDATED) { } + ~XG_UndoData_MarksUpdated() override { } @@ -67,6 +74,8 @@ struct XG_UndoData_HintsUpdated : XG_UndoData { std::vector vecHorzHints; BOOL bShowHints; + XG_UndoData_HintsUpdated() : XG_UndoData(UC_HINTS_UPDATED) { } + ~XG_UndoData_HintsUpdated() override { } @@ -79,6 +88,8 @@ struct XG_UndoData_HintsUpdated : XG_UndoData { struct XG_UndoData_NumCro : XG_UndoData { bool bNumCro; + XG_UndoData_NumCro() : XG_UndoData(UC_NUMCRO) { } + ~XG_UndoData_NumCro() override { } @@ -91,6 +102,8 @@ struct XG_UndoData_NumCro : XG_UndoData { struct XG_UndoData_ViewMode : XG_UndoData { XG_VIEW_MODE nViewMode; + XG_UndoData_ViewMode() : XG_UndoData(UC_VIEWMODE) { } + ~XG_UndoData_ViewMode() override { } @@ -103,6 +116,8 @@ struct XG_UndoData_ViewMode : XG_UndoData { struct XG_UndoData_Boxes : XG_UndoData { XGStringW boxes; + XG_UndoData_Boxes() : XG_UndoData(UC_BOXES) { } + ~XG_UndoData_Boxes() override { } @@ -134,6 +149,8 @@ struct XG_UndoData_SetAll : XG_UndoData { XG_VIEW_MODE nViewMode; XGStringW boxes; + XG_UndoData_SetAll() : XG_UndoData(UC_SETALL) { } + ~XG_UndoData_SetAll() override { } @@ -154,24 +171,32 @@ struct XG_UndoInfo { XG_UndoInfo(UINT id, XG_UndoData *before, XG_UndoData *after) : nCommandID(id), pBefore(before), pAfter(after) { + assert(nCommandID == pBefore->m_cmdId); + assert(nCommandID == pAfter->m_cmdId); } XG_UndoInfo(UINT id, shared_ptr before, shared_ptr after) noexcept : nCommandID(id), pBefore(before), pAfter(after) { + assert(nCommandID == pBefore->m_cmdId); + assert(nCommandID == pAfter->m_cmdId); } XG_UndoInfo(const XG_UndoInfo& info) noexcept : nCommandID(info.nCommandID), pBefore(info.pBefore), pAfter(info.pAfter) { + assert(nCommandID == pBefore->m_cmdId); + assert(nCommandID == pAfter->m_cmdId); } XG_UndoInfo& operator=(const XG_UndoInfo& info) noexcept { nCommandID = info.nCommandID; pBefore = info.pBefore; pAfter = info.pAfter; + assert(nCommandID == pBefore->m_cmdId); + assert(nCommandID == pAfter->m_cmdId); return *this; }