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