⛏️ index : haiku.git

#include <ControlLook.h>
#include <ScrollView.h>
#include <String.h>
#include <TextView.h>
#include <limits.h>

#include "ErrorLogWindow.h"

class Error : public BView {
	public:
		Error(BRect rect, alert_type type, const char *tag, const char *message, bool timestamp,
			rgb_color backgroundColor, rgb_color foregroundColor);

		void GetPreferredSize(float *width, float *height);
		void Draw(BRect updateRect);
		void FrameResized(float w, float h);
	private:
		alert_type type;
};


class ErrorPanel : public BView {
	public:
		ErrorPanel(BRect rect) : BView(rect, "ErrorScrollPanel", B_FOLLOW_ALL_SIDES,
			B_DRAW_ON_CHILDREN | B_FRAME_EVENTS), alerts_displayed(0), add_next_at(0)
		{
			AdoptSystemColors();
		}

		void GetPreferredSize(float *width, float *height) {
			*width = Bounds().Width();
			*height = add_next_at;
		}

		void TargetedByScrollView(BScrollView *scroll_view)
		{
			scroll = scroll_view;
		}

		void FrameResized(float w, float /*h*/) {
			add_next_at = 0;
			for (int32 i = 0; i < CountChildren(); i++) {
				ChildAt(i)->MoveTo(BPoint(0,add_next_at));
				ChildAt(i)->ResizeTo(w, ChildAt(i)->Frame().Height());
				ChildAt(i)->ResizeToPreferred();
				add_next_at += ChildAt(i)->Bounds().Height();
			}
		}
		int32 alerts_displayed;
		float add_next_at;
		BScrollView *scroll;
};


//	#pragma mark -


ErrorLogWindow::ErrorLogWindow(BRect rect, const char *name, window_type type)
	:
	BWindow(rect, name, type, B_NO_WORKSPACE_ACTIVATION | B_NOT_MINIMIZABLE
		| B_ASYNCHRONOUS_CONTROLS),
	fIsRunning(false)
{
	SetColumnColors(ui_color(B_LIST_BACKGROUND_COLOR));
	fTextColor = ui_color(B_LIST_ITEM_TEXT_COLOR);

	rect = Bounds();
	rect.right -= be_control_look->GetScrollBarWidth(B_VERTICAL);

	view = new ErrorPanel(rect);
	AddChild(new BScrollView("ErrorScroller", view, B_FOLLOW_ALL_SIDES, 0, false, true));
	Show();
	Hide();
}


void
ErrorLogWindow::AddError(alert_type type, const char *message, const char *tag, bool timestamp)
{
	ErrorPanel *panel = (ErrorPanel *)view;

	// first call?
	if (!fIsRunning) {
		fIsRunning = true;
		Show();
	}

	Lock();

	Error *newError = new Error(BRect(0, panel->add_next_at, panel->Bounds().right,
		panel->add_next_at + 1), type, tag, message, timestamp,
		(panel->alerts_displayed++ % 2 == 0) ? fColumnColor : fColumnAlternateColor, fTextColor);

	newError->ResizeToPreferred();
	panel->add_next_at += newError->Bounds().Height();
	panel->AddChild(newError);
	panel->ResizeToPreferred();

	if (panel->add_next_at > Frame().Height()) {
		BScrollBar *bar = panel->scroll->ScrollBar(B_VERTICAL);

		bar->SetRange(0, panel->add_next_at - Frame().Height());
		bar->SetSteps(1, Frame().Height());
		bar->SetProportion(Frame().Height() / panel->add_next_at);
	} else
		panel->scroll->ScrollBar(B_VERTICAL)->SetRange(0,0);

	if (IsHidden())
		Show();

	Unlock();
}


void
ErrorLogWindow::MessageReceived(BMessage* message)
{
	rgb_color color;
	if (message->what == B_COLORS_UPDATED) {
		if (message->FindColor(ui_color_name(B_LIST_BACKGROUND_COLOR), &color) == B_OK)
			SetColumnColors(color);

		if (message->FindColor(ui_color_name(B_LIST_ITEM_TEXT_COLOR), &color) == B_OK)
			fTextColor = color;
	}
	BWindow::MessageReceived(message);
}


