Index: src/preferences/virtualmemory/Settings.cpp
===================================================================
--- src/preferences/virtualmemory/Settings.cpp	(revision 40096)
+++ src/preferences/virtualmemory/Settings.cpp	(working copy)
@@ -1,6 +1,7 @@
 /*
- * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
- * Distributed under the terms of the MIT License.
+ * Copyright 2005, Axel Dörfler, axeld@pinc-software.de
+ * Copyright 2010-2011, Hamish Morrison, hamish@lavabit.com
+ * All rights reserved. Distributed under the terms of the MIT License.
  */
 
 
@@ -21,12 +22,12 @@
 
 static const char* kWindowSettingsFile = "VM_data";
 static const char* kVirtualMemorySettings = "virtual_memory";
+static const int64 kMegaByte = 1048576;
 
 
 Settings::Settings()
 	:
-	fPositionUpdated(false),
-	fSwapUpdated(false)
+	fPositionUpdated(false)
 {
 	ReadWindowSettings();
 	ReadSwapSettings();
@@ -50,7 +51,6 @@
 		path.Append(kWindowSettingsFile);
 		BFile file;
 		if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK)
-			// Now read in the data
 			if (file.Read(&fWindowPosition, sizeof(BPoint)) == sizeof(BPoint))
 				success = true;
 	}
@@ -92,38 +92,41 @@
 void
 Settings::ReadSwapSettings()
 {
-	// read current swap settings from disk
-	void* settings = load_driver_settings("virtual_memory");
+	void* settings = load_driver_settings(kVirtualMemorySettings);
 	if (settings != NULL) {
-		fSwapEnabled = get_driver_boolean_parameter(settings, "vm", false, false);
-
-		const char* string = get_driver_parameter(settings, "swap_size", NULL, NULL);
-		fSwapSize = string ? atoll(string) : 0;
-
-		if (fSwapSize <= 0) {
-			fSwapEnabled = false;
-			fSwapSize = 0;
-		}
+		SetSwapEnabled(get_driver_boolean_parameter(settings, "vm", false, false));
+		const char* swapSize = get_driver_parameter(settings, "swap_size", NULL, NULL);
+		SetSwapSize(swapSize ? atoll(swapSize) : 0);
+		
+#ifdef SWAP_VOLUME_IMPLEMENTED
+		// we need to hang onto this one
+		fBadVolName = strdup(get_driver_parameter(settings, "swap_volume", NULL, NULL));
+		
+		BVolumeRoster volumeRoster;
+		BVolume temporaryVolume;
+		
+		if (fBadVolName != NULL) {
+			status_t result = volumeRoster.GetNextVolume(&temporaryVolume);
+			char volumeName[B_FILE_NAME_LENGTH];
+			while (result != B_BAD_VALUE) {
+				temporaryVolume.GetName(volumeName);
+				if (strcmp(volumeName, fBadVolName) == 0 
+					&& temporaryVolume.IsPersistent() && volumeName[0]) {
+					SetSwapVolume(temporaryVolume);
+					break;
+				}
+				result = volumeRoster.GetNextVolume(&temporaryVolume);
+			}
+		}	
+#endif
 		unload_driver_settings(settings);
-	} else {
-		// settings are not available, try to find out what the kernel is up to
-		// ToDo: introduce a kernel call for this!
-		fSwapSize = 0;
+	} else
+		SetSwapNull();
 
-		BPath path;
-		if (find_directory(B_COMMON_VAR_DIRECTORY, &path) == B_OK) {
-			path.Append("swap");
-			BEntry swap(path.Path());
-			if (swap.GetSize(&fSwapSize) != B_OK)
-				fSwapSize = 0;
-		}
-
-		fSwapEnabled = fSwapSize != 0;
-	}
-
-	// ToDo: read those as well
+#ifndef SWAP_VOLUME_IMPLEMENTED
 	BVolumeRoster volumeRoster;
 	volumeRoster.GetBootVolume(&fSwapVolume);
