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_TRANSLATE_CONTEXT 41 #define B_TRANSLATE_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 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 (void)alert->Go(); 140 return; 141 } 142 143 if (_IsServerRunning()) { 144 // Server team 145 team_id team = be_roster->TeamFor(kNotificationServerSignature); 146 147 // Establish a connection to infopopper_server 148 status_t ret = B_ERROR; 149 BMessenger messenger(kNotificationServerSignature, team, &ret); 150 if (ret != B_OK) { 151 BAlert* alert = new BAlert(B_TRANSLATE( 152 "Notifications"), B_TRANSLATE("Notifications " 153 "cannot be stopped, because the server can't be" 154 " reached."), B_TRANSLATE("OK"), NULL, NULL, 155 B_WIDTH_AS_USUAL, B_STOP_ALERT); 156 (void)alert->Go(); 157 return; 158 } 159 160 // Send quit message 161 if (messenger.SendMessage(new BMessage(B_QUIT_REQUESTED)) != B_OK) { 162 BAlert* alert = new BAlert(B_TRANSLATE( 163 "Notifications"), B_TRANSLATE("Cannot disable" 164 " notifications because the server can't be " 165 "reached."), B_TRANSLATE("OK"), 166 NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 167 (void)alert->Go(); 168 return; 169 } 170 171 fServerButton->SetLabel(kStartServer); 172 fStatusLabel->SetText(kStopped); 173 } else { 174 // Start server 175 status_t err = be_roster->Launch(kNotificationServerSignature); 176 if (err != B_OK) { 177 BAlert* alert = new BAlert(B_TRANSLATE( 178 "Notifications"), B_TRANSLATE("Cannot enable" 179 " notifications because the server cannot be " 180 "found.\nThis means your InfoPopper installation" 181 " was not successfully completed."), 182 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 183 B_STOP_ALERT); 184 (void)alert->Go(); 185 return; 186 } 187 188 fServerButton->SetLabel(kStopServer); 189 fStatusLabel->SetText(kStarted); 190 } 191 } break; 192 case kSettingChanged: 193 SettingsPane::MessageReceived(msg); 194 break; 195 default: 196 BView::MessageReceived(msg); 197 break; 198 } 199 } 200 201 202 status_t 203 GeneralView::Load() 204 { 205 BPath path; 206 207 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 208 return B_ERROR; 209 210 path.Append(kSettingsDirectory); 211 212 if (create_directory(path.Path(), 0755) != B_OK) { 213 BAlert* alert = new BAlert("", 214 B_TRANSLATE("There was a problem saving the preferences.\n" 215 "It's possible you don't have write access to the " 216 "settings directory."), B_TRANSLATE("OK"), NULL, NULL, 217 B_WIDTH_AS_USUAL, B_STOP_ALERT); 218 (void)alert->Go(); 219 } 220 221 path.Append(kGeneralSettings); 222 223 BMessage settings; 224 BFile file(path.Path(), B_READ_ONLY); 225 settings.Unflatten(&file); 226 227 char buffer[255]; 228 229 bool autoStart; 230 if (settings.FindBool(kAutoStartName, &autoStart) != B_OK) 231 autoStart = kDefaultAutoStart; 232 fAutoStart->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF); 233 234 int32 timeout; 235 if (settings.FindInt32(kTimeoutName, &timeout) != B_OK) 236 timeout = kDefaultTimeout; 237 (void)sprintf(buffer, "%ld", timeout); 238 fTimeout->SetText(buffer); 239 240 return B_OK; 241 } 242 243 244 status_t 245 GeneralView::Save() 246 { 247 BPath path; 248 249 status_t ret = B_OK; 250 ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 251 if (ret != B_OK) 252 return ret; 253 254 path.Append(kSettingsDirectory); 255 path.Append(kGeneralSettings); 256 257 BMessage settings; 258 259 bool autoStart = (fAutoStart->Value() == B_CONTROL_ON); 260 settings.AddBool(kAutoStartName, autoStart); 261 262 int32 timeout = atol(fTimeout->Text()); 263 settings.AddInt32(kTimeoutName, timeout); 264 265 // Save settings file 266 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 267 ret = settings.Flatten(&file); 268 if (ret != B_OK) { 269 BAlert* alert = new BAlert("", 270 B_TRANSLATE("An error occurred saving the preferences.\n" 271 "It's possible you are running out of disk space."), 272 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 273 (void)alert->Go(); 274 return ret; 275 } 276 277 // Find server path 278 entry_ref ref; 279 if (!_CanFindServer(&ref)) { 280 BAlert* alert = new BAlert("", 281 B_TRANSLATE("The notifications server cannot be found.\n" 282 "A possible cause is an installation not done correctly"), 283 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 284 (void)alert->Go(); 285 return B_ERROR; 286 } 287 288 // UserBootscript command 289 BPath serverPath(&ref); 290 291 // Start server at boot time 292 ret = find_directory(B_USER_BOOT_DIRECTORY, &path, true); 293 if (ret != B_OK) { 294 BAlert* alert = new BAlert("", 295 B_TRANSLATE("Can't save preferences, you probably don't have " 296 "write access to the boot settings directory."), B_TRANSLATE("OK"), 297 NULL, NULL, 298 B_WIDTH_AS_USUAL, B_STOP_ALERT); 299 (void)alert->Go(); 300 return ret; 301 } 302 303 path.Append("launch"); 304 BDirectory directory(path.Path()); 305 BEntry entry(&directory, serverPath.Leaf()); 306 307 // Remove symbolic link 308 entry.Remove(); 309 310 if (autoStart) { 311 // Put a symlink into ~/config/boot/launch 312 if ((ret = directory.CreateSymLink(serverPath.Leaf(), 313 serverPath.Path(), NULL) != B_OK)) { 314 BAlert* alert = new BAlert("", 315 B_TRANSLATE("Can't enable notifications at startup time, " 316 "you probably don't have write permission to the boot settings" 317 " directory."), B_TRANSLATE("OK"), NULL, NULL, 318 B_WIDTH_AS_USUAL, B_STOP_ALERT); 319 (void)alert->Go(); 320 return ret; 321 } 322 } 323 324 return B_OK; 325 } 326 327 328 status_t 329 GeneralView::Revert() 330 { 331 return B_ERROR; 332 } 333 334 335 bool 336 GeneralView::_CanFindServer(entry_ref* ref) 337 { 338 // Try searching with be_roster 339 if (be_roster->FindApp(kNotificationServerSignature, ref) == B_OK) 340 return true; 341 342 // Try with a query and take the first result 343 BVolumeRoster vroster; 344 BVolume volume; 345 char volName[B_FILE_NAME_LENGTH]; 346 347 vroster.Rewind(); 348 349 while (vroster.GetNextVolume(&volume) == B_OK) { 350 if ((volume.InitCheck() != B_OK) || !volume.KnowsQuery()) 351 continue; 352 353 volume.GetName(volName); 354 355 BQuery *query = new BQuery(); 356 query->SetPredicate("(BEOS:APP_SIG==\""kNotificationServerSignature"\")"); 357 query->SetVolume(&volume); 358 query->Fetch(); 359 360 if (query->GetNextRef(ref) == B_OK) 361 return true; 362 } 363 364 return false; 365 } 366 367 368 bool 369 GeneralView::_IsServerRunning() 370 { 371 return be_roster->IsRunning(kNotificationServerSignature); 372 } 373