1d5ef985eSStephan Aßmus /* 2d5ef985eSStephan Aßmus * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>. 339f99591SJulian Harnath * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>. 4d5ef985eSStephan Aßmus * All rights reserved. Distributed under the terms of the MIT License. 5d5ef985eSStephan Aßmus */ 6d5ef985eSStephan Aßmus 7d5ef985eSStephan Aßmus #include "ScreenshotWindow.h" 8d5ef985eSStephan Aßmus 9d5ef985eSStephan Aßmus #include <algorithm> 10d5ef985eSStephan Aßmus #include <stdio.h> 11d5ef985eSStephan Aßmus 12d5ef985eSStephan Aßmus #include <Autolock.h> 13d5ef985eSStephan Aßmus #include <Catalog.h> 14d5ef985eSStephan Aßmus #include <LayoutBuilder.h> 153ca9d5e9SJulian Harnath #include <MessageRunner.h> 16c210060fSJulian Harnath #include <StringView.h> 17d5ef985eSStephan Aßmus 18c210060fSJulian Harnath #include "BarberPole.h" 19d5ef985eSStephan Aßmus #include "BitmapView.h" 20*a9edb9bfSAndrew Lindesay #include "HaikuDepotConstants.h" 21d5ef985eSStephan Aßmus #include "WebAppInterface.h" 22d5ef985eSStephan Aßmus 23d5ef985eSStephan Aßmus 24d5ef985eSStephan Aßmus #undef B_TRANSLATION_CONTEXT 25d5ef985eSStephan Aßmus #define B_TRANSLATION_CONTEXT "ScreenshotWindow" 26d5ef985eSStephan Aßmus 27d5ef985eSStephan Aßmus 2839f99591SJulian Harnath static const rgb_color kBackgroundColor = { 51, 102, 152, 255 }; 2939f99591SJulian Harnath // Drawn as a border around the screenshots and also what's behind their 3039f99591SJulian Harnath // transparent regions 3139f99591SJulian Harnath 32622e144fSJulian Harnath static BitmapRef sNextButtonIcon( 33*a9edb9bfSAndrew Lindesay new(std::nothrow) SharedBitmap(RSRC_ARROW_LEFT), true); 34622e144fSJulian Harnath static BitmapRef sPreviousButtonIcon( 35*a9edb9bfSAndrew Lindesay new(std::nothrow) SharedBitmap(RSRC_ARROW_RIGHT), true); 36622e144fSJulian Harnath 3739f99591SJulian Harnath 38d5ef985eSStephan Aßmus ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame) 39d5ef985eSStephan Aßmus : 40d5ef985eSStephan Aßmus BWindow(frame, B_TRANSLATE("Screenshot"), 41d5ef985eSStephan Aßmus B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL, 42d5ef985eSStephan Aßmus B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 433ca9d5e9SJulian Harnath fBarberPoleShown(false), 44d5ef985eSStephan Aßmus fDownloadPending(false), 45d5ef985eSStephan Aßmus fWorkerThread(-1) 46d5ef985eSStephan Aßmus { 47d5ef985eSStephan Aßmus AddToSubset(parent); 48d5ef985eSStephan Aßmus 49c210060fSJulian Harnath atomic_set(&fCurrentScreenshotIndex, 0); 50c210060fSJulian Harnath 51c210060fSJulian Harnath fBarberPole = new BarberPole("barber pole"); 523ca9d5e9SJulian Harnath fBarberPole->SetExplicitMaxSize(BSize(100, B_SIZE_UNLIMITED)); 533ca9d5e9SJulian Harnath fBarberPole->Hide(); 54c210060fSJulian Harnath 55c210060fSJulian Harnath fIndexView = new BStringView("screenshot index", NULL); 56c210060fSJulian Harnath 57c210060fSJulian Harnath fToolBar = new BToolBar(); 58622e144fSJulian Harnath fToolBar->AddAction(MSG_PREVIOUS_SCREENSHOT, this, 59622e144fSJulian Harnath sNextButtonIcon->Bitmap(SharedBitmap::SIZE_22), 60622e144fSJulian Harnath NULL, NULL); 61622e144fSJulian Harnath fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this, 62622e144fSJulian Harnath sPreviousButtonIcon->Bitmap(SharedBitmap::SIZE_22), 63622e144fSJulian Harnath NULL, NULL); 64c210060fSJulian Harnath fToolBar->AddView(fIndexView); 65c210060fSJulian Harnath fToolBar->AddGlue(); 66c210060fSJulian Harnath fToolBar->AddView(fBarberPole); 67c210060fSJulian Harnath 68d5ef985eSStephan Aßmus fScreenshotView = new BitmapView("screenshot view"); 69d5ef985eSStephan Aßmus fScreenshotView->SetExplicitMaxSize( 70d5ef985eSStephan Aßmus BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 71416c1160SJulian Harnath fScreenshotView->SetScaleBitmap(false); 72d5ef985eSStephan Aßmus 732afa3f3bSStephan Aßmus BGroupView* groupView = new BGroupView(B_VERTICAL); 7439f99591SJulian Harnath groupView->SetViewColor(kBackgroundColor); 752afa3f3bSStephan Aßmus 76d5ef985eSStephan Aßmus // Build layout 77c210060fSJulian Harnath BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 78622e144fSJulian Harnath .SetInsets(0, 3, 0, 0) 79c210060fSJulian Harnath .Add(fToolBar) 80622e144fSJulian Harnath .AddStrut(3) 812afa3f3bSStephan Aßmus .AddGroup(groupView) 82d5ef985eSStephan Aßmus .Add(fScreenshotView) 83d5ef985eSStephan Aßmus .SetInsets(B_USE_WINDOW_INSETS) 842afa3f3bSStephan Aßmus .End() 85d5ef985eSStephan Aßmus ; 86d5ef985eSStephan Aßmus 8739f99591SJulian Harnath fScreenshotView->SetLowColor(kBackgroundColor); 8839f99591SJulian Harnath // Set after attaching all views to prevent it from being overriden 8939f99591SJulian Harnath // again by BitmapView::AllAttached() 9039f99591SJulian Harnath 912afa3f3bSStephan Aßmus CenterOnScreen(); 92d5ef985eSStephan Aßmus } 93d5ef985eSStephan Aßmus 94d5ef985eSStephan Aßmus 95d5ef985eSStephan Aßmus ScreenshotWindow::~ScreenshotWindow() 96d5ef985eSStephan Aßmus { 97d5ef985eSStephan Aßmus BAutolock locker(&fLock); 98d5ef985eSStephan Aßmus 99d5ef985eSStephan Aßmus if (fWorkerThread >= 0) 100d5ef985eSStephan Aßmus wait_for_thread(fWorkerThread, NULL); 101d5ef985eSStephan Aßmus } 102d5ef985eSStephan Aßmus 103d5ef985eSStephan Aßmus 104d5ef985eSStephan Aßmus bool 105d5ef985eSStephan Aßmus ScreenshotWindow::QuitRequested() 106d5ef985eSStephan Aßmus { 107d5ef985eSStephan Aßmus if (fOnCloseTarget.IsValid() && fOnCloseMessage.what != 0) 108d5ef985eSStephan Aßmus fOnCloseTarget.SendMessage(&fOnCloseMessage); 109d5ef985eSStephan Aßmus 110d5ef985eSStephan Aßmus Hide(); 111d5ef985eSStephan Aßmus return false; 112d5ef985eSStephan Aßmus } 113d5ef985eSStephan Aßmus 114d5ef985eSStephan Aßmus 115d5ef985eSStephan Aßmus void 116d5ef985eSStephan Aßmus ScreenshotWindow::MessageReceived(BMessage* message) 117d5ef985eSStephan Aßmus { 118d5ef985eSStephan Aßmus switch (message->what) { 119c210060fSJulian Harnath case MSG_NEXT_SCREENSHOT: 120c210060fSJulian Harnath { 121c210060fSJulian Harnath atomic_add(&fCurrentScreenshotIndex, 1); 122c210060fSJulian Harnath _UpdateToolBar(); 123c210060fSJulian Harnath _DownloadScreenshot(); 124c210060fSJulian Harnath break; 125c210060fSJulian Harnath } 126c210060fSJulian Harnath 127c210060fSJulian Harnath case MSG_PREVIOUS_SCREENSHOT: 128c210060fSJulian Harnath atomic_add(&fCurrentScreenshotIndex, -1); 129c210060fSJulian Harnath _UpdateToolBar(); 130c210060fSJulian Harnath _DownloadScreenshot(); 131c210060fSJulian Harnath break; 132c210060fSJulian Harnath 133c210060fSJulian Harnath case MSG_DOWNLOAD_START: 1343ca9d5e9SJulian Harnath if (!fBarberPoleShown) { 135c210060fSJulian Harnath fBarberPole->Start(); 1363ca9d5e9SJulian Harnath fBarberPole->Show(); 1373ca9d5e9SJulian Harnath fBarberPoleShown = true; 1383ca9d5e9SJulian Harnath } 139c210060fSJulian Harnath break; 140c210060fSJulian Harnath 141c210060fSJulian Harnath case MSG_DOWNLOAD_STOP: 1423ca9d5e9SJulian Harnath if (fBarberPoleShown) { 1433ca9d5e9SJulian Harnath fBarberPole->Hide(); 144c210060fSJulian Harnath fBarberPole->Stop(); 1453ca9d5e9SJulian Harnath fBarberPoleShown = true; 1463ca9d5e9SJulian Harnath } 147c210060fSJulian Harnath break; 148c210060fSJulian Harnath 149d5ef985eSStephan Aßmus default: 150d5ef985eSStephan Aßmus BWindow::MessageReceived(message); 151d5ef985eSStephan Aßmus break; 152d5ef985eSStephan Aßmus } 153d5ef985eSStephan Aßmus } 154d5ef985eSStephan Aßmus 155d5ef985eSStephan Aßmus 156d5ef985eSStephan Aßmus void 157d5ef985eSStephan Aßmus ScreenshotWindow::SetOnCloseMessage( 158d5ef985eSStephan Aßmus const BMessenger& messenger, const BMessage& message) 159d5ef985eSStephan Aßmus { 160d5ef985eSStephan Aßmus fOnCloseTarget = messenger; 161d5ef985eSStephan Aßmus fOnCloseMessage = message; 162d5ef985eSStephan Aßmus } 163d5ef985eSStephan Aßmus 164d5ef985eSStephan Aßmus 165d5ef985eSStephan Aßmus void 166d5ef985eSStephan Aßmus ScreenshotWindow::SetPackage(const PackageInfoRef& package) 167d5ef985eSStephan Aßmus { 168d5ef985eSStephan Aßmus if (fPackage == package) 169d5ef985eSStephan Aßmus return; 170d5ef985eSStephan Aßmus 171d5ef985eSStephan Aßmus fPackage = package; 172d5ef985eSStephan Aßmus 173d5ef985eSStephan Aßmus BString title = B_TRANSLATE("Screenshot"); 174d5ef985eSStephan Aßmus if (package.Get() != NULL) { 175d5ef985eSStephan Aßmus title = package->Title(); 176d5ef985eSStephan Aßmus _DownloadScreenshot(); 177d5ef985eSStephan Aßmus } 178d5ef985eSStephan Aßmus SetTitle(title); 179c210060fSJulian Harnath 180c210060fSJulian Harnath atomic_set(&fCurrentScreenshotIndex, 0); 181c210060fSJulian Harnath 182c210060fSJulian Harnath _UpdateToolBar(); 183d5ef985eSStephan Aßmus } 184d5ef985eSStephan Aßmus 185d5ef985eSStephan Aßmus 186622e144fSJulian Harnath /* static */ void 187622e144fSJulian Harnath ScreenshotWindow::CleanupIcons() 188622e144fSJulian Harnath { 189622e144fSJulian Harnath sNextButtonIcon.Unset(); 190622e144fSJulian Harnath sPreviousButtonIcon.Unset(); 191622e144fSJulian Harnath } 192622e144fSJulian Harnath 193622e144fSJulian Harnath 194d5ef985eSStephan Aßmus // #pragma mark - private 195d5ef985eSStephan Aßmus 196d5ef985eSStephan Aßmus 197d5ef985eSStephan Aßmus void 198d5ef985eSStephan Aßmus ScreenshotWindow::_DownloadScreenshot() 199d5ef985eSStephan Aßmus { 200d5ef985eSStephan Aßmus BAutolock locker(&fLock); 201d5ef985eSStephan Aßmus 202d5ef985eSStephan Aßmus if (fWorkerThread >= 0) { 203d5ef985eSStephan Aßmus fDownloadPending = true; 204d5ef985eSStephan Aßmus return; 205d5ef985eSStephan Aßmus } 206d5ef985eSStephan Aßmus 207d5ef985eSStephan Aßmus thread_id thread = spawn_thread(&_DownloadThreadEntry, 208d5ef985eSStephan Aßmus "Screenshot Loader", B_NORMAL_PRIORITY, this); 209d5ef985eSStephan Aßmus if (thread >= 0) 210d5ef985eSStephan Aßmus _SetWorkerThread(thread); 211d5ef985eSStephan Aßmus } 212d5ef985eSStephan Aßmus 213d5ef985eSStephan Aßmus 214d5ef985eSStephan Aßmus void 215d5ef985eSStephan Aßmus ScreenshotWindow::_SetWorkerThread(thread_id thread) 216d5ef985eSStephan Aßmus { 217d5ef985eSStephan Aßmus if (!Lock()) 218d5ef985eSStephan Aßmus return; 219d5ef985eSStephan Aßmus 220d5ef985eSStephan Aßmus // bool enabled = thread < 0; 221d5ef985eSStephan Aßmus // 222d5ef985eSStephan Aßmus // fPreviewsButton->SetEnabled(enabled); 223d5ef985eSStephan Aßmus // fNextButton->SetEnabled(enabled); 224d5ef985eSStephan Aßmus // fCloseButton->SetEnabled(enabled); 225d5ef985eSStephan Aßmus 226d5ef985eSStephan Aßmus if (thread >= 0) { 227d5ef985eSStephan Aßmus fWorkerThread = thread; 228d5ef985eSStephan Aßmus resume_thread(fWorkerThread); 229d5ef985eSStephan Aßmus } else { 230d5ef985eSStephan Aßmus fWorkerThread = -1; 231d5ef985eSStephan Aßmus 232d5ef985eSStephan Aßmus if (fDownloadPending) { 233d5ef985eSStephan Aßmus _DownloadScreenshot(); 234d5ef985eSStephan Aßmus fDownloadPending = false; 235d5ef985eSStephan Aßmus } 236d5ef985eSStephan Aßmus } 237d5ef985eSStephan Aßmus 238d5ef985eSStephan Aßmus Unlock(); 239d5ef985eSStephan Aßmus } 240d5ef985eSStephan Aßmus 241d5ef985eSStephan Aßmus 242d5ef985eSStephan Aßmus int32 243d5ef985eSStephan Aßmus ScreenshotWindow::_DownloadThreadEntry(void* data) 244d5ef985eSStephan Aßmus { 245d5ef985eSStephan Aßmus ScreenshotWindow* window 246d5ef985eSStephan Aßmus = reinterpret_cast<ScreenshotWindow*>(data); 247d5ef985eSStephan Aßmus window->_DownloadThread(); 248d5ef985eSStephan Aßmus window->_SetWorkerThread(-1); 249d5ef985eSStephan Aßmus return 0; 250d5ef985eSStephan Aßmus } 251d5ef985eSStephan Aßmus 252d5ef985eSStephan Aßmus 253d5ef985eSStephan Aßmus void 254d5ef985eSStephan Aßmus ScreenshotWindow::_DownloadThread() 255d5ef985eSStephan Aßmus { 256d5ef985eSStephan Aßmus printf("_DownloadThread()\n"); 257d5ef985eSStephan Aßmus if (!Lock()) { 258d5ef985eSStephan Aßmus printf(" failed to lock screenshot window\n"); 259d5ef985eSStephan Aßmus return; 260d5ef985eSStephan Aßmus } 261d5ef985eSStephan Aßmus 2622a36368bSMichael Lotz fScreenshotView->UnsetBitmap(); 263d5ef985eSStephan Aßmus 264d5ef985eSStephan Aßmus ScreenshotInfoList screenshotInfos; 265d5ef985eSStephan Aßmus if (fPackage.Get() != NULL) 266d5ef985eSStephan Aßmus screenshotInfos = fPackage->ScreenshotInfos(); 267d5ef985eSStephan Aßmus 268d5ef985eSStephan Aßmus Unlock(); 269d5ef985eSStephan Aßmus 270d5ef985eSStephan Aßmus if (screenshotInfos.CountItems() == 0) { 271d5ef985eSStephan Aßmus printf(" package has no screenshots\n"); 272d5ef985eSStephan Aßmus return; 273d5ef985eSStephan Aßmus } 274d5ef985eSStephan Aßmus 275d5ef985eSStephan Aßmus // Obtain the correct code for the screenshot to display 276d5ef985eSStephan Aßmus // TODO: Once navigation buttons are added, we could use the 277d5ef985eSStephan Aßmus // ScreenshotInfo at the "current" index. 278c210060fSJulian Harnath const ScreenshotInfo& info = screenshotInfos.ItemAtFast( 279c210060fSJulian Harnath atomic_get(&fCurrentScreenshotIndex)); 280d5ef985eSStephan Aßmus 281d5ef985eSStephan Aßmus BMallocIO buffer; 282d5ef985eSStephan Aßmus WebAppInterface interface; 283d5ef985eSStephan Aßmus 2843ca9d5e9SJulian Harnath // Only indicate being busy with the download if it takes a little while 285c210060fSJulian Harnath BMessenger messenger(this); 2863ca9d5e9SJulian Harnath BMessageRunner delayedMessenger(messenger, 2873ca9d5e9SJulian Harnath new BMessage(MSG_DOWNLOAD_START), 2883ca9d5e9SJulian Harnath kProgressIndicatorDelay, 1); 289c210060fSJulian Harnath 290d5ef985eSStephan Aßmus // Retrieve screenshot from web-app 291d5ef985eSStephan Aßmus status_t status = interface.RetrieveScreenshot(info.Code(), 292d5ef985eSStephan Aßmus info.Width(), info.Height(), &buffer); 293c210060fSJulian Harnath 2943ca9d5e9SJulian Harnath delayedMessenger.SetCount(0); 295c210060fSJulian Harnath messenger.SendMessage(MSG_DOWNLOAD_STOP); 296c210060fSJulian Harnath 297d5ef985eSStephan Aßmus if (status == B_OK && Lock()) { 298d5ef985eSStephan Aßmus printf("got screenshot"); 299d5ef985eSStephan Aßmus fScreenshot = BitmapRef(new(std::nothrow)SharedBitmap(buffer), true); 3002a36368bSMichael Lotz fScreenshotView->SetBitmap(fScreenshot); 3012afa3f3bSStephan Aßmus _ResizeToFitAndCenter(); 302d5ef985eSStephan Aßmus Unlock(); 303d5ef985eSStephan Aßmus } else { 304d5ef985eSStephan Aßmus printf(" failed to download screenshot\n"); 305d5ef985eSStephan Aßmus } 306d5ef985eSStephan Aßmus } 3072afa3f3bSStephan Aßmus 3082afa3f3bSStephan Aßmus 3092afa3f3bSStephan Aßmus void 3102afa3f3bSStephan Aßmus ScreenshotWindow::_ResizeToFitAndCenter() 3112afa3f3bSStephan Aßmus { 312416c1160SJulian Harnath // Find out dimensions of the largest screenshot of this package 313416c1160SJulian Harnath ScreenshotInfoList screenshotInfos; 314416c1160SJulian Harnath if (fPackage.Get() != NULL) 315416c1160SJulian Harnath screenshotInfos = fPackage->ScreenshotInfos(); 316416c1160SJulian Harnath 317416c1160SJulian Harnath int32 largestScreenshotWidth = 0; 318416c1160SJulian Harnath int32 largestScreenshotHeight = 0; 319416c1160SJulian Harnath 320416c1160SJulian Harnath const uint32 numScreenshots = fPackage->ScreenshotInfos().CountItems(); 321416c1160SJulian Harnath for (uint32 i = 0; i < numScreenshots; i++) { 322416c1160SJulian Harnath const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i); 323416c1160SJulian Harnath if (info.Width() > largestScreenshotWidth) 324416c1160SJulian Harnath largestScreenshotWidth = info.Width(); 325416c1160SJulian Harnath if (info.Height() > largestScreenshotHeight) 326416c1160SJulian Harnath largestScreenshotHeight = info.Height(); 327416c1160SJulian Harnath } 328416c1160SJulian Harnath 329416c1160SJulian Harnath fScreenshotView->SetExplicitMinSize( 330416c1160SJulian Harnath BSize(largestScreenshotWidth, largestScreenshotHeight)); 331416c1160SJulian Harnath Layout(false); 332416c1160SJulian Harnath 333416c1160SJulian Harnath // TODO: Limit window size to screen size (with a little margin), 334416c1160SJulian Harnath // the image should then become scrollable. 335416c1160SJulian Harnath 3362afa3f3bSStephan Aßmus float minWidth; 3372afa3f3bSStephan Aßmus float minHeight; 3382afa3f3bSStephan Aßmus GetSizeLimits(&minWidth, NULL, &minHeight, NULL); 3392afa3f3bSStephan Aßmus ResizeTo(minWidth, minHeight); 3402afa3f3bSStephan Aßmus CenterOnScreen(); 3412afa3f3bSStephan Aßmus } 342c210060fSJulian Harnath 343c210060fSJulian Harnath 344c210060fSJulian Harnath void 345c210060fSJulian Harnath ScreenshotWindow::_UpdateToolBar() 346c210060fSJulian Harnath { 347416c1160SJulian Harnath const int32 numScreenshots = fPackage->ScreenshotInfos().CountItems(); 348416c1160SJulian Harnath const int32 currentIndex = atomic_get(&fCurrentScreenshotIndex); 349416c1160SJulian Harnath 350c210060fSJulian Harnath fToolBar->SetActionEnabled(MSG_PREVIOUS_SCREENSHOT, 351c210060fSJulian Harnath currentIndex > 0); 352c210060fSJulian Harnath fToolBar->SetActionEnabled(MSG_NEXT_SCREENSHOT, 353416c1160SJulian Harnath currentIndex < numScreenshots - 1); 354c210060fSJulian Harnath 355c210060fSJulian Harnath BString text; 356c210060fSJulian Harnath text << currentIndex + 1; 357c210060fSJulian Harnath text << " / "; 358416c1160SJulian Harnath text << numScreenshots; 359c210060fSJulian Harnath fIndexView->SetText(text); 360c210060fSJulian Harnath } 361