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