btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / sudoku / SudokuWindow.cpp
blobf84dd5aa8df2bd0f544713d330a30d816ceaab3a
1 /*
2 * Copyright 2007-2015, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "SudokuWindow.h"
9 #include <stdio.h>
11 #include <Alert.h>
12 #include <Application.h>
13 #include <Catalog.h>
14 #include <File.h>
15 #include <FilePanel.h>
16 #include <FindDirectory.h>
17 #include <LayoutBuilder.h>
18 #include <Menu.h>
19 #include <MenuBar.h>
20 #include <MenuItem.h>
21 #include <Path.h>
22 #include <Roster.h>
24 #include <be_apps/Tracker/RecentItems.h>
26 #include "ProgressWindow.h"
27 #include "Sudoku.h"
28 #include "SudokuField.h"
29 #include "SudokuGenerator.h"
30 #include "SudokuView.h"
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "SudokuWindow"
37 const uint32 kMsgOpenFilePanel = 'opfp';
38 const uint32 kMsgGenerateSudoku = 'gnsu';
39 const uint32 kMsgAbortSudokuGenerator = 'asgn';
40 const uint32 kMsgSudokuGenerated = 'sugn';
41 const uint32 kMsgMarkInvalid = 'minv';
42 const uint32 kMsgMarkValidHints = 'mvht';
43 const uint32 kMsgStoreState = 'stst';
44 const uint32 kMsgRestoreState = 'rest';
45 const uint32 kMsgNewBlank = 'new ';
46 const uint32 kMsgStartAgain = 'stag';
47 const uint32 kMsgExportAs = 'expt';
50 enum sudoku_level {
51 kEasyLevel = 0,
52 kAdvancedLevel = 2,
53 kHardLevel = 4,
57 class GenerateSudoku {
58 public:
59 GenerateSudoku(SudokuField& field, int32 level,
60 BMessenger progress, BMessenger target);
61 ~GenerateSudoku();
63 void Abort();
65 private:
66 void _Generate();
67 static status_t _GenerateThread(void* self);
69 SudokuField fField;
70 BMessenger fTarget;
71 BMessenger fProgress;
72 thread_id fThread;
73 int32 fLevel;
74 bool fQuit;
78 GenerateSudoku::GenerateSudoku(SudokuField& field, int32 level,
79 BMessenger progress, BMessenger target)
81 fField(field),
82 fTarget(target),
83 fProgress(progress),
84 fLevel(level),
85 fQuit(false)
87 fThread = spawn_thread(_GenerateThread, "sudoku generator",
88 B_LOW_PRIORITY, this);
89 if (fThread >= B_OK)
90 resume_thread(fThread);
91 else
92 _Generate();
96 GenerateSudoku::~GenerateSudoku()
98 Abort();
102 void
103 GenerateSudoku::Abort()
105 fQuit = true;
107 status_t status;
108 wait_for_thread(fThread, &status);
112 void
113 GenerateSudoku::_Generate()
115 SudokuGenerator generator;
117 bigtime_t start = system_time();
118 generator.Generate(&fField, 40 - fLevel * 5, fProgress, &fQuit);
119 printf("generated in %g msecs\n", (system_time() - start) / 1000.0);
121 BMessage done(kMsgSudokuGenerated);
122 if (!fQuit) {
123 BMessage field;
124 if (fField.Archive(&field, true) == B_OK)
125 done.AddMessage("field", &field);
128 fTarget.SendMessage(&done);
132 /*static*/ status_t
133 GenerateSudoku::_GenerateThread(void* _self)
135 GenerateSudoku* self = (GenerateSudoku*)_self;
136 self->_Generate();
137 return B_OK;
141 // #pragma mark -
144 SudokuWindow::SudokuWindow()
146 BWindow(BRect(-1, -1, 400, 420), B_TRANSLATE_SYSTEM_NAME("Sudoku"),
147 B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE),
148 fGenerator(NULL),
149 fStoredState(NULL),
150 fExportFormat(kExportAsText)
152 BMessage settings;
153 _LoadSettings(settings);
155 BRect frame;
156 if (settings.FindRect("window frame", &frame) == B_OK) {
157 MoveTo(frame.LeftTop());
158 ResizeTo(frame.Width(), frame.Height());
159 frame.OffsetTo(B_ORIGIN);
160 } else {
161 float scaling = std::max(1.0f, be_plain_font->Size() / 12.0f);
162 ResizeTo(Frame().Width() * scaling, Frame().Height() * scaling);
163 frame = Bounds();
166 MoveOnScreen();
168 if (settings.HasMessage("stored state")) {
169 fStoredState = new BMessage;
170 if (settings.FindMessage("stored state", fStoredState) != B_OK) {
171 delete fStoredState;
172 fStoredState = NULL;
176 int32 level = 0;
177 settings.FindInt32("level", &level);
179 // Create GUI
181 BMenuBar* menuBar = new BMenuBar("menu");
182 fSudokuView = new SudokuView("sudoku view", settings);
184 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
185 .Add(menuBar)
186 .Add(fSudokuView);
188 // Build menu
190 // "File" menu
191 BMenu* menu = new BMenu(B_TRANSLATE("File"));
192 fNewMenu = new BMenu(B_TRANSLATE("New"));
193 menu->AddItem(new BMenuItem(fNewMenu, new BMessage(kMsgGenerateSudoku)));
194 fNewMenu->Superitem()->SetShortcut('N', B_COMMAND_KEY);
196 BMessage* message = new BMessage(kMsgGenerateSudoku);
197 message->AddInt32("level", kEasyLevel);
198 fNewMenu->AddItem(new BMenuItem(B_TRANSLATE("Easy"), message));
199 message = new BMessage(kMsgGenerateSudoku);
200 message->AddInt32("level", kAdvancedLevel);
201 fNewMenu->AddItem(new BMenuItem(B_TRANSLATE("Advanced"), message));
202 message = new BMessage(kMsgGenerateSudoku);
203 message->AddInt32("level", kHardLevel);
204 fNewMenu->AddItem(new BMenuItem(B_TRANSLATE("Hard"), message));
206 fNewMenu->AddSeparatorItem();
207 fNewMenu->AddItem(new BMenuItem(B_TRANSLATE("Blank"),
208 new BMessage(kMsgNewBlank)));
210 menu->AddItem(new BMenuItem(B_TRANSLATE("Start again"),
211 new BMessage(kMsgStartAgain)));
212 menu->AddSeparatorItem();
213 BMenu* recentsMenu = BRecentFilesList::NewFileListMenu(
214 B_TRANSLATE("Open file" B_UTF8_ELLIPSIS), NULL, NULL, this, 10, false,
215 NULL, kSignature);
216 BMenuItem *item;
217 menu->AddItem(item = new BMenuItem(recentsMenu,
218 new BMessage(kMsgOpenFilePanel)));
219 item->SetShortcut('O', B_COMMAND_KEY);
221 menu->AddSeparatorItem();
223 BMenu* subMenu = new BMenu(B_TRANSLATE("Export as" B_UTF8_ELLIPSIS));
224 message = new BMessage(kMsgExportAs);
225 message->AddInt32("as", kExportAsText);
226 subMenu->AddItem(new BMenuItem(B_TRANSLATE("Text"), message));
227 message= new BMessage(kMsgExportAs);
228 message->AddInt32("as", kExportAsHTML);
229 subMenu->AddItem(new BMenuItem(B_TRANSLATE("HTML"), message));
230 menu->AddItem(subMenu);
232 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy"),
233 new BMessage(B_COPY), 'C'));
235 menu->AddSeparatorItem();
237 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
238 new BMessage(B_QUIT_REQUESTED), 'Q'));
239 menu->SetTargetForItems(this);
240 item->SetTarget(be_app);
241 menuBar->AddItem(menu);
243 // "View" menu
244 menu = new BMenu(B_TRANSLATE("View"));
245 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Mark invalid values"),
246 new BMessage(kMsgMarkInvalid)));
247 if ((fSudokuView->HintFlags() & kMarkInvalid) != 0)
248 item->SetMarked(true);
249 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Mark valid hints"),
250 new BMessage(kMsgMarkValidHints)));
251 if ((fSudokuView->HintFlags() & kMarkValidHints) != 0)
252 item->SetMarked(true);
253 menu->SetTargetForItems(this);
254 menuBar->AddItem(menu);
256 // "Help" menu
257 menu = new BMenu(B_TRANSLATE("Help"));
258 menu->AddItem(fUndoItem = new BMenuItem(B_TRANSLATE("Undo"),
259 new BMessage(B_UNDO), 'Z'));
260 fUndoItem->SetEnabled(false);
261 menu->AddItem(fRedoItem = new BMenuItem(B_TRANSLATE("Redo"),
262 new BMessage(B_REDO), 'Z', B_SHIFT_KEY));
263 fRedoItem->SetEnabled(false);
264 menu->AddSeparatorItem();
266 menu->AddItem(new BMenuItem(B_TRANSLATE("Snapshot current"),
267 new BMessage(kMsgStoreState)));
268 menu->AddItem(fRestoreStateItem = new BMenuItem(
269 B_TRANSLATE("Restore snapshot"), new BMessage(kMsgRestoreState)));
270 fRestoreStateItem->SetEnabled(fStoredState != NULL);
271 menu->AddSeparatorItem();
273 menu->AddItem(new BMenuItem(B_TRANSLATE("Set all hints"),
274 new BMessage(kMsgSetAllHints)));
275 menu->AddSeparatorItem();
277 menu->AddItem(new BMenuItem(B_TRANSLATE("Solve"),
278 new BMessage(kMsgSolveSudoku)));
279 menu->AddItem(new BMenuItem(B_TRANSLATE("Solve single field"),
280 new BMessage(kMsgSolveSingle)));
281 menu->SetTargetForItems(fSudokuView);
282 menuBar->AddItem(menu);
284 fOpenPanel = new BFilePanel(B_OPEN_PANEL);
285 fOpenPanel->SetTarget(this);
286 fSavePanel = new BFilePanel(B_SAVE_PANEL);
287 fSavePanel->SetTarget(this);
289 _SetLevel(level);
291 fSudokuView->StartWatching(this, kUndoRedoChanged);
292 // we like to know whenever the undo/redo state changes
294 fProgressWindow = new ProgressWindow(this,
295 new BMessage(kMsgAbortSudokuGenerator));
297 if (fSudokuView->Field()->IsEmpty())
298 PostMessage(kMsgGenerateSudoku);
302 SudokuWindow::~SudokuWindow()
304 delete fOpenPanel;
305 delete fSavePanel;
306 delete fGenerator;
308 if (fProgressWindow->Lock())
309 fProgressWindow->Quit();
313 status_t
314 SudokuWindow::_OpenSettings(BFile& file, uint32 mode)
316 BPath path;
317 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
318 return B_ERROR;
320 path.Append("Sudoku settings");
322 return file.SetTo(path.Path(), mode);
326 status_t
327 SudokuWindow::_LoadSettings(BMessage& settings)
329 BFile file;
330 status_t status = _OpenSettings(file, B_READ_ONLY);
331 if (status != B_OK)
332 return status;
334 return settings.Unflatten(&file);
338 status_t
339 SudokuWindow::_SaveSettings()
341 BFile file;
342 status_t status = _OpenSettings(file, B_WRITE_ONLY | B_CREATE_FILE
343 | B_ERASE_FILE);
344 if (status != B_OK)
345 return status;
347 BMessage settings('sudo');
348 status = settings.AddRect("window frame", Frame());
349 if (status == B_OK)
350 status = fSudokuView->SaveState(settings);
351 if (status == B_OK && fStoredState != NULL)
352 status = settings.AddMessage("stored state", fStoredState);
353 if (status == B_OK)
354 status = settings.AddInt32("level", _Level());
355 if (status == B_OK)
356 status = settings.Flatten(&file);
358 return status;
362 void
363 SudokuWindow::_ResetStoredState()
365 delete fStoredState;
366 fStoredState = NULL;
367 fRestoreStateItem->SetEnabled(false);
371 void
372 SudokuWindow::_MessageDropped(BMessage* message)
374 status_t status = B_MESSAGE_NOT_UNDERSTOOD;
375 bool hasRef = false;
377 entry_ref ref;
378 if (message->FindRef("refs", &ref) != B_OK) {
379 const void* data;
380 ssize_t size;
381 if (message->FindData("text/plain", B_MIME_TYPE, &data,
382 &size) == B_OK) {
383 status = fSudokuView->SetTo((const char*)data);
384 } else
385 return;
386 } else {
387 status = fSudokuView->SetTo(ref);
388 if (status == B_OK)
389 be_roster->AddToRecentDocuments(&ref, kSignature);
391 BEntry entry(&ref);
392 entry_ref parent;
393 if (entry.GetParent(&entry) == B_OK
394 && entry.GetRef(&parent) == B_OK)
395 fSavePanel->SetPanelDirectory(&parent);
397 hasRef = true;
400 if (status < B_OK) {
401 char buffer[1024];
402 if (hasRef) {
403 snprintf(buffer, sizeof(buffer),
404 B_TRANSLATE("Could not open \"%s\":\n%s\n"), ref.name,
405 strerror(status));
406 } else {
407 snprintf(buffer, sizeof(buffer),
408 B_TRANSLATE("Could not set Sudoku:\n%s\n"),
409 strerror(status));
412 BAlert* alert = new BAlert(B_TRANSLATE("Sudoku request"),
413 buffer, B_TRANSLATE("OK"), NULL, NULL,
414 B_WIDTH_AS_USUAL, B_STOP_ALERT);
415 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
416 alert->Go();
421 void
422 SudokuWindow::_Generate(int32 level)
424 if (fGenerator != NULL)
425 delete fGenerator;
427 fSudokuView->SetEditable(false);
428 fProgressWindow->Start(this);
429 _ResetStoredState();
431 fGenerator = new GenerateSudoku(*fSudokuView->Field(), level,
432 fProgressWindow, this);
436 void
437 SudokuWindow::MessageReceived(BMessage* message)
439 if (message->WasDropped()) {
440 _MessageDropped(message);
441 return;
444 switch (message->what) {
445 case kMsgOpenFilePanel:
446 fOpenPanel->Show();
447 break;
449 case B_REFS_RECEIVED:
450 case B_SIMPLE_DATA:
451 _MessageDropped(message);
452 break;
454 case kMsgGenerateSudoku:
456 int32 level;
457 if (message->FindInt32("level", &level) != B_OK)
458 level = _Level();
460 _SetLevel(level);
461 _Generate(level);
462 break;
464 case kMsgAbortSudokuGenerator:
465 if (fGenerator != NULL)
466 fGenerator->Abort();
467 break;
468 case kMsgSudokuGenerated:
470 BMessage archive;
471 if (message->FindMessage("field", &archive) == B_OK) {
472 SudokuField* field = new SudokuField(&archive);
473 fSudokuView->SetTo(field);
475 fSudokuView->SetEditable(true);
476 fProgressWindow->Stop();
478 delete fGenerator;
479 fGenerator = NULL;
480 break;
483 case kMsgExportAs:
485 if (message->FindInt32("as", (int32 *)&fExportFormat) < B_OK)
486 fExportFormat = kExportAsText;
487 fSavePanel->Show();
488 break;
491 case B_COPY:
492 fSudokuView->CopyToClipboard();
493 break;
495 case B_SAVE_REQUESTED:
497 entry_ref directoryRef;
498 const char* name;
499 if (message->FindRef("directory", &directoryRef) != B_OK
500 || message->FindString("name", &name) != B_OK)
501 break;
503 BDirectory directory(&directoryRef);
504 BEntry entry(&directory, name);
506 entry_ref ref;
507 if (entry.GetRef(&ref) == B_OK)
508 fSudokuView->SaveTo(ref, fExportFormat);
509 break;
512 case kMsgNewBlank:
513 _ResetStoredState();
514 fSudokuView->ClearAll();
515 break;
517 case kMsgStartAgain:
518 fSudokuView->ClearChanged();
519 break;
521 case kMsgMarkInvalid:
522 case kMsgMarkValidHints:
524 BMenuItem* item;
525 if (message->FindPointer("source", (void**)&item) != B_OK)
526 return;
528 uint32 flag = message->what == kMsgMarkInvalid
529 ? kMarkInvalid : kMarkValidHints;
531 item->SetMarked(!item->IsMarked());
532 if (item->IsMarked())
533 fSudokuView->SetHintFlags(fSudokuView->HintFlags() | flag);
534 else
535 fSudokuView->SetHintFlags(fSudokuView->HintFlags() & ~flag);
536 break;
539 case kMsgStoreState:
540 delete fStoredState;
541 fStoredState = new BMessage;
542 fSudokuView->Field()->Archive(fStoredState, true);
543 fRestoreStateItem->SetEnabled(true);
544 break;
546 case kMsgRestoreState:
548 if (fStoredState == NULL)
549 break;
551 SudokuField* field = new SudokuField(fStoredState);
552 fSudokuView->SetTo(field);
553 break;
556 case kMsgSudokuSolved:
558 BAlert* alert = new BAlert(B_TRANSLATE("Sudoku request"),
559 B_TRANSLATE("Sudoku solved - congratulations!\n"),
560 B_TRANSLATE("OK"), NULL, NULL,
561 B_WIDTH_AS_USUAL, B_IDEA_ALERT);
562 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
563 alert->Go();
564 break;
567 case B_OBSERVER_NOTICE_CHANGE:
569 int32 what;
570 if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK)
571 break;
573 if (what == kUndoRedoChanged) {
574 fUndoItem->SetEnabled(fSudokuView->CanUndo());
575 fRedoItem->SetEnabled(fSudokuView->CanRedo());
577 break;
580 default:
581 BWindow::MessageReceived(message);
582 break;
587 bool
588 SudokuWindow::QuitRequested()
590 _SaveSettings();
591 be_app->PostMessage(B_QUIT_REQUESTED);
592 return true;
596 int32
597 SudokuWindow::_Level() const
599 BMenuItem* item = fNewMenu->FindMarked();
600 if (item == NULL)
601 return 0;
603 BMessage* message = item->Message();
604 if (message == NULL)
605 return 0;
607 return message->FindInt32("level");
611 void
612 SudokuWindow::_SetLevel(int32 level)
614 for (int32 i = 0; i < fNewMenu->CountItems(); i++) {
615 BMenuItem* item = fNewMenu->ItemAt(i);
617 BMessage* message = item->Message();
618 if (message != NULL && message->HasInt32("level")
619 && message->FindInt32("level") == level)
620 item->SetMarked(true);
621 else
622 item->SetMarked(false);