xref: /haiku/src/apps/haikudepot/ui/ScreenshotWindow.cpp (revision 7cea5bf07ffaec7e25508f3b81a2e5bd989e1b34)
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