⛏️ index : haiku.git

/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2011-2016, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */


#include "GraphicalUserInterface.h"

#include <Alert.h>
#include <AutoDeleter.h>
#include <Autolock.h>
#include <FilePanel.h>
#include <Locker.h>

#include "AlertWithCheckbox.h"
#include "GuiTeamUiSettings.h"
#include "MessageCodes.h"
#include "TeamWindow.h"
#include "Tracing.h"


// #pragma mark - GraphicalUserInterface::FilePanelHandler


class GraphicalUserInterface::FilePanelHandler : public BHandler {
public:
								FilePanelHandler();
	virtual						~FilePanelHandler();

			status_t			Init();

	virtual	void				MessageReceived(BMessage* message);

			status_t			WaitForPanel();

			void				SetCurrentRef(entry_ref* ref);

			BLocker&			Locker()
									{ return fPanelLock; }

private:
			entry_ref*			fCurrentRef;
			BLocker				fPanelLock;
			sem_id				fPanelWaitSem;
};


GraphicalUserInterface::FilePanelHandler::FilePanelHandler()
	:
	BHandler("GuiPanelHandler"),
	fCurrentRef(NULL),
	fPanelLock(),
	fPanelWaitSem(-1)
{
}


GraphicalUserInterface::FilePanelHandler::~FilePanelHandler()
{
	if (fPanelWaitSem >= 0)
		delete_sem(fPanelWaitSem);
}


status_t
GraphicalUserInterface::FilePanelHandler::Init()
{
	fPanelWaitSem = create_sem(0, "FilePanelWaitSem");

	if (fPanelWaitSem < 0)
		return fPanelWaitSem;

	return B_OK;
}


void
GraphicalUserInterface::FilePanelHandler::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case MSG_USER_INTERFACE_FILE_CHOSEN:
		{
			entry_ref ref;
			if (message->FindRef("refs", &ref) == B_OK
				&& fCurrentRef != NULL) {
				*fCurrentRef = ref;
				fCurrentRef = NULL;
			}
			// fall through
		}

		case B_CANCEL:
		{
			release_sem(fPanelWaitSem);
			break;
		}

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


status_t
GraphicalUserInterface::FilePanelHandler::WaitForPanel()
{
	status_t result = B_OK;
	do {
		result = acquire_sem(fPanelWaitSem);
	} while (result == B_INTERRUPTED);

	return result;
}


void
GraphicalUserInterface::FilePanelHandler::SetCurrentRef(entry_ref* ref)
{
	fCurrentRef = ref;
}


// #pragma mark - GraphicalUserInterface


GraphicalUserInterface::GraphicalUserInterface()
	:
	fTeamWindow(NULL),
	fTeamWindowMessenger(NULL),
	fFilePanelHandler(NULL),
	fFilePanel(NULL),
	fDefaultActions(10)
{
}


GraphicalUserInterface::~GraphicalUserInterface()
{
	delete fTeamWindowMessenger;
	delete fFilePanel;
	delete fFilePanelHandler;
}


const char*
GraphicalUserInterface::ID() const
{
	return "GraphicalUserInterface";
}


status_t
GraphicalUserInterface::Init(Team* team, UserInterfaceListener* listener)
{
	try {
		fTeamWindow = TeamWindow::Create(team, listener);
		fTeamWindowMessenger = new BMessenger(fTeamWindow);
		fFilePanelHandler = new FilePanelHandler();
		status_t error = fFilePanelHandler->Init();
		if (error != B_OK) {
			ERROR("Error: Failed to create file panel semaphore!\n");
			return error;
		}
		fTeamWindow->AddHandler(fFilePanelHandler);

		// start the message loop
		fTeamWindow->Hide();
		fTeamWindow->Show();
	} catch (...) {
		// TODO: Notify the user!
		ERROR("Error: Failed to create team window!\n");
		return B_NO_MEMORY;
	}

	return B_OK;
}


void
GraphicalUserInterface::Show()
{
	if (fTeamWindow->IsHidden())
		fTeamWindow->Show();
	else
		fTeamWindow->Activate();
}