+#endif
 
 	fInitialSwapEnabled = fSwapEnabled;
 	fInitialSwapSize = fSwapSize;
@@ -133,8 +136,8 @@
 
 void
 Settings::WriteSwapSettings()
-{
-	if (!SwapChanged())
+{	
+	if (!IsRevertible())
 		return;
 
 	BPath path;
@@ -145,12 +148,24 @@
 	path.Append(kVirtualMemorySettings);
 
 	BFile file;
-	if (file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK)
+	if (file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)
+		!= B_OK)
 		return;
-
+	
 	char buffer[256];
+#ifdef SWAP_VOLUME_IMPLEMENTED
+	char volumeName[B_FILE_NAME_LENGTH] = {0};
+	if (SwapVolume().InitCheck() != B_NO_INIT)
+		SwapVolume().GetName(volumeName);
+	else if (fBadVolName)
+		strcpy(volumeName, fBadVolName);
+	snprintf(buffer, sizeof(buffer), "vm %s\nswap_size %Ld\nswap_volume %s\n",
+		SwapEnabled() ? "on" : "off", SwapSize(), 
+		volumeName[0] ? volumeName : NULL);
+#else
 	snprintf(buffer, sizeof(buffer), "vm %s\nswap_size %Ld\n",
 		fSwapEnabled ? "on" : "off", fSwapSize);
+#endif
 
 	file.Write(buffer, strlen(buffer));
 }
@@ -173,7 +188,7 @@
 void 
 Settings::SetSwapVolume(BVolume &volume)
 {
-	if (volume.Device() == fSwapVolume.Device()
+	if (volume.Device() == SwapVolume().Device()
 		|| volume.InitCheck() != B_OK)
 		return;
 
@@ -182,16 +197,14 @@
 
 
 void
-Settings::SetSwapDefaults()
+Settings::SetSwapNull()
 {
-	fSwapEnabled = true;
-
+	SetSwapEnabled(false);
 	BVolumeRoster volumeRoster;
-	volumeRoster.GetBootVolume(&fSwapVolume);
-
-	system_info info;
-	get_system_info(&info);
-	fSwapSize = (off_t)info.max_pages * B_PAGE_SIZE;
+	BVolume temporaryVolume;
+	volumeRoster.GetBootVolume(&temporaryVolume);
+	SetSwapVolume(temporaryVolume);
+	SetSwapSize(0);
 }
 
 
@@ -205,18 +218,9 @@
 
 
 bool
-Settings::IsDefaultable()
+Settings::IsRevertible()
 {
 	return fSwapEnabled != fInitialSwapEnabled
-		|| fSwapSize != fInitialSwapSize;
-}
-
-
-bool
-Settings::SwapChanged()
-{
-	return fSwapEnabled != fInitialSwapEnabled
 		|| fSwapSize != fInitialSwapSize
 		|| fSwapVolume.Device() != fInitialSwapVolume;
 }
-
Index: src/preferences/virtualmemory/Settings.h
===================================================================
--- src/preferences/virtualmemory/Settings.h	(revision 40096)
+++ src/preferences/virtualmemory/Settings.h	(working copy)
@@ -25,12 +25,11 @@
 		void SetSwapSize(off_t size);
 		void SetSwapVolume(BVolume& volume);
 
-		void SetSwapDefaults();
 		void RevertSwapChanges();
-		bool IsDefaultable();
-		bool SwapChanged();
+		bool IsRevertible();
 
 	private:
+		void SetSwapNull();
 		void ReadWindowSettings();
 		void WriteWindowSettings();
 
@@ -47,7 +46,8 @@
 		off_t		fInitialSwapSize;
 		dev_t		fInitialSwapVolume;
 
-		bool		fPositionUpdated, fSwapUpdated;
+		bool		fPositionUpdated;
+		const char*	fBadVolName;
 };
 
 #endif	/* SETTINGS_H */
Index: src/preferences/virtualmemory/SettingsWindow.cpp
===================================================================
--- src/preferences/virtualmemory/SettingsWindow.cpp	(revision 40096)
+++ src/preferences/virtualmemory/SettingsWindow.cpp	(working copy)
@@ -1,6 +1,7 @@
 /*
- * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
- * Distributed under the terms of the MIT License.
+ * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de
+ * Copyright 2010-2011, Hamish Morrison, hamish@lavabit.com 
+ * All rights reserved. Distributed under the terms of the MIT License.
  */
 
 
@@ -39,6 +40,8 @@
 static const uint32 kMsgRevert = 'rvrt';
 static const uint32 kMsgSliderUpdate = 'slup';
 static const uint32 kMsgSwapEnabledUpdate = 'swen';
+static const uint32 kMsgVolumeSelected = 'vlsl';
+static const int64 kMegaByte = 1048576;
 
 
 class SizeSlider : public BSlider {
@@ -54,10 +57,21 @@
 };
 
 
-static const int64 kMegaByte = 1048576;
+SizeSlider::SizeSlider(const char* name, const char* label,
+	BMessage* message, int32 min, int32 max, uint32 flags)
+	: BSlider(name, label, message, min, max, B_HORIZONTAL, B_BLOCK_THUMB, flags)
+{
+	rgb_color color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
+	UseFillColor(true, &color);
+}
 
 
-const char *
+SizeSlider::~SizeSlider()
+{
+}
+
+
+const char*
 byte_string(int64 size)
 {
 	double value = 1. * size;
@@ -80,7 +94,7 @@
 		} while (value >= 1024 && units[i + 1]);
 
 		off_t rounded = off_t(value * 100LL);
-		sprintf(string, "%g %s", rounded / 100.0,
+		snprintf(string, sizeof(string), "%g %s", rounded / 100.0,
 			B_TRANSLATE_NOCOLLECT(units[i]));
 	}
 
@@ -88,39 +102,42 @@
 }
 
 
