/*
 * Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
 * Copyright 2009, Pier Luigi Fiorini.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
 *		Brian Hill, supernova@tycho.email
 */
 
#include <Alert.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <ColumnListView.h>
#include <ColumnTypes.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <Notification.h>
#include <Path.h>
#include <TextControl.h>
#include <Window.h>
 
#include <notification/Notifications.h>
#include <notification/NotificationReceived.h>
 
#include "NotificationsConstants.h"
#include "NotificationsView.h"
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "NotificationView"
 
// Applications column indexes
const int32 kAppNameIndex = 0;
const int32 kAppEnabledIndex = 1;
 
 
AppRow::AppRow(const char* name, const char* signature, bool allowed)
	:
	BRow(),
	fName(name),
	fSignature(signature),
	fAllowed(allowed)
{
	SetField(new BStringField(fName.String()), kAppNameIndex);
	BString text = fAllowed ? B_TRANSLATE("Allowed") : B_TRANSLATE("Muted");
	SetField(new BStringField(text.String()), kAppEnabledIndex);
}
 
 
void
AppRow::SetAllowed(bool allowed)
{
	fAllowed = allowed;
	RefreshEnabledField();
}
 
 
void
AppRow::RefreshEnabledField()
{
	BStringField* field = (BStringField*)GetField(kAppEnabledIndex);
	BString text = fAllowed ? B_TRANSLATE("Allowed") : B_TRANSLATE("Muted");
	field->SetString(text.String());
	Invalidate();
}
 
 
NotificationsView::NotificationsView(SettingsHost* host)
	:
	SettingsPane("apps", host),
	fSelectedRow(NULL)
{
	// Applications list
	fApplications = new BColumnListView(B_TRANSLATE("Applications"),
		0, B_FANCY_BORDER, false);
	fApplications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
	fApplications->SetSelectionMessage(new BMessage(kApplicationSelected));
 
	float colWidth = be_plain_font->StringWidth(B_TRANSLATE("Application"))
		+ (kCLVTitlePadding * 2);
	fAppCol = new BStringColumn(B_TRANSLATE("Application"), colWidth * 2,
		colWidth, colWidth * 4, B_TRUNCATE_END, B_ALIGN_LEFT);
	fApplications->AddColumn(fAppCol, kAppNameIndex);
 
	colWidth = be_plain_font->StringWidth(B_TRANSLATE("Status"))
		+ (kCLVTitlePadding * 2);
	fAppEnabledCol = new BStringColumn(B_TRANSLATE("Status"), colWidth * 1.5,
		colWidth, colWidth * 3, B_TRUNCATE_END, B_ALIGN_LEFT);
	fApplications->AddColumn(fAppEnabledCol, kAppEnabledIndex);
	fApplications->SetSortColumn(fAppCol, true, true);
	
	fAddButton = new BButton("add_app", B_TRANSLATE("Add" B_UTF8_ELLIPSIS),
		new BMessage(kAddApplication));
	fRemoveButton = new BButton("add_app", B_TRANSLATE("Remove"),
		new BMessage(kRemoveApplication));
	fRemoveButton->SetEnabled(false);
	
	fMuteAll = new BCheckBox("block", B_TRANSLATE("Mute notifications from "
		"this application"),
		new BMessage(kMuteChanged));
 
	// Add views
	BLayoutBuilder::Group<>(this, B_VERTICAL)
		.AddGroup(B_HORIZONTAL)
			.Add(fApplications)
			.AddGroup(B_VERTICAL)
				.Add(fAddButton)
				.Add(fRemoveButton)
				.AddGlue()
			.End()
		.End()
		.Add(fMuteAll)
		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
			B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
 
	// Set button sizes
	float maxButtonWidth = std::max(fAddButton->PreferredSize().Width(),
		fRemoveButton->PreferredSize().Width());
	fAddButton->SetExplicitMaxSize(BSize(maxButtonWidth, B_SIZE_UNSET));
	fRemoveButton->SetExplicitMaxSize(BSize(maxButtonWidth, B_SIZE_UNSET));
 
	// File Panel
	fPanelFilter = new AppRefFilter();
	fAddAppPanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false,
		NULL, fPanelFilter);
}
 
 
NotificationsView::~NotificationsView()
{
	delete fAddAppPanel;
	delete fPanelFilter;
}
 
 
void
NotificationsView::AttachedToWindow()
{
	fApplications->SetTarget(this);
	fApplications->SetInvocationMessage(new BMessage(kApplicationSelected));
	fAddButton->SetTarget(this);
	fRemoveButton->SetTarget(this);
	fMuteAll->SetTarget(this);
	fAddAppPanel->SetTarget(this);
	_RecallItemSettings();
}
 
 
void
NotificationsView::MessageReceived(BMessage* msg)
{
	switch (msg->what) {
		case kApplicationSelected:
		{
			Window()->Lock();
			_ClearItemSettings();
			_UpdateSelectedItem();
			_RecallItemSettings();
			Window()->Unlock();
			break;
		}
		case kMuteChanged:
		{
			bool allowed = fMuteAll->Value() == B_CONTROL_OFF;
			fSelectedRow->SetAllowed(allowed);
			appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
			if (it != fAppFilters.end())
				it->second->SetAllowed(allowed);
			Window()->PostMessage(kApply);
			break;
		}
		case kAddApplication:
		{
			BMessage addmsg(kAddApplicationRef);
			fAddAppPanel->SetMessage(&addmsg);
			fAddAppPanel->Show();
			break;
		}
		case kAddApplicationRef:
		{
			entry_ref srcRef;
			msg->FindRef("refs", &srcRef);
			BEntry srcEntry(&srcRef, true);
			BPath path(&srcEntry);
			BNode node(&srcEntry);
			char *buf = new char[B_ATTR_NAME_LENGTH];
			ssize_t size;
			if ( (size = node.ReadAttr("BEOS:APP_SIG", 0, 0, buf,
				B_ATTR_NAME_LENGTH)) > 0 )
			{
				// Search for already existing app
				appusage_t::iterator it = fAppFilters.find(buf);
				if (it != fAppFilters.end()) {
					BString text(path.Leaf());
					text.Append(B_TRANSLATE_COMMENT(" is already listed",
							"Alert message"));
					BAlert* alert = new BAlert("", text.String(),
						B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
						B_WARNING_ALERT);
					alert->Go(NULL);
				} else {
					AppUsage* appUsage = new AppUsage(path.Leaf(), buf, true);
					fAppFilters[appUsage->Signature()] = appUsage;
					AppRow* row = new AppRow(appUsage->AppName(),
						appUsage->Signature(), appUsage->Allowed());
					fApplications->AddRow(row);
					fApplications->DeselectAll();
					fApplications->AddToSelection(row);
					fApplications->ScrollTo(row);
					_UpdateSelectedItem();
					_RecallItemSettings();
					//row->Invalidate();
					//fApplications->InvalidateRow(row);
					// TODO redraw row properly
					Window()->PostMessage(kApply);
				}
			} else {
				BAlert* alert = new BAlert("",
					B_TRANSLATE_COMMENT("Application does not have "
						"a valid signature", "Alert message"),
					B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
					B_WARNING_ALERT);
				alert->Go(NULL);
			}
			delete[] buf;
			break;
		}
		case kRemoveApplication:
		{
			if (fSelectedRow) {
				appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
				if (it != fAppFilters.end()) {
					delete it->second;
					fAppFilters.erase(it);
				}
				fApplications->RemoveRow(fSelectedRow);
				delete fSelectedRow;
				fSelectedRow = NULL;
				_ClearItemSettings();
				_UpdateSelectedItem();
				_RecallItemSettings();
				Window()->PostMessage(kApply);
			}
			break;
		}
		default:
			BView::MessageReceived(msg);
			break;
	}
}
 
 
status_t
NotificationsView::Load(BMessage& settings)
{
	type_code type;
	int32 count = 0;
 
	if (settings.GetInfo("app_usage", &type, &count) != B_OK)
		return B_ERROR;
 
	// Clean filters
	appusage_t::iterator auIt;
	for (auIt = fAppFilters.begin(); auIt != fAppFilters.end(); auIt++)
		delete auIt->second;
	fAppFilters.clear();
 
	// Add new filters
	for (int32 i = 0; i < count; i++) {
		AppUsage* app = new AppUsage();
		settings.FindFlat("app_usage", i, app);
		fAppFilters[app->Signature()] = app;
	}
 
	// Load the applications list
	_PopulateApplications();
 
	return B_OK;
}
 
 
status_t
NotificationsView::Save(BMessage& storage)
{
	appusage_t::iterator fIt;
	for (fIt = fAppFilters.begin(); fIt != fAppFilters.end(); fIt++)
		storage.AddFlat("app_usage", fIt->second);
 
	return B_OK;
}
 
 
void
NotificationsView::_ClearItemSettings()
{
	fMuteAll->SetValue(B_CONTROL_OFF);
}
 
 
void
NotificationsView::_UpdateSelectedItem()
{
	fSelectedRow = dynamic_cast<AppRow*>(fApplications->CurrentSelection());
	
}
 
 
void
NotificationsView::_RecallItemSettings()
{
	// No selected item
	if(fSelectedRow == NULL)
	{
		fMuteAll->SetValue(B_CONTROL_OFF);
		fMuteAll->SetEnabled(false);
		fRemoveButton->SetEnabled(false);
	} else {
		fMuteAll->SetEnabled(true);
		fRemoveButton->SetEnabled(true);
		appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
		if (it != fAppFilters.end())
			fMuteAll->SetValue(!(it->second->Allowed()));
	}
}
 
 
status_t
NotificationsView::Revert()
{
	return B_OK;
}
 
 
bool
NotificationsView::RevertPossible()
{
	return false;
}
 
 
status_t
NotificationsView::Defaults()
{
	return B_OK;
}
 
 
bool
NotificationsView::DefaultsPossible()
{
	return false;
}
 
 
bool
NotificationsView::UseDefaultRevertButtons()
{
	return false;
}
 
 
void
NotificationsView::_PopulateApplications()
{
	fApplications->Clear();
 
	appusage_t::iterator it;
	for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
		AppUsage* appUsage = it->second;
		AppRow* row = new AppRow(appUsage->AppName(),
			appUsage->Signature(), appUsage->Allowed());
		fApplications->AddRow(row);
	}
}

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

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