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