1 /* 2 * Copyright 2002-2010 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Erik Jaesler <ejakowatz@users.sourceforge.net> 7 * Ithamar R. Adema <ithamar@unet.nl> 8 * Ingo Weinhold <ingo_weinhold@gmx.de> 9 * Stephan Aßmus <superstippi@gmx.de> 10 */ 11 12 #include "MainWindow.h" 13 14 #include <stdio.h> 15 #include <string.h> 16 17 #include <Alert.h> 18 #include <Application.h> 19 #include <Catalog.h> 20 #include <ColumnListView.h> 21 #include <ColumnTypes.h> 22 #include <Debug.h> 23 #include <DiskDevice.h> 24 #include <DiskDeviceVisitor.h> 25 #include <DiskDeviceTypes.h> 26 #include <DiskSystem.h> 27 #include <Locale.h> 28 #include <MenuItem.h> 29 #include <MenuBar.h> 30 #include <Menu.h> 31 #include <Path.h> 32 #include <Partition.h> 33 #include <PartitioningInfo.h> 34 #include <Roster.h> 35 #include <Screen.h> 36 #include <Volume.h> 37 #include <VolumeRoster.h> 38 39 #include "CreateParamsPanel.h" 40 #include "DiskView.h" 41 #include "InitParamsPanel.h" 42 #include "PartitionList.h" 43 #include "Support.h" 44 #include "tracker_private.h" 45 46 47 #undef B_TRANSLATION_CONTEXT 48 #define B_TRANSLATION_CONTEXT "MainWindow" 49 50 51 class ListPopulatorVisitor : public BDiskDeviceVisitor { 52 public: 53 ListPopulatorVisitor(PartitionListView* list, int32& diskCount, 54 SpaceIDMap& spaceIDMap) 55 : 56 fPartitionList(list), 57 fDiskCount(diskCount), 58 fSpaceIDMap(spaceIDMap) 59 { 60 fDiskCount = 0; 61 fSpaceIDMap.Clear(); 62 // start with an empty list 63 int32 rows = fPartitionList->CountRows(); 64 for (int32 i = rows - 1; i >= 0; i--) { 65 BRow* row = fPartitionList->RowAt(i); 66 fPartitionList->RemoveRow(row); 67 delete row; 68 } 69 } 70 71 virtual bool Visit(BDiskDevice* device) 72 { 73 fDiskCount++; 74 // if we don't prepare the device for modifications, 75 // we cannot get information about available empty 76 // regions on the device or child partitions 77 device->PrepareModifications(); 78 _AddPartition(device); 79 return false; // Don't stop yet! 80 } 81 82 virtual bool Visit(BPartition* partition, int32 level) 83 { 84 _AddPartition(partition); 85 return false; // Don't stop yet! 86 } 87 88 private: 89 void _AddPartition(BPartition* partition) const 90 { 91 // add the partition itself 92 fPartitionList->AddPartition(partition); 93 94 // add any available space on it 95 BPartitioningInfo info; 96 status_t ret = partition->GetPartitioningInfo(&info); 97 if (ret >= B_OK) { 98 partition_id parentID = partition->ID(); 99 off_t offset; 100 off_t size; 101 for (int32 i = 0; 102 info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK; 103 i++) { 104 // TODO: remove again once Disk Device API is fixed 105 if (!is_valid_partitionable_space(size)) 106 continue; 107 // 108 partition_id id = fSpaceIDMap.SpaceIDFor(parentID, offset); 109 fPartitionList->AddSpace(parentID, id, offset, size); 110 } 111 } 112 } 113 114 PartitionListView* fPartitionList; 115 int32& fDiskCount; 116 SpaceIDMap& fSpaceIDMap; 117 BDiskDevice* fLastPreparedDevice; 118 }; 119 120 121 class MountAllVisitor : public BDiskDeviceVisitor { 122 public: 123 MountAllVisitor() 124 { 125 } 126 127 virtual bool Visit(BDiskDevice* device) 128 { 129 return false; // Don't stop yet! 130 } 131 132 virtual bool Visit(BPartition* partition, int32 level) 133 { 134 partition->Mount(); 135 return false; // Don't stop yet! 136 } 137 138 private: 139 PartitionListView* fPartitionList; 140 }; 141 142 143 enum { 144 MSG_MOUNT_ALL = 'mnta', 145 MSG_MOUNT = 'mnts', 146 MSG_UNMOUNT = 'unmt', 147 MSG_FORMAT = 'frmt', 148 MSG_CREATE = 'crtp', 149 MSG_INITIALIZE = 'init', 150 MSG_DELETE = 'delt', 151 MSG_EJECT = 'ejct', 152 MSG_SURFACE_TEST = 'sfct', 153 MSG_RESCAN = 'rscn', 154 155 MSG_PARTITION_ROW_SELECTED = 'prsl', 156 }; 157 158 159 // #pragma mark - 160 161 162 MainWindow::MainWindow(BRect frame) 163 : 164 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("DriveSetup"), B_DOCUMENT_WINDOW, 165 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE), 166 fCurrentDisk(NULL), 167 fCurrentPartitionID(-1), 168 fSpaceIDMap() 169 { 170 BMenuBar* menuBar = new BMenuBar(Bounds(), "root menu"); 171 172 // create all the menu items 173 fWipeMI = new BMenuItem(B_TRANSLATE("Wipe (not implemented)"), 174 new BMessage(MSG_FORMAT)); 175 fEjectMI = new BMenuItem(B_TRANSLATE("Eject"), 176 new BMessage(MSG_EJECT), 'E'); 177 fSurfaceTestMI = new BMenuItem( 178 B_TRANSLATE("Surface test (not implemented)"), 179 new BMessage(MSG_SURFACE_TEST)); 180 fRescanMI = new BMenuItem(B_TRANSLATE("Rescan"), new BMessage(MSG_RESCAN)); 181 182 fCreateMI = new BMenuItem(B_TRANSLATE("Create" B_UTF8_ELLIPSIS), 183 new BMessage(MSG_CREATE), 'C'); 184 fDeleteMI = new BMenuItem(B_TRANSLATE("Delete"), 185 new BMessage(MSG_DELETE), 'D'); 186 187 fMountMI = new BMenuItem(B_TRANSLATE("Mount"), 188 new BMessage(MSG_MOUNT), 'M'); 189 fUnmountMI = new BMenuItem(B_TRANSLATE("Unmount"), 190 new BMessage(MSG_UNMOUNT), 'U'); 191 fMountAllMI = new BMenuItem(B_TRANSLATE("Mount all"), 192 new BMessage(MSG_MOUNT_ALL), 'M', B_SHIFT_KEY); 193 194 // Disk menu 195 fDiskMenu = new BMenu(B_TRANSLATE("Disk")); 196 197 // fDiskMenu->AddItem(fWipeMI); 198 fDiskInitMenu = new BMenu(B_TRANSLATE("Initialize")); 199 fDiskMenu->AddItem(fDiskInitMenu); 200 201 fDiskMenu->AddSeparatorItem(); 202 203 fDiskMenu->AddItem(fEjectMI); 204 // fDiskMenu->AddItem(fSurfaceTestMI); 205 fDiskMenu->AddItem(fRescanMI); 206 207 menuBar->AddItem(fDiskMenu); 208 209 // Parition menu 210 fPartitionMenu = new BMenu(B_TRANSLATE("Partition")); 211 fPartitionMenu->AddItem(fCreateMI); 212 213 fFormatMenu = new BMenu(B_TRANSLATE("Format")); 214 fPartitionMenu->AddItem(fFormatMenu); 215 216 fPartitionMenu->AddItem(fDeleteMI); 217 218 fPartitionMenu->AddSeparatorItem(); 219 220 fPartitionMenu->AddItem(fMountMI); 221 fPartitionMenu->AddItem(fUnmountMI); 222 223 fPartitionMenu->AddSeparatorItem(); 224 225 fPartitionMenu->AddItem(fMountAllMI); 226 menuBar->AddItem(fPartitionMenu); 227 228 AddChild(menuBar); 229 230 // add DiskView 231 BRect r(Bounds()); 232 r.top = menuBar->Frame().bottom + 1; 233 r.bottom = floorf(r.top + r.Height() * 0.33); 234 fDiskView = new DiskView(r, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, 235 fSpaceIDMap); 236 AddChild(fDiskView); 237 238 // add PartitionListView 239 r.top = r.bottom + 2; 240 r.bottom = Bounds().bottom; 241 r.InsetBy(-1, -1); 242 fListView = new PartitionListView(r, B_FOLLOW_ALL); 243 AddChild(fListView); 244 245 // configure PartitionListView 246 fListView->SetSelectionMode(B_SINGLE_SELECTION_LIST); 247 fListView->SetSelectionMessage(new BMessage(MSG_PARTITION_ROW_SELECTED)); 248 fListView->SetTarget(this); 249 fListView->MakeFocus(true); 250 251 status_t ret = fDDRoster.StartWatching(BMessenger(this)); 252 if (ret != B_OK) { 253 fprintf(stderr, "Failed to start watching for device changes: %s\n", 254 strerror(ret)); 255 } 256 257 // visit all disks in the system and show their contents 258 _ScanDrives(); 259 260 if (!be_roster->IsRunning(kDeskbarSignature)) 261 SetFlags(Flags() | B_NOT_MINIMIZABLE); 262 } 263 264 265 MainWindow::~MainWindow() 266 { 267 BDiskDeviceRoster().StopWatching(this); 268 delete fCurrentDisk; 269 } 270 271 272 void 273 MainWindow::MessageReceived(BMessage* message) 274 { 275 switch (message->what) { 276 case MSG_MOUNT_ALL: 277 _MountAll(); 278 break; 279 case MSG_MOUNT: 280 _Mount(fCurrentDisk, fCurrentPartitionID); 281 break; 282 case MSG_UNMOUNT: 283 _Unmount(fCurrentDisk, fCurrentPartitionID); 284 break; 285 286 case MSG_FORMAT: 287 printf("MSG_FORMAT\n"); 288 break; 289 290 case MSG_CREATE: { 291 _Create(fCurrentDisk, fCurrentPartitionID); 292 break; 293 } 294 295 case MSG_INITIALIZE: { 296 BString diskSystemName; 297 if (message->FindString("disk system", &diskSystemName) != B_OK) 298 break; 299 _Initialize(fCurrentDisk, fCurrentPartitionID, diskSystemName); 300 break; 301 } 302 303 case MSG_DELETE: 304 _Delete(fCurrentDisk, fCurrentPartitionID); 305 break; 306 307 case MSG_EJECT: 308 // TODO: completely untested, especially interesting 309 // if partition list behaves when partitions disappear 310 if (fCurrentDisk) { 311 // TODO: only if no partitions are mounted anymore? 312 fCurrentDisk->Eject(true); 313 _ScanDrives(); 314 } 315 break; 316 case MSG_SURFACE_TEST: 317 printf("MSG_SURFACE_TEST\n"); 318 break; 319 320 // TODO: this could probably be done better! 321 case B_DEVICE_UPDATE: 322 printf("B_DEVICE_UPDATE\n"); 323 case MSG_RESCAN: 324 _ScanDrives(); 325 break; 326 327 case MSG_PARTITION_ROW_SELECTED: 328 // selection of partitions via list view 329 _AdaptToSelectedPartition(); 330 break; 331 case MSG_SELECTED_PARTITION_ID: { 332 // selection of partitions via disk view 333 partition_id id; 334 if (message->FindInt32("partition_id", &id) == B_OK) { 335 if (BRow* row = fListView->FindRow(id)) { 336 fListView->DeselectAll(); 337 fListView->AddToSelection(row); 338 _AdaptToSelectedPartition(); 339 } 340 } 341 break; 342 } 343 344 default: 345 BWindow::MessageReceived(message); 346 break; 347 } 348 } 349 350 351 bool 352 MainWindow::QuitRequested() 353 { 354 // TODO: ask about any unsaved changes 355 be_app->PostMessage(B_QUIT_REQUESTED); 356 Hide(); 357 return false; 358 } 359 360 361 // #pragma mark - 362 363 364 status_t 365 MainWindow::StoreSettings(BMessage* archive) const 366 { 367 if (archive->ReplaceRect("window frame", Frame()) < B_OK) 368 archive->AddRect("window frame", Frame()); 369 370 BMessage columnSettings; 371 fListView->SaveState(&columnSettings); 372 if (archive->ReplaceMessage("column settings", &columnSettings) < B_OK) 373 archive->AddMessage("column settings", &columnSettings); 374 375 return B_OK; 376 } 377 378 379 status_t 380 MainWindow::RestoreSettings(BMessage* archive) 381 { 382 BRect frame; 383 if (archive->FindRect("window frame", &frame) == B_OK) { 384 BScreen screen(this); 385 if (frame.Intersects(screen.Frame())) { 386 MoveTo(frame.LeftTop()); 387 ResizeTo(frame.Width(), frame.Height()); 388 } 389 } 390 391 BMessage columnSettings; 392 if (archive->FindMessage("column settings", &columnSettings) == B_OK) 393 fListView->LoadState(&columnSettings); 394 395 return B_OK; 396 } 397 398 399 void 400 MainWindow::ApplyDefaultSettings() 401 { 402 if (!Lock()) 403 return; 404 405 fListView->ResizeAllColumnsToPreferred(); 406 407 // Adjust window size for convenience 408 float enlargeBy = fListView->PreferredSize().width 409 - fListView->Bounds().Width(); 410 if (enlargeBy > 0.0f) { 411 BScreen screen(this); 412 float windowWidth = Frame().Width() + enlargeBy; 413 if (windowWidth > screen.Frame().Width() - 20.0f) 414 windowWidth = screen.Frame().Width() - 20.0f; 415 416 ResizeTo(windowWidth, Frame().Height()); 417 } 418 419 CenterOnScreen(); 420 421 Unlock(); 422 } 423 424 425 // #pragma mark - 426 427 428 void 429 MainWindow::_ScanDrives() 430 { 431 fSpaceIDMap.Clear(); 432 int32 diskCount = 0; 433 ListPopulatorVisitor driveVisitor(fListView, diskCount, fSpaceIDMap); 434 fDDRoster.VisitEachPartition(&driveVisitor); 435 fDiskView->SetDiskCount(diskCount); 436 437 // restore selection 438 PartitionListRow* previousSelection 439 = fListView->FindRow(fCurrentPartitionID); 440 if (previousSelection) { 441 fListView->AddToSelection(previousSelection); 442 _UpdateMenus(fCurrentDisk, fCurrentPartitionID, 443 previousSelection->ParentID()); 444 fDiskView->ForceUpdate(); 445 } else { 446 _UpdateMenus(NULL, -1, -1); 447 } 448 } 449 450 451 // #pragma mark - 452 453 454 void 455 MainWindow::_AdaptToSelectedPartition() 456 { 457 partition_id diskID = -1; 458 partition_id partitionID = -1; 459 partition_id parentID = -1; 460 461 BRow* _selectedRow = fListView->CurrentSelection(); 462 if (_selectedRow) { 463 // go up to top level row 464 BRow* _topLevelRow = _selectedRow; 465 BRow* parent = NULL; 466 while (fListView->FindParent(_topLevelRow, &parent, NULL)) 467 _topLevelRow = parent; 468 469 PartitionListRow* topLevelRow 470 = dynamic_cast<PartitionListRow*>(_topLevelRow); 471 PartitionListRow* selectedRow 472 = dynamic_cast<PartitionListRow*>(_selectedRow); 473 474 if (topLevelRow) 475 diskID = topLevelRow->ID(); 476 if (selectedRow) { 477 partitionID = selectedRow->ID(); 478 parentID = selectedRow->ParentID(); 479 } 480 } 481 482 _SetToDiskAndPartition(diskID, partitionID, parentID); 483 } 484 485 486 void 487 MainWindow::_SetToDiskAndPartition(partition_id disk, partition_id partition, 488 partition_id parent) 489 { 490 //printf("MainWindow::_SetToDiskAndPartition(disk: %ld, partition: %ld, " 491 // "parent: %ld)\n", disk, partition, parent); 492 493 BDiskDevice* oldDisk = NULL; 494 if (!fCurrentDisk || fCurrentDisk->ID() != disk) { 495 oldDisk = fCurrentDisk; 496 fCurrentDisk = NULL; 497 if (disk >= 0) { 498 BDiskDevice* newDisk = new BDiskDevice(); 499 status_t ret = newDisk->SetTo(disk); 500 if (ret < B_OK) { 501 printf("error switching disks: %s\n", strerror(ret)); 502 delete newDisk; 503 } else 504 fCurrentDisk = newDisk; 505 } 506 } 507 508 fCurrentPartitionID = partition; 509 510 fDiskView->SetDisk(fCurrentDisk, fCurrentPartitionID); 511 _UpdateMenus(fCurrentDisk, fCurrentPartitionID, parent); 512 513 delete oldDisk; 514 } 515 516 517 void 518 MainWindow::_UpdateMenus(BDiskDevice* disk, 519 partition_id selectedPartition, partition_id parentID) 520 { 521 while (BMenuItem* item = fFormatMenu->RemoveItem(0L)) 522 delete item; 523 while (BMenuItem* item = fDiskInitMenu->RemoveItem(0L)) 524 delete item; 525 526 fCreateMI->SetEnabled(false); 527 fUnmountMI->SetEnabled(false); 528 fDiskInitMenu->SetEnabled(false); 529 fFormatMenu->SetEnabled(false); 530 531 if (!disk) { 532 fWipeMI->SetEnabled(false); 533 fEjectMI->SetEnabled(false); 534 fSurfaceTestMI->SetEnabled(false); 535 } else { 536 // fWipeMI->SetEnabled(true); 537 fWipeMI->SetEnabled(false); 538 fEjectMI->SetEnabled(disk->IsRemovableMedia()); 539 // fSurfaceTestMI->SetEnabled(true); 540 fSurfaceTestMI->SetEnabled(false); 541 542 // Create menu and items 543 BPartition* parentPartition = NULL; 544 if (selectedPartition <= -2) { 545 // a partitionable space item is selected 546 parentPartition = disk->FindDescendant(parentID); 547 } 548 549 if (parentPartition && parentPartition->ContainsPartitioningSystem()) 550 fCreateMI->SetEnabled(true); 551 552 bool prepared = disk->PrepareModifications() == B_OK; 553 fFormatMenu->SetEnabled(prepared); 554 fDeleteMI->SetEnabled(prepared); 555 556 BPartition* partition = disk->FindDescendant(selectedPartition); 557 558 BDiskSystem diskSystem; 559 fDDRoster.RewindDiskSystems(); 560 while (fDDRoster.GetNextDiskSystem(&diskSystem) == B_OK) { 561 if (!diskSystem.SupportsInitializing()) 562 continue; 563 564 BMessage* message = new BMessage(MSG_INITIALIZE); 565 message->AddInt32("parent id", parentID); 566 message->AddString("disk system", diskSystem.PrettyName()); 567 568 BString label = diskSystem.PrettyName(); 569 label << B_UTF8_ELLIPSIS; 570 BMenuItem* item = new BMenuItem(label.String(), message); 571 572 // TODO: Very unintuitive that we have to use PrettyName (vs Name) 573 item->SetEnabled(partition != NULL 574 && partition->CanInitialize(diskSystem.PrettyName())); 575 576 if (disk->ID() == selectedPartition 577 && !diskSystem.IsFileSystem()) { 578 // Disk is selected, and DiskSystem is a partition map 579 fDiskInitMenu->AddItem(item); 580 } else if (diskSystem.IsFileSystem()) { 581 // Otherwise a filesystem 582 fFormatMenu->AddItem(item); 583 } 584 } 585 586 // Mount items 587 if (partition) { 588 fFormatMenu->SetEnabled(!partition->IsMounted() 589 && !partition->IsReadOnly() 590 && partition->Device()->HasMedia() 591 && fFormatMenu->CountItems() > 0); 592 593 fDiskInitMenu->SetEnabled(!partition->IsMounted() 594 && !partition->IsReadOnly() 595 && partition->Device()->HasMedia() 596 && partition->IsDevice() 597 && fDiskInitMenu->CountItems() > 0); 598 599 fDeleteMI->SetEnabled(!partition->IsMounted() 600 && !partition->IsDevice()); 601 602 fMountMI->SetEnabled(!partition->IsMounted()); 603 604 bool unMountable = false; 605 if (partition->IsMounted()) { 606 // see if this partition is the boot volume 607 BVolume volume; 608 BVolume bootVolume; 609 if (BVolumeRoster().GetBootVolume(&bootVolume) == B_OK 610 && partition->GetVolume(&volume) == B_OK) { 611 unMountable = volume != bootVolume; 612 } else 613 unMountable = true; 614 } 615 fUnmountMI->SetEnabled(unMountable); 616 } else { 617 fDeleteMI->SetEnabled(false); 618 fMountMI->SetEnabled(false); 619 fFormatMenu->SetEnabled(false); 620 fDiskInitMenu->SetEnabled(false); 621 } 622 623 if (prepared) 624 disk->CancelModifications(); 625 626 fMountAllMI->SetEnabled(true); 627 } 628 if (selectedPartition < 0) { 629 fDeleteMI->SetEnabled(false); 630 fMountMI->SetEnabled(false); 631 } 632 } 633 634 635 void 636 MainWindow::_DisplayPartitionError(BString _message, 637 const BPartition* partition, status_t error) const 638 { 639 char message[1024]; 640 641 if (partition && _message.FindFirst("%s") >= 0) { 642 BString name; 643 name << "\"" << partition->ContentName() << "\""; 644 snprintf(message, sizeof(message), _message.String(), name.String()); 645 } else { 646 _message.ReplaceAll("%s", ""); 647 snprintf(message, sizeof(message), _message.String()); 648 } 649 650 if (error < B_OK) { 651 BString helper = message; 652 const char* errorString 653 = B_TRANSLATE_COMMENT("Error: ", "in any error alert"); 654 snprintf(message, sizeof(message), "%s\n\n%s%s", helper.String(), 655 errorString, strerror(error)); 656 } 657 658 BAlert* alert = new BAlert("error", message, B_TRANSLATE("OK"), NULL, NULL, 659 B_WIDTH_FROM_WIDEST, error < B_OK ? B_STOP_ALERT : B_INFO_ALERT); 660 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 661 alert->Go(NULL); 662 } 663 664 665 // #pragma mark - 666 667 668 void 669 MainWindow::_Mount(BDiskDevice* disk, partition_id selectedPartition) 670 { 671 if (!disk || selectedPartition < 0) { 672 _DisplayPartitionError(B_TRANSLATE("You need to select a partition " 673 "entry from the list.")); 674 return; 675 } 676 677 BPartition* partition = disk->FindDescendant(selectedPartition); 678 if (!partition) { 679 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected " 680 "partition by ID.")); 681 return; 682 } 683 684 if (!partition->IsMounted()) { 685 status_t ret = partition->Mount(); 686 if (ret < B_OK) { 687 _DisplayPartitionError( 688 B_TRANSLATE("Could not mount partition %s."), partition, ret); 689 } else { 690 // successful mount, adapt to the changes 691 _ScanDrives(); 692 } 693 } else { 694 _DisplayPartitionError( 695 B_TRANSLATE("The partition %s is already mounted."), partition); 696 } 697 } 698 699 700 void 701 MainWindow::_Unmount(BDiskDevice* disk, partition_id selectedPartition) 702 { 703 if (!disk || selectedPartition < 0) { 704 _DisplayPartitionError(B_TRANSLATE("You need to select a partition " 705 "entry from the list.")); 706 return; 707 } 708 709 BPartition* partition = disk->FindDescendant(selectedPartition); 710 if (!partition) { 711 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected " 712 "partition by ID.")); 713 return; 714 } 715 716 if (partition->IsMounted()) { 717 BPath path; 718 partition->GetMountPoint(&path); 719 status_t ret = partition->Unmount(); 720 if (ret < B_OK) { 721 _DisplayPartitionError( 722 B_TRANSLATE("Could not unmount partition %s."), 723 partition, ret); 724 } else { 725 if (dev_for_path(path.Path()) == dev_for_path("/")) 726 rmdir(path.Path()); 727 // successful unmount, adapt to the changes 728 _ScanDrives(); 729 } 730 } else { 731 _DisplayPartitionError( 732 B_TRANSLATE("The partition %s is already unmounted."), 733 partition); 734 } 735 } 736 737 738 void 739 MainWindow::_MountAll() 740 { 741 MountAllVisitor visitor; 742 fDDRoster.VisitEachPartition(&visitor); 743 } 744 745 746 // #pragma mark - 747 748 749 class ModificationPreparer { 750 public: 751 ModificationPreparer(BDiskDevice* disk) 752 : 753 fDisk(disk), 754 fModificationStatus(fDisk->PrepareModifications()) 755 { 756 } 757 ~ModificationPreparer() 758 { 759 if (fModificationStatus == B_OK) 760 fDisk->CancelModifications(); 761 } 762 status_t ModificationStatus() const 763 { 764 return fModificationStatus; 765 } 766 status_t CommitModifications() 767 { 768 status_t ret = fDisk->CommitModifications(); 769 if (ret == B_OK) 770 fModificationStatus = B_ERROR; 771 772 return ret; 773 } 774 775 private: 776 BDiskDevice* fDisk; 777 status_t fModificationStatus; 778 }; 779 780 781 void 782 MainWindow::_Initialize(BDiskDevice* disk, partition_id selectedPartition, 783 const BString& diskSystemName) 784 { 785 if (!disk || selectedPartition < 0) { 786 _DisplayPartitionError(B_TRANSLATE("You need to select a partition " 787 "entry from the list.")); 788 return; 789 } 790 791 if (disk->IsReadOnly()) { 792 _DisplayPartitionError(B_TRANSLATE("The selected disk is read-only.")); 793 return; 794 } 795 796 BPartition* partition = disk->FindDescendant(selectedPartition); 797 if (!partition) { 798 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected " 799 "partition by ID.")); 800 return; 801 } 802 803 if (partition->IsMounted()) { 804 _DisplayPartitionError( 805 B_TRANSLATE("The partition %s is currently mounted.")); 806 // TODO: option to unmount and continue on success to unmount 807 return; 808 } 809 810 BDiskSystem diskSystem; 811 fDDRoster.RewindDiskSystems(); 812 bool found = false; 813 while (fDDRoster.GetNextDiskSystem(&diskSystem) == B_OK) { 814 if (diskSystem.SupportsInitializing()) { 815 if (diskSystemName == diskSystem.PrettyName()) { 816 found = true; 817 break; 818 } 819 } 820 } 821 822 char message[512]; 823 824 if (!found) { 825 snprintf(message, sizeof(message), B_TRANSLATE("Disk system \"%s\"\" " 826 "not found!")); 827 _DisplayPartitionError(message); 828 return; 829 } 830 831 if (diskSystem.IsFileSystem()) { 832 if (disk->ID() == selectedPartition) { 833 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 834 "want to format a raw disk? (most people initialize the disk " 835 "with a partitioning system first) You will be asked " 836 "again before changes are written to the disk.")); 837 } else if (partition->ContentName() 838 && strlen(partition->ContentName()) > 0) { 839 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 840 "want to format the partition \"%s\"? You will be asked " 841 "again before changes are written to the disk."), 842 partition->ContentName()); 843 } else { 844 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 845 "want to format the partition? You will be asked again " 846 "before changes are written to the disk.")); 847 } 848 } else { 849 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 850 "want to initialize the selected disk? All data will be lost. " 851 "You will be asked again before changes are written to the " 852 "disk.\n")); 853 } 854 BAlert* alert = new BAlert("first notice", message, 855 B_TRANSLATE("Continue"), B_TRANSLATE("Cancel"), NULL, 856 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT); 857 alert->SetShortcut(1, B_ESCAPE); 858 int32 choice = alert->Go(); 859 860 if (choice == 1) 861 return; 862 863 ModificationPreparer modificationPreparer(disk); 864 status_t ret = modificationPreparer.ModificationStatus(); 865 if (ret != B_OK) { 866 _DisplayPartitionError(B_TRANSLATE("There was an error preparing the " 867 "disk for modifications."), NULL, ret); 868 return; 869 } 870 871 BString name; 872 BString parameters; 873 874 // TODO: diskSystem.IsFileSystem() seems like a better fit here? 875 if (diskSystemName == "Be File System") { 876 InitParamsPanel* panel = new InitParamsPanel(this, diskSystemName, 877 partition); 878 if (panel->Go(name, parameters) == GO_CANCELED) 879 return; 880 } else if (diskSystemName == "Intel Partition Map") { 881 // TODO: parameters? 882 } else if (diskSystemName == "Intel Extended Partition") { 883 // TODO: parameters? 884 } 885 886 bool supportsName = diskSystem.SupportsContentName(); 887 BString validatedName(name); 888 ret = partition->ValidateInitialize(diskSystem.PrettyName(), 889 supportsName ? &validatedName : NULL, parameters.String()); 890 if (ret != B_OK) { 891 _DisplayPartitionError(B_TRANSLATE("Validation of the given " 892 "initialization parameters failed."), partition, ret); 893 return; 894 } 895 896 BString previousName = partition->ContentName(); 897 898 ret = partition->Initialize(diskSystem.PrettyName(), 899 supportsName ? validatedName.String() : NULL, parameters.String()); 900 if (ret != B_OK) { 901 _DisplayPartitionError(B_TRANSLATE("Initialization of the partition " 902 "%s failed. (Nothing has been written to disk.)"), partition, ret); 903 return; 904 } 905 906 // everything looks fine, we are ready to actually write the changes 907 // to disk 908 909 // Warn the user one more time... 910 if (previousName.Length() > 0) { 911 if (partition->IsDevice()) { 912 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 913 "want to write the changes back to disk now?\n\n" 914 "All data on the disk %s will be irretrievably lost if you " 915 "do so!"), previousName.String()); 916 } else { 917 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 918 "want to write the changes back to disk now?\n\n" 919 "All data on the partition %s will be irretrievably lost if you " 920 "do so!"), previousName.String()); 921 } 922 } else { 923 if (partition->IsDevice()) { 924 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 925 "want to write the changes back to disk now?\n\n" 926 "All data on the selected disk will be irretrievably lost if " 927 "you do so!")); 928 } else { 929 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you " 930 "want to write the changes back to disk now?\n\n" 931 "All data on the selected partition will be irretrievably lost " 932 "if you do so!")); 933 } 934 } 935 alert = new BAlert("final notice", message, 936 B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL, 937 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT); 938 alert->SetShortcut(1, B_ESCAPE); 939 choice = alert->Go(); 940 941 if (choice == 1) 942 return; 943 944 // commit 945 ret = modificationPreparer.CommitModifications(); 946 947 // The partition pointer is toast now! Use the partition ID to 948 // retrieve it again. 949 partition = disk->FindDescendant(selectedPartition); 950 951 if (ret == B_OK) { 952 if (diskSystem.IsFileSystem()) { 953 _DisplayPartitionError(B_TRANSLATE("The partition %s has been " 954 "successfully formatted.\n"), partition); 955 } else { 956 _DisplayPartitionError(B_TRANSLATE("The disk has been " 957 "successfully initialized.\n"), partition); 958 } 959 } else { 960 if (diskSystem.IsFileSystem()) { 961 _DisplayPartitionError(B_TRANSLATE("Failed to format the " 962 "partition %s!\n"), partition, ret); 963 } else { 964 _DisplayPartitionError(B_TRANSLATE("Failed to initialize the " 965 "disk %s!\n"), partition, ret); 966 } 967 } 968 969 _ScanDrives(); 970 } 971 972 973 void 974 MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition) 975 { 976 if (!disk || selectedPartition > -2) { 977 _DisplayPartitionError(B_TRANSLATE("The currently selected partition " 978 "is not empty.")); 979 return; 980 } 981 982 if (disk->IsReadOnly()) { 983 _DisplayPartitionError(B_TRANSLATE("The selected disk is read-only.")); 984 return; 985 } 986 987 PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>( 988 fListView->CurrentSelection()); 989 if (!currentSelection) { 990 _DisplayPartitionError(B_TRANSLATE("There was an error acquiring the " 991 "partition row.")); 992 return; 993 } 994 995 BPartition* parent = disk->FindDescendant(currentSelection->ParentID()); 996 if (!parent) { 997 _DisplayPartitionError(B_TRANSLATE("The currently selected partition " 998 "does not have a parent partition.")); 999 return; 1000 } 1001 1002 if (!parent->ContainsPartitioningSystem()) { 1003 _DisplayPartitionError(B_TRANSLATE("The selected partition does not " 1004 "contain a partitioning system.")); 1005 return; 1006 } 1007 1008 ModificationPreparer modificationPreparer(disk); 1009 status_t ret = modificationPreparer.ModificationStatus(); 1010 if (ret != B_OK) { 1011 _DisplayPartitionError(B_TRANSLATE("There was an error preparing the " 1012 "disk for modifications."), NULL, ret); 1013 return; 1014 } 1015 1016 // get partitioning info 1017 BPartitioningInfo partitioningInfo; 1018 status_t error = parent->GetPartitioningInfo(&partitioningInfo); 1019 if (error != B_OK) { 1020 _DisplayPartitionError(B_TRANSLATE("Could not aquire partitioning " 1021 "information.")); 1022 return; 1023 } 1024 1025 int32 spacesCount = partitioningInfo.CountPartitionableSpaces(); 1026 if (spacesCount == 0) { 1027 _DisplayPartitionError(B_TRANSLATE("There's no space on the partition " 1028 "where a child partition could be created.")); 1029 return; 1030 } 1031 1032 BString name, type, parameters; 1033 off_t offset = currentSelection->Offset(); 1034 off_t size = currentSelection->Size(); 1035 1036 CreateParamsPanel* panel = new CreateParamsPanel(this, parent, offset, 1037 size); 1038 if (panel->Go(offset, size, name, type, parameters) == GO_CANCELED) 1039 return; 1040 1041 ret = parent->ValidateCreateChild(&offset, &size, type.String(), 1042 &name, parameters.String()); 1043 1044 if (ret != B_OK) { 1045 _DisplayPartitionError(B_TRANSLATE("Validation of the given creation " 1046 "parameters failed.")); 1047 return; 1048 } 1049 1050 // Warn the user one more time... 1051 BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you " 1052 "want to write the changes back to disk now?\n\n" 1053 "All data on the partition will be irretrievably lost if you do " 1054 "so!"), B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL, 1055 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT); 1056 alert->SetShortcut(1, B_ESCAPE); 1057 int32 choice = alert->Go(); 1058 1059 if (choice == 1) 1060 return; 1061 1062 ret = parent->CreateChild(offset, size, type.String(), 1063 name.String(), parameters.String()); 1064 1065 if (ret != B_OK) { 1066 _DisplayPartitionError(B_TRANSLATE("Creation of the partition has " 1067 "failed.")); 1068 return; 1069 } 1070 1071 // commit 1072 ret = modificationPreparer.CommitModifications(); 1073 1074 if (ret != B_OK) { 1075 _DisplayPartitionError(B_TRANSLATE("Failed to format the " 1076 "partition. No changes have been written to disk.")); 1077 return; 1078 } 1079 1080 // The disk layout has changed, update disk information 1081 bool updated; 1082 ret = disk->Update(&updated); 1083 1084 _ScanDrives(); 1085 fDiskView->ForceUpdate(); 1086 } 1087 1088 1089 void 1090 MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition) 1091 { 1092 if (!disk || selectedPartition < 0) { 1093 _DisplayPartitionError(B_TRANSLATE("You need to select a partition " 1094 "entry from the list.")); 1095 return; 1096 } 1097 1098 if (disk->IsReadOnly()) { 1099 _DisplayPartitionError(B_TRANSLATE("The selected disk is read-only.")); 1100 return; 1101 } 1102 1103 BPartition* partition = disk->FindDescendant(selectedPartition); 1104 if (!partition) { 1105 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected " 1106 "partition by ID.")); 1107 return; 1108 } 1109 1110 BPartition* parent = partition->Parent(); 1111 if (!parent) { 1112 _DisplayPartitionError(B_TRANSLATE("The currently selected partition " 1113 "does not have a parent partition.")); 1114 return; 1115 } 1116 1117 ModificationPreparer modificationPreparer(disk); 1118 status_t ret = modificationPreparer.ModificationStatus(); 1119 if (ret != B_OK) { 1120 _DisplayPartitionError(B_TRANSLATE("There was an error preparing the " 1121 "disk for modifications."), NULL, ret); 1122 return; 1123 } 1124 1125 if (!parent->CanDeleteChild(partition->Index())) { 1126 _DisplayPartitionError( 1127 B_TRANSLATE("Cannot delete the selected partition.")); 1128 return; 1129 } 1130 1131 // Warn the user one more time... 1132 BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you " 1133 "want to delete the selected partition?\n\n" 1134 "All data on the partition will be irretrievably lost if you " 1135 "do so!"), B_TRANSLATE("Delete partition"), B_TRANSLATE("Cancel"), NULL, 1136 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT); 1137 alert->SetShortcut(1, B_ESCAPE); 1138 int32 choice = alert->Go(); 1139 1140 if (choice == 1) 1141 return; 1142 1143 ret = parent->DeleteChild(partition->Index()); 1144 if (ret != B_OK) { 1145 _DisplayPartitionError( 1146 B_TRANSLATE("Could not delete the selected partition.")); 1147 return; 1148 } 1149 1150 ret = modificationPreparer.CommitModifications(); 1151 1152 if (ret != B_OK) { 1153 _DisplayPartitionError(B_TRANSLATE("Failed to delete the partition. " 1154 "No changes have been written to disk.")); 1155 return; 1156 } 1157 1158 _ScanDrives(); 1159 fDiskView->ForceUpdate(); 1160 } 1161