⛏️ index : haiku.git

/*
 * Copyright 2013-2015, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */
#include "ImageStopConfigView.h"

#include <Button.h>
#include <CheckBox.h>
#include <LayoutBuilder.h>
#include <ListView.h>
#include <MenuField.h>
#include <ScrollView.h>

#include <AutoDeleter.h>
#include <AutoLocker.h>

#include "AppMessageCodes.h"
#include "FunctionInstance.h"
#include "Image.h"
#include "ImageDebugInfo.h"
#include "MessageCodes.h"
#include "UserInterface.h"


enum {
	MSG_SET_STOP_FOR_ALL_IMAGES				= 'sfai',
	MSG_SET_STOP_FOR_CUSTOM_IMAGES			= 'sfci',
	MSG_IMAGE_NAME_SELECTION_CHANGED		= 'insc',
	MSG_ADD_IMAGE_NAME						= 'anin',
	MSG_REMOVE_IMAGE_NAME					= 'arin',
	MSG_IMAGE_NAME_INPUT_CHANGED			= 'inic'
};


static int SortStringItems(const void* a, const void* b)
{
	BStringItem* item1 = *(BStringItem**)a;
	BStringItem* item2 = *(BStringItem**)b;

	return strcmp(item1->Text(), item2->Text());
}


ImageStopConfigView::ImageStopConfigView(::Team* team,
	UserInterfaceListener* listener)
	:
	BGroupView(B_VERTICAL),
	fTeam(team),
	fListener(listener),
	fStopOnImageLoad(NULL),
	fStopImageConstraints(NULL),
	fStopImageNames(NULL),
	fStopImageNameInput(NULL),
	fAddImageNameButton(NULL),
	fRemoveImageNameButton(NULL),
	fCustomImageGroup(NULL),
	fStopOnLoadEnabled(false),
	fUseCustomImages(false)
{
	SetName("Images");
	fTeam->AddListener(this);
}


ImageStopConfigView::~ImageStopConfigView()
{
	fTeam->RemoveListener(this);
}


ImageStopConfigView*
ImageStopConfigView::Create(::Team* team, UserInterfaceListener* listener)
{
	ImageStopConfigView* self = new ImageStopConfigView(team, listener);

	try {
		self->_Init();
	} catch (...) {
		delete self;
		throw;
	}

	return self;
}


void
ImageStopConfigView::AttachedToWindow()
{
	fAddImageNameButton->SetEnabled(false);
	fRemoveImageNameButton->SetEnabled(false);

	fStopImageConstraints->Menu()->SetTargetForItems(this);
	fStopOnImageLoad->SetTarget(this);
	fAddImageNameButton->SetTarget(this);
	fRemoveImageNameButton->SetTarget(this);
	fStopImageNames->SetTarget(this);
	fStopImageNameInput->SetTarget(this);

	AutoLocker< ::Team> teamLocker(fTeam);
	_UpdateStopImageState();

	BGroupView::AttachedToWindow();
}


void
ImageStopConfigView::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case MSG_SET_STOP_FOR_ALL_IMAGES:
		{
			fListener->SetStopOnImageLoadRequested(
				fStopOnImageLoad->Value() == B_CONTROL_ON,
				false);
			break;
		}

		case MSG_SET_STOP_FOR_CUSTOM_IMAGES:
		{
			fListener->SetStopOnImageLoadRequested(
				fStopOnImageLoad->Value() == B_CONTROL_ON,
				true);
			break;
		}

		case MSG_IMAGE_NAME_SELECTION_CHANGED:
		{
			if (!fUseCustomImages)
				break;

			fRemoveImageNameButton->SetEnabled(
				fStopImageNames->CurrentSelection() >= 0);
			break;
		}

		case MSG_IMAGE_NAME_INPUT_CHANGED:
		{
			BString imageName(fStopImageNameInput->Text());
			imageName.Trim();
			fAddImageNameButton->SetEnabled(!imageName.IsEmpty());
			break;
		}

		case MSG_STOP_ON_IMAGE_LOAD:
		{
			fListener->SetStopOnImageLoadRequested(
				fStopOnImageLoad->Value() == B_CONTROL_ON,
				fUseCustomImages);
			break;
		}

		case MSG_STOP_IMAGE_SETTINGS_CHANGED:
		{
			_UpdateStopImageState();
			break;
		}

		case MSG_ADD_IMAGE_NAME:
		{
			BString imageName(fStopImageNameInput->Text());
			imageName.Trim();
			AutoLocker< ::Team> teamLocker(fTeam);
			if (fTeam->StopImageNames().HasString(imageName))
				break;

			fStopImageNameInput->SetText("");
			fListener->AddStopImageNameRequested(imageName.String());
			break;
		}

		case MSG_STOP_IMAGE_NAME_ADDED:
		{
			const char* imageName;
			if (message->FindString("name", &imageName) != B_OK)
				break;

			BStringItem* item = new(std::nothrow) BStringItem(imageName);
			if (item == NULL)
				break;

			ObjectDeleter<BStringItem> itemDeleter(item);
			if (!fStopImageNames->AddItem(item)) {
				break;
			}
			itemDeleter.Detach();
			fStopImageNames->SortItems(SortStringItems);
			break;
		}

		case MSG_REMOVE_IMAGE_NAME:
		{
			BStringItem* item;
			int32 selectedIndex;
			AutoLocker< ::Team> teamLocker(fTeam);
			int32 i = 0;
			while ((selectedIndex = fStopImageNames->CurrentSelection(i++))
				>= 0) {
				item = (BStringItem*)fStopImageNames->ItemAt(selectedIndex);
				fListener->RemoveStopImageNameRequested(item->Text());
			}
			break;
		}

		case MSG_STOP_IMAGE_NAME_REMOVED:
		{
			const char* imageName;
			if (message->FindString("name", &imageName) != B_OK)
				break;

			for (int32 i = 0; i < fStopImageNames->CountItems(); i++) {
				BStringItem* item = (BStringItem*)fStopImageNames->ItemAt(i);
				if (strcmp(item->Text(), imageName) == 0) {
					fStopImageNames->RemoveItem(i);
					delete item;
				}
			}
			break;
		}


		default:
			BGroupView::MessageReceived(message);
			break;
	}

}


