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