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