xref: /haiku/src/preferences/virtualmemory/SettingsWindow.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
1 /*
2  * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "SettingsWindow.h"
8 #include "Settings.h"
9 
10 #include <Application.h>
11 #include <Alert.h>
12 #include <Box.h>
13 #include <Button.h>
14 #include <Catalog.h>
15 #include <CheckBox.h>
16 #include <GroupLayout.h>
17 #include <GroupLayoutBuilder.h>
18 #include <Locale.h>
19 #include <StringView.h>
20 #include <String.h>
21 #include <Slider.h>
22 #include <PopUpMenu.h>
23 #include <MenuItem.h>
24 #include <MenuField.h>
25 #include <Screen.h>
26 #include <FindDirectory.h>
27 #include <Path.h>
28 #include <Volume.h>
29 #include <VolumeRoster.h>
30 
31 #include <stdio.h>
32 
33 
34 #undef TR_CONTEXT
35 #define TR_CONTEXT "SettingsWindow"
36 
37 
38 static const uint32 kMsgDefaults = 'dflt';
39 static const uint32 kMsgRevert = 'rvrt';
40 static const uint32 kMsgSliderUpdate = 'slup';
41 static const uint32 kMsgSwapEnabledUpdate = 'swen';
42 
43 
44 class SizeSlider : public BSlider {
45 	public:
46 		SizeSlider(const char* name, const char* label,
47 			BMessage* message, int32 min, int32 max, uint32 flags);
48 		virtual ~SizeSlider();
49 
50 		virtual const char* UpdateText() const;
51 
52 	private:
53 		mutable BString	fText;
54 };
55 
56 
57 static const int64 kMegaByte = 1048576;
58 
59 
60 const char *
61 byte_string(int64 size)
62 {
63 	double value = 1. * size;
64 	static char string[256];
65 
66 	if (value < 1024)
67 		snprintf(string, sizeof(string), TR("%Ld B"), size);
68 	else {
69 		static const char *units[] = {TR_MARK("KB"), TR_MARK("MB"), TR_MARK("GB"), NULL};
70 		int32 i = -1;
71 
72 		do {
73 			value /= 1024.0;
74 			i++;
75 		} while (value >= 1024 && units[i + 1]);
76 
77 		off_t rounded = off_t(value * 100LL);
78 		sprintf(string, "%g %s", rounded / 100.0, TR(units[i]));
79 	}
80 
81 	return string;
82 }
83 
84 
85 //	#pragma mark -
86 
87 
88 SizeSlider::SizeSlider(const char* name, const char* label,
89 	BMessage* message, int32 min, int32 max, uint32 flags)
90 	: BSlider(name, label, message, min, max, B_HORIZONTAL, B_BLOCK_THUMB, flags)
91 {
92 	rgb_color color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
93 	UseFillColor(true, &color);
94 }
95 
96 
97 SizeSlider::~SizeSlider()
98 {
99 }
100 
101 
102 const char *
103 SizeSlider::UpdateText() const
104 {
105 	fText = byte_string(Value() * kMegaByte);
106 
107 	return fText.String();
108 }
109 
110 
111 //	#pragma mark -
112 
113 
114 SettingsWindow::SettingsWindow()
115 	: BWindow(BRect(0, 0, 269, 172), TR("VirtualMemory"), B_TITLED_WINDOW,
116 			B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
117 			| B_AUTO_UPDATE_SIZE_LIMITS)
118 {
119 	BView* view = new BGroupView();
120 
121 	fSwapEnabledCheckBox = new BCheckBox("enable swap",
122 		TR("Enable virtual memory"),
123 		new BMessage(kMsgSwapEnabledUpdate));
124 	fSwapEnabledCheckBox->SetValue(fSettings.SwapEnabled());
125 
126 	BBox* box = new BBox("box", B_FOLLOW_LEFT_RIGHT);
127 	box->SetLabel(fSwapEnabledCheckBox);
128 
129 	system_info info;
130 	get_system_info(&info);
131 
132 	BString string = TR("Physical memory: ");
133 	string << byte_string((off_t)info.max_pages * B_PAGE_SIZE);
134 	BStringView* memoryView = new BStringView("physical memory", string.String());
135 
136 	string = TR("Current swap file size: ");
137 	string << byte_string(fSettings.SwapSize());
138 	BStringView* swapfileView = new BStringView("current swap size", string.String());
139 
140 	BPopUpMenu* menu = new BPopUpMenu("volumes");
141 
142 	// collect volumes
143 	// TODO: listen to volume changes!
144 	// TODO: accept dropped volumes
145 
146 	BVolumeRoster volumeRoster;
147 	BVolume volume;
148 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
149 		char name[B_FILE_NAME_LENGTH];
150 		if (!volume.IsPersistent() || volume.GetName(name) != B_OK || !name[0])
151 			continue;
152 
153 		BMenuItem* item = new BMenuItem(name, NULL);
154 		menu->AddItem(item);
155 
156 		if (volume.Device() == fSettings.SwapVolume().Device())
157 			item->SetMarked(true);
158 	}
159 
160 	BMenuField* field = new BMenuField("devices", TR("Use volume:"), menu);
161 	field->SetEnabled(false);
162 
163 	off_t minSize, maxSize;
164 	_GetSwapFileLimits(minSize, maxSize);
165 
166 	fSizeSlider = new SizeSlider("size slider", TR("Requested swap file size:"),
167 		new BMessage(kMsgSliderUpdate), minSize / kMegaByte, maxSize / kMegaByte,
168 		B_WILL_DRAW | B_FRAME_EVENTS);
169 	fSizeSlider->SetLimitLabels(TR("999 MB"), TR("999 MB"));
170 	fSizeSlider->SetViewColor(255, 0, 255);
171 
172 	fWarningStringView = new BStringView("", "");
173 	fWarningStringView->SetAlignment(B_ALIGN_CENTER);
174 
175 	view->SetLayout(new BGroupLayout(B_HORIZONTAL));
176 	view->AddChild(BGroupLayoutBuilder(B_VERTICAL, 10)
177 		.AddGroup(B_HORIZONTAL)
178 			.Add(memoryView)
179 			.AddGlue()
180 		.End()
181 		.AddGroup(B_HORIZONTAL)
182 			.Add(swapfileView)
183 			.AddGlue()
184 		.End()
185 		.AddGroup(B_HORIZONTAL)
186 			.Add(field)
187 			.AddGlue()
188 		.End()
189 		.Add(fSizeSlider)
190 		.Add(fWarningStringView)
191 		.SetInsets(10, 10, 10, 10)
192 	);
193 	box->AddChild(view);
194 
195 	// Add "Defaults" and "Revert" buttons
196 
197 	fDefaultsButton = new BButton("defaults", TR("Defaults"), new BMessage(kMsgDefaults));
198 	fDefaultsButton->SetEnabled(fSettings.IsDefaultable());
199 
200 	fRevertButton = new BButton("revert", TR("Revert"), new BMessage(kMsgRevert));
201 	fRevertButton->SetEnabled(false);
202 
203 	SetLayout(new BGroupLayout(B_HORIZONTAL));
204 	AddChild(BGroupLayoutBuilder(B_VERTICAL, 10)
205 		.Add(box)
206 		.AddGroup(B_HORIZONTAL, 10)
207 			.Add(fDefaultsButton)
208 			.Add(fRevertButton)
209 			.AddGlue()
210 		.End()
211 		.SetInsets(10, 10, 10, 10)
212 	);
213 
214 	_Update();
215 
216 	BScreen screen;
217 	BRect screenFrame = screen.Frame();
218 
219 	if (!screenFrame.Contains(fSettings.WindowPosition())) {
220 		// move on screen, centered
221 		CenterOnScreen();
222 	} else
223 		MoveTo(fSettings.WindowPosition());
224 }
225 
226 
227 SettingsWindow::~SettingsWindow()
228 {
229 }
230 
231 
232 	void
233 SettingsWindow::_Update()
234 {
235 	if ((fSwapEnabledCheckBox->Value() != 0) != fSettings.SwapEnabled())
236 		fSwapEnabledCheckBox->SetValue(fSettings.SwapEnabled());
237 
238 	off_t minSize, maxSize;
239 	if (_GetSwapFileLimits(minSize, maxSize) == B_OK) {
240 		BString minLabel, maxLabel;
241 		minLabel << byte_string(minSize);
242 		maxLabel << byte_string(maxSize);
243 		if (minLabel != fSizeSlider->MinLimitLabel()
244 				|| maxLabel != fSizeSlider->MaxLimitLabel()) {
245 			fSizeSlider->SetLimitLabels(minLabel.String(), maxLabel.String());
246 #ifdef __HAIKU__
247 			fSizeSlider->SetLimits(minSize / kMegaByte, maxSize / kMegaByte);
248 #endif
249 		}
250 
251 		if (fSizeSlider->Value() != fSettings.SwapSize() / kMegaByte)
252 			fSizeSlider->SetValue(fSettings.SwapSize() / kMegaByte);
253 
254 		fSizeSlider->SetEnabled(true);
255 	} else {
256 		fSizeSlider->SetValue(minSize);
257 		fSizeSlider->SetEnabled(false);
258 	}
259 
260 	// ToDo: set volume
261 
262 	fDefaultsButton->SetEnabled(fSettings.IsDefaultable());
263 
264 	bool changed = fSettings.SwapChanged();
265 	if (fRevertButton->IsEnabled() != changed) {
266 		fRevertButton->SetEnabled(changed);
267 		if (changed)
268 			fWarningStringView->SetText(TR("Changes will take effect on restart!"));
269 		else
270 			fWarningStringView->SetText("");
271 	}
272 }
273 
274 
275 	status_t
276 SettingsWindow::_GetSwapFileLimits(off_t& minSize, off_t& maxSize)
277 {
278 	// minimum size is an arbitrarily chosen MB
279 	minSize = kMegaByte;
280 
281 	// maximum size is the free space on the current volume
282 	// (minus some safety offset, depending on the disk size)
283 
284 	off_t freeSpace = fSettings.SwapVolume().FreeBytes();
285 	off_t safetyFreeSpace = fSettings.SwapVolume().Capacity() / 100;
286 	if (safetyFreeSpace > 1024 * kMegaByte)
287 		safetyFreeSpace = 1024 * kMegaByte;
288 
289 	// check if there already is a page file on this disk and
290 	// adjust the free space accordingly
291 
292 	BPath path;
293 	if (find_directory(B_COMMON_VAR_DIRECTORY, &path, false,
294 			&fSettings.SwapVolume()) == B_OK) {
295 		path.Append("swap");
296 		BEntry swap(path.Path());
297 
298 		off_t size;
299 		if (swap.GetSize(&size) == B_OK)
300 			freeSpace += size;
301 	}
302 
303 	maxSize = freeSpace - safetyFreeSpace;
304 
305 	if (maxSize < minSize) {
306 		maxSize = minSize;
307 		return B_ERROR;
308 	}
309 
310 	return B_OK;
311 }
312 
313 
314 void
315 SettingsWindow::MessageReceived(BMessage* message)
316 {
317 	switch (message->what) {
318 		case kMsgRevert:
319 			fSettings.RevertSwapChanges();
320 			_Update();
321 			break;
322 		case kMsgDefaults:
323 			fSettings.SetSwapDefaults();
324 			_Update();
325 			break;
326 		case kMsgSliderUpdate:
327 			fSettings.SetSwapSize((off_t)fSizeSlider->Value() * kMegaByte);
328 			_Update();
329 			break;
330 		case kMsgSwapEnabledUpdate:
331 		{
332 			int32 value;
333 			if (message->FindInt32("be:value", &value) != B_OK)
334 				break;
335 
336 			if (value == 0) {
337 				// print out warning, give the user the time to think about it :)
338 				// ToDo: maybe we want to remove this possibility in the GUI
339 				//	as Be did, but I thought a proper warning could be helpful
340 				//	(for those that want to change that anyway)
341 				int32 choice = (new BAlert("VirtualMemory",
342 					TR("Disabling virtual memory will have unwanted effects on "
343 					"system stability once the memory is used up.\n"
344 					"Virtual memory does not affect system performance "
345 					"until this point is reached.\n\n"
346 					"Are you really sure you want to turn it off?"),
347 					TR("Turn off"), TR("Keep enabled"), NULL,
348 					B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
349 				if (choice == 1) {
350 					fSwapEnabledCheckBox->SetValue(1);
351 					break;
352 				}
353 			}
354 
355 			fSettings.SetSwapEnabled(value != 0);
356 			_Update();
357 			break;
358 		}
359 
360 		default:
361 			BWindow::MessageReceived(message);
362 	}
363 }
364 
365 
366 bool
367 SettingsWindow::QuitRequested()
368 {
369 	fSettings.SetWindowPosition(Frame().LeftTop());
370 	be_app->PostMessage(B_QUIT_REQUESTED);
371 	return true;
372 }
373 
374