-//	#pragma mark -
-
-
-SizeSlider::SizeSlider(const char* name, const char* label,
-	BMessage* message, int32 min, int32 max, uint32 flags)
-	: BSlider(name, label, message, min, max, B_HORIZONTAL, B_BLOCK_THUMB, flags)
+const char*
+SizeSlider::UpdateText() const
 {
-	rgb_color color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
-	UseFillColor(true, &color);
+	fText = byte_string(Value() * kMegaByte);
+	return fText.String();
 }
 
 
-SizeSlider::~SizeSlider()
+class VolumeMenuItem : public BMenuItem {
+	public:
+		VolumeMenuItem(const char* label, BMessage* message, BVolume* volume);
+		virtual ~VolumeMenuItem();
+		BVolume* fVolume;
+};
+
+
+VolumeMenuItem::VolumeMenuItem(const char* label, BMessage* message,
+	BVolume* volume)
+	: BMenuItem(label, message)
 {
+	fVolume = volume;
 }
 
 
-const char *
-SizeSlider::UpdateText() const
+VolumeMenuItem::~VolumeMenuItem()
 {
-	fText = byte_string(Value() * kMegaByte);
-
-	return fText.String();
 }
 
 
-//	#pragma mark -
-
-
 SettingsWindow::SettingsWindow()
