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 "HaikuDepotConstants.h" 21 #include "WebAppInterface.h" 22 23 24 #undef B_TRANSLATION_CONTEXT 25 #define B_TRANSLATION_CONTEXT "ScreenshotWindow" 26 27 28 static const rgb_color kBackgroundColor = { 51, 102, 152, 255 }; 29 // Drawn as a border around the screenshots and also what's behind their 30 // transparent regions 31 32 static BitmapRef sNextButtonIcon( 33 new(std::nothrow) SharedBitmap(RSRC_ARROW_LEFT), true); 34 static BitmapRef sPreviousButtonIcon( 35 new(std::nothrow) SharedBitmap(RSRC_ARROW_RIGHT), true); 36 37 38 ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame) 39 : 40 BWindow(frame, B_TRANSLATE("Screenshot"), 41 B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL, 42 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 43 fBarberPoleShown(false), 44 fDownloadPending(false), 45 fWorkerThread(-1) 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 sNextButtonIcon->Bitmap(SharedBitmap::SIZE_22), 60 NULL, NULL); 61 fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this, 62 sPreviousButtonIcon->Bitmap(SharedBitmap::SIZE_22), 63 NULL, NULL); 64 fToolBar->AddView(fIndexView); 65 fToolBar->AddGlue(); 66 fToolBar->AddView(fBarberPole); 67 68 fScreenshotView = new BitmapView("screenshot view"); 69 fScreenshotView->SetExplicitMaxSize( 70 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 71 fScreenshotView->SetScaleBitmap(false); 72 73 BGroupView* groupView = new BGroupView(B_VERTICAL); 74 groupView->SetViewColor(kBackgroundColor); 75 76 // Build layout 77 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 78 .SetInsets(0, 3, 0, 0) 79 .Add(fToolBar) 80 .AddStrut(3) 81 .AddGroup(groupView) 82 .Add(fScreenshotView) 83 .SetInsets(B_USE_WINDOW_INSETS) 84 .End() 85 ; 86 87 fScreenshotView->SetLowColor(kBackgroundColor); 88 // Set after attaching all views to prevent it from being overriden 89 // again by BitmapView::AllAttached() 90 91 CenterOnScreen(); 92 } 93 94 95 ScreenshotWindow::~ScreenshotWindow() 96 { 97 BAutolock locker(&fLock); 98 99 if (fWorkerThread >= 0) 100 wait_for_thread(fWorkerThread, NULL); 101 } 102 103 104 bool 105 ScreenshotWindow::QuitRequested() 106 { 107 if (fOnCloseTarget.IsValid() && fOnCloseMessage.what != 0) 108 fOnCloseTarget.SendMessage(&fOnCloseMessage); 109 110 Hide(); 111 return false; 112 } 113 114 115 void 116 ScreenshotWindow::MessageReceived(BMessage* message) 117 { 118 switch (message->what) { 119 case MSG_NEXT_SCREENSHOT: 120 { 121 atomic_add(&fCurrentScreenshotIndex, 1); 122 _UpdateToolBar(); 123 _DownloadScreenshot(); 124 break; 125 } 126 127 case MSG_PREVIOUS_SCREENSHOT: 128 atomic_add(&fCurrentScreenshotIndex, -1); 129 _UpdateToolBar(); 130 _DownloadScreenshot(); 131 break; 132 133 case MSG_DOWNLOAD_START: 134 if (!fBarberPoleShown) { 135 fBarberPole->Start(); 136 fBarberPole->Show(); 137 fBarberPoleShown = true; 138 } 139 break; 140 141 case MSG_DOWNLOAD_STOP: 142 if (fBarberPoleShown) { 143 fBarberPole->Hide(); 144 fBarberPole->Stop(); 145 fBarberPoleShown = true; 146 } 147 break; 148 149 default: 150 BWindow::MessageReceived(message); 151 break; 152 } 153 } 154 155 156 void 157 ScreenshotWindow::SetOnCloseMessage( 158 const BMessenger& messenger, const BMessage& message) 159 { 160 fOnCloseTarget = messenger; 161 fOnCloseMessage = message; 162 } 163 164 165 void 166 ScreenshotWindow::SetPackage(const PackageInfoRef& package) 167 { 168 if (fPackage == package) 169 return; 170 171 fPackage = package; 172 173 BString title = B_TRANSLATE("Screenshot"); 174 if (package.Get() != NULL) { 175 title = package->Title(); 176 _DownloadScreenshot(); 177 } 178 SetTitle(title); 179 180 atomic_set(&fCurrentScreenshotIndex, 0); 181 182 _UpdateToolBar(); 183 } 184 185 186 /* static */ void 187 ScreenshotWindow::CleanupIcons() 188 { 189 sNextButtonIcon.Unset(); 190 sPreviousButtonIcon.Unset(); 191 } 192 193 194 // #pragma mark - private 195 196 197 void 198 ScreenshotWindow::_DownloadScreenshot() 199 { 200 BAutolock locker(&fLock); 201 202 if (fWorkerThread >= 0) { 203 fDownloadPending = true; 204 return; 205 } 206 207 thread_id thread = spawn_thread(&_DownloadThreadEntry, 208 "Screenshot Loader", B_NORMAL_PRIORITY, this); 209 if (thread >= 0) 210 _SetWorkerThread(thread); 211 } 212 213 214 void 215 ScreenshotWindow::_SetWorkerThread(thread_id thread) 216 { 217 if (!Lock()) 218 return; 219 220 // bool enabled = thread < 0; 221 // 222 // fPreviewsButton->SetEnabled(enabled); 223 // fNextButton->SetEnabled(enabled); 224 // fCloseButton->SetEnabled(enabled); 225 226 if (thread >= 0) { 227 fWorkerThread = thread; 228 resume_thread(fWorkerThread); 229 } else { 230 fWorkerThread = -1; 231 232 if (fDownloadPending) { 233 _DownloadScreenshot(); 234 fDownloadPending = false; 235 } 236 } 237 238 Unlock(); 239 } 240 241 242 int32 243 ScreenshotWindow::_DownloadThreadEntry(void* data) 244 { 245 ScreenshotWindow* window 246 = reinterpret_cast<ScreenshotWindow*>(data); 247 window->_DownloadThread(); 248 window->_SetWorkerThread(-1); 249 return 0; 250 } 251 252 253 void 254 ScreenshotWindow::_DownloadThread() 255 { 256 printf("_DownloadThread()\n"); 257 if (!Lock()) { 258 printf(" failed to lock screenshot window\n"); 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 printf(" package has no screenshots\n"); 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 printf("got screenshot"); 299 fScreenshot = BitmapRef(new(std::nothrow)SharedBitmap(buffer), true); 300 fScreenshotView->SetBitmap(fScreenshot); 301 _ResizeToFitAndCenter(); 302 Unlock(); 303 } else { 304 printf(" failed to download screenshot\n"); 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