xref: /haiku/src/apps/drivesetup/MainWindow.cpp (revision 072d3935c2497638e9c2502f574c133caeba9d3d)
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_OPEN_DISKPROBE			= 'opdp',
69 	MSG_SURFACE_TEST			= 'sfct',
70 	MSG_RESCAN					= 'rscn',
71 
72 	MSG_PARTITION_ROW_SELECTED	= 'prsl',
73 };
74 
75 
76 class ListPopulatorVisitor : public BDiskDeviceVisitor {
77 public:
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 
113 private:
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 
146 class MountAllVisitor : public BDiskDeviceVisitor {
147 public:
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 
166 private:
167 	PartitionListView* fPartitionList;
168 };
169 
170 
171 class ModificationPreparer {
172 public:
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 
197 private:
198 	BDiskDevice*	fDisk;
199 	status_t		fModificationStatus;
200 };
201 
202 
203 // #pragma mark -
204 
205 
206 MainWindow::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 
349 MainWindow::~MainWindow()
350 {
351 	BDiskDeviceRoster().StopWatching(this);
352 	delete fCurrentDisk;
353 	delete fContextMenu;
354 }
355 
356 
357 void
358 MainWindow::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 
470 bool
471 MainWindow::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 
483 status_t
484 MainWindow::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 
498 status_t
499 MainWindow::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 
518 void
519 MainWindow::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 
556 void
557 MainWindow::_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->AddToSelection(previousSelection);
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 
584 void
585 MainWindow::_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 
616 void
617 MainWindow::_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 
647 void
648 MainWindow::_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) {
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 		fChangeContextMenuItem->SetEnabled(prepared);
698 		fDeleteContextMenuItem->SetEnabled(prepared);
699 		fFormatContextMenuItem->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 notMountedAndWritable = !partition->IsMounted()
744 				&& !partition->IsReadOnly()
745 				&& partition->Device()->HasMedia();
746 
747 			fFormatMenu->SetEnabled(notMountedAndWritable
748 				&& fFormatMenu->CountItems() > 0);
749 
750 			fDiskInitMenu->SetEnabled(notMountedAndWritable
751 				&& partition->IsDevice()
752 				&& fDiskInitMenu->CountItems() > 0);
753 
754 			fChangeMenuItem->SetEnabled(notMountedAndWritable);
755 
756 			fDeleteMenuItem->SetEnabled(notMountedAndWritable
757 				&& !partition->IsDevice());
758 
759 			fMountMenuItem->SetEnabled(!partition->IsMounted());
760 
761 			fFormatContextMenuItem->SetEnabled(notMountedAndWritable
762 				&& fFormatContextMenuItem->CountItems() > 0);
763 			fChangeContextMenuItem->SetEnabled(notMountedAndWritable);
764 			fDeleteContextMenuItem->SetEnabled(notMountedAndWritable
765 				&& !partition->IsDevice());
766 			fMountContextMenuItem->SetEnabled(notMountedAndWritable);
767 
768 			bool unMountable = false;
769 			if (partition->IsMounted()) {
770 				// see if this partition is the boot volume
771 				BVolume volume;
772 				BVolume bootVolume;
773 				if (BVolumeRoster().GetBootVolume(&bootVolume) == B_OK
774 					&& partition->GetVolume(&volume) == B_OK) {
775 					unMountable = volume != bootVolume;
776 				} else
777 					unMountable = true;
778 			}
779 			fUnmountMenuItem->SetEnabled(unMountable);
780 			fUnmountContextMenuItem->SetEnabled(unMountable);
781 		} else {
782 			fDeleteMenuItem->SetEnabled(false);
783 			fChangeMenuItem->SetEnabled(false);
784 			fMountMenuItem->SetEnabled(false);
785 			fFormatMenu->SetEnabled(false);
786 			fDiskInitMenu->SetEnabled(false);
787 
788 			fDeleteContextMenuItem->SetEnabled(false);
789 			fChangeContextMenuItem->SetEnabled(false);
790 			fMountContextMenuItem->SetEnabled(false);
791 			fFormatContextMenuItem->SetEnabled(false);
792 		}
793 
794 		fOpenDiskProbeMenuItem->SetEnabled(true);
795 		fOpenDiskProbeContextMenuItem->SetEnabled(true);
796 
797 		if (prepared)
798 			disk->CancelModifications();
799 
800 		fMountAllMenuItem->SetEnabled(true);
801 	}
802 	if (selectedPartition < 0) {
803 		fDeleteMenuItem->SetEnabled(false);
804 		fChangeMenuItem->SetEnabled(false);
805 		fMountMenuItem->SetEnabled(false);
806 
807 		fDeleteContextMenuItem->SetEnabled(false);
808 		fChangeContextMenuItem->SetEnabled(false);
809 		fMountContextMenuItem->SetEnabled(false);
810 	}
811 }
812 
813 
814 void
815 MainWindow::_DisplayPartitionError(BString _message,
816 	const BPartition* partition, status_t error) const
817 {
818 	char message[1024];
819 
820 	if (partition && _message.FindFirst("%s") >= 0) {
821 		BString name;
822 		name << "\"" << partition->ContentName() << "\"";
823 		snprintf(message, sizeof(message), _message.String(), name.String());
824 	} else {
825 		_message.ReplaceAll("%s", "");
826 		strlcpy(message, _message.String(), sizeof(message));
827 	}
828 
829 	if (error < B_OK) {
830 		BString helper = message;
831 		const char* errorString
832 			= B_TRANSLATE_COMMENT("Error: ", "in any error alert");
833 		snprintf(message, sizeof(message), "%s\n\n%s%s", helper.String(),
834 			errorString, strerror(error));
835 	}
836 
837 	BAlert* alert = new BAlert("error", message, B_TRANSLATE("OK"), NULL, NULL,
838 		B_WIDTH_FROM_WIDEST, error < B_OK ? B_STOP_ALERT : B_INFO_ALERT);
839 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
840 	alert->Go(NULL);
841 }
842 
843 
844 // #pragma mark -
845 
846 
847 void
848 MainWindow::_Mount(BDiskDevice* disk, partition_id selectedPartition)
849 {
850 	if (!disk || selectedPartition < 0) {
851 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
852 			"entry from the list."));
853 		return;
854 	}
855 
856 	BPartition* partition = disk->FindDescendant(selectedPartition);
857 	if (!partition) {
858 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
859 			"partition by ID."));
860 		return;
861 	}
862 
863 	if (!partition->IsMounted()) {
864 		status_t status = partition->Mount();
865 		if (status != B_OK) {
866 			_DisplayPartitionError(B_TRANSLATE("Could not mount partition %s."),
867 				partition, status);
868 		} else {
869 			// successful mount, adapt to the changes
870 			_ScanDrives();
871 		}
872 	} else {
873 		_DisplayPartitionError(
874 			B_TRANSLATE("The partition %s is already mounted."), partition);
875 	}
876 }
877 
878 
879 void
880 MainWindow::_Unmount(BDiskDevice* disk, partition_id selectedPartition)
881 {
882 	if (!disk || selectedPartition < 0) {
883 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
884 			"entry from the list."));
885 		return;
886 	}
887 
888 	BPartition* partition = disk->FindDescendant(selectedPartition);
889 	if (!partition) {
890 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
891 			"partition by ID."));
892 		return;
893 	}
894 
895 	if (partition->IsMounted()) {
896 		BPath path;
897 		partition->GetMountPoint(&path);
898 		status_t status = partition->Unmount();
899 		if (status != B_OK) {
900 			BString message = B_TRANSLATE("Could not unmount partition");
901 			message << " \"" << partition->ContentName() << "\":\n\t"
902 				<< strerror(status) << "\n\n"
903 				<< B_TRANSLATE("Should unmounting be forced?\n\n"
904 				"Note: If an application is currently writing to the volume, "
905 				"unmounting it now might result in loss of data.\n");
906 
907 			BAlert* alert = new BAlert(B_TRANSLATE("Force unmount"), message,
908 				B_TRANSLATE("Cancel"), B_TRANSLATE("Force unmount"), NULL,
909 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
910 			alert->SetShortcut(0, B_ESCAPE);
911 
912 			if (alert->Go() == 1)
913 				status = partition->Unmount(B_FORCE_UNMOUNT);
914 			else
915 				return;
916 		}
917 
918 		if (status != B_OK) {
919 			_DisplayPartitionError(
920 				B_TRANSLATE("Could not unmount partition %s."),
921 				partition, status);
922 		} else {
923 			if (dev_for_path(path.Path()) == dev_for_path("/"))
924 				rmdir(path.Path());
925 			// successful unmount, adapt to the changes
926 			_ScanDrives();
927 		}
928 	} else {
929 		_DisplayPartitionError(
930 			B_TRANSLATE("The partition %s is already unmounted."),
931 			partition);
932 	}
933 }
934 
935 
936 void
937 MainWindow::_MountAll()
938 {
939 	MountAllVisitor visitor;
940 	fDiskDeviceRoster.VisitEachPartition(&visitor);
941 }
942 
943 
944 // #pragma mark -
945 
946 
947 void
948 MainWindow::_Initialize(BDiskDevice* disk, partition_id selectedPartition,
949 	const BString& diskSystemName)
950 {
951 	if (!disk || selectedPartition < 0) {
952 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
953 			"entry from the list."));
954 		return;
955 	}
956 
957 	if (disk->IsReadOnly()) {
958 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
959 		return;
960 	}
961 
962 	BPartition* partition = disk->FindDescendant(selectedPartition);
963 	if (!partition) {
964 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
965 			"partition by ID."));
966 		return;
967 	}
968 
969 	if (partition->IsMounted()) {
970 		_DisplayPartitionError(
971 			B_TRANSLATE("The partition %s is currently mounted."));
972 		// TODO: option to unmount and continue on success to unmount
973 		return;
974 	}
975 
976 	BDiskSystem diskSystem;
977 	fDiskDeviceRoster.RewindDiskSystems();
978 	bool found = false;
979 	while (fDiskDeviceRoster.GetNextDiskSystem(&diskSystem) == B_OK) {
980 		if (diskSystem.SupportsInitializing()) {
981 			if (diskSystemName == diskSystem.PrettyName()) {
982 				found = true;
983 				break;
984 			}
985 		}
986 	}
987 
988 	if (!found) {
989 		_DisplayPartitionError(B_TRANSLATE("Disk system \"%s\" not found!"));
990 		return;
991 	}
992 
993 	BString message;
994 
995 	if (diskSystem.IsFileSystem()) {
996 		BString intelExtendedPartition = "Intel Extended Partition";
997 		if (disk->ID() == selectedPartition) {
998 			message = B_TRANSLATE("Are you sure you "
999 				"want to format a raw disk? (Most people initialize the disk "
1000 				"with a partitioning system first) You will be asked "
1001 				"again before changes are written to the disk.");
1002 		} else if (partition->ContentName()
1003 			&& strlen(partition->ContentName()) > 0) {
1004 			message = B_TRANSLATE("Are you sure you "
1005 				"want to format the partition \"%s\"? You will be asked "
1006 				"again before changes are written to the disk.");
1007 			message.ReplaceFirst("%s", partition->ContentName());
1008 		} else if (partition->Type() == intelExtendedPartition) {
1009 			message = B_TRANSLATE("Are you sure you "
1010 				"want to format the Intel Extended Partition? Any "
1011 				"subpartitions it contains will be overwritten if you "
1012 				"continue. You will be asked again before changes are "
1013 				"written to the disk.");
1014 		} else {
1015 			message = B_TRANSLATE("Are you sure you "
1016 				"want to format the partition? You will be asked again "
1017 				"before changes are written to the disk.");
1018 		}
1019 	} else {
1020 		message = B_TRANSLATE("Are you sure you "
1021 			"want to initialize the selected disk? All data will be lost. "
1022 			"You will be asked again before changes are written to the "
1023 			"disk.\n");
1024 	}
1025 	BAlert* alert = new BAlert("first notice", message.String(),
1026 		B_TRANSLATE("Continue"), B_TRANSLATE("Cancel"), NULL,
1027 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1028 	alert->SetShortcut(1, B_ESCAPE);
1029 	int32 choice = alert->Go();
1030 
1031 	if (choice == 1)
1032 		return;
1033 
1034 	ModificationPreparer modificationPreparer(disk);
1035 	status_t status = modificationPreparer.ModificationStatus();
1036 	if (status != B_OK) {
1037 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1038 			"disk for modifications."), NULL, status);
1039 		return;
1040 	}
1041 
1042 	BString name;
1043 	BString parameters;
1044 	InitParametersPanel* panel = new InitParametersPanel(this, diskSystemName,
1045 		partition);
1046 	if (panel->Go(name, parameters) != B_OK)
1047 		return;
1048 
1049 	bool supportsName = diskSystem.SupportsContentName();
1050 	BString validatedName(name);
1051 	status = partition->ValidateInitialize(diskSystem.PrettyName(),
1052 		supportsName ? &validatedName : NULL, parameters.String());
1053 	if (status != B_OK) {
1054 		_DisplayPartitionError(B_TRANSLATE("Validation of the given "
1055 			"initialization parameters failed."), partition, status);
1056 		return;
1057 	}
1058 
1059 	BString previousName = partition->ContentName();
1060 
1061 	status = partition->Initialize(diskSystem.PrettyName(),
1062 		supportsName ? validatedName.String() : NULL, parameters.String());
1063 	if (status != B_OK) {
1064 		_DisplayPartitionError(B_TRANSLATE("Initialization of the partition "
1065 			"%s failed. (Nothing has been written to disk.)"), partition,
1066 			status);
1067 		return;
1068 	}
1069 
1070 	// Also set the partition name in the partition table if supported
1071 	if (partition->CanSetName()
1072 		&& partition->ValidateSetName(&validatedName) == B_OK) {
1073 		partition->SetName(validatedName.String());
1074 	}
1075 
1076 	// everything looks fine, we are ready to actually write the changes
1077 	// to disk
1078 
1079 	// Warn the user one more time...
1080 	if (previousName.Length() > 0) {
1081 		if (partition->IsDevice()) {
1082 			message = B_TRANSLATE("Are you sure you "
1083 				"want to write the changes back to disk now?\n\n"
1084 				"All data on the disk %s will be irretrievably lost if you "
1085 				"do so!");
1086 			message.ReplaceFirst("%s", previousName.String());
1087 		} else {
1088 			message = B_TRANSLATE("Are you sure you "
1089 				"want to write the changes back to disk now?\n\n"
1090 				"All data on the partition %s will be irretrievably lost if you "
1091 				"do so!");
1092 			message.ReplaceFirst("%s", previousName.String());
1093 		}
1094 	} else {
1095 		if (partition->IsDevice()) {
1096 			message = B_TRANSLATE("Are you sure you "
1097 				"want to write the changes back to disk now?\n\n"
1098 				"All data on the selected disk will be irretrievably lost if "
1099 				"you do so!");
1100 		} else {
1101 			message = B_TRANSLATE("Are you sure you "
1102 				"want to write the changes back to disk now?\n\n"
1103 				"All data on the selected partition will be irretrievably lost "
1104 				"if you do so!");
1105 		}
1106 	}
1107 	alert = new BAlert("final notice", message.String(),
1108 		B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1109 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1110 	alert->SetShortcut(1, B_ESCAPE);
1111 	choice = alert->Go();
1112 
1113 	if (choice == 1)
1114 		return;
1115 
1116 	// commit
1117 	status = modificationPreparer.CommitModifications();
1118 
1119 	// The partition pointer is toast now! Use the partition ID to
1120 	// retrieve it again.
1121 	partition = disk->FindDescendant(selectedPartition);
1122 
1123 	if (status == B_OK) {
1124 		if (diskSystem.IsFileSystem()) {
1125 			_DisplayPartitionError(B_TRANSLATE("The partition %s has been "
1126 				"successfully formatted.\n"), partition);
1127 		} else {
1128 			_DisplayPartitionError(B_TRANSLATE("The disk has been "
1129 				"successfully initialized.\n"), partition);
1130 		}
1131 	} else {
1132 		if (diskSystem.IsFileSystem()) {
1133 			_DisplayPartitionError(B_TRANSLATE("Failed to format the "
1134 				"partition %s!\n"), partition, status);
1135 		} else {
1136 			_DisplayPartitionError(B_TRANSLATE("Failed to initialize the "
1137 				"disk %s!\n"), partition, status);
1138 		}
1139 	}
1140 
1141 	_ScanDrives();
1142 }
1143 
1144 
1145 void
1146 MainWindow::_Create(BDiskDevice* disk, partition_id selectedPartition)
1147 {
1148 	if (!disk || selectedPartition > -2) {
1149 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1150 			"is not empty."));
1151 		return;
1152 	}
1153 
1154 	if (disk->IsReadOnly()) {
1155 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1156 		return;
1157 	}
1158 
1159 	PartitionListRow* currentSelection = dynamic_cast<PartitionListRow*>(
1160 		fListView->CurrentSelection());
1161 	if (!currentSelection) {
1162 		_DisplayPartitionError(B_TRANSLATE("There was an error acquiring the "
1163 			"partition row."));
1164 		return;
1165 	}
1166 
1167 	BPartition* parent = disk->FindDescendant(currentSelection->ParentID());
1168 	if (!parent) {
1169 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1170 			"does not have a parent partition."));
1171 		return;
1172 	}
1173 
1174 	if (!parent->ContainsPartitioningSystem()) {
1175 		_DisplayPartitionError(B_TRANSLATE("The selected partition does not "
1176 			"contain a partitioning system."));
1177 		return;
1178 	}
1179 
1180 	ModificationPreparer modificationPreparer(disk);
1181 	status_t status = modificationPreparer.ModificationStatus();
1182 	if (status != B_OK) {
1183 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1184 			"disk for modifications."), NULL, status);
1185 		return;
1186 	}
1187 
1188 	// get partitioning info
1189 	BPartitioningInfo partitioningInfo;
1190 	status_t error = parent->GetPartitioningInfo(&partitioningInfo);
1191 	if (error != B_OK) {
1192 		_DisplayPartitionError(B_TRANSLATE("Could not acquire partitioning "
1193 			"information."));
1194 		return;
1195 	}
1196 
1197 	int32 spacesCount = partitioningInfo.CountPartitionableSpaces();
1198 	if (spacesCount == 0) {
1199 		_DisplayPartitionError(B_TRANSLATE("There's no space on the partition "
1200 			"where a child partition could be created."));
1201 		return;
1202 	}
1203 
1204 	BString name, type, parameters;
1205 	off_t offset = currentSelection->Offset();
1206 	off_t size = currentSelection->Size();
1207 
1208 	CreateParametersPanel* panel = new CreateParametersPanel(this, parent,
1209 		offset, size);
1210 	status = panel->Go(offset, size, name, type, parameters);
1211 	if (status != B_OK) {
1212 		if (status != B_CANCELED) {
1213 			_DisplayPartitionError(B_TRANSLATE("The panel could not return "
1214 				"successfully."), NULL, status);
1215 		}
1216 		return;
1217 	}
1218 
1219 	status = parent->ValidateCreateChild(&offset, &size, type.String(),
1220 		&name, parameters.String());
1221 
1222 	if (status != B_OK) {
1223 		_DisplayPartitionError(B_TRANSLATE("Validation of the given creation "
1224 			"parameters failed."), NULL, status);
1225 		return;
1226 	}
1227 
1228 	// Warn the user one more time...
1229 	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1230 		"want to write the changes back to disk now?\n\n"
1231 		"All data on the partition will be irretrievably lost if you do "
1232 		"so!"), B_TRANSLATE("Write changes"), B_TRANSLATE("Cancel"), NULL,
1233 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1234 	alert->SetShortcut(1, B_ESCAPE);
1235 	int32 choice = alert->Go();
1236 
1237 	if (choice == 1)
1238 		return;
1239 
1240 	status = parent->CreateChild(offset, size, type.String(), name.String(),
1241 		parameters.String());
1242 
1243 	if (status != B_OK) {
1244 		_DisplayPartitionError(B_TRANSLATE("Creation of the partition has "
1245 			"failed."), NULL, status);
1246 		return;
1247 	}
1248 
1249 	// commit
1250 	status = modificationPreparer.CommitModifications();
1251 
1252 	if (status != B_OK) {
1253 		_DisplayPartitionError(B_TRANSLATE("Failed to format the "
1254 			"partition. No changes have been written to disk."), NULL, status);
1255 		return;
1256 	}
1257 
1258 	// The disk layout has changed, update disk information
1259 	bool updated;
1260 	status = disk->Update(&updated);
1261 
1262 	_ScanDrives();
1263 	fDiskView->ForceUpdate();
1264 }
1265 
1266 
1267 void
1268 MainWindow::_Delete(BDiskDevice* disk, partition_id selectedPartition)
1269 {
1270 	if (!disk || selectedPartition < 0) {
1271 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1272 			"entry from the list."));
1273 		return;
1274 	}
1275 
1276 	if (disk->IsReadOnly()) {
1277 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1278 		return;
1279 	}
1280 
1281 	BPartition* partition = disk->FindDescendant(selectedPartition);
1282 	if (!partition) {
1283 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1284 			"partition by ID."));
1285 		return;
1286 	}
1287 
1288 	BPartition* parent = partition->Parent();
1289 	if (!parent) {
1290 		_DisplayPartitionError(B_TRANSLATE("The currently selected partition "
1291 			"does not have a parent partition."));
1292 		return;
1293 	}
1294 
1295 	ModificationPreparer modificationPreparer(disk);
1296 	status_t status = modificationPreparer.ModificationStatus();
1297 	if (status != B_OK) {
1298 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1299 			"disk for modifications."), NULL, status);
1300 		return;
1301 	}
1302 
1303 	if (!parent->CanDeleteChild(partition->Index())) {
1304 		_DisplayPartitionError(
1305 			B_TRANSLATE("Cannot delete the selected partition."));
1306 		return;
1307 	}
1308 
1309 	// Warn the user one more time...
1310 	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1311 		"want to delete the selected partition?\n\n"
1312 		"All data on the partition will be irretrievably lost if you "
1313 		"do so!"), B_TRANSLATE("Delete partition"), B_TRANSLATE("Cancel"), NULL,
1314 		B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1315 	alert->SetShortcut(1, B_ESCAPE);
1316 	int32 choice = alert->Go();
1317 
1318 	if (choice == 1)
1319 		return;
1320 
1321 	status = parent->DeleteChild(partition->Index());
1322 	if (status != B_OK) {
1323 		_DisplayPartitionError(B_TRANSLATE("Could not delete the selected "
1324 			"partition."), NULL, status);
1325 		return;
1326 	}
1327 
1328 	status = modificationPreparer.CommitModifications();
1329 
1330 	if (status != B_OK) {
1331 		_DisplayPartitionError(B_TRANSLATE("Failed to delete the partition. "
1332 			"No changes have been written to disk."), NULL, status);
1333 		return;
1334 	}
1335 
1336 	_ScanDrives();
1337 	fDiskView->ForceUpdate();
1338 }
1339 
1340 
1341 void
1342 MainWindow::_ChangeParameters(BDiskDevice* disk, partition_id selectedPartition)
1343 {
1344 	if (disk == NULL || selectedPartition < 0) {
1345 		_DisplayPartitionError(B_TRANSLATE("You need to select a partition "
1346 			"entry from the list."));
1347 		return;
1348 	}
1349 
1350 	if (disk->IsReadOnly()) {
1351 		_DisplayPartitionError(B_TRANSLATE("The selected disk is read-only."));
1352 		return;
1353 	}
1354 
1355 	BPartition* partition = disk->FindDescendant(selectedPartition);
1356 	if (partition == NULL) {
1357 		_DisplayPartitionError(B_TRANSLATE("Unable to find the selected "
1358 			"partition by ID."));
1359 		return;
1360 	}
1361 
1362 	ModificationPreparer modificationPreparer(disk);
1363 	status_t status = modificationPreparer.ModificationStatus();
1364 	if (status != B_OK) {
1365 		_DisplayPartitionError(B_TRANSLATE("There was an error preparing the "
1366 			"disk for modifications."), NULL, status);
1367 		return;
1368 	}
1369 
1370 	ChangeParametersPanel* panel = new ChangeParametersPanel(this, partition);
1371 
1372 	BString name, type, parameters;
1373 	status = panel->Go(name, type, parameters);
1374 	if (status != B_OK) {
1375 		if (status != B_CANCELED) {
1376 			_DisplayPartitionError(B_TRANSLATE("The panel experienced a "
1377 				"problem!"), NULL, status);
1378 		}
1379 		// TODO: disk systems without an editor and support for name/type
1380 		// changing will return B_CANCELED here -- we need to check this
1381 		// before, and disable the menu entry instead
1382 		return;
1383 	}
1384 
1385 	if (partition->CanSetType())
1386 		status = partition->ValidateSetType(type.String());
1387 	if (status == B_OK && partition->CanSetName())
1388 		status = partition->ValidateSetName(&name);
1389 	if (status != B_OK) {
1390 		_DisplayPartitionError(B_TRANSLATE("Validation of the given parameters "
1391 			"failed."));
1392 		return;
1393 	}
1394 
1395 	// Warn the user one more time...
1396 	BAlert* alert = new BAlert("final notice", B_TRANSLATE("Are you sure you "
1397 		"want to change parameters of the selected partition?\n\n"
1398 		"The partition may no longer be recognized by other operating systems "
1399 		"anymore!"), B_TRANSLATE("Change parameters"), B_TRANSLATE("Cancel"),
1400 		NULL, B_WIDTH_FROM_WIDEST, B_WARNING_ALERT);
1401 	alert->SetShortcut(1, B_ESCAPE);
1402 	int32 choice = alert->Go();
1403 
1404 	if (choice == 1)
1405 		return;
1406 
1407 	if (partition->CanSetType())
1408 		status = partition->SetType(type.String());
1409 	if (status == B_OK && partition->CanSetName())
1410 		status = partition->SetName(name.String());
1411 	if (status == B_OK && partition->CanEditParameters())
1412 		status = partition->SetParameters(parameters.String());
1413 
1414 	if (status != B_OK) {
1415 		_DisplayPartitionError(
1416 			B_TRANSLATE("Could not change the parameters of the selected "
1417 				"partition."), NULL, status);
1418 		return;
1419 	}
1420 
1421 	status = modificationPreparer.CommitModifications();
1422 
1423 	if (status != B_OK) {
1424 		_DisplayPartitionError(B_TRANSLATE("Failed to change the parameters "
1425 			"of the partition. No changes have been written to disk."), NULL,
1426 			status);
1427 		return;
1428 	}
1429 
1430 	_ScanDrives();
1431 	fDiskView->ForceUpdate();
1432 }
1433 
1434 
1435 float
1436 MainWindow::_ColumnListViewHeight(BColumnListView* list, BRow* currentRow)
1437 {
1438 	float height = 0;
1439 	int32 rows = list->CountRows(currentRow);
1440 	for (int32 i = 0; i < rows; i++) {
1441 		BRow* row = list->RowAt(i, currentRow);
1442 		height += row->Height() + 1;
1443 		if (row->IsExpanded() && list->CountRows(row) > 0)
1444 			height += _ColumnListViewHeight(list, row);
1445 	}
1446 	return height;
1447 }
1448 
1449 
1450 void
1451 MainWindow::_UpdateWindowZoomLimits()
1452 {
1453 	float maxHeight = 0;
1454 	int32 numColumns = fListView->CountColumns();
1455 	BRow* parentRow = fListView->RowAt(0, NULL);
1456 	BColumn* column = NULL;
1457 
1458 	maxHeight += _ColumnListViewHeight(fListView, NULL);
1459 
1460 	float maxWidth = fListView->LatchWidth();
1461 	for (int32 i = 0; i < numColumns; i++) {
1462 		column = fListView->ColumnAt(i);
1463 		maxWidth += column->Width();
1464 	}
1465 
1466 	maxHeight += B_H_SCROLL_BAR_HEIGHT;
1467 	maxHeight += 1.5 * parentRow->Height();	// the label row
1468 	maxHeight += fDiskView->Bounds().Height();
1469 	maxHeight += fMenuBar->Bounds().Height();
1470 	maxWidth += 1.5 * B_V_SCROLL_BAR_WIDTH;	// scroll bar & borders
1471 
1472 	SetZoomLimits(maxWidth, maxHeight);
1473 }
1474 
1475