1 /* 2 * Copyright 2011, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "DrivesPage.h" 8 9 #include <Catalog.h> 10 #include <ControlLook.h> 11 #include <DiskDeviceRoster.h> 12 #include <DiskDevice.h> 13 #include <LayoutBuilder.h> 14 #include <ListView.h> 15 #include <Path.h> 16 #include <ScrollView.h> 17 #include <TextView.h> 18 #include <Bitmap.h> 19 20 #include <StringForSize.h> 21 22 #include "BootDrive.h" 23 #include "WizardView.h" 24 25 26 #undef B_TRANSLATION_CONTEXT 27 #define B_TRANSLATION_CONTEXT "DrivesPage" 28 29 30 const uint32 kMsgSelectionChanged = 'slch'; 31 32 33 class DriveItem : public BListItem { 34 public: 35 DriveItem(const BDiskDevice& device, 36 const BootMenuList& menus); 37 virtual ~DriveItem(); 38 39 bool IsInstalled() const; 40 bool CanBeInstalled() const; 41 bool IsBootDrive() const; 42 const char* Path() const { return fPath.Path(); } 43 44 BootDrive* Drive() { return fDrive; } 45 46 protected: 47 virtual void DrawItem(BView* owner, BRect frame, 48 bool complete = false); 49 virtual void Update(BView* owner, const BFont* font); 50 51 private: 52 BootDrive* fDrive; 53 BBitmap* fIcon; 54 BString fName; 55 BPath fPath; 56 BString fSize; 57 float fBaselineOffset; 58 float fSecondBaselineOffset; 59 float fSizeWidth; 60 status_t fCanBeInstalled; 61 bool fIsInstalled; 62 }; 63 64 65 DriveItem::DriveItem(const BDiskDevice& device, const BootMenuList& menus) 66 : 67 fBaselineOffset(0), 68 fSizeWidth(0) 69 { 70 device.GetPath(&fPath); 71 if (device.Name() != NULL && device.Name()[0]) 72 fName = device.Name(); 73 else if (strstr(fPath.Path(), "usb") != NULL) 74 fName = B_TRANSLATE_COMMENT("USB Drive", "Default disk name"); 75 else 76 fName = B_TRANSLATE_COMMENT("Hard Drive", "Default disk name"); 77 78 fIcon = new BBitmap(BRect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1), 79 B_RGBA32); 80 if (device.GetIcon(fIcon, B_LARGE_ICON) != B_OK) 81 memset(fIcon->Bits(), 0, fIcon->BitsLength()); 82 83 fDrive = new BootDrive(fPath.Path()); 84 85 fIsInstalled = fDrive->InstalledMenu(menus) != NULL; 86 fCanBeInstalled = fDrive->CanMenuBeInstalled(menus); 87 88 char buffer[256]; 89 fSize = string_for_size(device.Size(), buffer, sizeof(buffer)); 90 } 91 92 93 DriveItem::~DriveItem() 94 { 95 delete fDrive; 96 delete fIcon; 97 } 98 99 100 bool 101 DriveItem::IsInstalled() const 102 { 103 return fIsInstalled; 104 } 105 106 107 bool 108 DriveItem::CanBeInstalled() const 109 { 110 return fCanBeInstalled == B_OK; 111 } 112 113 114 bool 115 DriveItem::IsBootDrive() const 116 { 117 return fDrive->IsBootDrive(); 118 } 119 120 121 void 122 DriveItem::DrawItem(BView* owner, BRect frame, bool complete) 123 { 124 owner->PushState(); 125 owner->SetDrawingMode(B_OP_ALPHA); 126 if (IsSelected() || complete) { 127 if (IsSelected()) { 128 owner->SetHighColor(tint_color(owner->LowColor(), B_DARKEN_2_TINT)); 129 owner->SetLowColor(owner->HighColor()); 130 } else 131 owner->SetHighColor(owner->LowColor()); 132 133 owner->FillRect(frame); 134 } 135 136 rgb_color black = {0, 0, 0, 255}; 137 138 if (!IsEnabled()) 139 owner->SetHighColor(tint_color(black, B_LIGHTEN_2_TINT)); 140 else 141 owner->SetHighColor(black); 142 143 // icon 144 owner->MovePenTo(frame.left + 4, frame.top + 1); 145 owner->DrawBitmap(fIcon); 146 147 // device 148 owner->MovePenTo(frame.left + 8 + fIcon->Bounds().Width(), frame.top + fSecondBaselineOffset); 149 owner->DrawString(fPath.Path()); 150 151 // size 152 owner->MovePenTo(frame.right - 4 - fSizeWidth, frame.top + fBaselineOffset); 153 owner->DrawString(fSize.String()); 154 155 // name 156 BFont boldFont; 157 owner->GetFont(&boldFont); 158 boldFont.SetFace(B_BOLD_FACE); 159 owner->SetFont(&boldFont); 160 161 owner->MovePenTo(frame.left + 8 + fIcon->Bounds().Width(), frame.top + fBaselineOffset); 162 owner->DrawString(fName.String()); 163 164 if (fCanBeInstalled != B_OK) { 165 owner->SetHighColor(140, 0, 0); 166 owner->MovePenBy(fBaselineOffset, 0); 167 const char* message; 168 switch (fCanBeInstalled) { 169 case B_PARTITION_TOO_SMALL: 170 message = B_TRANSLATE_COMMENT("No space available!", 171 "Cannot install"); 172 break; 173 case B_ENTRY_NOT_FOUND: 174 message = B_TRANSLATE_COMMENT("Incompatible format!", 175 "Cannot install"); 176 break; 177 default: 178 message = B_TRANSLATE_COMMENT("Cannot access!", 179 "Cannot install"); 180 break; 181 } 182 owner->DrawString(message); 183 } 184 185 owner->PopState(); 186 } 187 188 189 void 190 DriveItem::Update(BView* owner, const BFont* font) 191 { 192 fSizeWidth = font->StringWidth(fSize.String()); 193 194 BFont boldFont(font); 195 boldFont.SetFace(B_BOLD_FACE); 196 float width = 8 + boldFont.StringWidth(fPath.Path()) 197 + be_control_look->DefaultItemSpacing() + fSizeWidth; 198 float pathWidth = font->StringWidth(fPath.Path()); 199 if (width < pathWidth) 200 width = pathWidth; 201 202 SetWidth(width); 203 204 font_height fheight; 205 font->GetHeight(&fheight); 206 207 float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent) 208 + ceilf(fheight.leading); 209 210 fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2); 211 fSecondBaselineOffset = fBaselineOffset + lineHeight; 212 213 SetHeight(2 * lineHeight + 4); 214 } 215 216 217 // #pragma mark - 218 219 220 DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus, 221 BMessage* settings, const char* name) 222 : 223 WizardPageView(settings, name), 224 fWizardView(wizardView), 225 fHasInstallableItems(false) 226 { 227 BString text; 228 text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n" 229 << B_TRANSLATE("Please select the drive you want the boot manager to " 230 "be installed to or uninstalled from."); 231 BTextView* description = CreateDescription("description", text); 232 MakeHeading(description); 233 234 fDrivesView = new BListView("drives"); 235 fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged)); 236 237 BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0, 238 false, true); 239 240 SetLayout(new BGroupLayout(B_VERTICAL)); 241 242 BLayoutBuilder::Group<>((BGroupLayout*)GetLayout()) 243 .Add(description, 0.5) 244 .Add(scrollView, 1); 245 246 _UpdateWizardButtons(NULL); 247 _FillDrivesView(menus); 248 } 249 250 251 DrivesPage::~DrivesPage() 252 { 253 } 254 255 256 void 257 DrivesPage::PageCompleted() 258 { 259 DriveItem* item = _SelectedDriveItem(); 260 261 if (fSettings->ReplaceString("disk", item->Path()) != B_OK) 262 fSettings->AddString("disk", item->Path()); 263 } 264 265 266 void 267 DrivesPage::AttachedToWindow() 268 { 269 fDrivesView->SetTarget(this); 270 } 271 272 273 void 274 DrivesPage::MessageReceived(BMessage* message) 275 { 276 switch (message->what) { 277 case kMsgSelectionChanged: 278 { 279 _UpdateWizardButtons(_SelectedDriveItem()); 280 break; 281 } 282 283 default: 284 WizardPageView::MessageReceived(message); 285 break; 286 } 287 } 288 289 290 /*! Builds the list view items, and adds them to fDriveView. 291 Sets the fHasInstallableItems member to indicate if there 292 are any possible install targets. Automatically 293 selects the boot drive. 294 */ 295 void 296 DrivesPage::_FillDrivesView(const BootMenuList& menus) 297 { 298 const char* selected = fSettings->FindString("disk"); 299 300 BDiskDeviceRoster roster; 301 BDiskDevice device; 302 while (roster.GetNextDevice(&device) == B_OK) { 303 if (device.HasMedia() && !device.IsReadOnly()) { 304 DriveItem* item = new DriveItem(device, menus); 305 if (item->CanBeInstalled()) 306 fHasInstallableItems = true; 307 fDrivesView->AddItem(item); 308 309 if ((selected == NULL && item->IsBootDrive()) 310 || (selected != NULL && !strcmp(item->Path(), selected))) { 311 fDrivesView->Select(fDrivesView->CountItems() - 1); 312 _UpdateWizardButtons(item); 313 } 314 } 315 } 316 } 317 318 319 DriveItem* 320 DrivesPage::_SelectedDriveItem() 321 { 322 return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection()); 323 } 324 325 326 void 327 DrivesPage::_UpdateWizardButtons(DriveItem* item) 328 { 329 fWizardView->SetPreviousButtonHidden(!fHasInstallableItems); 330 fWizardView->SetPreviousButtonLabel( 331 B_TRANSLATE_COMMENT("Uninstall", "Button")); 332 if (item == NULL) { 333 fWizardView->SetPreviousButtonEnabled(false); 334 fWizardView->SetNextButtonEnabled(false); 335 } else { 336 fWizardView->SetPreviousButtonEnabled( 337 item->CanBeInstalled() && item->IsInstalled()); 338 fWizardView->SetNextButtonEnabled(item->CanBeInstalled()); 339 340 fWizardView->SetNextButtonLabel( 341 item->IsInstalled() && item->CanBeInstalled() 342 ? B_TRANSLATE_COMMENT("Update", "Button") 343 : B_TRANSLATE_COMMENT("Install", "Button")); 344 } 345 346 } 347