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 <ScrollBar.h>
39#include <Volume.h>
40#include <VolumeRoster.h>
41
42#include <fs_volume.h>
43#include <tracker_private.h>
44
45#include "ChangeParametersPanel.h"
46#include "ColumnListView.h"
47#include "CreateParametersPanel.h"
48#include "DiskView.h"
49#include "InitParametersPanel.h"
50#include "PartitionList.h"
51#include "Support.h"
52
53
54#undef B_TRANSLATION_CONTEXT
55#define B_TRANSLATION_CONTEXT "MainWindow"
56
57
58enum {
59	MSG_MOUNT_ALL				= 'mnta',
60	MSG_MOUNT					= 'mnts',
61	MSG_UNMOUNT					= 'unmt',
62	MSG_FORMAT					= 'frmt',
63	MSG_CREATE					= 'crtp',
64	MSG_CHANGE					= 'chgp',
65	MSG_INITIALIZE				= 'init',
66	MSG_DELETE					= 'delt',
67	MSG_EJECT					= 'ejct',
68	MSG_OPEN_DISKPROBE			= 'opdp',
69	MSG_SURFACE_TEST			= 'sfct',
70	MSG_RESCAN					= 'rscn',
71
72	MSG_PARTITION_ROW_SELECTED	= 'prsl',
73};
74
75
76class ListPopulatorVisitor : public BDiskDeviceVisitor {
77public:
78	ListPopulatorVisitor(PartitionListView* list, int32& diskCount,
79			SpaceIDMap& spaceIDMap)
80		:
81		fPartitionList(list),
82		fDiskCount(diskCount),
83		fSpaceIDMap(spaceIDMap)
84	{
85		fDiskCount = 0;
86		fSpaceIDMap.Clear();
87		// start with an empty list
88		int32 rows = fPartitionList->CountRows();
89		for (int32 i = rows - 1; i >= 0; i--) {
90			BRow* row = fPartitionList->RowAt(i);
91			fPartitionList->RemoveRow(row);
92			delete row;
93		}
94	}
95
96	virtual bool Visit(BDiskDevice* device)
97	{
98		fDiskCount++;
99		// if we don't prepare the device for modifications,
100		// we cannot get information about available empty
101		// regions on the device or child partitions
102		device->PrepareModifications();
103		_AddPartition(device);
104		return false; // Don't stop yet!
105	}
106
107	virtual bool Visit(BPartition* partition, int32 level)
108	{
109		_AddPartition(partition);
110		return false; // Don't stop yet!
111	}
112
113private:
114	void _AddPartition(BPartition* partition) const
115	{
116		// add the partition itself
117		fPartitionList->AddPartition(partition);
118
119		// add any available space on it
120		BPartitioningInfo info;
121		status_t status = partition->GetPartitioningInfo(&info);
122		if (status >= B_OK) {
123			partition_id parentID = partition->ID();
124			off_t offset;
125			off_t size;
126			for (int32 i = 0;
127					info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
128					i++) {
129				// TODO: remove again once Disk Device API is fixed
130				if (!is_valid_partitionable_space(size))
131					continue;
132				//
133				partition_id id = fSpaceIDMap.SpaceIDFor(parentID, offset);
134				fPartitionList->AddSpace(parentID, id, offset, size);
135			}
136		}
137	}
138
139	PartitionListView*	fPartitionList;
140	int32&				fDiskCount;
141	SpaceIDMap&			fSpaceIDMap;
142	BDiskDevice*		fLastPreparedDevice;
143};
144
145
146class MountAllVisitor : public BDiskDeviceVisitor {
147public:
148	MountAllVisitor()
149	{
150	}
151
152	virtual bool Visit(BDiskDevice* device)
153	{
154		if (device->ContainsFileSystem())
155			device->Mount();
156
157		return false; // Don't stop yet!
158	}
159
160	virtual bool Visit(BPartition* partition, int32 level)
161	{
162		partition->Mount();
163		return false; // Don't stop yet!
164	}
165
166private:
167	PartitionListView* fPartitionList;
168};
169
170
171class ModificationPreparer {
172public:
173	ModificationPreparer(BDiskDevice* disk)
174		:
175		fDisk(disk),
176		fModificationStatus(fDisk->PrepareModifications())
177	{
178	}
179	~ModificationPreparer()
180	{
181		if (fModificationStatus == B_OK)
182			fDisk->CancelModifications();
183	}
184	status_t ModificationStatus() const
185	{
186		return fModificationStatus;
187	}
188	status_t CommitModifications()
189	{
190		status_t status = fDisk->CommitModifications();
191		if (status == B_OK)
192			fModificationStatus = B_ERROR;
193
194		return status;
195	}
196
197private:
198	BDiskDevice*	fDisk;
199	status_t		fModificationStatus;
200};
201
202
203// #pragma mark -
204
205
206MainWindow::MainWindow()
207	:
208	BWindow(BRect(50, 50, 600, 500), B_TRANSLATE_SYSTEM_NAME("DriveSetup"),
209		B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS),
210	fCurrentDisk(NULL),
211	fCurrentPartitionID(-1),
212	fSpaceIDMap()
213{
214	fMenuBar = new BMenuBar(Bounds(), "root menu");
215
216	// create all the menu items
217	fWipeMenuItem = new BMenuItem(B_TRANSLATE("Wipe (not implemented)"),
218		new BMessage(MSG_FORMAT));
219	fEjectMenuItem = new BMenuItem(B_TRANSLATE("Eject"),
220		new BMessage(MSG_EJECT), 'E');
221	fOpenDiskProbeMenuItem = new BMenuItem(B_TRANSLATE("Open with DiskProbe"),
222		new BMessage(MSG_OPEN_DISKPROBE));
223
224	fSurfaceTestMenuItem = new BMenuItem(
225		B_TRANSLATE("Surface test (not implemented)"),
226		new BMessage(MSG_SURFACE_TEST));
227	fRescanMenuItem = new BMenuItem(B_TRANSLATE("Rescan"),
228		new BMessage(MSG_RESCAN));
229
230	fCreateMenuItem = new BMenuItem(B_TRANSLATE("Create" B_UTF8_ELLIPSIS),
231		new BMessage(MSG_CREATE), 'C');
232	fChangeMenuItem = new BMenuItem(
233		B_TRANSLATE("Change parameters" B_UTF8_ELLIPSIS),
234		new BMessage(MSG_CHANGE));
235	fDeleteMenuItem = new BMenuItem(B_TRANSLATE("Delete"),
236		new BMessage(MSG_DELETE), 'D');
237
238	fMountMenuItem = new BMenuItem(B_TRANSLATE("Mount"),
239		new BMessage(MSG_MOUNT), 'M');
240	fUnmountMenuItem = new BMenuItem(B_TRANSLATE("Unmount"),
241		new BMessage(MSG_UNMOUNT), 'U');
242	fMountAllMenuItem = new BMenuItem(B_TRANSLATE("Mount all"),
243		new BMessage(MSG_MOUNT_ALL), 'M', B_SHIFT_KEY);
244
245	// Disk menu
246	fDiskMenu = new BMenu(B_TRANSLATE("Disk"));
247
248	// fDiskMenu->AddItem(fWipeMenuItem);
249	fDiskInitMenu = new BMenu(B_TRANSLATE("Initialize"));
250	fDiskMenu->AddItem(fDiskInitMenu);
251
252	fDiskMenu->AddSeparatorItem();
253
254	fDiskMenu->AddItem(fEjectMenuItem);
255	// fDiskMenu->AddItem(fSurfaceTestMenuItem);
256	fDiskMenu->AddItem(fRescanMenuItem);
257
258	fMenuBar->AddItem(fDiskMenu);
259
260	// Parition menu
261	fPartitionMenu = new BMenu(B_TRANSLATE("Partition"));
262	fPartitionMenu->AddItem(fCreateMenuItem);
263
264	fFormatMenu = new BMenu(B_TRANSLATE("Format"));
265	fPartitionMenu->AddItem(fFormatMenu);
266
267	fPartitionMenu->AddItem(fChangeMenuItem);
268	fPartitionMenu->AddItem(fDeleteMenuItem);
269
270	fPartitionMenu->AddSeparatorItem();
271
272	fPartitionMenu->AddItem(fMountMenuItem);
273	fPartitionMenu->AddItem(fUnmountMenuItem);
274
275	fPartitionMenu->AddSeparatorItem();
276
277	fPartitionMenu->AddItem(fMountAllMenuItem);
278
279	fPartitionMenu->AddSeparatorItem();
280
281	fPartitionMenu->AddItem(fOpenDiskProbeMenuItem);
282	fMenuBar->AddItem(fPartitionMenu);
283
284	AddChild(fMenuBar);
285
286	// Partition / Drives context menu
287	fContextMenu = new BPopUpMenu("Partition", false, false);
288	fCreateContextMenuItem = new BMenuItem(B_TRANSLATE("Create" B_UTF8_ELLIPSIS),
289		new BMessage(MSG_CREATE), 'C');
290	fChangeContextMenuItem = new BMenuItem(
291		B_TRANSLATE("Change parameters" B_UTF8_ELLIPSIS),
292		new BMessage(MSG_CHANGE));
293	fDeleteContextMenuItem = new BMenuItem(B_TRANSLATE("Delete"),
294		new BMessage(MSG_DELETE), 'D');
295	fMountContextMenuItem = new BMenuItem(B_TRANSLATE("Mount"),
296		new BMessage(MSG_MOUNT), 'M');
297	fUnmountContextMenuItem = new BMenuItem(B_TRANSLATE("Unmount"),
298		new BMessage(MSG_UNMOUNT), 'U');
299	fOpenDiskProbeContextMenuItem = new BMenuItem(B_TRANSLATE("Open with DiskProbe"),
300		new BMessage(MSG_OPEN_DISKPROBE));
301	fFormatContextMenuItem = new BMenu(B_TRANSLATE("Format"));
302
303	fContextMenu->AddItem(fCreateContextMenuItem);
304	fContextMenu->AddItem(fFormatContextMenuItem);
305	fContextMenu->AddItem(fChangeContextMenuItem);
306	fContextMenu->AddItem(fDeleteContextMenuItem);
307	fContextMenu->AddSeparatorItem();
308	fContextMenu->AddItem(fMountContextMenuItem);
309	fContextMenu->AddItem(fUnmountContextMenuItem);
310	fContextMenu->AddSeparatorItem();
311	fContextMenu->AddItem(fOpenDiskProbeContextMenuItem);
312	fContextMenu->SetTargetForItems(this);
313
314	// add DiskView
315	BRect r(Bounds());
316	r.top = fMenuBar->Frame().bottom + 1;
317	r.bottom = floorf(r.top + r.Height() * 0.33);
318	fDiskView = new DiskView(r, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
319		fSpaceIDMap);
320	AddChild(fDiskView);
321
322	// add PartitionListView
323	r.top = r.bottom + 2;
324	r.bottom = Bounds().bottom;
325	r.InsetBy(-1, -1);
326	fListView = new PartitionListView(r, B_FOLLOW_ALL);
327	AddChild(fListView);
328
329	// configure PartitionListView
330	fListView->SetSelectionMode(B_SINGLE_SELECTION_LIST);
331	fListView->SetSelectionMessage(new BMessage(MSG_PARTITION_ROW_SELECTED));
332	fListView->SetTarget(this);
333	fListView->MakeFocus(true);
334
335	status_t status = fDiskDeviceRoster.StartWatching(BMessenger(this));
336	if (status != B_OK) {
337		fprintf(stderr, "Failed to start watching for device changes: %s\n",
338			strerror(status));
339	}
340
341	// visit all disks in the system and show their contents
342	_ScanDrives();
343
344	if (!be_roster->IsRunning(kDeskbarSignature))
345		SetFlags(Flags() | B_NOT_MINIMIZABLE);
346}
347
348
349MainWindow::~MainWindow()
350{
351	BDiskDeviceRoster().StopWatching(this);
352	delete fCurrentDisk;
353	delete fContextMenu;
354}
355
356
357void
358MainWindow::MessageReceived(BMessage* message)
359{
360	switch (message->what) {
361		case MSG_MOUNT_ALL:
362			_MountAll();
363			break;
364		case MSG_MOUNT:
365			_Mount(fCurrentDisk, fCurrentPartitionID);
366			break;
367		case MSG_UNMOUNT:
368			_Unmount(fCurrentDisk, fCurrentPartitionID);
369			break;
370
371		case MSG_FORMAT:
372			printf("MSG_FORMAT\n");
373			break;
374
375		case MSG_CREATE:
376			_Create(fCurrentDisk, fCurrentPartitionID);
377			break;
378
379		case MSG_INITIALIZE: {
380			BString diskSystemName;
381			if (message->FindString("disk system", &diskSystemName) != B_OK)
382				break;
383			_Initialize(fCurrentDisk, fCurrentPartitionID, diskSystemName);
384			break;
385		}
386
387		case MSG_CHANGE:
388			_ChangeParameters(fCurrentDisk, fCurrentPartitionID);
389			break;
390
391		case MSG_DELETE:
392			_Delete(fCurrentDisk, fCurrentPartitionID);
393			break;
394
395		case MSG_EJECT:
396			// TODO: completely untested, especially interesting
397			// if partition list behaves when partitions disappear
398			if (fCurrentDisk) {
399				// TODO: only if no partitions are mounted anymore?
400				fCurrentDisk->Eject(true);
401				_ScanDrives();
402			}
403			break;
404		case MSG_OPEN_DISKPROBE:
405		{
406			PartitionListRow* row = dynamic_cast<PartitionListRow*>(
407				fListView->CurrentSelection());
408			const char* args[] = { row->DevicePath(), NULL };
409
410			be_roster->Launch("application/x-vnd.Haiku-DiskProbe", 1,
411				(char**)args);
412
413			break;
414		}
415		case MSG_SURFACE_TEST:
416			printf("MSG_SURFACE_TEST\n");
417			break;
418
419		// TODO: this could probably be done better!
420		case B_DEVICE_UPDATE:
421			printf("B_DEVICE_UPDATE\n");
422		case MSG_RESCAN:
423			_ScanDrives();
424			break;
425
426		case MSG_PARTITION_ROW_SELECTED: {
427			// selection of partitions via list view
428			_AdaptToSelectedPartition();
429
430			BPoint where;
431			uint32 buttons;
432			fListView->GetMouse(&where, &buttons);
433			where.x += 2; // to prevent occasional select
434			if (buttons & B_SECONDARY_MOUSE_BUTTON)
435				fContextMenu->Go(fListView->ConvertToScreen(where),
436					true, false, true);
437			break;
438		}
439		case MSG_SELECTED_PARTITION_ID: {
440			// selection of partitions via disk view
441			partition_id id;
442			if (message->FindInt32("partition_id", &id) == B_OK) {
443				if (BRow* row = fListView->FindRow(id)) {
444					fListView->DeselectAll();
445					fListView->AddToSelection(row);
446					_AdaptToSelectedPartition();
447				}
448			}
449			BPoint where;
450			uint32 buttons;
451			fListView->GetMouse(&where, &buttons);
452			where.x += 2; // to prevent occasional select
453			if (buttons & B_SECONDARY_MOUSE_BUTTON)
454				fContextMenu->Go(fListView->ConvertToScreen(where),
455					true, false, true);
456			break;
457		}
458
459		case MSG_UPDATE_ZOOM_LIMITS:
460			_UpdateWindowZoomLimits();
461			break;
462
463		default:
464			BWindow::MessageReceived(message);
465			break;
466	}
467}
468
469
470bool
471MainWindow::QuitRequested()
472{
473	// TODO: ask about any unsaved changes
474	be_app->PostMessage(B_QUIT_REQUESTED);
475	Hide();
476	return false;
477}
478
479
480// #pragma mark -
481
482
483status_t
484MainWindow::StoreSettings(BMessage* archive) const
485{
486	if (archive->ReplaceRect("window frame", Frame()) < B_OK)
487		archive->AddRect("window frame", Frame());
488
489	BMessage columnSettings;
490	fListView->SaveState(&columnSettings);
491	if (archive->ReplaceMessage("column settings", &columnSettings) < B_OK)
492		archive->AddMessage("column settings", &columnSettings);
493
494	return B_OK;
495}
496
497
498status_t
499MainWindow::RestoreSettings(BMessage* archive)
500{
501	BRect frame;
502	if (archive->FindRect("window frame", &frame) == B_OK) {
503		BScreen screen(this);
504		if (frame.Intersects(screen.Frame())) {
505			MoveTo(frame.LeftTop());
506			ResizeTo(frame.Width(), frame.Height());
507		}
508	}
509
510	BMessage columnSettings;
511	if (archive->FindMessage("column settings", &columnSettings) == B_OK)
512		fListView->LoadState(&columnSettings);
513
514	return B_OK;
515}
516
517
518void
519MainWindow::ApplyDefaultSettings()
520{
521	if (!Lock())
522		return;
523
524	fListView->ResizeAllColumnsToPreferred();
525
526	// Adjust window size for convenience
527	BScreen screen(this);
528	float windowWidth = Frame().Width();
529	float windowHeight = Frame().Height();
530
531	float enlargeWidthBy = fListView->PreferredSize().width
532		- fListView->Bounds().Width();
533	float enlargeHeightBy = fListView->PreferredSize().height
534		- fListView->Bounds().Height();
535
536	if (enlargeWidthBy > 0.0f)
537		windowWidth += enlargeWidthBy;
538	if (enlargeHeightBy > 0.0f)
539		windowHeight += enlargeHeightBy;
540
541	if (windowWidth > screen.Frame().Width() - 20.0f)
542		windowWidth = screen.Frame().Width() - 20.0f;
543	if (windowHeight > screen.Frame().Height() - 20.0f)
544		windowHeight = screen.Frame().Height() - 20.0f;
545
546	ResizeTo(windowWidth, windowHeight);
547	CenterOnScreen();
548
549	Unlock();
550}
551
552
553// #pragma mark -
554
555
556void
557MainWindow::_ScanDrives()
558{
559	fSpaceIDMap.Clear();
560	int32 diskCount = 0;
561	ListPopulatorVisitor driveVisitor(fListView, diskCount, fSpaceIDMap);
562	fDiskDeviceRoster.VisitEachPartition(&driveVisitor);
563	fDiskView->SetDiskCount(diskCount);
564
565	// restore selection
566	PartitionListRow* previousSelection
567		= fListView->FindRow(fCurrentPartitionID);
568	if (previousSelection) {
569		fListView->SetFocusRow(previousSelection, true);
570		_UpdateMenus(fCurrentDisk, fCurrentPartitionID,
571			previousSelection->ParentID());
572		fDiskView->ForceUpdate();
573	} else {
574		_UpdateMenus(NULL, -1, -1);
575	}
576
577	PostMessage(MSG_UPDATE_ZOOM_LIMITS);
578}
579
580
581// #pragma mark -
582
583
584void
585MainWindow::_AdaptToSelectedPartition()
586{
587	partition_id diskID = -1;
588	partition_id partitionID = -1;
589	partition_id parentID = -1;
590
591	BRow* _selectedRow = fListView->CurrentSelection();
592	if (_selectedRow) {
593		// go up to top level row
594		BRow* _topLevelRow = _selectedRow;
595		BRow* parent = NULL;
596		while (fListView->FindParent(_topLevelRow, &parent, NULL))
597			_topLevelRow = parent;
598
599		PartitionListRow* topLevelRow
600			= dynamic_cast<PartitionListRow*>(_topLevelRow);
601		PartitionListRow* selectedRow
602			= dynamic_cast<PartitionListRow*>(_selectedRow);
603
604		if (topLevelRow)
605			diskID = topLevelRow->ID();
606		if (selectedRow) {
607			partitionID = selectedRow->ID();
608			parentID = selectedRow->ParentID();
609		}
610	}
611
612	_SetToDiskAndPartition(diskID, partitionID, parentID);
613}
614
615
616void
617MainWindow::_SetToDiskAndPartition(partition_id disk, partition_id partition,
618	partition_id parent)
619{
620//printf("MainWindow::_SetToDiskAndPartition(disk: %ld, partition: %ld, "
621//	"parent: %ld)\n", disk, partition, parent);
622
623	BDiskDevice* oldDisk = NULL;
624	if (!fCurrentDisk || fCurrentDisk->ID() != disk) {
625		oldDisk = fCurrentDisk;
626		fCurrentDisk = NULL;
627		if (disk >= 0) {
628			BDiskDevice* newDisk = new BDiskDevice();
629			status_t status = newDisk->SetTo(disk);
630			if (status != B_OK) {
631				printf("error switching disks: %s\n", strerror(status));
632				delete newDisk;
633			} else
634				fCurrentDisk = newDisk;
635		}
636	}
637
638	fCurrentPartitionID = partition;
639
640	fDiskView->SetDisk(fCurrentDisk, fCurrentPartitionID);
641	_UpdateMenus(fCurrentDisk, fCurrentPartitionID, parent);
642
643	delete oldDisk;
644}
645
646
647void
648MainWindow::_UpdateMenus(BDiskDevice* disk,
649	partition_id selectedPartition, partition_id parentID)
650{
651	while (BMenuItem* item = fFormatMenu->RemoveItem((int32)0))
652		delete item;
653	while (BMenuItem* item = fDiskInitMenu->RemoveItem((int32)0))
654		delete item;
655	while (BMenuItem* item = fFormatContextMenuItem->RemoveItem((int32)0))
656		delete item;
657
658	fCreateMenuItem->SetEnabled(false);
659	fUnmountMenuItem->SetEnabled(false);
660	fDiskInitMenu->SetEnabled(false);
661	fFormatMenu->SetEnabled(false);
662
663	fCreateContextMenuItem->SetEnabled(false);
664	fUnmountContextMenuItem->SetEnabled(false);
665	fFormatContextMenuItem->SetEnabled(false);
666
667	if (disk == NULL) {
668		fWipeMenuItem->SetEnabled(false);
669		fEjectMenuItem->SetEnabled(false);
670		fSurfaceTestMenuItem->SetEnabled(false);
671		fOpenDiskProbeMenuItem->SetEnabled(false);
672		fOpenDiskProbeContextMenuItem->SetEnabled(false);
673	} else {
674//		fWipeMenuItem->SetEnabled(true);
675		fWipeMenuItem->SetEnabled(false);
676		fEjectMenuItem->SetEnabled(disk->IsRemovableMedia());
677//		fSurfaceTestMenuItem->SetEnabled(true);
678		fSurfaceTestMenuItem->SetEnabled(false);
679
680		// Create menu and items
681		BPartition* parentPartition = NULL;
682		if (selectedPartition <= -2) {
683			// a partitionable space item is selected
684			parentPartition = disk->FindDescendant(parentID);
685		}
686
687		if (parentPartition && parentPartition->ContainsPartitioningSystem()) {
688			fCreateMenuItem->SetEnabled(true);
689			fCreateContextMenuItem->SetEnabled(true);
690		}
691		bool prepared = disk->PrepareModifications() == B_OK;
692
693		fFormatMenu->SetEnabled(prepared);
694		fDeleteMenuItem->SetEnabled(prepared);
695		fChangeMenuItem->SetEnabled(prepared);
696
697		fFormatContextMenuItem->SetEnabled(prepared);
698		fDeleteContextMenuItem->SetEnabled(prepared);
699		fChangeContextMenuItem->SetEnabled(prepared);
700
701		BPartition* partition = disk->FindDescendant(selectedPartition);
702
703		BDiskSystem diskSystem;
704		fDiskDeviceRoster.RewindDiskSystems();
705		while (fDiskDeviceRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
706			if (!diskSystem.SupportsInitializing())
707				continue;
708
709			BMessage* message = new BMessage(MSG_INITIALIZE);
710			message->AddInt32("parent id", parentID);
711			message->AddString("disk system", diskSystem.PrettyName());
712
713			BString label = diskSystem.PrettyName();
714			label << B_UTF8_ELLIPSIS;
715			BMenuItem* item = new BMenuItem(label.String(), message);
716
717			// TODO: Very unintuitive that we have to use PrettyName (vs Name)
718			item->SetEnabled(partition != NULL
719				&& partition->CanInitialize(diskSystem.PrettyName()));
720
721			if (disk->ID() == selectedPartition
722				&& !diskSystem.IsFileSystem()) {
723				// Disk is selected, and DiskSystem is a partition map
724				fDiskInitMenu->AddItem(item);
725			} else if (diskSystem.IsFileSystem()) {
726				// Otherwise a filesystem
727				fFormatMenu->AddItem(item);
728
729				// Context menu
730				BMessage* message = new BMessage(MSG_INITIALIZE);
731				message->AddInt32("parent id", parentID);
732				message->AddString("disk system", diskSystem.PrettyName());
733				BMenuItem* popUpItem = new BMenuItem(label.String(), message);
734				popUpItem->SetEnabled(partition != NULL
735					&& partition->CanInitialize(diskSystem.PrettyName()));
736				fFormatContextMenuItem->AddItem(popUpItem);
737				fFormatContextMenuItem->SetTargetForItems(this);
738			}
739		}
740
741		// Mount items
742		if (partition != NULL) {
743			bool writable = !partition->IsReadOnly()
744				&& partition->Device()->HasMedia();
745			bool notMountedAndWritable = !partition->IsMounted() && writable;
746
747			fFormatMenu->SetEnabled(writable && fFormatMenu->CountItems() > 0);
748
749			fDiskInitMenu->SetEnabled(notMountedAndWritable
750				&& partition->IsDevice()
751				&& fDiskInitMenu->CountItems() > 0);
752
753			fChangeMenuItem->SetEnabled(writable
754				&& partition->CanEditParameters());
755			fChangeContextMenuItem->SetEnabled(writable
756				&& partition->CanEditParameters());
757
758			fDeleteMenuItem->SetEnabled(notMountedAndWritable
759				&& !partition->IsDevice());
760			fDeleteContextMenuItem->SetEnabled(notMountedAndWritable
761				&& !partition->IsDevice());
762
763			fMountMenuItem->SetEnabled(!partition->IsMounted());
764			fMountContextMenuItem->SetEnabled(!partition->IsMounted());
765
766			fFormatContextMenuItem->SetEnabled(notMountedAndWritable
767				&& fFormatContextMenuItem->CountItems() > 0);
768
769			bool unMountable = false;
770			if (partition->IsMounted()) {
771				// see if this partition is the boot volume
772				BVolume volume;
773				BVolume bootVolume;
774				if (BVolumeRoster().GetBootVolume(&bootVolume) == B_OK
775					&& partition->GetVolume(&volume) == B_OK) {
776					unMountable = volume != bootVolume;
777				} else
778					unMountable = true;
779			}
780			fUnmountMenuItem->SetEnabled(unMountable);
781			fUnmountContextMenuItem->SetEnabled(unMountable);
782		} else {
783			fDeleteMenuItem->SetEnabled(false);
784			fChangeMenuItem->SetEnabled(false);
785			fMountMenuItem->SetEnabled(false);
786			fFormatMenu->SetEnabled(false);
787			fDiskInitMenu->SetEnabled(false);
788
789			fDeleteContextMenuItem->SetEnabled(false);
790			fChangeContextMenuItem->SetEnabled(false);
791			fMountContextMenuItem->SetEnabled(false);
792			fFormatContextMenuItem->SetEnabled(false);
793		}
794
795		if (prepared)
796			disk->CancelModifications();
797
798		fOpenDiskProbeMenuItem->SetEnabled(true);
799		fOpenDiskProbeContextMenuItem->SetEnabled(true);
800
801		fMountAllMenuItem->SetEnabled(true);
802	}
803	if (selectedPartition < 0) {
804		fDeleteMenuItem->SetEnabled(false);
805		fChangeMenuItem->SetEnabled(false);
806		fMountMenuItem->SetEnabled(false);
807
808		fDeleteContextMenuItem->SetEnabled(false);
809		fChangeContextMenuItem->SetEnabled(false);
810		fMountContextMenuItem->SetEnabled(false);
811	}
812}
813
814
815void
816MainWindow::_DisplayPartitionError(BString _message,
817	const BPartition* partition, status_t error) const
818{
819	char message[1024];
820
821	if (partition && _message.FindFirst("%s") >= 0) {
822		BString name;
823		name << "\"" << partition->ContentName() << "\"";
824		snprintf(message, sizeof(message), _message.String(), name.String());
825	} else {
826		_message.ReplaceAll("%s", "");
827		strlcpy(message, _message.String(), sizeof(message));
828	}
829
830	if (error < B_OK) {
831		BString helper = message;
832		const char* errorString
833			= B_TRANSLATE_COMMENT("Error: ", "in any error alert");
834		snprintf(message, sizeof(message), "%s\n\n%s%s", helper.String(),
835			errorString, strerror(error));
836	}
837
838	BAlert* alert = new BAlert("error", message, B_TRANSLATE("OK"), NULL, NULL,
839		B_WIDTH_FROM_WIDEST, error < B_OK ? B_STOP_ALERT : B_INFO_ALERT);
840	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
841	alert->Go(NULL);
842}
843
844
845// #pragma mark -
846
847
848void
849MainWindow::_Mount(BDiskDevice* disk, partition_id selectedPartition)
850{
851	if (!disk || selectedPartition < 0) {
852		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
853			"entry from the list."));
854		return;
855	}
856
857	BPartition* partition = disk->FindDescendant(selectedPartition);
858	if (!partition) {
859		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
860			"partition by ID."));
861		return;
862	}
863
864	if (!partition->IsMounted()) {
865		status_t status = partition->Mount();
866		if (status != B_OK) {
867			_DisplayPartitionError(B_TRANSLATE("Could not mount partition %s."),
868				partition, status);
869		} else {
870			// successful mount, adapt to the changes
871			_ScanDrives();
872		}
873	} else {
874		_DisplayPartitionError(
875			B_TRANSLATE("The partition %s is already mounted."), partition);
876	}
877}
878
879
880void
881MainWindow::_Unmount(BDiskDevice* disk, partition_id selectedPartition)
882{
883	if (!disk || selectedPartition < 0) {
884		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
885			"entry from the list."));
886		return;
887	}
888
889	BPartition* partition = disk->FindDescendant(selectedPartition);
890	if (!partition) {
891		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
892			"partition by ID."));
893		return;
894	}
895
896	if (partition->IsMounted()) {
897		BPath path;
898		partition->GetMountPoint(&path);
899		status_t status = partition->Unmount();
900		if (status != B_OK) {
901			BString message = B_TRANSLATE("Could not unmount partition");
902			message << " \"" << partition->ContentName() << "\":\n\t"
903				<< strerror(status) << "\n\n"
904				<< B_TRANSLATE("Should unmounting be forced?\n\n"
905				"Note: If an application is currently writing to the volume, "
906				"unmounting it now might result in loss of data.\n");
907
908			BAlert* alert = new BAlert(B_TRANSLATE("Force unmount"), message,
909				B_TRANSLATE("Cancel"), B_TRANSLATE("Force unmount"), NULL,
910				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
911			alert->SetShortcut(0, B_ESCAPE);
912
913			if (alert->Go() == 1)
914				status = partition->Unmount(B_FORCE_UNMOUNT);
915			else
916				return;
917		}
918
919		if (status != B_OK) {
920			_DisplayPartitionError(
921				B_TRANSLATE("Could not unmount partition %s."),
922				partition, status);
923		} else {
924			if (dev_for_path(path.Path()) == dev_for_path("/"))
925				rmdir(path.Path());
926			// successful unmount, adapt to the changes
927			_ScanDrives();
928		}
929	} else {
930		_DisplayPartitionError(
931			B_TRANSLATE("The partition %s is already unmounted."),
932			partition);
933	}
934}
935
936
937void
938MainWindow::_MountAll()
939{
940	MountAllVisitor visitor;
941	fDiskDeviceRoster.VisitEachPartition(&visitor);
942}
943
944
945// #pragma mark -
946
947
948void
949MainWindow::_Initialize(BDiskDevice* disk, partition_id selectedPartition,
950	const BString& diskSystemName)
951{
952	if (!disk || selectedPartition < 0) {
953		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
954			"entry from the list."));
955		return;
956	}
957
958	if (disk->IsReadOnly()) {
959		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
960		return;
961	}
962
963	BPartition* partition = disk->FindDescendant(selectedPartition);
964	if (!partition) {
965		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
966			"partition by ID."));
967		return;
968	}
969
970	if (partition->IsMounted()) {
971		if (partition->Unmount() != B_OK) {
972			// Probably it's the system partition
973			_DisplayPartitionError(
974				B_TRANSLATE("The partition cannot be unmounted."));
975
976			return;
977		}
978	}
979
980	BDiskSystem diskSystem;
981	fDiskDeviceRoster.RewindDiskSystems();
982	bool found = false;
983	while (fDiskDeviceRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
984		if (diskSystem.SupportsInitializing()) {
985			if (diskSystemName == diskSystem.PrettyName()) {
986				found = true;
987				break;
988			}
989		}
990	}
991
992	if (!found) {
993		_DisplayPartitionError(B_TRANSLATE("Disk system \"%s\" not found!"));
994		return;
995	}
996
997	BString message;
998
999	if (diskSystem.IsFileSystem()) {
1000		BString intelExtendedPartition = "Intel Extended Partition";
1001		if (disk->ID() == selectedPartition) {
1002			message = B_TRANSLATE("Are you sure you "
1003				"want to format a raw disk? (Most people initialize the disk "
1004				"with a partitioning system first) You will be asked "
1005				"again before changes are written to the disk.");
1006		} else if (partition->ContentName()
1007			&& strlen(partition->ContentName()) > 0) {
1008			message = B_TRANSLATE("Are you sure you "
1009				"want to format the partition \"%s\"? You will be asked "
1010				"again before changes are written to the disk.");
1011			message.ReplaceFirst("%s", partition->ContentName());
1012		} else if (partition->Type() == intelExtendedPartition) {
1013			message = B_TRANSLATE("Are you sure you "
1014				"want to format the Intel Extended Partition? Any "
1015				"subpartitions it contains will be overwritten if you "
1016				"continue. You will be asked again before changes are "
1017				"written to the disk.");
1018		} else {
1019			message = B_TRANSLATE("Are you sure you "
1020				"want to format the partition? You will be asked again "
1021				"before changes are written to the disk.");
1022		}
1023	} else {
1024		message = B_TRANSLATE("Are you sure you "
1025			"want to initialize the selected disk? All data will be lost. "
1026			"You will be asked again before changes are written to the "
1027			"disk.\n");
1028	}
1029	BAlert* alert = new BAlert("first notice", message.String(),
1030		B_TRANSLATE("Continue"), B_TRANSLATE("Cancel"), NULL,
1031		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1032	alert->SetShortcut(1, B_ESCAPE);
1033	int32 choice = alert->Go();
1034
1035	if (choice == 1)
1036		return;
1037
1038	ModificationPreparer modificationPreparer(disk);
1039	status_t status = modificationPreparer.ModificationStatus();
1040	if (status != B_OK) {
1041		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1042			"disk for modifications."), NULL, status);
1043		return;
1044	}
1045
1046	BString name;
1047	BString parameters;
1048	InitParametersPanel* panel = new InitParametersPanel(this, diskSystemName,
1049		partition);
1050	if (panel->Go(name, parameters) != B_OK)
1051		return;
1052
1053	bool supportsName = diskSystem.SupportsContentName();
1054	BString validatedName(name);
1055	status = partition->ValidateInitialize(diskSystem.PrettyName(),
1056		supportsName ? &validatedName : NULL, parameters.String());
1057	if (status != B_OK) {
1058		_DisplayPartitionError(B_TRANSLATE("Validation of the given "
1059			"initialization parameters failed."), partition, status);
1060		return;
1061	}
1062
1063	BString previousName = partition->ContentName();
1064
1065	status = partition->Initialize(diskSystem.PrettyName(),
1066		supportsName ? validatedName.String() : NULL, parameters.String());
1067	if (status != B_OK) {
1068		_DisplayPartitionError(B_TRANSLATE("Initialization of the partition "
1069			"%s failed. (Nothing has been written to disk.)"), partition,
1070			status);
1071		return;
1072	}
1073
1074	// Also set the partition name in the partition table if supported
1075	if (partition->CanSetName()
1076		&& partition->ValidateSetName(&validatedName) == B_OK) {
1077		partition->SetName(validatedName.String());
1078	}
1079
1080	// everything looks fine, we are ready to actually write the changes
1081	// to disk
1082
1083	// Warn the user one more time...
1084	if (previousName.Length() > 0) {
1085		if (partition->IsDevice()) {
1086			message = B_TRANSLATE("Are you sure you "
1087				"want to write the changes back to disk now?\n\n"
1088				"All data on the disk %s will be irretrievably lost if you "
1089				"do so!");
1090			message.ReplaceFirst("%s", previousName.String());
1091		} else {
1092			message = B_TRANSLATE("Are you sure you "
1093				"want to write the changes back to disk now?\n\n"
1094				"All data on the partition %s will be irretrievably lost if you "
1095				"do so!");
1096			message.ReplaceFirst("%s", previousName.String());
1097		}
1098	} else {
1099		if (partition->IsDevice()) {
1100			message = B_TRANSLATE("Are you sure you "
1101				"want to write the changes back to disk now?\n\n"
1102				"All data on the selected disk will be irretrievably lost if "
1103				"you do so!");
1104		} else {
1105			message = B_TRANSLATE("Are you sure you "
1106				"want to write the changes back to disk now?\n\n"
1107				"All data on the selected partition will be irretrievably lost "
1108				"if you do so!");
1109		}
1110	}
1111	alert = new BAlert("final notice", message.String(),
1112		B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1113		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1114	alert->SetShortcut(1, B_ESCAPE);
1115	choice = alert->Go();
1116
1117	if (choice == 1)
1118		return;
1119
1120	// commit
1121	status = modificationPreparer.CommitModifications();
1122
1123	// The partition pointer is toast now! Use the partition ID to
1124	// retrieve it again.
1125	partition = disk->FindDescendant(selectedPartition);
1126
1127	if (status == B_OK) {
1128		if (diskSystem.IsFileSystem()) {
1129			_DisplayPartitionError(B_TRANSLATE("The partition %s has been "
1130				"successfully formatted.\n"), partition);
1131		} else {
1132			_DisplayPartitionError(B_TRANSLATE("The disk has been "
1133				"successfully initialized.\n"), partition);
1134		}
1135	} else {
1136		if (diskSystem.IsFileSystem()) {
1137			_DisplayPartitionError(B_TRANSLATE("Failed to format the "
1138				"partition %s!\n"), partition, status);
1139		} else {
1140			_DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
1141				"disk %s!\n"), partition, status);
1142		}
1143	}
1144
1145	_ScanDrives();
1146}
1147
1148
1149void
1150MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition)
1151{
1152	if (!disk || selectedPartition > -2) {
1153		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1154			"is not empty."));
1155		return;
1156	}
1157
1158	if (disk->IsReadOnly()) {
1159		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1160		return;
1161	}
1162
1163	PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>(
1164		fListView->CurrentSelection());
1165	if (!currentSelection) {
1166		_DisplayPartitionError(B_TRANSLATE("There was an error acquiring the "
1167			"partition row."));
1168		return;
1169	}
1170
1171	BPartition* parent = disk->FindDescendant(currentSelection->ParentID());
1172	if (!parent) {
1173		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1174			"does not have a parent partition."));
1175		return;
1176	}
1177
1178	if (!parent->ContainsPartitioningSystem()) {
1179		_DisplayPartitionError(B_TRANSLATE("The selected partition does not "
1180			"contain a partitioning system."));
1181		return;
1182	}
1183
1184	ModificationPreparer modificationPreparer(disk);
1185	status_t status = modificationPreparer.ModificationStatus();
1186	if (status != B_OK) {
1187		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1188			"disk for modifications."), NULL, status);
1189		return;
1190	}
1191
1192	// get partitioning info
1193	BPartitioningInfo partitioningInfo;
1194	status_t error = parent->GetPartitioningInfo(&partitioningInfo);
1195	if (error != B_OK) {
1196		_DisplayPartitionError(B_TRANSLATE("Could not acquire partitioning "
1197			"information."));
1198		return;
1199	}
1200
1201	int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
1202	if (spacesCount == 0) {
1203		_DisplayPartitionError(B_TRANSLATE("There's no space on the partition "
1204			"where a child partition could be created."));
1205		return;
1206	}
1207
1208	BString name, type, parameters;
1209	off_t offset = currentSelection->Offset();
1210	off_t size = currentSelection->Size();
1211
1212	CreateParametersPanel* panel = new CreateParametersPanel(this, parent,
1213		offset, size);
1214	status = panel->Go(offset, size, name, type, parameters);
1215	if (status != B_OK) {
1216		if (status != B_CANCELED) {
1217			_DisplayPartitionError(B_TRANSLATE("The panel could not return "
1218				"successfully."), NULL, status);
1219		}
1220		return;
1221	}
1222
1223	status = parent->ValidateCreateChild(&offset, &size, type.String(),
1224		&name, parameters.String());
1225
1226	if (status != B_OK) {
1227		_DisplayPartitionError(B_TRANSLATE("Validation of the given creation "
1228			"parameters failed."), NULL, status);
1229		return;
1230	}
1231
1232	// Warn the user one more time...
1233	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1234		"want to write the changes back to disk now?\n\n"
1235		"All data on the partition will be irretrievably lost if you do "
1236		"so!"), B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1237		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1238	alert->SetShortcut(1, B_ESCAPE);
1239	int32 choice = alert->Go();
1240
1241	if (choice == 1)
1242		return;
1243
1244	status = parent->CreateChild(offset, size, type.String(), name.String(),
1245		parameters.String());
1246
1247	if (status != B_OK) {
1248		_DisplayPartitionError(B_TRANSLATE("Creation of the partition has "
1249			"failed."), NULL, status);
1250		return;
1251	}
1252
1253	// commit
1254	status = modificationPreparer.CommitModifications();
1255
1256	if (status != B_OK) {
1257		_DisplayPartitionError(B_TRANSLATE("Failed to create the "
1258			"partition. No changes have been written to disk."), NULL, status);
1259		return;
1260	}
1261
1262	// The disk layout has changed, update disk information
1263	bool updated;
1264	status = disk->Update(&updated);
1265
1266	_ScanDrives();
1267	fDiskView->ForceUpdate();
1268}
1269
1270
1271void
1272MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition)
1273{
1274	if (!disk || selectedPartition < 0) {
1275		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1276			"entry from the list."));
1277		return;
1278	}
1279
1280	if (disk->IsReadOnly()) {
1281		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1282		return;
1283	}
1284
1285	BPartition* partition = disk->FindDescendant(selectedPartition);
1286	if (!partition) {
1287		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1288			"partition by ID."));
1289		return;
1290	}
1291
1292	BPartition* parent = partition->Parent();
1293	if (!parent) {
1294		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1295			"does not have a parent partition."));
1296		return;
1297	}
1298
1299	ModificationPreparer modificationPreparer(disk);
1300	status_t status = modificationPreparer.ModificationStatus();
1301	if (status != B_OK) {
1302		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1303			"disk for modifications."), NULL, status);
1304		return;
1305	}
1306
1307	if (!parent->CanDeleteChild(partition->Index())) {
1308		_DisplayPartitionError(
1309			B_TRANSLATE("Cannot delete the selected partition."));
1310		return;
1311	}
1312
1313	// Warn the user one more time...
1314	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1315		"want to delete the selected partition?\n\n"
1316		"All data on the partition will be irretrievably lost if you "
1317		"do so!"), B_TRANSLATE("Delete partition"), B_TRANSLATE("Cancel"), NULL,
1318		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1319	alert->SetShortcut(1, B_ESCAPE);
1320	int32 choice = alert->Go();
1321
1322	if (choice == 1)
1323		return;
1324
1325	status = parent->DeleteChild(partition->Index());
1326	if (status != B_OK) {
1327		_DisplayPartitionError(B_TRANSLATE("Could not delete the selected "
1328			"partition."), NULL, status);
1329		return;
1330	}
1331
1332	status = modificationPreparer.CommitModifications();
1333
1334	if (status != B_OK) {
1335		_DisplayPartitionError(B_TRANSLATE("Failed to delete the partition. "
1336			"No changes have been written to disk."), NULL, status);
1337		return;
1338	}
1339
1340	_ScanDrives();
1341	fDiskView->ForceUpdate();
1342}
1343
1344
1345void
1346MainWindow::_ChangeParameters(BDiskDevice* disk, partition_id selectedPartition)
1347{
1348	if (disk == NULL || selectedPartition < 0) {
1349		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1350			"entry from the list."));
1351		return;
1352	}
1353
1354	if (disk->IsReadOnly()) {
1355		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1356		return;
1357	}
1358
1359	BPartition* partition = disk->FindDescendant(selectedPartition);
1360	if (partition == NULL) {
1361		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1362			"partition by ID."));
1363		return;
1364	}
1365
1366	ModificationPreparer modificationPreparer(disk);
1367	status_t status = modificationPreparer.ModificationStatus();
1368	if (status != B_OK) {
1369		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1370			"disk for modifications."), NULL, status);
1371		return;
1372	}
1373
1374	ChangeParametersPanel* panel = new ChangeParametersPanel(this, partition);
1375
1376	BString name, type, parameters;
1377	status = panel->Go(name, type, parameters);
1378	if (status != B_OK) {
1379		if (status != B_CANCELED) {
1380			_DisplayPartitionError(B_TRANSLATE("The panel experienced a "
1381				"problem!"), NULL, status);
1382		}
1383		return;
1384	}
1385
1386	if (partition->CanSetType())
1387		status = partition->ValidateSetType(type.String());
1388	if (status == B_OK && partition->CanSetName())
1389		status = partition->ValidateSetName(&name);
1390	if (status != B_OK) {
1391		_DisplayPartitionError(B_TRANSLATE("Validation of the given parameters "
1392			"failed."));
1393		return;
1394	}
1395
1396	// Warn the user one more time...
1397	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1398		"want to change parameters of the selected partition?\n\n"
1399		"The partition may no longer be recognized by other operating systems "
1400		"anymore!"), B_TRANSLATE("Change parameters"), B_TRANSLATE("Cancel"),
1401		NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1402	alert->SetShortcut(1, B_ESCAPE);
1403	int32 choice = alert->Go();
1404
1405	if (choice == 1)
1406		return;
1407
1408	if (partition->CanSetType())
1409		status = partition->SetType(type.String());
1410	if (status == B_OK && partition->CanSetName())
1411		status = partition->SetName(name.String());
1412	if (status == B_OK && partition->CanEditParameters())
1413		status = partition->SetParameters(parameters.String());
1414
1415	if (status != B_OK) {
1416		_DisplayPartitionError(
1417			B_TRANSLATE("Could not change the parameters of the selected "
1418				"partition."), NULL, status);
1419		return;
1420	}
1421
1422	status = modificationPreparer.CommitModifications();
1423
1424	if (status != B_OK) {
1425		_DisplayPartitionError(B_TRANSLATE("Failed to change the parameters "
1426			"of the partition. No changes have been written to disk."), NULL,
1427			status);
1428		return;
1429	}
1430
1431	_ScanDrives();
1432	fDiskView->ForceUpdate();
1433}
1434
1435
1436float
1437MainWindow::_ColumnListViewHeight(BColumnListView* list, BRow* currentRow)
1438{
1439	float height = 0;
1440	int32 rows = list->CountRows(currentRow);
1441	for (int32 i = 0; i < rows; i++) {
1442		BRow* row = list->RowAt(i, currentRow);
1443		height += row->Height() + 1;
1444		if (row->IsExpanded() && list->CountRows(row) > 0)
1445			height += _ColumnListViewHeight(list, row);
1446	}
1447	return height;
1448}
1449
1450
1451void
1452MainWindow::_UpdateWindowZoomLimits()
1453{
1454	float maxHeight = 0;
1455	int32 numColumns = fListView->CountColumns();
1456	BRow* parentRow = fListView->RowAt(0, NULL);
1457	BColumn* column = NULL;
1458
1459	maxHeight += _ColumnListViewHeight(fListView, NULL);
1460
1461	float maxWidth = fListView->LatchWidth();
1462	for (int32 i = 0; i < numColumns; i++) {
1463		column = fListView->ColumnAt(i);
1464		maxWidth += column->Width();
1465	}
1466
1467	maxHeight += B_H_SCROLL_BAR_HEIGHT;
1468	maxHeight += 1.5 * parentRow->Height();	// the label row
1469	maxHeight += fDiskView->Bounds().Height();
1470	maxHeight += fMenuBar->Bounds().Height();
1471	maxWidth += 1.5 * B_V_SCROLL_BAR_WIDTH;	// scroll bar & borders
1472
1473	SetZoomLimits(maxWidth, maxHeight);
1474}
1475
1476