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