⛏️ index : haiku.git

/*
 * Copyright 2010, Axel DΓΆrfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "RadioView.h"

#include <stdio.h>

#include <MessageRunner.h>


const uint32 kMsgPulse = 'puls';

const bigtime_t kMinPulseInterval = 100000;
const bigtime_t kMaxPulseInterval = 300000;
const float kMinStep = 3.f;


RadioView::RadioView(BRect frame, const char* name, int32 resizingMode)
	:
	BView(frame, name, resizingMode,
		B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW | B_FRAME_EVENTS),
	fPercent(0),
	fPulse(NULL),
	fPhase(0),
	fMax(DefaultMax())
{
}


RadioView::~RadioView()
{
}


void
RadioView::SetPercent(int32 percent)
{
	if (percent < 0)
		percent = 0;
	if (percent > 100)
		percent = 100;

	if (percent == fPercent)
		return;

	fPercent = percent;
	Invalidate();
}


void
RadioView::SetMax(int32 max)
{
	if (max < 0)
		max = 0;
	if (max > 100)
		max = 100;
	if (max == fMax)
		return;

	fMax = max;
	Invalidate();
}


void
RadioView::StartPulsing()
{
	fPhase = 0;
	_RestartPulsing();
}


void
RadioView::StopPulsing()
{
	if (!IsPulsing())
		return;

	delete fPulse;
	fPulse = NULL;
	fPhase = 0;
	Invalidate();
}


/*static*/ void
RadioView::Draw(BView* view, BRect rect, int32 percent, int32 maxCount)
{
	view->PushState();

	BPoint center;
	int32 count;
	float step;
	_Compute(rect, center, count, maxCount, step);

	for (int32 i = 0; i < count; i++) {
		_SetColor(view, percent, 0, i, count);
		_DrawBow(view, i, center, count, step);
	}
	view->PopState();
}


/*static*/ int32
RadioView::DefaultMax()
{
	return 7;
}


void
RadioView::AttachedToWindow()
{
	if (Parent() != NULL)
		SetViewColor(Parent()->ViewColor());
	else
		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}


void
RadioView::DetachedFromWindow()
{
	StopPulsing();
}


void
RadioView::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case kMsgPulse:
			fPhase++;
			Invalidate();
			break;

		default:
			BView::MessageReceived(message);
			break;
	}
}


void
RadioView::Draw(BRect updateRect)
{
	SetLowColor(ViewColor());

	BPoint center;
	int32 count;
	float step;
	_Compute(Bounds(), center, count, fMax, step);

	for (int32 i = 0; i < count; i++) {
		_SetColor(this, fPercent, fPhase, i, count);
		if (step == kMinStep && _IsDisabled(fPercent, i, count))
			continue;

		_DrawBow(this, i, center, count, step);
	}
}


void
RadioView::FrameResized(float /*width*/, float /*height*/)
{
	if (IsPulsing())
		_RestartPulsing();
}


void
RadioView::_RestartPulsing()
{
	delete fPulse;

	// The pulse speed depends on the size of the view
	BPoint center;
	int32 count;
	float step;
	_Compute(Bounds(), center, count, fMax, step);

	BMessage message(kMsgPulse);
	fPulse = new BMessageRunner(this, &message, (bigtime_t)(kMinPulseInterval
			+ (kMaxPulseInterval - kMinPulseInterval) / step), -1);
}


/*static*/ void
RadioView::_Compute(const BRect& bounds, BPoint& center, int32& count,
	int32 max, float& step)
{
	center.Set(roundf(bounds.Width() / 2), bounds.bottom);
	float size = min_c(center.x * 3 / 2, center.y);
	step = floorf(size / max);
	if (step < kMinStep) {
		count = (int32)(size / kMinStep);
		step = kMinStep;
	} else
		count = max;

	center.x += bounds.left;
}


/*static*/ void
RadioView::_DrawBow(BView* view, int32 index, const BPoint& center,
	int32 count, float step)
{
	float radius = step * index + 1;

	if (step < 4)
		view->SetPenSize(step / 2);
	else
		view->SetPenSize(step * 2 / 3);

	view->SetLineMode(B_ROUND_CAP, B_ROUND_JOIN);
	view->StrokeArc(center, radius, radius, 50, 80);
}


/*static*/ void
RadioView::_SetColor(BView* view, int32 percent, int32 phase, int32 index,
	int32 count)
{
	if (_IsDisabled(percent, index, count)) {
		// disabled
		view->SetHighColor(tint_color(view->LowColor(), B_DARKEN_1_TINT));
	} else if (phase == 0 || phase % count != index) {
		// enabled
		view->SetHighColor(tint_color(view->LowColor(), B_DARKEN_3_TINT));
	} else {
		// pulsing
		view->SetHighColor(tint_color(view->LowColor(), B_DARKEN_2_TINT));
	}
}


/*static*/ bool
RadioView::_IsDisabled(int32 percent, int32 index, int32 count)
{
	return percent < 100 * index / count;
}