void
ImageStopConfigView::StopOnImageLoadSettingsChanged(
	const Team::ImageLoadEvent& event)
{
	BMessage message(MSG_STOP_IMAGE_SETTINGS_CHANGED);
	message.AddBool("enabled", event.StopOnImageLoad());
	message.AddBool("useNameList", event.StopImageNameListEnabled());
	BMessenger(this).SendMessage(&message);
}


void
ImageStopConfigView::StopOnImageLoadNameAdded(
	const Team::ImageLoadNameEvent& event)
{
	BMessage message(MSG_STOP_IMAGE_NAME_ADDED);
	message.AddString("name", event.ImageName());
	BMessenger(this).SendMessage(&message);
}


void
ImageStopConfigView::StopOnImageLoadNameRemoved(
	const Team::ImageLoadNameEvent& event)
{
	BMessage message(MSG_STOP_IMAGE_NAME_REMOVED);
	message.AddString("name", event.ImageName());
	BMessenger(this).SendMessage(&message);
}


void
ImageStopConfigView::_Init()
{
	BMenu* stopImageMenu = new BMenu("stopImageTypesMenu");

	stopImageMenu->AddItem(new BMenuItem("All",
		new BMessage(MSG_SET_STOP_FOR_ALL_IMAGES)));
	stopImageMenu->AddItem(new BMenuItem("Custom",
		new BMessage(MSG_SET_STOP_FOR_CUSTOM_IMAGES)));

	fStopImageNames = new BListView("customImageList",
		B_MULTIPLE_SELECTION_LIST);
	fStopImageNames->SetSelectionMessage(
		new BMessage(MSG_IMAGE_NAME_SELECTION_CHANGED));

	fCustomImageGroup = new BGroupView();
	BLayoutBuilder::Group<>(fCustomImageGroup, B_VERTICAL, 0.0)
		.Add(new BScrollView("stopImageScroll", fStopImageNames,
			0, false, true))
		.Add(fStopImageNameInput = new BTextControl("stopImageName",
			"Image:", NULL,	NULL))
		.AddGroup(B_HORIZONTAL)
			.SetInsets(B_USE_SMALL_SPACING)
			.AddGlue()
			.Add(fAddImageNameButton = new BButton("Add",
				new BMessage(MSG_ADD_IMAGE_NAME)))
			.Add(fRemoveImageNameButton = new BButton("Remove",
				new BMessage(MSG_REMOVE_IMAGE_NAME)))
		.End();

	BLayoutBuilder::Group<>(this, B_VERTICAL)
		.SetInsets(B_USE_SMALL_SPACING)
		.Add(fStopOnImageLoad = new BCheckBox("stopOnImage",
			"Stop when an image is loaded",
			new BMessage(MSG_STOP_ON_IMAGE_LOAD)))
		.Add(fStopImageConstraints = new BMenuField(
			"stopTypes", "Types:", stopImageMenu))
		.Add(fCustomImageGroup);

	font_height fontHeight;
	be_plain_font->GetHeight(&fontHeight);
	float minListHeight = 5 * (fontHeight.ascent + fontHeight.descent
			+ fontHeight.leading);
	fStopImageNames->SetExplicitMinSize(BSize(B_SIZE_UNSET, minListHeight));

	stopImageMenu->SetLabelFromMarked(true);
	fStopImageNameInput->SetModificationMessage(
		new BMessage(MSG_IMAGE_NAME_INPUT_CHANGED));
}


void
ImageStopConfigView::_UpdateStopImageState()
{
	fStopOnLoadEnabled = fTeam->StopOnImageLoad();
	fStopOnImageLoad->SetValue(
		fStopOnLoadEnabled ? B_CONTROL_ON : B_CONTROL_OFF);
	fUseCustomImages = fTeam->StopImageNameListEnabled();
	fStopImageConstraints->Menu()
		->ItemAt(fUseCustomImages ? 1 : 0)->SetMarked(true);

	fStopImageNames->MakeEmpty();
	const BStringList& imageNames = fTeam->StopImageNames();
	for (int32 i = 0; i < imageNames.CountStrings(); i++) {
		BStringItem* item = new(std::nothrow) BStringItem(
			imageNames.StringAt(i));
		if (item == NULL)
			return;
		item->SetEnabled(fUseCustomImages);
		ObjectDeleter<BStringItem> itemDeleter(item);
		if (!fStopImageNames->AddItem(item))
			return;
		itemDeleter.Detach();
	}

	fStopImageConstraints->SetEnabled(fStopOnLoadEnabled);
	fStopImageNameInput->SetEnabled(fUseCustomImages);
}