void
GraphicalUserInterface::Terminate()
{
	// quit window
	if (fTeamWindowMessenger && fTeamWindowMessenger->LockTarget())
		fTeamWindow->Quit();
}


UserInterface*
GraphicalUserInterface::Clone() const
{
	return new(std::nothrow) GraphicalUserInterface;
}


bool
GraphicalUserInterface::IsInteractive() const
{
	return true;
}


status_t
GraphicalUserInterface::LoadSettings(const TeamUiSettings* settings)
{
	status_t result = fTeamWindow->LoadSettings((GuiTeamUiSettings*)settings);

	return result;
}


status_t
GraphicalUserInterface::SaveSettings(TeamUiSettings*& settings) const
{
	settings = new(std::nothrow) GuiTeamUiSettings(ID());
	if (settings == NULL)
		return B_NO_MEMORY;

	fTeamWindow->SaveSettings((GuiTeamUiSettings*)settings);

	return B_OK;
}


void
GraphicalUserInterface::NotifyUser(const char* title, const char* message,
	user_notification_type type)
{
	// convert notification type to alert type
	alert_type alertType;
	switch (type) {
		case USER_NOTIFICATION_INFO:
			alertType = B_INFO_ALERT;
			break;
		case USER_NOTIFICATION_WARNING:
		case USER_NOTIFICATION_ERROR:
		default:
			alertType = B_WARNING_ALERT;
			break;
	}

	BAlert* alert = new(std::nothrow) BAlert(title, message, "OK",
		NULL, NULL, B_WIDTH_AS_USUAL, alertType);
	if (alert != NULL)
		alert->Go(NULL);

	// TODO: We need to let the alert run asynchronously, but we shouldn't just
	// create it and don't care anymore. Maybe an error window, which can
	// display a list of errors would be the better choice.
}


void
GraphicalUserInterface::NotifyBackgroundWorkStatus(const char* message)
{
	fTeamWindow->DisplayBackgroundStatus(message);
}


int32
GraphicalUserInterface::SynchronouslyAskUser(const char* title,
	const char* message, const char* choice1, const char* choice2,
	const char* choice3)
{
	// If the user already answered the question and asked for their choice to be remembered,
	// return the previously made choice again
	BString key(title);
	key += choice1;
	key += choice2;
	key += choice3;

	for (int i = 0; i < fDefaultActions.CountItems(); i++) {
		if (fDefaultActions.ItemAt(i)->fKey == key)
			return fDefaultActions.ItemAt(i)->fDecision;
	}

	AlertWithCheckbox* alert = new(std::nothrow) AlertWithCheckbox(title, message,
		"Don't ask again", choice1, choice2, choice3);

	if (alert == NULL)
		return 0;

	bool dontAskAgain = false;
	int result = alert->Go(dontAskAgain);

	if (dontAskAgain) {
		DefaultAction* defaultAction = new DefaultAction;
		defaultAction->fKey = key;
		defaultAction->fDecision = result;
		fDefaultActions.AddItem(defaultAction);
	}

	return result;
}


status_t
GraphicalUserInterface::SynchronouslyAskUserForFile(entry_ref* _ref)
{
	BAutolock lock(&fFilePanelHandler->Locker());

	if (fFilePanel == NULL) {
		BMessenger messenger(fFilePanelHandler);
		BMessage* message = new(std::nothrow) BMessage(
			MSG_USER_INTERFACE_FILE_CHOSEN);
		if (message == NULL)
			return B_NO_MEMORY;
		ObjectDeleter<BMessage> messageDeleter(message);
		fFilePanel = new(std::nothrow) BFilePanel(B_OPEN_PANEL,
			&messenger, NULL, B_FILE_NODE, false, message);
		if (fFilePanel == NULL)
			return B_NO_MEMORY;
		messageDeleter.Detach();
	}

	fFilePanelHandler->SetCurrentRef(_ref);
	fFilePanel->Show();
	return fFilePanelHandler->WaitForPanel();
}