haiku/src/apps/drivesetup/MainWindow.cpp

1476 lines
39 KiB
C++

/*
* 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 <stdio.h>
#include <string.h>
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <ColumnListView.h>
#include <ColumnTypes.h>
#include <Debug.h>
#include <DiskDevice.h>
#include <DiskDeviceVisitor.h>
#include <DiskDeviceTypes.h>
#include <DiskSystem.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_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();
// start with an empty list
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++;
// if we don't prepare the device for modifications,
// we cannot get information about available empty
// regions on the device or child partitions
device->PrepareModifications();
_AddPartition(device);
return false; // Don't stop yet!
}
virtual bool Visit(BPartition* partition, int32 level)
{
_AddPartition(partition);
return false; // Don't stop yet!
}
private:
void _AddPartition(BPartition* partition) const
{
// add the partition itself
fPartitionList->AddPartition(partition);
// add any available space on it
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++) {
// TODO: remove again once Disk Device API is fixed
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; // Don't stop yet!
}
virtual bool Visit(BPartition* partition, int32 level)
{
partition->Mount();
return false; // Don't stop yet!
}
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;
};
// #pragma mark -
MainWindow::MainWindow()
:
BWindow(BRect(50, 50, 600, 500), B_TRANSLATE_SYSTEM_NAME("DriveSetup"),
B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS),
fCurrentDisk(NULL),
fCurrentPartitionID(-1),
fSpaceIDMap()
{
fMenuBar = new BMenuBar(Bounds(), "root menu");
// create all the menu items
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);
// Disk menu
fDiskMenu = new BMenu(B_TRANSLATE("Disk"));
// fDiskMenu->AddItem(fWipeMenuItem);
fDiskInitMenu = new BMenu(B_TRANSLATE("Initialize"));
fDiskMenu->AddItem(fDiskInitMenu);
fDiskMenu->AddSeparatorItem();
fDiskMenu->AddItem(fEjectMenuItem);
// fDiskMenu->AddItem(fSurfaceTestMenuItem);
fDiskMenu->AddItem(fRescanMenuItem);
fMenuBar->AddItem(fDiskMenu);
// Parition menu
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);
AddChild(fMenuBar);
// Partition / Drives context menu
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);
// add DiskView
BRect r(Bounds());
r.top = fMenuBar->Frame().bottom + 1;
r.bottom = floorf(r.top + r.Height() * 0.33);
fDiskView = new DiskView(r, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
fSpaceIDMap);
AddChild(fDiskView);
// add PartitionListView
r.top = r.bottom + 2;
r.bottom = Bounds().bottom;
r.InsetBy(-1, -1);
fListView = new PartitionListView(r, B_FOLLOW_ALL);
AddChild(fListView);
// configure PartitionListView
fListView->SetSelectionMode(B_SINGLE_SELECTION_LIST);
fListView->SetSelectionMessage(new BMessage(MSG_PARTITION_ROW_SELECTED));
fListView->SetTarget(this);
fListView->MakeFocus(true);
status_t status = fDiskDeviceRoster.StartWatching(BMessenger(this));
if (status != B_OK) {
fprintf(stderr, "Failed to start watching for device changes: %s\n",
strerror(status));
}
// visit all disks in the system and show their contents
_ScanDrives();
if (!be_roster->IsRunning(kDeskbarSignature))
SetFlags(Flags() | B_NOT_MINIMIZABLE);
}
MainWindow::~MainWindow()
{
BDiskDeviceRoster().StopWatching(this);
delete fCurrentDisk;
delete fContextMenu;
}
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:
// TODO: completely untested, especially interesting
// if partition list behaves when partitions disappear
if (fCurrentDisk) {
// TODO: only if no partitions are mounted anymore?
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;
// TODO: this could probably be done better!
case B_DEVICE_UPDATE:
printf("B_DEVICE_UPDATE\n");
case MSG_RESCAN:
_ScanDrives();
break;
case MSG_PARTITION_ROW_SELECTED: {
// selection of partitions via list view
_AdaptToSelectedPartition();
BPoint where;
uint32 buttons;
fListView->GetMouse(&where, &buttons);
where.x += 2; // to prevent occasional select
if (buttons & B_SECONDARY_MOUSE_BUTTON)
fContextMenu->Go(fListView->ConvertToScreen(where),
true, false, true);
break;
}
case MSG_SELECTED_PARTITION_ID: {
// selection of partitions via disk view
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; // to prevent occasional select
if (buttons & B_SECONDARY_MOUSE_BUTTON)
fContextMenu->Go(fListView->ConvertToScreen(where),
true, false, true);
break;
}
case MSG_UPDATE_ZOOM_LIMITS:
_UpdateWindowZoomLimits();
break;
default:
BWindow::MessageReceived(message);
break;
}
}
bool
MainWindow::QuitRequested()
{
// TODO: ask about any unsaved changes
be_app->PostMessage(B_QUIT_REQUESTED);
Hide();
return false;
}
// #pragma mark -
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();
// Adjust window size for convenience
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();
}
// #pragma mark -
void
MainWindow::_ScanDrives()
{
fSpaceIDMap.Clear();
int32 diskCount = 0;
ListPopulatorVisitor driveVisitor(fListView, diskCount, fSpaceIDMap);
fDiskDeviceRoster.VisitEachPartition(&driveVisitor);
fDiskView->SetDiskCount(diskCount);
// restore selection
PartitionListRow* previousSelection
= fListView->FindRow(fCurrentPartitionID);
if (previousSelection) {
fListView->AddToSelection(previousSelection);
_UpdateMenus(fCurrentDisk, fCurrentPartitionID,
previousSelection->ParentID());
fDiskView->ForceUpdate();
} else {
_UpdateMenus(NULL, -1, -1);
}
PostMessage(MSG_UPDATE_ZOOM_LIMITS);
}
// #pragma mark -
void
MainWindow::_AdaptToSelectedPartition()
{
partition_id diskID = -1;
partition_id partitionID = -1;
partition_id parentID = -1;
BRow* _selectedRow = fListView->CurrentSelection();
if (_selectedRow) {
// go up to top level row
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)
{
//printf("MainWindow::_SetToDiskAndPartition(disk: %ld, partition: %ld, "
// "parent: %ld)\n", disk, partition, 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);
fOpenDiskProbeMenuItem->SetEnabled(false);
fOpenDiskProbeContextMenuItem->SetEnabled(false);
} else {
// fWipeMenuItem->SetEnabled(true);
fWipeMenuItem->SetEnabled(false);
fEjectMenuItem->SetEnabled(disk->IsRemovableMedia());
// fSurfaceTestMenuItem->SetEnabled(true);
fSurfaceTestMenuItem->SetEnabled(false);
// Create menu and items
BPartition* parentPartition = NULL;
if (selectedPartition <= -2) {
// a partitionable space item is selected
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);
// TODO: Very unintuitive that we have to use PrettyName (vs Name)
item->SetEnabled(partition != NULL
&& partition->CanInitialize(diskSystem.PrettyName()));
if (disk->ID() == selectedPartition
&& !diskSystem.IsFileSystem()) {
// Disk is selected, and DiskSystem is a partition map
fDiskInitMenu->AddItem(item);
} else if (diskSystem.IsFileSystem()) {
// Otherwise a filesystem
fFormatMenu->AddItem(item);
// Context menu
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);
}
}
// Mount items
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()) {
// see if this partition is the boot volume
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);
}
// #pragma mark -
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 {
// successful mount, adapt to the changes
_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());
// successful unmount, adapt to the changes
_ScanDrives();
}
} else {
_DisplayPartitionError(
B_TRANSLATE("The partition %s is already unmounted."),
partition);
}
}
void
MainWindow::_MountAll()
{
MountAllVisitor visitor;
fDiskDeviceRoster.VisitEachPartition(&visitor);
}
// #pragma mark -
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) {
// Probably it's the system partition
_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;
}
// Also set the partition name in the partition table if supported
if (partition->CanSetName()
&& partition->ValidateSetName(&validatedName) == B_OK) {
partition->SetName(validatedName.String());
}
// everything looks fine, we are ready to actually write the changes
// to disk
// Warn the user one more time...
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;
// commit
status = modificationPreparer.CommitModifications();
// The partition pointer is toast now! Use the partition ID to
// retrieve it again.
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;
}
// get partitioning info
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;
}
// Warn the user one more time...
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;
}
// commit
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;
}
// The disk layout has changed, update disk information
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;
}
// Warn the user one more time...
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;
}
// Warn the user one more time...
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(); // the label row
maxHeight += fDiskView->Bounds().Height();
maxHeight += fMenuBar->Bounds().Height();
maxWidth += 1.5 * B_V_SCROLL_BAR_WIDTH; // scroll bar & borders
SetZoomLimits(maxWidth, maxHeight);
}