⛏️ index : haiku.git

/*
 * Copyright (c) 1998-2007 Matthijs Hollemans
 * Distributed under the terms of the MIT License.
 */


#include "Model.h"

#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <List.h>
#include <MenuItem.h>
#include <Path.h>
#include <Roster.h>

#include "GlobalDefs.h"


using std::nothrow;


Model::Model()
	:
	fDirectory(),
	fSelectedFiles((uint32)0),

	fRecurseDirs(true),
	fRecurseLinks(false),
	fSkipDotDirs(true),
	fCaseSensitive(false),
	fRegularExpression(false),
	fTextOnly(true),
	fInvokeEditor(false),

	fFrame(100, 100, 500, 400),

	fState(STATE_IDLE),

	fFilePanelPath(""),

	fShowLines(true),
	fEncoding(0)
{
	BPath path;
	if (find_directory(B_USER_DIRECTORY, &path) == B_OK)
		fFilePanelPath = path.Path();
	else
		fFilePanelPath = "/boot/home";
}


status_t
Model::LoadPrefs()
{
	BFile file;
	status_t status = _OpenFile(&file, PREFS_FILE);
	if (status != B_OK)
		return status;

	status = file.Lock();
	if (status != B_OK)
		return status;

	int32 value;

	if (file.ReadAttr("RecurseDirs", B_INT32_TYPE, 0, &value,
			sizeof(int32)) > 0)
		fRecurseDirs = (value != 0);

	if (file.ReadAttr("RecurseLinks", B_INT32_TYPE, 0, &value,
			sizeof(int32)) > 0)
		fRecurseLinks = (value != 0);

	if (file.ReadAttr("SkipDotDirs", B_INT32_TYPE, 0, &value,
			sizeof(int32)) > 0)
		fSkipDotDirs = (value != 0);

	if (file.ReadAttr("CaseSensitive", B_INT32_TYPE, 0, &value,
			sizeof(int32)) > 0)
		fCaseSensitive = (value != 0);

	if (file.ReadAttr("RegularExpression", B_INT32_TYPE, 0, &value,
			sizeof(int32)) > 0)
		fRegularExpression = (value != 0);

	if (file.ReadAttr("TextOnly", B_INT32_TYPE, 0, &value, sizeof(int32)) > 0)
		fTextOnly = (value != 0);

	if (file.ReadAttr("InvokeEditor", B_INT32_TYPE, 0, &value, sizeof(int32))
			> 0)
		fInvokeEditor = (value != 0);

	char buffer [B_PATH_NAME_LENGTH+1];
	int32 length = file.ReadAttr("FilePanelPath", B_STRING_TYPE, 0, &buffer,
		sizeof(buffer));
	if (length > 0) {
		buffer[length] = '\0';
		fFilePanelPath = buffer;
	}

	file.ReadAttr("WindowFrame", B_RECT_TYPE, 0, &fFrame, sizeof(BRect));

	if (file.ReadAttr("ShowLines", B_INT32_TYPE, 0, &value,
			sizeof(int32)) > 0)
		fShowLines = (value != 0);

	if (file.ReadAttr("Encoding", B_INT32_TYPE, 0, &value, sizeof(int32)) > 0)
		fEncoding = value;

	file.Unlock();

	return B_OK;
}


status_t
Model::SavePrefs()
{
	BFile file;
	status_t status = _OpenFile(&file, PREFS_FILE,
		B_CREATE_FILE | B_WRITE_ONLY);
	if (status != B_OK)
		return status;

	status = file.Lock();
	if (status != B_OK)
		return status;

	int32 value = 2;
	file.WriteAttr("Version", B_INT32_TYPE, 0, &value, sizeof(int32));

	value = fRecurseDirs ? 1 : 0;
	file.WriteAttr("RecurseDirs", B_INT32_TYPE, 0, &value, sizeof(int32));

	value = fRecurseLinks ? 1 : 0;
	file.WriteAttr("RecurseLinks", B_INT32_TYPE, 0, &value, sizeof(int32));

	value = fSkipDotDirs ? 1 : 0;
	file.WriteAttr("SkipDotDirs", B_INT32_TYPE, 0, &value, sizeof(int32));

	value = fCaseSensitive ? 1 : 0;
	file.WriteAttr("CaseSensitive", B_INT32_TYPE, 0, &value, sizeof(int32));

	value = fRegularExpression ? 1 : 0;
	file.WriteAttr("RegularExpression", B_INT32_TYPE, 0, &value, sizeof(int32));

	value = fTextOnly ? 1 : 0;
	file.WriteAttr("TextOnly", B_INT32_TYPE, 0, &value, sizeof(int32));

	value = fInvokeEditor ? 1 : 0;
	file.WriteAttr("InvokeEditor", B_INT32_TYPE, 0, &value, sizeof(int32));

	file.WriteAttr("WindowFrame", B_RECT_TYPE, 0, &fFrame, sizeof(BRect));

	file.WriteAttr("FilePanelPath", B_STRING_TYPE, 0, fFilePanelPath.String(),
		fFilePanelPath.Length() + 1);

	value = fShowLines ? 1 : 0;
	file.WriteAttr("ShowLines", B_INT32_TYPE, 0, &value, sizeof(int32));

	file.WriteAttr("Encoding", B_INT32_TYPE, 0, &fEncoding, sizeof(int32));

	file.Sync();
	file.Unlock();

	return B_OK;
}


