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