/*
* Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2010-2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Hamish Morrison, hamish@lavabit.com
* Alexander von Gluck, kallisti5@unixzen.com
*/
#include "SettingsWindow.h"
#include <Application.h>
#include <Alert.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <MenuItem.h>
#include <MenuField.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Screen.h>
#include <StringForSize.h>
#include <StringView.h>
#include <String.h>
#include <Slider.h>
#include <system_info.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include "Settings.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "SettingsWindow"
static const uint32 kMsgDefaults = 'dflt';
static const uint32 kMsgRevert = 'rvrt';
static const uint32 kMsgSliderUpdate = 'slup';
static const uint32 kMsgSwapEnabledUpdate = 'swen';
static const uint32 kMsgSwapAutomaticUpdate = 'swat';
static const uint32 kMsgVolumeSelected = 'vlsl';
static const off_t kMegaByte = 1024 * 1024;
static dev_t gBootDev = -1;
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::UpdateText() const
{
return string_for_size(Value() * kMegaByte, fText, sizeof(fText));
}
VolumeMenuItem::VolumeMenuItem(BVolume volume, BMessage* message)
:
BMenuItem("", message),
fVolume(volume)
{
GenerateLabel();
}
void
VolumeMenuItem::MessageReceived(BMessage* message)
{
if (message->what == B_NODE_MONITOR) {
int32 code;
if (message->FindInt32("opcode", &code) == B_OK)
if (code == B_ENTRY_MOVED)
GenerateLabel();
}
}
void
VolumeMenuItem::GenerateLabel()
{
char name[B_FILE_NAME_LENGTH + 1];
fVolume.GetName(name);
BDirectory dir;
if (fVolume.GetRootDirectory(&dir) == B_OK) {
BEntry entry;
if (dir.GetEntry(&entry) == B_OK) {
BPath path;
if (entry.GetPath(&path) == B_OK) {
BString label;
label << name << " (" << path.Path() << ")";
SetLabel(label);
return;
}
}
}
SetLabel(name);
}
SettingsWindow::SettingsWindow()
:
BWindow(BRect(0, 0, 269, 172), B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
B_TITLED_WINDOW, B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS
| B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
fSwapEnabledCheckBox(NULL),
fSwapAutomaticCheckBox(NULL),
fSizeSlider(NULL),
fDefaultsButton(NULL),
fRevertButton(NULL),
fWarningStringView(NULL),
fVolumeMenuField(NULL),
fSwapUsageBar(NULL),
fSetupComplete(false)
{
gBootDev = dev_for_path("/boot");
BAlignment align(B_ALIGN_LEFT, B_ALIGN_MIDDLE);
if (fSettings.ReadWindowSettings() != B_OK)
CenterOnScreen();
else
MoveTo(fSettings.WindowPosition());
status_t result = fSettings.ReadSwapSettings();
if (result == kErrorSettingsNotFound)
fSettings.DefaultSwapSettings(false);
else if (result == kErrorSettingsInvalid) {
int32 choice = (new BAlert(B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
B_TRANSLATE("The settings specified in the settings file "
"are invalid. You can load the defaults or quit."),
B_TRANSLATE("Load defaults"), B_TRANSLATE("Quit")))->Go();
if (choice == 1) {
be_app->PostMessage(B_QUIT_REQUESTED);
return;
}
fSettings.DefaultSwapSettings(false);
} else if (result == kErrorVolumeNotFound) {
int32 choice = (new BAlert(B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
B_TRANSLATE("The volume specified in the settings file "
"could not be found. You can use the boot volume or quit."),
B_TRANSLATE("Use boot volume"), B_TRANSLATE("Quit")))->Go();
if (choice == 1) {
be_app->PostMessage(B_QUIT_REQUESTED);
return;
}
fSettings.SetSwapVolume(gBootDev, false);
}
fSwapEnabledCheckBox = new BCheckBox("enable swap",
B_TRANSLATE("Enable virtual memory"),
new BMessage(kMsgSwapEnabledUpdate));
fSwapEnabledCheckBox->SetExplicitAlignment(align);
fSwapAutomaticCheckBox = new BCheckBox("auto swap",
B_TRANSLATE("Automatic swap management"),
new BMessage(kMsgSwapAutomaticUpdate));
fSwapEnabledCheckBox->SetExplicitAlignment(align);
fSwapUsageBar = new BStatusBar("swap usage");
BPopUpMenu* menu = new BPopUpMenu("volume menu");
fVolumeMenuField = new BMenuField("volume menu field",
B_TRANSLATE("Use volume:"), menu);
fVolumeMenuField->SetExplicitAlignment(align);
BVolumeRoster roster;
BVolume vol;
while (roster.GetNextVolume(&vol) == B_OK) {
if (!vol.IsPersistent() || vol.IsReadOnly() || vol.IsRemovable()
|| vol.IsShared())
continue;
_AddVolumeMenuItem(vol.Device());
}
watch_node(NULL, B_WATCH_MOUNT, this, this);
fSizeSlider = new SizeSlider("size slider",
B_TRANSLATE("Requested swap file size:"),
new BMessage(kMsgSliderUpdate), 0, 0, B_WILL_DRAW | B_FRAME_EVENTS);
fSizeSlider->SetViewColor(255, 0, 255);
fSizeSlider->SetExplicitAlignment(align);
fWarningStringView = new BStringView("warning",
B_TRANSLATE("Changes will take effect upon reboot."));
BBox* box = new BBox("box");
box->SetLabel(fSwapEnabledCheckBox);
box->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
.SetInsets(B_USE_DEFAULT_SPACING)
.Add(fSwapUsageBar)
.Add(fSwapAutomaticCheckBox)
.Add(fVolumeMenuField)
.Add(fSizeSlider)
.Add(fWarningStringView)
.View());
fDefaultsButton = new BButton("defaults", B_TRANSLATE("Defaults"),
new BMessage(kMsgDefaults));
fRevertButton = new BButton("revert", B_TRANSLATE("Revert"),
new BMessage(kMsgRevert));
fRevertButton->SetEnabled(false);
BLayoutBuilder::Group<>(this, B_VERTICAL)
.SetInsets(B_USE_WINDOW_SPACING)
.Add(box)
.AddGroup(B_HORIZONTAL)
.Add(fDefaultsButton)
.Add(fRevertButton)
.AddGlue()
.End();
BScreen screen;
BRect screenFrame = screen.Frame();
if (!screenFrame.Contains(fSettings.WindowPosition()))
CenterOnScreen();
#ifdef SWAP_VOLUME_IMPLEMENTED
// Validate the volume specified in settings file
status_t result = fSettings.SwapVolume().InitCheck();
if (result != B_OK) {
BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
B_TRANSLATE("The swap volume specified in the settings file is ",
"invalid.\n You can keep the current setting or switch to the "
"default swap volume."),
B_TRANSLATE("Keep"), B_TRANSLATE("Switch"), NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
int32 choice = alert->Go();
if (choice == 1) {
BVolumeRoster volumeRoster;
BVolume bootVolume;
volumeRoster.GetBootVolume(&bootVolume);
fSettings.SetSwapVolume(bootVolume);
}
}
#endif
_Update();
// TODO: We may want to run this at an interval
_UpdateSwapInfo();
fSetupComplete = true;
}
void
SettingsWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_NODE_MONITOR:
{
int32 opcode;
if (message->FindInt32("opcode", &opcode) != B_OK)
break;
dev_t device;
if (opcode == B_DEVICE_MOUNTED
&& message->FindInt32("new device", &device) == B_OK) {
BVolume vol(device);
if (!vol.IsPersistent() || vol.IsReadOnly()
|| vol.IsRemovable() || vol.IsShared()) {
break;
}
_AddVolumeMenuItem(device);
} else if (opcode == B_DEVICE_UNMOUNTED
&& message->FindInt32("device", &device) == B_OK) {
_RemoveVolumeMenuItem(device);
}
_Update();
break;
}
case kMsgRevert:
fSettings.RevertSwapSettings();
_Update();
break;
case kMsgDefaults:
fSettings.DefaultSwapSettings();
_Update();
break;
case kMsgSliderUpdate:
_RecordChoices();
_Update();
break;
case kMsgVolumeSelected:
_RecordChoices();
_Update();
break;
case kMsgSwapEnabledUpdate:
{
if (fSwapEnabledCheckBox->Value() == 0) {
// print out warning, give the user the
// time to think about it :)
// ToDo: maybe we want to remove this possibility in the GUI
// as Be did, but I thought a proper warning could be helpful
// (for those that want to change that anyway)
BAlert* alert = new BAlert(
B_TRANSLATE_SYSTEM_NAME("VirtualMemory"), B_TRANSLATE(
"Disabling virtual memory will have unwanted effects on "
"system stability once the memory is used up.\n"
"Virtual memory does not affect system performance "
"until this point is reached.\n\n"
"Are you really sure you want to turn it off?"),
B_TRANSLATE("Turn off"), B_TRANSLATE("Keep enabled"), NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
int32 choice = alert->Go();
if (choice == 1) {
fSwapEnabledCheckBox->SetValue(1);
break;
}
}
_RecordChoices();
_Update();
break;
}
case kMsgSwapAutomaticUpdate:
{
_RecordChoices();
_Update();
break;
}
default:
BWindow::MessageReceived(message);
}
}
bool
SettingsWindow::QuitRequested()
{
if (!fSetupComplete)
return true;
fSettings.SetWindowPosition(Frame().LeftTop());
_RecordChoices();
fSettings.WriteWindowSettings();
fSettings.WriteSwapSettings();
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}
status_t
SettingsWindow::_AddVolumeMenuItem(dev_t device)
{
if (_FindVolumeMenuItem(device) != NULL)
return B_ERROR;
VolumeMenuItem* item = new VolumeMenuItem(device,
new BMessage(kMsgVolumeSelected));
fs_info info;
if (fs_stat_dev(device, &info) == 0) {
node_ref node;
node.device = info.dev;
node.node = info.root;
AddHandler(item);
watch_node(&node, B_WATCH_NAME, item);
}
fVolumeMenuField->Menu()->AddItem(item);
return B_OK;
}
status_t
SettingsWindow::_RemoveVolumeMenuItem(dev_t device)
{
VolumeMenuItem* item = _FindVolumeMenuItem(device);
if (item != NULL) {
fVolumeMenuField->Menu()->RemoveItem(item);
delete item;
return B_OK;
}
return B_ERROR;
}
VolumeMenuItem*
SettingsWindow::_FindVolumeMenuItem(dev_t device)
{
VolumeMenuItem* item = NULL;
int32 count = fVolumeMenuField->Menu()->CountItems();
for (int i = 0; i < count; i++) {
item = (VolumeMenuItem*)fVolumeMenuField->Menu()->ItemAt(i);
if (item->Volume().Device() == device)
return item;
}
return NULL;
}
void
SettingsWindow::_RecordChoices()
{
fSettings.SetSwapAutomatic(fSwapAutomaticCheckBox->Value());
fSettings.SetSwapEnabled(fSwapEnabledCheckBox->Value());
fSettings.SetSwapSize((off_t)fSizeSlider->Value() * kMegaByte);
fSettings.SetSwapVolume(((VolumeMenuItem*)fVolumeMenuField
->Menu()->FindMarked())->Volume().Device());
}
void
SettingsWindow::_Update()
{
fSwapEnabledCheckBox->SetValue(fSettings.SwapEnabled());
fSwapAutomaticCheckBox->SetValue(fSettings.SwapAutomatic());
VolumeMenuItem* item = _FindVolumeMenuItem(fSettings.SwapVolume());
if (item != NULL) {
fSizeSlider->SetEnabled(true);
item->SetMarked(true);
BEntry swapFile;
if (gBootDev == item->Volume().Device())
swapFile.SetTo("/var/swap");
else {
BDirectory root;
item->Volume().GetRootDirectory(&root);
swapFile.SetTo(&root, "swap");
}
off_t swapFileSize = 0;
swapFile.GetSize(&swapFileSize);
char sizeStr[16];
off_t freeSpace = item->Volume().FreeBytes() + swapFileSize;
off_t safeSpace = freeSpace - (off_t)(0.15 * freeSpace);
(safeSpace >>= 20) <<= 20;
off_t minSize = B_PAGE_SIZE + kMegaByte;
(minSize >>= 20) <<= 20;
BString minLabel, maxLabel;
minLabel << string_for_size(minSize, sizeStr, sizeof(sizeStr));
maxLabel << string_for_size(safeSpace, sizeStr, sizeof(sizeStr));
fSizeSlider->SetLimitLabels(minLabel.String(), maxLabel.String());
fSizeSlider->SetLimits(minSize / kMegaByte, safeSpace / kMegaByte);
fSizeSlider->SetValue(fSettings.SwapSize() / kMegaByte);
} else
fSizeSlider->SetEnabled(false);
bool revertable = fSettings.IsRevertable();
if (revertable)
fWarningStringView->Show();
else
fWarningStringView->Hide();
fRevertButton->SetEnabled(revertable);
fDefaultsButton->SetEnabled(fSettings.IsDefaultable());
// Automatic Swap depends on swap being enabled
fSwapAutomaticCheckBox->SetEnabled(fSettings.SwapEnabled());
// Manual swap settings depend on enabled swap
// and automatic swap being disabled
fSizeSlider->SetEnabled(fSettings.SwapEnabled()
&& !fSwapAutomaticCheckBox->Value());
fVolumeMenuField->SetEnabled(fSettings.SwapEnabled()
&& !fSwapAutomaticCheckBox->Value());
}
void
SettingsWindow::_UpdateSwapInfo()
{
system_info info;
get_system_info(&info);
off_t currentSwapSize = info.max_swap_pages * B_PAGE_SIZE;
off_t currentSwapUsed
= (info.max_swap_pages - info.free_swap_pages) * B_PAGE_SIZE;
char sizeStr[16];
BString swapSizeStr = string_for_size(currentSwapSize, sizeStr,
sizeof(sizeStr));
BString swapUsedStr = string_for_size(currentSwapUsed, sizeStr,
sizeof(sizeStr));
BString string = swapUsedStr << " / " << swapSizeStr;
fSwapUsageBar->SetMaxValue(currentSwapSize / kMegaByte);
fSwapUsageBar->Update(currentSwapUsed / kMegaByte,
B_TRANSLATE("Current Swap:"), string.String());
}
↑ V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.