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.
9 * Hamish Morrison, hamish@lavabit.com
10 * Alexander von Gluck, kallisti5@unixzen.com
14 #include "SettingsWindow.h"
16 #include <Application.h>
22 #include <Directory.h>
23 #include <FindDirectory.h>
24 #include <LayoutBuilder.h>
26 #include <MenuField.h>
27 #include <NodeMonitor.h>
29 #include <PopUpMenu.h>
31 #include <StringForSize.h>
32 #include <StringView.h>
35 #include <system_info.h>
37 #include <VolumeRoster.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
,
62 rgb_color color
= ui_color(B_CONTROL_HIGHLIGHT_COLOR
);
63 UseFillColor(true, &color
);
68 SizeSlider::UpdateText() const
70 return string_for_size(Value() * kMegaByte
, fText
, sizeof(fText
));
74 VolumeMenuItem::VolumeMenuItem(BVolume volume
, BMessage
* message
)
76 BMenuItem("", message
),
84 VolumeMenuItem::MessageReceived(BMessage
* message
)
86 if (message
->what
== B_NODE_MONITOR
) {
88 if (message
->FindInt32("opcode", &code
) == B_OK
)
89 if (code
== B_ENTRY_MOVED
)
96 VolumeMenuItem::GenerateLabel()
98 char name
[B_FILE_NAME_LENGTH
+ 1];
99 fVolume
.GetName(name
);
102 if (fVolume
.GetRootDirectory(&dir
) == B_OK
) {
104 if (dir
.GetEntry(&entry
) == B_OK
) {
106 if (entry
.GetPath(&path
) == B_OK
) {
108 label
<< name
<< " (" << path
.Path() << ")";
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
),
127 fDefaultsButton(NULL
),
129 fWarningStringView(NULL
),
130 fVolumeMenuField(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
)
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();
151 be_app
->PostMessage(B_QUIT_REQUESTED
);
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();
161 be_app
->PostMessage(B_QUIT_REQUESTED
);
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
;
186 while (roster
.GetNextVolume(&vol
) == B_OK
) {
187 if (!vol
.IsPersistent() || vol
.IsReadOnly() || vol
.IsRemovable()
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
)
210 .Add(fSwapAutomaticCheckBox
)
211 .Add(fVolumeMenuField
)
213 .Add(fWarningStringView
)
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
)
226 .AddGroup(B_HORIZONTAL
)
227 .Add(fDefaultsButton
)
233 BRect screenFrame
= screen
.Frame();
234 if (!screenFrame
.Contains(fSettings
.WindowPosition()))
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();
251 BVolumeRoster volumeRoster
;
253 volumeRoster
.GetBootVolume(&bootVolume
);
254 fSettings
.SetSwapVolume(bootVolume
);
261 // TODO: We may want to run this at an interval
263 fSetupComplete
= true;
268 SettingsWindow::MessageReceived(BMessage
* message
)
270 switch (message
->what
) {
274 if (message
->FindInt32("opcode", &opcode
) != B_OK
)
277 if (opcode
== B_DEVICE_MOUNTED
278 && message
->FindInt32("new device", &device
) == B_OK
) {
280 if (!vol
.IsPersistent() || vol
.IsReadOnly()
281 || vol
.IsRemovable() || vol
.IsShared()) {
284 _AddVolumeMenuItem(device
);
285 } else if (opcode
== B_DEVICE_UNMOUNTED
286 && message
->FindInt32("device", &device
) == B_OK
) {
287 _RemoveVolumeMenuItem(device
);
293 fSettings
.RevertSwapSettings();
297 fSettings
.DefaultSwapSettings();
300 case kMsgSliderUpdate
:
304 case kMsgVolumeSelected
:
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();
328 fSwapEnabledCheckBox
->SetValue(1);
337 case kMsgSwapAutomaticUpdate
:
345 BWindow::MessageReceived(message
);
351 SettingsWindow::QuitRequested()
356 fSettings
.SetWindowPosition(Frame().LeftTop());
358 fSettings
.WriteWindowSettings();
359 fSettings
.WriteSwapSettings();
360 be_app
->PostMessage(B_QUIT_REQUESTED
);
366 SettingsWindow::_AddVolumeMenuItem(dev_t device
)
368 if (_FindVolumeMenuItem(device
) != NULL
)
371 VolumeMenuItem
* item
= new VolumeMenuItem(device
,
372 new BMessage(kMsgVolumeSelected
));
375 if (fs_stat_dev(device
, &info
) == 0) {
377 node
.device
= info
.dev
;
378 node
.node
= info
.root
;
380 watch_node(&node
, B_WATCH_NAME
, item
);
383 fVolumeMenuField
->Menu()->AddItem(item
);
389 SettingsWindow::_RemoveVolumeMenuItem(dev_t device
)
391 VolumeMenuItem
* item
= _FindVolumeMenuItem(device
);
393 fVolumeMenuField
->Menu()->RemoveItem(item
);
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
)
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());
428 SettingsWindow::_Update()
430 fSwapEnabledCheckBox
->SetValue(fSettings
.SwapEnabled());
431 fSwapAutomaticCheckBox
->SetValue(fSettings
.SwapAutomatic());
433 VolumeMenuItem
* item
= _FindVolumeMenuItem(fSettings
.SwapVolume());
435 fSizeSlider
->SetEnabled(true);
436 item
->SetMarked(true);
438 if (gBootDev
== item
->Volume().Device())
439 swapFile
.SetTo("/var/swap");
442 item
->Volume().GetRootDirectory(&root
);
443 swapFile
.SetTo(&root
, "swap");
446 off_t swapFileSize
= 0;
447 swapFile
.GetSize(&swapFileSize
);
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
);
464 fSizeSlider
->SetEnabled(false);
466 bool revertable
= fSettings
.IsRevertable();
468 fWarningStringView
->Show();
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());
488 SettingsWindow::_UpdateSwapInfo()
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
;
498 BString swapSizeStr
= string_for_size(currentSwapSize
, sizeStr
,
500 BString swapUsedStr
= string_for_size(currentSwapUsed
, sizeStr
,
503 BString string
= swapUsedStr
<< " / " << swapSizeStr
;
505 fSwapUsageBar
->SetMaxValue(currentSwapSize
/ kMegaByte
);
506 fSwapUsageBar
->Update(currentSwapUsed
/ kMegaByte
,
507 B_TRANSLATE("Current Swap:"), string
.String());