diff --git a/src/apps/mandelbrot/FractalEngine.cpp b/src/apps/mandelbrot/FractalEngine.cpp index e0927c0549..32e0dd28e0 100644 --- a/src/apps/mandelbrot/FractalEngine.cpp +++ b/src/apps/mandelbrot/FractalEngine.cpp @@ -221,7 +221,7 @@ void FractalEngine::Render(double locationX, double locationY, double size) fLocationY = locationY; fSize = size; - TRACE("Location: %g;%g;%g\n", fLocationX, fLocationY, fSize); + TRACE("Location (%%.100g): %.100g;%.100g;%.100g\n", fLocationX, fLocationY, fSize); for (uint8 i = 0; i < fThreadCount; i++) { release_sem(fRenderSem); diff --git a/src/apps/mandelbrot/Jamfile b/src/apps/mandelbrot/Jamfile index 690ddce62d..2bf6c7dd7c 100644 --- a/src/apps/mandelbrot/Jamfile +++ b/src/apps/mandelbrot/Jamfile @@ -7,7 +7,7 @@ UsePrivateHeaders interface shared system ; Application Mandelbrot : Mandelbrot.cpp FractalEngine.cpp - : be [ TargetLibsupc++ ] localestub + : be [ TargetLibsupc++ ] localestub tracker translation : Mandelbrot.rdef ; diff --git a/src/apps/mandelbrot/Mandelbrot.cpp b/src/apps/mandelbrot/Mandelbrot.cpp index 9fac754046..ede3da1832 100644 --- a/src/apps/mandelbrot/Mandelbrot.cpp +++ b/src/apps/mandelbrot/Mandelbrot.cpp @@ -11,11 +11,23 @@ #include #include #include +#include +#include #include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include #include +#include +#include #include @@ -62,7 +74,16 @@ public: void RedrawFractal(); void UpdateSize(); void CreateDisplayBitmap(uint16 width, uint16 height); + + void StartSave(); + void WriteImage(entry_ref*, char*); + void EndSave(); + FractalEngine* fFractalEngine; + enum { + MSG_START_SAVE, + MSG_WRITE_IMAGE + }; private: BRect GetDragFrame(); @@ -77,6 +98,10 @@ private: double fLocationX; double fLocationY; double fSize; + + BFilePanel* fSavePanel; + + bool fSaving; }; @@ -88,7 +113,9 @@ FractalView::FractalView() fDisplayBitmap(NULL), fLocationX(0), fLocationY(0), - fSize(0.005) + fSize(0.005), + fSavePanel(NULL), + fSaving(false) { SetHighColor(make_color(255, 255, 255, 255)); } @@ -128,15 +155,15 @@ void FractalView::UpdateSize() TRACE("Update Size\n"); BMessage msg(FractalEngine::MSG_RESIZE); - uint16 width = (uint16)Frame().Width()+1; - uint16 height = (uint16)Frame().Height()+1; + uint16 width = (uint16)Frame().Width(); + uint16 height = (uint16)Frame().Height(); msg.AddUInt16("width", width); msg.AddUInt16("height", height); - CreateDisplayBitmap(width,height); + CreateDisplayBitmap(width, height); - msg.AddPointer("bitmap",fDisplayBitmap); + msg.AddPointer("bitmap", fDisplayBitmap); fFractalEngine->PostMessage(&msg); // Create the new buffer } @@ -287,6 +314,24 @@ void FractalView::MessageReceived(BMessage* msg) ImportBitsAndInvalidate(); break; + case MSG_WRITE_IMAGE: { + delete fSavePanel; + fSavePanel = NULL; + + entry_ref dirRef; + char* name; + msg->FindRef("directory", &dirRef); + msg->FindString((const char*)"name", (const char**) &name); + + WriteImage(&dirRef, name); + break; + } + + case B_CANCEL: + // image is frozen before the FilePanel is shown + EndSave(); + break; + default: BView::MessageReceived(msg); break; @@ -302,6 +347,10 @@ void FractalView::Pulse() void FractalView::ImportBitsAndInvalidate() { + if (fSaving) { + TRACE("Not importing bits because saving.\n"); + return; + } TRACE("Importing bits...\n"); fFractalEngine->WriteToBitmap(fDisplayBitmap); @@ -328,6 +377,56 @@ void FractalView::Draw(BRect updateRect) } +void FractalView::StartSave() { + TRACE("Got to start save\n"); + fSaving = true; + + BMessenger messenger(this); + BMessage message(MSG_WRITE_IMAGE); + fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false, + &message); + BString* filename = new BString(); + filename->SetToFormat("%g-%g-%g.png", fLocationX, fLocationY, fSize); + + fSavePanel->SetSaveText(filename->String()); + fSavePanel->Show(); +} + + +void FractalView::WriteImage(entry_ref* dirRef, char* name) +{ + TRACE("Got to write save handler\n"); + + BFile file; + BDirectory parentDir(dirRef); + parentDir.CreateFile(name, &file); + + // Write the screenshot bitmap to the file + BBitmapStream stream(fDisplayBitmap); + BTranslatorRoster* roster = BTranslatorRoster::Default(); + roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT, + B_TRANSLATOR_BITMAP); + + BNodeInfo info(&file); + if (info.InitCheck() == B_OK) + info.SetType("image/png"); + + BBitmap* bitmap; + stream.DetachBitmap(&bitmap); + // The stream takes over ownership of the bitmap + + // unfreeze the image, image was frozen before invoke of FilePanel + EndSave(); +} + + +void FractalView::EndSave() +{ + fSaving = false; + ImportBitsAndInvalidate(); +} + + // #pragma mark - MandelbrotWindow @@ -363,14 +462,24 @@ public: MSG_SUBSAMPLING_1, MSG_SUBSAMPLING_2, MSG_SUBSAMPLING_3, - MSG_SUBSAMPLING_4 + MSG_SUBSAMPLING_4, + + MSG_TOGGLE_FULLSCREEN }; MandelbrotWindow(BRect frame); ~MandelbrotWindow() {} + void ToggleFullscreen(); + + virtual void DispatchMessage(BMessage* message, BHandler* target); virtual void MessageReceived(BMessage* msg); virtual bool QuitRequested(); + bool fFullScreen; + + BMenuBar* fMenuBar; + BRect fWindowFrame; + private: FractalView* fFractalView; }; @@ -382,16 +491,24 @@ MandelbrotWindow::MandelbrotWindow(BRect frame) B_NORMAL_WINDOW_FEEL, 0L), fFractalView(new FractalView) { - BMenuBar* menuBar = new BMenuBar("MenuBar"); + fFullScreen = false; + fMenuBar = new BMenuBar("MenuBar"); BMenu* setMenu; BMenu* paletteMenu; BMenu* iterMenu; BMenu* subsamplingMenu; - BLayoutBuilder::Menu<>(menuBar) + BLayoutBuilder::Menu<>(fMenuBar) .AddMenu(B_TRANSLATE("File")) + .AddItem(B_TRANSLATE("Save as image" B_UTF8_ELLIPSIS), + FractalView::MSG_START_SAVE, 'S') + .AddSeparator() .AddItem(B_TRANSLATE("About"), B_ABOUT_REQUESTED) .AddItem(B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q') .End() + .AddMenu(B_TRANSLATE("View")) + .AddItem(B_TRANSLATE("Full screen"), MSG_TOGGLE_FULLSCREEN, + B_RETURN) + .End() .AddMenu(B_TRANSLATE("Set")) .GetMenu(setMenu) .AddItem(B_TRANSLATE("Mandelbrot"), MSG_MANDELBROT_SET) @@ -442,11 +559,39 @@ MandelbrotWindow::MandelbrotWindow(BRect frame) BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .SetInsets(0) - .Add(menuBar) + .Add(fMenuBar) .Add(fFractalView) .End(); } +void +MandelbrotWindow::ToggleFullscreen() { + BRect frame; + fFullScreen = !fFullScreen; + if (fFullScreen) { + TRACE("Enabling fullscreen\n"); + BScreen screen; + fWindowFrame = Frame(); + frame = screen.Frame(); + frame.top -= fMenuBar->Bounds().Height() + 1; + + SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE); + + Activate(); + // make the window frontmost + } else { + TRACE("Disabling fullscreen\n"); + frame = fWindowFrame; + + SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); + } + + MoveTo(frame.left, frame.top); + ResizeTo(frame.Width(), frame.Height()); + + Layout(false); +} + #define HANDLE_SET(uiwhat, id) \ case uiwhat: { \ @@ -481,6 +626,37 @@ MandelbrotWindow::MandelbrotWindow(BRect frame) fFractalView->RedrawFractal(); \ break; \ } + + +void +MandelbrotWindow::DispatchMessage(BMessage* message, BHandler* target) +{ + const char* bytes; + int32 modifierKeys; + if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) + && message->FindString("bytes", &bytes) == B_OK + && message->FindInt32("modifiers", &modifierKeys) == B_OK) { + if (bytes[0] == B_FUNCTION_KEY) { + // Matches WebPositive fullscreen key (F11) + int32 key; + if (message->FindInt32("key", &key) == B_OK) { + switch (key) { + case B_F11_KEY: { + ToggleFullscreen(); + break; + } + + default: + break; + } + } + } + } + + BWindow::DispatchMessage(message, target); +} + + void MandelbrotWindow::MessageReceived(BMessage* msg) { @@ -515,6 +691,15 @@ MandelbrotWindow::MessageReceived(BMessage* msg) HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_3, 3) HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_4, 4) + case FractalView::MSG_START_SAVE: { + fFractalView->StartSave(); + break; + } + + case MSG_TOGGLE_FULLSCREEN: + ToggleFullscreen(); + break; + case B_ABOUT_REQUESTED: { BAboutWindow* wind = new BAboutWindow("Mandelbrot", "application/x-vnd.Haiku-Mandelbrot"); @@ -530,6 +715,14 @@ MandelbrotWindow::MessageReceived(BMessage* msg) break; } + case B_KEY_DOWN: { + int8 val; + if (msg->FindInt8("byte", &val) == B_OK && val == B_ESCAPE + && fFullScreen) + ToggleFullscreen(); + break; + } + default: BWindow::MessageReceived(msg); break;