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