176140Sdd/*
276140Sdd * Copyright 2002-2013 Haiku, Inc. All rights reserved.
376140Sdd * Distributed under the terms of the MIT license.
476140Sdd *
5250746Sgabor * Authors:
6250746Sgabor *		Ithamar R. Adema <ithamar@unet.nl>
7260657Shrs *		Stephan A��mus <superstippi@gmx.de>
8260657Shrs *		Axel D��rfler, axeld@pinc-software.de.
9250746Sgabor *		Erik Jaesler <ejakowatz@users.sourceforge.net>
10250746Sgabor *		Ingo Weinhold <ingo_weinhold@gmx.de>
11250746Sgabor */
12250746Sgabor
13250746Sgabor
14250746Sgabor#include "MainWindow.h"
15250746Sgabor
16260657Shrs#include <stdio.h>
17260657Shrs#include <string.h>
18250746Sgabor
1979462Sbmah#include <Alert.h>
20250746Sgabor#include <Application.h>
21260657Shrs#include <Catalog.h>
2276222Sbmah#include <ColumnListView.h>
23260657Shrs#include <ColumnTypes.h>
2498515Sbmah#include <Debug.h>
2598515Sbmah#include <DiskDevice.h>
26133094Ssimon#include <DiskDeviceVisitor.h>
27133094Ssimon#include <DiskDeviceTypes.h>
28133094Ssimon#include <DiskSystem.h>
29133094Ssimon#include <Locale.h>
30133094Ssimon#include <MenuItem.h>
31133094Ssimon#include <MenuBar.h>
32133094Ssimon#include <Menu.h>
33133094Ssimon#include <Path.h>
34133094Ssimon#include <Partition.h>
35133094Ssimon#include <PartitioningInfo.h>
36133094Ssimon#include <Roster.h>
37241096Sgabor#include <Screen.h>
38250746Sgabor#include <Volume.h>
39133094Ssimon#include <VolumeRoster.h>
40133322Shrs
41166778Sbmah#include <tracker_private.h>
42166778Sbmah
43166778Sbmah#include "CreateParametersPanel.h"
44166778Sbmah#include "DiskView.h"
45166778Sbmah#include "InitParametersPanel.h"
46133322Shrs#include "PartitionList.h"
47133094Ssimon#include "Support.h"
48250746Sgabor
49250746Sgabor
50133322Shrs#undef B_TRANSLATION_CONTEXT
51250746Sgabor#define B_TRANSLATION_CONTEXT "MainWindow"
52
53
54class ListPopulatorVisitor : public BDiskDeviceVisitor {
55public:
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
91private:
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
124class MountAllVisitor : public BDiskDeviceVisitor {
125public:
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
141private:
142	PartitionListView* fPartitionList;
143};
144
145
146class ModificationPreparer {
147public:
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
172private:
173	BDiskDevice*	fDisk;
174	status_t		fModificationStatus;
175};
176
177
178enum {
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
197MainWindow::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
301MainWindow::~MainWindow()
302{
303	BDiskDeviceRoster().StopWatching(this);
304	delete fCurrentDisk;
305}
306
307
308void
309MainWindow::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
387bool
388MainWindow::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
400status_t
401MainWindow::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
415status_t
416MainWindow::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
435void
436MainWindow::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
473void
474MainWindow::_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
499void
500MainWindow::_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
531void
532MainWindow::_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
562void
563MainWindow::_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
680void
681MainWindow::_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
713void
714MainWindow::_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
745void
746MainWindow::_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
783void
784MainWindow::_MountAll()
785{
786	MountAllVisitor visitor;
787	fDDRoster.VisitEachPartition(&visitor);
788}
789
790
791// #pragma mark -
792
793
794void
795MainWindow::_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
978void
979MainWindow::_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
1094void
1095MainWindow::_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