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