1 /* 2 * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>. 4 * Copyright 2020-2021, 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(BITMAP_SIZE_22), 61 NULL, NULL); 62 fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this, 63 sPreviousButtonIcon->Bitmap(BITMAP_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.IsSet()) { 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 ScreenshotInfoRef info; 258 259 if (!Lock()) { 260 HDERROR("failed to lock screenshot window"); 261 return; 262 } 263 264 fScreenshotView->UnsetBitmap(); 265 _ResizeToFitAndCenter(); 266 267 if (!fPackage.IsSet()) 268 HDINFO("package not set"); 269 else { 270 if (fPackage->CountScreenshotInfos() == 0) 271 HDINFO("package has no screenshots"); 272 else { 273 int32 index = atomic_get(&fCurrentScreenshotIndex); 274 info = fPackage->ScreenshotInfoAtIndex(index); 275 } 276 } 277 278 Unlock(); 279 280 if (!info.IsSet()) { 281 HDINFO("screenshot not set"); 282 return; 283 } 284 285 BMallocIO buffer; 286 WebAppInterface interface; 287 288 // Only indicate being busy with the download if it takes a little while 289 BMessenger messenger(this); 290 BMessageRunner delayedMessenger(messenger, 291 new BMessage(MSG_DOWNLOAD_START), 292 kProgressIndicatorDelay, 1); 293 294 // Retrieve screenshot from web-app 295 status_t status = interface.RetrieveScreenshot(info->Code(), 296 info->Width(), info->Height(), &buffer); 297 298 delayedMessenger.SetCount(0); 299 messenger.SendMessage(MSG_DOWNLOAD_STOP); 300 301 if (status == B_OK && Lock()) { 302 HDINFO("got screenshot"); 303 fScreenshot = BitmapRef(new(std::nothrow)SharedBitmap(buffer), true); 304 fScreenshotView->SetBitmap(fScreenshot); 305 _ResizeToFitAndCenter(); 306 Unlock(); 307 } else 308 HDERROR("failed to download screenshot"); 309 } 310 311 312 BSize 313 ScreenshotWindow::_MaxWidthAndHeightOfAllScreenshots() 314 { 315 BSize size(0, 0); 316 317 // Find out dimensions of the largest screenshot of this package 318 if (fPackage.IsSet()) { 319 int count = fPackage->CountScreenshotInfos(); 320 for(int32 i = 0; i < count; i++) { 321 const ScreenshotInfoRef& info = fPackage->ScreenshotInfoAtIndex(i); 322 if (info.Get() != NULL) { 323 float w = (float) info->Width(); 324 float h = (float) info->Height(); 325 if (w > size.Width()) 326 size.SetWidth(w); 327 if (h > size.Height()) 328 size.SetHeight(h); 329 } 330 } 331 } 332 333 return size; 334 } 335 336 337 void 338 ScreenshotWindow::_ResizeToFitAndCenter() 339 { 340 fScreenshotView->SetExplicitMinSize(_MaxWidthAndHeightOfAllScreenshots()); 341 Layout(false); 342 343 // TODO: Limit window size to screen size (with a little margin), 344 // the image should then become scrollable. 345 346 float minWidth; 347 float minHeight; 348 GetSizeLimits(&minWidth, NULL, &minHeight, NULL); 349 ResizeTo(minWidth, minHeight); 350 CenterOnScreen(); 351 } 352 353 354 void 355 ScreenshotWindow::_UpdateToolBar() 356 { 357 const int32 numScreenshots = fPackage->CountScreenshotInfos(); 358 const int32 currentIndex = atomic_get(&fCurrentScreenshotIndex); 359 360 fToolBar->SetActionEnabled(MSG_PREVIOUS_SCREENSHOT, 361 currentIndex > 0); 362 fToolBar->SetActionEnabled(MSG_NEXT_SCREENSHOT, 363 currentIndex < numScreenshots - 1); 364 365 BString text; 366 text << currentIndex + 1; 367 text << " / "; 368 text << numScreenshots; 369 fIndexView->SetText(text); 370 } 371