1/* 2 * Copyright 2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Clemens Zeidler, haiku@clemens-zeidler.de 7 */ 8#ifndef PREFERENCES_WINDOW_h 9#define PREFERENCES_WINDOW_h 10 11 12#include <Application.h> 13#include <Button.h> 14#include <Catalog.h> 15#include <Debug.h> 16#include <Entry.h> 17#include <FindDirectory.h> 18#include <File.h> 19#include <GroupView.h> 20#include <Locale.h> 21#include <MessageFilter.h> 22#include <NodeMonitor.h> 23#include <Path.h> 24#include <Screen.h> 25#include <SpaceLayoutItem.h> 26#include <String.h> 27#include <Window.h> 28 29 30#if DEBUG 31# define LOG(text...) PRINT((text)) 32#else 33# define LOG(text...) 34#endif 35 36 37#undef B_TRANSLATION_CONTEXT 38#define B_TRANSLATION_CONTEXT "Pref Window" 39 40// messages PrefFileWatcher 41const uint32 kUpdatedPreferences = '&UdP'; 42 43 44template<typename Preferences> 45class PreferencesStorage { 46public: 47 PreferencesStorage(const char* file, 48 const Preferences& defaultPreferences); 49 ~PreferencesStorage(); 50 51 void Revert(); 52 void Defaults(); 53 54 BPoint WindowPosition() {return fWindowPosition; } 55 void SetWindowPosition(BPoint position) 56 { fWindowPosition = position; } 57 58 Preferences* GetPreferences() { return &fPreferences; } 59 60 status_t LoadPreferences(); 61 status_t SavePreferences(); 62 63 BString& PreferencesFile() { return fPreferencesFile; } 64 status_t GetPreferencesPath(BPath &path); 65 66 bool DefaultsSet(); 67 bool StartPrefsSet(); 68 69private: 70 BString fPreferencesFile; 71 72 Preferences fPreferences; 73 Preferences fStartPreferences; 74 const Preferences& fDefaultPreferences; 75 BPoint fWindowPosition; 76}; 77 78 79template<typename Preferences> 80class PrefFileWatcher : public BMessageFilter { 81public: 82 PrefFileWatcher( 83 PreferencesStorage<Preferences>* storage, 84 BHandler* target); 85 virtual ~PrefFileWatcher(); 86 87 virtual filter_result Filter(BMessage* message, BHandler** _target); 88 89private: 90 PreferencesStorage<Preferences>* fPreferencesStorage; 91 node_ref fPreferencesNode; 92 node_ref fPreferencesDirectoryNode; 93 94 BHandler* fTarget; 95}; 96 97 98template<typename Preferences> 99class PreferencesWindow : public BWindow, 100 public PreferencesStorage<Preferences> { 101public: 102 PreferencesWindow(const char* title, 103 const char* file, 104 const Preferences& defaultPreferences); 105 virtual ~PreferencesWindow(); 106 virtual void MessageReceived(BMessage *msg); 107 virtual bool QuitRequested(); 108 109 virtual bool SetPreferencesView(BView* prefView); 110 111private: 112 void _MoveToPosition(); 113 void _UpdateButtons(); 114 115 BView* fPreferencesView; 116 BButton* fRevertButton; 117 BButton* fDefaultButton; 118 BGroupLayout* fRootLayout; 119}; 120 121 122const uint32 kDefaultMsg = 'dems'; 123const uint32 kRevertMsg = 'rems'; 124const uint32 kConfigChangedMsg = '&cgh'; 125 126 127template<typename Preferences> 128PreferencesStorage<Preferences>::PreferencesStorage(const char* file, 129 const Preferences& defaultPreferences) 130 : 131 fDefaultPreferences(defaultPreferences) 132{ 133 // default center position 134 fWindowPosition.x = -1; 135 fWindowPosition.y = -1; 136 137 fPreferencesFile = file; 138 if (LoadPreferences() != B_OK) 139 Defaults(); 140 fStartPreferences = fPreferences; 141} 142 143 144template<typename Preferences> 145PreferencesStorage<Preferences>::~PreferencesStorage() 146{ 147 SavePreferences(); 148} 149 150 151template<typename Preferences> 152void 153PreferencesStorage<Preferences>::Revert() 154{ 155 fPreferences = fStartPreferences; 156} 157 158 159template<typename Preferences> 160void 161PreferencesStorage<Preferences>::Defaults() 162{ 163 fPreferences = fDefaultPreferences; 164} 165 166 167template<typename Preferences> 168status_t 169PreferencesStorage<Preferences>::GetPreferencesPath(BPath &path) 170{ 171 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 172 if (status < B_OK) 173 return status; 174 175 return path.Append(fPreferencesFile.String()); 176} 177 178 179template<typename Preferences> 180status_t 181PreferencesStorage<Preferences>::LoadPreferences() 182{ 183 BPath path; 184 status_t status = GetPreferencesPath(path); 185 if (status != B_OK) 186 return status; 187 188 BFile settingsFile(path.Path(), B_READ_ONLY); 189 status = settingsFile.InitCheck(); 190 if (status != B_OK) 191 return status; 192 193 if (settingsFile.Read(&fWindowPosition, sizeof(BPoint)) 194 != sizeof(BPoint)) { 195 LOG("failed to load settings\n"); 196 return B_ERROR; 197 } 198 199 if (settingsFile.Read(&fPreferences, sizeof(Preferences)) 200 != sizeof(Preferences)) { 201 LOG("failed to load settings\n"); 202 return B_ERROR; 203 } 204 205 return B_OK; 206} 207 208 209template<typename Preferences> 210status_t 211PreferencesStorage<Preferences>::SavePreferences() 212{ 213 BPath path; 214 status_t status = GetPreferencesPath(path); 215 if (status != B_OK) 216 return status; 217 218 BFile settingsFile(path.Path(), B_READ_WRITE | B_CREATE_FILE); 219 status = settingsFile.InitCheck(); 220 if (status != B_OK) { 221 LOG("InitCheck() settings file failed \n"); 222 return status; 223 } 224 225 if (settingsFile.Write(&fWindowPosition, sizeof(BPoint)) 226 != sizeof(BPoint)) { 227 LOG("can't save window position\n"); 228 return B_ERROR; 229 } 230 231 if (settingsFile.Write(&fPreferences, sizeof(Preferences)) 232 != sizeof(Preferences)) { 233 LOG("can't save settings\n"); 234 return B_ERROR; 235 } 236 237 return B_OK; 238} 239 240 241template<typename Preferences> 242bool 243PreferencesStorage<Preferences>::DefaultsSet() 244{ 245 return fPreferences.IsEqual(fDefaultPreferences); 246} 247 248 249template<typename Preferences> 250bool 251PreferencesStorage<Preferences>::StartPrefsSet() 252{ 253 return fPreferences.IsEqual(fStartPreferences); 254} 255 256 257template<typename Preferences> 258PrefFileWatcher<Preferences>::PrefFileWatcher( 259 PreferencesStorage<Preferences>* storage, BHandler* target) 260 : 261 BMessageFilter(B_PROGRAMMED_DELIVERY, B_ANY_SOURCE), 262 fPreferencesStorage(storage), 263 fTarget(target) 264{ 265 BPath path; 266 find_directory(B_USER_SETTINGS_DIRECTORY, &path); 267 268 BEntry entry(path.Path()); 269 if (entry.GetNodeRef(&fPreferencesDirectoryNode) == B_OK) 270 watch_node(&fPreferencesDirectoryNode, B_WATCH_DIRECTORY, fTarget); 271 272 path.Append(fPreferencesStorage->PreferencesFile().String()); 273 entry.SetTo(path.Path()); 274 275 if (entry.GetNodeRef(&fPreferencesNode) == B_OK) 276 watch_node(&fPreferencesNode, B_WATCH_STAT, fTarget); 277} 278 279 280template<typename Preferences> 281PrefFileWatcher<Preferences>::~PrefFileWatcher() 282{ 283 stop_watching(fTarget); 284} 285 286 287template<typename Preferences> 288filter_result 289PrefFileWatcher<Preferences>::Filter(BMessage *msg, BHandler **target) 290{ 291 const char *name; 292 ino_t dir = -1; 293 filter_result result = B_DISPATCH_MESSAGE; 294 int32 opcode; 295 BPath path; 296 node_ref nref; 297 298 if (msg->what != B_NODE_MONITOR 299 || msg->FindInt32("opcode", &opcode) != B_OK) 300 return result; 301 302 switch (opcode) { 303 case B_ENTRY_MOVED: 304 msg->FindInt64("to directory", dir); 305 if (dir != fPreferencesDirectoryNode.node) 306 break; 307 // supposed to fall through 308 309 case B_ENTRY_CREATED: 310 msg->FindString("name", &name); 311 fPreferencesStorage->GetPreferencesPath(path); 312 if (path.Path() == name) { 313 msg->FindInt32("device", &fPreferencesNode.device); 314 msg->FindInt64("node", &fPreferencesNode.node); 315 watch_node(&fPreferencesNode, B_WATCH_STAT, fTarget); 316 } 317 fPreferencesStorage->LoadPreferences(); 318 msg->what = kUpdatedPreferences; 319 break; 320 321 case B_ENTRY_REMOVED: 322 msg->FindInt32("device", &nref.device); 323 msg->FindInt64("node", &nref.node); 324 if (fPreferencesNode == nref) { 325 // stop all watching 326 stop_watching(fTarget); 327 // and start watching the directory again 328 watch_node(&fPreferencesDirectoryNode, B_WATCH_DIRECTORY, 329 fTarget); 330 msg->what = kUpdatedPreferences; 331 } 332 break; 333 334 case B_STAT_CHANGED: 335 msg->FindInt32("device", &nref.device); 336 msg->FindInt64("node", &nref.node); 337 if (fPreferencesNode == nref) { 338 fPreferencesStorage->LoadPreferences(); 339 msg->what = kUpdatedPreferences; 340 } 341 break; 342 } 343 return result; 344} 345 346 347template<typename Preferences> 348PreferencesWindow<Preferences>::PreferencesWindow(const char* title, 349 const char* file, const Preferences& defaultPreferences) 350 : 351 BWindow(BRect(50, 50, 400, 350), title, B_TITLED_WINDOW, 352 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS), 353 PreferencesStorage<Preferences>(file, defaultPreferences), 354 fPreferencesView(NULL) 355{ 356 BGroupView* buttonView = new BGroupView(B_HORIZONTAL); 357 fDefaultButton = new BButton(B_TRANSLATE("Defaults"), 358 new BMessage(kDefaultMsg)); 359 360 buttonView->AddChild(fDefaultButton); 361 buttonView->GetLayout()->AddItem( 362 BSpaceLayoutItem::CreateHorizontalStrut(7)); 363 fRevertButton = new BButton(B_TRANSLATE("Revert"), 364 new BMessage(kRevertMsg)); 365 366 buttonView->AddChild(fRevertButton); 367 buttonView->GetLayout()->AddItem(BSpaceLayoutItem::CreateGlue()); 368 369 _UpdateButtons(); 370 371 SetLayout(new BGroupLayout(B_VERTICAL)); 372 fRootLayout = new BGroupLayout(B_VERTICAL); 373 fRootLayout->SetInsets(10, 10, 10, 10); 374 fRootLayout->SetSpacing(10); 375 BView* rootView = new BView("root view", 0, fRootLayout); 376 AddChild(rootView); 377 rootView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 378 379 fRootLayout->AddView(buttonView); 380 381 BSize size = fRootLayout->PreferredSize(); 382 ResizeTo(size.width, size.height); 383 _MoveToPosition(); 384} 385 386 387template<typename Preferences> 388PreferencesWindow<Preferences>::~PreferencesWindow() 389{ 390 PreferencesStorage<Preferences>::SetWindowPosition(Frame().LeftTop()); 391} 392 393 394template<typename Preferences> 395void 396PreferencesWindow<Preferences>::MessageReceived(BMessage *msg) 397{ 398 switch(msg->what) 399 { 400 case kConfigChangedMsg: 401 _UpdateButtons(); 402 break; 403 404 case kDefaultMsg: 405 PreferencesStorage<Preferences>::Defaults(); 406 _UpdateButtons(); 407 if (fPreferencesView) 408 PostMessage(kDefaultMsg, fPreferencesView); 409 break; 410 411 case kRevertMsg: 412 PreferencesStorage<Preferences>::Revert(); 413 _UpdateButtons(); 414 if (fPreferencesView) 415 PostMessage(kRevertMsg, fPreferencesView); 416 break; 417 418 default: 419 BWindow::MessageReceived(msg); 420 } 421} 422 423 424template<typename Preferences> 425bool 426PreferencesWindow<Preferences>::QuitRequested() 427{ 428 be_app->PostMessage(B_QUIT_REQUESTED); 429 return true; 430} 431 432 433template<typename Preferences> 434bool 435PreferencesWindow<Preferences>::SetPreferencesView(BView* prefView) 436{ 437 if (fPreferencesView) 438 return false; 439 440 fPreferencesView = prefView; 441 fRootLayout->AddView(0, fPreferencesView); 442 443 BSize size = fRootLayout->PreferredSize(); 444 ResizeTo(size.width, size.height); 445 _MoveToPosition(); 446 447 return true; 448} 449 450 451template<typename Preferences> 452void 453PreferencesWindow<Preferences>::_MoveToPosition() 454{ 455 BPoint position = PreferencesStorage<Preferences>::WindowPosition(); 456 // center window on screen if it had a bad position 457 if (position.x < 0 && position.y < 0){ 458 BRect rect = BScreen().Frame(); 459 BRect windowFrame = Frame(); 460 position.x = (rect.Width() - windowFrame.Width()) / 2; 461 position.y = (rect.Height() - windowFrame.Height()) / 2; 462 } 463 MoveTo(position); 464} 465 466 467template<typename Preferences> 468void 469PreferencesWindow<Preferences>::_UpdateButtons() 470{ 471 if (!PreferencesStorage<Preferences>::DefaultsSet()) 472 fDefaultButton->SetEnabled(true); 473 else 474 fDefaultButton->SetEnabled(false); 475 if (!PreferencesStorage<Preferences>::StartPrefsSet()) 476 fRevertButton->SetEnabled(true); 477 else 478 fRevertButton->SetEnabled(false); 479} 480 481 482#undef B_TRANSLATION_CONTEXT 483 484#endif // PREFERENCES_WINDOW_h 485