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