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