vfs: check userland buffers before reading them.
[haiku.git] / src / apps / drivesetup / MainWindow.cpp
blobb1ca297d2c2dcd6cbff97d6da2f9b2bcca9766ac
1 /*
2 * Copyright 2002-2013 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Ithamar R. Adema <ithamar@unet.nl>
7 * Stephan Aßmus <superstippi@gmx.de>
8 * Axel Dörfler, axeld@pinc-software.de.
9 * Erik Jaesler <ejakowatz@users.sourceforge.net>
10 * Ingo Weinhold <ingo_weinhold@gmx.de>
14 #include "MainWindow.h"
16 #include <stdio.h>
17 #include <string.h>
19 #include <Alert.h>
20 #include <Application.h>
21 #include <Catalog.h>
22 #include <ColumnListView.h>
23 #include <ColumnTypes.h>
24 #include <Debug.h>
25 #include <DiskDevice.h>
26 #include <DiskDeviceVisitor.h>
27 #include <DiskDeviceTypes.h>
28 #include <DiskSystem.h>
29 #include <Locale.h>
30 #include <MenuItem.h>
31 #include <MenuBar.h>
32 #include <Menu.h>
33 #include <Path.h>
34 #include <Partition.h>
35 #include <PartitioningInfo.h>
36 #include <Roster.h>
37 #include <Screen.h>
38 #include <ScrollBar.h>
39 #include <Volume.h>
40 #include <VolumeRoster.h>
42 #include <fs_volume.h>
43 #include <tracker_private.h>
45 #include "ChangeParametersPanel.h"
46 #include "ColumnListView.h"
47 #include "CreateParametersPanel.h"
48 #include "DiskView.h"
49 #include "InitParametersPanel.h"
50 #include "PartitionList.h"
51 #include "Support.h"
54 #undef B_TRANSLATION_CONTEXT
55 #define B_TRANSLATION_CONTEXT "MainWindow"
58 enum {
59 MSG_MOUNT_ALL = 'mnta',
60 MSG_MOUNT = 'mnts',
61 MSG_UNMOUNT = 'unmt',
62 MSG_FORMAT = 'frmt',
63 MSG_CREATE = 'crtp',
64 MSG_CHANGE = 'chgp',
65 MSG_INITIALIZE = 'init',
66 MSG_DELETE = 'delt',
67 MSG_EJECT = 'ejct',
68 MSG_SURFACE_TEST = 'sfct',
69 MSG_RESCAN = 'rscn',
71 MSG_PARTITION_ROW_SELECTED = 'prsl',
75 class ListPopulatorVisitor : public BDiskDeviceVisitor {
76 public:
77 ListPopulatorVisitor(PartitionListView* list, int32& diskCount,
78 SpaceIDMap& spaceIDMap)
80 fPartitionList(list),
81 fDiskCount(diskCount),
82 fSpaceIDMap(spaceIDMap)
84 fDiskCount = 0;
85 fSpaceIDMap.Clear();
86 // start with an empty list
87 int32 rows = fPartitionList->CountRows();
88 for (int32 i = rows - 1; i >= 0; i--) {
89 BRow* row = fPartitionList->RowAt(i);
90 fPartitionList->RemoveRow(row);
91 delete row;
95 virtual bool Visit(BDiskDevice* device)
97 fDiskCount++;
98 // if we don't prepare the device for modifications,
99 // we cannot get information about available empty
100 // regions on the device or child partitions
101 device->PrepareModifications();
102 _AddPartition(device);
103 return false; // Don't stop yet!
106 virtual bool Visit(BPartition* partition, int32 level)
108 _AddPartition(partition);
109 return false; // Don't stop yet!
112 private:
113 void _AddPartition(BPartition* partition) const
115 // add the partition itself
116 fPartitionList->AddPartition(partition);
118 // add any available space on it
119 BPartitioningInfo info;
120 status_t status = partition->GetPartitioningInfo(&info);
121 if (status >= B_OK) {
122 partition_id parentID = partition->ID();
123 off_t offset;
124 off_t size;
125 for (int32 i = 0;
126 info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
127 i++) {
128 // TODO: remove again once Disk Device API is fixed
129 if (!is_valid_partitionable_space(size))
130 continue;
132 partition_id id = fSpaceIDMap.SpaceIDFor(parentID, offset);
133 fPartitionList->AddSpace(parentID, id, offset, size);
138 PartitionListView* fPartitionList;
139 int32& fDiskCount;
140 SpaceIDMap& fSpaceIDMap;
141 BDiskDevice* fLastPreparedDevice;
145 class MountAllVisitor : public BDiskDeviceVisitor {
146 public:
147 MountAllVisitor()
151 virtual bool Visit(BDiskDevice* device)
153 if (device->ContainsFileSystem())
154 device->Mount();
156 return false; // Don't stop yet!
159 virtual bool Visit(BPartition* partition, int32 level)
161 partition->Mount();
162 return false; // Don't stop yet!
165 private:
166 PartitionListView* fPartitionList;
170 class ModificationPreparer {
171 public:
172 ModificationPreparer(BDiskDevice* disk)
174 fDisk(disk),
175 fModificationStatus(fDisk->PrepareModifications())
178 ~ModificationPreparer()
180 if (fModificationStatus == B_OK)
181 fDisk->CancelModifications();
183 status_t ModificationStatus() const
185 return fModificationStatus;
187 status_t CommitModifications()
189 status_t status = fDisk->CommitModifications();
190 if (status == B_OK)
191 fModificationStatus = B_ERROR;
193 return status;
196 private:
197 BDiskDevice* fDisk;
198 status_t fModificationStatus;
202 // #pragma mark -
205 MainWindow::MainWindow()
207 BWindow(BRect(50, 50, 600, 500), B_TRANSLATE_SYSTEM_NAME("DriveSetup"),
208 B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS),
209 fCurrentDisk(NULL),
210 fCurrentPartitionID(-1),
211 fSpaceIDMap()
213 fMenuBar = new BMenuBar(Bounds(), "root menu");
215 // create all the menu items
216 fWipeMenuItem = new BMenuItem(B_TRANSLATE("Wipe (not implemented)"),
217 new BMessage(MSG_FORMAT));
218 fEjectMenuItem = new BMenuItem(B_TRANSLATE("Eject"),
219 new BMessage(MSG_EJECT), 'E');
220 fSurfaceTestMenuItem = new BMenuItem(
221 B_TRANSLATE("Surface test (not implemented)"),
222 new BMessage(MSG_SURFACE_TEST));
223 fRescanMenuItem = new BMenuItem(B_TRANSLATE("Rescan"),
224 new BMessage(MSG_RESCAN));
226 fCreateMenuItem = new BMenuItem(B_TRANSLATE("Create" B_UTF8_ELLIPSIS),
227 new BMessage(MSG_CREATE), 'C');
228 fChangeMenuItem = new BMenuItem(
229 B_TRANSLATE("Change parameters" B_UTF8_ELLIPSIS),
230 new BMessage(MSG_CHANGE));
231 fDeleteMenuItem = new BMenuItem(B_TRANSLATE("Delete"),
232 new BMessage(MSG_DELETE), 'D');
234 fMountMenuItem = new BMenuItem(B_TRANSLATE("Mount"),
235 new BMessage(MSG_MOUNT), 'M');
236 fUnmountMenuItem = new BMenuItem(B_TRANSLATE("Unmount"),
237 new BMessage(MSG_UNMOUNT), 'U');
238 fMountAllMenuItem = new BMenuItem(B_TRANSLATE("Mount all"),
239 new BMessage(MSG_MOUNT_ALL), 'M', B_SHIFT_KEY);
241 // Disk menu
242 fDiskMenu = new BMenu(B_TRANSLATE("Disk"));
244 // fDiskMenu->AddItem(fWipeMenuItem);
245 fDiskInitMenu = new BMenu(B_TRANSLATE("Initialize"));
246 fDiskMenu->AddItem(fDiskInitMenu);
248 fDiskMenu->AddSeparatorItem();
250 fDiskMenu->AddItem(fEjectMenuItem);
251 // fDiskMenu->AddItem(fSurfaceTestMenuItem);
252 fDiskMenu->AddItem(fRescanMenuItem);
254 fMenuBar->AddItem(fDiskMenu);
256 // Parition menu
257 fPartitionMenu = new BMenu(B_TRANSLATE("Partition"));
258 fPartitionMenu->AddItem(fCreateMenuItem);
260 fFormatMenu = new BMenu(B_TRANSLATE("Format"));
261 fPartitionMenu->AddItem(fFormatMenu);
263 fPartitionMenu->AddItem(fChangeMenuItem);
264 fPartitionMenu->AddItem(fDeleteMenuItem);
266 fPartitionMenu->AddSeparatorItem();
268 fPartitionMenu->AddItem(fMountMenuItem);
269 fPartitionMenu->AddItem(fUnmountMenuItem);
271 fPartitionMenu->AddSeparatorItem();
273 fPartitionMenu->AddItem(fMountAllMenuItem);
274 fMenuBar->AddItem(fPartitionMenu);
276 AddChild(fMenuBar);
278 // Partition / Drives context menu
279 fContextMenu = new BPopUpMenu("Partition", false, false);
280 fCreateContextMenuItem = new BMenuItem(B_TRANSLATE("Create" B_UTF8_ELLIPSIS),
281 new BMessage(MSG_CREATE), 'C');
282 fChangeContextMenuItem = new BMenuItem(
283 B_TRANSLATE("Change parameters" B_UTF8_ELLIPSIS),
284 new BMessage(MSG_CHANGE));
285 fDeleteContextMenuItem = new BMenuItem(B_TRANSLATE("Delete"),
286 new BMessage(MSG_DELETE), 'D');
287 fMountContextMenuItem = new BMenuItem(B_TRANSLATE("Mount"),
288 new BMessage(MSG_MOUNT), 'M');
289 fUnmountContextMenuItem = new BMenuItem(B_TRANSLATE("Unmount"),
290 new BMessage(MSG_UNMOUNT), 'U');
291 fFormatContextMenuItem = new BMenu(B_TRANSLATE("Format"));
293 fContextMenu->AddItem(fCreateContextMenuItem);
294 fContextMenu->AddItem(fFormatContextMenuItem);
295 fContextMenu->AddItem(fChangeContextMenuItem);
296 fContextMenu->AddItem(fDeleteContextMenuItem);
297 fContextMenu->AddSeparatorItem();
298 fContextMenu->AddItem(fMountContextMenuItem);
299 fContextMenu->AddItem(fUnmountContextMenuItem);
300 fContextMenu->SetTargetForItems(this);
302 // add DiskView
303 BRect r(Bounds());
304 r.top = fMenuBar->Frame().bottom + 1;
305 r.bottom = floorf(r.top + r.Height() * 0.33);
306 fDiskView = new DiskView(r, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
307 fSpaceIDMap);
308 AddChild(fDiskView);
310 // add PartitionListView
311 r.top = r.bottom + 2;
312 r.bottom = Bounds().bottom;
313 r.InsetBy(-1, -1);
314 fListView = new PartitionListView(r, B_FOLLOW_ALL);
315 AddChild(fListView);
317 // configure PartitionListView
318 fListView->SetSelectionMode(B_SINGLE_SELECTION_LIST);
319 fListView->SetSelectionMessage(new BMessage(MSG_PARTITION_ROW_SELECTED));
320 fListView->SetTarget(this);
321 fListView->MakeFocus(true);
323 status_t status = fDiskDeviceRoster.StartWatching(BMessenger(this));
324 if (status != B_OK) {
325 fprintf(stderr, "Failed to start watching for device changes: %s\n",
326 strerror(status));
329 // visit all disks in the system and show their contents
330 _ScanDrives();
332 if (!be_roster->IsRunning(kDeskbarSignature))
333 SetFlags(Flags() | B_NOT_MINIMIZABLE);
337 MainWindow::~MainWindow()
339 BDiskDeviceRoster().StopWatching(this);
340 delete fCurrentDisk;
341 delete fContextMenu;
345 void
346 MainWindow::MessageReceived(BMessage* message)
348 switch (message->what) {
349 case MSG_MOUNT_ALL:
350 _MountAll();
351 break;
352 case MSG_MOUNT:
353 _Mount(fCurrentDisk, fCurrentPartitionID);
354 break;
355 case MSG_UNMOUNT:
356 _Unmount(fCurrentDisk, fCurrentPartitionID);
357 break;
359 case MSG_FORMAT:
360 printf("MSG_FORMAT\n");
361 break;
363 case MSG_CREATE:
364 _Create(fCurrentDisk, fCurrentPartitionID);
365 break;
367 case MSG_INITIALIZE: {
368 BString diskSystemName;
369 if (message->FindString("disk system", &diskSystemName) != B_OK)
370 break;
371 _Initialize(fCurrentDisk, fCurrentPartitionID, diskSystemName);
372 break;
375 case MSG_CHANGE:
376 _ChangeParameters(fCurrentDisk, fCurrentPartitionID);
377 break;
379 case MSG_DELETE:
380 _Delete(fCurrentDisk, fCurrentPartitionID);
381 break;
383 case MSG_EJECT:
384 // TODO: completely untested, especially interesting
385 // if partition list behaves when partitions disappear
386 if (fCurrentDisk) {
387 // TODO: only if no partitions are mounted anymore?
388 fCurrentDisk->Eject(true);
389 _ScanDrives();
391 break;
392 case MSG_SURFACE_TEST:
393 printf("MSG_SURFACE_TEST\n");
394 break;
396 // TODO: this could probably be done better!
397 case B_DEVICE_UPDATE:
398 printf("B_DEVICE_UPDATE\n");
399 case MSG_RESCAN:
400 _ScanDrives();
401 break;
403 case MSG_PARTITION_ROW_SELECTED: {
404 // selection of partitions via list view
405 _AdaptToSelectedPartition();
407 BPoint where;
408 uint32 buttons;
409 fListView->GetMouse(&where, &buttons);
410 where.x += 2; // to prevent occasional select
411 if (buttons & B_SECONDARY_MOUSE_BUTTON)
412 fContextMenu->Go(fListView->ConvertToScreen(where),
413 true, false, true);
414 break;
416 case MSG_SELECTED_PARTITION_ID: {
417 // selection of partitions via disk view
418 partition_id id;
419 if (message->FindInt32("partition_id", &id) == B_OK) {
420 if (BRow* row = fListView->FindRow(id)) {
421 fListView->DeselectAll();
422 fListView->AddToSelection(row);
423 _AdaptToSelectedPartition();
426 BPoint where;
427 uint32 buttons;
428 fListView->GetMouse(&where, &buttons);
429 where.x += 2; // to prevent occasional select
430 if (buttons & B_SECONDARY_MOUSE_BUTTON)
431 fContextMenu->Go(fListView->ConvertToScreen(where),
432 true, false, true);
433 break;
436 case MSG_UPDATE_ZOOM_LIMITS:
437 _UpdateWindowZoomLimits();
438 break;
440 default:
441 BWindow::MessageReceived(message);
442 break;
447 bool
448 MainWindow::QuitRequested()
450 // TODO: ask about any unsaved changes
451 be_app->PostMessage(B_QUIT_REQUESTED);
452 Hide();
453 return false;
457 // #pragma mark -
460 status_t
461 MainWindow::StoreSettings(BMessage* archive) const
463 if (archive->ReplaceRect("window frame", Frame()) < B_OK)
464 archive->AddRect("window frame", Frame());
466 BMessage columnSettings;
467 fListView->SaveState(&columnSettings);
468 if (archive->ReplaceMessage("column settings", &columnSettings) < B_OK)
469 archive->AddMessage("column settings", &columnSettings);
471 return B_OK;
475 status_t
476 MainWindow::RestoreSettings(BMessage* archive)
478 BRect frame;
479 if (archive->FindRect("window frame", &frame) == B_OK) {
480 BScreen screen(this);
481 if (frame.Intersects(screen.Frame())) {
482 MoveTo(frame.LeftTop());
483 ResizeTo(frame.Width(), frame.Height());
487 BMessage columnSettings;
488 if (archive->FindMessage("column settings", &columnSettings) == B_OK)
489 fListView->LoadState(&columnSettings);
491 return B_OK;
495 void
496 MainWindow::ApplyDefaultSettings()
498 if (!Lock())
499 return;
501 fListView->ResizeAllColumnsToPreferred();
503 // Adjust window size for convenience
504 BScreen screen(this);
505 float windowWidth = Frame().Width();
506 float windowHeight = Frame().Height();
508 float enlargeWidthBy = fListView->PreferredSize().width
509 - fListView->Bounds().Width();
510 float enlargeHeightBy = fListView->PreferredSize().height
511 - fListView->Bounds().Height();
513 if (enlargeWidthBy > 0.0f)
514 windowWidth += enlargeWidthBy;
515 if (enlargeHeightBy > 0.0f)
516 windowHeight += enlargeHeightBy;
518 if (windowWidth > screen.Frame().Width() - 20.0f)
519 windowWidth = screen.Frame().Width() - 20.0f;
520 if (windowHeight > screen.Frame().Height() - 20.0f)
521 windowHeight = screen.Frame().Height() - 20.0f;
523 ResizeTo(windowWidth, windowHeight);
524 CenterOnScreen();
526 Unlock();
530 // #pragma mark -
533 void
534 MainWindow::_ScanDrives()
536 fSpaceIDMap.Clear();
537 int32 diskCount = 0;
538 ListPopulatorVisitor driveVisitor(fListView, diskCount, fSpaceIDMap);
539 fDiskDeviceRoster.VisitEachPartition(&driveVisitor);
540 fDiskView->SetDiskCount(diskCount);
542 // restore selection
543 PartitionListRow* previousSelection
544 = fListView->FindRow(fCurrentPartitionID);
545 if (previousSelection) {
546 fListView->AddToSelection(previousSelection);
547 _UpdateMenus(fCurrentDisk, fCurrentPartitionID,
548 previousSelection->ParentID());
549 fDiskView->ForceUpdate();
550 } else {
551 _UpdateMenus(NULL, -1, -1);
554 PostMessage(MSG_UPDATE_ZOOM_LIMITS);
558 // #pragma mark -
561 void
562 MainWindow::_AdaptToSelectedPartition()
564 partition_id diskID = -1;
565 partition_id partitionID = -1;
566 partition_id parentID = -1;
568 BRow* _selectedRow = fListView->CurrentSelection();
569 if (_selectedRow) {
570 // go up to top level row
571 BRow* _topLevelRow = _selectedRow;
572 BRow* parent = NULL;
573 while (fListView->FindParent(_topLevelRow, &parent, NULL))
574 _topLevelRow = parent;
576 PartitionListRow* topLevelRow
577 = dynamic_cast<PartitionListRow*>(_topLevelRow);
578 PartitionListRow* selectedRow
579 = dynamic_cast<PartitionListRow*>(_selectedRow);
581 if (topLevelRow)
582 diskID = topLevelRow->ID();
583 if (selectedRow) {
584 partitionID = selectedRow->ID();
585 parentID = selectedRow->ParentID();
589 _SetToDiskAndPartition(diskID, partitionID, parentID);
593 void
594 MainWindow::_SetToDiskAndPartition(partition_id disk, partition_id partition,
595 partition_id parent)
597 //printf("MainWindow::_SetToDiskAndPartition(disk: %ld, partition: %ld, "
598 // "parent: %ld)\n", disk, partition, parent);
600 BDiskDevice* oldDisk = NULL;
601 if (!fCurrentDisk || fCurrentDisk->ID() != disk) {
602 oldDisk = fCurrentDisk;
603 fCurrentDisk = NULL;
604 if (disk >= 0) {
605 BDiskDevice* newDisk = new BDiskDevice();
606 status_t status = newDisk->SetTo(disk);
607 if (status != B_OK) {
608 printf("error switching disks: %s\n", strerror(status));
609 delete newDisk;
610 } else
611 fCurrentDisk = newDisk;
615 fCurrentPartitionID = partition;
617 fDiskView->SetDisk(fCurrentDisk, fCurrentPartitionID);
618 _UpdateMenus(fCurrentDisk, fCurrentPartitionID, parent);
620 delete oldDisk;
624 void
625 MainWindow::_UpdateMenus(BDiskDevice* disk,
626 partition_id selectedPartition, partition_id parentID)
628 while (BMenuItem* item = fFormatMenu->RemoveItem((int32)0))
629 delete item;
630 while (BMenuItem* item = fDiskInitMenu->RemoveItem((int32)0))
631 delete item;
632 while (BMenuItem* item = fFormatContextMenuItem->RemoveItem((int32)0))
633 delete item;
635 fCreateMenuItem->SetEnabled(false);
636 fUnmountMenuItem->SetEnabled(false);
637 fDiskInitMenu->SetEnabled(false);
638 fFormatMenu->SetEnabled(false);
640 fCreateContextMenuItem->SetEnabled(false);
641 fUnmountContextMenuItem->SetEnabled(false);
642 fFormatContextMenuItem->SetEnabled(false);
644 if (!disk) {
645 fWipeMenuItem->SetEnabled(false);
646 fEjectMenuItem->SetEnabled(false);
647 fSurfaceTestMenuItem->SetEnabled(false);
648 } else {
649 // fWipeMenuItem->SetEnabled(true);
650 fWipeMenuItem->SetEnabled(false);
651 fEjectMenuItem->SetEnabled(disk->IsRemovableMedia());
652 // fSurfaceTestMenuItem->SetEnabled(true);
653 fSurfaceTestMenuItem->SetEnabled(false);
655 // Create menu and items
656 BPartition* parentPartition = NULL;
657 if (selectedPartition <= -2) {
658 // a partitionable space item is selected
659 parentPartition = disk->FindDescendant(parentID);
662 if (parentPartition && parentPartition->ContainsPartitioningSystem()) {
663 fCreateMenuItem->SetEnabled(true);
664 fCreateContextMenuItem->SetEnabled(true);
666 bool prepared = disk->PrepareModifications() == B_OK;
668 fFormatMenu->SetEnabled(prepared);
669 fDeleteMenuItem->SetEnabled(prepared);
670 fChangeMenuItem->SetEnabled(prepared);
672 fChangeContextMenuItem->SetEnabled(prepared);
673 fDeleteContextMenuItem->SetEnabled(prepared);
674 fFormatContextMenuItem->SetEnabled(prepared);
676 BPartition* partition = disk->FindDescendant(selectedPartition);
678 BDiskSystem diskSystem;
679 fDiskDeviceRoster.RewindDiskSystems();
680 while (fDiskDeviceRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
681 if (!diskSystem.SupportsInitializing())
682 continue;
684 BMessage* message = new BMessage(MSG_INITIALIZE);
685 message->AddInt32("parent id", parentID);
686 message->AddString("disk system", diskSystem.PrettyName());
688 BString label = diskSystem.PrettyName();
689 label << B_UTF8_ELLIPSIS;
690 BMenuItem* item = new BMenuItem(label.String(), message);
692 // TODO: Very unintuitive that we have to use PrettyName (vs Name)
693 item->SetEnabled(partition != NULL
694 && partition->CanInitialize(diskSystem.PrettyName()));
696 if (disk->ID() == selectedPartition
697 && !diskSystem.IsFileSystem()) {
698 // Disk is selected, and DiskSystem is a partition map
699 fDiskInitMenu->AddItem(item);
700 } else if (diskSystem.IsFileSystem()) {
701 // Otherwise a filesystem
702 fFormatMenu->AddItem(item);
704 // Context menu
705 BMessage* message = new BMessage(MSG_INITIALIZE);
706 message->AddInt32("parent id", parentID);
707 message->AddString("disk system", diskSystem.PrettyName());
708 BMenuItem* popUpItem = new BMenuItem(label.String(), message);
709 popUpItem->SetEnabled(partition != NULL
710 && partition->CanInitialize(diskSystem.PrettyName()));
711 fFormatContextMenuItem->AddItem(popUpItem);
712 fFormatContextMenuItem->SetTargetForItems(this);
716 // Mount items
717 if (partition != NULL) {
718 bool notMountedAndWritable = !partition->IsMounted()
719 && !partition->IsReadOnly()
720 && partition->Device()->HasMedia();
722 fFormatMenu->SetEnabled(notMountedAndWritable
723 && fFormatMenu->CountItems() > 0);
725 fDiskInitMenu->SetEnabled(notMountedAndWritable
726 && partition->IsDevice()
727 && fDiskInitMenu->CountItems() > 0);
729 fChangeMenuItem->SetEnabled(notMountedAndWritable);
731 fDeleteMenuItem->SetEnabled(notMountedAndWritable
732 && !partition->IsDevice());
734 fMountMenuItem->SetEnabled(!partition->IsMounted());
736 fFormatContextMenuItem->SetEnabled(notMountedAndWritable
737 && fFormatContextMenuItem->CountItems() > 0);
738 fChangeContextMenuItem->SetEnabled(notMountedAndWritable);
739 fDeleteContextMenuItem->SetEnabled(notMountedAndWritable
740 && !partition->IsDevice());
741 fMountContextMenuItem->SetEnabled(notMountedAndWritable);
743 bool unMountable = false;
744 if (partition->IsMounted()) {
745 // see if this partition is the boot volume
746 BVolume volume;
747 BVolume bootVolume;
748 if (BVolumeRoster().GetBootVolume(&bootVolume) == B_OK
749 && partition->GetVolume(&volume) == B_OK) {
750 unMountable = volume != bootVolume;
751 } else
752 unMountable = true;
754 fUnmountMenuItem->SetEnabled(unMountable);
755 fUnmountContextMenuItem->SetEnabled(unMountable);
756 } else {
757 fDeleteMenuItem->SetEnabled(false);
758 fChangeMenuItem->SetEnabled(false);
759 fMountMenuItem->SetEnabled(false);
760 fFormatMenu->SetEnabled(false);
761 fDiskInitMenu->SetEnabled(false);
763 fDeleteContextMenuItem->SetEnabled(false);
764 fChangeContextMenuItem->SetEnabled(false);
765 fMountContextMenuItem->SetEnabled(false);
766 fFormatContextMenuItem->SetEnabled(false);
769 if (prepared)
770 disk->CancelModifications();
772 fMountAllMenuItem->SetEnabled(true);
774 if (selectedPartition < 0) {
775 fDeleteMenuItem->SetEnabled(false);
776 fChangeMenuItem->SetEnabled(false);
777 fMountMenuItem->SetEnabled(false);
779 fDeleteContextMenuItem->SetEnabled(false);
780 fChangeContextMenuItem->SetEnabled(false);
781 fMountContextMenuItem->SetEnabled(false);
786 void
787 MainWindow::_DisplayPartitionError(BString _message,
788 const BPartition* partition, status_t error) const
790 char message[1024];
792 if (partition && _message.FindFirst("%s") >= 0) {
793 BString name;
794 name << "\"" << partition->ContentName() << "\"";
795 snprintf(message, sizeof(message), _message.String(), name.String());
796 } else {
797 _message.ReplaceAll("%s", "");
798 snprintf(message, sizeof(message), _message.String());
801 if (error < B_OK) {
802 BString helper = message;
803 const char* errorString
804 = B_TRANSLATE_COMMENT("Error: ", "in any error alert");
805 snprintf(message, sizeof(message), "%s\n\n%s%s", helper.String(),
806 errorString, strerror(error));
809 BAlert* alert = new BAlert("error", message, B_TRANSLATE("OK"), NULL, NULL,
810 B_WIDTH_FROM_WIDEST, error < B_OK ? B_STOP_ALERT : B_INFO_ALERT);
811 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
812 alert->Go(NULL);
816 // #pragma mark -
819 void
820 MainWindow::_Mount(BDiskDevice* disk, partition_id selectedPartition)
822 if (!disk || selectedPartition < 0) {
823 _DisplayPartitionError(B_TRANSLATE("You need to select a partition "
824 "entry from the list."));
825 return;
828 BPartition* partition = disk->FindDescendant(selectedPartition);
829 if (!partition) {
830 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
831 "partition by ID."));
832 return;
835 if (!partition->IsMounted()) {
836 status_t status = partition->Mount();
837 if (status != B_OK) {
838 _DisplayPartitionError(B_TRANSLATE("Could not mount partition %s."),
839 partition, status);
840 } else {
841 // successful mount, adapt to the changes
842 _ScanDrives();
844 } else {
845 _DisplayPartitionError(
846 B_TRANSLATE("The partition %s is already mounted."), partition);
851 void
852 MainWindow::_Unmount(BDiskDevice* disk, partition_id selectedPartition)
854 if (!disk || selectedPartition < 0) {
855 _DisplayPartitionError(B_TRANSLATE("You need to select a partition "
856 "entry from the list."));
857 return;
860 BPartition* partition = disk->FindDescendant(selectedPartition);
861 if (!partition) {
862 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
863 "partition by ID."));
864 return;
867 if (partition->IsMounted()) {
868 BPath path;
869 partition->GetMountPoint(&path);
870 status_t status = partition->Unmount();
871 if (status != B_OK) {
872 BString message = B_TRANSLATE("Could not unmount partition");
873 message << " \"" << partition->ContentName() << "\":\n\t"
874 << strerror(status) << "\n\n"
875 << B_TRANSLATE("Should unmounting be forced?\n\n"
876 "Note: If an application is currently writing to the volume, "
877 "unmounting it now might result in loss of data.\n");
879 BAlert* alert = new BAlert(B_TRANSLATE("Force unmount"), message,
880 B_TRANSLATE("Cancel"), B_TRANSLATE("Force unmount"), NULL,
881 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
882 alert->SetShortcut(0, B_ESCAPE);
884 if (alert->Go() == 1)
885 status = partition->Unmount(B_FORCE_UNMOUNT);
886 else
887 return;
890 if (status != B_OK) {
891 _DisplayPartitionError(
892 B_TRANSLATE("Could not unmount partition %s."),
893 partition, status);
894 } else {
895 if (dev_for_path(path.Path()) == dev_for_path("/"))
896 rmdir(path.Path());
897 // successful unmount, adapt to the changes
898 _ScanDrives();
900 } else {
901 _DisplayPartitionError(
902 B_TRANSLATE("The partition %s is already unmounted."),
903 partition);
908 void
909 MainWindow::_MountAll()
911 MountAllVisitor visitor;
912 fDiskDeviceRoster.VisitEachPartition(&visitor);
916 // #pragma mark -
919 void
920 MainWindow::_Initialize(BDiskDevice* disk, partition_id selectedPartition,
921 const BString& diskSystemName)
923 if (!disk || selectedPartition < 0) {
924 _DisplayPartitionError(B_TRANSLATE("You need to select a partition "
925 "entry from the list."));
926 return;
929 if (disk->IsReadOnly()) {
930 _DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
931 return;
934 BPartition* partition = disk->FindDescendant(selectedPartition);
935 if (!partition) {
936 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
937 "partition by ID."));
938 return;
941 if (partition->IsMounted()) {
942 _DisplayPartitionError(
943 B_TRANSLATE("The partition %s is currently mounted."));
944 // TODO: option to unmount and continue on success to unmount
945 return;
948 BDiskSystem diskSystem;
949 fDiskDeviceRoster.RewindDiskSystems();
950 bool found = false;
951 while (fDiskDeviceRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
952 if (diskSystem.SupportsInitializing()) {
953 if (diskSystemName == diskSystem.PrettyName()) {
954 found = true;
955 break;
960 char message[512];
962 if (!found) {
963 snprintf(message, sizeof(message), B_TRANSLATE("Disk system \"%s\" "
964 "not found!"));
965 _DisplayPartitionError(message);
966 return;
969 if (diskSystem.IsFileSystem()) {
970 if (disk->ID() == selectedPartition) {
971 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
972 "want to format a raw disk? (Most people initialize the disk "
973 "with a partitioning system first) You will be asked "
974 "again before changes are written to the disk."));
975 } else if (partition->ContentName()
976 && strlen(partition->ContentName()) > 0) {
977 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
978 "want to format the partition \"%s\"? You will be asked "
979 "again before changes are written to the disk."),
980 partition->ContentName());
981 } else {
982 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
983 "want to format the partition? You will be asked again "
984 "before changes are written to the disk."));
986 } else {
987 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
988 "want to initialize the selected disk? All data will be lost. "
989 "You will be asked again before changes are written to the "
990 "disk.\n"));
992 BAlert* alert = new BAlert("first notice", message,
993 B_TRANSLATE("Continue"), B_TRANSLATE("Cancel"), NULL,
994 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
995 alert->SetShortcut(1, B_ESCAPE);
996 int32 choice = alert->Go();
998 if (choice == 1)
999 return;
1001 ModificationPreparer modificationPreparer(disk);
1002 status_t status = modificationPreparer.ModificationStatus();
1003 if (status != B_OK) {
1004 _DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1005 "disk for modifications."), NULL, status);
1006 return;
1009 BString name;
1010 BString parameters;
1011 InitParametersPanel* panel = new InitParametersPanel(this, diskSystemName,
1012 partition);
1013 if (panel->Go(name, parameters) != B_OK)
1014 return;
1016 bool supportsName = diskSystem.SupportsContentName();
1017 BString validatedName(name);
1018 status = partition->ValidateInitialize(diskSystem.PrettyName(),
1019 supportsName ? &validatedName : NULL, parameters.String());
1020 if (status != B_OK) {
1021 _DisplayPartitionError(B_TRANSLATE("Validation of the given "
1022 "initialization parameters failed."), partition, status);
1023 return;
1026 BString previousName = partition->ContentName();
1028 status = partition->Initialize(diskSystem.PrettyName(),
1029 supportsName ? validatedName.String() : NULL, parameters.String());
1030 if (status != B_OK) {
1031 _DisplayPartitionError(B_TRANSLATE("Initialization of the partition "
1032 "%s failed. (Nothing has been written to disk.)"), partition,
1033 status);
1034 return;
1037 // Also set the partition name in the partition table if supported
1038 if (partition->CanSetName()
1039 && partition->ValidateSetName(&validatedName) == B_OK) {
1040 partition->SetName(validatedName.String());
1043 // everything looks fine, we are ready to actually write the changes
1044 // to disk
1046 // Warn the user one more time...
1047 if (previousName.Length() > 0) {
1048 if (partition->IsDevice()) {
1049 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
1050 "want to write the changes back to disk now?\n\n"
1051 "All data on the disk %s will be irretrievably lost if you "
1052 "do so!"), previousName.String());
1053 } else {
1054 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
1055 "want to write the changes back to disk now?\n\n"
1056 "All data on the partition %s will be irretrievably lost if you "
1057 "do so!"), previousName.String());
1059 } else {
1060 if (partition->IsDevice()) {
1061 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
1062 "want to write the changes back to disk now?\n\n"
1063 "All data on the selected disk will be irretrievably lost if "
1064 "you do so!"));
1065 } else {
1066 snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
1067 "want to write the changes back to disk now?\n\n"
1068 "All data on the selected partition will be irretrievably lost "
1069 "if you do so!"));
1072 alert = new BAlert("final notice", message,
1073 B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1074 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1075 alert->SetShortcut(1, B_ESCAPE);
1076 choice = alert->Go();
1078 if (choice == 1)
1079 return;
1081 // commit
1082 status = modificationPreparer.CommitModifications();
1084 // The partition pointer is toast now! Use the partition ID to
1085 // retrieve it again.
1086 partition = disk->FindDescendant(selectedPartition);
1088 if (status == B_OK) {
1089 if (diskSystem.IsFileSystem()) {
1090 _DisplayPartitionError(B_TRANSLATE("The partition %s has been "
1091 "successfully formatted.\n"), partition);
1092 } else {
1093 _DisplayPartitionError(B_TRANSLATE("The disk has been "
1094 "successfully initialized.\n"), partition);
1096 } else {
1097 if (diskSystem.IsFileSystem()) {
1098 _DisplayPartitionError(B_TRANSLATE("Failed to format the "
1099 "partition %s!\n"), partition, status);
1100 } else {
1101 _DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
1102 "disk %s!\n"), partition, status);
1106 _ScanDrives();
1110 void
1111 MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition)
1113 if (!disk || selectedPartition > -2) {
1114 _DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1115 "is not empty."));
1116 return;
1119 if (disk->IsReadOnly()) {
1120 _DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1121 return;
1124 PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>(
1125 fListView->CurrentSelection());
1126 if (!currentSelection) {
1127 _DisplayPartitionError(B_TRANSLATE("There was an error acquiring the "
1128 "partition row."));
1129 return;
1132 BPartition* parent = disk->FindDescendant(currentSelection->ParentID());
1133 if (!parent) {
1134 _DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1135 "does not have a parent partition."));
1136 return;
1139 if (!parent->ContainsPartitioningSystem()) {
1140 _DisplayPartitionError(B_TRANSLATE("The selected partition does not "
1141 "contain a partitioning system."));
1142 return;
1145 ModificationPreparer modificationPreparer(disk);
1146 status_t status = modificationPreparer.ModificationStatus();
1147 if (status != B_OK) {
1148 _DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1149 "disk for modifications."), NULL, status);
1150 return;
1153 // get partitioning info
1154 BPartitioningInfo partitioningInfo;
1155 status_t error = parent->GetPartitioningInfo(&partitioningInfo);
1156 if (error != B_OK) {
1157 _DisplayPartitionError(B_TRANSLATE("Could not acquire partitioning "
1158 "information."));
1159 return;
1162 int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
1163 if (spacesCount == 0) {
1164 _DisplayPartitionError(B_TRANSLATE("There's no space on the partition "
1165 "where a child partition could be created."));
1166 return;
1169 BString name, type, parameters;
1170 off_t offset = currentSelection->Offset();
1171 off_t size = currentSelection->Size();
1173 CreateParametersPanel* panel = new CreateParametersPanel(this, parent,
1174 offset, size);
1175 status = panel->Go(offset, size, name, type, parameters);
1176 if (status != B_OK) {
1177 if (status != B_CANCELED) {
1178 _DisplayPartitionError(B_TRANSLATE("The panel could not return "
1179 "successfully."), NULL, status);
1181 return;
1184 status = parent->ValidateCreateChild(&offset, &size, type.String(),
1185 &name, parameters.String());
1187 if (status != B_OK) {
1188 _DisplayPartitionError(B_TRANSLATE("Validation of the given creation "
1189 "parameters failed."), NULL, status);
1190 return;
1193 // Warn the user one more time...
1194 BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1195 "want to write the changes back to disk now?\n\n"
1196 "All data on the partition will be irretrievably lost if you do "
1197 "so!"), B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1198 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1199 alert->SetShortcut(1, B_ESCAPE);
1200 int32 choice = alert->Go();
1202 if (choice == 1)
1203 return;
1205 status = parent->CreateChild(offset, size, type.String(), name.String(),
1206 parameters.String());
1208 if (status != B_OK) {
1209 _DisplayPartitionError(B_TRANSLATE("Creation of the partition has "
1210 "failed."), NULL, status);
1211 return;
1214 // commit
1215 status = modificationPreparer.CommitModifications();
1217 if (status != B_OK) {
1218 _DisplayPartitionError(B_TRANSLATE("Failed to format the "
1219 "partition. No changes have been written to disk."), NULL, status);
1220 return;
1223 // The disk layout has changed, update disk information
1224 bool updated;
1225 status = disk->Update(&updated);
1227 _ScanDrives();
1228 fDiskView->ForceUpdate();
1232 void
1233 MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition)
1235 if (!disk || selectedPartition < 0) {
1236 _DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1237 "entry from the list."));
1238 return;
1241 if (disk->IsReadOnly()) {
1242 _DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1243 return;
1246 BPartition* partition = disk->FindDescendant(selectedPartition);
1247 if (!partition) {
1248 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1249 "partition by ID."));
1250 return;
1253 BPartition* parent = partition->Parent();
1254 if (!parent) {
1255 _DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1256 "does not have a parent partition."));
1257 return;
1260 ModificationPreparer modificationPreparer(disk);
1261 status_t status = modificationPreparer.ModificationStatus();
1262 if (status != B_OK) {
1263 _DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1264 "disk for modifications."), NULL, status);
1265 return;
1268 if (!parent->CanDeleteChild(partition->Index())) {
1269 _DisplayPartitionError(
1270 B_TRANSLATE("Cannot delete the selected partition."));
1271 return;
1274 // Warn the user one more time...
1275 BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1276 "want to delete the selected partition?\n\n"
1277 "All data on the partition will be irretrievably lost if you "
1278 "do so!"), B_TRANSLATE("Delete partition"), B_TRANSLATE("Cancel"), NULL,
1279 B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1280 alert->SetShortcut(1, B_ESCAPE);
1281 int32 choice = alert->Go();
1283 if (choice == 1)
1284 return;
1286 status = parent->DeleteChild(partition->Index());
1287 if (status != B_OK) {
1288 _DisplayPartitionError(B_TRANSLATE("Could not delete the selected "
1289 "partition."), NULL, status);
1290 return;
1293 status = modificationPreparer.CommitModifications();
1295 if (status != B_OK) {
1296 _DisplayPartitionError(B_TRANSLATE("Failed to delete the partition. "
1297 "No changes have been written to disk."), NULL, status);
1298 return;
1301 _ScanDrives();
1302 fDiskView->ForceUpdate();
1306 void
1307 MainWindow::_ChangeParameters(BDiskDevice* disk, partition_id selectedPartition)
1309 if (disk == NULL || selectedPartition < 0) {
1310 _DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1311 "entry from the list."));
1312 return;
1315 if (disk->IsReadOnly()) {
1316 _DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1317 return;
1320 BPartition* partition = disk->FindDescendant(selectedPartition);
1321 if (partition == NULL) {
1322 _DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1323 "partition by ID."));
1324 return;
1327 ModificationPreparer modificationPreparer(disk);
1328 status_t status = modificationPreparer.ModificationStatus();
1329 if (status != B_OK) {
1330 _DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1331 "disk for modifications."), NULL, status);
1332 return;
1335 ChangeParametersPanel* panel = new ChangeParametersPanel(this, partition);
1337 BString name, type, parameters;
1338 status = panel->Go(name, type, parameters);
1339 if (status != B_OK) {
1340 if (status != B_CANCELED) {
1341 _DisplayPartitionError(B_TRANSLATE("The panel experienced a "
1342 "problem!"), NULL, status);
1344 // TODO: disk systems without an editor and support for name/type
1345 // changing will return B_CANCELED here -- we need to check this
1346 // before, and disable the menu entry instead
1347 return;
1350 if (partition->CanSetType())
1351 status = partition->ValidateSetType(type.String());
1352 if (status == B_OK && partition->CanSetName())
1353 status = partition->ValidateSetName(&name);
1354 if (status != B_OK) {
1355 _DisplayPartitionError(B_TRANSLATE("Validation of the given parameters "
1356 "failed."));
1357 return;
1360 // Warn the user one more time...
1361 BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1362 "want to change parameters of the selected partition?\n\n"
1363 "The partition may no longer be recognized by other operating systems "
1364 "anymore!"), B_TRANSLATE("Change parameters"), B_TRANSLATE("Cancel"),
1365 NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1366 alert->SetShortcut(1, B_ESCAPE);
1367 int32 choice = alert->Go();
1369 if (choice == 1)
1370 return;
1372 if (partition->CanSetType())
1373 status = partition->SetType(type.String());
1374 if (status == B_OK && partition->CanSetName())
1375 status = partition->SetName(name.String());
1376 if (status == B_OK && partition->CanEditParameters())
1377 status = partition->SetParameters(parameters.String());
1379 if (status != B_OK) {
1380 _DisplayPartitionError(
1381 B_TRANSLATE("Could not change the parameters of the selected "
1382 "partition."), NULL, status);
1383 return;
1386 status = modificationPreparer.CommitModifications();
1388 if (status != B_OK) {
1389 _DisplayPartitionError(B_TRANSLATE("Failed to change the parameters "
1390 "of the partition. No changes have been written to disk."), NULL,
1391 status);
1392 return;
1395 _ScanDrives();
1396 fDiskView->ForceUpdate();
1400 float
1401 MainWindow::_ColumnListViewHeight(BColumnListView* list, BRow* currentRow)
1403 float height = 0;
1404 int32 rows = list->CountRows(currentRow);
1405 for (int32 i = 0; i < rows; i++) {
1406 BRow* row = list->RowAt(i, currentRow);
1407 height += row->Height() + 1;
1408 if (row->IsExpanded() && list->CountRows(row) > 0)
1409 height += _ColumnListViewHeight(list, row);
1411 return height;
1415 void
1416 MainWindow::_UpdateWindowZoomLimits()
1418 float maxHeight = 0;
1419 int32 numColumns = fListView->CountColumns();
1420 BRow* parentRow = NULL;
1421 BColumn* column = NULL;
1423 maxHeight += _ColumnListViewHeight(fListView, NULL);
1425 float maxWidth = fListView->LatchWidth();
1426 for (int32 i = 0; i < numColumns; i++) {
1427 column = fListView->ColumnAt(i);
1428 maxWidth += column->Width();
1431 parentRow = fListView->RowAt(0, NULL);
1432 maxHeight += B_H_SCROLL_BAR_HEIGHT;
1433 maxHeight += 1.5 * parentRow->Height(); // the label row
1434 maxHeight += fDiskView->Bounds().Height();
1435 maxHeight += fMenuBar->Bounds().Height();
1436 maxWidth += 1.5 * B_V_SCROLL_BAR_WIDTH; // scroll bar & borders
1438 SetZoomLimits(maxWidth, maxHeight);