xref: /haiku/src/apps/drivesetup/MainWindow.cpp (revision e0bc2fcce4c5ceb7b57d978b752cda01639d0098)
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, "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 			fInitMenu->SetEnabled(!partition->IsMounted()
579 				&& !partition->IsReadOnly()
580 				&& partition->Device()->HasMedia());
581 
582 			fDeleteMI->SetEnabled(!partition->IsMounted()
583 				&& !partition->IsDevice());
584 
585 			fMountMI->SetEnabled(!partition->IsMounted());
586 
587 			bool unMountable = false;
588 			if (partition->IsMounted()) {
589 				// see if this partition is the boot volume
590 				BVolume volume;
591 				BVolume bootVolume;
592 				if (BVolumeRoster().GetBootVolume(&bootVolume) == B_OK
593 					&& partition->GetVolume(&volume) == B_OK) {
594 					unMountable = volume != bootVolume;
595 				} else
596 					unMountable = true;
597 			}
598 			fUnmountMI->SetEnabled(unMountable);
599 		} else {
600 			fInitMenu->SetEnabled(false);
601 			fDeleteMI->SetEnabled(false);
602 			fMountMI->SetEnabled(false);
603 			fUnmountMI->SetEnabled(false);
604 		}
605 
606 		if (prepared)
607 			disk->CancelModifications();
608 
609 		fMountAllMI->SetEnabled(true);
610 	}
611 	if (selectedPartition < 0) {
612 		fDeleteMI->SetEnabled(false);
613 		fMountMI->SetEnabled(false);
614 	}
615 }
616 
617 
618 void
619 MainWindow::_DisplayPartitionError(BString _message,
620 	const BPartition* partition, status_t error) const
621 {
622 	char message[1024];
623 
624 	if (partition && _message.FindFirst("%s") >= 0) {
625 		BString name;
626 		name << "\"" << partition->ContentName() << "\"";
627 		snprintf(message, sizeof(message), _message.String(), name.String());
628 	} else {
629 		_message.ReplaceAll("%s", "");
630 		snprintf(message, sizeof(message), _message.String());
631 	}
632 
633 	if (error < B_OK) {
634 		BString helper = message;
635 		const char* errorString =
636 			B_TRANSLATE_COMMENT("Error: ", "in any error alert");
637 		snprintf(message, sizeof(message), "%s\n\n%s%s", helper.String(),
638 			errorString, strerror(error));
639 	}
640 
641 	BAlert* alert = new BAlert("error", message, B_TRANSLATE("OK"), NULL, NULL,
642 		B_WIDTH_FROM_WIDEST, error < B_OK ? B_STOP_ALERT : B_INFO_ALERT);
643 	alert->Go(NULL);
644 }
645 
646 
647 // #pragma mark -
648 
649 
650 void
651 MainWindow::_Mount(BDiskDevice* disk, partition_id selectedPartition)
652 {
653 	if (!disk || selectedPartition < 0) {
654 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
655 			"entry from the list."));
656 		return;
657 	}
658 
659 	BPartition* partition = disk->FindDescendant(selectedPartition);
660 	if (!partition) {
661 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
662 			"partition by ID."));
663 		return;
664 	}
665 
666 	if (!partition->IsMounted()) {
667 		status_t ret = partition->Mount();
668 		if (ret < B_OK) {
669 			_DisplayPartitionError(
670 				B_TRANSLATE("Could not mount partition %s."), partition, ret);
671 		} else {
672 			// successful mount, adapt to the changes
673 			_ScanDrives();
674 		}
675 	} else {
676 		_DisplayPartitionError(
677 			B_TRANSLATE("The partition %s is already mounted."), partition);
678 	}
679 }
680 
681 
682 void
683 MainWindow::_Unmount(BDiskDevice* disk, partition_id selectedPartition)
684 {
685 	if (!disk || selectedPartition < 0) {
686 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
687 			"entry from the list."));
688 		return;
689 	}
690 
691 	BPartition* partition = disk->FindDescendant(selectedPartition);
692 	if (!partition) {
693 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
694 			"partition by ID."));
695 		return;
696 	}
697 
698 	if (partition->IsMounted()) {
699 		BPath path;
700 		partition->GetMountPoint(&path);
701 		status_t ret = partition->Unmount();
702 		if (ret < B_OK) {
703 			_DisplayPartitionError(
704 				B_TRANSLATE("Could not unmount partition %s."),
705 				partition, ret);
706 		} else {
707 			if (dev_for_path(path.Path()) == dev_for_path("/"))
708 				rmdir(path.Path());
709 			// successful unmount, adapt to the changes
710 			_ScanDrives();
711 		}
712 	} else {
713 		_DisplayPartitionError(
714 			B_TRANSLATE("The partition %s is already unmounted."),
715 			partition);
716 	}
717 }
718 
719 
720 void
721 MainWindow::_MountAll()
722 {
723 	MountAllVisitor visitor;
724 	fDDRoster.VisitEachPartition(&visitor);
725 }
726 
727 
728 // #pragma mark -
729 
730 
731 class ModificationPreparer {
732 public:
733 	ModificationPreparer(BDiskDevice* disk)
734 		:
735 		fDisk(disk),
736 		fModificationStatus(fDisk->PrepareModifications())
737 	{
738 	}
739 	~ModificationPreparer()
740 	{
741 		if (fModificationStatus == B_OK)
742 			fDisk->CancelModifications();
743 	}
744 	status_t ModificationStatus() const
745 	{
746 		return fModificationStatus;
747 	}
748 	status_t CommitModifications()
749 	{
750 		status_t ret = fDisk->CommitModifications();
751 		if (ret == B_OK)
752 			fModificationStatus = B_ERROR;
753 
754 		return ret;
755 	}
756 
757 private:
758 	BDiskDevice*	fDisk;
759 	status_t		fModificationStatus;
760 };
761 
762 
763 void
764 MainWindow::_Initialize(BDiskDevice* disk, partition_id selectedPartition,
765 	const BString& diskSystemName)
766 {
767 	if (!disk || selectedPartition < 0) {
768 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
769 			"entry from the list."));
770 		return;
771 	}
772 
773 	if (disk->IsReadOnly()) {
774 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
775 		return;
776 	}
777 
778 	BPartition* partition = disk->FindDescendant(selectedPartition);
779 	if (!partition) {
780 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
781 			"partition by ID."));
782 		return;
783 	}
784 
785 	if (partition->IsMounted()) {
786 		_DisplayPartitionError(
787 			B_TRANSLATE("The partition %s is currently mounted."));
788 		// TODO: option to unmount and continue on success to unmount
789 		return;
790 	}
791 
792 	char message[512];
793 	if (partition->ContentName() && strlen(partition->ContentName()) > 0) {
794 		snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you want "
795 			"to initialize the partition \"%s\"? After entering the "
796 			"initialization parameters, you can abort this operation "
797 			"right before writing changes back to the disk."),
798 			partition->ContentName());
799 	} else {
800 		snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you want "
801 			"to initialize the selected partition? After entering the "
802 			"initialization parameters, you can abort this operation "
803 			"right before writing changes back to the disk."));
804 	}
805 	BAlert* alert = new BAlert("first notice", message,
806 		B_TRANSLATE("Continue"), B_TRANSLATE("Cancel"), NULL,
807 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
808 	int32 choice = alert->Go();
809 
810 	if (choice == 1)
811 		return;
812 
813 	BDiskSystem diskSystem;
814 	fDDRoster.RewindDiskSystems();
815 	bool found = false;
816 	while (fDDRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
817 		if (diskSystem.SupportsInitializing()) {
818 			if (diskSystemName == diskSystem.PrettyName()) {
819 				found = true;
820 				break;
821 			}
822 		}
823 	}
824 
825 	if (!found) {
826 		snprintf(message, sizeof(message), B_TRANSLATE("Disk system \"%s\"\" "
827 			"not found!"));
828 		_DisplayPartitionError(message);
829 		return;
830 	}
831 
832 	ModificationPreparer modificationPreparer(disk);
833 	status_t ret = modificationPreparer.ModificationStatus();
834 	if (ret != B_OK) {
835 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
836 			"disk for modifications."), NULL, ret);
837 		return;
838 	}
839 
840 	BString name;
841 	BString parameters;
842 	if (diskSystemName == "Be File System") {
843 		InitParamsPanel* panel = new InitParamsPanel(this, diskSystemName,
844 			partition);
845 		if (panel->Go(name, parameters) == GO_CANCELED)
846 			return;
847 	} else if (diskSystemName == "Intel Partition Map") {
848 		// TODO: parameters?
849 	} else if (diskSystemName == "Intel Extended Partition") {
850 		// TODO: parameters?
851 	}
852 
853 	bool supportsName = diskSystem.SupportsContentName();
854 	BString validatedName(name);
855 	ret = partition->ValidateInitialize(diskSystem.PrettyName(),
856 		supportsName ? &validatedName : NULL, parameters.String());
857 	if (ret != B_OK) {
858 		_DisplayPartitionError(B_TRANSLATE("Validation of the given "
859 			"initialization parameters failed."), partition, ret);
860 		return;
861 	}
862 
863 	BString previousName = partition->ContentName();
864 
865 	ret = partition->Initialize(diskSystem.PrettyName(),
866 		supportsName ? validatedName.String() : NULL, parameters.String());
867 	if (ret != B_OK) {
868 		_DisplayPartitionError(B_TRANSLATE("Initialization of the partition "
869 			"%s failed. (Nothing has been written to disk.)"), partition, ret);
870 		return;
871 	}
872 
873 	// everything looks fine, we are ready to actually write the changes
874 	// to disk
875 
876 	// Warn the user one more time...
877 	if (previousName.Length() > 0) {
878 		if (partition->IsDevice()) {
879 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
880 				"want to write the changes back to disk now?\n\n"
881 				"All data on the disk %s will be irretrievably lost if you "
882 				"do so!"), previousName.String());
883 		} else {
884 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
885 				"want to write the changes back to disk now?\n\n"
886 				"All data on the partition %s will be irretrievably lost if you "
887 				"do so!"), previousName.String());
888 		}
889 	} else {
890 		if (partition->IsDevice()) {
891 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
892 				"want to write the changes back to disk now?\n\n"
893 				"All data on the selected disk will be irretrievably lost if "
894 				"you do so!"));
895 		} else {
896 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
897 				"want to write the changes back to disk now?\n\n"
898 				"All data on the selected partition will be irretrievably lost "
899 				"if you do so!"));
900 		}
901 	}
902 	alert = new BAlert("final notice", message,
903 		B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
904 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
905 	choice = alert->Go();
906 
907 	if (choice == 1)
908 		return;
909 
910 	// commit
911 	ret = modificationPreparer.CommitModifications();
912 
913 	// The partition pointer is toast now! Use the partition ID to
914 	// retrieve it again.
915 	partition = disk->FindDescendant(selectedPartition);
916 
917 	if (ret == B_OK) {
918 		_DisplayPartitionError(B_TRANSLATE("The partition %s has been "
919 			"successfully initialized.\n"), partition);
920 	} else {
921 		_DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
922 			"partition %s!\n"), partition, ret);
923 	}
924 
925 	_ScanDrives();
926 }
927 
928 
929 void
930 MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition)
931 {
932 	if (!disk || selectedPartition > -2) {
933 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
934 			"is not empty."));
935 		return;
936 	}
937 
938 	if (disk->IsReadOnly()) {
939 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
940 		return;
941 	}
942 
943 	PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>(
944 		fListView->CurrentSelection());
945 	if (!currentSelection) {
946 		_DisplayPartitionError(B_TRANSLATE("There was an error acquiring the "
947 			"partition row."));
948 		return;
949 	}
950 
951 	BPartition* parent = disk->FindDescendant(currentSelection->ParentID());
952 	if (!parent) {
953 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
954 			"does not have a parent partition."));
955 		return;
956 	}
957 
958 	if (!parent->ContainsPartitioningSystem()) {
959 		_DisplayPartitionError(B_TRANSLATE("The selected partition does not "
960 			"contain a partitioning system."));
961 		return;
962 	}
963 
964 	ModificationPreparer modificationPreparer(disk);
965 	status_t ret = modificationPreparer.ModificationStatus();
966 	if (ret != B_OK) {
967 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
968 			"disk for modifications."), NULL, ret);
969 		return;
970 	}
971 
972 	// get partitioning info
973 	BPartitioningInfo partitioningInfo;
974 	status_t error = parent->GetPartitioningInfo(&partitioningInfo);
975 	if (error != B_OK) {
976 		_DisplayPartitionError(B_TRANSLATE("Could not aquire partitioning "
977 			"information."));
978 		return;
979 	}
980 
981 	int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
982 	if (spacesCount == 0) {
983 		_DisplayPartitionError(B_TRANSLATE("There's no space on the partition "
984 			"where a child partition could be created."));
985 		return;
986 	}
987 
988 	BString name, type, parameters;
989 	off_t offset = currentSelection->Offset();
990 	off_t size = currentSelection->Size();
991 
992 	CreateParamsPanel* panel = new CreateParamsPanel(this, parent, offset,
993 		size);
994 	if (panel->Go(offset, size, name, type, parameters) == GO_CANCELED)
995 		return;
996 
997 	ret = parent->ValidateCreateChild(&offset, &size, type.String(),
998 		&name, parameters.String());
999 
1000 	if (ret != B_OK) {
1001 		_DisplayPartitionError(B_TRANSLATE("Validation of the given creation "
1002 			"parameters failed."));
1003 		return;
1004 	}
1005 
1006 	// Warn the user one more time...
1007 	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1008 		"want to write the changes back to disk now?\n\n"
1009 		"All data on the partition will be irretrievably lost if you do "
1010 		"so!"), B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1011 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1012 	int32 choice = alert->Go();
1013 
1014 	if (choice == 1)
1015 		return;
1016 
1017 	ret = parent->CreateChild(offset, size, type.String(),
1018 		name.String(), parameters.String());
1019 
1020 	if (ret != B_OK) {
1021 		_DisplayPartitionError(B_TRANSLATE("Creation of the partition has "
1022 			"failed."));
1023 		return;
1024 	}
1025 
1026 	// commit
1027 	ret = modificationPreparer.CommitModifications();
1028 
1029 	if (ret != B_OK) {
1030 		_DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
1031 			"partition. No changes have been written to disk."));
1032 		return;
1033 	}
1034 
1035 	// The disk layout has changed, update disk information
1036 	bool updated;
1037 	ret = disk->Update(&updated);
1038 
1039 	_ScanDrives();
1040 	fDiskView->ForceUpdate();
1041 }
1042 
1043 void
1044 MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition)
1045 {
1046 	if (!disk || selectedPartition < 0) {
1047 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1048 			"entry from the list."));
1049 		return;
1050 	}
1051 
1052 	if (disk->IsReadOnly()) {
1053 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1054 		return;
1055 	}
1056 
1057 	BPartition* partition = disk->FindDescendant(selectedPartition);
1058 	if (!partition) {
1059 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1060 			"partition by ID."));
1061 		return;
1062 	}
1063 
1064 	BPartition* parent = partition->Parent();
1065 	if (!parent) {
1066 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1067 			"does not have a parent partition."));
1068 		return;
1069 	}
1070 
1071 	ModificationPreparer modificationPreparer(disk);
1072 	status_t ret = modificationPreparer.ModificationStatus();
1073 	if (ret != B_OK) {
1074 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1075 			"disk for modifications."), NULL, ret);
1076 		return;
1077 	}
1078 
1079 	if (!parent->CanDeleteChild(partition->Index())) {
1080 		_DisplayPartitionError(
1081 			B_TRANSLATE("Cannot delete the selected partition."));
1082 		return;
1083 	}
1084 
1085 	// Warn the user one more time...
1086 	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1087 		"want to delete the selected partition?\n\n"
1088 		"All data on the partition will be irretrievably lost if you "
1089 		"do so!"), B_TRANSLATE("Delete partition"), B_TRANSLATE("Cancel"), NULL,
1090 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1091 	int32 choice = alert->Go();
1092 
1093 	if (choice == 1)
1094 		return;
1095 
1096 	ret = parent->DeleteChild(partition->Index());
1097 	if (ret != B_OK) {
1098 		_DisplayPartitionError(
1099 			B_TRANSLATE("Could not delete the selected partition."));
1100 		return;
1101 	}
1102 
1103 	ret = modificationPreparer.CommitModifications();
1104 
1105 	if (ret != B_OK) {
1106 		_DisplayPartitionError(B_TRANSLATE("Failed to delete the partition. "
1107 			"No changes have been written to disk."));
1108 		return;
1109 	}
1110 
1111 	_ScanDrives();
1112 	fDiskView->ForceUpdate();
1113 }
1114