From 3b54b5c49e0b0937d9eae2da56817328ec3e8095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sat, 24 Nov 2007 14:36:13 +0000 Subject: [PATCH] * Renamed "Store Current" to "Snapshot Current", and "Restore Saved" to "Restore Snapshot". * Implemented a simple undo/redo. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22987 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/sudoku/Jamfile | 2 + src/apps/sudoku/SudokuView.cpp | 69 ++++++++++++++++++++++++++++++++ src/apps/sudoku/SudokuView.h | 13 ++++++ src/apps/sudoku/SudokuWindow.cpp | 30 ++++++++++++-- src/apps/sudoku/SudokuWindow.h | 2 + 5 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/apps/sudoku/Jamfile b/src/apps/sudoku/Jamfile index 038f5384fc..f5996a64cf 100644 --- a/src/apps/sudoku/Jamfile +++ b/src/apps/sudoku/Jamfile @@ -2,6 +2,8 @@ SubDir HAIKU_TOP src apps sudoku ; SetSubDirSupportedPlatformsBeOSCompatible ; +UsePrivateHeaders shared ; + Application Sudoku : CenteredViewContainer.cpp ProgressWindow.cpp diff --git a/src/apps/sudoku/SudokuView.cpp b/src/apps/sudoku/SudokuView.cpp index 70627f1de3..0e799b2006 100644 --- a/src/apps/sudoku/SudokuView.cpp +++ b/src/apps/sudoku/SudokuView.cpp @@ -144,6 +144,7 @@ SudokuView::SetTo(entry_ref& ref) } } + _PushUndo(); status = fField->SetTo(_BaseCharacter(), buffer); Invalidate(); return status; @@ -164,6 +165,7 @@ SudokuView::SetTo(const char* data) if (status < B_OK) return B_BAD_VALUE; + _PushUndo(); status = fField->SetTo(_BaseCharacter(), buffer); Invalidate(); return status; @@ -176,6 +178,7 @@ SudokuView::SetTo(SudokuField* field) if (field == NULL || field == fField) return B_BAD_VALUE; + _PushUndo(); delete fField; fField = field; @@ -222,6 +225,8 @@ SudokuView::SaveTo(entry_ref& ref, bool asText) void SudokuView::ClearChanged() { + _PushUndo(); + for (uint32 y = 0; y < fField->Size(); y++) { for (uint32 x = 0; x < fField->Size(); x++) { if ((fField->FlagsAt(x, y) & kInitialValue) == 0) @@ -236,6 +241,7 @@ SudokuView::ClearChanged() void SudokuView::ClearAll() { + _PushUndo(); fField->Reset(); Invalidate(); } @@ -413,6 +419,55 @@ SudokuView::_RemoveHint() } +void +SudokuView::_UndoRedo(BObjectList& undos, + BObjectList& redos) +{ + if (undos.IsEmpty()) + return; + + BMessage* undo = undos.RemoveItemAt(undos.CountItems() - 1); + + BMessage* redo = new BMessage; + if (fField->Archive(redo, true) == B_OK) + redos.AddItem(redo); + + SudokuField field(undo); + delete undo; + + fField->SetTo(&field); + + SendNotices(kUndoRedoChanged); + Invalidate(); +} + + +void +SudokuView::Undo() +{ + _UndoRedo(fUndos, fRedos); +} + + +void +SudokuView::Redo() +{ + _UndoRedo(fRedos, fUndos); +} + + +void +SudokuView::_PushUndo() +{ + fRedos.MakeEmpty(); + + BMessage* undo = new BMessage; + if (fField->Archive(undo, true) == B_OK + && fUndos.AddItem(undo)) + SendNotices(kUndoRedoChanged); +} + + void SudokuView::MouseDown(BPoint where) { @@ -433,6 +488,7 @@ SudokuView::MouseDown(BPoint where) uint32 value = hintX + hintY * fBlockSize; uint32 field = x + y * fField->Size(); + _PushUndo(); if (clicks == 2 && fLastHintValue == value && fLastField == field || (buttons & (B_SECONDARY_MOUSE_BUTTON @@ -540,6 +596,7 @@ SudokuView::_InsertKey(char rawKey, int32 modifiers) if (value == 0) return; + _PushUndo(); uint32 hintMask = fField->HintMaskAt(fKeyboardX, fKeyboardY); uint32 valueMask = 1UL << (value - 1); if (modifiers & B_OPTION_KEY) @@ -550,6 +607,7 @@ SudokuView::_InsertKey(char rawKey, int32 modifiers) fField->SetValueAt(fKeyboardX, fKeyboardY, 0); fField->SetHintMaskAt(fKeyboardX, fKeyboardY, hintMask); } else { + _PushUndo(); fField->SetValueAt(fKeyboardX, fKeyboardY, value); if (value) BMessenger(this).SendMessage(kMsgCheckSolved); @@ -638,6 +696,14 @@ SudokuView::MessageReceived(BMessage* message) } break; + case B_UNDO: + Undo(); + break; + + case B_REDO: + Redo(); + break; + case kMsgSolveSudoku: { SudokuSolver solver; @@ -647,6 +713,7 @@ SudokuView::MessageReceived(BMessage* message) printf("found %ld solutions in %g msecs\n", solver.CountSolutions(), (system_time() - start) / 1000.0); if (solver.CountSolutions() > 0) { + _PushUndo(); fField->SetTo(solver.SolutionAt(0)); Invalidate(); } else @@ -668,6 +735,8 @@ SudokuView::MessageReceived(BMessage* message) printf("found %ld solutions in %g msecs\n", solver.CountSolutions(), (system_time() - start) / 1000.0); if (solver.CountSolutions() > 0) { + _PushUndo(); + // find free spot uint32 x, y; do { diff --git a/src/apps/sudoku/SudokuView.h b/src/apps/sudoku/SudokuView.h index d9cd312ead..1eb3bc7cae 100644 --- a/src/apps/sudoku/SudokuView.h +++ b/src/apps/sudoku/SudokuView.h @@ -7,6 +7,7 @@ #include +#include class SudokuField; struct entry_ref; @@ -43,6 +44,11 @@ public: void SetEditable(bool editable); bool Editable() const { return fEditable; } + bool CanUndo() { return !fUndos.IsEmpty(); } + bool CanRedo() { return !fRedos.IsEmpty(); } + void Undo(); + void Redo(); + protected: virtual void AttachedToWindow(); @@ -75,9 +81,13 @@ private: void _FitFont(BFont& font, float width, float height); void _DrawKeyboardFocus(); void _DrawHints(uint32 x, uint32 y); + void _UndoRedo(BObjectList& undos, BObjectList& redos); + void _PushUndo(); rgb_color fBackgroundColor; SudokuField* fField; + BObjectList fUndos; + BObjectList fRedos; uint32 fBlockSize; float fWidth, fHeight, fBaseline; BFont fFieldFont; @@ -98,4 +108,7 @@ static const uint32 kMsgSudokuSolved = 'susl'; static const uint32 kMsgSolveSudoku = 'slvs'; static const uint32 kMsgSolveSingle = 'slsg'; +// you can observe these: +static const int32 kUndoRedoChanged = 'unre'; + #endif // SUDOKU_VIEW_H diff --git a/src/apps/sudoku/SudokuWindow.cpp b/src/apps/sudoku/SudokuWindow.cpp index c878568070..156b54bba9 100644 --- a/src/apps/sudoku/SudokuWindow.cpp +++ b/src/apps/sudoku/SudokuWindow.cpp @@ -210,8 +210,7 @@ SudokuWindow::SudokuWindow() new BMessage(B_ABOUT_REQUESTED))); menu->AddSeparatorItem(); - menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), - 'Q', B_COMMAND_KEY)); + menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q')); menu->SetTargetForItems(this); item->SetTarget(be_app); menuBar->AddItem(menu); @@ -231,8 +230,15 @@ SudokuWindow::SudokuWindow() // "Help" menu menu = new BMenu("Help"); - menu->AddItem(new BMenuItem("Store Current", new BMessage(kMsgStoreState))); - menu->AddItem(fRestoreStateItem = new BMenuItem("Restore Saved", + menu->AddItem(fUndoItem = new BMenuItem("Undo", new BMessage(B_UNDO), 'Z')); + fUndoItem->SetEnabled(false); + menu->AddItem(fRedoItem = new BMenuItem("Redo", new BMessage(B_REDO), 'Z', + B_SHIFT_KEY)); + fRedoItem->SetEnabled(false); + menu->AddSeparatorItem(); + + menu->AddItem(new BMenuItem("Snapshot Current", new BMessage(kMsgStoreState))); + menu->AddItem(fRestoreStateItem = new BMenuItem("Restore Snapshot", new BMessage(kMsgRestoreState))); fRestoreStateItem->SetEnabled(fStoredState != NULL); menu->AddSeparatorItem(); @@ -248,6 +254,9 @@ SudokuWindow::SudokuWindow() fSavePanel = new BFilePanel(B_SAVE_PANEL); fSavePanel->SetTarget(this); + fSudokuView->StartWatching(this, kUndoRedoChanged); + // we like to know whenever the undo/redo state changes + fProgressWindow = new ProgressWindow(this, new BMessage(kMsgAbortSudokuGenerator)); } @@ -499,6 +508,19 @@ SudokuWindow::MessageReceived(BMessage* message) B_WIDTH_AS_USUAL, B_IDEA_ALERT))->Go(); break; + case B_OBSERVER_NOTICE_CHANGE: + { + int32 what; + if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK) + break; + + if (what == kUndoRedoChanged) { + fUndoItem->SetEnabled(fSudokuView->CanUndo()); + fRedoItem->SetEnabled(fSudokuView->CanRedo()); + } + break; + } + default: BWindow::MessageReceived(message); break; diff --git a/src/apps/sudoku/SudokuWindow.h b/src/apps/sudoku/SudokuWindow.h index 562e884164..c6108db318 100644 --- a/src/apps/sudoku/SudokuWindow.h +++ b/src/apps/sudoku/SudokuWindow.h @@ -39,6 +39,8 @@ private: SudokuView* fSudokuView; GenerateSudoku* fGenerator; BMenuItem* fRestoreStateItem; + BMenuItem* fUndoItem; + BMenuItem* fRedoItem; BMessage* fStoredState; };