⛏️ index : haiku.git

/*
 * Copyright 2021, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Emmanuel Gil Peyrot
 */


#include "ConfigView.h"

#include <stdio.h>
#include <string.h>

#include <Catalog.h>
#include <CheckBox.h>
#include <LayoutBuilder.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Message.h>
#include <PopUpMenu.h>
#include <Slider.h>
#include <StringView.h>

#include "avif/avif.h"

#include "TranslatorSettings.h"
#include "AVIFTranslator.h"


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ConfigView"


static const uint32 kMsgLossless = 'losl';
static const uint32 kMsgPixelFormat = 'pfmt';
static const uint32 kMsgQuality	= 'qlty';
static const uint32 kMsgSpeed = 'sped';
static const uint32 kMsgTilesHorizontal = 'tilh';
static const uint32 kMsgTilesVertical = 'tilv';


ConfigView::ConfigView(TranslatorSettings* settings)
	:
	BGroupView(B_TRANSLATE("AVIFTranslator Settings"), B_VERTICAL),
	fSettings(settings)
{
	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);

	BStringView* title = new BStringView("title",
		B_TRANSLATE("AVIF image translator"));
	title->SetFont(be_bold_font);

	char versionString[256];
	sprintf(versionString, "v%d.%d.%d, %s",
		static_cast<int>(B_TRANSLATION_MAJOR_VERSION(AVIF_TRANSLATOR_VERSION)),
		static_cast<int>(B_TRANSLATION_MINOR_VERSION(AVIF_TRANSLATOR_VERSION)),
		static_cast<int>(B_TRANSLATION_REVISION_VERSION(
			AVIF_TRANSLATOR_VERSION)),
		__DATE__);

	BStringView* version = new BStringView("version", versionString);

	BString copyrightsText;
	BStringView *copyrightView = new BStringView("Copyright",
		B_TRANSLATE(B_UTF8_COPYRIGHT "2021 Emmanuel Gil Peyrot"));

	BString libavifInfo = B_TRANSLATE(
		"Based on libavif %version%");
	libavifInfo.ReplaceAll("%version%", avifVersion());

	BStringView *copyright2View = new BStringView("Copyright2",
		libavifInfo.String());
	BStringView *copyright3View = new BStringView("Copyright3",
		B_TRANSLATE(B_UTF8_COPYRIGHT "2019 Joe Drago. All rights reserved."));

	// output parameters

	fLosslessCheckBox = new BCheckBox("lossless",
		B_TRANSLATE("Lossless"), new BMessage(kMsgLossless));
	bool lossless;
	fSettings->SetGetBool(AVIF_SETTING_LOSSLESS, &lossless);
	if (lossless)
		fLosslessCheckBox->SetValue(B_CONTROL_ON);

	fPixelFormatMenu = new BPopUpMenu(B_TRANSLATE("Pixel format"));

	static const avifPixelFormat pixelFormats[4] = {
		AVIF_PIXEL_FORMAT_YUV444,
		AVIF_PIXEL_FORMAT_YUV420,
		AVIF_PIXEL_FORMAT_YUV400,
		AVIF_PIXEL_FORMAT_YUV422,
	};
	for (size_t i = 0; i < 4; ++i) {
		BMessage* msg = new BMessage(kMsgPixelFormat);
		msg->AddInt32("value", pixelFormats[i]);

		BMenuItem* item = new BMenuItem(
			avifPixelFormatToString(pixelFormats[i]), msg);
		if (fSettings->SetGetInt32(AVIF_SETTING_PIXEL_FORMAT) == pixelFormats[i])
			item->SetMarked(true);
		fPixelFormatMenu->AddItem(item);
	}

	BMenuField* pixelFormatField = new BMenuField(B_TRANSLATE("Pixel format:"),
		fPixelFormatMenu);

	rgb_color barColor = { 0, 0, 229, 255 };

	fQualitySlider = new BSlider("quality", B_TRANSLATE("Output quality:"),
		new BMessage(kMsgQuality), AVIF_QUANTIZER_BEST_QUALITY,
		AVIF_QUANTIZER_WORST_QUALITY, B_HORIZONTAL, B_BLOCK_THUMB);
	fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
	fQualitySlider->SetHashMarkCount(8);
	fQualitySlider->SetLimitLabels(B_TRANSLATE("Best"), B_TRANSLATE("Worst"));
	fQualitySlider->UseFillColor(true, &barColor);
	fQualitySlider->SetValue(fSettings->SetGetInt32(AVIF_SETTING_QUALITY));
	fQualitySlider->SetEnabled(!lossless);

	fSpeedSlider = new BSlider("speed", B_TRANSLATE("Compression speed:"),
		new BMessage(kMsgSpeed), AVIF_SPEED_SLOWEST,
		AVIF_SPEED_FASTEST, B_HORIZONTAL, B_BLOCK_THUMB);
	fSpeedSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
	fSpeedSlider->SetHashMarkCount(11);
	fSpeedSlider->SetLimitLabels(B_TRANSLATE("Slow"),
		B_TRANSLATE("Faster but worse quality"));
	fSpeedSlider->UseFillColor(true, &barColor);
	fSpeedSlider->SetValue(fSettings->SetGetInt32(AVIF_SETTING_SPEED));

	fHTilesSlider = new BSlider("htiles", B_TRANSLATE("Horizontal tiles:"),
		new BMessage(kMsgTilesHorizontal), 1, 6, B_HORIZONTAL,
		B_BLOCK_THUMB);
	fHTilesSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
	fHTilesSlider->SetHashMarkCount(6);
	fHTilesSlider->SetLimitLabels(B_TRANSLATE("1"),
		B_TRANSLATE("2⁶"));
	fHTilesSlider->UseFillColor(true, &barColor);
	fHTilesSlider->SetValue(
		fSettings->SetGetInt32(AVIF_SETTING_TILES_HORIZONTAL));

	fVTilesSlider = new BSlider("vtiles", B_TRANSLATE("Vertical tiles:"),
		new BMessage(kMsgTilesVertical), 1, 6, B_HORIZONTAL,
		B_BLOCK_THUMB);
	fVTilesSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
	fVTilesSlider->SetHashMarkCount(6);
	fVTilesSlider->SetLimitLabels(B_TRANSLATE("1"),
		B_TRANSLATE("2⁶"));
	fVTilesSlider->UseFillColor(true, &barColor);
	fVTilesSlider->SetValue(
		fSettings->SetGetInt32(AVIF_SETTING_TILES_VERTICAL));

	// Build the layout
	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
		.SetInsets(B_USE_DEFAULT_SPACING)
		.Add(title)
		.Add(version)
		.Add(copyrightView)
		.AddGlue()
		.Add(fLosslessCheckBox)
		.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
		        .Add(pixelFormatField->CreateLabelLayoutItem(), 0, 0)
			.AddGroup(B_HORIZONTAL, 0.0f, 1, 0)
				.Add(pixelFormatField->CreateMenuBarLayoutItem(), 0.0f)
				.AddGlue()
				.End()
			.End()
		.Add(fQualitySlider)
		.Add(fSpeedSlider)
		.Add(fHTilesSlider)
		.Add(fVTilesSlider)
		.AddGlue()
		.Add(copyright2View)
		.Add(copyright3View);
}


