* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* Copyright 2020-2024, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "ScreenshotWindow.h"
#include <algorithm>
#include <Autolock.h>
#include <Catalog.h>
#include <LayoutBuilder.h>
#include <MessageRunner.h>
#include <StringView.h>
#include "BarberPole.h"
#include "BitmapView.h"
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "Model.h"
#include "PackageUtils.h"
#include "SharedIcons.h"
#include "WebAppInterface.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ScreenshotWindow"
static const rgb_color kBackgroundColor = { 51, 102, 152, 255 };
ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame, Model* model)
:
BWindow(frame, B_TRANSLATE("Screenshot"),
B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
fBarberPoleShown(false),
fDownloadPending(false),
fWorkerThread(-1),
fModel(model)
{
AddToSubset(parent);
atomic_set(&fCurrentScreenshotIndex, 0);
fBarberPole = new BarberPole("barber pole");
fBarberPole->SetExplicitMaxSize(BSize(100, B_SIZE_UNLIMITED));
fBarberPole->Hide();
fIndexView = new BStringView("screenshot index", NULL);
fToolBar = new BToolBar();
fToolBar->AddAction(MSG_PREVIOUS_SCREENSHOT, this,
SharedIcons::IconArrowLeft22Scaled()->Bitmap(), NULL, NULL);
fToolBar->AddAction(MSG_NEXT_SCREENSHOT, this, SharedIcons::IconArrowRight22Scaled()->Bitmap(),
NULL, NULL);
fToolBar->AddView(fIndexView);
fToolBar->AddGlue();
fToolBar->AddView(fBarberPole);
fScreenshotView = new BitmapView("screenshot view");
fScreenshotView->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
fScreenshotView->SetScaleBitmap(false);
BGroupView* groupView = new BGroupView(B_VERTICAL);
groupView->SetViewColor(kBackgroundColor);
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(0, 3, 0, 0)
.Add(fToolBar)
.AddStrut(3)
.AddGroup(groupView)
.Add(fScreenshotView)
.SetInsets(B_USE_WINDOW_INSETS)
.End()
;
fScreenshotView->SetLowColor(kBackgroundColor);
CenterOnScreen();
}
ScreenshotWindow::~ScreenshotWindow()
{
BAutolock locker(&fLock);
if (fWorkerThread >= 0)
wait_for_thread(fWorkerThread, NULL);
}
bool
ScreenshotWindow::QuitRequested()
{
if (fOnCloseTarget.IsValid() && fOnCloseMessage.what != 0)
fOnCloseTarget.SendMessage(&fOnCloseMessage);
Hide();
return false;
}
void
ScreenshotWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_NEXT_SCREENSHOT:
{
atomic_add(&fCurrentScreenshotIndex, 1);
_UpdateToolBar();
_DownloadScreenshot();
break;
}
case MSG_PREVIOUS_SCREENSHOT:
atomic_add(&fCurrentScreenshotIndex, -1);
_UpdateToolBar();
_DownloadScreenshot();
break;
case MSG_DOWNLOAD_START:
if (!fBarberPoleShown) {
fBarberPole->Start();
fBarberPole->Show();
fBarberPoleShown = true;
}
break;
case MSG_DOWNLOAD_STOP:
if (fBarberPoleShown) {
fBarberPole->Hide();
fBarberPole->Stop();
fBarberPoleShown = true;
}
break;
default:
BWindow::MessageReceived(message);
break;
}
}
void
ScreenshotWindow::SetOnCloseMessage(
const BMessenger& messenger, const BMessage& message)
{
fOnCloseTarget = messenger;
fOnCloseMessage = message;
}
void
ScreenshotWindow::SetPackage(const PackageInfoRef& package)
{
if (!package.IsSet())
HDFATAL("attempt to provide an unset package");
if (fPackage == package)
return;
fPackage = package;
BString title = B_TRANSLATE("Screenshot");
PackageUtils::TitleOrName(fPackage, title);
SetTitle(title);
if (package.IsSet())
_DownloadScreenshot();
atomic_set(&fCurrentScreenshotIndex, 0);
_UpdateToolBar();
}
void
ScreenshotWindow::_DownloadScreenshot()
{
BAutolock locker(&fLock);
if (fWorkerThread >= 0) {
fDownloadPending = true;
return;
}
thread_id thread = spawn_thread(&_DownloadThreadEntry,
"Screenshot Loader", B_NORMAL_PRIORITY, this);
if (thread >= 0)
_SetWorkerThread(thread);
}
void
ScreenshotWindow::_SetWorkerThread(thread_id thread)
{
if (!Lock())
return;
if (thread >= 0) {
fWorkerThread = thread;
resume_thread(fWorkerThread);
} else {
fWorkerThread = -1;
if (fDownloadPending) {
_DownloadScreenshot();
fDownloadPending = false;
}
}
Unlock();
}
int32
ScreenshotWindow::_DownloadThreadEntry(void* data)
{
ScreenshotWindow* window
= reinterpret_cast<ScreenshotWindow*>(data);
window->_DownloadThread();
window->_SetWorkerThread(-1);
return 0;
}
void
ScreenshotWindow::_DownloadThread()
{
ScreenshotInfoRef info;
if (!Lock()) {
HDERROR("failed to lock screenshot window");
return;
}
fScreenshotView->UnsetBitmap();
_ResizeToFitAndCenter();
if (!fPackage.IsSet())
HDINFO("package not set");
else {
PackageScreenshotInfoRef screenshotInfo = fPackage->ScreenshotInfo();
if (!screenshotInfo.IsSet() || screenshotInfo->Count() == 0) {
HDINFO("package has no screenshots");
} else {
int32 index = atomic_get(&fCurrentScreenshotIndex);
info = screenshotInfo->ScreenshotAtIndex(index);
}
}
Unlock();
if (!info.IsSet()) {
HDINFO("screenshot not set");
return;
}
BMessenger messenger(this);
BMessageRunner delayedMessenger(messenger,
new BMessage(MSG_DOWNLOAD_START),
kProgressIndicatorDelay, 1);
BitmapHolderRef screenshot;
status_t status = fModel->GetPackageScreenshotRepository()->LoadScreenshot(
ScreenshotCoordinate(info->Code(), info->Width(), info->Height()), screenshot);
delayedMessenger.SetCount(0);
messenger.SendMessage(MSG_DOWNLOAD_STOP);
if (status == B_OK && Lock()) {
HDINFO("got screenshot");
fScreenshot = screenshot;
fScreenshotView->SetBitmap(fScreenshot);
_ResizeToFitAndCenter();
Unlock();
} else
HDERROR("failed to download screenshot");
}
BSize
ScreenshotWindow::_MaxWidthAndHeightOfAllScreenshots()
{
BSize size(0, 0);
if (fPackage.IsSet()) {
PackageScreenshotInfoRef screenshotInfo = fPackage->ScreenshotInfo();
int count = 0;
if (screenshotInfo.IsSet())
count = screenshotInfo->Count();
for(int32 i = 0; i < count; i++) {
const ScreenshotInfoRef& screenshot = screenshotInfo->ScreenshotAtIndex(i);
if (screenshot.IsSet()) {
float w = static_cast<float>(screenshot->Width());
float h = static_cast<float>(screenshot->Height());
if (w > size.Width())
size.SetWidth(w);
if (h > size.Height())
size.SetHeight(h);
}
}
}
return size;
}
void
ScreenshotWindow::_ResizeToFitAndCenter()
{
fScreenshotView->SetExplicitMinSize(_MaxWidthAndHeightOfAllScreenshots());
Layout(false);
float minWidth;
float minHeight;
GetSizeLimits(&minWidth, NULL, &minHeight, NULL);
ResizeTo(minWidth, minHeight);
CenterOnScreen();
}
void
ScreenshotWindow::_UpdateToolBar()
{
int32 numScreenshots = 0;
if (fPackage.IsSet()) {
PackageScreenshotInfoRef screenshotInfo = fPackage->ScreenshotInfo();
if (screenshotInfo.IsSet())
numScreenshots = screenshotInfo->Count();
}
const int32 currentIndex = atomic_get(&fCurrentScreenshotIndex);
fToolBar->SetActionEnabled(MSG_PREVIOUS_SCREENSHOT,
currentIndex > 0);
fToolBar->SetActionEnabled(MSG_NEXT_SCREENSHOT,
currentIndex < numScreenshots - 1);
BString text;
text << currentIndex + 1;
text << " / ";
text << numScreenshots;
fIndexView->SetText(text);
}