1 /*
2 * Copyright 2016, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 * Augustin Cavalier <waddlesplash>
7 * kerwizzy
8 */
9
10
11 #include <AboutWindow.h>
12 #include <Application.h>
13 #include <Bitmap.h>
14 #include <BitmapStream.h>
15 #include <String.h>
16 #include <Catalog.h>
17 #include <Directory.h>
18 #include <File.h>
19 #include <FilePanel.h>
20 #include <FindDirectory.h>
21 #include <MenuBar.h>
22 #include <NodeInfo.h>
23 #include <Path.h>
24 #include <TranslationUtils.h>
25 #include <TranslatorRoster.h>
26 #include <LayoutBuilder.h>
27 #include <View.h>
28 #include <Window.h>
29 #include <Screen.h>
30 #include <ScrollView.h>
31
32 #include <algorithm>
33
34 #include "FractalEngine.h"
35
36 #undef B_TRANSLATION_CONTEXT
37 #define B_TRANSLATION_CONTEXT "MandelbrotWindow"
38
39 #define MANDELBROT_VIEW_REFRESH_FPS 10
40
41 // #pragma mark - FractalView
42
43 //#define TRACE_MANDELBROT_VIEW
44 #ifdef TRACE_MANDELBROT_VIEW
45 # include <stdio.h>
46 # define TRACE(x...) printf(x)
47 #else
48 # define TRACE(x...)
49 #endif
50
51
52 class FractalView : public BView {
53 public:
54 FractalView();
55 ~FractalView();
56
57 virtual void AttachedToWindow();
58 virtual void FrameResized(float, float);
59 virtual void Pulse();
60
61 virtual void MouseDown(BPoint where);
62 virtual void MouseMoved(BPoint where, uint32 mode, const BMessage*);
63 virtual void MouseUp(BPoint where);
64
65 virtual void MessageReceived(BMessage* msg);
66 virtual void Draw(BRect updateRect);
67
68 void ResetPosition();
69 void SetLocationFromFrame(double frameX, double frameY);
70 void ZoomFractal(double originX, double originY, double zoomFactor);
71 void ZoomFractalFromFrame(double frameOriginX, double frameOriginY,
72 double zoomFactor);
73 void ImportBitsAndInvalidate();
74 void RedrawFractal();
75 void UpdateSize();
76 void CreateDisplayBitmap(uint16 width, uint16 height);
77
78 void StartSave();
79 void WriteImage(entry_ref*, char*);
80 void EndSave();
81
82 FractalEngine* fFractalEngine;
83 enum {
84 MSG_START_SAVE,
85 MSG_WRITE_IMAGE
86 };
87
88 private:
89 BRect GetDragFrame();
90
91 BPoint fSelectStart;
92 BPoint fSelectEnd;
93 bool fSelecting;
94 uint32 fMouseButtons;
95
96 BBitmap* fDisplayBitmap;
97
98 double fLocationX;
99 double fLocationY;
100 double fSize;
101
102 BFilePanel* fSavePanel;
103
104 bool fSaving;
105 };
106
107
FractalView()108 FractalView::FractalView()
109 :
110 BView(NULL, B_WILL_DRAW | B_FRAME_EVENTS | B_PULSE_NEEDED),
111 fFractalEngine(NULL),
112 fSelecting(false),
113 fDisplayBitmap(NULL),
114 fLocationX(0),
115 fLocationY(0),
116 fSize(0.005),
117 fSavePanel(NULL),
118 fSaving(false)
119 {
120 SetHighColor(make_color(255, 255, 255, 255));
121 }
122
123
~FractalView()124 FractalView::~FractalView()
125 {
126 delete fDisplayBitmap;
127 }
128
129
ResetPosition()130 void FractalView::ResetPosition()
131 {
132 fLocationX = 0;
133 fLocationY = 0;
134 fSize = 0.005;
135 }
136
137
AttachedToWindow()138 void FractalView::AttachedToWindow()
139 {
140 fFractalEngine = new FractalEngine(this, Window());
141 fFractalEngine->Run();
142 TRACE("Attached to window\n");
143 }
144
145
FrameResized(float,float)146 void FractalView::FrameResized(float, float)
147 {
148 TRACE("Frame Resize\n");
149 UpdateSize();
150 }
151
152
UpdateSize()153 void FractalView::UpdateSize()
154 {
155 TRACE("Update Size\n");
156 BMessage msg(FractalEngine::MSG_RESIZE);
157
158 uint16 width = (uint16)Frame().Width();
159 uint16 height = (uint16)Frame().Height();
160
161 msg.AddUInt16("width", width);
162 msg.AddUInt16("height", height);
163
164 CreateDisplayBitmap(width, height);
165
166 msg.AddPointer("bitmap", fDisplayBitmap);
167
168 fFractalEngine->PostMessage(&msg); // Create the new buffer
169 }
170
171
CreateDisplayBitmap(uint16 width,uint16 height)172 void FractalView::CreateDisplayBitmap(uint16 width,uint16 height)
173 {
174 delete fDisplayBitmap;
175 fDisplayBitmap = NULL;
176 TRACE("width %u height %u\n",width,height);
177 BRect rect(0, 0, width, height);
178 fDisplayBitmap = new BBitmap(rect, B_RGB24);
179 }
180
181
GetDragFrame()182 BRect FractalView::GetDragFrame()
183 {
184 BRect dragZone = BRect(std::min(fSelectStart.x, fSelectEnd.x),
185 std::min(fSelectStart.y, fSelectEnd.y),
186 std::max(fSelectStart.x, fSelectEnd.x),
187 std::max(fSelectStart.y, fSelectEnd.y)),
188 frame = Frame();
189 float width = dragZone.Width(),
190 height = width * (frame.Height() / frame.Width());
191
192 float x1 = fSelectStart.x, y1 = fSelectStart.y, x2, y2;
193 if (fSelectStart.x < fSelectEnd.x)
194 x2 = x1 + width;
195 else
196 x2 = x1 - width;
197 if (fSelectStart.y < fSelectEnd.y)
198 y2 = y1 + height;
199 else
200 y2 = y1 - height;
201 return BRect(x1, y1, x2, y2);
202 }
203
204
MouseDown(BPoint where)205 void FractalView::MouseDown(BPoint where)
206 {
207 fSelecting = true;
208 fSelectStart = where;
209 fMouseButtons = 0;
210 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&fMouseButtons);
211 }
212
213
MouseMoved(BPoint where,uint32 mode,const BMessage *)214 void FractalView::MouseMoved(BPoint where, uint32 mode, const BMessage*)
215 {
216 if (fSelecting) {
217 fSelectEnd = where;
218 Invalidate();
219 }
220 }
221
222
SetLocationFromFrame(double frameX,double frameY)223 void FractalView::SetLocationFromFrame(double frameX,double frameY)
224 {
225 BRect frame = Frame();
226
227 fLocationX = ((frameX - frame.Width() / 2) * fSize + fLocationX);
228 fLocationY = ((frameY - frame.Height() / 2) * -fSize + fLocationY);
229 // -fSize because is in raster coordinates (y swapped)
230 }
231
232
ZoomFractalFromFrame(double frameOriginX,double frameOriginY,double zoomFactor)233 void FractalView::ZoomFractalFromFrame(double frameOriginX, double frameOriginY,
234 double zoomFactor)
235 {
236 BRect frame = Frame();
237
238 ZoomFractal((frameOriginX - frame.Width() / 2) * fSize + fLocationX,
239 (frameOriginY - frame.Height() / 2) * -fSize + fLocationY,
240 zoomFactor);
241 }
242
243
ZoomFractal(double originX,double originY,double zoomFactor)244 void FractalView::ZoomFractal(double originX, double originY, double zoomFactor)
245 {
246 double deltaX = originX - fLocationX;
247 double deltaY = originY - fLocationY;
248
249 TRACE("oX %g oY %g zoom %g\n", originX, originY, zoomFactor);
250
251 deltaX /= zoomFactor;
252 deltaY /= zoomFactor;
253
254 fLocationX = originX - deltaX;
255 fLocationY = originY - deltaY;
256 fSize /= zoomFactor;
257 }
258
259
MouseUp(BPoint where)260 void FractalView::MouseUp(BPoint where)
261 {
262 BRect frame = Frame();
263 fSelecting = false;
264 if (fabs(fSelectStart.x - where.x) > 4) {
265 fSelectEnd = where;
266 BRect dragFrame = GetDragFrame();
267 BPoint lt = dragFrame.LeftTop();
268 float centerX = lt.x + dragFrame.Width() / 2,
269 centerY = lt.y + dragFrame.Height() / 2;
270
271 SetLocationFromFrame(centerX, centerY);
272 fSize = std::fabs((dragFrame.Width() * fSize) / frame.Width());
273 } else {
274 if (fMouseButtons & B_PRIMARY_MOUSE_BUTTON) {
275 SetLocationFromFrame(where.x, where.y);
276 ZoomFractal(fLocationX, fLocationY, 2);
277 } else {
278 ZoomFractal(fLocationX, fLocationY, 0.5);
279 }
280 }
281 RedrawFractal();
282 }
283
284
MessageReceived(BMessage * msg)285 void FractalView::MessageReceived(BMessage* msg)
286 {
287 switch (msg->what) {
288 case B_MOUSE_WHEEL_CHANGED: {
289 float change = msg->FindFloat("be:wheel_delta_y");
290 BPoint where;
291 GetMouse(&where, NULL);
292 double zoomFactor;
293 if (change < 0)
294 zoomFactor = 3.0/2.0;
295 else
296 zoomFactor = 2.0/3.0;
297 ZoomFractalFromFrame(where.x, where.y, zoomFactor);
298
299 RedrawFractal();
300 break;
301 }
302
303 case FractalEngine::MSG_BUFFER_CREATED:
304 TRACE("Got buffer created msg.\n");
305
306 ImportBitsAndInvalidate();
307 RedrawFractal();
308 break;
309
310 case FractalEngine::MSG_RENDER_COMPLETE:
311 TRACE("Got render complete msg.\n");
312
313 Window()->SetPulseRate(0);
314 ImportBitsAndInvalidate();
315 break;
316
317 case MSG_WRITE_IMAGE: {
318 delete fSavePanel;
319 fSavePanel = NULL;
320
321 entry_ref dirRef;
322 char* name;
323 msg->FindRef("directory", &dirRef);
324 msg->FindString((const char*)"name", (const char**) &name);
325
326 WriteImage(&dirRef, name);
327 break;
328 }
329
330 case B_CANCEL:
331 // image is frozen before the FilePanel is shown
332 EndSave();
333 break;
334
335 default:
336 BView::MessageReceived(msg);
337 break;
338 }
339 }
340
341
Pulse()342 void FractalView::Pulse()
343 {
344 ImportBitsAndInvalidate();
345 }
346
347
ImportBitsAndInvalidate()348 void FractalView::ImportBitsAndInvalidate()
349 {
350 if (fSaving) {
351 TRACE("Not importing bits because saving.\n");
352 return;
353 }
354 TRACE("Importing bits...\n");
355
356 fFractalEngine->WriteToBitmap(fDisplayBitmap);
357 Invalidate();
358 }
359
360
RedrawFractal()361 void FractalView::RedrawFractal()
362 {
363 Window()->SetPulseRate(1000000 / MANDELBROT_VIEW_REFRESH_FPS);
364 BMessage message(FractalEngine::MSG_RENDER);
365 message.AddDouble("locationX", fLocationX);
366 message.AddDouble("locationY", fLocationY);
367 message.AddDouble("size", fSize);
368 fFractalEngine->PostMessage(&message);
369 }
370
371
Draw(BRect updateRect)372 void FractalView::Draw(BRect updateRect)
373 {
374 DrawBitmap(fDisplayBitmap, updateRect, updateRect);
375 if (fSelecting)
376 StrokeRect(GetDragFrame());
377 }
378
379
StartSave()380 void FractalView::StartSave() {
381 TRACE("Got to start save\n");
382 fSaving = true;
383
384 BMessenger messenger(this);
385 BMessage message(MSG_WRITE_IMAGE);
386 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false,
387 &message);
388 BString* filename = new BString();
389 filename->SetToFormat("%g-%g-%g.png", fLocationX, fLocationY, fSize);
390
391 fSavePanel->SetSaveText(filename->String());
392 fSavePanel->Show();
393 }
394
395
WriteImage(entry_ref * dirRef,char * name)396 void FractalView::WriteImage(entry_ref* dirRef, char* name)
397 {
398 TRACE("Got to write save handler\n");
399
400 BFile file;
401 BDirectory parentDir(dirRef);
402 parentDir.CreateFile(name, &file);
403
404 // Write the screenshot bitmap to the file
405 BBitmapStream stream(fDisplayBitmap);
406 BTranslatorRoster* roster = BTranslatorRoster::Default();
407 roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT,
408 B_TRANSLATOR_BITMAP);
409
410 BNodeInfo info(&file);
411 if (info.InitCheck() == B_OK)
412 info.SetType("image/png");
413
414 BBitmap* bitmap;
415 stream.DetachBitmap(&bitmap);
416 // The stream takes over ownership of the bitmap
417
418 // unfreeze the image, image was frozen before invoke of FilePanel
419 EndSave();
420 }
421
422
EndSave()423 void FractalView::EndSave()
424 {
425 fSaving = false;
426 ImportBitsAndInvalidate();
427 }
428
429
430 // #pragma mark - MandelbrotWindow
431
432
433 class MandelbrotWindow : public BWindow
434 {
435 public:
436 enum {
437 MSG_MANDELBROT_SET = 'MndW',
438 MSG_BURNINGSHIP_SET,
439 MSG_TRICORN_SET,
440 MSG_JULIA_SET,
441 MSG_ORBITTRAP_SET,
442 MSG_MULTIBROT_SET,
443
444 MSG_ROYAL_PALETTE,
445 MSG_DEEPFROST_PALETTE,
446 MSG_FROST_PALETTE,
447 MSG_FIRE_PALETTE,
448 MSG_MIDNIGHT_PALETTE,
449 MSG_GRASSLAND_PALETTE,
450 MSG_LIGHTNING_PALETTE,
451 MSG_SPRING_PALETTE,
452 MSG_HIGHCONTRAST_PALETTE,
453
454 MSG_ITER_128,
455 MSG_ITER_512,
456 MSG_ITER_1024,
457 MSG_ITER_4096,
458 MSG_ITER_8192,
459 MSG_ITER_12288,
460 MSG_ITER_16384,
461
462 MSG_SUBSAMPLING_1,
463 MSG_SUBSAMPLING_2,
464 MSG_SUBSAMPLING_3,
465 MSG_SUBSAMPLING_4,
466
467 MSG_TOGGLE_FULLSCREEN
468 };
469 MandelbrotWindow(BRect frame);
~MandelbrotWindow()470 ~MandelbrotWindow() {}
471
472 void ToggleFullscreen();
473
474 virtual void DispatchMessage(BMessage* message, BHandler* target);
475 virtual void MessageReceived(BMessage* msg);
476 virtual bool QuitRequested();
477
478 bool fFullScreen;
479
480 BMenuBar* fMenuBar;
481 BRect fWindowFrame;
482
483 private:
484 FractalView* fFractalView;
485 };
486
487
MandelbrotWindow(BRect frame)488 MandelbrotWindow::MandelbrotWindow(BRect frame)
489 :
490 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Mandelbrot"), B_TITLED_WINDOW_LOOK,
491 B_NORMAL_WINDOW_FEEL, 0L),
492 fFractalView(new FractalView)
493 {
494 fFullScreen = false;
495 fMenuBar = new BMenuBar("MenuBar");
496 BMenu* setMenu;
497 BMenu* paletteMenu;
498 BMenu* iterMenu;
499 BMenu* subsamplingMenu;
500 BLayoutBuilder::Menu<>(fMenuBar)
501 .AddMenu(B_TRANSLATE("File"))
502 .AddItem(B_TRANSLATE("Save as image" B_UTF8_ELLIPSIS),
503 FractalView::MSG_START_SAVE, 'S')
504 .AddSeparator()
505 .AddItem(B_TRANSLATE("About"), B_ABOUT_REQUESTED)
506 .AddItem(B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q')
507 .End()
508 .AddMenu(B_TRANSLATE("View"))
509 .AddItem(B_TRANSLATE("Full screen"), MSG_TOGGLE_FULLSCREEN,
510 B_RETURN)
511 .End()
512 .AddMenu(B_TRANSLATE("Set"))
513 .GetMenu(setMenu)
514 .AddItem(B_TRANSLATE("Mandelbrot"), MSG_MANDELBROT_SET)
515 .AddItem(B_TRANSLATE("Burning Ship"), MSG_BURNINGSHIP_SET)
516 .AddItem(B_TRANSLATE("Tricorn"), MSG_TRICORN_SET)
517 .AddItem(B_TRANSLATE("Julia"), MSG_JULIA_SET)
518 .AddItem(B_TRANSLATE("Orbit Trap"), MSG_ORBITTRAP_SET)
519 .AddItem(B_TRANSLATE("Multibrot"), MSG_MULTIBROT_SET)
520 .End()
521 .AddMenu(B_TRANSLATE("Palette"))
522 .GetMenu(paletteMenu)
523 .AddItem(B_TRANSLATE("Royal"), MSG_ROYAL_PALETTE)
524 .AddItem(B_TRANSLATE("Deepfrost"), MSG_DEEPFROST_PALETTE)
525 .AddItem(B_TRANSLATE("Frost"), MSG_FROST_PALETTE)
526 .AddItem(B_TRANSLATE("Fire"), MSG_FIRE_PALETTE)
527 .AddItem(B_TRANSLATE("Midnight"), MSG_MIDNIGHT_PALETTE)
528 .AddItem(B_TRANSLATE("Grassland"), MSG_GRASSLAND_PALETTE)
529 .AddItem(B_TRANSLATE("Lightning"), MSG_LIGHTNING_PALETTE)
530 .AddItem(B_TRANSLATE("Spring"), MSG_SPRING_PALETTE)
531 .AddItem(B_TRANSLATE("High contrast"), MSG_HIGHCONTRAST_PALETTE)
532 .End()
533 .AddMenu(B_TRANSLATE("Iterations"))
534 .GetMenu(iterMenu)
535 .AddItem("128", MSG_ITER_128)
536 .AddItem("512", MSG_ITER_512)
537 .AddItem("1024", MSG_ITER_1024)
538 .AddItem("4096", MSG_ITER_4096)
539 .AddItem("8192", MSG_ITER_8192)
540 .AddItem("12288", MSG_ITER_12288)
541 .AddItem("16384", MSG_ITER_16384)
542 .End()
543 .AddMenu(B_TRANSLATE("Subsampling"))
544 .GetMenu(subsamplingMenu)
545 .AddItem(B_TRANSLATE("1 (none)"), MSG_SUBSAMPLING_1)
546 .AddItem("4", MSG_SUBSAMPLING_2)
547 .AddItem("9", MSG_SUBSAMPLING_3)
548 .AddItem("16", MSG_SUBSAMPLING_4)
549 .End()
550 .End();
551 setMenu->SetRadioMode(true);
552 setMenu->FindItem(MSG_MANDELBROT_SET)->SetMarked(true);
553 paletteMenu->SetRadioMode(true);
554 paletteMenu->FindItem(MSG_ROYAL_PALETTE)->SetMarked(true);
555 iterMenu->SetRadioMode(true);
556 iterMenu->FindItem(MSG_ITER_1024)->SetMarked(true);
557 subsamplingMenu->SetRadioMode(true);
558 subsamplingMenu->FindItem(MSG_SUBSAMPLING_2)->SetMarked(true);
559
560 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
561 .SetInsets(0)
562 .Add(fMenuBar)
563 .Add(fFractalView)
564 .End();
565 }
566
567 void
ToggleFullscreen()568 MandelbrotWindow::ToggleFullscreen() {
569 BRect frame;
570 fFullScreen = !fFullScreen;
571 if (fFullScreen) {
572 TRACE("Enabling fullscreen\n");
573 BScreen screen;
574 fWindowFrame = Frame();
575 frame = screen.Frame();
576 frame.top -= fMenuBar->Bounds().Height() + 1;
577
578 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE);
579
580 Activate();
581 // make the window frontmost
582 } else {
583 TRACE("Disabling fullscreen\n");
584 frame = fWindowFrame;
585
586 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
587 }
588
589 MoveTo(frame.left, frame.top);
590 ResizeTo(frame.Width(), frame.Height());
591
592 Layout(false);
593 }
594
595
596 #define HANDLE_SET(uiwhat, id) \
597 case uiwhat: { \
598 BMessage msg(FractalEngine::MSG_CHANGE_SET); \
599 msg.AddUInt8("set", id); \
600 fFractalView->fFractalEngine->PostMessage(&msg); \
601 fFractalView->ResetPosition(); \
602 fFractalView->RedrawFractal(); \
603 break; \
604 }
605 #define HANDLE_PALETTE(uiwhat, id) \
606 case uiwhat: { \
607 BMessage msg(FractalEngine::MSG_SET_PALETTE); \
608 msg.AddUInt8("palette", id); \
609 fFractalView->fFractalEngine->PostMessage(&msg); \
610 fFractalView->RedrawFractal(); \
611 break; \
612 }
613 #define HANDLE_ITER(uiwhat, id) \
614 case uiwhat: { \
615 BMessage msg(FractalEngine::MSG_SET_ITERATIONS); \
616 msg.AddUInt16("iterations", id); \
617 fFractalView->fFractalEngine->PostMessage(&msg); \
618 fFractalView->RedrawFractal(); \
619 break; \
620 }
621 #define HANDLE_SUBSAMPLING(uiwhat, id) \
622 case uiwhat: { \
623 BMessage msg(FractalEngine::MSG_SET_SUBSAMPLING); \
624 msg.AddUInt8("subsampling", id); \
625 fFractalView->fFractalEngine->PostMessage(&msg); \
626 fFractalView->RedrawFractal(); \
627 break; \
628 }
629
630
631 void
DispatchMessage(BMessage * message,BHandler * target)632 MandelbrotWindow::DispatchMessage(BMessage* message, BHandler* target)
633 {
634 const char* bytes;
635 int32 modifierKeys;
636 if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)
637 && message->FindString("bytes", &bytes) == B_OK
638 && message->FindInt32("modifiers", &modifierKeys) == B_OK) {
639 if (bytes[0] == B_FUNCTION_KEY) {
640 // Matches WebPositive fullscreen key (F11)
641 int32 key;
642 if (message->FindInt32("key", &key) == B_OK) {
643 switch (key) {
644 case B_F11_KEY: {
645 ToggleFullscreen();
646 break;
647 }
648
649 default:
650 break;
651 }
652 }
653 }
654 }
655
656 BWindow::DispatchMessage(message, target);
657 }
658
659
660 void
MessageReceived(BMessage * msg)661 MandelbrotWindow::MessageReceived(BMessage* msg)
662 {
663 switch (msg->what) {
664 HANDLE_SET(MSG_MANDELBROT_SET, 0)
665 HANDLE_SET(MSG_BURNINGSHIP_SET, 1)
666 HANDLE_SET(MSG_TRICORN_SET, 2)
667 HANDLE_SET(MSG_JULIA_SET, 3)
668 HANDLE_SET(MSG_ORBITTRAP_SET, 4)
669 HANDLE_SET(MSG_MULTIBROT_SET, 5)
670
671 HANDLE_PALETTE(MSG_ROYAL_PALETTE, 0)
672 HANDLE_PALETTE(MSG_DEEPFROST_PALETTE, 1)
673 HANDLE_PALETTE(MSG_FROST_PALETTE, 2)
674 HANDLE_PALETTE(MSG_FIRE_PALETTE, 3)
675 HANDLE_PALETTE(MSG_MIDNIGHT_PALETTE, 4)
676 HANDLE_PALETTE(MSG_GRASSLAND_PALETTE, 5)
677 HANDLE_PALETTE(MSG_LIGHTNING_PALETTE, 6)
678 HANDLE_PALETTE(MSG_SPRING_PALETTE, 7)
679 HANDLE_PALETTE(MSG_HIGHCONTRAST_PALETTE, 8)
680
681 HANDLE_ITER(MSG_ITER_128, 128)
682 HANDLE_ITER(MSG_ITER_512, 512)
683 HANDLE_ITER(MSG_ITER_1024, 1024)
684 HANDLE_ITER(MSG_ITER_4096, 4096)
685 HANDLE_ITER(MSG_ITER_8192, 8192)
686 HANDLE_ITER(MSG_ITER_12288, 12288)
687 HANDLE_ITER(MSG_ITER_16384, 16384)
688
689 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_1, 1)
690 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_2, 2)
691 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_3, 3)
692 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_4, 4)
693
694 case FractalView::MSG_START_SAVE: {
695 fFractalView->StartSave();
696 break;
697 }
698
699 case MSG_TOGGLE_FULLSCREEN:
700 ToggleFullscreen();
701 break;
702
703 case B_ABOUT_REQUESTED: {
704 BAboutWindow* wind = new BAboutWindow("Mandelbrot",
705 "application/x-vnd.Haiku-Mandelbrot");
706
707 const char* authors[] = {
708 "Augustin Cavalier <waddlesplash>",
709 "kerwizzy",
710 NULL
711 };
712 wind->AddCopyright(2016, "Haiku, Inc.");
713 wind->AddAuthors(authors);
714 wind->Show();
715 break;
716 }
717
718 case B_KEY_DOWN: {
719 int8 val;
720 if (msg->FindInt8("byte", &val) == B_OK && val == B_ESCAPE
721 && fFullScreen)
722 ToggleFullscreen();
723 break;
724 }
725
726 default:
727 BWindow::MessageReceived(msg);
728 break;
729 }
730 }
731 #undef HANDLE_SET
732 #undef HANDLE_PALETTE
733 #undef HANDLE_ITER
734 #undef HANDLE_SUBSAMPLING
735
736
737 bool
QuitRequested()738 MandelbrotWindow::QuitRequested()
739 {
740 if (BWindow::QuitRequested()) {
741 be_app->PostMessage(B_QUIT_REQUESTED);
742 return true;
743 }
744 return false;
745 }
746
747
748 // #pragma mark - MandelbrotApp
749
750
751 class MandelbrotApp : public BApplication
752 {
753 public:
MandelbrotApp()754 MandelbrotApp()
755 : BApplication("application/x-vnd.Haiku-Mandelbrot") {}
756
757 void ReadyToRun();
QuitRequested()758 bool QuitRequested() { return true; }
759 };
760
761
762 void
ReadyToRun()763 MandelbrotApp::ReadyToRun()
764 {
765 MandelbrotWindow* wind = new MandelbrotWindow(BRect(0, 0, 640, 480));
766 wind->CenterOnScreen();
767 wind->Show();
768 }
769
770
771 int
main(int argc,char * argv[])772 main(int argc, char* argv[])
773 {
774 MandelbrotApp().Run();
775 return 0;
776 }
777