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