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