xref: /haiku/src/apps/drivesetup/MainWindow.cpp (revision 56187df6ebd9d1fb16bf954fce8df0999e9e3048)
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 			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\"? You will be asked again "
796 			"before changes are written to the disk."),
797 			partition->ContentName());
798 	} else {
799 		snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you want "
800 			"to initialize the partition? You will be asked again "
801 			"before changes are written 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 	ModificationPreparer modificationPreparer(disk);
831 	status_t ret = modificationPreparer.ModificationStatus();
832 	if (ret != B_OK) {
833 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
834 			"disk for modifications."), NULL, ret);
835 		return;
836 	}
837 
838 	BString name;
839 	BString parameters;
840 	if (diskSystemName == "Be File System") {
841 		InitParamsPanel* panel = new InitParamsPanel(this, diskSystemName,
842 			partition);
843 		if (panel->Go(name, parameters) == GO_CANCELED)
844 			return;
845 	} else if (diskSystemName == "Intel Partition Map") {
846 		// TODO: parameters?
847 	} else if (diskSystemName == "Intel Extended Partition") {
848 		// TODO: parameters?
849 	}
850 
851 	bool supportsName = diskSystem.SupportsContentName();
852 	BString validatedName(name);
853 	ret = partition->ValidateInitialize(diskSystem.PrettyName(),
854 		supportsName ? &validatedName : NULL, parameters.String());
855 	if (ret != B_OK) {
856 		_DisplayPartitionError(B_TRANSLATE("Validation of the given "
857 			"initialization parameters failed."), partition, ret);
858 		return;
859 	}
860 
861 	BString previousName = partition->ContentName();
862 
863 	ret = partition->Initialize(diskSystem.PrettyName(),
864 		supportsName ? validatedName.String() : NULL, parameters.String());
865 	if (ret != B_OK) {
866 		_DisplayPartitionError(B_TRANSLATE("Initialization of the partition "
867 			"%s failed. (Nothing has been written to disk.)"), partition, ret);
868 		return;
869 	}
870 
871 	// everything looks fine, we are ready to actually write the changes
872 	// to disk
873 
874 	// Warn the user one more time...
875 	if (previousName.Length() > 0) {
876 		if (partition->IsDevice()) {
877 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
878 				"want to write the changes back to disk now?\n\n"
879 				"All data on the disk %s will be irretrievably lost if you "
880 				"do so!"), previousName.String());
881 		} else {
882 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
883 				"want to write the changes back to disk now?\n\n"
884 				"All data on the partition %s will be irretrievably lost if you "
885 				"do so!"), previousName.String());
886 		}
887 	} else {
888 		if (partition->IsDevice()) {
889 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
890 				"want to write the changes back to disk now?\n\n"
891 				"All data on the selected disk will be irretrievably lost if "
892 				"you do so!"));
893 		} else {
894 			snprintf(message, sizeof(message), B_TRANSLATE("Are you sure you "
895 				"want to write the changes back to disk now?\n\n"
896 				"All data on the selected partition will be irretrievably lost "
897 				"if you do so!"));
898 		}
899 	}
900 	alert = new BAlert("final notice", message,
901 		B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
902 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
903 	choice = alert->Go();
904 
905 	if (choice == 1)
906 		return;
907 
908 	// commit
909 	ret = modificationPreparer.CommitModifications();
910 
911 	// The partition pointer is toast now! Use the partition ID to
912 	// retrieve it again.
913 	partition = disk->FindDescendant(selectedPartition);
914 
915 	if (ret == B_OK) {
916 		_DisplayPartitionError(B_TRANSLATE("The partition %s has been "
917 			"successfully initialized.\n"), partition);
918 	} else {
919 		_DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
920 			"partition %s!\n"), partition, ret);
921 	}
922 
923 	_ScanDrives();
924 }
925 
926 
927 void
928 MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition)
929 {
930 	if (!disk || selectedPartition > -2) {
931 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
932 			"is not empty."));
933 		return;
934 	}
935 
936 	if (disk->IsReadOnly()) {
937 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
938 		return;
939 	}
940 
941 	PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>(
942 		fListView->CurrentSelection());
943 	if (!currentSelection) {
944 		_DisplayPartitionError(B_TRANSLATE("There was an error acquiring the "
945 			"partition row."));
946 		return;
947 	}
948 
949 	BPartition* parent = disk->FindDescendant(currentSelection->ParentID());
950 	if (!parent) {
951 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
952 			"does not have a parent partition."));
953 		return;
954 	}
955 
956 	if (!parent->ContainsPartitioningSystem()) {
957 		_DisplayPartitionError(B_TRANSLATE("The selected partition does not "
958 			"contain a partitioning system."));
959 		return;
960 	}
961 
962 	ModificationPreparer modificationPreparer(disk);
963 	status_t ret = modificationPreparer.ModificationStatus();
964 	if (ret != B_OK) {
965 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
966 			"disk for modifications."), NULL, ret);
967 		return;
968 	}
969 
970 	// get partitioning info
971 	BPartitioningInfo partitioningInfo;
972 	status_t error = parent->GetPartitioningInfo(&partitioningInfo);
973 	if (error != B_OK) {
974 		_DisplayPartitionError(B_TRANSLATE("Could not aquire partitioning "
975 			"information."));
976 		return;
977 	}
978 
979 	int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
980 	if (spacesCount == 0) {
981 		_DisplayPartitionError(B_TRANSLATE("There's no space on the partition "
982 			"where a child partition could be created."));
983 		return;
984 	}
985 
986 	BString name, type, parameters;
987 	off_t offset = currentSelection->Offset();
988 	off_t size = currentSelection->Size();
989 
990 	CreateParamsPanel* panel = new CreateParamsPanel(this, parent, offset,
991 		size);
992 	if (panel->Go(offset, size, name, type, parameters) == GO_CANCELED)
993 		return;
994 
995 	ret = parent->ValidateCreateChild(&offset, &size, type.String(),
996 		&name, parameters.String());
997 
998 	if (ret != B_OK) {
999 		_DisplayPartitionError(B_TRANSLATE("Validation of the given creation "
1000 			"parameters failed."));
1001 		return;
1002 	}
1003 
1004 	// Warn the user one more time...
1005 	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1006 		"want to write the changes back to disk now?\n\n"
1007 		"All data on the partition will be irretrievably lost if you do "
1008 		"so!"), B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1009 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1010 	int32 choice = alert->Go();
1011 
1012 	if (choice == 1)
1013 		return;
1014 
1015 	ret = parent->CreateChild(offset, size, type.String(),
1016 		name.String(), parameters.String());
1017 
1018 	if (ret != B_OK) {
1019 		_DisplayPartitionError(B_TRANSLATE("Creation of the partition has "
1020 			"failed."));
1021 		return;
1022 	}
1023 
1024 	// commit
1025 	ret = modificationPreparer.CommitModifications();
1026 
1027 	if (ret != B_OK) {
1028 		_DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
1029 			"partition. No changes have been written to disk."));
1030 		return;
1031 	}
1032 
1033 	// The disk layout has changed, update disk information
1034 	bool updated;
1035 	ret = disk->Update(&updated);
1036 
1037 	_ScanDrives();
1038 	fDiskView->ForceUpdate();
1039 }
1040 
1041 void
1042 MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition)
1043 {
1044 	if (!disk || selectedPartition < 0) {
1045 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1046 			"entry from the list."));
1047 		return;
1048 	}
1049 
1050 	if (disk->IsReadOnly()) {
1051 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1052 		return;
1053 	}
1054 
1055 	BPartition* partition = disk->FindDescendant(selectedPartition);
1056 	if (!partition) {
1057 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1058 			"partition by ID."));
1059 		return;
1060 	}
1061 
1062 	BPartition* parent = partition->Parent();
1063 	if (!parent) {
1064 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1065 			"does not have a parent partition."));
1066 		return;
1067 	}
1068 
1069 	ModificationPreparer modificationPreparer(disk);
1070 	status_t ret = modificationPreparer.ModificationStatus();
1071 	if (ret != B_OK) {
1072 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1073 			"disk for modifications."), NULL, ret);
1074 		return;
1075 	}
1076 
1077 	if (!parent->CanDeleteChild(partition->Index())) {
1078 		_DisplayPartitionError(
1079 			B_TRANSLATE("Cannot delete the selected partition."));
1080 		return;
1081 	}
1082 
1083 	// Warn the user one more time...
1084 	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1085 		"want to delete the selected partition?\n\n"
1086 		"All data on the partition will be irretrievably lost if you "
1087 		"do so!"), B_TRANSLATE("Delete partition"), B_TRANSLATE("Cancel"), NULL,
1088 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1089 	int32 choice = alert->Go();
1090 
1091 	if (choice == 1)
1092 		return;
1093 
1094 	ret = parent->DeleteChild(partition->Index());
1095 	if (ret != B_OK) {
1096 		_DisplayPartitionError(
1097 			B_TRANSLATE("Could not delete the selected partition."));
1098 		return;
1099 	}
1100 
1101 	ret = modificationPreparer.CommitModifications();
1102 
1103 	if (ret != B_OK) {
1104 		_DisplayPartitionError(B_TRANSLATE("Failed to delete the partition. "
1105 			"No changes have been written to disk."));
1106 		return;
1107 	}
1108 
1109 	_ScanDrives();
1110 	fDiskView->ForceUpdate();
1111 }
1112