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