/*
 * Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de>
 * Copyright 2005-2008, Jérôme DUVAL
 * All rights reserved. Distributed under the terms of the MIT License.
 */
 
 
#include "InstallerWindow.h"
 
#include <stdio.h>
#include <strings.h>
 
#include <Alert.h>
#include <Application.h>
#include <Autolock.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <LayoutUtils.h>
#include <Locale.h>
#include <MenuBar.h>
#include <MenuField.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollView.h>
#include <SeparatorView.h>
#include <SpaceLayoutItem.h>
#include <StatusBar.h>
#include <String.h>
#include <TextView.h>
#include <TranslationUtils.h>
#include <TranslatorFormats.h>
 
#include "tracker_private.h"
 
#include "DialogPane.h"
#include "InstallerDefs.h"
#include "PackageViews.h"
#include "PartitionMenuItem.h"
#include "WorkerThread.h"
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "InstallerWindow"
 
 
static const char* kDriveSetupSignature = "application/x-vnd.Haiku-DriveSetup";
static const char* kBootManagerSignature
	= "application/x-vnd.Haiku-BootManager";
 
const uint32 BEGIN_MESSAGE = 'iBGN';
const uint32 SHOW_BOTTOM_MESSAGE = 'iSBT';
const uint32 LAUNCH_DRIVE_SETUP = 'iSEP';
const uint32 LAUNCH_BOOTMAN = 'iWBM';
const uint32 START_SCAN = 'iSSC';
const uint32 PACKAGE_CHECKBOX = 'iPCB';
const uint32 ENCOURAGE_DRIVESETUP = 'iENC';
 
 
class LogoView : public BView {
public:
								LogoView(const BRect& frame);
								LogoView();
	virtual						~LogoView();
 
	virtual	void				Draw(BRect update);
 
	virtual	void				GetPreferredSize(float* _width,
									float* _height);
 
private:
			void				_Init();
 
