libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / preferences / virtualmemory / SettingsWindow.cpp
blob155264e62f7e83f5aa5c00a719cf6d574d535757
1 /*
2 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de
3 * All rights reserved. Distributed under the terms of the MIT License.
5 * Copyright 2010-2012 Haiku, Inc. All rights reserved.
6 * Distributed under the terms of the MIT License.
8 * Authors:
9 * Hamish Morrison, hamish@lavabit.com
10 * Alexander von Gluck, kallisti5@unixzen.com
14 #include "SettingsWindow.h"
16 #include <Application.h>
17 #include <Alert.h>
18 #include <Box.h>
19 #include <Button.h>
20 #include <Catalog.h>
21 #include <CheckBox.h>
22 #include <Directory.h>
23 #include <FindDirectory.h>
24 #include <LayoutBuilder.h>
25 #include <MenuItem.h>
26 #include <MenuField.h>
27 #include <NodeMonitor.h>
28 #include <Path.h>
29 #include <PopUpMenu.h>
30 #include <Screen.h>
31 #include <StringForSize.h>
32 #include <StringView.h>
33 #include <String.h>
34 #include <Slider.h>
35 #include <system_info.h>
36 #include <Volume.h>
37 #include <VolumeRoster.h>
39 #include "Settings.h"
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "SettingsWindow"
46 static const uint32 kMsgDefaults = 'dflt';
47 static const uint32 kMsgRevert = 'rvrt';
48 static const uint32 kMsgSliderUpdate = 'slup';
49 static const uint32 kMsgSwapEnabledUpdate = 'swen';
50 static const uint32 kMsgSwapAutomaticUpdate = 'swat';
51 static const uint32 kMsgVolumeSelected = 'vlsl';
52 static const off_t kMegaByte = 1024 * 1024;
53 static dev_t gBootDev = -1;
56 SizeSlider::SizeSlider(const char* name, const char* label,
57 BMessage* message, int32 min, int32 max, uint32 flags)
59 BSlider(name, label, message, min, max, B_HORIZONTAL,
60 B_BLOCK_THUMB, flags)
62 rgb_color color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
63 UseFillColor(true, &color);
67 const char*
68 SizeSlider::UpdateText() const
70 return string_for_size(Value() * kMegaByte, fText, sizeof(fText));
74 VolumeMenuItem::VolumeMenuItem(BVolume volume, BMessage* message)
76 BMenuItem("", message),
77 fVolume(volume)
79 GenerateLabel();
83 void
84 VolumeMenuItem::MessageReceived(BMessage* message)
86 if (message->what == B_NODE_MONITOR) {
87 int32 code;
88 if (message->FindInt32("opcode", &code) == B_OK)
89 if (code == B_ENTRY_MOVED)
90 GenerateLabel();
95 void
96 VolumeMenuItem::GenerateLabel()
98 char name[B_FILE_NAME_LENGTH + 1];
99 fVolume.GetName(name);
101 BDirectory dir;
102 if (fVolume.GetRootDirectory(&dir) == B_OK) {
103 BEntry entry;
104 if (dir.GetEntry(&entry) == B_OK) {
105 BPath path;
106 if (entry.GetPath(&path) == B_OK) {
107 BString label;
108 label << name << " (" << path.Path() << ")";
109 SetLabel(label);
110 return;
115 SetLabel(name);
119 SettingsWindow::SettingsWindow()
121 BWindow(BRect(0, 0, 269, 172), B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
122 B_TITLED_WINDOW, B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS
123 | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
124 fSwapEnabledCheckBox(NULL),
125 fSwapAutomaticCheckBox(NULL),
126 fSizeSlider(NULL),
127 fDefaultsButton(NULL),
128 fRevertButton(NULL),
129 fWarningStringView(NULL),
130 fVolumeMenuField(NULL),
131 fSwapUsageBar(NULL),
132 fSetupComplete(false)
134 gBootDev = dev_for_path("/boot");
135 BAlignment align(B_ALIGN_LEFT, B_ALIGN_MIDDLE);
137 if (fSettings.ReadWindowSettings() != B_OK)
138 CenterOnScreen();
139 else
140 MoveTo(fSettings.WindowPosition());
142 status_t result = fSettings.ReadSwapSettings();
143 if (result == kErrorSettingsNotFound)
144 fSettings.DefaultSwapSettings(false);
145 else if (result == kErrorSettingsInvalid) {
146 int32 choice = (new BAlert(B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
147 B_TRANSLATE("The settings specified in the settings file "
148 "are invalid. You can load the defaults or quit."),
149 B_TRANSLATE("Load defaults"), B_TRANSLATE("Quit")))->Go();
150 if (choice == 1) {
151 be_app->PostMessage(B_QUIT_REQUESTED);
152 return;
154 fSettings.DefaultSwapSettings(false);
155 } else if (result == kErrorVolumeNotFound) {
156 int32 choice = (new BAlert(B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
157 B_TRANSLATE("The volume specified in the settings file "
158 "could not be found. You can use the boot volume or quit."),
159 B_TRANSLATE("Use boot volume"), B_TRANSLATE("Quit")))->Go();
160 if (choice == 1) {
161 be_app->PostMessage(B_QUIT_REQUESTED);
162 return;
164 fSettings.SetSwapVolume(gBootDev, false);
167 fSwapEnabledCheckBox = new BCheckBox("enable swap",
168 B_TRANSLATE("Enable virtual memory"),
169 new BMessage(kMsgSwapEnabledUpdate));
170 fSwapEnabledCheckBox->SetExplicitAlignment(align);
172 fSwapAutomaticCheckBox = new BCheckBox("auto swap",
173 B_TRANSLATE("Automatic swap management"),
174 new BMessage(kMsgSwapAutomaticUpdate));
175 fSwapEnabledCheckBox->SetExplicitAlignment(align);
177 fSwapUsageBar = new BStatusBar("swap usage");
179 BPopUpMenu* menu = new BPopUpMenu("volume menu");
180 fVolumeMenuField = new BMenuField("volume menu field",
181 B_TRANSLATE("Use volume:"), menu);
182 fVolumeMenuField->SetExplicitAlignment(align);
184 BVolumeRoster roster;
185 BVolume vol;
186 while (roster.GetNextVolume(&vol) == B_OK) {
187 if (!vol.IsPersistent() || vol.IsReadOnly() || vol.IsRemovable()
188 || vol.IsShared())
189 continue;
190 _AddVolumeMenuItem(vol.Device());
193 watch_node(NULL, B_WATCH_MOUNT, this, this);
195 fSizeSlider = new SizeSlider("size slider",
196 B_TRANSLATE("Requested swap file size:"),
197 new BMessage(kMsgSliderUpdate), 0, 0, B_WILL_DRAW | B_FRAME_EVENTS);
198 fSizeSlider->SetViewColor(255, 0, 255);
199 fSizeSlider->SetExplicitAlignment(align);
201 fWarningStringView = new BStringView("warning",
202 B_TRANSLATE("Changes will take effect upon reboot."));
204 BBox* box = new BBox("box");
205 box->SetLabel(fSwapEnabledCheckBox);
207 box->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
208 .SetInsets(B_USE_DEFAULT_SPACING)
209 .Add(fSwapUsageBar)
210 .Add(fSwapAutomaticCheckBox)
211 .Add(fVolumeMenuField)
212 .Add(fSizeSlider)
213 .Add(fWarningStringView)
214 .View());
216 fDefaultsButton = new BButton("defaults", B_TRANSLATE("Defaults"),
217 new BMessage(kMsgDefaults));
219 fRevertButton = new BButton("revert", B_TRANSLATE("Revert"),
220 new BMessage(kMsgRevert));
221 fRevertButton->SetEnabled(false);
223 BLayoutBuilder::Group<>(this, B_VERTICAL)
224 .SetInsets(B_USE_WINDOW_SPACING)
225 .Add(box)
226 .AddGroup(B_HORIZONTAL)
227 .Add(fDefaultsButton)
228 .Add(fRevertButton)
229 .AddGlue()
230 .End();
232 BScreen screen;
233 BRect screenFrame = screen.Frame();
234 if (!screenFrame.Contains(fSettings.WindowPosition()))
235 CenterOnScreen();
237 #ifdef SWAP_VOLUME_IMPLEMENTED
238 // Validate the volume specified in settings file
239 status_t result = fSettings.SwapVolume().InitCheck();
241 if (result != B_OK) {
242 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("VirtualMemory"),
243 B_TRANSLATE("The swap volume specified in the settings file is ",
244 "invalid.\n You can keep the current setting or switch to the "
245 "default swap volume."),
246 B_TRANSLATE("Keep"), B_TRANSLATE("Switch"), NULL,
247 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
248 alert->SetShortcut(0, B_ESCAPE);
249 int32 choice = alert->Go();
250 if (choice == 1) {
251 BVolumeRoster volumeRoster;
252 BVolume bootVolume;
253 volumeRoster.GetBootVolume(&bootVolume);
254 fSettings.SetSwapVolume(bootVolume);
257 #endif
259 _Update();
261 // TODO: We may want to run this at an interval
262 _UpdateSwapInfo();
263 fSetupComplete = true;
267 void
268 SettingsWindow::MessageReceived(BMessage* message)
270 switch (message->what) {
271 case B_NODE_MONITOR:
273 int32 opcode;
274 if (message->FindInt32("opcode", &opcode) != B_OK)
275 break;
276 dev_t device;
277 if (opcode == B_DEVICE_MOUNTED
278 && message->FindInt32("new device", &device) == B_OK) {
279 BVolume vol(device);
280 if (!vol.IsPersistent() || vol.IsReadOnly()
281 || vol.IsRemovable() || vol.IsShared()) {
282 break;
284 _AddVolumeMenuItem(device);
285 } else if (opcode == B_DEVICE_UNMOUNTED
286 && message->FindInt32("device", &device) == B_OK) {
287 _RemoveVolumeMenuItem(device);
289 _Update();
290 break;
292 case kMsgRevert:
293 fSettings.RevertSwapSettings();
294 _Update();
295 break;
296 case kMsgDefaults:
297 fSettings.DefaultSwapSettings();
298 _Update();
299 break;
300 case kMsgSliderUpdate:
301 _RecordChoices();
302 _Update();
303 break;
304 case kMsgVolumeSelected:
305 _RecordChoices();
306 _Update();
307 break;
308 case kMsgSwapEnabledUpdate:
310 if (fSwapEnabledCheckBox->Value() == 0) {
311 // print out warning, give the user the
312 // time to think about it :)
313 // ToDo: maybe we want to remove this possibility in the GUI
314 // as Be did, but I thought a proper warning could be helpful
315 // (for those that want to change that anyway)
316 BAlert* alert = new BAlert(
317 B_TRANSLATE_SYSTEM_NAME("VirtualMemory"), B_TRANSLATE(
318 "Disabling virtual memory will have unwanted effects on "
319 "system stability once the memory is used up.\n"
320 "Virtual memory does not affect system performance "
321 "until this point is reached.\n\n"
322 "Are you really sure you want to turn it off?"),
323 B_TRANSLATE("Turn off"), B_TRANSLATE("Keep enabled"), NULL,
324 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
325 alert->SetShortcut(1, B_ESCAPE);
326 int32 choice = alert->Go();
327 if (choice == 1) {
328 fSwapEnabledCheckBox->SetValue(1);
329 break;
333 _RecordChoices();
334 _Update();
335 break;
337 case kMsgSwapAutomaticUpdate:
339 _RecordChoices();
340 _Update();
341 break;
344 default:
345 BWindow::MessageReceived(message);
350 bool
351 SettingsWindow::QuitRequested()
353 if (!fSetupComplete)
354 return true;
356 fSettings.SetWindowPosition(Frame().LeftTop());
357 _RecordChoices();
358 fSettings.WriteWindowSettings();
359 fSettings.WriteSwapSettings();
360 be_app->PostMessage(B_QUIT_REQUESTED);
361 return true;
365 status_t
366 SettingsWindow::_AddVolumeMenuItem(dev_t device)
368 if (_FindVolumeMenuItem(device) != NULL)
369 return B_ERROR;
371 VolumeMenuItem* item = new VolumeMenuItem(device,
372 new BMessage(kMsgVolumeSelected));
374 fs_info info;
375 if (fs_stat_dev(device, &info) == 0) {
376 node_ref node;
377 node.device = info.dev;
378 node.node = info.root;
379 AddHandler(item);
380 watch_node(&node, B_WATCH_NAME, item);
383 fVolumeMenuField->Menu()->AddItem(item);
384 return B_OK;
388 status_t
389 SettingsWindow::_RemoveVolumeMenuItem(dev_t device)
391 VolumeMenuItem* item = _FindVolumeMenuItem(device);
392 if (item != NULL) {
393 fVolumeMenuField->Menu()->RemoveItem(item);
394 delete item;
395 return B_OK;
397 return B_ERROR;
401 VolumeMenuItem*
402 SettingsWindow::_FindVolumeMenuItem(dev_t device)
404 VolumeMenuItem* item = NULL;
405 int32 count = fVolumeMenuField->Menu()->CountItems();
406 for (int i = 0; i < count; i++) {
407 item = (VolumeMenuItem*)fVolumeMenuField->Menu()->ItemAt(i);
408 if (item->Volume().Device() == device)
409 return item;
412 return NULL;
416 void
417 SettingsWindow::_RecordChoices()
419 fSettings.SetSwapAutomatic(fSwapAutomaticCheckBox->Value());
420 fSettings.SetSwapEnabled(fSwapEnabledCheckBox->Value());
421 fSettings.SetSwapSize((off_t)fSizeSlider->Value() * kMegaByte);
422 fSettings.SetSwapVolume(((VolumeMenuItem*)fVolumeMenuField
423 ->Menu()->FindMarked())->Volume().Device());
427 void
428 SettingsWindow::_Update()
430 fSwapEnabledCheckBox->SetValue(fSettings.SwapEnabled());
431 fSwapAutomaticCheckBox->SetValue(fSettings.SwapAutomatic());
433 VolumeMenuItem* item = _FindVolumeMenuItem(fSettings.SwapVolume());
434 if (item != NULL) {
435 fSizeSlider->SetEnabled(true);
436 item->SetMarked(true);
437 BEntry swapFile;
438 if (gBootDev == item->Volume().Device())
439 swapFile.SetTo("/var/swap");
440 else {
441 BDirectory root;
442 item->Volume().GetRootDirectory(&root);
443 swapFile.SetTo(&root, "swap");
446 off_t swapFileSize = 0;
447 swapFile.GetSize(&swapFileSize);
449 char sizeStr[16];
451 off_t freeSpace = item->Volume().FreeBytes() + swapFileSize;
452 off_t safeSpace = freeSpace - (off_t)(0.15 * freeSpace);
453 (safeSpace >>= 20) <<= 20;
454 off_t minSize = B_PAGE_SIZE + kMegaByte;
455 (minSize >>= 20) <<= 20;
456 BString minLabel, maxLabel;
457 minLabel << string_for_size(minSize, sizeStr, sizeof(sizeStr));
458 maxLabel << string_for_size(safeSpace, sizeStr, sizeof(sizeStr));
460 fSizeSlider->SetLimitLabels(minLabel.String(), maxLabel.String());
461 fSizeSlider->SetLimits(minSize / kMegaByte, safeSpace / kMegaByte);
462 fSizeSlider->SetValue(fSettings.SwapSize() / kMegaByte);
463 } else
464 fSizeSlider->SetEnabled(false);
466 bool revertable = fSettings.IsRevertable();
467 if (revertable)
468 fWarningStringView->Show();
469 else
470 fWarningStringView->Hide();
472 fRevertButton->SetEnabled(revertable);
473 fDefaultsButton->SetEnabled(fSettings.IsDefaultable());
475 // Automatic Swap depends on swap being enabled
476 fSwapAutomaticCheckBox->SetEnabled(fSettings.SwapEnabled());
478 // Manual swap settings depend on enabled swap
479 // and automatic swap being disabled
480 fSizeSlider->SetEnabled(fSettings.SwapEnabled()
481 && !fSwapAutomaticCheckBox->Value());
482 fVolumeMenuField->SetEnabled(fSettings.SwapEnabled()
483 && !fSwapAutomaticCheckBox->Value());
487 void
488 SettingsWindow::_UpdateSwapInfo()
490 system_info info;
491 get_system_info(&info);
493 off_t currentSwapSize = info.max_swap_pages * B_PAGE_SIZE;
494 off_t currentSwapUsed
495 = (info.max_swap_pages - info.free_swap_pages) * B_PAGE_SIZE;
497 char sizeStr[16];
498 BString swapSizeStr = string_for_size(currentSwapSize, sizeStr,
499 sizeof(sizeStr));
500 BString swapUsedStr = string_for_size(currentSwapUsed, sizeStr,
501 sizeof(sizeStr));
503 BString string = swapUsedStr << " / " << swapSizeStr;
505 fSwapUsageBar->SetMaxValue(currentSwapSize / kMegaByte);
506 fSwapUsageBar->Update(currentSwapUsed / kMegaByte,
507 B_TRANSLATE("Current Swap:"), string.String());