* Copyright 2002-2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Ithamar R. Adema <ithamar@unet.nl>
* Stephan Aßmus <superstippi@gmx.de>
* Axel Dörfler, axeld@pinc-software.de.
* Erik Jaesler <ejakowatz@users.sourceforge.net>
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include "MainWindow.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <Alert.h>
#include <AppFileInfo.h>
#include <Application.h>
#include <Catalog.h>
#include <ColumnListView.h>
#include <ColumnTypes.h>
#include <ControlLook.h>
#include <Debug.h>
#include <DiskDevice.h>
#include <DiskDeviceRoster.h>
#include <DiskDeviceVisitor.h>
#include <DiskDeviceTypes.h>
#include <DiskSystem.h>
#include <IconUtils.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <MenuItem.h>
#include <MenuBar.h>
#include <Menu.h>
#include <Path.h>
#include <Partition.h>
#include <PartitioningInfo.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollBar.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <fs_volume.h>
#include <tracker_private.h>
#include "ChangeParametersPanel.h"
#include "ColumnListView.h"
#include "CreateParametersPanel.h"
#include "DiskView.h"
#include "InitParametersPanel.h"
#include "PartitionList.h"
#include "Support.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MainWindow"
enum {
MSG_MOUNT_ALL = 'mnta',
MSG_MOUNT = 'mnts',
MSG_UNMOUNT = 'unmt',
MSG_FORMAT = 'frmt',
MSG_CREATE = 'crtp',
MSG_CHANGE = 'chgp',
MSG_INITIALIZE = 'init',
MSG_DELETE = 'delt',
MSG_EJECT = 'ejct',
MSG_OPEN_DISKPROBE = 'opdp',
MSG_SURFACE_TEST = 'sfct',
MSG_RESCAN = 'rscn',
MSG_REGISTER = 'regi',
MSG_UNREGISTER = 'unrg',
MSG_SAVE = 'save',
MSG_WRITE = 'writ',
MSG_COMPLETE = 'comp',
MSG_PARTITION_ROW_SELECTED = 'prsl',
};
class ListPopulatorVisitor : public BDiskDeviceVisitor {
public:
ListPopulatorVisitor(PartitionListView* list, int32& diskCount,
SpaceIDMap& spaceIDMap)
:
fPartitionList(list),
fDiskCount(diskCount),
fSpaceIDMap(spaceIDMap)
{
fDiskCount = 0;
fSpaceIDMap.Clear();
int32 rows = fPartitionList->CountRows();
for (int32 i = rows - 1; i >= 0; i--) {
BRow* row = fPartitionList->RowAt(i);
fPartitionList->RemoveRow(row);
delete row;
}
}
virtual bool Visit(BDiskDevice* device)
{
fDiskCount++;
device->PrepareModifications();
_AddPartition(device);
return false;
}
virtual bool Visit(BPartition* partition, int32 level)
{
_AddPartition(partition);
return false;
}
private:
void _AddPartition(BPartition* partition) const
{
fPartitionList->AddPartition(partition);
BPartitioningInfo info;
status_t status = partition->GetPartitioningInfo(&info);
if (status >= B_OK) {
partition_id parentID = partition->ID();
off_t offset;
off_t size;
for (int32 i = 0;
info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
i++) {
if (!is_valid_partitionable_space(size))
continue;
partition_id id = fSpaceIDMap.SpaceIDFor(parentID, offset);
fPartitionList->AddSpace(parentID, id, offset, size);
}
}
}
PartitionListView* fPartitionList;
int32& fDiskCount;
SpaceIDMap& fSpaceIDMap;
BDiskDevice* fLastPreparedDevice;
};
class MountAllVisitor : public BDiskDeviceVisitor {
public:
MountAllVisitor()
{
}
virtual bool Visit(BDiskDevice* device)
{
if (device->ContainsFileSystem())
device->Mount();
return false;
}
virtual bool Visit(BPartition* partition, int32 level)
{
partition->Mount();
return false;
}
private:
PartitionListView* fPartitionList;
};
class ModificationPreparer {
public:
ModificationPreparer(BDiskDevice* disk)
:
fDisk(disk),
fModificationStatus(fDisk->PrepareModifications())
{
}
~ModificationPreparer()
{
if (fModificationStatus == B_OK)
fDisk->CancelModifications();
}
status_t ModificationStatus() const
{
return fModificationStatus;
}
status_t CommitModifications()
{
status_t status = fDisk->CommitModifications();
if (status == B_OK)
fModificationStatus = B_ERROR;
return status;
}
private:
BDiskDevice* fDisk;
status_t fModificationStatus;
};
MainWindow::MainWindow()
:
BWindow(BRect(50, 50, 600, 500), B_TRANSLATE_SYSTEM_NAME("DriveSetup"),
B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
fCurrentDisk(NULL),
fCurrentPartitionID(-1),
fSpaceIDMap()
{
fMenuBar = new BMenuBar("root menu");
fWipeMenuItem = new BMenuItem(B_TRANSLATE("Wipe (not implemented)"),
new BMessage(MSG_FORMAT));
fEjectMenuItem = new BMenuItem(B_TRANSLATE("Eject"),
new BMessage(MSG_EJECT), 'E');
fOpenDiskProbeMenuItem = new BMenuItem(B_TRANSLATE("Open with DiskProbe"),
new BMessage(MSG_OPEN_DISKPROBE));
fSurfaceTestMenuItem = new BMenuItem(
B_TRANSLATE("Surface test (not implemented)"),
new BMessage(MSG_SURFACE_TEST));
fRescanMenuItem = new BMenuItem(B_TRANSLATE("Rescan"),
new BMessage(MSG_RESCAN));
fCreateMenuItem = new BMenuItem(B_TRANSLATE("Create" B_UTF8_ELLIPSIS),
new BMessage(MSG_CREATE), 'C');
fChangeMenuItem = new BMenuItem(
B_TRANSLATE("Change parameters" B_UTF8_ELLIPSIS),
new BMessage(MSG_CHANGE));
fDeleteMenuItem = new BMenuItem(B_TRANSLATE("Delete"),
new BMessage(MSG_DELETE), 'D');
fMountMenuItem = new BMenuItem(B_TRANSLATE("Mount"),
new BMessage(MSG_MOUNT), 'M');
fUnmountMenuItem = new BMenuItem(B_TRANSLATE("Unmount"),
new BMessage(MSG_UNMOUNT), 'U');
fMountAllMenuItem = new BMenuItem(B_TRANSLATE("Mount all"),
new BMessage(MSG_MOUNT_ALL), 'M', B_SHIFT_KEY);
fRegisterMenuItem = new BMenuItem(
B_TRANSLATE("Register disk image" B_UTF8_ELLIPSIS),
new BMessage(MSG_REGISTER));
fUnRegisterMenuItem = new BMenuItem(
B_TRANSLATE("Unregister disk image"),
new BMessage(MSG_UNREGISTER));
fSaveMenuItem = new BMenuItem(
B_TRANSLATE("Save image" B_UTF8_ELLIPSIS),
new BMessage(MSG_SAVE));
fWriteMenuItem = new BMenuItem(
B_TRANSLATE("Write image" B_UTF8_ELLIPSIS),
new BMessage(MSG_WRITE));
fDiskMenu = new BMenu(B_TRANSLATE("Disk"));
fDiskInitMenu = new BMenu(B_TRANSLATE("Initialize"));
fDiskMenu->AddItem(fDiskInitMenu);
fDiskMenu->AddSeparatorItem();
fDiskMenu->AddItem(fEjectMenuItem);
fDiskMenu->AddItem(fRescanMenuItem);
fMenuBar->AddItem(fDiskMenu);
fPartitionMenu = new BMenu(B_TRANSLATE("Partition"));
fPartitionMenu->AddItem(fCreateMenuItem);
fFormatMenu = new BMenu(B_TRANSLATE("Format"));
fPartitionMenu->AddItem(fFormatMenu);
fPartitionMenu->AddItem(fChangeMenuItem);
fPartitionMenu->AddItem(fDeleteMenuItem);
fPartitionMenu->AddSeparatorItem();
fPartitionMenu->AddItem(fMountMenuItem);
fPartitionMenu->AddItem(fUnmountMenuItem);
fPartitionMenu->AddSeparatorItem();
fPartitionMenu->AddItem(fMountAllMenuItem);
fPartitionMenu->AddSeparatorItem();
fPartitionMenu->AddItem(fOpenDiskProbeMenuItem);
fMenuBar->AddItem(fPartitionMenu);
fDiskImageMenu = new BMenu(B_TRANSLATE("Disk image"));
fDiskImageMenu->AddItem(fRegisterMenuItem);
fDiskImageMenu->AddItem(fUnRegisterMenuItem);
fDiskImageMenu->AddSeparatorItem();
fDiskImageMenu->AddItem(fSaveMenuItem);
fDiskImageMenu->AddItem(fWriteMenuItem);
fMenuBar->AddItem(fDiskImageMenu);
BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 0);
layout->SetInsets(-1, 0, -1, -1);
SetLayout(layout);
AddChild(fMenuBar);
fContextMenu = new BPopUpMenu("Partition", false, false);
fCreateContextMenuItem = new BMenuItem(B_TRANSLATE("Create" B_UTF8_ELLIPSIS),
new BMessage(MSG_CREATE), 'C');
fChangeContextMenuItem = new BMenuItem(
B_TRANSLATE("Change parameters" B_UTF8_ELLIPSIS),
new BMessage(MSG_CHANGE));
fDeleteContextMenuItem = new BMenuItem(B_TRANSLATE("Delete"),
new BMessage(MSG_DELETE), 'D');
fMountContextMenuItem = new BMenuItem(B_TRANSLATE("Mount"),
new BMessage(MSG_MOUNT), 'M');
fUnmountContextMenuItem = new BMenuItem(B_TRANSLATE("Unmount"),
new BMessage(MSG_UNMOUNT), 'U');
fOpenDiskProbeContextMenuItem = new BMenuItem(B_TRANSLATE("Open with DiskProbe"),
new BMessage(MSG_OPEN_DISKPROBE));
fFormatContextMenuItem = new BMenu(B_TRANSLATE("Format"));
fContextMenu->AddItem(fCreateContextMenuItem);
fContextMenu->AddItem(fFormatContextMenuItem);
fContextMenu->AddItem(fChangeContextMenuItem);
fContextMenu->AddItem(fDeleteContextMenuItem);
fContextMenu->AddSeparatorItem();
fContextMenu->AddItem(fMountContextMenuItem);
fContextMenu->AddItem(fUnmountContextMenuItem);
fContextMenu->AddSeparatorItem();
fContextMenu->AddItem(fOpenDiskProbeContextMenuItem);
fContextMenu->SetTargetForItems(this);
fDiskView = new DiskView(fSpaceIDMap);
AddChild(fDiskView);
fListView = new PartitionListView();
AddChild(fListView);
fListView->SetSelectionMode(B_SINGLE_SELECTION_LIST);
fListView->SetSelectionMessage(new BMessage(MSG_PARTITION_ROW_SELECTED));
fListView->SetTarget(this);
fListView->MakeFocus(true);
fRegisterFilePanel = NULL;
fWriteImageFilePanel = NULL;
fReadImageFilePanel = NULL;
fNotification = new BNotification(B_INFORMATION_NOTIFICATION);
fNotification->SetGroup(BString(B_TRANSLATE_SYSTEM_NAME("DriveSetup")));
app_info info;
be_roster->GetAppInfo("application/x-vnd.Haiku-DriveSetup", &info);
BBitmap icon(BRect(0, 0, 32, 32), B_RGBA32);
BNode node(&info.ref);
if (BIconUtils::GetVectorIcon(&node, "BEOS:ICON", &icon) == B_OK)
fNotification->SetIcon(&icon);
status_t status = fDiskDeviceRoster.StartWatching(BMessenger(this));
if (status != B_OK) {
fprintf(stderr, "Failed to start watching for device changes: %s\n",
strerror(status));
}
_ScanDrives();
if (!be_roster->IsRunning(kDeskbarSignature)) {
SetFlags(Flags() | B_NOT_MINIMIZABLE);
SetWorkspaces(B_ALL_WORKSPACES);
}
}
MainWindow::~MainWindow()
{
BDiskDeviceRoster().StopWatching(this);
delete fCurrentDisk;
delete fContextMenu;
delete fRegisterFilePanel;
delete fReadImageFilePanel;
delete fWriteImageFilePanel;
delete fNotification;
}
void
MainWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_MOUNT_ALL:
_MountAll();
break;
case MSG_MOUNT:
_Mount(fCurrentDisk, fCurrentPartitionID);
break;
case MSG_UNMOUNT:
_Unmount(fCurrentDisk, fCurrentPartitionID);
break;
case MSG_FORMAT:
printf("MSG_FORMAT\n");
break;
case MSG_CREATE:
_Create(fCurrentDisk, fCurrentPartitionID);
break;
case MSG_INITIALIZE: {
BString diskSystemName;
if (message->FindString("disk system", &diskSystemName) != B_OK)
break;
_Initialize(fCurrentDisk, fCurrentPartitionID, diskSystemName);
break;
}
case MSG_CHANGE:
_ChangeParameters(fCurrentDisk, fCurrentPartitionID);
break;
case MSG_DELETE:
_Delete(fCurrentDisk, fCurrentPartitionID);
break;
case MSG_EJECT:
if (fCurrentDisk) {
fCurrentDisk->Eject(true);
_ScanDrives();
}
break;
case MSG_OPEN_DISKPROBE:
{
PartitionListRow* row = dynamic_cast<PartitionListRow*>(
fListView->CurrentSelection());
const char* args[] = { row->DevicePath(), NULL };
be_roster->Launch("application/x-vnd.Haiku-DiskProbe", 1,
(char**)args);
break;
}
case MSG_SURFACE_TEST:
printf("MSG_SURFACE_TEST\n");
break;
case B_DEVICE_UPDATE:
printf("B_DEVICE_UPDATE\n");
case MSG_RESCAN:
_ScanDrives();
break;
case MSG_PARTITION_ROW_SELECTED: {
_AdaptToSelectedPartition();
BPoint where;
uint32 buttons;
fListView->GetMouse(&where, &buttons);
where.x += 2;
if (buttons & B_SECONDARY_MOUSE_BUTTON)
fContextMenu->Go(fListView->ConvertToScreen(where),
true, false, true);
break;
}
case MSG_SELECTED_PARTITION_ID: {
partition_id id;
if (message->FindInt32("partition_id", &id) == B_OK) {
if (BRow* row = fListView->FindRow(id)) {
fListView->DeselectAll();
fListView->AddToSelection(row);
_AdaptToSelectedPartition();
}
}
BPoint where;
uint32 buttons;
fListView->GetMouse(&where, &buttons);
where.x += 2;
if (buttons & B_SECONDARY_MOUSE_BUTTON)
fContextMenu->Go(fListView->ConvertToScreen(where),
true, false, true);
break;
}
case MSG_REGISTER:
{
entry_ref entryRef;
message->FindRef("refs", &entryRef);
BEntry entry(&entryRef);
BPath path;
if (entry.GetPath(&path) == B_OK) {
RegisterFileDiskDevice(path.Path());
break;
}
if (fRegisterFilePanel == NULL) {
fRegisterFilePanel = new(std::nothrow) BFilePanel(B_OPEN_PANEL,
new BMessenger(this), NULL, B_FILE_NODE, false,
new BMessage(MSG_REGISTER), NULL, true);
BString title(B_TRANSLATE("%appname%: Register disk image"));
title.ReplaceFirst("%appname%", B_TRANSLATE_SYSTEM_NAME("DriveSetup"));
fRegisterFilePanel->Window()->SetTitle(title);
fRegisterFilePanel->SetButtonLabel(B_DEFAULT_BUTTON, B_TRANSLATE("Register"));
}
if (fRegisterFilePanel != NULL)
fRegisterFilePanel->Show();
break;
}
case MSG_UNREGISTER:
{
BPath path;
fCurrentDisk->GetPath(&path);
UnRegisterFileDiskDevice(path.Path());
break;
}
case MSG_WRITE:
{
entry_ref entryRef;
message->FindRef("refs", &entryRef);
BEntry entry(&entryRef);
BPath path;
if (entry.GetPath(&path) == B_OK) {
BMessage* msg = new BMessage();
PartitionListRow* row = dynamic_cast<PartitionListRow*>(
fListView->CurrentSelection());
msg->AddString("source", path.Path());
msg->AddString("target", row->DevicePath());
msg->AddMessenger("messenger", BMessenger(this));
thread_id write_thread = spawn_thread(WriteDiskImage,
"WriteDiskImage", B_LOW_PRIORITY, (void*)msg);
resume_thread(write_thread);
} else {
if (fWriteImageFilePanel == NULL) {
fWriteImageFilePanel = new(std::nothrow) BFilePanel(
B_OPEN_PANEL, new BMessenger(this), NULL, B_FILE_NODE,
false, new BMessage(MSG_WRITE), NULL, true);
BString title(B_TRANSLATE("%appname%: Select disk image"));
title.ReplaceFirst("%appname%", B_TRANSLATE_SYSTEM_NAME("DriveSetup"));
fWriteImageFilePanel->Window()->SetTitle(title);
fWriteImageFilePanel->SetButtonLabel(B_DEFAULT_BUTTON, B_TRANSLATE("Write"));
}
if (fWriteImageFilePanel != NULL)
fWriteImageFilePanel->Show();
}
break;
}
case MSG_SAVE:
{
PartitionListRow* row = dynamic_cast<PartitionListRow*>(
fListView->CurrentSelection());
if (fReadImageFilePanel == NULL) {
fReadImageFilePanel = new(std::nothrow) BFilePanel(B_SAVE_PANEL,
new BMessenger(this), NULL, B_FILE_NODE, false, NULL, NULL,
true);
BString title(B_TRANSLATE("%appname%: Save disk image"));
title.ReplaceFirst("%appname%", B_TRANSLATE_SYSTEM_NAME("DriveSetup"));
fReadImageFilePanel->Window()->SetTitle(title);
}
if (fReadImageFilePanel != NULL) {
BStringField* field = (BStringField*)row->GetField(2);
fReadImageFilePanel->SetSaveText(field->String());
fReadImageFilePanel->Show();
}
break;
}
case B_SAVE_REQUESTED:
{
BMessage* msg = new BMessage();
entry_ref entryRef;
message->FindRef("directory", &entryRef);
BEntry entry(&entryRef);
BPath path;
entry.GetPath(&path);
msg->AddString("targetfolder", path.Path());
const char* name;
message->FindString("name", &name);
path.Append(name);
PartitionListRow* row = dynamic_cast<PartitionListRow*>(
fListView->CurrentSelection());
msg->AddString("source", row->DevicePath());
msg->AddString("target", path.Path());
msg->AddInt32("partitionID", row->ID());
msg->AddMessenger("messenger", BMessenger(this));
thread_id save_thread = spawn_thread(SaveDiskImage, "SaveDiskImage",
B_LOW_PRIORITY, (void*)msg);
resume_thread(save_thread);
break;
}
case MSG_COMPLETE:
{
const char* status = "";
const char* jobid = "";
message->FindString("status", &status);
message->FindString("jobid", &jobid);
fNotification->SetTitle(BString(B_TRANSLATE("Disk image")));
fNotification->SetContent(BString(status));
fNotification->SetMessageID(BString(jobid));
fNotification->Send();
break;
}
case MSG_UPDATE_ZOOM_LIMITS:
_UpdateWindowZoomLimits();
break;
case B_REFS_RECEIVED:
{
entry_ref ref;
int32 i = 0;
while (message->FindRef("refs", i++, &ref) == B_OK) {
BEntry entry(&ref, true);
BPath path(&entry);
BNode node(&entry);
if (node.IsFile()) {
if (entry.GetPath(&path) == B_OK)
RegisterFileDiskDevice(path.Path());
}
}
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
bool
MainWindow::QuitRequested()
{
be_app->PostMessage(B_QUIT_REQUESTED);
Hide();
return false;
}
status_t
MainWindow::StoreSettings(BMessage* archive) const
{
if (archive->ReplaceRect("window frame", Frame()) < B_OK)
archive->AddRect("window frame", Frame());
BMessage columnSettings;
fListView->SaveState(&columnSettings);
if (archive->ReplaceMessage("column settings", &columnSettings) < B_OK)
archive->AddMessage("column settings", &columnSettings);
return B_OK;
}
status_t
MainWindow::RestoreSettings(BMessage* archive)
{
BRect frame;
if (archive->FindRect("window frame", &frame) == B_OK) {
BScreen screen(this);
if (frame.Intersects(screen.Frame())) {
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
}
}
BMessage columnSettings;
if (archive->FindMessage("column settings", &columnSettings) == B_OK)
fListView->LoadState(&columnSettings);
return B_OK;
}
void
MainWindow::ApplyDefaultSettings()
{
if (!Lock())
return;
fListView->ResizeAllColumnsToPreferred();
BScreen screen(this);
float windowWidth = Frame().Width();
float windowHeight = Frame().Height();
float enlargeWidthBy = fListView->PreferredSize().width
- fListView->Bounds().Width();
float enlargeHeightBy = fListView->PreferredSize().height
- fListView->Bounds().Height();
if (enlargeWidthBy > 0.0f)
windowWidth += enlargeWidthBy;
if (enlargeHeightBy > 0.0f)
windowHeight += enlargeHeightBy;
if (windowWidth > screen.Frame().Width() - 20.0f)
windowWidth = screen.Frame().Width() - 20.0f;
if (windowHeight > screen.Frame().Height() - 20.0f)
windowHeight = screen.Frame().Height() - 20.0f;
ResizeTo(windowWidth, windowHeight);
CenterOnScreen();
Unlock();
}
static void
FileErrorAlert(const char* text, const char* fileName, alert_type type)
{
BString message;
message.SetToFormat(text, fileName);
BString title(B_TRANSLATE("%appname% error"));
title.ReplaceFirst("%appname%", B_TRANSLATE_SYSTEM_NAME("DriveSetup"));
BAlert* alert = new BAlert(title, message.String(),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_WIDEST, type);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(NULL);
}
status_t
MainWindow::RegisterFileDiskDevice(const char* fileName)
{
BString message;
struct stat st;
if (lstat(fileName, &st) != 0) {
status_t error = errno;
FileErrorAlert(B_TRANSLATE("Failed to get information about '%s'."),
fileName, B_STOP_ALERT);
return error;
}
if (!S_ISREG(st.st_mode)) {
FileErrorAlert(B_TRANSLATE("'%s' is not a regular file."), fileName,
B_STOP_ALERT);
return B_BAD_VALUE;
}
BDiskDeviceRoster roster;
partition_id id = roster.RegisterFileDevice(fileName);
if (id < 0) {
FileErrorAlert(B_TRANSLATE("Failed to register file disk device: %s"),
fileName, B_STOP_ALERT);
return id;
}
fNotification->SetTitle(BString(B_TRANSLATE("Disk image")));
fNotification->SetContent(BString(
B_TRANSLATE("Disk image has been added.")));
fNotification->Send();
return B_OK;
}
status_t
MainWindow::UnRegisterFileDiskDevice(const char* fileNameOrID)
{
BString message;
char* numberEnd;
partition_id id = strtol(fileNameOrID, &numberEnd, 0);
BDiskDeviceRoster roster;
if (id >= 0 && numberEnd != fileNameOrID && *numberEnd == '\0') {
BDiskDevice device;
if (roster.GetDeviceWithID(id, &device) == B_OK && device.IsFile()) {
status_t error = roster.UnregisterFileDevice(id);
if (error != B_OK) {
FileErrorAlert(B_TRANSLATE(
"Failed to unregister file disk device."), NULL,
B_STOP_ALERT);
return error;
}
fNotification->SetTitle(BString(B_TRANSLATE("Disk image")));
fNotification->SetContent(BString(
B_TRANSLATE("Disk image has been removed.")));
fNotification->Send();
return B_OK;
} else {
FileErrorAlert(B_TRANSLATE(
"No file disk device found with the given ID, trying file '%s'."),
fileNameOrID, B_WARNING_ALERT);
}
}
struct stat st;
if (lstat(fileNameOrID, &st) != 0) {
status_t error = errno;
FileErrorAlert(B_TRANSLATE("Failed to get information about '%s'."),
fileNameOrID, B_STOP_ALERT);
return error;
}
dev_t volumeID = st.st_dev;
ino_t nodeID = st.st_ino;
BDiskDevice device;
while (roster.GetNextDevice(&device) == B_OK) {
if (!device.IsFile())
continue;
BPath path;
if ((device.GetFilePath(&path) == B_OK && lstat(path.Path(), &st) == 0
&& volumeID == st.st_dev && nodeID == st.st_ino)
|| (device.GetPath(&path) == B_OK && lstat(path.Path(), &st) == 0
&& volumeID == st.st_dev && nodeID == st.st_ino)) {
status_t error = roster.UnregisterFileDevice(device.ID());
if (error != B_OK) {
FileErrorAlert(B_TRANSLATE(
"Failed to unregister file disk device %s."), fileNameOrID,
B_STOP_ALERT);
return error;
}
fNotification->SetTitle(BString(B_TRANSLATE("Disk image")));
fNotification->SetContent(BString(
B_TRANSLATE("Disk image has been removed.")));
fNotification->Send();
return B_OK;
}
}
FileErrorAlert(B_TRANSLATE("%s does not refer to a file disk device."),
fileNameOrID, B_WARNING_ALERT);
return B_BAD_VALUE;
}
int32
MainWindow::SaveDiskImage(void* data)
{
BMessage* msg = (BMessage*)data;
const char* sourcepath;
const char* targetpath;
const char* targetfolder;
int32 partitionID;
BMessenger messenger;
msg->FindString("source", &sourcepath);
msg->FindString("target", &targetpath);
msg->FindString("targetfolder", &targetfolder);
msg->FindInt32("partitionID", &partitionID);
msg->FindMessenger("messenger", &messenger);
BFile source(sourcepath, B_READ_ONLY);
BFile target(targetpath, B_READ_WRITE | B_CREATE_FILE);
if (source.InitCheck() != B_OK) {
FileErrorAlert(B_TRANSLATE("Cannot init source volume."), NULL,
B_STOP_ALERT);
return -1;
}
if (target.InitCheck() != B_OK) {
FileErrorAlert(B_TRANSLATE("Cannot init target file."), NULL,
B_STOP_ALERT);
return -1;
}
BDirectory* targetdir = new BDirectory(targetfolder);
node_ref node;
targetdir->GetNodeRef(&node);
BVolume volume(node.device);
if (volume.InitCheck()) {
FileErrorAlert(B_TRANSLATE("Cannot init target volume."), NULL,
B_STOP_ALERT);
return -1;
}
if (volume.IsReadOnly()) {
FileErrorAlert(B_TRANSLATE("Target volume is read only."), NULL,
B_STOP_ALERT);
return -1;
}
BPartition *partition;
BDiskDevice device;
BDiskDeviceRoster().GetPartitionWithID(partitionID, &device, &partition);
if (volume.FreeBytes() < partition->Size()) {
FileErrorAlert(B_TRANSLATE("There is not enough free space on target "
"device."), NULL, B_STOP_ALERT);
return -1;
}
_WriteDiskImage(messenger, source, target, targetpath,
BString(B_TRANSLATE("Creating disk image")),
BString(B_TRANSLATE("Disk image successfully created.")));
return 0;
}
int32
MainWindow::WriteDiskImage(void* data)
{
BMessage* msg = (BMessage*)data;
const char* sourcepath;
const char* targetpath;
BMessenger messenger;
msg->FindString("source", &sourcepath);
msg->FindString("target", &targetpath);
msg->FindMessenger("messenger", &messenger);
BFile source(sourcepath, B_READ_ONLY);
BFile target(targetpath, B_READ_WRITE);
if (target.InitCheck() != B_OK) {
FileErrorAlert(B_TRANSLATE("Cannot init target partition."), NULL,
B_STOP_ALERT);
return -1;
}
BPartition *partition;
BDiskDevice device;
BDiskDeviceRoster().GetPartitionForPath(targetpath, &device, &partition);
if (partition->IsReadOnly()) {
FileErrorAlert(B_TRANSLATE("Target partition is read only."), NULL,
B_STOP_ALERT);
return -1;
}
off_t size;
source.GetSize(&size);
if (partition->Size() < size) {
FileErrorAlert(B_TRANSLATE("The target partition is smaller than the "
"image file."), NULL, B_STOP_ALERT);
return -1;
}
_WriteDiskImage(messenger, source, target, targetpath,
BString(B_TRANSLATE("Writing image to disk")),
BString(B_TRANSLATE("Disk image successfully written to the target.")));
return 0;
}
void
MainWindow::_WriteDiskImage(BMessenger messenger, BFile source, BFile target,
const char* targetpath, BString title, BString status)
{
size_t bufsize = 1024 * 1024;
off_t sourcesize;
ssize_t targetbytes = 0;
source.GetSize(&sourcesize);
char maximumBuffer[16], currentBuffer[16];
BString statusprogress;
BNotification progress(B_PROGRESS_NOTIFICATION);
progress.SetGroup(BString(B_TRANSLATE_SYSTEM_NAME("DriveSetup")));
progress.SetMessageID(BString(targetpath));
progress.SetTitle(title);
char* buffer = (char*)malloc(bufsize);
while (true) {
ssize_t bytes = source.Read(buffer, bufsize);
if (bytes > 0) {
target.Write(buffer, (size_t)bytes);
targetbytes += bytes;
statusprogress.SetToFormat("%s: %s / %s", targetpath,
string_for_size(targetbytes, currentBuffer, 16),
string_for_size(sourcesize, maximumBuffer, 16));
progress.SetContent(statusprogress);
progress.SetProgress((float)targetbytes / sourcesize);
progress.Send();
} else
break;
}
free(buffer);
BMessage message(MSG_COMPLETE);
message.AddString("status", status);
message.AddString("jobid", targetpath);
messenger.SendMessage(&message);
}
void
MainWindow::_ScanDrives()
{
fSpaceIDMap.Clear();
int32 diskCount = 0;
ListPopulatorVisitor driveVisitor(fListView, diskCount, fSpaceIDMap);
fDiskDeviceRoster.VisitEachPartition(&driveVisitor);
fDiskView->SetDiskCount(diskCount);
PartitionListRow* previousSelection
= fListView->FindRow(fCurrentPartitionID);
if (previousSelection) {
fListView->SetFocusRow(previousSelection, true);
_UpdateMenus(fCurrentDisk, fCurrentPartitionID,
previousSelection->ParentID());
fDiskView->ForceUpdate();
} else {
_UpdateMenus(NULL, -1, -1);
}
PostMessage(MSG_UPDATE_ZOOM_LIMITS);
}
void
MainWindow::_AdaptToSelectedPartition()
{
partition_id diskID = -1;
partition_id partitionID = -1;
partition_id parentID = -1;
BRow* _selectedRow = fListView->CurrentSelection();
if (_selectedRow) {
BRow* _topLevelRow = _selectedRow;
BRow* parent = NULL;
while (fListView->FindParent(_topLevelRow, &parent, NULL))
_topLevelRow = parent;
PartitionListRow* topLevelRow
= dynamic_cast<PartitionListRow*>(_topLevelRow);
PartitionListRow* selectedRow
= dynamic_cast<PartitionListRow*>(_selectedRow);
if (topLevelRow)
diskID = topLevelRow->ID();
if (selectedRow) {
partitionID = selectedRow->ID();
parentID = selectedRow->ParentID();
}
}
_SetToDiskAndPartition(diskID, partitionID, parentID);
}
void
MainWindow::_SetToDiskAndPartition(partition_id disk, partition_id partition,
partition_id parent)
{
BDiskDevice* oldDisk = NULL;
if (!fCurrentDisk || fCurrentDisk->ID() != disk) {
oldDisk = fCurrentDisk;
fCurrentDisk = NULL;
if (disk >= 0) {
BDiskDevice* newDisk = new BDiskDevice();
status_t status = newDisk->SetTo(disk);
if (status != B_OK) {
printf("error switching disks: %s\n", strerror(status));
delete newDisk;
} else
fCurrentDisk = newDisk;
}
}
fCurrentPartitionID = partition;
fDiskView->SetDisk(fCurrentDisk, fCurrentPartitionID);
_UpdateMenus(fCurrentDisk, fCurrentPartitionID, parent);
delete oldDisk;
}
void
MainWindow::_UpdateMenus(BDiskDevice* disk,
partition_id selectedPartition, partition_id parentID)
{
while (BMenuItem* item = fFormatMenu->RemoveItem((int32)0))
delete item;
while (BMenuItem* item = fDiskInitMenu->RemoveItem((int32)0))
delete item;
while (BMenuItem* item = fFormatContextMenuItem->RemoveItem((int32)0))
delete item;
fCreateMenuItem->SetEnabled(false);
fUnmountMenuItem->SetEnabled(false);
fDiskInitMenu->SetEnabled(false);
fFormatMenu->SetEnabled(false);
fCreateContextMenuItem->SetEnabled(false);
fUnmountContextMenuItem->SetEnabled(false);
fFormatContextMenuItem->SetEnabled(false);
if (disk == NULL) {
fWipeMenuItem->SetEnabled(false);
fEjectMenuItem->SetEnabled(false);
fSurfaceTestMenuItem->SetEnabled(false);
fUnRegisterMenuItem->SetEnabled(false);
fSaveMenuItem->SetEnabled(false);
fWriteMenuItem->SetEnabled(false);
fOpenDiskProbeMenuItem->SetEnabled(false);
fOpenDiskProbeContextMenuItem->SetEnabled(false);
} else {
fWipeMenuItem->SetEnabled(false);
fEjectMenuItem->SetEnabled(disk->IsRemovableMedia());
fSurfaceTestMenuItem->SetEnabled(false);
fUnRegisterMenuItem->SetEnabled(disk->IsFile());
fSaveMenuItem->SetEnabled(true);
fWriteMenuItem->SetEnabled(!disk->IsReadOnly());
BPartition* parentPartition = NULL;
if (selectedPartition <= -2) {
parentPartition = disk->FindDescendant(parentID);
}
if (parentPartition && parentPartition->ContainsPartitioningSystem()) {
fCreateMenuItem->SetEnabled(true);
fCreateContextMenuItem->SetEnabled(true);
}
bool prepared = disk->PrepareModifications() == B_OK;
fFormatMenu->SetEnabled(prepared);
fDeleteMenuItem->SetEnabled(prepared);
fChangeMenuItem->SetEnabled(prepared);
fFormatContextMenuItem->SetEnabled(prepared);
fDeleteContextMenuItem->SetEnabled(prepared);
fChangeContextMenuItem->SetEnabled(prepared);
BPartition* partition = disk->FindDescendant(selectedPartition);
BDiskSystem diskSystem;
fDiskDeviceRoster.RewindDiskSystems();
while (fDiskDeviceRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
if (!diskSystem.SupportsInitializing())
continue;
BMessage* message = new BMessage(MSG_INITIALIZE);
message->AddInt32("parent id", parentID);
message->AddString("disk system", diskSystem.PrettyName());
BString label = diskSystem.PrettyName();
label << B_UTF8_ELLIPSIS;
BMenuItem* item = new BMenuItem(label.String(), message);
item->SetEnabled(partition != NULL
&& partition->CanInitialize(diskSystem.PrettyName()));
if (disk->ID() == selectedPartition
&& !diskSystem.IsFileSystem()) {
fDiskInitMenu->AddItem(item);
} else if (diskSystem.IsFileSystem()) {
fFormatMenu->AddItem(item);
BMessage* message = new BMessage(MSG_INITIALIZE);
message->AddInt32("parent id", parentID);
message->AddString("disk system", diskSystem.PrettyName());
BMenuItem* popUpItem = new BMenuItem(label.String(), message);
popUpItem->SetEnabled(partition != NULL
&& partition->CanInitialize(diskSystem.PrettyName()));
fFormatContextMenuItem->AddItem(popUpItem);
fFormatContextMenuItem->SetTargetForItems(this);
}
}
if (partition != NULL) {
bool writable = !partition->IsReadOnly()
&& partition->Device()->HasMedia();
bool notMountedAndWritable = !partition->IsMounted() && writable;
fFormatMenu->SetEnabled(writable && fFormatMenu->CountItems() > 0);
fDiskInitMenu->SetEnabled(notMountedAndWritable
&& partition->IsDevice()
&& fDiskInitMenu->CountItems() > 0);
fChangeMenuItem->SetEnabled(writable
&& partition->CanEditParameters());
fChangeContextMenuItem->SetEnabled(writable
&& partition->CanEditParameters());
fDeleteMenuItem->SetEnabled(notMountedAndWritable
&& !partition->IsDevice());
fDeleteContextMenuItem->SetEnabled(notMountedAndWritable
&& !partition->IsDevice());
fMountMenuItem->SetEnabled(!partition->IsMounted());
fMountContextMenuItem->SetEnabled(!partition->IsMounted());
fFormatContextMenuItem->SetEnabled(notMountedAndWritable
&& fFormatContextMenuItem->CountItems() > 0);
bool unMountable = false;
if (partition->IsMounted()) {
BVolume volume;
BVolume bootVolume;
if (BVolumeRoster().GetBootVolume(&bootVolume) == B_OK
&& partition->GetVolume(&volume) == B_OK) {
unMountable = volume != bootVolume;
} else
unMountable = true;
}
fUnmountMenuItem->SetEnabled(unMountable);
fUnmountContextMenuItem->SetEnabled(unMountable);
} else {
fDeleteMenuItem->SetEnabled(false);
fChangeMenuItem->SetEnabled(false);
fMountMenuItem->SetEnabled(false);
fFormatMenu->SetEnabled(false);
fDiskInitMenu->SetEnabled(false);
fDeleteContextMenuItem->SetEnabled(false);
fChangeContextMenuItem->SetEnabled(false);
fMountContextMenuItem->SetEnabled(false);
fFormatContextMenuItem->SetEnabled(false);
}
if (prepared)
disk->CancelModifications();
fOpenDiskProbeMenuItem->SetEnabled(true);
fOpenDiskProbeContextMenuItem->SetEnabled(true);
fMountAllMenuItem->SetEnabled(true);
}
if (selectedPartition < 0) {
fDeleteMenuItem->SetEnabled(false);
fChangeMenuItem->SetEnabled(false);
fMountMenuItem->SetEnabled(false);
fDeleteContextMenuItem->SetEnabled(false);
fChangeContextMenuItem->SetEnabled(false);
fMountContextMenuItem->SetEnabled(false);
}
}
void
MainWindow::_DisplayPartitionError(BString _message,
const BPartition* partition, status_t error) const
{
char message[1024];
if (partition && _message.FindFirst("%s") >= 0) {
BString name;
name << "\"" << partition->ContentName() << "\"";
snprintf(message, sizeof(message), _message.String(), name.String());
} else {
_message.ReplaceAll("%s", "");
strlcpy(message, _message.String(), sizeof(message));
}
if (error < B_OK) {
BString helper = message;
const char* errorString
= B_TRANSLATE_COMMENT("Error:", "in any error alert");
snprintf(message, sizeof(message), "%s\n\n%s %s", helper.String(),
errorString, strerror(error));
}
BAlert* alert = new BAlert("error", message, B_TRANSLATE("OK"), NULL, NULL,
B_WIDTH_FROM_WIDEST, error < B_OK ? B_STOP_ALERT : B_INFO_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(NULL);
}
void
MainWindow::_Mount(BDiskDevice* disk, partition_id selectedPartition)
{
if (!disk || selectedPartition < 0) {
_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
"entry from the list."));
return;
}
BPartition* partition = disk->FindDescendant(selectedPartition);
if (!partition) {
_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
"partition by ID."));
return;
}
if (!partition->IsMounted()) {
status_t status = partition->Mount();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Could not mount partition %s."),
partition, status);
} else {
_ScanDrives();
}
} else {
_DisplayPartitionError(
B_TRANSLATE("The partition %s is already mounted."), partition);
}
}
void
MainWindow::_Unmount(BDiskDevice* disk, partition_id selectedPartition)
{
if (!disk || selectedPartition < 0) {
_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
"entry from the list."));
return;
}
BPartition* partition = disk->FindDescendant(selectedPartition);
if (!partition) {
_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
"partition by ID."));
return;
}
if (partition->IsMounted()) {
BPath path;
partition->GetMountPoint(&path);
status_t status = partition->Unmount();
if (status != B_OK) {
BString message = B_TRANSLATE("Could not unmount partition");
message << " \"" << partition->ContentName() << "\":\n\t"
<< strerror(status) << "\n\n"
<< B_TRANSLATE("Should unmounting be forced?\n\n"
"Note: If an application is currently writing to the volume, "
"unmounting it now might result in loss of data.\n");
BAlert* alert = new BAlert(B_TRANSLATE("Force unmount"), message,
B_TRANSLATE("Cancel"), B_TRANSLATE("Force unmount"), NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 1)
status = partition->Unmount(B_FORCE_UNMOUNT);
else
return;
}
if (status != B_OK) {
_DisplayPartitionError(
B_TRANSLATE("Could not unmount partition %s."),
partition, status);
} else {
if (dev_for_path(path.Path()) == dev_for_path("/"))
rmdir(path.Path());
_ScanDrives();
}
} else {
_DisplayPartitionError(
B_TRANSLATE("The partition %s is already unmounted."),
partition);
}
}
void
MainWindow::_MountAll()
{
MountAllVisitor visitor;
fDiskDeviceRoster.VisitEachPartition(&visitor);
}
void
MainWindow::_Initialize(BDiskDevice* disk, partition_id selectedPartition,
const BString& diskSystemName)
{
if (!disk || selectedPartition < 0) {
_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
"entry from the list."));
return;
}
if (disk->IsReadOnly()) {
_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
return;
}
BPartition* partition = disk->FindDescendant(selectedPartition);
if (!partition) {
_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
"partition by ID."));
return;
}
if (partition->IsMounted()) {
if (partition->Unmount() != B_OK) {
_DisplayPartitionError(
B_TRANSLATE("The partition cannot be unmounted."));
return;
}
}
BDiskSystem diskSystem;
fDiskDeviceRoster.RewindDiskSystems();
bool found = false;
while (fDiskDeviceRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
if (diskSystem.SupportsInitializing()) {
if (diskSystemName == diskSystem.PrettyName()) {
found = true;
break;
}
}
}
if (!found) {
_DisplayPartitionError(B_TRANSLATE("Disk system %s not found!"));
return;
}
BString message;
if (diskSystem.IsFileSystem()) {
BString intelExtendedPartition = "Intel Extended Partition";
if (disk->ID() == selectedPartition) {
message = B_TRANSLATE("Are you sure you "
"want to format a raw disk? (Most people initialize the disk "
"with a partitioning system first) You will be asked "
"again before changes are written to the disk.");
} else if (partition->ContentName()
&& strlen(partition->ContentName()) > 0) {
message = B_TRANSLATE("Are you sure you "
"want to format the partition \"%s\"? You will be asked "
"again before changes are written to the disk.");
message.ReplaceFirst("%s", partition->ContentName());
} else if (partition->Type() == intelExtendedPartition) {
message = B_TRANSLATE("Are you sure you "
"want to format the Intel Extended Partition? Any "
"subpartitions it contains will be overwritten if you "
"continue. You will be asked again before changes are "
"written to the disk.");
} else {
message = B_TRANSLATE("Are you sure you "
"want to format the partition? You will be asked again "
"before changes are written to the disk.");
}
} else {
message = B_TRANSLATE("Are you sure you "
"want to initialize the selected disk? All data will be lost. "
"You will be asked again before changes are written to the "
"disk.\n");
}
BAlert* alert = new BAlert("first notice", message.String(),
B_TRANSLATE("Continue"), B_TRANSLATE("Cancel"), NULL,
B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
int32 choice = alert->Go();
if (choice == 1)
return;
ModificationPreparer modificationPreparer(disk);
status_t status = modificationPreparer.ModificationStatus();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
"disk for modifications."), NULL, status);
return;
}
BString name;
BString parameters;
InitParametersPanel* panel = new InitParametersPanel(this, diskSystemName,
partition);
if (panel->Go(name, parameters) != B_OK)
return;
bool supportsName = diskSystem.SupportsContentName();
BString validatedName(name);
status = partition->ValidateInitialize(diskSystem.PrettyName(),
supportsName ? &validatedName : NULL, parameters.String());
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Validation of the given "
"initialization parameters failed."), partition, status);
return;
}
BString previousName = partition->ContentName();
status = partition->Initialize(diskSystem.PrettyName(),
supportsName ? validatedName.String() : NULL, parameters.String());
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Initialization of the partition "
"%s failed. (Nothing has been written to disk.)"), partition,
status);
return;
}
if (partition->CanSetName()
&& partition->ValidateSetName(&validatedName) == B_OK) {
partition->SetName(validatedName.String());
}
if (previousName.Length() > 0) {
if (partition->IsDevice()) {
message = B_TRANSLATE("Are you sure you "
"want to write the changes back to disk now?\n\n"
"All data on the disk \"%s\" will be irretrievably lost if you "
"do so!");
message.ReplaceFirst("%s", previousName.String());
} else {
message = B_TRANSLATE("Are you sure you "
"want to write the changes back to disk now?\n\n"
"All data on the partition \"%s\" will be irretrievably lost if you "
"do so!");
message.ReplaceFirst("%s", previousName.String());
}
} else {
if (partition->IsDevice()) {
message = B_TRANSLATE("Are you sure you "
"want to write the changes back to disk now?\n\n"
"All data on the selected disk will be irretrievably lost if "
"you do so!");
} else {
message = B_TRANSLATE("Are you sure you "
"want to write the changes back to disk now?\n\n"
"All data on the selected partition will be irretrievably lost "
"if you do so!");
}
}
alert = new BAlert("final notice", message.String(),
B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
choice = alert->Go();
if (choice == 1)
return;
status = modificationPreparer.CommitModifications();
partition = disk->FindDescendant(selectedPartition);
if (status == B_OK) {
if (diskSystem.IsFileSystem()) {
_DisplayPartitionError(B_TRANSLATE("The partition %s has been "
"successfully formatted.\n"), partition);
} else {
_DisplayPartitionError(B_TRANSLATE("The disk has been "
"successfully initialized.\n"), partition);
}
} else {
if (diskSystem.IsFileSystem()) {
_DisplayPartitionError(B_TRANSLATE("Failed to format the "
"partition %s!\n"), partition, status);
} else {
_DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
"disk %s!\n"), partition, status);
}
}
_ScanDrives();
}
void
MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition)
{
if (!disk || selectedPartition > -2) {
_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
"is not empty."));
return;
}
if (disk->IsReadOnly()) {
_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
return;
}
PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>(
fListView->CurrentSelection());
if (!currentSelection) {
_DisplayPartitionError(B_TRANSLATE("There was an error acquiring the "
"partition row."));
return;
}
BPartition* parent = disk->FindDescendant(currentSelection->ParentID());
if (!parent) {
_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
"does not have a parent partition."));
return;
}
if (!parent->ContainsPartitioningSystem()) {
_DisplayPartitionError(B_TRANSLATE("The selected partition does not "
"contain a partitioning system."));
return;
}
ModificationPreparer modificationPreparer(disk);
status_t status = modificationPreparer.ModificationStatus();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
"disk for modifications."), NULL, status);
return;
}
BPartitioningInfo partitioningInfo;
status_t error = parent->GetPartitioningInfo(&partitioningInfo);
if (error != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Could not acquire partitioning "
"information."));
return;
}
int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
if (spacesCount == 0) {
_DisplayPartitionError(B_TRANSLATE("There's no space on the partition "
"where a child partition could be created."));
return;
}
BString name, type, parameters;
off_t offset = currentSelection->Offset();
off_t size = currentSelection->Size();
CreateParametersPanel* panel = new CreateParametersPanel(this, parent,
offset, size);
status = panel->Go(offset, size, name, type, parameters);
if (status != B_OK) {
if (status != B_CANCELED) {
_DisplayPartitionError(B_TRANSLATE("The panel could not return "
"successfully."), NULL, status);
}
return;
}
status = parent->ValidateCreateChild(&offset, &size, type.String(),
&name, parameters.String());
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Validation of the given creation "
"parameters failed."), NULL, status);
return;
}
BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
"want to write the changes back to disk now?\n\n"
"All data on the partition will be irretrievably lost if you do "
"so!"), B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
int32 choice = alert->Go();
if (choice == 1)
return;
status = parent->CreateChild(offset, size, type.String(), name.String(),
parameters.String());
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Creation of the partition has "
"failed."), NULL, status);
return;
}
status = modificationPreparer.CommitModifications();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Failed to create the "
"partition. No changes have been written to disk."), NULL, status);
return;
}
bool updated;
status = disk->Update(&updated);
_ScanDrives();
fDiskView->ForceUpdate();
}
void
MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition)
{
if (!disk || selectedPartition < 0) {
_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
"entry from the list."));
return;
}
if (disk->IsReadOnly()) {
_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
return;
}
BPartition* partition = disk->FindDescendant(selectedPartition);
if (!partition) {
_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
"partition by ID."));
return;
}
BPartition* parent = partition->Parent();
if (!parent) {
_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
"does not have a parent partition."));
return;
}
ModificationPreparer modificationPreparer(disk);
status_t status = modificationPreparer.ModificationStatus();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
"disk for modifications."), NULL, status);
return;
}
if (!parent->CanDeleteChild(partition->Index())) {
_DisplayPartitionError(
B_TRANSLATE("Cannot delete the selected partition."));
return;
}
BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
"want to delete the selected partition?\n\n"
"All data on the partition will be irretrievably lost if you "
"do so!"), B_TRANSLATE("Delete partition"), B_TRANSLATE("Cancel"), NULL,
B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
int32 choice = alert->Go();
if (choice == 1)
return;
status = parent->DeleteChild(partition->Index());
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Could not delete the selected "
"partition."), NULL, status);
return;
}
status = modificationPreparer.CommitModifications();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Failed to delete the partition. "
"No changes have been written to disk."), NULL, status);
return;
}
_ScanDrives();
fDiskView->ForceUpdate();
}
void
MainWindow::_ChangeParameters(BDiskDevice* disk, partition_id selectedPartition)
{
if (disk == NULL || selectedPartition < 0) {
_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
"entry from the list."));
return;
}
if (disk->IsReadOnly()) {
_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
return;
}
BPartition* partition = disk->FindDescendant(selectedPartition);
if (partition == NULL) {
_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
"partition by ID."));
return;
}
ModificationPreparer modificationPreparer(disk);
status_t status = modificationPreparer.ModificationStatus();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
"disk for modifications."), NULL, status);
return;
}
ChangeParametersPanel* panel = new ChangeParametersPanel(this, partition);
BString name, type, parameters;
status = panel->Go(name, type, parameters);
if (status != B_OK) {
if (status != B_CANCELED) {
_DisplayPartitionError(B_TRANSLATE("The panel experienced a "
"problem!"), NULL, status);
}
return;
}
if (partition->CanSetType())
status = partition->ValidateSetType(type.String());
if (status == B_OK && partition->CanSetName())
status = partition->ValidateSetName(&name);
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Validation of the given parameters "
"failed."));
return;
}
BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
"want to change parameters of the selected partition?\n\n"
"The partition may no longer be recognized by other operating systems "
"anymore!"), B_TRANSLATE("Change parameters"), B_TRANSLATE("Cancel"),
NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
int32 choice = alert->Go();
if (choice == 1)
return;
if (partition->CanSetType())
status = partition->SetType(type.String());
if (status == B_OK && partition->CanSetName())
status = partition->SetName(name.String());
if (status == B_OK && partition->CanEditParameters())
status = partition->SetParameters(parameters.String());
if (status != B_OK) {
_DisplayPartitionError(
B_TRANSLATE("Could not change the parameters of the selected "
"partition."), NULL, status);
return;
}
status = modificationPreparer.CommitModifications();
if (status != B_OK) {
_DisplayPartitionError(B_TRANSLATE("Failed to change the parameters "
"of the partition. No changes have been written to disk."), NULL,
status);
return;
}
_ScanDrives();
fDiskView->ForceUpdate();
}
float
MainWindow::_ColumnListViewHeight(BColumnListView* list, BRow* currentRow)
{
float height = 0;
int32 rows = list->CountRows(currentRow);
for (int32 i = 0; i < rows; i++) {
BRow* row = list->RowAt(i, currentRow);
height += row->Height() + 1;
if (row->IsExpanded() && list->CountRows(row) > 0)
height += _ColumnListViewHeight(list, row);
}
return height;
}
void
MainWindow::_UpdateWindowZoomLimits()
{
float maxHeight = 0;
int32 numColumns = fListView->CountColumns();
BRow* parentRow = fListView->RowAt(0, NULL);
BColumn* column = NULL;
maxHeight += _ColumnListViewHeight(fListView, NULL);
float maxWidth = fListView->LatchWidth();
for (int32 i = 0; i < numColumns; i++) {
column = fListView->ColumnAt(i);
maxWidth += column->Width();
}
maxHeight += B_H_SCROLL_BAR_HEIGHT;
maxHeight += 1.5 * parentRow->Height();
maxHeight += fDiskView->Bounds().Height();
maxHeight += fMenuBar->Bounds().Height();
maxWidth += 1.5 * B_V_SCROLL_BAR_WIDTH;
SetZoomLimits(maxWidth, maxHeight);
}