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