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 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 default: 199 message = B_TRANSLATE_COMMENT("Cannot access!", 200 "Cannot install"); 201 break; 202 } 203 BString truncatedMessage = message; 204 owner->TruncateString(&truncatedMessage, B_TRUNCATE_END, messageWidth); 205 owner->DrawString(truncatedMessage); 206 owner->SetHighColor(highColor); 207 } 208 owner->SetFont(&ownerFont); 209 // size 210 BPoint sizePosition(frame.right - 4 - fSizeWidth, 211 frame.top + fBaselineOffset); 212 if (sizePosition.x > namePosition.x + nameWidth) { 213 owner->MovePenTo(sizePosition); 214 owner->DrawString(fSize.String()); 215 } 216 217 owner->PopState(); 218 } 219 220 221 void 222 DriveItem::Update(BView* owner, const BFont* font) 223 { 224 fSizeWidth = font->StringWidth(fSize.String()); 225 226 BFont boldFont(font); 227 boldFont.SetFace(B_BOLD_FACE); 228 float width = 8 + boldFont.StringWidth(fPath.Path()) 229 + be_control_look->DefaultItemSpacing() + fSizeWidth; 230 float pathWidth = font->StringWidth(fPath.Path()); 231 if (width < pathWidth) 232 width = pathWidth; 233 234 SetWidth(width); 235 236 font_height fheight; 237 font->GetHeight(&fheight); 238 239 float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent) 240 + ceilf(fheight.leading); 241 242 fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2); 243 fSecondBaselineOffset = fBaselineOffset + lineHeight; 244 245 SetHeight(2 * lineHeight + 4); 246 } 247 248 249 // #pragma mark - 250 251 252 DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus, 253 BMessage* settings, const char* name) 254 : 255 WizardPageView(settings, name), 256 fWizardView(wizardView), 257 fHasInstallableItems(false) 258 { 259 BString text; 260 text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n" 261 << B_TRANSLATE("Please select the drive you want the boot manager to " 262 "be installed to or uninstalled from."); 263 BTextView* description = CreateDescription("description", text); 264 MakeHeading(description); 265 266 fDrivesView = new BListView("drives", B_SINGLE_SELECTION_LIST, 267 B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE); 268 fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged)); 269 270 BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0, 271 false, true); 272 273 SetLayout(new BGroupLayout(B_VERTICAL)); 274 275 BLayoutBuilder::Group<>((BGroupLayout*)GetLayout()) 276 .Add(description, 0.5) 277 .Add(scrollView, 1); 278 279 _UpdateWizardButtons(NULL); 280 _FillDrivesView(menus); 281 } 282 283 284 DrivesPage::~DrivesPage() 285 { 286 } 287 288 289 void 290 DrivesPage::PageCompleted() 291 { 292 DriveItem* item = _SelectedDriveItem(); 293 294 if (fSettings->ReplaceString("disk", item->Path()) != B_OK) 295 fSettings->AddString("disk", item->Path()); 296 } 297 298 299 void 300 DrivesPage::AttachedToWindow() 301 { 302 fDrivesView->SetTarget(this); 303 } 304 305 306 void 307 DrivesPage::MessageReceived(BMessage* message) 308 { 309 switch (message->what) { 310 case kMsgSelectionChanged: 311 { 312 _UpdateWizardButtons(_SelectedDriveItem()); 313 break; 314 } 315 316 default: 317 WizardPageView::MessageReceived(message); 318 break; 319 } 320 } 321 322 323 /*! Builds the list view items, and adds them to fDriveView. 324 Sets the fHasInstallableItems member to indicate if there 325 are any possible install targets. Automatically 326 selects the boot drive. 327 */ 328 void 329 DrivesPage::_FillDrivesView(const BootMenuList& menus) 330 { 331 const char* selected = fSettings->FindString("disk"); 332 333 BDiskDeviceRoster roster; 334 BDiskDevice device; 335 while (roster.GetNextDevice(&device) == B_OK) { 336 if (device.HasMedia() && !device.IsReadOnly()) { 337 DriveItem* item = new DriveItem(device, menus); 338 if (item->CanBeInstalled()) 339 fHasInstallableItems = true; 340 fDrivesView->AddItem(item); 341 342 if ((selected == NULL && item->IsBootDrive()) 343 || (selected != NULL && !strcmp(item->Path(), selected))) { 344 fDrivesView->Select(fDrivesView->CountItems() - 1); 345 _UpdateWizardButtons(item); 346 } 347 } 348 } 349 } 350 351 352 DriveItem* 353 DrivesPage::_SelectedDriveItem() 354 { 355 return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection()); 356 } 357 358 359 void 360 DrivesPage::_UpdateWizardButtons(DriveItem* item) 361 { 362 fWizardView->SetPreviousButtonHidden(!fHasInstallableItems); 363 fWizardView->SetPreviousButtonLabel( 364 B_TRANSLATE_COMMENT("Uninstall", "Button")); 365 if (item == NULL) { 366 fWizardView->SetPreviousButtonEnabled(false); 367 fWizardView->SetNextButtonEnabled(false); 368 } else { 369 fWizardView->SetPreviousButtonEnabled( 370 item->CanBeInstalled() && item->IsInstalled()); 371 fWizardView->SetNextButtonEnabled(item->CanBeInstalled()); 372 373 fWizardView->SetNextButtonLabel( 374 item->IsInstalled() && item->CanBeInstalled() 375 ? B_TRANSLATE_COMMENT("Update", "Button") 376 : B_TRANSLATE_COMMENT("Install", "Button")); 377 } 378 379 } 380