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