bool
ErrorLogWindow::QuitRequested()
{
	Hide();

	while (view->CountChildren() != 0) {
		BView* child = view->ChildAt(0);
		view->RemoveChild(child);
		delete child;
	}

	ErrorPanel *panel = (ErrorPanel *)(view);
	panel->add_next_at = 0;
	panel->alerts_displayed = 0;

	view->ResizeToPreferred();
	return false;
}


void
ErrorLogWindow::FrameResized(float newWidth, float newHeight)
{
	ErrorPanel *panel = (ErrorPanel *)view;
	panel->Invalidate();

	if (panel->add_next_at > newHeight) {
		BScrollBar *bar = panel->scroll->ScrollBar(B_VERTICAL);

		bar->SetRange(0, panel->add_next_at - Frame().Height());
		bar->SetSteps(1, Frame().Height());
		bar->SetProportion(Frame().Height() / panel->add_next_at);
	} else
		panel->scroll->ScrollBar(B_VERTICAL)->SetRange(0,0);
}


void
ErrorLogWindow::SetColumnColors(rgb_color color)
{
	fColumnColor = color;
	if (fColumnColor.IsDark())
		fColumnAlternateColor = tint_color(color, 0.85f);
	else
		fColumnAlternateColor = tint_color(color, B_DARKEN_2_TINT);
}


//	#pragma mark -


Error::Error(BRect rect, alert_type atype, const char *tag, const char *message,
	bool timestamp, rgb_color backgroundColor, rgb_color foregroundColor)
	:
	BView(rect,"error", B_FOLLOW_LEFT | B_FOLLOW_RIGHT | B_FOLLOW_TOP,
		B_NAVIGABLE | B_WILL_DRAW | B_FRAME_EVENTS), type(atype)
{
	SetViewColor(backgroundColor);
	SetLowColor(backgroundColor);

	text_run_array array;
	array.count = 1;
	array.runs[0].offset = 0;
	array.runs[0].font = *be_bold_font;
	array.runs[0].color = foregroundColor;

	BString msgString(message);
	msgString.RemoveAll("\r");

	BTextView *view = new BTextView(BRect(20, 0, rect.Width(), rect.Height()),
		"error_display", BRect(0,3,rect.Width() - 20 - 3, LONG_MAX),
		B_FOLLOW_ALL_SIDES);
	view->SetLowColor(backgroundColor);
	view->SetViewColor(backgroundColor);
	view->SetText(msgString.String());
	view->MakeSelectable(true);
	view->SetStylable(true);
	view->MakeEditable(false);

	if (tag != NULL) {
		BString tagString(tag);
		tagString += " ";
		view->Insert(0, tagString.String(), tagString.Length(), &array);
	}

	if (timestamp) {
		array.runs[0].color = foregroundColor.IsLight()
			? tint_color(foregroundColor, B_LIGHTEN_1_TINT)
			: tint_color(foregroundColor, B_DARKEN_2_TINT);
		array.runs[0].font.SetSize(9);
		time_t thetime = time(NULL);
		BString atime = asctime(localtime(&thetime));
		atime.Prepend(" [");
		atime.RemoveAll("\n");
		atime.Append("]");
		view->Insert(view->TextLength(),atime.String(),atime.Length(),&array);
	}

	float height,width;
	width = view->Frame().Width();
	height = view->TextHeight(0,view->CountLines()) + 3;
	view->ResizeTo(width,height);
	AddChild(view);
}


void
Error::GetPreferredSize(float *width, float *height)
{
	BTextView *view = static_cast<BTextView *>(FindView("error_display"));

	*width = view->Frame().Width() + 20;
	*height = view->TextHeight(0, INT32_MAX) + 3;
}


void
Error::Draw(BRect updateRect)
{
	FillRect(updateRect, B_SOLID_LOW);
}


void
Error::FrameResized(float w, float h)
{
	BTextView *view = static_cast<BTextView *>(FindView("error_display"));

	view->ResizeTo(w - 20, h);
	view->SetTextRect(BRect(0, 3, w - 20, h));
}