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