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