-	: BWindow(BRect(0, 0, 269, 172), B_TRANSLATE("VirtualMemory"),
+	:
+	BWindow(BRect(0, 0, 269, 172), B_TRANSLATE("VirtualMemory"),
 		B_TITLED_WINDOW, B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS
-			| B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS)
+		| B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
+	fLocked(false)
+	
 {
 	BView* view = new BGroupView();
 
@@ -142,34 +159,33 @@
 	string << byte_string(fSettings.SwapSize());
 	BStringView* swapfileView = new BStringView("current swap size", string.String());
 
-	BPopUpMenu* menu = new BPopUpMenu("volumes");
+	BPopUpMenu* menu = new BPopUpMenu("invalid");
 
 	// collect volumes
 	// TODO: listen to volume changes!
 	// TODO: accept dropped volumes
 
 	BVolumeRoster volumeRoster;
-	BVolume volume;
-	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
-		char name[B_FILE_NAME_LENGTH];
-		if (!volume.IsPersistent() || volume.GetName(name) != B_OK || !name[0])
+	BVolume* volume = new BVolume();
+	char name[B_FILE_NAME_LENGTH];
+	while (volumeRoster.GetNextVolume(volume) == B_OK) {	
+		if (!volume->IsPersistent() || volume->GetName(name) != B_OK || !name[0])
 			continue;
-
-		BMenuItem* item = new BMenuItem(name, NULL);
+		VolumeMenuItem* item = new VolumeMenuItem(name, 
+			new BMessage(kMsgVolumeSelected), volume);
 		menu->AddItem(item);
-
-		if (volume.Device() == fSettings.SwapVolume().Device())
-			item->SetMarked(true);
+		volume = new BVolume();
 	}
 
-	fVolumeMenuField = new BMenuField("devices", B_TRANSLATE("Use volume:"), menu);
+	fVolumeMenuField = new BMenuField("volumes", B_TRANSLATE("Use volume:"), menu);
 	
-	// When swap volume changing support is implemeneted, remove me:
+#ifndef SWAP_VOLUME_IMPLEMENTED
 	fVolumeMenuField->SetEnabled(false);
+#endif
 	
 	fSizeSlider = new SizeSlider("size slider", 
 		B_TRANSLATE("Requested swap file size:"), new BMessage(kMsgSliderUpdate),
-		0, 0, B_WILL_DRAW | B_FRAME_EVENTS);
+		1, 1, B_WILL_DRAW | B_FRAME_EVENTS);
 	fSizeSlider->SetViewColor(255, 0, 255);
 
 	fWarningStringView = new BStringView("", "");
@@ -195,11 +211,8 @@
 	);
 	box->AddChild(view);
 
-	// Add "Defaults" and "Revert" buttons
-
 	fDefaultsButton = new BButton("defaults", B_TRANSLATE("Defaults"),
 		new BMessage(kMsgDefaults));
-	fDefaultsButton->SetEnabled(fSettings.IsDefaultable());
 
 	fRevertButton = new BButton("revert", B_TRANSLATE("Revert"),
 		new BMessage(kMsgRevert));
@@ -216,15 +229,34 @@
 		.SetInsets(10, 10, 10, 10)
 	);
 
-	_Update();
-
 	BScreen screen;
 	BRect screenFrame = screen.Frame();
-
 	if (!screenFrame.Contains(fSettings.WindowPosition()))
 		CenterOnScreen();
 	else
 		MoveTo(fSettings.WindowPosition());
