/*
 * Copyright 2002-2012, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Vlad Slepukhin
 *		Siarzhuk Zharski
 */
 
 
#include "StatusView.h"
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <Catalog.h>
#include <CharacterSet.h>
#include <CharacterSetRoster.h>
#include <ControlLook.h>
#include <MenuItem.h>
#include <Message.h>
#include <PopUpMenu.h>
#include <ScrollView.h>
#include <StringView.h>
#include <Window.h>
 
#include <tracker_private.h>
#include "DirMenu.h"
 
#include "Constants.h"
 
 
const float kHorzSpacing = 5.f;
#define UTF8_EXPAND_ARROW "\xe2\x96\xbe"
 
using namespace BPrivate;
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "StatusView"
 
 
StatusView::StatusView(BScrollView* scrollView)
			:
			BView(BRect(), "statusview",
				B_FOLLOW_BOTTOM | B_FOLLOW_LEFT, B_WILL_DRAW),
			fScrollView(scrollView),
			fPreferredSize(0., 0.),
			fReadOnly(false),
			fCanUnlock(false)
{
	memset(fCellWidth, 0, sizeof(fCellWidth));
}
 
 
StatusView::~StatusView()
{
}
 
 
void
StatusView::AttachedToWindow()
{
	SetFont(be_plain_font);
	SetFontSize(10.);
 
	BMessage message(UPDATE_STATUS);
	message.AddInt32("line", 1);
	message.AddInt32("column", 1);
	message.AddString("encoding", "");
	SetStatus(&message);
 
	BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL);
	MoveTo(0., scrollBar->Frame().top);
 
	rgb_color color = B_TRANSPARENT_COLOR;
	BView* parent = Parent();
	if (parent != NULL)
		color = parent->ViewColor();
 
	if (color == B_TRANSPARENT_COLOR)
		color = ui_color(B_PANEL_BACKGROUND_COLOR);
 
	SetViewColor(color);
 
	ResizeToPreferred();
}
 
 
void
StatusView::GetPreferredSize(float* _width, float* _height)
{
	_ValidatePreferredSize();
 
	if (_width)
		*_width = fPreferredSize.width;
 
	if (_height)
		*_height = fPreferredSize.height;
}
 
 
void
StatusView::ResizeToPreferred()
{
	float width, height;
	GetPreferredSize(&width, &height);
 
	if (Bounds().Width() > width)
		width = Bounds().Width();
 
	BView::ResizeTo(width, height);
}
 
 
void
StatusView::Draw(BRect updateRect)
{
	if (fPreferredSize.width <= 0)
		return;
 
	if (be_control_look != NULL) {
		BRect bounds(Bounds());
		be_control_look->DrawMenuBarBackground(this,
			bounds, updateRect,	ViewColor());
	}
 
	BRect bounds(Bounds());
	rgb_color highColor = HighColor();
	SetHighColor(tint_color(ViewColor(), B_DARKEN_2_TINT));
	StrokeLine(bounds.LeftTop(), bounds.RightTop());
 
	float x = bounds.left;
	for (size_t i = 0; i < kStatusCellCount - 1; i++) {
		x += fCellWidth[i];
		StrokeLine(BPoint(x, bounds.top + 3), BPoint(x, bounds.bottom - 3));
	}
 
	SetLowColor(ViewColor());
	SetHighColor(highColor);
 
	font_height fontHeight;
	GetFontHeight(&fontHeight);
 
	x = bounds.left;
	float y = (bounds.bottom + bounds.top
		+ ceilf(fontHeight.ascent) - ceilf(fontHeight.descent)) / 2;
 
	for (size_t i = 0; i < kStatusCellCount; i++) {
		if (fCellText[i].Length() == 0)
			continue;
		DrawString(fCellText[i], BPoint(x + kHorzSpacing, y));
		x += fCellWidth[i];
	}
}
 
 
void
StatusView::MouseDown(BPoint where)
{
	if (where.x < fCellWidth[kPositionCell]) {
		_ShowDirMenu();
		return;
	}
 
	if (!fReadOnly || !fCanUnlock)
		return;
 
	float left = fCellWidth[kPositionCell] + fCellWidth[kEncodingCell];
	if (where.x < left)
		return;
 
	int32 clicks = 0;
	BMessage* message = Window()->CurrentMessage();
	if (message != NULL
		&& message->FindInt32("clicks", &clicks) == B_OK && clicks > 1)
			return;
 
	BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
	menu->AddItem(new BMenuItem(B_TRANSLATE("Unlock file"),
					new BMessage(UNLOCK_FILE)));
	where.x = left;
	where.y = Bounds().bottom;
 
	ConvertToScreen(&where);
	menu->SetTargetForItems(this);
	menu->Go(where, true, true,	true);
}
 
 
void
StatusView::SetStatus(BMessage* message)
{
	int32 line = 0, column = 0;
	if (B_OK == message->FindInt32("line", &line)
		&& B_OK == message->FindInt32("column", &column))
	{
		char info[256];
		snprintf(info, sizeof(info),
				B_TRANSLATE("line %d, column %d"), line, column);
		fCellText[kPositionCell].SetTo(info);
	}
 
	if (B_OK == message->FindString("encoding", &fEncoding)) {
		// sometime corresponding Int-32 "encoding" attrib is read as string :(
		if (fEncoding.Length() == 0
			|| fEncoding.Compare("\xff\xff") == 0
			|| fEncoding.Compare("UTF-8") == 0)
		{
			// do not display default UTF-8 encoding
			fCellText[kEncodingCell].Truncate(0);
			fEncoding.Truncate(0);
		} else {
			const BCharacterSet* charset
				= BCharacterSetRoster::FindCharacterSetByName(fEncoding);
			fCellText[kEncodingCell]
				= charset != NULL ? charset->GetPrintName() : "";
		}
	}
 
	bool modified = false;
	fReadOnly = false;
	fCanUnlock = false;
	if (B_OK == message->FindBool("modified", &modified) && modified) {
		fCellText[kFileStateCell] = B_TRANSLATE("Modified");
	} else if (B_OK == message->FindBool("readOnly", &fReadOnly) && fReadOnly) {
		fCellText[kFileStateCell] = B_TRANSLATE("Read-only");
	    if (B_OK == message->FindBool("canUnlock", &fCanUnlock) && fCanUnlock)
			fCellText[kFileStateCell] << " " UTF8_EXPAND_ARROW;
	} else
		fCellText[kFileStateCell].Truncate(0);
 
	_ValidatePreferredSize();
	Invalidate();
}
 
 
void
StatusView::SetRef(const entry_ref& ref)
{
	fRef = ref;
}
 
 
void
StatusView::_ValidatePreferredSize()
{
	float orgWidth = fPreferredSize.width;
	// width
	fPreferredSize.width = 0.f;
	for (size_t i = 0; i < kStatusCellCount; i++) {
		if (fCellText[i].Length() == 0) {
			fCellWidth[i] = 0;
			continue;
		}
		float width = ceilf(StringWidth(fCellText[i]));
		if (width > 0)
			width += kHorzSpacing * 2;
		if (width > fCellWidth[i] || i != kPositionCell)
			fCellWidth[i] = width;
		fPreferredSize.width += fCellWidth[i];
	}
 
	// height
	font_height fontHeight;
	GetFontHeight(&fontHeight);
 
	fPreferredSize.height = ceilf(fontHeight.ascent + fontHeight.descent
		+ fontHeight.leading);
 
	if (fPreferredSize.height < B_H_SCROLL_BAR_HEIGHT)
		fPreferredSize.height = B_H_SCROLL_BAR_HEIGHT;
 
	float delta = fPreferredSize.width - orgWidth;
	ResizeBy(delta, 0);
	BScrollBar* scrollBar = fScrollView->ScrollBar(B_HORIZONTAL);
	scrollBar->ResizeBy(-delta, 0);
	scrollBar->MoveBy(delta, 0);
}
 
 
void
StatusView::_ShowDirMenu()
{
	BEntry entry;
	status_t status = entry.SetTo(&fRef);
 
	if (status != B_OK || !entry.Exists())
		return;
 
	BPrivate::BDirMenu* menu = new BDirMenu(NULL,
		BMessenger(kTrackerSignature), B_REFS_RECEIVED);
 
	menu->Populate(&entry, Window(), false, false, true, false, true);
 
	BPoint point = Bounds().LeftBottom();
	point.y += 3;
	ConvertToScreen(&point);
	BRect clickToOpenRect(Bounds());
	ConvertToScreen(&clickToOpenRect);
	menu->Go(point, true, true, clickToOpenRect);
	delete menu;
}
 

V773 Visibility scope of the 'menu' pointer was exited without releasing the memory. A memory leak is possible.