/* * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #include "DrivesPage.h" #include #include #include #include #include #include #include #include #include #include #include #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(BPoint(0, 0), be_control_look->ComposeIconSize(B_LARGE_ICON)), 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) .Add(scrollView, 10.0); _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")); } }