⛏️ index : haiku.git

/*
 * Copyright 2023 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		John Scipione, jscipione@gmail.com
 */


#include "StatusMenuField.h"

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

#include <Bitmap.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <IconUtils.h>
#include <InterfaceDefs.h>
#include <LayoutUtils.h>


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Modifier keys window"


#ifdef DEBUG_ALERT
#	define FTRACE(x) fprintf(x)
#else
#	define FTRACE(x) /* nothing */
#endif


static const char* kDuplicate = "duplicate";
static const char* kUnmatched = "unmatched";


//	#pragma mark - StatusMenuItem


StatusMenuItem::StatusMenuItem(const char* name, BMessage* message)
	:
	BMenuItem(name, message),
	fIcon(NULL)
{
}


StatusMenuItem::StatusMenuItem(BMessage* archive)
	:
	BMenuItem(archive),
	fIcon(NULL)
{
}


BArchivable*
StatusMenuItem::Instantiate(BMessage* data)
{
	if (validate_instantiation(data, "StatusMenuItem"))
		return new StatusMenuItem(data);

	return NULL;
}


status_t
StatusMenuItem::Archive(BMessage* data, bool deep) const
{
	status_t result = BMenuItem::Archive(data, deep);

	return result;
}


void
StatusMenuItem::DrawContent()
{
	if (fIcon == NULL)
		return BMenuItem::DrawContent();

	// blend transparency
	Menu()->SetDrawingMode(B_OP_ALPHA);
	Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);

	// draw bitmap
	Menu()->DrawBitmapAsync(fIcon, IconRect().LeftTop());

	BMenuItem::DrawContent();
}


void
StatusMenuItem::GetContentSize(float* _width, float* _height)
{
	float width;
	float height;
	BMenuItem::GetContentSize(&width, &height);

	// prevent label from drawing over icon
	if (_width != NULL)
		*_width = Menu()->Bounds().Width() - IconRect().Width() - Spacing() * 4;

	if (_height != NULL)
		*_height = height;
}


BBitmap*
StatusMenuItem::Icon()
{
	return fIcon;
}


void
StatusMenuItem::SetIcon(BBitmap* icon)
{
	fIcon = icon;
}


BRect
StatusMenuItem::IconRect()
{
	BRect bounds(Menu()->Bounds());
	bounds.top += roundf(Spacing() / 2); // center inside menu field vertically
	bounds.right -= Spacing() * 3; // move inside menu field horizontally
	return BLayoutUtils::AlignInFrame(bounds, IconSize(),
		BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP));
}


BSize
StatusMenuItem::IconSize()
{
	return be_control_look->ComposeIconSize(B_MINI_ICON);
}


float
StatusMenuItem::Spacing()
{
	return be_control_look->DefaultLabelSpacing();
}


//	#pragma mark - StatusMenuField


StatusMenuField::StatusMenuField(const char* label, BMenu* menu)
	:
	BMenuField(label, menu),
	fStatus(B_EMPTY_STRING),
	fStopIcon(NULL),
	fWarnIcon(NULL)
{
	_FillIcons();
}


StatusMenuField::~StatusMenuField()
{
	delete fStopIcon;
	delete fWarnIcon;
}


void
StatusMenuField::SetDuplicate(bool on)
{
	ShowStopIcon(on);
	on ? SetStatus(kDuplicate) : ClearStatus();
	Invalidate();
}


void
StatusMenuField::SetUnmatched(bool on)
{
	ShowWarnIcon(on);
	on ? SetStatus(kUnmatched) : ClearStatus();
	Invalidate();
}


void
StatusMenuField::ShowStopIcon(bool show)
{
	// show or hide the stop icon
	StatusMenuItem* item = dynamic_cast<StatusMenuItem*>(MenuItem());
	if (item != NULL)
		item->SetIcon(show ? fStopIcon : NULL);
}


void
StatusMenuField::ShowWarnIcon(bool show)
{
	// show or hide the warn icon
	StatusMenuItem* item = dynamic_cast<StatusMenuItem*>(MenuItem());
	if (item != NULL)
		item->SetIcon(show ? fWarnIcon : NULL);
}


void
StatusMenuField::ClearStatus()
{
	fStatus = B_EMPTY_STRING;
	SetToolTip((const char*)NULL);
}


void
StatusMenuField::SetStatus(BString status)
{
	fStatus = status;

	const char* tooltip = B_EMPTY_STRING;
	if (fStatus == kDuplicate)
		tooltip = B_TRANSLATE("Error: duplicate keys");
	else if (fStatus == kUnmatched)
		tooltip = B_TRANSLATE("Warning: left and right key roles do not match");

	SetToolTip(tooltip);
}


//	#pragma mark - StatusMenuField private methods


void
StatusMenuField::_FillIcons()
{
	// fill out the icons with the stop and warn icons from app_server
	// TODO find better icons

	if (fStopIcon == NULL) {
		// allocate the fStopIcon bitmap
		fStopIcon = new (std::nothrow) BBitmap(_IconRect(), 0, B_RGBA32);
		if (fStopIcon == NULL || fStopIcon->InitCheck() != B_OK) {
			FTRACE((stderr, "MKW::_FillIcons() - No memory for stop bitmap\n"));
			delete fStopIcon;
			fStopIcon = NULL;
			return;
		}

		// load dialog-error icon bitmap
		if (BIconUtils::GetSystemIcon("dialog-error", fStopIcon) != B_OK) {
			delete fStopIcon;
			fStopIcon = NULL;
			return;
		}
	}

	if (fWarnIcon == NULL) {
		// allocate the fWarnIcon bitmap
		fWarnIcon = new (std::nothrow) BBitmap(_IconRect(), 0, B_RGBA32);
		if (fWarnIcon == NULL || fWarnIcon->InitCheck() != B_OK) {
			FTRACE((stderr, "MKW::_FillIcons() - No memory for warn bitmap\n"));
			delete fWarnIcon;
			fWarnIcon = NULL;
			return;
		}

		// load dialog-warning icon bitmap
		if (BIconUtils::GetSystemIcon("dialog-warning", fWarnIcon) != B_OK) {
			delete fWarnIcon;
			fWarnIcon = NULL;
			return;
		}
	}
}


BRect
StatusMenuField::_IconRect()
{
	BSize iconSize = be_control_look->ComposeIconSize(B_MINI_ICON);
	return BRect(0, 0, iconSize.Width() - 1, iconSize.Height() - 1);
}