xref: /haiku/src/apps/drivesetup/MainWindow.cpp (revision 9563f44bfd75799f67201067483694e82b5779b9)
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 	status_t ret = fDDRoster.StartWatching(BMessenger(this));
233 	if (ret != B_OK) {
234 		fprintf(stderr, "Failed to start watching for device changes: %s\n",
235 			strerror(ret));
236 	}
237 
238 	// visit all disks in the system and show their contents
239 	_ScanDrives();
240 
241 	if (!be_roster->IsRunning(kDeskbarSignature))
242 		SetFlags(Flags() | B_NOT_MINIMIZABLE);
243 }
244 
245 
246 MainWindow::~MainWindow()
247 {
248 	BDiskDeviceRoster().StopWatching(this);
249 	delete fCurrentDisk;
250 }
251 
252 
253 void
254 MainWindow::MessageReceived(BMessage* message)
255 {
256 	switch (message->what) {
257 		case MSG_MOUNT_ALL:
258 			_MountAll();
259 			break;
260 		case MSG_MOUNT:
261 			_Mount(fCurrentDisk, fCurrentPartitionID);
262 			break;
263 		case MSG_UNMOUNT:
264 			_Unmount(fCurrentDisk, fCurrentPartitionID);
265 			break;
266 
267 		case MSG_FORMAT:
268 			printf("MSG_FORMAT\n");
269 			break;
270 
271 		case MSG_CREATE: {
272 			_Create(fCurrentDisk, fCurrentPartitionID);
273 			break;
274 		}
275 
276 		case MSG_INITIALIZE: {
277 			BString diskSystemName;
278 			if (message->FindString("disk system", &diskSystemName) != B_OK)
279 				break;
280 			_Initialize(fCurrentDisk, fCurrentPartitionID, diskSystemName);
281 			break;
282 		}
283 
284 		case MSG_DELETE:
285 			_Delete(fCurrentDisk, fCurrentPartitionID);
286 			break;
287 
288 		case MSG_EJECT:
289 			// TODO: completely untested, especially interesting
290 			// if partition list behaves when partitions disappear
291 			if (fCurrentDisk) {
292 				// TODO: only if no partitions are mounted anymore?
293 				fCurrentDisk->Eject(true);
294 				_ScanDrives();
295 			}
296 			break;
297 		case MSG_SURFACE_TEST:
298 			printf("MSG_SURFACE_TEST\n");
299 			break;
300 
301 		// TODO: this could probably be done better!
302 		case B_DEVICE_UPDATE:
303 			printf("B_DEVICE_UPDATE\n");
304 		case MSG_RESCAN:
305 			_ScanDrives();
306 			break;
307 
308 		case MSG_PARTITION_ROW_SELECTED:
309 			// selection of partitions via list view
310 			_AdaptToSelectedPartition();
311 			break;
312 		case MSG_SELECTED_PARTITION_ID: {
313 			// selection of partitions via disk view
314 			partition_id id;
315 			if (message->FindInt32("partition_id", &id) == B_OK) {
316 				if (BRow* row = fListView->FindRow(id)) {
317 					fListView->DeselectAll();
318 					fListView->AddToSelection(row);
319 					_AdaptToSelectedPartition();
320 				}
321 			}
322 			break;
323 		}
324 
325 		default:
326 			BWindow::MessageReceived(message);
327 			break;
328 	}
329 }
330 
331 
332 bool
333 MainWindow::QuitRequested()
334 {
335 	// TODO: ask about any unsaved changes
336 	be_app->PostMessage(B_QUIT_REQUESTED);
337 	Hide();
338 	return false;
339 }
340 
341 
342 // #pragma mark -
343 
344 
345 status_t
346 MainWindow::StoreSettings(BMessage* archive) const
347 {
348 	if (archive->ReplaceRect("window frame", Frame()) < B_OK)
349 		archive->AddRect("window frame", Frame());
350 
351 	BMessage columnSettings;
352 	fListView->SaveState(&columnSettings);
353 	if (archive->ReplaceMessage("column settings", &columnSettings) < B_OK)
354 		archive->AddMessage("column settings", &columnSettings);
355 
356 	return B_OK;
357 }
358 
359 
360 status_t
361 MainWindow::RestoreSettings(BMessage* archive)
362 {
363 	BRect frame;
364 	if (archive->FindRect("window frame", &frame) == B_OK) {
365 		BScreen screen(this);
366 		if (frame.Intersects(screen.Frame())) {
367 			MoveTo(frame.LeftTop());
368 			ResizeTo(frame.Width(), frame.Height());
369 		}
370 	}
371 
372 	BMessage columnSettings;
373 	if (archive->FindMessage("column settings", &columnSettings) == B_OK)
374 		fListView->LoadState(&columnSettings);
375 
376 	return B_OK;
377 }
378 
379 
380 void
381 MainWindow::ApplyDefaultSettings()
382 {
383 	if (!Lock())
384 		return;
385 
386 	fListView->ResizeAllColumnsToPreferred();
387 
388 	// Adjust window size for convenience
389 	float enlargeBy = fListView->PreferredSize().width
390 		- fListView->Bounds().Width();
391 	if (enlargeBy > 0.0f) {
392 		BScreen screen(this);
393 		float windowWidth = Frame().Width() + enlargeBy;
394 		if (windowWidth > screen.Frame().Width() - 20.0f)
395 			windowWidth = screen.Frame().Width() - 20.0f;
396 
397 		ResizeTo(windowWidth, Frame().Height());
398 	}
399 
400 	CenterOnScreen();
401 
402 	Unlock();
403 }
404 
405 
406 // #pragma mark -
407 
408 
409 void
410 MainWindow::_ScanDrives()
411 {
412 	fSpaceIDMap.Clear();
413 	int32 diskCount = 0;
414 	ListPopulatorVisitor driveVisitor(fListView, diskCount, fSpaceIDMap);
415 	fDDRoster.VisitEachPartition(&driveVisitor);
416 	fDiskView->SetDiskCount(diskCount);
417 
418 	// restore selection
419 	PartitionListRow* previousSelection
420 		= fListView->FindRow(fCurrentPartitionID);
421 	if (previousSelection) {
422 		fListView->AddToSelection(previousSelection);
423 		_UpdateMenus(fCurrentDisk, fCurrentPartitionID,
424 			previousSelection->ParentID());
425 		fDiskView->ForceUpdate();
426 	} else {
427 		_UpdateMenus(NULL, -1, -1);
428 	}
429 }
430 
431 
432 // #pragma mark -
433 
434 
435 void
436 MainWindow::_AdaptToSelectedPartition()
437 {
438 	partition_id diskID = -1;
439 	partition_id partitionID = -1;
440 	partition_id parentID = -1;
441 
442 	BRow* _selectedRow = fListView->CurrentSelection();
443 	if (_selectedRow) {
444 		// go up to top level row
445 		BRow* _topLevelRow = _selectedRow;
446 		BRow* parent = NULL;
447 		while (fListView->FindParent(_topLevelRow, &parent, NULL))
448 			_topLevelRow = parent;
449 
450 		PartitionListRow* topLevelRow
451 			= dynamic_cast<PartitionListRow*>(_topLevelRow);
452 		PartitionListRow* selectedRow
453 			= dynamic_cast<PartitionListRow*>(_selectedRow);
454 
455 		if (topLevelRow)
456 			diskID = topLevelRow->ID();
457 		if (selectedRow) {
458 			partitionID = selectedRow->ID();
459 			parentID = selectedRow->ParentID();
460 		}
461 	}
462 
463 	_SetToDiskAndPartition(diskID, partitionID, parentID);
464 }
465 
466 
467 void
468 MainWindow::_SetToDiskAndPartition(partition_id disk, partition_id partition,
469 	partition_id parent)
470 {
471 printf("MainWindow::_SetToDiskAndPartition(disk: %ld, partition: %ld, "
472 	"parent: %ld)\n", disk, partition, parent);
473 
474 	BDiskDevice* oldDisk = NULL;
475 	if (!fCurrentDisk || fCurrentDisk->ID() != disk) {
476 		oldDisk = fCurrentDisk;
477 		fCurrentDisk = NULL;
478 		if (disk >= 0) {
479 			BDiskDevice* newDisk = new BDiskDevice();
480 			status_t ret = newDisk->SetTo(disk);
481 			if (ret < B_OK) {
482 				printf("error switching disks: %s\n", strerror(ret));
483 				delete newDisk;
484 			} else
485 				fCurrentDisk = newDisk;
486 		}
487 	}
488 
489 	fCurrentPartitionID = partition;
490 
491 	fDiskView->SetDisk(fCurrentDisk, fCurrentPartitionID);
492 	_UpdateMenus(fCurrentDisk, fCurrentPartitionID, parent);
493 
494 	delete oldDisk;
495 }
496 
497 
498 void
499 MainWindow::_UpdateMenus(BDiskDevice* disk,
500 	partition_id selectedPartition, partition_id parentID)
501 {
502 	while (BMenuItem* item = fInitMenu->RemoveItem(0L))
503 		delete item;
504 
505 	if (!disk) {
506 		fFormatMI->SetEnabled(false);
507 		fEjectMI->SetEnabled(false);
508 		fSurfaceTestMI->SetEnabled(false);
509 
510 		fPartitionMenu->SetEnabled(false);
511 	} else {
512 //		fFormatMI->SetEnabled(true);
513 		fFormatMI->SetEnabled(false);
514 		fEjectMI->SetEnabled(disk->IsRemovableMedia());
515 //		fSurfaceTestMI->SetEnabled(true);
516 		fSurfaceTestMI->SetEnabled(false);
517 		fCreateMI->SetEnabled(false);
518 
519 		// Create menu and items
520 		fPartitionMenu->SetEnabled(true);
521 
522 		BPartition* parentPartition = NULL;
523 		if (selectedPartition <= -2)
524 			parentPartition = disk->FindDescendant(parentID);
525 
526 		if (parentPartition && parentPartition->ContainsPartitioningSystem())
527 			fCreateMI->SetEnabled(true);
528 
529 		bool prepared = disk->PrepareModifications() == B_OK;
530 		fInitMenu->SetEnabled(prepared);
531 		fDeleteMI->SetEnabled(prepared);
532 
533 		BPartition* partition = disk->FindDescendant(selectedPartition);
534 		if (partition == NULL)
535 			partition = disk;
536 
537 		BDiskSystem diskSystem;
538 		fDDRoster.RewindDiskSystems();
539 		while (fDDRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
540 			if (!diskSystem.SupportsInitializing())
541 				continue;
542 
543 			if (disk->ID() != selectedPartition
544 				&& disk->ContainsPartitioningSystem()
545 				&& !diskSystem.IsFileSystem()) {
546 				// Do not confuse the user with nested partition maps?
547 				continue;
548 			}
549 			BMessage* message = new BMessage(MSG_INITIALIZE);
550 			message->AddInt32("parent id", parentID);
551 			message->AddString("disk system", diskSystem.PrettyName());
552 
553 			BString label = diskSystem.PrettyName();
554 			label << B_UTF8_ELLIPSIS;
555 			BMenuItem* item = new BMenuItem(label.String(), message);
556 
557 // TODO: Very unintuitive that we have to use the pretty name here!
558 //			item->SetEnabled(partition->CanInitialize(diskSystem.Name()));
559 			item->SetEnabled(partition->CanInitialize(diskSystem.PrettyName()));
560 			fInitMenu->AddItem(item);
561 		}
562 
563 		// Mount items
564 		if (partition) {
565 			fInitMenu->SetEnabled(!partition->IsMounted()
566 				&& !partition->IsReadOnly()
567 				&& partition->Device()->HasMedia());
568 
569 			fDeleteMI->SetEnabled(!partition->IsMounted()
570 				&& !partition->IsDevice());
571 
572 			fMountMI->SetEnabled(!partition->IsMounted());
573 
574 			bool unMountable = false;
575 			if (partition->IsMounted()) {
576 				// see if this partition is the boot volume
577 				BVolume volume;
578 				BVolume bootVolume;
579 				if (BVolumeRoster().GetBootVolume(&bootVolume) == B_OK
580 					&& partition->GetVolume(&volume) == B_OK) {
581 					unMountable = volume != bootVolume;
582 				} else
583 					unMountable = true;
584 			}
585 			fUnmountMI->SetEnabled(unMountable);
586 		} else {
587 			fInitMenu->SetEnabled(false);
588 			fDeleteMI->SetEnabled(false);
589 			fMountMI->SetEnabled(false);
590 			fUnmountMI->SetEnabled(false);
591 		}
592 
593 		if (prepared)
594 			disk->CancelModifications();
595 
596 		fMountAllMI->SetEnabled(true);
597 	}
598 	if (selectedPartition < 0) {
599 		fDeleteMI->SetEnabled(false);
600 		fMountMI->SetEnabled(false);
601 	}
602 }
603 
604 
605 void
606 MainWindow::_DisplayPartitionError(BString _message,
607 	const BPartition* partition, status_t error) const
608 {
609 	char message[1024];
610 
611 	if (partition && _message.FindFirst("%s") >= 0) {
612 		BString name;
613 		name << "\"" << partition->ContentName() << "\"";
614 		sprintf(message, _message.String(), name.String());
615 	} else {
616 		_message.ReplaceAll("%s", "");
617 		sprintf(message, _message.String());
618 	}
619 
620 	if (error < B_OK) {
621 		BString helper = message;
622 		sprintf(message, "%s\n\nError: %s", helper.String(), strerror(error));
623 	}
624 
625 	BAlert* alert = new BAlert("error", message, "Ok", NULL, NULL,
626 		B_WIDTH_FROM_WIDEST, error < B_OK ? B_STOP_ALERT : B_INFO_ALERT);
627 	alert->Go(NULL);
628 }
629 
630 
631 // #pragma mark -
632 
633 
634 void
635 MainWindow::_Mount(BDiskDevice* disk, partition_id selectedPartition)
636 {
637 	if (!disk || selectedPartition < 0) {
638 		_DisplayPartitionError("You need to select a partition "
639 			"entry from the list.");
640 		return;
641 	}
642 
643 	BPartition* partition = disk->FindDescendant(selectedPartition);
644 	if (!partition) {
645 		_DisplayPartitionError("Unable to find the selected partition by id.");
646 		return;
647 	}
648 
649 	if (!partition->IsMounted()) {
650 		status_t ret = partition->Mount();
651 		if (ret < B_OK) {
652 			_DisplayPartitionError("Could not mount partition %s.",
653 				partition, ret);
654 		} else {
655 			// successful mount, adapt to the changes
656 			_ScanDrives();
657 		}
658 	} else {
659 		_DisplayPartitionError("The partition %s is already mounted.",
660 			partition);
661 	}
662 }
663 
664 
665 void
666 MainWindow::_Unmount(BDiskDevice* disk, partition_id selectedPartition)
667 {
668 	if (!disk || selectedPartition < 0) {
669 		_DisplayPartitionError("You need to select a partition "
670 			"entry from the list.");
671 		return;
672 	}
673 
674 	BPartition* partition = disk->FindDescendant(selectedPartition);
675 	if (!partition) {
676 		_DisplayPartitionError("Unable to find the selected partition by id.");
677 		return;
678 	}
679 
680 	if (partition->IsMounted()) {
681 		BPath path;
682 		partition->GetMountPoint(&path);
683 		status_t ret = partition->Unmount();
684 		if (ret < B_OK) {
685 			_DisplayPartitionError("Could not unmount partition %s.",
686 				partition, ret);
687 		} else {
688 			if (dev_for_path(path.Path()) == dev_for_path("/"))
689 				rmdir(path.Path());
690 			// successful unmount, adapt to the changes
691 			_ScanDrives();
692 		}
693 	} else {
694 		_DisplayPartitionError("The partition %s is already unmounted.",
695 			partition);
696 	}
697 }
698 
699 
700 void
701 MainWindow::_MountAll()
702 {
703 	MountAllVisitor visitor;
704 	fDDRoster.VisitEachPartition(&visitor);
705 }
706 
707 
708 // #pragma mark -
709 
710 
711 class ModificationPreparer {
712 public:
713 	ModificationPreparer(BDiskDevice* disk)
714 		:
715 		fDisk(disk),
716 		fModificationStatus(fDisk->PrepareModifications())
717 	{
718 	}
719 	~ModificationPreparer()
720 	{
721 		if (fModificationStatus == B_OK)
722 			fDisk->CancelModifications();
723 	}
724 	status_t ModificationStatus() const
725 	{
726 		return fModificationStatus;
727 	}
728 	status_t CommitModifications()
729 	{
730 		status_t ret = fDisk->CommitModifications();
731 		if (ret == B_OK)
732 			fModificationStatus = B_ERROR;
733 
734 		return ret;
735 	}
736 
737 private:
738 	BDiskDevice*	fDisk;
739 	status_t		fModificationStatus;
740 };
741 
742 
743 void
744 MainWindow::_Initialize(BDiskDevice* disk, partition_id selectedPartition,
745 	const BString& diskSystemName)
746 {
747 	if (!disk || selectedPartition < 0) {
748 		_DisplayPartitionError("You need to select a partition "
749 			"entry from the list.");
750 		return;
751 	}
752 
753 	if (disk->IsReadOnly()) {
754 		_DisplayPartitionError("The selected disk is read-only.");
755 		return;
756 	}
757 
758 	BPartition* partition = disk->FindDescendant(selectedPartition);
759 	if (!partition) {
760 		_DisplayPartitionError("Unable to find the selected partition by id.");
761 		return;
762 	}
763 
764 	if (partition->IsMounted()) {
765 		_DisplayPartitionError("The partition %s is currently mounted.");
766 		// TODO: option to unmount and continue on success to unmount
767 		return;
768 	}
769 
770 	BString message("Are you sure you want to initialize the partition ");
771 	message << "\"" << partition->ContentName();
772 	message << "\"? After entering the initialization parameters, ";
773 	message << "you can abort this operation right before writing ";
774 	message << "changes back to the disk.";
775 	BAlert* alert = new BAlert("first notice", message.String(),
776 		"Continue", "Cancel", NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
777 	int32 choice = alert->Go();
778 
779 	if (choice == 1)
780 		return;
781 
782 	BDiskSystem diskSystem;
783 	fDDRoster.RewindDiskSystems();
784 	bool found = false;
785 	while (fDDRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
786 		if (diskSystem.SupportsInitializing()) {
787 			if (diskSystemName == diskSystem.PrettyName()) {
788 				found = true;
789 				break;
790 			}
791 		}
792 	}
793 
794 	if (!found) {
795 		BString message("Disk system \"");
796 		message << diskSystemName << "\" not found!";
797 		_DisplayPartitionError(message);
798 		return;
799 	}
800 
801 
802 	// allow BFS only, since our parameter string
803 	// construction only handles BFS at the moment
804 	if (diskSystemName != "Be File System"
805 		&& diskSystemName != "Intel Partition Map"
806 		&& diskSystemName != "Intel Extended Partition") {
807 		_DisplayPartitionError("Don't know how to gather initialization "
808 			"parameters for this file system.");
809 		return;
810 	}
811 
812 	ModificationPreparer modificationPreparer(disk);
813 	status_t ret = modificationPreparer.ModificationStatus();
814 	if (ret != B_OK) {
815 		_DisplayPartitionError("There was an error preparing the "
816 			"disk for modifications.", NULL, ret);
817 		return;
818 	}
819 
820 	BString name;
821 	BString parameters;
822 	if (diskSystemName == "Be File System") {
823 		InitParamsPanel* panel = new InitParamsPanel(this, diskSystemName,
824 			partition);
825 		if (panel->Go(name, parameters) == GO_CANCELED)
826 			return;
827 	} else if (diskSystemName == "Intel Partition Map") {
828 		// TODO: parameters?
829 	} else if (diskSystemName == "Intel Extended Partition") {
830 		// TODO: parameters?
831 	}
832 
833 	bool supportsName = diskSystem.SupportsContentName();
834 	BString validatedName(name);
835 	ret = partition->ValidateInitialize(diskSystem.PrettyName(),
836 		supportsName ? &validatedName : NULL, parameters.String());
837 	if (ret != B_OK) {
838 		_DisplayPartitionError("Validation of the given initialization "
839 			"parameters failed.", partition, ret);
840 		return;
841 	}
842 
843 	BString previousName = partition->ContentName();
844 
845 	ret = partition->Initialize(diskSystem.PrettyName(),
846 		supportsName ? validatedName.String() : NULL, parameters.String());
847 	if (ret != B_OK) {
848 		_DisplayPartitionError("Initialization of the partition %s "
849 			"failed. (Nothing has been written to disk.)", partition, ret);
850 		return;
851 	}
852 
853 	// everything looks fine, we are ready to actually write the changes
854 	// to disk
855 
856 	// Warn the user one more time...
857 	message = "Are you sure you want to write the changes back to "
858 		"disk now?\n\n";
859 	if (partition->IsDevice())
860 		message << "All data on the disk";
861 	else
862 		message << "All data on the partition";
863 	if (previousName.Length() > 0)
864 		message << " \"" << previousName << "\"";
865 	message << " will be irrevertably lost if you do so!";
866 	alert = new BAlert("final notice", message.String(),
867 		"Write Changes", "Cancel", NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
868 	choice = alert->Go();
869 
870 	if (choice == 1)
871 		return;
872 
873 	// commit
874 	ret = modificationPreparer.CommitModifications();
875 
876 	// The partition pointer is toast now! Use the partition id to
877 	// retrieve it again.
878 	partition = disk->FindDescendant(selectedPartition);
879 
880 	if (ret == B_OK) {
881 		_DisplayPartitionError("The partition %s has been successfully "
882 			"initialized.\n", partition);
883 	} else {
884 		_DisplayPartitionError("Failed to initialize the partition "
885 			"%s!\n", partition, ret);
886 	}
887 
888 	_ScanDrives();
889 }
890 
891 
892 void
893 MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition)
894 {
895 	if (!disk || selectedPartition > -2) {
896 		_DisplayPartitionError("The currently selected partition is not"
897 			" empty");
898 		return;
899 	}
900 
901 	if (disk->IsReadOnly()) {
902 		_DisplayPartitionError("The selected disk is read-only.");
903 		return;
904 	}
905 
906 	PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>(
907 		fListView->CurrentSelection());
908 	if (!currentSelection) {
909 		_DisplayPartitionError("There was an error acquiring the partition "
910 			"row");
911 		return;
912 	}
913 
914 	BPartition* parent = disk->FindDescendant(currentSelection->ParentID());
915 	if (!parent) {
916 		_DisplayPartitionError("The currently selected partition does not "
917 			"have a parent partition");
918 		return;
919 	}
920 
921 	if (!parent->ContainsPartitioningSystem()) {
922 		_DisplayPartitionError("The selected partition does not contain "
923 			"a partitioning system.\n");
924 		return;
925 	}
926 
927 	ModificationPreparer modificationPreparer(disk);
928 	status_t ret = modificationPreparer.ModificationStatus();
929 	if (ret != B_OK) {
930 		_DisplayPartitionError("There was an error preparing the "
931 			"disk for modifications.", NULL, ret);
932 		return;
933 	}
934 
935 	// get partitioning info
936 	BPartitioningInfo partitioningInfo;
937 	status_t error = parent->GetPartitioningInfo(&partitioningInfo);
938 	if (error != B_OK) {
939 		_DisplayPartitionError("Could not aquire partitioning information.\n");
940 		return;
941 	}
942 
943 	int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
944 	if (spacesCount == 0) {
945 		_DisplayPartitionError("There's no space on the partition where a "
946 			"child partition could be created\n");
947 		return;
948 	}
949 
950 	BString name, type, parameters;
951 	off_t offset = currentSelection->Offset();
952 	off_t size = currentSelection->Size();
953 
954 	CreateParamsPanel* panel = new CreateParamsPanel(this, parent, offset,
955 		size);
956 	if (panel->Go(offset, size, name, type, parameters) == GO_CANCELED)
957 		return;
958 
959 	ret = parent->ValidateCreateChild(&offset, &size, type.String(),
960 		&name, parameters.String());
961 
962 	if (ret != B_OK) {
963 		_DisplayPartitionError("Validation of the given creation "
964 			"parameters failed.");
965 		return;
966 	}
967 
968 	// Warn the user one more time...
969 	BString message = "Are you sure you want to write the changes back to "
970 		"disk now?\n\n";
971 	message << "All data on the partition";
972 	message << " will be irrevertably lost if you do so!";
973 	BAlert* alert = new BAlert("final notice", message.String(),
974 		"Write Changes", "Cancel", NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
975 	int32 choice = alert->Go();
976 
977 	if (choice == 1)
978 		return;
979 
980 	ret = parent->CreateChild(offset, size, type.String(),
981 		name.String(), parameters.String());
982 
983 	if (ret != B_OK) {
984 		_DisplayPartitionError("Creation of the partition has failed\n");
985 		return;
986 	}
987 
988 	// commit
989 	ret = modificationPreparer.CommitModifications();
990 
991 	if (ret != B_OK) {
992 		_DisplayPartitionError("Failed to initialize the partition. "
993 			"This operation is exiting.\nNo changes have been made!\n");
994 		return;
995 	}
996 
997 	// The disk layout has changed, update disk information
998 	bool updated;
999 	ret = disk->Update(&updated);
1000 
1001 	_ScanDrives();
1002 	fDiskView->ForceUpdate();
1003 }
1004 
1005 void
1006 MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition)
1007 {
1008 	if (!disk || selectedPartition < 0) {
1009 		_DisplayPartitionError("You need to select a partition "
1010 			"entry from the list.");
1011 		return;
1012 	}
1013 
1014 	if (disk->IsReadOnly()) {
1015 		_DisplayPartitionError("The selected disk is read-only.");
1016 		return;
1017 	}
1018 
1019 	BPartition* partition = disk->FindDescendant(selectedPartition);
1020 	if (!partition) {
1021 		_DisplayPartitionError("Unable to find the selected partition by id.");
1022 		return;
1023 	}
1024 
1025 	BPartition* parent = partition->Parent();
1026 	if (!parent) {
1027 		_DisplayPartitionError("The currently selected partition does not "
1028 			"have a parent partition");
1029 		return;
1030 	}
1031 
1032 	ModificationPreparer modificationPreparer(disk);
1033 	status_t ret = modificationPreparer.ModificationStatus();
1034 	if (ret != B_OK) {
1035 		_DisplayPartitionError("There was an error preparing the "
1036 			"disk for modifications.", NULL, ret);
1037 		return;
1038 	}
1039 
1040 	if (!parent->CanDeleteChild(partition->Index())) {
1041 		_DisplayPartitionError("Cannot delete the selected partition");
1042 		return;
1043 	}
1044 
1045 	// Warn the user one more time...
1046 	BString message = "Are you sure you want to delete the selected ";
1047 	message << "partition?\n\nAll data on the partition";
1048 	message << " will be irrevertably lost if you do so!";
1049 	BAlert* alert = new BAlert("final notice", message.String(),
1050 		"Delete Partition", "Cancel", NULL, B_WIDTH_FROM_WIDEST,
1051 		B_WARNING_ALERT);
1052 	int32 choice = alert->Go();
1053 
1054 	if (choice == 1)
1055 		return;
1056 
1057 	ret = parent->DeleteChild(partition->Index());
1058 	if (ret != B_OK) {
1059 		_DisplayPartitionError("Could not delete the selected partition");
1060 		return;
1061 	}
1062 
1063 	ret = modificationPreparer.CommitModifications();
1064 
1065 	if (ret != B_OK) {
1066 		_DisplayPartitionError("Failed to delete the partition. "
1067 			"This operation is exiting.\nNo changes have been made!\n");
1068 		return;
1069 	}
1070 
1071 	_ScanDrives();
1072 	fDiskView->ForceUpdate();
1073 }
1074