1 /* 2 * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 7 #include "ScreenshotWindow.h" 8 9 #include <algorithm> 10 #include <stdio.h> 11 12 #include <Autolock.h> 13 #include <Catalog.h> 14 #include <LayoutBuilder.h> 15 #include <MessageRunner.h> 16 #include <StringView.h> 17 18 #include "BarberPole.h" 19 #include "BitmapView.h" 20 #include "WebAppInterface.h" 21 22 23 #undef B_TRANSLATION_CONTEXT 24 #define B_TRANSLATION_CONTEXT "ScreenshotWindow" 25 26 27 static const rgb_color kBackgroundColor = { 51, 102, 152, 255 }; 28 // Drawn as a border around the screenshots and also what's behind their 29 // transparent regions 30 31 static BitmapRef sNextButtonIcon( 32 new(std::nothrow) SharedBitmap(505), true); 33 static BitmapRef sPreviousButtonIcon( 34 new(std::nothrow) SharedBitmap(506), true); 35 36 37 ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame) 38 : 39 BWindow(frame, B_TRANSLATE("Screenshot"), 40 B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL, 41 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 42 fBarberPoleShown(false), 43 fDownloadPending(false), 44 fWorkerThread(-1) 45 { 46 AddToSubset(parent); 47 48 atomic_set(&fCurrentScreenshotIndex, 0); 49 50 fBarberPole = new BarberPole("barber pole"); 51 fBarberPole->SetExplicitMaxSize(BSize(100, B_SIZE_UNLIMITED)); 52 fBarberPole->Hide(); 53 54 fIndexView = new BStringView("screenshot index", NULL); 55 56 fToolBar = new BToolBar(); 57 fToolBar->AddAction(MSG_PREVIOUS_SCREENSHOT, this, 58 sNextButtonIcon->Bitmap(SharedBitmap::SIZE_22), 59 NULL, NULL); 60 fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this, 61 sPreviousButtonIcon->Bitmap(SharedBitmap::SIZE_22), 62 NULL, NULL); 63 fToolBar->AddView(fIndexView); 64 fToolBar->AddGlue(); 65 fToolBar->AddView(fBarberPole); 66 67 fScreenshotView = new BitmapView("screenshot view"); 68 fScreenshotView->SetExplicitMaxSize( 69 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 70 fScreenshotView->SetScaleBitmap(false); 71 72 BGroupView* groupView = new BGroupView(B_VERTICAL); 73 groupView->SetViewColor(kBackgroundColor); 74 75 // Build layout 76 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 77 .SetInsets(0, 3, 0, 0) 78 .Add(fToolBar) 79 .AddStrut(3) 80 .AddGroup(groupView) 81 .Add(fScreenshotView) 82 .SetInsets(B_USE_WINDOW_INSETS) 83 .End() 84 ; 85 86 fScreenshotView->SetLowColor(kBackgroundColor); 87 // Set after attaching all views to prevent it from being overriden 88 // again by BitmapView::AllAttached() 89 90 CenterOnScreen(); 91 } 92 93 94 ScreenshotWindow::~ScreenshotWindow() 95 { 96 BAutolock locker(&fLock); 97 98 if (fWorkerThread >= 0) 99 wait_for_thread(fWorkerThread, NULL); 100 } 101 102 103 bool 104 ScreenshotWindow::QuitRequested() 105 { 106 if (fOnCloseTarget.IsValid() && fOnCloseMessage.what != 0) 107 fOnCloseTarget.SendMessage(&fOnCloseMessage); 108 109 Hide(); 110 return false; 111 } 112 113 114 void 115 ScreenshotWindow::MessageReceived(BMessage* message) 116 { 117 switch (message->what) { 118 case MSG_NEXT_SCREENSHOT: 119 { 120 atomic_add(&fCurrentScreenshotIndex, 1); 121 _UpdateToolBar(); 122 _DownloadScreenshot(); 123 break; 124 } 125 126 case MSG_PREVIOUS_SCREENSHOT: 127 atomic_add(&fCurrentScreenshotIndex, -1); 128 _UpdateToolBar(); 129 _DownloadScreenshot(); 130 break; 131 132 case MSG_DOWNLOAD_START: 133 if (!fBarberPoleShown) { 134 fBarberPole->Start(); 135 fBarberPole->Show(); 136 fBarberPoleShown = true; 137 } 138 break; 139 140 case MSG_DOWNLOAD_STOP: 141 if (fBarberPoleShown) { 142 fBarberPole->Hide(); 143 fBarberPole->Stop(); 144 fBarberPoleShown = true; 145 } 146 break; 147 148 default: 149 BWindow::MessageReceived(message); 150 break; 151 } 152 } 153 154 155 void 156 ScreenshotWindow::SetOnCloseMessage( 157 const BMessenger& messenger, const BMessage& message) 158 { 159 fOnCloseTarget = messenger; 160 fOnCloseMessage = message; 161 } 162 163 164 void 165 ScreenshotWindow::SetPackage(const PackageInfoRef& package) 166 { 167 if (fPackage == package) 168 return; 169 170 fPackage = package; 171 172 BString title = B_TRANSLATE("Screenshot"); 173 if (package.Get() != NULL) { 174 title = package->Title(); 175 _DownloadScreenshot(); 176 } 177 SetTitle(title); 178 179 atomic_set(&fCurrentScreenshotIndex, 0); 180 181 _UpdateToolBar(); 182 } 183 184 185 /* static */ void 186 ScreenshotWindow::CleanupIcons() 187 { 188 sNextButtonIcon.Unset(); 189 sPreviousButtonIcon.Unset(); 190 } 191 192 193 // #pragma mark - private 194 195 196 void 197 ScreenshotWindow::_DownloadScreenshot() 198 { 199 BAutolock locker(&fLock); 200 201 if (fWorkerThread >= 0) { 202 fDownloadPending = true; 203 return; 204 } 205 206 thread_id thread = spawn_thread(&_DownloadThreadEntry, 207 "Screenshot Loader", B_NORMAL_PRIORITY, this); 208 if (thread >= 0) 209 _SetWorkerThread(thread); 210 } 211 212 213 void 214 ScreenshotWindow::_SetWorkerThread(thread_id thread) 215 { 216 if (!Lock()) 217 return; 218 219 // bool enabled = thread < 0; 220 // 221 // fPreviewsButton->SetEnabled(enabled); 222 // fNextButton->SetEnabled(enabled); 223 // fCloseButton->SetEnabled(enabled); 224 225 if (thread >= 0) { 226 fWorkerThread = thread; 227 resume_thread(fWorkerThread); 228 } else { 229 fWorkerThread = -1; 230 231 if (fDownloadPending) { 232 _DownloadScreenshot(); 233 fDownloadPending = false; 234 } 235 } 236 237 Unlock(); 238 } 239 240 241 int32 242 ScreenshotWindow::_DownloadThreadEntry(void* data) 243 { 244 ScreenshotWindow* window 245 = reinterpret_cast<ScreenshotWindow*>(data); 246 window->_DownloadThread(); 247 window->_SetWorkerThread(-1); 248 return 0; 249 } 250 251 252 void 253 ScreenshotWindow::_DownloadThread() 254 { 255 printf("_DownloadThread()\n"); 256 if (!Lock()) { 257 printf(" failed to lock screenshot window\n"); 258 return; 259 } 260 261 fScreenshotView->UnsetBitmap(); 262 263 ScreenshotInfoList screenshotInfos; 264 if (fPackage.Get() != NULL) 265 screenshotInfos = fPackage->ScreenshotInfos(); 266 267 Unlock(); 268 269 if (screenshotInfos.CountItems() == 0) { 270 printf(" package has no screenshots\n"); 271 return; 272 } 273 274 // Obtain the correct code for the screenshot to display 275 // TODO: Once navigation buttons are added, we could use the 276 // ScreenshotInfo at the "current" index. 277 const ScreenshotInfo& info = screenshotInfos.ItemAtFast( 278 atomic_get(&fCurrentScreenshotIndex)); 279 280 BMallocIO buffer; 281 WebAppInterface interface; 282 283 // Only indicate being busy with the download if it takes a little while 284 BMessenger messenger(this); 285 BMessageRunner delayedMessenger(messenger, 286 new BMessage(MSG_DOWNLOAD_START), 287 kProgressIndicatorDelay, 1); 288 289 // Retrieve screenshot from web-app 290 status_t status = interface.RetrieveScreenshot(info.Code(), 291 info.Width(), info.Height(), &buffer); 292 293 delayedMessenger.SetCount(0); 294 messenger.SendMessage(MSG_DOWNLOAD_STOP); 295 296 if (status == B_OK && Lock()) { 297 printf("got screenshot"); 298 fScreenshot = BitmapRef(new(std::nothrow)SharedBitmap(buffer), true); 299 fScreenshotView->SetBitmap(fScreenshot); 300 _ResizeToFitAndCenter(); 301 Unlock(); 302 } else { 303 printf(" failed to download screenshot\n"); 304 } 305 } 306 307 308 void 309 ScreenshotWindow::_ResizeToFitAndCenter() 310 { 311 // Find out dimensions of the largest screenshot of this package 312 ScreenshotInfoList screenshotInfos; 313 if (fPackage.Get() != NULL) 314 screenshotInfos = fPackage->ScreenshotInfos(); 315 316 int32 largestScreenshotWidth = 0; 317 int32 largestScreenshotHeight = 0; 318 319 const uint32 numScreenshots = fPackage->ScreenshotInfos().CountItems(); 320 for (uint32 i = 0; i < numScreenshots; i++) { 321 const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i); 322 if (info.Width() > largestScreenshotWidth) 323 largestScreenshotWidth = info.Width(); 324 if (info.Height() > largestScreenshotHeight) 325 largestScreenshotHeight = info.Height(); 326 } 327 328 fScreenshotView->SetExplicitMinSize( 329 BSize(largestScreenshotWidth, largestScreenshotHeight)); 330 Layout(false); 331 332 // TODO: Limit window size to screen size (with a little margin), 333 // the image should then become scrollable. 334 335 float minWidth; 336 float minHeight; 337 GetSizeLimits(&minWidth, NULL, &minHeight, NULL); 338 ResizeTo(minWidth, minHeight); 339 CenterOnScreen(); 340 } 341 342 343 void 344 ScreenshotWindow::_UpdateToolBar() 345 { 346 const int32 numScreenshots = fPackage->ScreenshotInfos().CountItems(); 347 const int32 currentIndex = atomic_get(&fCurrentScreenshotIndex); 348 349 fToolBar->SetActionEnabled(MSG_PREVIOUS_SCREENSHOT, 350 currentIndex > 0); 351 fToolBar->SetActionEnabled(MSG_NEXT_SCREENSHOT, 352 currentIndex < numScreenshots - 1); 353 354 BString text; 355 text << currentIndex + 1; 356 text << " / "; 357 text << numScreenshots; 358 fIndexView->SetText(text); 359 } 360