ConfigView::~ConfigView()
{
	fSettings->Release();
}


void
ConfigView::AttachedToWindow()
{
	BGroupView::AttachedToWindow();

	fQualitySlider->SetTarget(this);
	fSpeedSlider->SetTarget(this);
	fHTilesSlider->SetTarget(this);
	fVTilesSlider->SetTarget(this);

	if (Parent() == NULL && Window()->GetLayout() == NULL) {
		Window()->SetLayout(new BGroupLayout(B_VERTICAL));
		Window()->ResizeTo(PreferredSize().Width(),
			PreferredSize().Height());
	}
}


void
ConfigView::MessageReceived(BMessage* message)
{
	struct {
		const char*		name;
		uint32			what;
		TranSettingType	type;
	} maps[] = {
		{ AVIF_SETTING_LOSSLESS, kMsgLossless, TRAN_SETTING_BOOL },
		{ AVIF_SETTING_PIXEL_FORMAT, kMsgPixelFormat,
			TRAN_SETTING_INT32 },
		{ AVIF_SETTING_QUALITY, kMsgQuality, TRAN_SETTING_INT32 },
		{ AVIF_SETTING_SPEED, kMsgSpeed, TRAN_SETTING_INT32 },
		{ AVIF_SETTING_TILES_HORIZONTAL, kMsgTilesHorizontal,
			TRAN_SETTING_INT32 },
		{ AVIF_SETTING_TILES_VERTICAL, kMsgTilesVertical,
			TRAN_SETTING_INT32 },
		{ NULL }
	};

	int i;
	for (i = 0; maps[i].name != NULL; i++) {
		if (maps[i].what == message->what)
			break;
	}

	if (maps[i].name == NULL) {
		BGroupView::MessageReceived(message);
		return;
	}

	int32 value;
	if (message->FindInt32("value", &value) == B_OK
		|| message->FindInt32("be:value", &value) == B_OK) {
		switch(maps[i].type) {
			case TRAN_SETTING_BOOL:
			{
				bool boolValue = value;
				fSettings->SetGetBool(maps[i].name, &boolValue);
				if (maps[i].what == kMsgLossless)
					fSpeedSlider->SetEnabled(!boolValue);
				break;
			}
			case TRAN_SETTING_INT32:
				fSettings->SetGetInt32(maps[i].name, &value);
				break;
		}
		fSettings->SaveSettings();
	}
}