			BBitmap*			fLogo;
};
 
 
LogoView::LogoView(const BRect& frame)
	:
	BView(frame, "logoview", B_FOLLOW_LEFT | B_FOLLOW_TOP,
		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
{
	_Init();
}
 
 
LogoView::LogoView()
	:
	BView("logoview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
{
	_Init();
}
 
 
LogoView::~LogoView(void)
{
	delete fLogo;
}
 
 
void
LogoView::Draw(BRect update)
{
	if (fLogo == NULL)
		return;
 
	BRect bounds(Bounds());
	BPoint placement;
	placement.x = (bounds.left + bounds.right - fLogo->Bounds().Width()) / 2;
	placement.y = (bounds.top + bounds.bottom - fLogo->Bounds().Height()) / 2;
 
	DrawBitmap(fLogo, placement);
}
 
 
void
LogoView::GetPreferredSize(float* _width, float* _height)
{
	float width = 0.0;
	float height = 0.0;
	if (fLogo) {
		width = fLogo->Bounds().Width();
		height = fLogo->Bounds().Height();
	}
	if (_width)
		*_width = width;
	if (_height)
		*_height = height;
}
 
 
void
LogoView::_Init()
{
	fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "logo.png");
}
 
 
// #pragma mark -
 
 
static BLayoutItem*
layout_item_for(BView* view)
{
	BLayout* layout = view->Parent()->GetLayout();
	int32 index = layout->IndexOfView(view);
	return layout->ItemAt(index);
}
 
 
InstallerWindow::InstallerWindow()
	:
	BWindow(BRect(-2000, -2000, -1800, -1800),
		B_TRANSLATE_SYSTEM_NAME("Installer"), B_TITLED_WINDOW,
		B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
	fEncouragedToSetupPartitions(false),
	fDriveSetupLaunched(false),
	fBootManagerLaunched(false),
	fInstallStatus(kReadyForInstall),
	fWorkerThread(new WorkerThread(this)),
	fCopyEngineCancelSemaphore(-1)
{
	if (!be_roster->IsRunning(kTrackerSignature))
		SetWorkspaces(B_ALL_WORKSPACES);
 
	LogoView* logoView = new LogoView();
 
	fStatusView = new BTextView("statusView", be_plain_font, NULL,
		B_WILL_DRAW);
	fStatusView->SetViewColor(255, 255, 255, 255);
	fStatusView->SetInsets(10, 0, 10, 0);
	fStatusView->MakeEditable(false);
	fStatusView->MakeSelectable(false);
 
	BSize logoSize = logoView->MinSize();
	logoView->SetExplicitMaxSize(logoSize);
	fStatusView->SetExplicitMinSize(BSize(logoSize.width * 0.8,
		logoSize.height));
 
	// Explicitly create group view to set the background white in case
	// height resizing is needed for the status view
	fLogoGroup = new BGroupView(B_HORIZONTAL, 0);
	fLogoGroup->SetViewColor(255, 255, 255);
	fLogoGroup->GroupLayout()->AddView(logoView);
	fLogoGroup->GroupLayout()->AddView(fStatusView);
 
	fDestMenu = new BPopUpMenu(B_TRANSLATE("scanning" B_UTF8_ELLIPSIS),
		true, false);
	fSrcMenu = new BPopUpMenu(B_TRANSLATE("scanning" B_UTF8_ELLIPSIS),
		true, false);
 
	fSrcMenuField = new BMenuField("srcMenuField",
		B_TRANSLATE("Install from:"), fSrcMenu);
	fSrcMenuField->SetAlignment(B_ALIGN_RIGHT);
 
	fDestMenuField = new BMenuField("destMenuField", B_TRANSLATE("Onto:"),
		fDestMenu);
	fDestMenuField->SetAlignment(B_ALIGN_RIGHT);
 
	fPackagesSwitch = new PaneSwitch("options_button");
	fPackagesSwitch->SetLabels(B_TRANSLATE("Hide optional packages"),
		B_TRANSLATE("Show optional packages"));
	fPackagesSwitch->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE));
	fPackagesSwitch->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
		B_SIZE_UNSET));
	fPackagesSwitch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
		B_ALIGN_TOP));
 
	fPackagesView = new PackagesView("packages_view");
	BScrollView* packagesScrollView = new BScrollView("packagesScroll",
		fPackagesView, B_WILL_DRAW, false, true);
 
	const char* requiredDiskSpaceString
		= B_TRANSLATE("Additional disk space required: 0.0 KiB");
	fSizeView = new BStringView("size_view", requiredDiskSpaceString);
	fSizeView->SetAlignment(B_ALIGN_RIGHT);
	fSizeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
	fSizeView->SetExplicitAlignment(
		BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE));
 
	fProgressBar = new BStatusBar("progress",
		B_TRANSLATE("Install progress:  "));
	fProgressBar->SetMaxValue(100.0);
 
	fBeginButton = new BButton("begin_button", B_TRANSLATE("Begin"),
		new BMessage(BEGIN_MESSAGE));
	fBeginButton->MakeDefault(true);
	fBeginButton->SetEnabled(false);
 
	fLaunchDriveSetupButton = new BButton("setup_button",
		B_TRANSLATE("Set up partitions" B_UTF8_ELLIPSIS),
		new BMessage(LAUNCH_DRIVE_SETUP));
 
	fLaunchBootManagerItem = new BMenuItem(B_TRANSLATE("Set up boot menu"),
		new BMessage(LAUNCH_BOOTMAN));
	fLaunchBootManagerItem->SetEnabled(false);
 
	fMakeBootableItem = new BMenuItem(B_TRANSLATE("Write boot sector"),
		new BMessage(MSG_WRITE_BOOT_SECTOR));
	fMakeBootableItem->SetEnabled(false);
	BMenuBar* mainMenu = new BMenuBar("main menu");
	BMenu* toolsMenu = new BMenu(B_TRANSLATE("Tools"));
	toolsMenu->AddItem(fLaunchBootManagerItem);
	toolsMenu->AddItem(fMakeBootableItem);
	mainMenu->AddItem(toolsMenu);
 
	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
		.Add(mainMenu)
		.Add(fLogoGroup)
		.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
		.AddGroup(B_VERTICAL, B_USE_ITEM_SPACING)
			.SetInsets(B_USE_WINDOW_SPACING)
			.AddGrid(new BGridView(B_USE_ITEM_SPACING, B_USE_ITEM_SPACING))
				.Add(fSrcMenuField->CreateLabelLayoutItem(), 0, 0)
				.Add(fSrcMenuField->CreateMenuBarLayoutItem(), 1, 0)
				.Add(fDestMenuField->CreateLabelLayoutItem(), 0, 1)
				.Add(fDestMenuField->CreateMenuBarLayoutItem(), 1, 1)
 
				.Add(BSpaceLayoutItem::CreateVerticalStrut(5), 0, 2, 2)
 
				.Add(fPackagesSwitch, 0, 3, 2)
				.Add(packagesScrollView, 0, 4, 2)
				.Add(fProgressBar, 0, 5, 2)
				.Add(fSizeView, 0, 6, 2)
			.End()
 
			.AddGroup(B_HORIZONTAL, B_USE_WINDOW_SPACING)
				.Add(fLaunchDriveSetupButton)
				.AddGlue()
				.Add(fBeginButton);
 
	// Make the optional packages and progress bar invisible on start
	fPackagesLayoutItem = layout_item_for(packagesScrollView);
	fPkgSwitchLayoutItem = layout_item_for(fPackagesSwitch);
	fSizeViewLayoutItem = layout_item_for(fSizeView);
	fProgressLayoutItem = layout_item_for(fProgressBar);
 
	fPackagesLayoutItem->SetVisible(false);
	fSizeViewLayoutItem->SetVisible(false);
	fProgressLayoutItem->SetVisible(false);
 
	// Setup tool tips for the non-obvious features
	fLaunchDriveSetupButton->SetToolTip(
		B_TRANSLATE("Launch the DriveSetup utility to partition\n"
		"available hard drives and other media.\n"
		"Partitions can be initialized with the\n"
		"Be File System needed for a Haiku boot\n"
		"partition."));
