haiku/src/apps/bootmanager/DrivesPage.cpp

384 lines
8.9 KiB
C++

/*
* Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "DrivesPage.h"
#include <Catalog.h>
#include <ControlLook.h>
#include <DiskDeviceRoster.h>
#include <DiskDevice.h>
#include <LayoutBuilder.h>
#include <ListView.h>
#include <Path.h>
#include <ScrollView.h>
#include <TextView.h>
#include <Bitmap.h>
#include <StringForSize.h>
#include "BootDrive.h"
#include "WizardView.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "DrivesPage"
const uint32 kMsgSelectionChanged = 'slch';
class DriveItem : public BListItem {
public:
DriveItem(const BDiskDevice& device,
const BootMenuList& menus);
virtual ~DriveItem();
bool IsInstalled() const;
bool CanBeInstalled() const;
bool IsBootDrive() const;
const char* Path() const { return fPath.Path(); }
BootDrive* Drive() { return fDrive; }
protected:
virtual void DrawItem(BView* owner, BRect frame,
bool complete = false);
virtual void Update(BView* owner, const BFont* font);
private:
BootDrive* fDrive;
BBitmap* fIcon;
BString fName;
BPath fPath;
BString fSize;
float fBaselineOffset;
float fSecondBaselineOffset;
float fSizeWidth;
status_t fCanBeInstalled;
bool fIsInstalled;
};
DriveItem::DriveItem(const BDiskDevice& device, const BootMenuList& menus)
:
fBaselineOffset(0),
fSizeWidth(0)
{
device.GetPath(&fPath);
if (device.Name() != NULL && device.Name()[0])
fName = device.Name();
else if (strstr(fPath.Path(), "usb") != NULL)
fName = B_TRANSLATE_COMMENT("USB Drive", "Default disk name");
else
fName = B_TRANSLATE_COMMENT("Hard Drive", "Default disk name");
fIcon = new BBitmap(BRect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1),
B_RGBA32);
if (device.GetIcon(fIcon, B_LARGE_ICON) != B_OK)
memset(fIcon->Bits(), 0, fIcon->BitsLength());
fDrive = new BootDrive(fPath.Path());
fIsInstalled = fDrive->InstalledMenu(menus) != NULL;
fCanBeInstalled = fDrive->CanMenuBeInstalled(menus);
char buffer[256];
fSize = string_for_size(device.Size(), buffer, sizeof(buffer));
}
DriveItem::~DriveItem()
{
delete fDrive;
delete fIcon;
}
bool
DriveItem::IsInstalled() const
{
return fIsInstalled;
}
bool
DriveItem::CanBeInstalled() const
{
return fCanBeInstalled == B_OK;
}
bool
DriveItem::IsBootDrive() const
{
return fDrive->IsBootDrive();
}
void
DriveItem::DrawItem(BView* owner, BRect frame, bool complete)
{
owner->PushState();
owner->SetDrawingMode(B_OP_ALPHA);
if (IsSelected() || complete) {
if (IsSelected()) {
owner->SetHighColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR));
owner->SetLowColor(owner->HighColor());
} else
owner->SetHighColor(owner->LowColor());
owner->FillRect(frame);
}
if (!IsEnabled()) {
rgb_color textColor;
if (IsSelected())
textColor = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR);
else
textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
if (textColor.red + textColor.green + textColor.blue > 128 * 3)
owner->SetHighColor(tint_color(textColor, B_DARKEN_1_TINT));
else
owner->SetHighColor(tint_color(textColor, B_LIGHTEN_1_TINT));
} else {
if (IsSelected())
owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
else
owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
}
// icon
owner->MovePenTo(frame.left + 4, frame.top + 1);
owner->DrawBitmap(fIcon);
// device
owner->MovePenTo(frame.left + 8 + fIcon->Bounds().Width(),
frame.top + fSecondBaselineOffset);
owner->DrawString(fPath.Path());
// name
BFont boldFont;
BFont ownerFont;
owner->GetFont(&ownerFont);
owner->GetFont(&boldFont);
boldFont.SetFace(B_BOLD_FACE);
owner->SetFont(&boldFont);
BPoint namePosition(frame.left + 8 + fIcon->Bounds().Width(),
frame.top + fBaselineOffset);
owner->MovePenTo(namePosition);
owner->DrawString(fName.String());
float nameWidth = owner->StringWidth(fName.String());
float messageWidth = frame.right - 4 - fSizeWidth
- (frame.left + 8 + fIcon->Bounds().Width()) - nameWidth
- fBaselineOffset * 2;
if (fCanBeInstalled != B_OK) {
rgb_color highColor = owner->HighColor();
owner->SetHighColor(ui_color(B_FAILURE_COLOR));
owner->MovePenBy(fBaselineOffset, 0);
const char* message;
switch (fCanBeInstalled) {
case B_PARTITION_TOO_SMALL:
message = B_TRANSLATE_COMMENT("No space available!",
"Cannot install");
break;
case B_ENTRY_NOT_FOUND:
message = B_TRANSLATE_COMMENT("Incompatible format!",
"Cannot install");
break;
case B_READ_ONLY_DEVICE:
message = B_TRANSLATE_COMMENT("Read only!",
"Cannot install");
break;
default:
message = B_TRANSLATE_COMMENT("Cannot access!",
"Cannot install");
break;
}
BString truncatedMessage = message;
owner->TruncateString(&truncatedMessage, B_TRUNCATE_END, messageWidth);
owner->DrawString(truncatedMessage);
owner->SetHighColor(highColor);
}
owner->SetFont(&ownerFont);
// size
BPoint sizePosition(frame.right - 4 - fSizeWidth,
frame.top + fBaselineOffset);
if (sizePosition.x > namePosition.x + nameWidth) {
owner->MovePenTo(sizePosition);
owner->DrawString(fSize.String());
}
owner->PopState();
}
void
DriveItem::Update(BView* owner, const BFont* font)
{
fSizeWidth = font->StringWidth(fSize.String());
BFont boldFont(font);
boldFont.SetFace(B_BOLD_FACE);
float width = 8 + boldFont.StringWidth(fPath.Path())
+ be_control_look->DefaultItemSpacing() + fSizeWidth;
float pathWidth = font->StringWidth(fPath.Path());
if (width < pathWidth)
width = pathWidth;
SetWidth(width);
font_height fheight;
font->GetHeight(&fheight);
float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent)
+ ceilf(fheight.leading);
fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2);
fSecondBaselineOffset = fBaselineOffset + lineHeight;
SetHeight(2 * lineHeight + 4);
}
// #pragma mark -
DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus,
BMessage* settings, const char* name)
:
WizardPageView(settings, name),
fWizardView(wizardView),
fHasInstallableItems(false)
{
BString text;
text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n"
<< B_TRANSLATE("Please select the drive you want the boot manager to "
"be installed to or uninstalled from.");
BTextView* description = CreateDescription("description", text);
MakeHeading(description);
fDrivesView = new BListView("drives", B_SINGLE_SELECTION_LIST,
B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged));
BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0,
false, true);
SetLayout(new BGroupLayout(B_VERTICAL));
BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
.Add(description, 0.5)
.Add(scrollView, 1);
_UpdateWizardButtons(NULL);
_FillDrivesView(menus);
}
DrivesPage::~DrivesPage()
{
}
void
DrivesPage::PageCompleted()
{
DriveItem* item = _SelectedDriveItem();
if (fSettings->ReplaceString("disk", item->Path()) != B_OK)
fSettings->AddString("disk", item->Path());
}
void
DrivesPage::AttachedToWindow()
{
fDrivesView->SetTarget(this);
}
void
DrivesPage::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgSelectionChanged:
{
_UpdateWizardButtons(_SelectedDriveItem());
break;
}
default:
WizardPageView::MessageReceived(message);
break;
}
}
/*! Builds the list view items, and adds them to fDriveView.
Sets the fHasInstallableItems member to indicate if there
are any possible install targets. Automatically
selects the boot drive.
*/
void
DrivesPage::_FillDrivesView(const BootMenuList& menus)
{
const char* selected = fSettings->FindString("disk");
BDiskDeviceRoster roster;
BDiskDevice device;
while (roster.GetNextDevice(&device) == B_OK) {
if (device.HasMedia() && !device.IsReadOnly()) {
DriveItem* item = new DriveItem(device, menus);
if (item->CanBeInstalled())
fHasInstallableItems = true;
fDrivesView->AddItem(item);
if ((selected == NULL && item->IsBootDrive())
|| (selected != NULL && !strcmp(item->Path(), selected))) {
fDrivesView->Select(fDrivesView->CountItems() - 1);
_UpdateWizardButtons(item);
}
}
}
}
DriveItem*
DrivesPage::_SelectedDriveItem()
{
return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection());
}
void
DrivesPage::_UpdateWizardButtons(DriveItem* item)
{
fWizardView->SetPreviousButtonHidden(!fHasInstallableItems);
fWizardView->SetPreviousButtonLabel(
B_TRANSLATE_COMMENT("Uninstall", "Button"));
if (item == NULL) {
fWizardView->SetPreviousButtonEnabled(false);
fWizardView->SetNextButtonEnabled(false);
} else {
fWizardView->SetPreviousButtonEnabled(
item->CanBeInstalled() && item->IsInstalled());
fWizardView->SetNextButtonEnabled(item->CanBeInstalled());
fWizardView->SetNextButtonLabel(
item->IsInstalled() && item->CanBeInstalled()
? B_TRANSLATE_COMMENT("Update", "Button")
: B_TRANSLATE_COMMENT("Install", "Button"));
}
}