void
Model::AddToHistory(const char* text)
{
	BList items;
	_LoadHistory(items);

	BString* string = new (nothrow) BString(text);
	if (string == NULL || !items.AddItem(string)) {
		delete string;
		_FreeHistory(items);
		return;
	}

	int32 count = items.CountItems() - 1;
		// don't check last item, since that's the one we just added
	for (int32 t = 0; t < count; ++t) {
		// If the same text is already in the list,
		// then remove it first. Case-sensitive.
		BString* string = static_cast<BString*>(items.ItemAt(t));
		if (*string == text) {
			delete static_cast<BString*>(items.RemoveItem(t));
			break;
		}
	}

	while (items.CountItems() >= HISTORY_LIMIT)
		delete static_cast<BString*>(items.RemoveItem((int32)0));

	_SaveHistory(items);
	_FreeHistory(items);
}


void
Model::ClearHistory()
{
	BList items;
	if (!_LoadHistory(items))
		return;

	items.RemoveItems(0, items.CountItems());

	_SaveHistory(items);
	_FreeHistory(items);
}


void
Model::FillHistoryMenu(BMenu* menu) const
{
	BList items;
	if (!_LoadHistory(items))
		return;

	for (int32 t = items.CountItems() - 1; t >= 0; --t) {
		BString* item = static_cast<BString*>(items.ItemAtFast(t));
		BMessage* message = new BMessage(MSG_SELECT_HISTORY);
		message->AddString("text", item->String());
		menu->AddItem(new BMenuItem(item->String(), message));
	}

	_FreeHistory(items);
}


BString
Model::GetHistoryItem(int32 index)
{
	BList items;
	if (!_LoadHistory(items))
		return NULL;

	int32 itemCount = items.CountItems() - 1;
	if (index > itemCount)
		return NULL;

	// latest entry is at the end of the BList
	BString* itemtext = static_cast<BString*>(items.ItemAt(itemCount - index));

	return itemtext->String();
}

// #pragma mark - private


bool
Model::_LoadHistory(BList& items) const
{
	BFile file;
	status_t status = _OpenFile(&file, PREFS_FILE);
	if (status != B_OK)
		return false;

	status = file.Lock();
	if (status != B_OK)
		return false;

	BMessage message;
	status = message.Unflatten(&file);
	if (status != B_OK)
		return false;

	file.Unlock();

	BString string;
	for (int32 x = 0; message.FindString("string", x, &string) == B_OK; x++) {
		BString* copy = new (nothrow) BString(string);
		if (copy == NULL || !items.AddItem(copy)) {
			delete copy;
			break;
		}
	}

	return true;
}


status_t
Model::_SaveHistory(const BList& items) const
{
	BFile file;
	status_t status = _OpenFile(&file, PREFS_FILE,
		B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE,
		B_USER_SETTINGS_DIRECTORY, NULL);
	if (status != B_OK)
		return status;

	status = file.Lock();
	if (status != B_OK)
		return status;

	BMessage message;
	int32 count = items.CountItems();
	for (int32 i = 0; i < count; i++) {
		BString* string = static_cast<BString*>(items.ItemAtFast(i));

		if (message.AddString("string", string->String()) != B_OK)
			break;
	}

	status = message.Flatten(&file);
	file.SetSize(message.FlattenedSize());
	file.Sync();
	file.Unlock();

	return status;
}


void
Model::_FreeHistory(const BList& items) const
{
	for (int32 t = items.CountItems() - 1; t >= 0; --t)
		delete static_cast<BString*>((items.ItemAtFast(t)));
}


status_t
Model::_OpenFile(BFile* file, const char* name, uint32 openMode,
	directory_which which, BVolume* volume) const
{
	if (file == NULL)
		return B_BAD_VALUE;

	BPath path;
	status_t status = find_directory(which, &path, true, volume);
	if (status != B_OK)
		return status;

	status = path.Append(PREFS_FILE);
	if (status != B_OK)
		return status;

	status = file->SetTo(path.Path(), openMode);
	if (status != B_OK)
		return status;

	status = file->InitCheck();
	if (status != B_OK)
		return status;

	return B_OK;
}