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