1 /* 2 * Copyright 2009-2011 Haiku, Inc. 3 * All Rights Reserved. Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * John Scipione, jscipione@gmail.com 7 * Jonas Sundström, jonas@kirilla.com 8 */ 9 10 11 #include "PreferencesWindow.h" 12 13 #include <ctype.h> 14 15 #include <Box.h> 16 #include <Button.h> 17 #include <Catalog.h> 18 #include <CheckBox.h> 19 #include <ControlLook.h> 20 #include <File.h> 21 #include <FormattingConventions.h> 22 #include <GroupLayout.h> 23 #include <ListView.h> 24 #include <Locale.h> 25 #include <LayoutBuilder.h> 26 #include <OpenWithTracker.h> 27 #include <Path.h> 28 #include <RadioButton.h> 29 #include <Roster.h> 30 #include <SeparatorView.h> 31 #include <Screen.h> 32 #include <Slider.h> 33 #include <SpaceLayoutItem.h> 34 #include <TextControl.h> 35 #include <View.h> 36 37 #include "BarApp.h" 38 #include "DeskbarUtils.h" 39 #include "StatusView.h" 40 41 42 static const float kIndentSpacing 43 = be_control_look->DefaultItemSpacing() * 2.3; 44 static const uint32 kSettingsViewChanged = 'Svch'; 45 static const char* kSettingsFileName = "prefs_window_settings"; 46 47 48 #undef B_TRANSLATION_CONTEXT 49 #define B_TRANSLATION_CONTEXT "PreferencesWindow" 50 51 52 PreferencesWindow::PreferencesWindow(BRect frame) 53 : 54 BWindow(frame, B_TRANSLATE("Deskbar preferences"), B_TITLED_WINDOW, 55 B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE) 56 { 57 // Initial settings (used by revert button) 58 memcpy(&fSettings, static_cast<TBarApp*>(be_app)->Settings(), 59 sizeof(desk_settings)); 60 61 // Menu controls 62 fMenuRecentDocuments = new BCheckBox(B_TRANSLATE("Recent documents:"), 63 new BMessage(kUpdateRecentCounts)); 64 fMenuRecentApplications = new BCheckBox(B_TRANSLATE("Recent applications:"), 65 new BMessage(kUpdateRecentCounts)); 66 fMenuRecentFolders = new BCheckBox(B_TRANSLATE("Recent folders:"), 67 new BMessage(kUpdateRecentCounts)); 68 69 fMenuRecentDocumentCount = new BTextControl(NULL, NULL, 70 new BMessage(kUpdateRecentCounts)); 71 fMenuRecentApplicationCount = new BTextControl(NULL, NULL, 72 new BMessage(kUpdateRecentCounts)); 73 fMenuRecentFolderCount = new BTextControl(NULL, NULL, 74 new BMessage(kUpdateRecentCounts)); 75 76 // Applications controls 77 fAppsSort = new BCheckBox(B_TRANSLATE("Sort applications by name"), 78 new BMessage(kSortRunningApps)); 79 fAppsSortTrackerFirst = new BCheckBox(B_TRANSLATE("Tracker always first"), 80 new BMessage(kTrackerFirst)); 81 fAppsShowExpanders = new BCheckBox(B_TRANSLATE("Show application expander"), 82 new BMessage(kSuperExpando)); 83 fAppsExpandNew = new BCheckBox(B_TRANSLATE("Expand new applications"), 84 new BMessage(kExpandNewTeams)); 85 fAppsHideLabels = new BCheckBox(B_TRANSLATE("Hide application names"), 86 new BMessage(kHideLabels)); 87 fAppsIconSizeSlider = new BSlider("icon_size", B_TRANSLATE("Icon size"), 88 new BMessage(kResizeTeamIcons), kMinimumIconSize / kIconSizeInterval, 89 kMaximumIconSize / kIconSizeInterval, B_HORIZONTAL); 90 fAppsIconSizeSlider->SetHashMarks(B_HASH_MARKS_BOTTOM); 91 fAppsIconSizeSlider->SetHashMarkCount((kMaximumIconSize - kMinimumIconSize) 92 / kIconSizeInterval + 1); 93 fAppsIconSizeSlider->SetLimitLabels(B_TRANSLATE("Small"), 94 B_TRANSLATE("Large")); 95 fAppsIconSizeSlider->SetModificationMessage(new BMessage(kResizeTeamIcons)); 96 97 // Window controls 98 fWindowAlwaysOnTop = new BCheckBox(B_TRANSLATE("Always on top"), 99 new BMessage(kAlwaysTop)); 100 fWindowAutoRaise = new BCheckBox(B_TRANSLATE("Auto-raise"), 101 new BMessage(kAutoRaise)); 102 fWindowAutoHide = new BCheckBox(B_TRANSLATE("Auto-hide"), 103 new BMessage(kAutoHide)); 104 105 // Menu settings 106 BTextView* docTextView = fMenuRecentDocumentCount->TextView(); 107 BTextView* appTextView = fMenuRecentApplicationCount->TextView(); 108 BTextView* folderTextView = fMenuRecentFolderCount->TextView(); 109 110 for (int32 i = 0; i < 256; i++) { 111 if (!isdigit(i)) { 112 docTextView->DisallowChar(i); 113 appTextView->DisallowChar(i); 114 folderTextView->DisallowChar(i); 115 } 116 } 117 118 docTextView->SetMaxBytes(4); 119 appTextView->SetMaxBytes(4); 120 folderTextView->SetMaxBytes(4); 121 122 int32 docCount = fSettings.recentDocsCount; 123 int32 appCount = fSettings.recentAppsCount; 124 int32 folderCount = fSettings.recentFoldersCount; 125 126 fMenuRecentDocuments->SetValue(fSettings.recentDocsEnabled); 127 fMenuRecentDocumentCount->SetEnabled(fSettings.recentDocsEnabled); 128 129 fMenuRecentApplications->SetValue(fSettings.recentAppsEnabled); 130 fMenuRecentApplicationCount->SetEnabled(fSettings.recentAppsEnabled); 131 132 fMenuRecentFolders->SetValue(fSettings.recentFoldersEnabled); 133 fMenuRecentFolderCount->SetEnabled(fSettings.recentFoldersEnabled); 134 135 BString docString; 136 BString appString; 137 BString folderString; 138 139 docString << docCount; 140 appString << appCount; 141 folderString << folderCount; 142 143 fMenuRecentDocumentCount->SetText(docString.String()); 144 fMenuRecentApplicationCount->SetText(appString.String()); 145 fMenuRecentFolderCount->SetText(folderString.String()); 146 147 // Applications settings 148 fAppsSort->SetValue(fSettings.sortRunningApps); 149 fAppsSortTrackerFirst->SetValue(fSettings.trackerAlwaysFirst); 150 fAppsShowExpanders->SetValue(fSettings.superExpando); 151 fAppsExpandNew->SetValue(fSettings.expandNewTeams); 152 fAppsHideLabels->SetValue(fSettings.hideLabels); 153 fAppsIconSizeSlider->SetValue(fSettings.iconSize 154 / kIconSizeInterval); 155 156 // Window settings 157 fWindowAlwaysOnTop->SetValue(fSettings.alwaysOnTop); 158 fWindowAutoRaise->SetValue(fSettings.autoRaise); 159 fWindowAutoHide->SetValue(fSettings.autoHide); 160 161 _EnableDisableDependentItems(); 162 163 // Targets 164 fAppsSort->SetTarget(be_app); 165 fAppsSortTrackerFirst->SetTarget(be_app); 166 fAppsShowExpanders->SetTarget(be_app); 167 fAppsExpandNew->SetTarget(be_app); 168 fAppsHideLabels->SetTarget(be_app); 169 fAppsIconSizeSlider->SetTarget(be_app); 170 171 fWindowAlwaysOnTop->SetTarget(be_app); 172 fWindowAutoRaise->SetTarget(be_app); 173 fWindowAutoHide->SetTarget(be_app); 174 175 // Applications 176 BBox* appsSettingsBox = new BBox("applications"); 177 appsSettingsBox->SetLabel(B_TRANSLATE("Applications")); 178 appsSettingsBox->AddChild(BLayoutBuilder::Group<>() 179 .AddGroup(B_VERTICAL, 0) 180 .Add(fAppsSort) 181 .Add(fAppsSortTrackerFirst) 182 .Add(fAppsShowExpanders) 183 .AddGroup(B_HORIZONTAL, 0) 184 .Add(BSpaceLayoutItem::CreateHorizontalStrut(kIndentSpacing)) 185 .Add(fAppsExpandNew) 186 .End() 187 .Add(fAppsHideLabels) 188 .AddGlue() 189 .Add(BSpaceLayoutItem::CreateVerticalStrut(B_USE_SMALL_SPACING)) 190 .Add(fAppsIconSizeSlider) 191 .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 192 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) 193 .End() 194 .View()); 195 196 // Menu 197 BBox* menuBox = new BBox("menu"); 198 menuBox->SetLabel(B_TRANSLATE("Menu")); 199 menuBox->AddChild(BLayoutBuilder::Group<>() 200 .AddGroup(B_VERTICAL, 0) 201 .AddGroup(B_HORIZONTAL, 0) 202 .AddGroup(B_VERTICAL, 0) 203 .Add(fMenuRecentDocuments) 204 .Add(fMenuRecentFolders) 205 .Add(fMenuRecentApplications) 206 .End() 207 .AddGroup(B_VERTICAL, 0) 208 .Add(fMenuRecentDocumentCount) 209 .Add(fMenuRecentFolderCount) 210 .Add(fMenuRecentApplicationCount) 211 .End() 212 .End() 213 .Add(BSpaceLayoutItem::CreateVerticalStrut( 214 B_USE_SMALL_SPACING)) 215 .Add(new BButton(B_TRANSLATE("Edit in Tracker" 216 B_UTF8_ELLIPSIS), new BMessage(kEditInTracker))) 217 .AddGlue() 218 .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 219 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) 220 .End() 221 .View()); 222 223 // Window 224 BBox* windowSettingsBox = new BBox("window"); 225 windowSettingsBox->SetLabel(B_TRANSLATE("Window")); 226 windowSettingsBox->AddChild(BLayoutBuilder::Group<>() 227 .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)) 228 .AddGroup(B_VERTICAL, 0) 229 .Add(fWindowAlwaysOnTop) 230 .Add(fWindowAutoRaise) 231 .Add(fWindowAutoHide) 232 .AddGlue() 233 .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 234 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) 235 .End() 236 .View()); 237 238 // Action Buttons 239 fDefaultsButton = new BButton(B_TRANSLATE("Defaults"), 240 new BMessage(kDefaults)); 241 fRevertButton = new BButton(B_TRANSLATE("Revert"), 242 new BMessage(kRevert)); 243 244 // Layout 245 BLayoutBuilder::Group<>(this) 246 .AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING) 247 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) 248 .Add(appsSettingsBox) 249 .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING) 250 .Add(menuBox) 251 .Add(windowSettingsBox) 252 .End() 253 .End() 254 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) 255 .Add(fDefaultsButton) 256 .Add(fRevertButton) 257 .AddGlue() 258 .End() 259 .SetInsets(B_USE_DEFAULT_SPACING) 260 .End(); 261 262 BMessage windowSettings; 263 BPoint where; 264 if (_LoadSettings(&windowSettings) == B_OK 265 && windowSettings.FindPoint("window_position", &where) == B_OK 266 && BScreen(this).Frame().Contains(where)) { 267 MoveTo(where); 268 } else 269 CenterOnScreen(); 270 } 271 272 273 PreferencesWindow::~PreferencesWindow() 274 { 275 _UpdateRecentCounts(); 276 } 277 278 279 void 280 PreferencesWindow::MessageReceived(BMessage* message) 281 { 282 switch (message->what) { 283 case kEditInTracker: 284 OpenWithTracker(B_USER_DESKBAR_DIRECTORY); 285 break; 286 287 case kUpdatePreferences: 288 _EnableDisableDependentItems(); 289 _UpdateButtons(); 290 break; 291 292 case kUpdateRecentCounts: 293 _UpdateRecentCounts(); 294 _UpdateButtons(); 295 break; 296 297 case kStateChanged: 298 _EnableDisableDependentItems(); 299 break; 300 301 case kRevert: 302 _UpdatePreferences(&fSettings); 303 break; 304 305 case kDefaults: 306 _UpdatePreferences( 307 static_cast<TBarApp*>(be_app)->DefaultSettings()); 308 break; 309 310 default: 311 BWindow::MessageReceived(message); 312 break; 313 } 314 } 315 316 317 bool 318 PreferencesWindow::QuitRequested() 319 { 320 BMessage windowSettings; 321 windowSettings.AddPoint("window_position", Frame().LeftTop()); 322 _SaveSettings(&windowSettings); 323 324 be_app->PostMessage(kConfigQuit); 325 326 return false; 327 } 328 329 330 void 331 PreferencesWindow::Show() 332 { 333 if (IsHidden()) 334 SetWorkspaces(B_CURRENT_WORKSPACE); 335 336 _UpdateButtons(); 337 338 BWindow::Show(); 339 } 340 341 342 // #pragma mark - private methods 343 344 345 void 346 PreferencesWindow::_EnableDisableDependentItems() 347 { 348 TBarApp* barApp = static_cast<TBarApp*>(be_app); 349 if (barApp->BarView()->Vertical() 350 && barApp->BarView()->ExpandoState()) { 351 fAppsShowExpanders->SetEnabled(true); 352 fAppsExpandNew->SetEnabled(fAppsShowExpanders->Value()); 353 } else { 354 fAppsShowExpanders->SetEnabled(false); 355 fAppsExpandNew->SetEnabled(false); 356 } 357 358 fMenuRecentDocumentCount->SetEnabled( 359 fMenuRecentDocuments->Value() != B_CONTROL_OFF); 360 fMenuRecentFolderCount->SetEnabled( 361 fMenuRecentFolders->Value() != B_CONTROL_OFF); 362 fMenuRecentApplicationCount->SetEnabled( 363 fMenuRecentApplications->Value() != B_CONTROL_OFF); 364 365 fWindowAutoRaise->SetEnabled( 366 fWindowAlwaysOnTop->Value() == B_CONTROL_OFF); 367 } 368 369 370 bool 371 PreferencesWindow::_IsDefaultable() 372 { 373 desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings(); 374 desk_settings* defaults = static_cast<TBarApp*>(be_app)->DefaultSettings(); 375 376 return defaults->sortRunningApps != settings->sortRunningApps 377 || defaults->trackerAlwaysFirst != settings->trackerAlwaysFirst 378 || defaults->superExpando != settings->superExpando 379 || defaults->expandNewTeams != settings->expandNewTeams 380 || defaults->hideLabels != settings->hideLabels 381 || defaults->iconSize != settings->iconSize 382 || defaults->recentAppsEnabled != settings->recentAppsEnabled 383 || defaults->recentDocsEnabled != settings->recentDocsEnabled 384 || defaults->recentFoldersEnabled 385 != settings->recentFoldersEnabled 386 || defaults->recentAppsCount != settings->recentAppsCount 387 || defaults->recentDocsCount != settings->recentDocsCount 388 || defaults->recentFoldersCount != settings->recentFoldersCount 389 || defaults->alwaysOnTop != settings->alwaysOnTop 390 || defaults->autoRaise != settings->autoRaise 391 || defaults->autoHide != settings->autoHide; 392 } 393 394 395 bool 396 PreferencesWindow::_IsRevertable() 397 { 398 desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings(); 399 400 return fSettings.sortRunningApps != settings->sortRunningApps 401 || fSettings.trackerAlwaysFirst != settings->trackerAlwaysFirst 402 || fSettings.superExpando != settings->superExpando 403 || fSettings.expandNewTeams != settings->expandNewTeams 404 || fSettings.hideLabels != settings->hideLabels 405 || fSettings.iconSize != settings->iconSize 406 || fSettings.recentAppsEnabled != settings->recentAppsEnabled 407 || fSettings.recentDocsEnabled != settings->recentDocsEnabled 408 || fSettings.recentFoldersEnabled 409 != settings->recentFoldersEnabled 410 || fSettings.recentAppsCount != settings->recentAppsCount 411 || fSettings.recentDocsCount != settings->recentDocsCount 412 || fSettings.recentFoldersCount != settings->recentFoldersCount 413 || fSettings.alwaysOnTop != settings->alwaysOnTop 414 || fSettings.autoRaise != settings->autoRaise 415 || fSettings.autoHide != settings->autoHide; 416 } 417 418 419 status_t 420 PreferencesWindow::_InitSettingsFile(BFile* file, bool write) 421 { 422 BPath prefsPath; 423 status_t status = GetDeskbarSettingsDirectory(prefsPath); 424 if (status != B_OK) 425 return status; 426 427 status = prefsPath.Append(kSettingsFileName); 428 if (status != B_OK) 429 return status; 430 431 if (write) { 432 status = file->SetTo(prefsPath.Path(), 433 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 434 } else 435 status = file->SetTo(prefsPath.Path(), B_READ_ONLY); 436 437 return status; 438 } 439 440 441 status_t 442 PreferencesWindow::_LoadSettings(BMessage* settings) 443 { 444 BFile prefsFile; 445 status_t status = _InitSettingsFile(&prefsFile, false); 446 if (status != B_OK) 447 return status; 448 449 return settings->Unflatten(&prefsFile); 450 } 451 452 453 status_t 454 PreferencesWindow::_SaveSettings(BMessage* settings) 455 { 456 BFile prefsFile; 457 status_t status = _InitSettingsFile(&prefsFile, true); 458 if (status != B_OK) 459 return status; 460 461 return settings->Flatten(&prefsFile); 462 } 463 464 465 void 466 PreferencesWindow::_UpdateButtons() 467 { 468 fDefaultsButton->SetEnabled(_IsDefaultable()); 469 fRevertButton->SetEnabled(_IsRevertable()); 470 } 471 472 473 void 474 PreferencesWindow::_UpdatePreferences(desk_settings* settings) 475 { 476 desk_settings* current = static_cast<TBarApp*>(be_app)->Settings(); 477 bool updateRecentCounts = false; 478 479 if (current->sortRunningApps != settings->sortRunningApps) { 480 fAppsSort->SetValue(settings->sortRunningApps); 481 fAppsSort->Invoke(); 482 } 483 if (current->trackerAlwaysFirst != settings->trackerAlwaysFirst) { 484 fAppsSortTrackerFirst->SetValue(settings->trackerAlwaysFirst); 485 fAppsSortTrackerFirst->Invoke(); 486 } 487 if (current->superExpando != settings->superExpando) { 488 fAppsShowExpanders->SetValue(settings->superExpando); 489 fAppsShowExpanders->Invoke(); 490 } 491 if (current->expandNewTeams != settings->expandNewTeams) { 492 fAppsExpandNew->SetValue(settings->expandNewTeams); 493 fAppsExpandNew->Invoke(); 494 } 495 if (current->hideLabels != settings->hideLabels) { 496 fAppsHideLabels->SetValue(settings->hideLabels); 497 fAppsHideLabels->Invoke(); 498 } 499 if (current->iconSize != settings->iconSize) { 500 fAppsIconSizeSlider->SetValue(settings->iconSize / kIconSizeInterval); 501 fAppsIconSizeSlider->Invoke(); 502 } 503 if (current->recentDocsEnabled != settings->recentDocsEnabled) { 504 fMenuRecentDocuments->SetValue(settings->recentDocsEnabled 505 ? B_CONTROL_ON : B_CONTROL_OFF); 506 updateRecentCounts = true; 507 } 508 if (current->recentFoldersEnabled != settings->recentFoldersEnabled) { 509 fMenuRecentFolders->SetValue(settings->recentFoldersEnabled 510 ? B_CONTROL_ON : B_CONTROL_OFF); 511 updateRecentCounts = true; 512 } 513 if (current->recentAppsEnabled != settings->recentAppsEnabled) { 514 fMenuRecentApplications->SetValue(settings->recentAppsEnabled 515 ? B_CONTROL_ON : B_CONTROL_OFF); 516 updateRecentCounts = true; 517 } 518 if (current->recentDocsCount != settings->recentDocsCount) { 519 BString docString; 520 docString << settings->recentDocsCount; 521 fMenuRecentDocumentCount->SetText(docString.String()); 522 updateRecentCounts = true; 523 } 524 if (current->recentFoldersCount != settings->recentFoldersCount) { 525 BString folderString; 526 folderString << settings->recentFoldersCount; 527 fMenuRecentFolderCount->SetText(folderString.String()); 528 updateRecentCounts = true; 529 } 530 if (current->recentAppsCount != settings->recentAppsCount) { 531 BString appString; 532 appString << settings->recentAppsCount; 533 fMenuRecentApplicationCount->SetText(appString.String()); 534 updateRecentCounts = true; 535 } 536 if (current->alwaysOnTop != settings->alwaysOnTop) { 537 fWindowAlwaysOnTop->SetValue(settings->alwaysOnTop); 538 fWindowAlwaysOnTop->Invoke(); 539 } 540 if (current->autoRaise != settings->autoRaise) { 541 fWindowAutoRaise->SetValue(settings->autoRaise); 542 fWindowAutoRaise->Invoke(); 543 } 544 if (current->autoHide != settings->autoHide) { 545 fWindowAutoHide->SetValue(settings->autoHide); 546 fWindowAutoHide->Invoke(); 547 } 548 549 if (updateRecentCounts) 550 _UpdateRecentCounts(); 551 } 552 553 554 void 555 PreferencesWindow::_UpdateRecentCounts() 556 { 557 BMessage message(kUpdateRecentCounts); 558 559 int32 docCount = atoi(fMenuRecentDocumentCount->Text()); 560 int32 appCount = atoi(fMenuRecentApplicationCount->Text()); 561 int32 folderCount = atoi(fMenuRecentFolderCount->Text()); 562 563 message.AddInt32("documents", max_c(0, docCount)); 564 message.AddInt32("applications", max_c(0, appCount)); 565 message.AddInt32("folders", max_c(0, folderCount)); 566 567 message.AddBool("documentsEnabled", fMenuRecentDocuments->Value()); 568 message.AddBool("applicationsEnabled", fMenuRecentApplications->Value()); 569 message.AddBool("foldersEnabled", fMenuRecentFolders->Value()); 570 571 be_app->PostMessage(&message); 572 573 _EnableDisableDependentItems(); 574 } 575