/*
 * Copyright 2005-2010, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT license.
 *
 * Authors:
 *		Robert Polic
 *		Axel Dörfler, axeld@pinc-software.de
 *		Stephan Aßmus <superstippi@gmx.de>
 *
 * Copyright 1999, Be Incorporated.   All Rights Reserved.
 * This file may be used under the terms of the Be Sample Code License.
 */
 
 
#include "PeopleApp.h"
 
#include <Alert.h>
#include <AutoDeleter.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <fs_index.h>
#include <Locale.h>
#include <Path.h>
#include <Roster.h>
#include <Screen.h>
#include <Volume.h>
#include <VolumeRoster.h>
 
#include "PersonWindow.h"
#include "PersonIcons.h"
 
#include <string.h>
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "People"
 
 
struct DefaultAttribute {
	const char*	attribute;
	int32		width;
	const char*	name;
};
 
// TODO: Add flags in attribute info message to find these.
static const char* kNameAttribute = "META:name";
static const char* kCategoryAttribute = "META:group";
 
struct DefaultAttribute sDefaultAttributes[] = {
	{ kNameAttribute, 120, B_TRANSLATE("Contact name") },
	{ "META:nickname", 120, B_TRANSLATE("Nickname") },
	{ "META:company", 120, B_TRANSLATE("Company") },
	{ "META:address", 120, B_TRANSLATE("Address") },
	{ "META:city", 90, B_TRANSLATE("City") },
	{ "META:state", 50, B_TRANSLATE("State") },
	{ "META:zip", 50, B_TRANSLATE("Zip") },
	{ "META:country", 120, B_TRANSLATE("Country") },
	{ "META:hphone", 90, B_TRANSLATE("Home phone") },
	{ "META:wphone", 90, B_TRANSLATE("Work phone") },
	{ "META:fax", 90, B_TRANSLATE("Fax") },
	{ "META:email", 120, B_TRANSLATE("E-mail") },
	{ "META:url", 120, B_TRANSLATE("URL") },
	{ kCategoryAttribute, 120, B_TRANSLATE("Group") },
	{ NULL, 0, NULL }
};
 
 
TPeopleApp::TPeopleApp()
	:
	BApplication(APP_SIG),
	fWindowCount(0),
	fAttributes(20, true)
{
	B_TRANSLATE_MARK_SYSTEM_NAME_VOID("People");
 
	fPosition.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH,
		TITLE_BAR_HEIGHT + WIND_HEIGHT);
	BPoint pos = fPosition.LeftTop();
 
	BPath path;
	find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
 
	BDirectory dir(path.Path());
	BEntry entry;
	if (dir.FindEntry("People_data", &entry) == B_OK) {
		fPrefs = new BFile(&entry, B_READ_WRITE);
		if (fPrefs->InitCheck() == B_NO_ERROR) {
			fPrefs->Read(&pos, sizeof(BPoint));
			if (BScreen(B_MAIN_SCREEN_ID).Frame().Contains(pos))
				fPosition.OffsetTo(pos);
		}
	} else {
		fPrefs = new BFile();
		if (dir.CreateFile("People_data", fPrefs) != B_OK) {
			delete fPrefs;
			fPrefs = NULL;
		}
	}
 
	// Read attributes from person mime type. If it does not exist,
	// or if it contains no attribute definitions, install a "clean"
	// person mime type from the hard-coded default attributes.
 
	bool valid = false;
	BMimeType mime(B_PERSON_MIMETYPE);
	if (mime.IsInstalled()) {
		BMessage info;
		if (mime.GetAttrInfo(&info) == B_NO_ERROR) {
			int32 index = 0;
			while (true) {
				int32 type;
				if (info.FindInt32("attr:type", index, &type) != B_OK)
					break;
				bool editable;
				if (info.FindBool("attr:editable", index, &editable) != B_OK)
					break;
 
				// TODO: Support other types besides string attributes.
				if (type != B_STRING_TYPE || !editable)
					break;
 
				Attribute* attribute = new Attribute();
				ObjectDeleter<Attribute> deleter(attribute);
				if (info.FindString("attr:public_name", index,
						&attribute->name) != B_OK) {
					break;
				}
				if (info.FindString("attr:name", index,
						&attribute->attribute) != B_OK) {
					break;
				}
 
				if (!fAttributes.AddItem(attribute))
					break;
 
				deleter.Detach();
				index++;
			}
		}
		if (fAttributes.CountItems() == 0) {
			valid = false;
			mime.Delete();
		} else
			valid = true;
	}
	if (!valid) {
		mime.Install();
		mime.SetShortDescription(B_TRANSLATE_CONTEXT("Person",
			"Short mimetype description"));
		mime.SetLongDescription(B_TRANSLATE_CONTEXT(
			"Contact information for a person.",
			"Long mimetype description"));
		mime.SetIcon(kPersonIcon, sizeof(kPersonIcon));
		mime.SetPreferredApp(APP_SIG);
 
		// add default person fields to meta-mime type
		BMessage fields;
		for (int32 i = 0; sDefaultAttributes[i].attribute; i++) {
			fields.AddString("attr:public_name", sDefaultAttributes[i].name);
			fields.AddString("attr:name", sDefaultAttributes[i].attribute);
			fields.AddInt32("attr:type", B_STRING_TYPE);
			fields.AddBool("attr:viewable", true);
			fields.AddBool("attr:editable", true);
			fields.AddInt32("attr:width", sDefaultAttributes[i].width);
			fields.AddInt32("attr:alignment", B_ALIGN_LEFT);
			fields.AddBool("attr:extra", false);
 
			// Add the default attribute to the attribute list, too.
			Attribute* attribute = new Attribute();
			attribute->name = sDefaultAttributes[i].name;
			attribute->attribute = sDefaultAttributes[i].attribute;
			if (!fAttributes.AddItem(attribute))
				delete attribute;
		}
 
		mime.SetAttrInfo(&fields);
	}
 
	// create indices on all volumes for the found attributes.
 
	int32 count = fAttributes.CountItems();
	BVolumeRoster volumeRoster;
	BVolume volume;
	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
		for (int32 i = 0; i < count; i++) {
			Attribute* attribute = fAttributes.ItemAt(i);
			fs_create_index(volume.Device(), attribute->attribute,
				B_STRING_TYPE, 0);
		}
	}
 
}
 
 
TPeopleApp::~TPeopleApp()
{
	delete fPrefs;
}
 
 
void
TPeopleApp::ArgvReceived(int32 argc, char** argv)
{
	BMessage message(B_REFS_RECEIVED);
 
	for (int32 i = 1; i < argc; i++) {
		BEntry entry(argv[i]);
		entry_ref ref;
		if (entry.Exists() && entry.GetRef(&ref) == B_OK)
			message.AddRef("refs", &ref);
	}
 
	RefsReceived(&message);
}
 
 
void
TPeopleApp::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case M_NEW:
		case B_SILENT_RELAUNCH:
			_NewWindow();
			break;
 
		case M_WINDOW_QUITS:
			_SavePreferences(message);
			fWindowCount--;
			if (fWindowCount < 1)
				PostMessage(B_QUIT_REQUESTED);
			break;
 
		case M_CONFIGURE_ATTRIBUTES:
		{
			const char* arguments[] = { "-type", B_PERSON_MIMETYPE, 0 };
			status_t ret = be_roster->Launch(
				"application/x-vnd.Haiku-FileTypes",
				sizeof(arguments) / sizeof(const char*) - 1,
				const_cast<char**>(arguments));
			if (ret != B_OK && ret != B_ALREADY_RUNNING) {
				BString errorMsg(B_TRANSLATE("Launching the FileTypes "
					"preflet to configure Person attributes has failed."
					"\n\nError: "));
				errorMsg << strerror(ret);
				BAlert* alert = new BAlert(B_TRANSLATE("Error"),
					errorMsg.String(), B_TRANSLATE("OK"), NULL, NULL,
					B_WIDTH_AS_USUAL, B_STOP_ALERT);
				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
				alert->Go(NULL);
			}
			break;
		}
 
		default:
			BApplication::MessageReceived(message);
	}
}
 
 
void
TPeopleApp::RefsReceived(BMessage* message)
{
	int32 index = 0;
	while (message->HasRef("refs", index)) {
		entry_ref ref;
		message->FindRef("refs", index++, &ref);
 
		PersonWindow* window = _FindWindow(ref);
		if (window != NULL)
			window->Activate(true);
		else {
			BFile file(&ref, B_READ_ONLY);
			if (file.InitCheck() == B_OK)
				_NewWindow(&ref);
		}
	}
}
 
 
void
TPeopleApp::ReadyToRun()
{
	if (fWindowCount < 1)
		_NewWindow();
}
 
 
// #pragma mark -
 
 
PersonWindow*
TPeopleApp::_NewWindow(entry_ref* ref)
{
	PersonWindow* window = new PersonWindow(fPosition,
		B_TRANSLATE("New person"), kNameAttribute,
		kCategoryAttribute, ref);
 
	_AddAttributes(window);
 
	window->Show();
 
	fWindowCount++;
 
	// Offset the position for the next window which will be opened and
	// reset it if it would open outside the screen bounds.
	fPosition.OffsetBy(20, 20);
	BScreen screen(window);
	if (fPosition.bottom > screen.Frame().bottom)
		fPosition.OffsetTo(fPosition.left, TITLE_BAR_HEIGHT);
	if (fPosition.right > screen.Frame().right)
		fPosition.OffsetTo(6, fPosition.top);
 
	return window;
}
 
 
void
TPeopleApp::_AddAttributes(PersonWindow* window) const
{
	int32 count = fAttributes.CountItems();
	for (int32 i = 0; i < count; i++) {
		Attribute* attribute = fAttributes.ItemAt(i);
		const char* label = attribute->name;
		if (attribute->attribute == kNameAttribute)
			label = B_TRANSLATE("Name");
 
		window->AddAttribute(label, attribute->attribute);
	}
}
 
 
PersonWindow*
TPeopleApp::_FindWindow(const entry_ref& ref) const
{
	for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
		PersonWindow* personWindow = dynamic_cast<PersonWindow*>(window);
		if (personWindow == NULL)
			continue;
		if (personWindow->RefersPersonFile(ref))
			return personWindow;
	}
	return NULL;
}
 
 
void
TPeopleApp::_SavePreferences(BMessage* message) const
{
	BRect frame;
	if (message->FindRect("frame", &frame) != B_OK)
		return;
 
	BPoint leftTop = frame.LeftTop();
 
	if (fPrefs != NULL) {
		fPrefs->Seek(0, 0);
		fPrefs->Write(&leftTop, sizeof(BPoint));
	}
}
 

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