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