1 /* 2 * Copyright 2010, Haiku, Inc. All Rights Reserved. 3 * Copyright 2009, Pier Luigi Fiorini. 4 * Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Pier Luigi Fiorini, pierluigi.fiorini@gmail.com 8 */ 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 13 #include <vector> 14 15 #include <Roster.h> 16 #include <GroupLayout.h> 17 #include <GroupLayoutBuilder.h> 18 #include <Alert.h> 19 #include <Font.h> 20 #include <Button.h> 21 #include <Catalog.h> 22 #include <StringView.h> 23 #include <TextControl.h> 24 #include <CheckBox.h> 25 #include <String.h> 26 #include <FindDirectory.h> 27 #include <Node.h> 28 #include <notification/Notifications.h> 29 #include <Path.h> 30 #include <File.h> 31 #include <Directory.h> 32 #include <VolumeRoster.h> 33 #include <Volume.h> 34 #include <Query.h> 35 #include <SymLink.h> 36 37 #include "GeneralView.h" 38 #include "SettingsHost.h" 39 40 #undef B_TRANSLATION_CONTEXT 41 #define B_TRANSLATION_CONTEXT "GeneralView" 42 const int32 kServer = '_TSR'; 43 44 const char* kStartServer = B_TRANSLATE("Enable notifications"); 45 const char* kStopServer = B_TRANSLATE("Disable notifications"); 46 const char* kStarted = B_TRANSLATE("Events are notified"); 47 const char* kStopped = B_TRANSLATE("Events are not notified"); 48 49 50 GeneralView::GeneralView(SettingsHost* host) 51 : 52 SettingsPane("general", host) 53 { 54 BFont statusFont; 55 56 // Set a smaller font for the status label 57 statusFont.SetSize(be_plain_font->Size() * 0.8); 58 59 // Status button and label 60 fServerButton = new BButton("server", kStartServer, new BMessage(kServer)); 61 fStatusLabel = new BStringView("status", kStopped); 62 fStatusLabel->SetFont(&statusFont); 63 64 // Update status label and server button 65 if (_IsServerRunning()) { 66 fServerButton->SetLabel(kStopServer); 67 fStatusLabel->SetText(kStarted); 68 } else { 69 fServerButton->SetLabel(kStartServer); 70 fStatusLabel->SetText(kStopped); 71 } 72 73 // Autostart 74 fAutoStart = new BCheckBox("autostart", 75 B_TRANSLATE("Enable notifications at startup"), 76 new BMessage(kSettingChanged)); 77 78 // Display time 79 fTimeout = new BTextControl(B_TRANSLATE("Hide notifications from screen" 80 " after"), NULL, new BMessage(kSettingChanged)); 81 BStringView* displayTimeLabel = new BStringView("dt_label", 82 B_TRANSLATE("seconds of inactivity")); 83 84 // Default position 85 // TODO: Here will come a screen representation with the four corners clickable 86 87 // Load settings 88 Load(); 89 90 // Calculate inset 91 float inset = ceilf(be_plain_font->Size() * 0.7f); 92 93 SetLayout(new BGroupLayout(B_VERTICAL)); 94 AddChild(BGroupLayoutBuilder(B_VERTICAL, inset) 95 .AddGroup(B_HORIZONTAL, inset) 96 .Add(fServerButton) 97 .Add(fStatusLabel) 98 .AddGlue() 99 .End() 100 101 .AddGroup(B_VERTICAL, inset) 102 .Add(fAutoStart) 103 .AddGroup(B_HORIZONTAL) 104 .AddGroup(B_HORIZONTAL, 2) 105 .Add(fTimeout) 106 .Add(displayTimeLabel) 107 .End() 108 .End() 109 .End() 110 .SetInsets(inset, inset, inset, inset) 111 .AddGlue() 112 ); 113 } 114 115 116 void 117 GeneralView::AttachedToWindow() 118 { 119 fServerButton->SetTarget(this); 120 fAutoStart->SetTarget(this); 121 fTimeout->SetTarget(this); 122 } 123 124 125 void 126 GeneralView::MessageReceived(BMessage* msg) 127 { 128 switch (msg->what) { 129 case kServer: { 130 entry_ref ref; 131 132 // Check if server is available 133 if (!_CanFindServer(&ref)) { 134 BAlert* alert = new BAlert(B_TRANSLATE("Notifications"), 135 B_TRANSLATE("The notifications server cannot be" 136 " found, this means your InfoPopper installation was" 137 " not successfully completed."), B_TRANSLATE("OK"), 138 NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 139 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 140 (void)alert->Go(); 141 return; 142 } 143 144 if (_IsServerRunning()) { 145 // Server team 146 team_id team = be_roster->TeamFor(kNotificationServerSignature); 147 148 // Establish a connection to infopopper_server 149 status_t ret = B_ERROR; 150 BMessenger messenger(kNotificationServerSignature, team, &ret); 151 if (ret != B_OK) { 152 BAlert* alert = new BAlert(B_TRANSLATE( 153 "Notifications"), B_TRANSLATE("Notifications " 154 "cannot be stopped, because the server can't be" 155 " reached."), B_TRANSLATE("OK"), NULL, NULL, 156 B_WIDTH_AS_USUAL, B_STOP_ALERT); 157 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 158 (void)alert->Go(); 159 return; 160 } 161 162 // Send quit message 163 if (messenger.SendMessage(new BMessage(B_QUIT_REQUESTED)) != B_OK) { 164 BAlert* alert = new BAlert(B_TRANSLATE( 165 "Notifications"), B_TRANSLATE("Cannot disable" 166 " notifications because the server can't be " 167 "reached."), B_TRANSLATE("OK"), 168 NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 169 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 170 (void)alert->Go(); 171 return; 172 } 173 174 fServerButton->SetLabel(kStartServer); 175 fStatusLabel->SetText(kStopped); 176 } else { 177 // Start server 178 status_t err = be_roster->Launch(kNotificationServerSignature); 179 if (err != B_OK) { 180 BAlert* alert = new BAlert(B_TRANSLATE( 181 "Notifications"), B_TRANSLATE("Cannot enable" 182 " notifications because the server cannot be " 183 "found.\nThis means your InfoPopper installation" 184 " was not successfully completed."), 185 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 186 B_STOP_ALERT); 187 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 188 (void)alert->Go(); 189 return; 190 } 191 192 fServerButton->SetLabel(kStopServer); 193 fStatusLabel->SetText(kStarted); 194 } 195 } break; 196 case kSettingChanged: 197 SettingsPane::MessageReceived(msg); 198 break; 199 default: 200 BView::MessageReceived(msg); 201 break; 202 } 203 } 204 205 206 status_t 207 GeneralView::Load() 208 { 209 BPath path; 210 211 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 212 return B_ERROR; 213 214 path.Append(kSettingsDirectory); 215 216 if (create_directory(path.Path(), 0755) != B_OK) { 217 BAlert* alert = new BAlert("", 218 B_TRANSLATE("There was a problem saving the preferences.\n" 219 "It's possible you don't have write access to the " 220 "settings directory."), B_TRANSLATE("OK"), NULL, NULL, 221 B_WIDTH_AS_USUAL, B_STOP_ALERT); 222 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 223 (void)alert->Go(); 224 } 225 226 path.Append(kGeneralSettings); 227 228 BMessage settings; 229 BFile file(path.Path(), B_READ_ONLY); 230 settings.Unflatten(&file); 231 232 char buffer[255]; 233 234 bool autoStart; 235 if (settings.FindBool(kAutoStartName, &autoStart) != B_OK) 236 autoStart = kDefaultAutoStart; 237 fAutoStart->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF); 238 239 int32 timeout; 240 if (settings.FindInt32(kTimeoutName, &timeout) != B_OK) 241 timeout = kDefaultTimeout; 242 (void)sprintf(buffer, "%" B_PRId32, timeout); 243 fTimeout->SetText(buffer); 244 245 return B_OK; 246 } 247 248 249 status_t 250 GeneralView::Save() 251 { 252 BPath path; 253 254 status_t ret = B_OK; 255 ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 256 if (ret != B_OK) 257 return ret; 258 259 path.Append(kSettingsDirectory); 260 path.Append(kGeneralSettings); 261 262 BMessage settings; 263 264 bool autoStart = (fAutoStart->Value() == B_CONTROL_ON); 265 settings.AddBool(kAutoStartName, autoStart); 266 267 int32 timeout = atol(fTimeout->Text()); 268 settings.AddInt32(kTimeoutName, timeout); 269 270 // Save settings file 271 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 272 ret = settings.Flatten(&file); 273 if (ret != B_OK) { 274 BAlert* alert = new BAlert("", 275 B_TRANSLATE("An error occurred saving the preferences.\n" 276 "It's possible you are running out of disk space."), 277 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 278 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 279 (void)alert->Go(); 280 return ret; 281 } 282 283 // Find server path 284 entry_ref ref; 285 if (!_CanFindServer(&ref)) { 286 BAlert* alert = new BAlert("", 287 B_TRANSLATE("The notifications server cannot be found.\n" 288 "A possible cause is an installation not done correctly"), 289 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 290 (void)alert->Go(); 291 return B_ERROR; 292 } 293 294 // UserBootscript command 295 BPath serverPath(&ref); 296 297 // Start server at boot time 298 ret = find_directory(B_USER_BOOT_DIRECTORY, &path, true); 299 if (ret != B_OK) { 300 BAlert* alert = new BAlert("", 301 B_TRANSLATE("Can't save preferences, you probably don't have " 302 "write access to the boot settings directory."), B_TRANSLATE("OK"), 303 NULL, NULL, 304 B_WIDTH_AS_USUAL, B_STOP_ALERT); 305 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 306 (void)alert->Go(); 307 return ret; 308 } 309 310 path.Append("launch"); 311 BDirectory directory(path.Path()); 312 BEntry entry(&directory, serverPath.Leaf()); 313 314 // Remove symbolic link 315 entry.Remove(); 316 317 if (autoStart) { 318 // Put a symlink into ~/config/boot/launch 319 if ((ret = directory.CreateSymLink(serverPath.Leaf(), 320 serverPath.Path(), NULL) != B_OK)) { 321 BAlert* alert = new BAlert("", 322 B_TRANSLATE("Can't enable notifications at startup time, " 323 "you probably don't have write permission to the boot settings" 324 " directory."), B_TRANSLATE("OK"), NULL, NULL, 325 B_WIDTH_AS_USUAL, B_STOP_ALERT); 326 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 327 (void)alert->Go(); 328 return ret; 329 } 330 } 331 332 return B_OK; 333 } 334 335 336 status_t 337 GeneralView::Revert() 338 { 339 return B_ERROR; 340 } 341 342 343 bool 344 GeneralView::_CanFindServer(entry_ref* ref) 345 { 346 // Try searching with be_roster 347 if (be_roster->FindApp(kNotificationServerSignature, ref) == B_OK) 348 return true; 349 350 // Try with a query and take the first result 351 BVolumeRoster vroster; 352 BVolume volume; 353 char volName[B_FILE_NAME_LENGTH]; 354 355 vroster.Rewind(); 356 357 while (vroster.GetNextVolume(&volume) == B_OK) { 358 if ((volume.InitCheck() != B_OK) || !volume.KnowsQuery()) 359 continue; 360 361 volume.GetName(volName); 362 363 BQuery *query = new BQuery(); 364 query->SetPredicate("(BEOS:APP_SIG==\""kNotificationServerSignature"\")"); 365 query->SetVolume(&volume); 366 query->Fetch(); 367 368 if (query->GetNextRef(ref) == B_OK) 369 return true; 370 } 371 372 return false; 373 } 374 375 376 bool 377 GeneralView::_IsServerRunning() 378 { 379 return be_roster->IsRunning(kNotificationServerSignature); 380 } 381