+
+#ifdef SWAP_VOLUME_IMPLEMENTED
+	// Validate the volume specified in settings file
+	status_t result = fSettings.SwapVolume().InitCheck();
+
+	if (result == B_NO_INIT) {
+		int32 choice = (new BAlert("VirtualMemory",	B_TRANSLATE(
+			"The device specified in the settings file is invalid.\n"
+			"You can keep the current setting or switch to the "
+			"default swap device."),
+			B_TRANSLATE("Keep"), B_TRANSLATE("Switch"), NULL,
+			B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
+		if (choice == 1) {
+			BVolumeRoster volumeRoster;
+			BVolume bootVolume;
+			volumeRoster.GetBootVolume(&bootVolume);
+			fSettings.SetSwapVolume(bootVolume);
+		}
+	}
+#endif
+
+	_Update();
 }
 
 
@@ -238,56 +270,68 @@
 {
 	if ((fSwapEnabledCheckBox->Value() != 0) != fSettings.SwapEnabled())
 		fSwapEnabledCheckBox->SetValue(fSettings.SwapEnabled());
-		
-	if (fSizeSlider->IsEnabled() != fSettings.SwapEnabled())
-		fSizeSlider->SetEnabled(fSettings.SwapEnabled());
-	
+
 #ifdef SWAP_VOLUME_IMPLEMENTED	
 	if (fVolumeMenuField->IsEnabled() != fSettings.SwapEnabled())
 		fVolumeMenuField->SetEnabled(fSettings.SwapEnabled());
+	VolumeMenuItem* selectedVolumeItem =
+		(VolumeMenuItem*)fVolumeMenuField->Menu()->FindMarked();
+	if (selectedVolumeItem == NULL) {
+		VolumeMenuItem* currentVolumeItem;
+		int32 items = fVolumeMenuField->Menu()->CountItems();
+		for (int32 index = 0; index < items; ++index) {
+			currentVolumeItem = ((VolumeMenuItem*)fVolumeMenuField->Menu()->ItemAt(index));
+			if (*(currentVolumeItem->fVolume) == fSettings.SwapVolume()) {
+				currentVolumeItem->SetMarked(true);
+				break;
+			}
+		}
+	} else if (*selectedVolumeItem->fVolume != fSettings.SwapVolume()) {
+		VolumeMenuItem* currentVolumeItem;
+		int32 items = fVolumeMenuField->Menu()->CountItems();
+		for (int32 index = 0; index < items; ++index) {
+			currentVolumeItem = ((VolumeMenuItem*)fVolumeMenuField->Menu()->ItemAt(index));
+			if (*(currentVolumeItem->fVolume) == fSettings.SwapVolume()) {
+				currentVolumeItem->SetMarked(true);
+				break;
+			}
+		}
+	}
 #endif
 
+	fWarningStringView->SetText("");
+	fLocked = false;
+	
+	if (fSettings.IsRevertible())
+			fWarningStringView->SetText(
+				B_TRANSLATE("Changes will take effect on restart!"));
+	if (fRevertButton->IsEnabled() != fSettings.IsRevertible())
+		fRevertButton->SetEnabled(fSettings.IsRevertible());
+	
 	off_t minSize, maxSize;
 	if (_GetSwapFileLimits(minSize, maxSize) == B_OK) {
+		// round to nearest MB -- slider steps in whole MBs
+		(minSize >>= 20) <<= 20;
+		(maxSize >>= 20) <<= 20;
 		BString minLabel, maxLabel;
 		minLabel << byte_string(minSize);
 		maxLabel << byte_string(maxSize);
 		if (minLabel != fSizeSlider->MinLimitLabel()
 			|| maxLabel != fSizeSlider->MaxLimitLabel()) {
 			fSizeSlider->SetLimitLabels(minLabel.String(), maxLabel.String());
-#ifdef __HAIKU__
 			fSizeSlider->SetLimits(minSize / kMegaByte, maxSize / kMegaByte);
-#endif
 		}
-
-		if (fSizeSlider->Value() != fSettings.SwapSize() / kMegaByte)
-			fSizeSlider->SetValue(fSettings.SwapSize() / kMegaByte);
-	} else {
-		// Not enough space on volume for a swap file. Make UI inoperable.
+	} else if (fSettings.SwapEnabled()) {
 		fWarningStringView->SetText(
 			B_TRANSLATE("Insufficient space for a swap file."));
-		fSwapEnabledCheckBox->SetEnabled(false);
-		fSizeSlider->SetEnabled(false);
-		// When swap volume is implemented, we'll want to keep the volume
-		// menu field enabled so the user can get around the space issue
-		// by selecting a different volume.
-#ifdef SWAP_VOLUME_IMPLEMENTED
-		fVolumeMenuField->SetEnabled(true);
-#endif
+		fLocked = true;
 	}
-
-	// ToDo: set volume
-
-	fDefaultsButton->SetEnabled(fSettings.IsDefaultable());
-
-	bool changed = fSettings.SwapChanged();
-	if (fRevertButton->IsEnabled() != changed) {
-		fRevertButton->SetEnabled(changed);
-		if (changed)
-			fWarningStringView->SetText(
-				B_TRANSLATE("Changes will take effect on restart!"));
-		else
-			fWarningStringView->SetText("");
+	if (fSizeSlider->Value() != fSettings.SwapSize() / kMegaByte)
+		fSizeSlider->SetValue(fSettings.SwapSize() / kMegaByte);
+	if (fSizeSlider->IsEnabled() != fSettings.SwapEnabled() || fLocked)
+	{
+		fSizeSlider->SetEnabled(fSettings.SwapEnabled() && !fLocked);
+		fSettings.SetSwapSize((off_t)fSizeSlider->Value() * kMegaByte);
 	}
 }
 
@@ -308,7 +352,7 @@
 	// adjust the free space accordingly
 	BPath path;
 	if (find_directory(B_COMMON_VAR_DIRECTORY, &path, false,
-			&fSettings.SwapVolume()) == B_OK) {
+		&fSettings.SwapVolume()) == B_OK) {
 		path.Append("swap");
 		BEntry swap(path.Path());
 
@@ -333,6 +377,30 @@
 
 
 void
+SettingsWindow::SetSwapDefaults()
+{
+	fSettings.SetSwapEnabled(true);
+
+	BVolumeRoster volumeRoster;
+	BVolume temporaryVolume;
+	volumeRoster.GetBootVolume(&temporaryVolume);
+	fSettings.SetSwapVolume(temporaryVolume);
+
+	system_info info;
+	get_system_info(&info);
+	
+	off_t defaultSize = (off_t)info.max_pages * B_PAGE_SIZE;
+	off_t minSize, maxSize;
+	_GetSwapFileLimits(minSize, maxSize);
+	
+	if (defaultSize > maxSize / 2)
+		defaultSize = maxSize / 2;
+		
+	fSettings.SetSwapSize(defaultSize);
+}
+
+
+void
 SettingsWindow::MessageReceived(BMessage* message)
 {
 	switch (message->what) {
@@ -341,13 +409,18 @@
 			_Update();
 			break;
 		case kMsgDefaults:
-			fSettings.SetSwapDefaults();
+			SetSwapDefaults();
 			_Update();
 			break;
 		case kMsgSliderUpdate:
 			fSettings.SetSwapSize((off_t)fSizeSlider->Value() * kMegaByte);
 			_Update();
 			break;
+		case kMsgVolumeSelected:
+			fSettings.SetSwapVolume(*((VolumeMenuItem*)fVolumeMenuField->Menu()
+				->FindMarked())->fVolume);
+			_Update();
+			break;
 		case kMsgSwapEnabledUpdate:
 		{
 			int32 value;
@@ -374,14 +447,7 @@
 				}
 			}
 
-			bool enabled = value != 0;
-			fSettings.SetSwapEnabled(enabled);
-			if (enabled && fSettings.SwapSize() == 0) {
-				off_t min;
-				off_t max;
-				_GetSwapFileLimits(min, max);
-				fSettings.SetSwapSize(min);
-			}
+			fSettings.SetSwapEnabled(value != 0);
 			_Update();
 			break;
 		}
Index: src/preferences/virtualmemory/SettingsWindow.h
===================================================================
--- src/preferences/virtualmemory/SettingsWindow.h	(revision 40096)
+++ src/preferences/virtualmemory/SettingsWindow.h	(working copy)
@@ -27,6 +27,7 @@
 	private:
 		void _Update();
 		status_t _GetSwapFileLimits(off_t& minSize, off_t& maxSize);
+		void SetSwapDefaults();
 
 		BCheckBox*		fSwapEnabledCheckBox;
 		BSlider*		fSizeSlider;
@@ -35,6 +36,8 @@
 		BStringView*	fWarningStringView;
 		BMenuField*		fVolumeMenuField;
 		Settings		fSettings;
+		
+		bool fLocked;
 };
 
 #endif	/* SETTINGS_WINDOW_H */
