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