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(BPoint(0, 0), be_control_look->ComposeIconSize(B_LARGE_ICON)), 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 127 if (IsSelected() || complete) { 128 if (IsSelected()) { 129 owner->SetHighColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR)); 130 owner->SetLowColor(owner->HighColor()); 131 } else 132 owner->SetHighColor(owner->LowColor()); 133 134 owner->FillRect(frame); 135 } 136 137 if (!IsEnabled()) { 138 rgb_color textColor; 139 if (IsSelected()) 140 textColor = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR); 141 else 142 textColor = ui_color(B_LIST_ITEM_TEXT_COLOR); 143 144 if (textColor.red + textColor.green + textColor.blue > 128 * 3) 145 owner->SetHighColor(tint_color(textColor, B_DARKEN_1_TINT)); 146 else 147 owner->SetHighColor(tint_color(textColor, B_LIGHTEN_1_TINT)); 148 } else { 149 if (IsSelected()) 150 owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR)); 151 else 152 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR)); 153 } 154 155 156 // icon 157 owner->MovePenTo(frame.left + 4, frame.top + 1); 158 owner->DrawBitmap(fIcon); 159 160 // device 161 owner->MovePenTo(frame.left + 8 + fIcon->Bounds().Width(), 162 frame.top + fSecondBaselineOffset); 163 owner->DrawString(fPath.Path()); 164 165 // name 166 BFont boldFont; 167 BFont ownerFont; 168 owner->GetFont(&ownerFont); 169 owner->GetFont(&boldFont); 170 boldFont.SetFace(B_BOLD_FACE); 171 owner->SetFont(&boldFont); 172 173 BPoint namePosition(frame.left + 8 + fIcon->Bounds().Width(), 174 frame.top + fBaselineOffset); 175 176 owner->MovePenTo(namePosition); 177 owner->DrawString(fName.String()); 178 179 float nameWidth = owner->StringWidth(fName.String()); 180 float messageWidth = frame.right - 4 - fSizeWidth 181 - (frame.left + 8 + fIcon->Bounds().Width()) - nameWidth 182 - fBaselineOffset * 2; 183 184 if (fCanBeInstalled != B_OK) { 185 rgb_color highColor = owner->HighColor(); 186 owner->SetHighColor(ui_color(B_FAILURE_COLOR)); 187 owner->MovePenBy(fBaselineOffset, 0); 188 const char* message; 189 switch (fCanBeInstalled) { 190 case B_PARTITION_TOO_SMALL: 191 message = B_TRANSLATE_COMMENT("No space available!", 192 "Cannot install"); 193 break; 194 case B_ENTRY_NOT_FOUND: 195 message = B_TRANSLATE_COMMENT("Incompatible format!", 196 "Cannot install"); 197 break; 198 case B_READ_ONLY_DEVICE: 199 message = B_TRANSLATE_COMMENT("Read only!", 200 "Cannot install"); 201 break; 202 default: 203 message = B_TRANSLATE_COMMENT("Cannot access!", 204 "Cannot install"); 205 break; 206 } 207 BString truncatedMessage = message; 208 owner->TruncateString(&truncatedMessage, B_TRUNCATE_END, messageWidth); 209 owner->DrawString(truncatedMessage); 210 owner->SetHighColor(highColor); 211 } 212 owner->SetFont(&ownerFont); 213 // size 214 BPoint sizePosition(frame.right - 4 - fSizeWidth, 215 frame.top + fBaselineOffset); 216 if (sizePosition.x > namePosition.x + nameWidth) { 217 owner->MovePenTo(sizePosition); 218 owner->DrawString(fSize.String()); 219 } 220 221 owner->PopState(); 222 } 223 224 225 void 226 DriveItem::Update(BView* owner, const BFont* font) 227 { 228 fSizeWidth = font->StringWidth(fSize.String()); 229 230 BFont boldFont(font); 231 boldFont.SetFace(B_BOLD_FACE); 232 float width = 8 + boldFont.StringWidth(fPath.Path()) 233 + be_control_look->DefaultItemSpacing() + fSizeWidth; 234 float pathWidth = font->StringWidth(fPath.Path()); 235 if (width < pathWidth) 236 width = pathWidth; 237 238 SetWidth(width); 239 240 font_height fheight; 241 font->GetHeight(&fheight); 242 243 float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent) 244 + ceilf(fheight.leading); 245 246 fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2); 247 fSecondBaselineOffset = fBaselineOffset + lineHeight; 248 249 SetHeight(2 * lineHeight + 4); 250 } 251 252 253 // #pragma mark - 254 255 256 DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus, 257 BMessage* settings, const char* name) 258 : 259 WizardPageView(settings, name), 260 fWizardView(wizardView), 261 fHasInstallableItems(false) 262 { 263 BString text; 264 text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n" 265 << B_TRANSLATE("Please select the drive you want the boot manager to " 266 "be installed to or uninstalled from."); 267 BTextView* description = CreateDescription("description", text); 268 MakeHeading(description); 269 270 fDrivesView = new BListView("drives", B_SINGLE_SELECTION_LIST, 271 B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE); 272 fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged)); 273 274 BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0, 275 false, true); 276 277 SetLayout(new BGroupLayout(B_VERTICAL)); 278 279 BLayoutBuilder::Group<>((BGroupLayout*)GetLayout()) 280 .Add(description) 281 .Add(scrollView, 10.0); 282 283 _UpdateWizardButtons(NULL); 284 _FillDrivesView(menus); 285 } 286 287 288 DrivesPage::~DrivesPage() 289 { 290 } 291 292 293 void 294 DrivesPage::PageCompleted() 295 { 296 DriveItem* item = _SelectedDriveItem(); 297 298 if (fSettings->ReplaceString("disk", item->Path()) != B_OK) 299 fSettings->AddString("disk", item->Path()); 300 } 301 302 303 void 304 DrivesPage::AttachedToWindow() 305 { 306 fDrivesView->SetTarget(this); 307 } 308 309 310 void 311 DrivesPage::MessageReceived(BMessage* message) 312 { 313 switch (message->what) { 314 case kMsgSelectionChanged: 315 { 316 _UpdateWizardButtons(_SelectedDriveItem()); 317 break; 318 } 319 320 default: 321 WizardPageView::MessageReceived(message); 322 break; 323 } 324 } 325 326 327 /*! Builds the list view items, and adds them to fDriveView. 328 Sets the fHasInstallableItems member to indicate if there 329 are any possible install targets. Automatically 330 selects the boot drive. 331 */ 332 void 333 DrivesPage::_FillDrivesView(const BootMenuList& menus) 334 { 335 const char* selected = fSettings->FindString("disk"); 336 337 BDiskDeviceRoster roster; 338 BDiskDevice device; 339 while (roster.GetNextDevice(&device) == B_OK) { 340 if (device.HasMedia() && !device.IsReadOnly()) { 341 DriveItem* item = new DriveItem(device, menus); 342 if (item->CanBeInstalled()) 343 fHasInstallableItems = true; 344 fDrivesView->AddItem(item); 345 346 if ((selected == NULL && item->IsBootDrive()) 347 || (selected != NULL && !strcmp(item->Path(), selected))) { 348 fDrivesView->Select(fDrivesView->CountItems() - 1); 349 _UpdateWizardButtons(item); 350 } 351 } 352 } 353 } 354 355 356 DriveItem* 357 DrivesPage::_SelectedDriveItem() 358 { 359 return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection()); 360 } 361 362 363 void 364 DrivesPage::_UpdateWizardButtons(DriveItem* item) 365 { 366 fWizardView->SetPreviousButtonHidden(!fHasInstallableItems); 367 fWizardView->SetPreviousButtonLabel( 368 B_TRANSLATE_COMMENT("Uninstall", "Button")); 369 if (item == NULL) { 370 fWizardView->SetPreviousButtonEnabled(false); 371 fWizardView->SetNextButtonEnabled(false); 372 } else { 373 fWizardView->SetPreviousButtonEnabled( 374 item->CanBeInstalled() && item->IsInstalled()); 375 fWizardView->SetNextButtonEnabled(item->CanBeInstalled()); 376 377 fWizardView->SetNextButtonLabel( 378 item->IsInstalled() && item->CanBeInstalled() 379 ? B_TRANSLATE_COMMENT("Update", "Button") 380 : B_TRANSLATE_COMMENT("Install", "Button")); 381 } 382 383 } 384