xref: /haiku/src/apps/haikudepot/ui/ScreenshotWindow.cpp (revision a9edb9bffa2100903fc7d6e2a42cedf1131f0172)
1d5ef985eSStephan Aßmus /*
2d5ef985eSStephan Aßmus  * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
339f99591SJulian Harnath  * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
4d5ef985eSStephan Aßmus  * All rights reserved. Distributed under the terms of the MIT License.
5d5ef985eSStephan Aßmus  */
6d5ef985eSStephan Aßmus 
7d5ef985eSStephan Aßmus #include "ScreenshotWindow.h"
8d5ef985eSStephan Aßmus 
9d5ef985eSStephan Aßmus #include <algorithm>
10d5ef985eSStephan Aßmus #include <stdio.h>
11d5ef985eSStephan Aßmus 
12d5ef985eSStephan Aßmus #include <Autolock.h>
13d5ef985eSStephan Aßmus #include <Catalog.h>
14d5ef985eSStephan Aßmus #include <LayoutBuilder.h>
153ca9d5e9SJulian Harnath #include <MessageRunner.h>
16c210060fSJulian Harnath #include <StringView.h>
17d5ef985eSStephan Aßmus 
18c210060fSJulian Harnath #include "BarberPole.h"
19d5ef985eSStephan Aßmus #include "BitmapView.h"
20*a9edb9bfSAndrew Lindesay #include "HaikuDepotConstants.h"
21d5ef985eSStephan Aßmus #include "WebAppInterface.h"
22d5ef985eSStephan Aßmus 
23d5ef985eSStephan Aßmus 
24d5ef985eSStephan Aßmus #undef B_TRANSLATION_CONTEXT
25d5ef985eSStephan Aßmus #define B_TRANSLATION_CONTEXT "ScreenshotWindow"
26d5ef985eSStephan Aßmus 
27d5ef985eSStephan Aßmus 
2839f99591SJulian Harnath static const rgb_color kBackgroundColor = { 51, 102, 152, 255 };
2939f99591SJulian Harnath 	// Drawn as a border around the screenshots and also what's behind their
3039f99591SJulian Harnath 	// transparent regions
3139f99591SJulian Harnath 
32622e144fSJulian Harnath static BitmapRef sNextButtonIcon(
33*a9edb9bfSAndrew Lindesay 	new(std::nothrow) SharedBitmap(RSRC_ARROW_LEFT), true);
34622e144fSJulian Harnath static BitmapRef sPreviousButtonIcon(
35*a9edb9bfSAndrew Lindesay 	new(std::nothrow) SharedBitmap(RSRC_ARROW_RIGHT), true);
36622e144fSJulian Harnath 
3739f99591SJulian Harnath 
38d5ef985eSStephan Aßmus ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame)
39d5ef985eSStephan Aßmus 	:
40d5ef985eSStephan Aßmus 	BWindow(frame, B_TRANSLATE("Screenshot"),
41d5ef985eSStephan Aßmus 		B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL,
42d5ef985eSStephan Aßmus 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
433ca9d5e9SJulian Harnath 	fBarberPoleShown(false),
44d5ef985eSStephan Aßmus 	fDownloadPending(false),
45d5ef985eSStephan Aßmus 	fWorkerThread(-1)
46d5ef985eSStephan Aßmus {
47d5ef985eSStephan Aßmus 	AddToSubset(parent);
48d5ef985eSStephan Aßmus 
49c210060fSJulian Harnath 	atomic_set(&fCurrentScreenshotIndex, 0);
50c210060fSJulian Harnath 
51c210060fSJulian Harnath 	fBarberPole = new BarberPole("barber pole");
523ca9d5e9SJulian Harnath 	fBarberPole->SetExplicitMaxSize(BSize(100, B_SIZE_UNLIMITED));
533ca9d5e9SJulian Harnath 	fBarberPole->Hide();
54c210060fSJulian Harnath 
55c210060fSJulian Harnath 	fIndexView = new BStringView("screenshot index", NULL);
56c210060fSJulian Harnath 
57c210060fSJulian Harnath 	fToolBar = new BToolBar();
58622e144fSJulian Harnath 	fToolBar->AddAction(MSG_PREVIOUS_SCREENSHOT, this,
59622e144fSJulian Harnath 		sNextButtonIcon->Bitmap(SharedBitmap::SIZE_22),
60622e144fSJulian Harnath 		NULL, NULL);
61622e144fSJulian Harnath 	fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this,
62622e144fSJulian Harnath 		sPreviousButtonIcon->Bitmap(SharedBitmap::SIZE_22),
63622e144fSJulian Harnath 		NULL, NULL);
64c210060fSJulian Harnath 	fToolBar->AddView(fIndexView);
65c210060fSJulian Harnath 	fToolBar->AddGlue();
66c210060fSJulian Harnath 	fToolBar->AddView(fBarberPole);
67c210060fSJulian Harnath 
68d5ef985eSStephan Aßmus 	fScreenshotView = new BitmapView("screenshot view");
69d5ef985eSStephan Aßmus 	fScreenshotView->SetExplicitMaxSize(
70d5ef985eSStephan Aßmus 		BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
71416c1160SJulian Harnath 	fScreenshotView->SetScaleBitmap(false);
72d5ef985eSStephan Aßmus 
732afa3f3bSStephan Aßmus 	BGroupView* groupView = new BGroupView(B_VERTICAL);
7439f99591SJulian Harnath 	groupView->SetViewColor(kBackgroundColor);
752afa3f3bSStephan Aßmus 
76d5ef985eSStephan Aßmus 	// Build layout
77c210060fSJulian Harnath 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
78622e144fSJulian Harnath 		.SetInsets(0, 3, 0, 0)
79c210060fSJulian Harnath 		.Add(fToolBar)
80622e144fSJulian Harnath 		.AddStrut(3)
812afa3f3bSStephan Aßmus 		.AddGroup(groupView)
82d5ef985eSStephan Aßmus 			.Add(fScreenshotView)
83d5ef985eSStephan Aßmus 			.SetInsets(B_USE_WINDOW_INSETS)
842afa3f3bSStephan Aßmus 		.End()
85d5ef985eSStephan Aßmus 	;
86d5ef985eSStephan Aßmus 
8739f99591SJulian Harnath 	fScreenshotView->SetLowColor(kBackgroundColor);
8839f99591SJulian Harnath 		// Set after attaching all views to prevent it from being overriden
8939f99591SJulian Harnath 		// again by BitmapView::AllAttached()
9039f99591SJulian Harnath 
912afa3f3bSStephan Aßmus 	CenterOnScreen();
92d5ef985eSStephan Aßmus }
93d5ef985eSStephan Aßmus 
94d5ef985eSStephan Aßmus 
95d5ef985eSStephan Aßmus ScreenshotWindow::~ScreenshotWindow()
96d5ef985eSStephan Aßmus {
97d5ef985eSStephan Aßmus 	BAutolock locker(&fLock);
98d5ef985eSStephan Aßmus 
99d5ef985eSStephan Aßmus 	if (fWorkerThread >= 0)
100d5ef985eSStephan Aßmus 		wait_for_thread(fWorkerThread, NULL);
101d5ef985eSStephan Aßmus }
102d5ef985eSStephan Aßmus 
103d5ef985eSStephan Aßmus 
104d5ef985eSStephan Aßmus bool
105d5ef985eSStephan Aßmus ScreenshotWindow::QuitRequested()
106d5ef985eSStephan Aßmus {
107d5ef985eSStephan Aßmus 	if (fOnCloseTarget.IsValid() && fOnCloseMessage.what != 0)
108d5ef985eSStephan Aßmus 		fOnCloseTarget.SendMessage(&fOnCloseMessage);
109d5ef985eSStephan Aßmus 
110d5ef985eSStephan Aßmus 	Hide();
111d5ef985eSStephan Aßmus 	return false;
112d5ef985eSStephan Aßmus }
113d5ef985eSStephan Aßmus 
114d5ef985eSStephan Aßmus 
115d5ef985eSStephan Aßmus void
116d5ef985eSStephan Aßmus ScreenshotWindow::MessageReceived(BMessage* message)
117d5ef985eSStephan Aßmus {
118d5ef985eSStephan Aßmus 	switch (message->what) {
119c210060fSJulian Harnath 		case MSG_NEXT_SCREENSHOT:
120c210060fSJulian Harnath 		{
121c210060fSJulian Harnath 			atomic_add(&fCurrentScreenshotIndex, 1);
122c210060fSJulian Harnath 			_UpdateToolBar();
123c210060fSJulian Harnath 			_DownloadScreenshot();
124c210060fSJulian Harnath 			break;
125c210060fSJulian Harnath 		}
126c210060fSJulian Harnath 
127c210060fSJulian Harnath 		case MSG_PREVIOUS_SCREENSHOT:
128c210060fSJulian Harnath 			atomic_add(&fCurrentScreenshotIndex, -1);
129c210060fSJulian Harnath 			_UpdateToolBar();
130c210060fSJulian Harnath 			_DownloadScreenshot();
131c210060fSJulian Harnath 			break;
132c210060fSJulian Harnath 
133c210060fSJulian Harnath 		case MSG_DOWNLOAD_START:
1343ca9d5e9SJulian Harnath 			if (!fBarberPoleShown) {
135c210060fSJulian Harnath 				fBarberPole->Start();
1363ca9d5e9SJulian Harnath 				fBarberPole->Show();
1373ca9d5e9SJulian Harnath 				fBarberPoleShown = true;
1383ca9d5e9SJulian Harnath 			}
139c210060fSJulian Harnath 			break;
140c210060fSJulian Harnath 
141c210060fSJulian Harnath 		case MSG_DOWNLOAD_STOP:
1423ca9d5e9SJulian Harnath 			if (fBarberPoleShown) {
1433ca9d5e9SJulian Harnath 				fBarberPole->Hide();
144c210060fSJulian Harnath 				fBarberPole->Stop();
1453ca9d5e9SJulian Harnath 				fBarberPoleShown = true;
1463ca9d5e9SJulian Harnath 			}
147c210060fSJulian Harnath 			break;
148c210060fSJulian Harnath 
149d5ef985eSStephan Aßmus 		default:
150d5ef985eSStephan Aßmus 			BWindow::MessageReceived(message);
151d5ef985eSStephan Aßmus 			break;
152d5ef985eSStephan Aßmus 	}
153d5ef985eSStephan Aßmus }
154d5ef985eSStephan Aßmus 
155d5ef985eSStephan Aßmus 
156d5ef985eSStephan Aßmus void
157d5ef985eSStephan Aßmus ScreenshotWindow::SetOnCloseMessage(
158d5ef985eSStephan Aßmus 	const BMessenger& messenger, const BMessage& message)
159d5ef985eSStephan Aßmus {
160d5ef985eSStephan Aßmus 	fOnCloseTarget = messenger;
161d5ef985eSStephan Aßmus 	fOnCloseMessage = message;
162d5ef985eSStephan Aßmus }
163d5ef985eSStephan Aßmus 
164d5ef985eSStephan Aßmus 
165d5ef985eSStephan Aßmus void
166d5ef985eSStephan Aßmus ScreenshotWindow::SetPackage(const PackageInfoRef& package)
167d5ef985eSStephan Aßmus {
168d5ef985eSStephan Aßmus 	if (fPackage == package)
169d5ef985eSStephan Aßmus 		return;
170d5ef985eSStephan Aßmus 
171d5ef985eSStephan Aßmus 	fPackage = package;
172d5ef985eSStephan Aßmus 
173d5ef985eSStephan Aßmus 	BString title = B_TRANSLATE("Screenshot");
174d5ef985eSStephan Aßmus 	if (package.Get() != NULL) {
175d5ef985eSStephan Aßmus 		title = package->Title();
176d5ef985eSStephan Aßmus 		_DownloadScreenshot();
177d5ef985eSStephan Aßmus 	}
178d5ef985eSStephan Aßmus 	SetTitle(title);
179c210060fSJulian Harnath 
180c210060fSJulian Harnath 	atomic_set(&fCurrentScreenshotIndex, 0);
181c210060fSJulian Harnath 
182c210060fSJulian Harnath 	_UpdateToolBar();
183d5ef985eSStephan Aßmus }
184d5ef985eSStephan Aßmus 
185d5ef985eSStephan Aßmus 
186622e144fSJulian Harnath /* static */ void
187622e144fSJulian Harnath ScreenshotWindow::CleanupIcons()
188622e144fSJulian Harnath {
189622e144fSJulian Harnath 	sNextButtonIcon.Unset();
190622e144fSJulian Harnath 	sPreviousButtonIcon.Unset();
191622e144fSJulian Harnath }
192622e144fSJulian Harnath 
193622e144fSJulian Harnath 
194d5ef985eSStephan Aßmus // #pragma mark - private
195d5ef985eSStephan Aßmus 
196d5ef985eSStephan Aßmus 
197d5ef985eSStephan Aßmus void
198d5ef985eSStephan Aßmus ScreenshotWindow::_DownloadScreenshot()
199d5ef985eSStephan Aßmus {
200d5ef985eSStephan Aßmus 	BAutolock locker(&fLock);
201d5ef985eSStephan Aßmus 
202d5ef985eSStephan Aßmus 	if (fWorkerThread >= 0) {
203d5ef985eSStephan Aßmus 		fDownloadPending = true;
204d5ef985eSStephan Aßmus 		return;
205d5ef985eSStephan Aßmus 	}
206d5ef985eSStephan Aßmus 
207d5ef985eSStephan Aßmus 	thread_id thread = spawn_thread(&_DownloadThreadEntry,
208d5ef985eSStephan Aßmus 		"Screenshot Loader", B_NORMAL_PRIORITY, this);
209d5ef985eSStephan Aßmus 	if (thread >= 0)
210d5ef985eSStephan Aßmus 		_SetWorkerThread(thread);
211d5ef985eSStephan Aßmus }
212d5ef985eSStephan Aßmus 
213d5ef985eSStephan Aßmus 
214d5ef985eSStephan Aßmus void
215d5ef985eSStephan Aßmus ScreenshotWindow::_SetWorkerThread(thread_id thread)
216d5ef985eSStephan Aßmus {
217d5ef985eSStephan Aßmus 	if (!Lock())
218d5ef985eSStephan Aßmus 		return;
219d5ef985eSStephan Aßmus 
220d5ef985eSStephan Aßmus //	bool enabled = thread < 0;
221d5ef985eSStephan Aßmus //
222d5ef985eSStephan Aßmus //	fPreviewsButton->SetEnabled(enabled);
223d5ef985eSStephan Aßmus //	fNextButton->SetEnabled(enabled);
224d5ef985eSStephan Aßmus //	fCloseButton->SetEnabled(enabled);
225d5ef985eSStephan Aßmus 
226d5ef985eSStephan Aßmus 	if (thread >= 0) {
227d5ef985eSStephan Aßmus 		fWorkerThread = thread;
228d5ef985eSStephan Aßmus 		resume_thread(fWorkerThread);
229d5ef985eSStephan Aßmus 	} else {
230d5ef985eSStephan Aßmus 		fWorkerThread = -1;
231d5ef985eSStephan Aßmus 
232d5ef985eSStephan Aßmus 		if (fDownloadPending) {
233d5ef985eSStephan Aßmus 			_DownloadScreenshot();
234d5ef985eSStephan Aßmus 			fDownloadPending = false;
235d5ef985eSStephan Aßmus 		}
236d5ef985eSStephan Aßmus 	}
237d5ef985eSStephan Aßmus 
238d5ef985eSStephan Aßmus 	Unlock();
239d5ef985eSStephan Aßmus }
240d5ef985eSStephan Aßmus 
241d5ef985eSStephan Aßmus 
242d5ef985eSStephan Aßmus int32
243d5ef985eSStephan Aßmus ScreenshotWindow::_DownloadThreadEntry(void* data)
244d5ef985eSStephan Aßmus {
245d5ef985eSStephan Aßmus 	ScreenshotWindow* window
246d5ef985eSStephan Aßmus 		= reinterpret_cast<ScreenshotWindow*>(data);
247d5ef985eSStephan Aßmus 	window->_DownloadThread();
248d5ef985eSStephan Aßmus 	window->_SetWorkerThread(-1);
249d5ef985eSStephan Aßmus 	return 0;
250d5ef985eSStephan Aßmus }
251d5ef985eSStephan Aßmus 
252d5ef985eSStephan Aßmus 
253d5ef985eSStephan Aßmus void
254d5ef985eSStephan Aßmus ScreenshotWindow::_DownloadThread()
255d5ef985eSStephan Aßmus {
256d5ef985eSStephan Aßmus 	printf("_DownloadThread()\n");
257d5ef985eSStephan Aßmus 	if (!Lock()) {
258d5ef985eSStephan Aßmus 		printf("  failed to lock screenshot window\n");
259d5ef985eSStephan Aßmus 		return;
260d5ef985eSStephan Aßmus 	}
261d5ef985eSStephan Aßmus 
2622a36368bSMichael Lotz 	fScreenshotView->UnsetBitmap();
263d5ef985eSStephan Aßmus 
264d5ef985eSStephan Aßmus 	ScreenshotInfoList screenshotInfos;
265d5ef985eSStephan Aßmus 	if (fPackage.Get() != NULL)
266d5ef985eSStephan Aßmus 		screenshotInfos = fPackage->ScreenshotInfos();
267d5ef985eSStephan Aßmus 
268d5ef985eSStephan Aßmus 	Unlock();
269d5ef985eSStephan Aßmus 
270d5ef985eSStephan Aßmus 	if (screenshotInfos.CountItems() == 0) {
271d5ef985eSStephan Aßmus 		printf("  package has no screenshots\n");
272d5ef985eSStephan Aßmus 		return;
273d5ef985eSStephan Aßmus 	}
274d5ef985eSStephan Aßmus 
275d5ef985eSStephan Aßmus 	// Obtain the correct code for the screenshot to display
276d5ef985eSStephan Aßmus 	// TODO: Once navigation buttons are added, we could use the
277d5ef985eSStephan Aßmus 	// ScreenshotInfo at the "current" index.
278c210060fSJulian Harnath 	const ScreenshotInfo& info = screenshotInfos.ItemAtFast(
279c210060fSJulian Harnath 		atomic_get(&fCurrentScreenshotIndex));
280d5ef985eSStephan Aßmus 
281d5ef985eSStephan Aßmus 	BMallocIO buffer;
282d5ef985eSStephan Aßmus 	WebAppInterface interface;
283d5ef985eSStephan Aßmus 
2843ca9d5e9SJulian Harnath 	// Only indicate being busy with the download if it takes a little while
285c210060fSJulian Harnath 	BMessenger messenger(this);
2863ca9d5e9SJulian Harnath 	BMessageRunner delayedMessenger(messenger,
2873ca9d5e9SJulian Harnath 		new BMessage(MSG_DOWNLOAD_START),
2883ca9d5e9SJulian Harnath 		kProgressIndicatorDelay, 1);
289c210060fSJulian Harnath 
290d5ef985eSStephan Aßmus 	// Retrieve screenshot from web-app
291d5ef985eSStephan Aßmus 	status_t status = interface.RetrieveScreenshot(info.Code(),
292d5ef985eSStephan Aßmus 		info.Width(), info.Height(), &buffer);
293c210060fSJulian Harnath 
2943ca9d5e9SJulian Harnath 	delayedMessenger.SetCount(0);
295c210060fSJulian Harnath 	messenger.SendMessage(MSG_DOWNLOAD_STOP);
296c210060fSJulian Harnath 
297d5ef985eSStephan Aßmus 	if (status == B_OK && Lock()) {
298d5ef985eSStephan Aßmus 		printf("got screenshot");
299d5ef985eSStephan Aßmus 		fScreenshot = BitmapRef(new(std::nothrow)SharedBitmap(buffer), true);
3002a36368bSMichael Lotz 		fScreenshotView->SetBitmap(fScreenshot);
3012afa3f3bSStephan Aßmus 		_ResizeToFitAndCenter();
302d5ef985eSStephan Aßmus 		Unlock();
303d5ef985eSStephan Aßmus 	} else {
304d5ef985eSStephan Aßmus 		printf("  failed to download screenshot\n");
305d5ef985eSStephan Aßmus 	}
306d5ef985eSStephan Aßmus }
3072afa3f3bSStephan Aßmus 
3082afa3f3bSStephan Aßmus 
3092afa3f3bSStephan Aßmus void
3102afa3f3bSStephan Aßmus ScreenshotWindow::_ResizeToFitAndCenter()
3112afa3f3bSStephan Aßmus {
312416c1160SJulian Harnath 	// Find out dimensions of the largest screenshot of this package
313416c1160SJulian Harnath 	ScreenshotInfoList screenshotInfos;
314416c1160SJulian Harnath 	if (fPackage.Get() != NULL)
315416c1160SJulian Harnath 		screenshotInfos = fPackage->ScreenshotInfos();
316416c1160SJulian Harnath 
317416c1160SJulian Harnath 	int32 largestScreenshotWidth = 0;
318416c1160SJulian Harnath 	int32 largestScreenshotHeight = 0;
319416c1160SJulian Harnath 
320416c1160SJulian Harnath 	const uint32 numScreenshots = fPackage->ScreenshotInfos().CountItems();
321416c1160SJulian Harnath 	for (uint32 i = 0; i < numScreenshots; i++) {
322416c1160SJulian Harnath 		const ScreenshotInfo& info = screenshotInfos.ItemAtFast(i);
323416c1160SJulian Harnath 		if (info.Width() > largestScreenshotWidth)
324416c1160SJulian Harnath 			largestScreenshotWidth = info.Width();
325416c1160SJulian Harnath 		if (info.Height() > largestScreenshotHeight)
326416c1160SJulian Harnath 			largestScreenshotHeight = info.Height();
327416c1160SJulian Harnath 	}
328416c1160SJulian Harnath 
329416c1160SJulian Harnath 	fScreenshotView->SetExplicitMinSize(
330416c1160SJulian Harnath 		BSize(largestScreenshotWidth, largestScreenshotHeight));
331416c1160SJulian Harnath 	Layout(false);
332416c1160SJulian Harnath 
333416c1160SJulian Harnath 	// TODO: Limit window size to screen size (with a little margin),
334416c1160SJulian Harnath 	//       the image should then become scrollable.
335416c1160SJulian Harnath 
3362afa3f3bSStephan Aßmus 	float minWidth;
3372afa3f3bSStephan Aßmus 	float minHeight;
3382afa3f3bSStephan Aßmus 	GetSizeLimits(&minWidth, NULL, &minHeight, NULL);
3392afa3f3bSStephan Aßmus 	ResizeTo(minWidth, minHeight);
3402afa3f3bSStephan Aßmus 	CenterOnScreen();
3412afa3f3bSStephan Aßmus }
342c210060fSJulian Harnath 
343c210060fSJulian Harnath 
344c210060fSJulian Harnath void
345c210060fSJulian Harnath ScreenshotWindow::_UpdateToolBar()
346c210060fSJulian Harnath {
347416c1160SJulian Harnath 	const int32 numScreenshots = fPackage->ScreenshotInfos().CountItems();
348416c1160SJulian Harnath 	const int32 currentIndex = atomic_get(&fCurrentScreenshotIndex);
349416c1160SJulian Harnath 
350c210060fSJulian Harnath 	fToolBar->SetActionEnabled(MSG_PREVIOUS_SCREENSHOT,
351c210060fSJulian Harnath 		currentIndex > 0);
352c210060fSJulian Harnath 	fToolBar->SetActionEnabled(MSG_NEXT_SCREENSHOT,
353416c1160SJulian Harnath 		currentIndex < numScreenshots - 1);
354c210060fSJulian Harnath 
355c210060fSJulian Harnath 	BString text;
356c210060fSJulian Harnath 	text << currentIndex + 1;
357c210060fSJulian Harnath 	text << " / ";
358416c1160SJulian Harnath 	text << numScreenshots;
359c210060fSJulian Harnath 	fIndexView->SetText(text);
360c210060fSJulian Harnath }
361