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