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