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