//	fLaunchBootManagerItem->SetToolTip(
//		B_TRANSLATE("Install or uninstall the Haiku boot menu, which allows "
//		"to choose an operating system to boot when the computer starts.\n"
//		"If this computer already has a boot manager such as GRUB installed, "
//		"it is better to add Haiku to that menu than to overwrite it."));
//	fMakeBootableItem->SetToolTip(
//		B_TRANSLATE("Writes the Haiku boot code to the partition start\n"
//		"sector. This step is automatically performed by\n"
//		"the installation, but you can manually make a\n"
//		"partition bootable in case you do not need to\n"
//		"perform an installation."));
 
	// finish creating window
	if (!be_roster->IsRunning(kDeskbarSignature))
		SetFlags(Flags() | B_NOT_MINIMIZABLE);
 
	CenterOnScreen();
	Show();
 
	// Register to receive notifications when apps launch or quit...
	be_roster->StartWatching(this);
	// ... and check the two we are interested in.
	fDriveSetupLaunched = be_roster->IsRunning(kDriveSetupSignature);
	fBootManagerLaunched = be_roster->IsRunning(kBootManagerSignature);
 
	if (Lock()) {
		fLaunchDriveSetupButton->SetEnabled(!fDriveSetupLaunched);
		fLaunchBootManagerItem->SetEnabled(!fBootManagerLaunched);
		Unlock();
	}
 
	PostMessage(START_SCAN);
}
 
 
InstallerWindow::~InstallerWindow()
{
	_SetCopyEngineCancelSemaphore(-1);
	be_roster->StopWatching(this);
}
 
 
void
InstallerWindow::MessageReceived(BMessage *msg)
{
	switch (msg->what) {
		case MSG_RESET:
		{
			_SetCopyEngineCancelSemaphore(-1);
 
			status_t error;
			if (msg->FindInt32("error", &error) == B_OK) {
				char errorMessage[2048];
				snprintf(errorMessage, sizeof(errorMessage),
					B_TRANSLATE("An error was encountered and the "
					"installation was not completed:\n\n"
					"Error:  %s"), strerror(error));
				BAlert* alert = new BAlert("error", errorMessage, B_TRANSLATE("OK"));
				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
				alert->Go();
			}
 
			_DisableInterface(false);
 
			fProgressLayoutItem->SetVisible(false);
			fPkgSwitchLayoutItem->SetVisible(true);
			_ShowOptionalPackages();
			_UpdateControls();
			break;
		}
		case START_SCAN:
			_ScanPartitions();
			break;
		case BEGIN_MESSAGE:
			switch (fInstallStatus) {
				case kReadyForInstall:
				{
					// get source and target
					PartitionMenuItem* targetItem
						= (PartitionMenuItem*)fDestMenu->FindMarked();
					PartitionMenuItem* srcItem
						= (PartitionMenuItem*)fSrcMenu->FindMarked();
					if (srcItem == NULL || targetItem == NULL)
						break;
 
					_SetCopyEngineCancelSemaphore(create_sem(1,
						"copy engine cancel"));
 
					BList* list = new BList();
					int32 size = 0;
					fPackagesView->GetPackagesToInstall(list, &size);
					fWorkerThread->SetLock(fCopyEngineCancelSemaphore);
					fWorkerThread->SetPackagesList(list);
					fWorkerThread->SetSpaceRequired(size);
					fInstallStatus = kInstalling;
					fWorkerThread->StartInstall(srcItem->ID(),
						targetItem->ID());
					fBeginButton->SetLabel(B_TRANSLATE("Stop"));
					_DisableInterface(true);
 
					fProgressBar->SetTo(0.0, NULL, NULL);
 
					fPkgSwitchLayoutItem->SetVisible(false);
					fPackagesLayoutItem->SetVisible(false);
					fSizeViewLayoutItem->SetVisible(false);
					fProgressLayoutItem->SetVisible(true);
					break;
				}
				case kInstalling:
				{
					_QuitCopyEngine(true);
					break;
				}
				case kFinished:
					PostMessage(B_QUIT_REQUESTED);
					break;
				case kCancelled:
					break;
			}
			break;
		case SHOW_BOTTOM_MESSAGE:
			_ShowOptionalPackages();
			break;
		case SOURCE_PARTITION:
			_PublishPackages();
			_UpdateControls();
			break;
		case TARGET_PARTITION:
			_UpdateControls();
			break;
		case LAUNCH_DRIVE_SETUP:
			_LaunchDriveSetup();
			break;
		case LAUNCH_BOOTMAN:
			_LaunchBootManager();
			break;
		case PACKAGE_CHECKBOX:
		{
			char buffer[15];
			fPackagesView->GetTotalSizeAsString(buffer, sizeof(buffer));
			char string[256];
			snprintf(string, sizeof(string),
				B_TRANSLATE("Additional disk space required: %s"), buffer);
			fSizeView->SetText(string);
			break;
		}
		case ENCOURAGE_DRIVESETUP:
		{
			BAlert* alert = new BAlert("use drive setup", B_TRANSLATE("No partitions have "
				"been found that are suitable for installation. Please set "
				"up partitions and initialize at least one partition with the "
				"Be File System."), B_TRANSLATE("OK"));
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();
			break;
		}
		case MSG_STATUS_MESSAGE:
		{
// TODO: Was this supposed to prevent status messages still arriving
// after the copy engine was shut down?
//			if (fInstallStatus != kInstalling)
//				break;
			float progress;
			if (msg->FindFloat("progress", &progress) == B_OK) {
				const char* currentItem;
				if (msg->FindString("item", &currentItem) != B_OK) {
					currentItem = B_TRANSLATE_COMMENT("???",
						"Unknown currently copied item");
				}
				BString trailingLabel;
				int32 currentCount;
				int32 maximumCount;
				if (msg->FindInt32("current", &currentCount) == B_OK
					&& msg->FindInt32("maximum", &maximumCount) == B_OK) {
					char buffer[64];
					snprintf(buffer, sizeof(buffer),
						B_TRANSLATE_COMMENT("%1ld of %2ld",
							"number of files copied"),
						currentCount, maximumCount);
					trailingLabel << buffer;
				} else {
					trailingLabel <<
						B_TRANSLATE_COMMENT("?? of ??", "Unknown progress");
				}
				fProgressBar->SetTo(progress, currentItem,
					trailingLabel.String());
			} else {
				const char *status;
				if (msg->FindString("status", &status) == B_OK) {
					fLastStatus = fStatusView->Text();
					_SetStatusMessage(status);
				} else
					_SetStatusMessage(fLastStatus.String());
			}
			break;
		}
		case MSG_INSTALL_FINISHED:
		{
 
			_SetCopyEngineCancelSemaphore(-1);
 
			PartitionMenuItem* dstItem
				= (PartitionMenuItem*)fDestMenu->FindMarked();
 
			BString status;
			if (be_roster->IsRunning(kDeskbarSignature)) {
				fBeginButton->SetLabel(B_TRANSLATE("Quit"));
				status.SetToFormat(B_TRANSLATE("Installation "
					"completed. Boot sector has been written to '%s'. Press "
					"Quit to leave the Installer or choose a new target "
					"volume to perform another installation."),
					dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???",
						"Unknown partition name"));
			} else {
				fBeginButton->SetLabel(B_TRANSLATE("Restart"));
				status.SetToFormat(B_TRANSLATE("Installation "
					"completed. Boot sector has been written to '%s'. Press "
					"Restart to restart the computer or choose a new target "
					"volume to perform another installation."),
					dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???",
						"Unknown partition name"));
			}
 
			_SetStatusMessage(status.String());
			fInstallStatus = kFinished;
			_DisableInterface(false);
			fProgressLayoutItem->SetVisible(false);
			fPkgSwitchLayoutItem->SetVisible(true);
			_ShowOptionalPackages();
			break;
		}
		case B_SOME_APP_LAUNCHED:
		case B_SOME_APP_QUIT:
		{
			const char *signature;
			if (msg->FindString("be:signature", &signature) != B_OK)
				break;
			bool isDriveSetup = !strcasecmp(signature, kDriveSetupSignature);
			bool isBootManager = !strcasecmp(signature, kBootManagerSignature);
			if (isDriveSetup || isBootManager) {
				bool scanPartitions = false;
				if (isDriveSetup) {
					bool launched = msg->what == B_SOME_APP_LAUNCHED;
					// We need to scan partitions if DriveSetup has quit.
					scanPartitions = fDriveSetupLaunched && !launched;
					fDriveSetupLaunched = launched;
				}
				if (isBootManager)
					fBootManagerLaunched = msg->what == B_SOME_APP_LAUNCHED;
 
				fBeginButton->SetEnabled(
					!fDriveSetupLaunched && !fBootManagerLaunched);
				_DisableInterface(fDriveSetupLaunched || fBootManagerLaunched);
				if (fDriveSetupLaunched && fBootManagerLaunched) {
					_SetStatusMessage(B_TRANSLATE("Running Boot Manager and "
						"DriveSetup" B_UTF8_ELLIPSIS
						"\n\nClose both applications to continue with the "
						"installation."));
				} else if (fDriveSetupLaunched) {
					_SetStatusMessage(B_TRANSLATE("Running DriveSetup"
						B_UTF8_ELLIPSIS
						"\n\nClose DriveSetup to continue with the "
						"installation."));
				} else if (fBootManagerLaunched) {
					_SetStatusMessage(B_TRANSLATE("Running Boot Manager"
						B_UTF8_ELLIPSIS
						"\n\nClose Boot Manager to continue with the "
						"installation."));
				} else {
					// If neither DriveSetup nor Bootman is running, we need
					// to scan partitions in case DriveSetup has quit, or
					// we need to update the guidance message, unless install
					// was already finished.
					if (scanPartitions)
						_ScanPartitions();
					else if (fInstallStatus != kFinished)
						_UpdateControls();
					else
						PostMessage(MSG_INSTALL_FINISHED);
				}
			}
			break;
		}
		case MSG_WRITE_BOOT_SECTOR:
			fWorkerThread->WriteBootSector(fDestMenu);
			break;
 
		default:
			BWindow::MessageReceived(msg);
			break;
	}
}
 
 
bool
InstallerWindow::QuitRequested()
{
	if ((Flags() & B_NOT_MINIMIZABLE) != 0) {
		// This means Deskbar is not running, i.e. Installer is the only
		// thing on the screen and we will reboot the machine once it quits.
 
		if (fDriveSetupLaunched && fBootManagerLaunched) {
			BAlert* alert = new BAlert(B_TRANSLATE("Quit Boot Manager and "
				"DriveSetup"),	B_TRANSLATE("Please close the Boot Manager "
				"and DriveSetup windows before closing the Installer window."),
				B_TRANSLATE("OK"));
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();
			return false;
		}
		if (fDriveSetupLaunched) {
			BAlert* alert = new BAlert(B_TRANSLATE("Quit DriveSetup"),
				B_TRANSLATE("Please close the DriveSetup window before "
				"closing the Installer window."), B_TRANSLATE("OK"));
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();
			return false;
		}
		if (fBootManagerLaunched) {
			BAlert* alert = new BAlert(B_TRANSLATE("Quit Boot Manager"),
				B_TRANSLATE("Please close the Boot Manager window before "
				"closing the Installer window."), B_TRANSLATE("OK"));
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();
			return false;
		}
		if (fInstallStatus != kFinished) {
			BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"),
				B_TRANSLATE("Are you sure you want to abort the "
					"installation and restart the system?"),
				B_TRANSLATE("Cancel"), B_TRANSLATE("Restart system"), NULL,
				B_WIDTH_AS_USUAL, B_STOP_ALERT);
			alert->SetShortcut(0, B_ESCAPE);
			if (alert->Go() == 0)
				return false;
		}
	} else if (fInstallStatus == kInstalling) {
			BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"),
				B_TRANSLATE("Are you sure you want to abort the installation?"),
				B_TRANSLATE("Cancel"), B_TRANSLATE("Abort"), NULL,
				B_WIDTH_AS_USUAL, B_STOP_ALERT);
			alert->SetShortcut(0, B_ESCAPE);
			if (alert->Go() == 0)
				return false;
	}
 
	_QuitCopyEngine(false);
	fWorkerThread->PostMessage(B_QUIT_REQUESTED);
	be_app->PostMessage(B_QUIT_REQUESTED);
	return true;
}
 
 
// #pragma mark -
 
 
void
InstallerWindow::_ShowOptionalPackages()
{
	if (fPackagesLayoutItem && fSizeViewLayoutItem) {
		fPackagesLayoutItem->SetVisible(fPackagesSwitch->Value());
		fSizeViewLayoutItem->SetVisible(fPackagesSwitch->Value());
	}
}
 
 
void
InstallerWindow::_LaunchDriveSetup()
{
	if (be_roster->Launch(kDriveSetupSignature) != B_OK) {
		// Try really hard to launch it. It's very likely that this fails,
		// when we run from the CD and there is only an incomplete mime
		// database for example...
		BPath path;
		if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK
			|| path.Append("DriveSetup") != B_OK) {
			path.SetTo("/boot/system/apps/DriveSetup");
		}
		BEntry entry(path.Path());
		entry_ref ref;
		if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) {
			BAlert* alert = new BAlert("error", B_TRANSLATE("DriveSetup, the "
				"application to configure disk partitions, could not be "
				"launched."), B_TRANSLATE("OK"));
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();
		}
	}
}
 
 
void
InstallerWindow::_LaunchBootManager()
{
	// TODO: Currently BootManager always tries to install to the "first"
	// harddisk. If/when it later supports being installed to a certain
	// harddisk, we would have to pass it the disk that contains the target
	// partition here.
	if (be_roster->Launch(kBootManagerSignature) != B_OK) {
		// Try really hard to launch it. It's very likely that this fails,
		// when we run from the CD and there is only an incomplete mime
		// database for example...
		BPath path;
		if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK
			|| path.Append("BootManager") != B_OK) {
			path.SetTo("/boot/system/apps/BootManager");
		}
		BEntry entry(path.Path());
		entry_ref ref;
		if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) {
			BAlert* alert = new BAlert(
				B_TRANSLATE("Failed to launch Boot Manager"),
				B_TRANSLATE("Boot Manager, the application to configure the "
					"Haiku boot menu, could not be launched."),
				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();
		}
	}
}
 
 
void
InstallerWindow::_DisableInterface(bool disable)
{
	fLaunchDriveSetupButton->SetEnabled(!disable);
	fLaunchBootManagerItem->SetEnabled(!disable);
	fMakeBootableItem->SetEnabled(!disable);
	fSrcMenuField->SetEnabled(!disable);
	fDestMenuField->SetEnabled(!disable);
}
 
 
void
InstallerWindow::_ScanPartitions()
{
	_SetStatusMessage(B_TRANSLATE("Scanning for disks" B_UTF8_ELLIPSIS));
 
	BMenuItem *item;
	while ((item = fSrcMenu->RemoveItem((int32)0)))
		delete item;
	while ((item = fDestMenu->RemoveItem((int32)0)))
		delete item;
 
	fWorkerThread->ScanDisksPartitions(fSrcMenu, fDestMenu);
 
	if (fSrcMenu->ItemAt(0) != NULL)
		_PublishPackages();
 
	_UpdateControls();
}
 
 
void
InstallerWindow::_UpdateControls()
{
	PartitionMenuItem* srcItem = (PartitionMenuItem*)fSrcMenu->FindMarked();
	BString label;
	if (srcItem) {
		label = srcItem->MenuLabel();
	} else {
		if (fSrcMenu->CountItems() == 0)
			label = B_TRANSLATE_COMMENT("<none>", "No partition available");
		else
			label = ((PartitionMenuItem*)fSrcMenu->ItemAt(0))->MenuLabel();
	}
	fSrcMenuField->MenuItem()->SetLabel(label.String());
 
	// Disable any unsuitable target items, check if at least one partition
	// is suitable.
	bool foundOneSuitableTarget = false;
	for (int32 i = fDestMenu->CountItems() - 1; i >= 0; i--) {
		PartitionMenuItem* dstItem
			= (PartitionMenuItem*)fDestMenu->ItemAt(i);
		if (srcItem != NULL && dstItem->ID() == srcItem->ID()) {
			// Prevent the user from having picked the same partition as source
			// and destination.
			dstItem->SetEnabled(false);
			dstItem->SetMarked(false);
		} else
			dstItem->SetEnabled(dstItem->IsValidTarget());
 
		if (dstItem->IsEnabled())
			foundOneSuitableTarget = true;
	}
 
	PartitionMenuItem* dstItem = (PartitionMenuItem*)fDestMenu->FindMarked();
	if (dstItem) {
		label = dstItem->MenuLabel();
	} else {
		if (fDestMenu->CountItems() == 0)
			label = B_TRANSLATE_COMMENT("<none>", "No partition available");
		else
			label = B_TRANSLATE("Please choose target");
	}
	fDestMenuField->MenuItem()->SetLabel(label.String());
 
	if (srcItem != NULL && dstItem != NULL) {
		BString message;
		message.SetToFormat(B_TRANSLATE("Press the Begin button to install "
			"from '%1s' onto '%2s'."), srcItem->Name(), dstItem->Name());
		_SetStatusMessage(message.String());
	} else if (srcItem != NULL) {
		_SetStatusMessage(B_TRANSLATE("Choose the disk you want to install "
			"onto from the pop-up menu. Then click \"Begin\"."));
	} else if (dstItem != NULL) {
		_SetStatusMessage(B_TRANSLATE("Choose the source disk from the "
			"pop-up menu. Then click \"Begin\"."));
	} else {
		_SetStatusMessage(B_TRANSLATE("Choose the source and destination disk "
			"from the pop-up menus. Then click \"Begin\"."));
	}
 
	fInstallStatus = kReadyForInstall;
	fBeginButton->SetLabel(B_TRANSLATE("Begin"));
	fBeginButton->SetEnabled(srcItem && dstItem);
 
	// adjust "Write Boot Sector" and "Set up boot menu" buttons
	if (dstItem != NULL) {
		char buffer[256];
		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Write boot sector to '%s'"),
			dstItem->Name());
		label = buffer;
	} else
		label = B_TRANSLATE("Write boot sector");
	fMakeBootableItem->SetEnabled(dstItem != NULL);
	fMakeBootableItem->SetLabel(label.String());
