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