// TODO: Once bootman support writing to specific disks, enable this, since
// we would pass it the disk which contains the target partition.
//	fLaunchBootManagerItem->SetEnabled(dstItem != NULL);
 
	if (!fEncouragedToSetupPartitions && !foundOneSuitableTarget) {
		// Focus the users attention on the DriveSetup button
		fEncouragedToSetupPartitions = true;
		PostMessage(ENCOURAGE_DRIVESETUP);
	}
}
 
 
void
InstallerWindow::_PublishPackages()
{
	fPackagesView->Clean();
	PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked();
	if (item == NULL)
		return;
 
	BPath directory;
	BDiskDeviceRoster roster;
	BDiskDevice device;
	BPartition *partition;
	if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) {
		if (partition->GetMountPoint(&directory) != B_OK)
			return;
	} else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) {
		if (device.GetMountPoint(&directory) != B_OK)
			return;
	} else
		return; // shouldn't happen
 
	directory.Append(kPackagesDirectoryPath);
	BDirectory dir(directory.Path());
	if (dir.InitCheck() != B_OK)
		return;
 
	BEntry packageEntry;
	BList packages;
	while (dir.GetNextEntry(&packageEntry) == B_OK) {
		Package* package = Package::PackageFromEntry(packageEntry);
		if (package != NULL)
			packages.AddItem(package);
	}
	packages.SortItems(_ComparePackages);
 
	fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX));
	PostMessage(PACKAGE_CHECKBOX);
}
 
 
void
InstallerWindow::_SetStatusMessage(const char *text)
{
	fStatusView->SetText(text);
	fLogoGroup->GroupLayout()->InvalidateLayout();
}
 
 
void
InstallerWindow::_SetCopyEngineCancelSemaphore(sem_id id, bool alreadyLocked)
{
	if (fCopyEngineCancelSemaphore >= 0) {
		if (!alreadyLocked)
			acquire_sem(fCopyEngineCancelSemaphore);
		delete_sem(fCopyEngineCancelSemaphore);
	}
	fCopyEngineCancelSemaphore = id;
}
 
 
void
InstallerWindow::_QuitCopyEngine(bool askUser)
{
	if (fCopyEngineCancelSemaphore < 0)
		return;
 
	// First of all block the copy engine, so that it doesn't continue
	// while the alert is showing, which would be irritating.
	acquire_sem(fCopyEngineCancelSemaphore);
 
	bool quit = true;
	if (askUser) {
		BAlert* alert = new BAlert("cancel",
			B_TRANSLATE("Are you sure you want to to stop the installation?"),
			B_TRANSLATE_COMMENT("Continue", "In alert after pressing Stop"),
			B_TRANSLATE_COMMENT("Stop", "In alert after pressing Stop"), 0,
			B_WIDTH_AS_USUAL, B_STOP_ALERT);
		alert->SetShortcut(1, B_ESCAPE);
		quit = alert->Go() != 0;
	}
 
	if (quit) {
		// Make it quit by having it's lock fail...
		_SetCopyEngineCancelSemaphore(-1, true);
	} else
		release_sem(fCopyEngineCancelSemaphore);
}
 
 
// #pragma mark -
 
 
int
InstallerWindow::_ComparePackages(const void* firstArg, const void* secondArg)
{
	const Group* group1 = *static_cast<const Group* const *>(firstArg);
	const Group* group2 = *static_cast<const Group* const *>(secondArg);
	const Package* package1 = dynamic_cast<const Package*>(group1);
	const Package* package2 = dynamic_cast<const Package*>(group2);
	int sameGroup = strcmp(group1->GroupName(), group2->GroupName());
	if (sameGroup != 0)
		return sameGroup;
	if (package2 == NULL)
		return -1;
	if (package1 == NULL)
		return 1;
	return strcmp(package1->Name(), package2->Name());
}
 
 

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

V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.

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.

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

V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.

V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.

V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.

V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.