1 /* 2 * Copyright 2010-2017, 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 * Brian Hill, supernova@tycho.email 9 */ 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 14 #include <vector> 15 16 #include <Alert.h> 17 #include <Box.h> 18 #include <Button.h> 19 #include <Catalog.h> 20 #include <Directory.h> 21 #include <File.h> 22 #include <FindDirectory.h> 23 #include <Font.h> 24 #include <LayoutBuilder.h> 25 #include <Node.h> 26 #include <Path.h> 27 #include <Query.h> 28 #include <Roster.h> 29 #include <String.h> 30 #include <SymLink.h> 31 #include <Volume.h> 32 #include <VolumeRoster.h> 33 34 #include <notification/Notifications.h> 35 36 #include "GeneralView.h" 37 #include "NotificationsConstants.h" 38 #include "SettingsHost.h" 39 40 #undef B_TRANSLATION_CONTEXT 41 #define B_TRANSLATION_CONTEXT "GeneralView" 42 43 const uint32 kToggleNotifications = '_TSR'; 44 const uint32 kWidthChanged = '_WIC'; 45 const uint32 kTimeoutChanged = '_TIC'; 46 const uint32 kPositionChanged = '_NPC'; 47 const uint32 kServerChangeTriggered = '_SCT'; 48 const BString kSampleMessageID("NotificationsSample"); 49 50 51 static int32 52 notification_position_to_index(uint32 notification_position) { 53 if (notification_position == B_FOLLOW_NONE) 54 return 0; 55 else if (notification_position == (B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM)) 56 return 1; 57 else if (notification_position == (B_FOLLOW_LEFT | B_FOLLOW_BOTTOM)) 58 return 2; 59 else if (notification_position == (B_FOLLOW_RIGHT | B_FOLLOW_TOP)) 60 return 3; 61 else if (notification_position == (B_FOLLOW_LEFT | B_FOLLOW_TOP)) 62 return 4; 63 return 0; 64 } 65 66 67 GeneralView::GeneralView(SettingsHost* host) 68 : 69 SettingsPane("general", host) 70 { 71 // Notification server 72 fNotificationBox = new BCheckBox("server", 73 B_TRANSLATE("Enable notifications"), 74 new BMessage(kToggleNotifications)); 75 BBox* box = new BBox("box"); 76 box->SetLabel(fNotificationBox); 77 78 // Window width 79 int32 minWidth = int32(kMinimumWidth / kWidthStep); 80 int32 maxWidth = int32(kMaximumWidth / kWidthStep); 81 fWidthSlider = new BSlider("width", B_TRANSLATE("Window width:"), 82 new BMessage(kWidthChanged), minWidth, maxWidth, B_HORIZONTAL); 83 fWidthSlider->SetHashMarks(B_HASH_MARKS_BOTTOM); 84 fWidthSlider->SetHashMarkCount(maxWidth - minWidth + 1); 85 BString minWidthLabel; 86 minWidthLabel << int32(kMinimumWidth); 87 BString maxWidthLabel; 88 maxWidthLabel << int32(kMaximumWidth); 89 fWidthSlider->SetLimitLabels( 90 B_TRANSLATE_COMMENT(minWidthLabel.String(), "Slider low text"), 91 B_TRANSLATE_COMMENT(maxWidthLabel.String(), "Slider high text")); 92 93 // Display time 94 fDurationSlider = new BSlider("duration", B_TRANSLATE("Duration:"), 95 new BMessage(kTimeoutChanged), kMinimumTimeout, kMaximumTimeout, 96 B_HORIZONTAL); 97 fDurationSlider->SetHashMarks(B_HASH_MARKS_BOTTOM); 98 fDurationSlider->SetHashMarkCount(kMaximumTimeout - kMinimumTimeout + 1); 99 BString minLabel; 100 minLabel << kMinimumTimeout; 101 BString maxLabel; 102 maxLabel << kMaximumTimeout; 103 fDurationSlider->SetLimitLabels( 104 B_TRANSLATE_COMMENT(minLabel.String(), "Slider low text"), 105 B_TRANSLATE_COMMENT(maxLabel.String(), "Slider high text")); 106 107 // Notification Position 108 fPositionMenu = new BPopUpMenu(B_TRANSLATE("Follow Deskbar")); 109 const char* positionLabels[] = { 110 B_TRANSLATE_MARK("Follow Deskbar"), 111 B_TRANSLATE_MARK("Lower right"), 112 B_TRANSLATE_MARK("Lower left"), 113 B_TRANSLATE_MARK("Upper right"), 114 B_TRANSLATE_MARK("Upper left") 115 }; 116 const uint32 positions[] = { 117 B_FOLLOW_DESKBAR, // Follow Deskbar 118 B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT, // Lower right 119 B_FOLLOW_BOTTOM | B_FOLLOW_LEFT, // Lower left 120 B_FOLLOW_TOP | B_FOLLOW_RIGHT, // Upper right 121 B_FOLLOW_TOP | B_FOLLOW_LEFT // Upper left 122 }; 123 for (int i=0; i < 5; i++) { 124 BMessage* message = new BMessage(kPositionChanged); 125 message->AddInt32(kNotificationPositionName, positions[i]); 126 127 fPositionMenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT( 128 positionLabels[i]), message)); 129 } 130 BMenuField* positionField = new BMenuField(B_TRANSLATE("Position:"), 131 fPositionMenu); 132 133 box->AddChild(BLayoutBuilder::Group<>(B_VERTICAL) 134 .SetInsets(B_USE_DEFAULT_SPACING) 135 .Add(fWidthSlider) 136 .Add(fDurationSlider) 137 .Add(positionField) 138 .AddGlue() 139 .View()); 140 141 BLayoutBuilder::Group<>(this, B_VERTICAL) 142 .SetInsets(B_USE_WINDOW_SPACING) 143 .Add(box) 144 .End(); 145 } 146 147 148 void 149 GeneralView::AttachedToWindow() 150 { 151 BView::AttachedToWindow(); 152 fNotificationBox->SetTarget(this); 153 fWidthSlider->SetTarget(this); 154 fDurationSlider->SetTarget(this); 155 fPositionMenu->SetTargetForItems(this); 156 } 157 158 159 void 160 GeneralView::MessageReceived(BMessage* msg) 161 { 162 switch (msg->what) { 163 case kToggleNotifications: 164 { 165 SettingsPane::SettingsChanged(false); 166 _EnableControls(); 167 break; 168 } 169 case kWidthChanged: { 170 int32 value = fWidthSlider->Value() * 50; 171 _SetWidthLabel(value); 172 SettingsPane::SettingsChanged(true); 173 break; 174 } 175 case kTimeoutChanged: 176 { 177 int32 value = fDurationSlider->Value(); 178 _SetTimeoutLabel(value); 179 SettingsPane::SettingsChanged(true); 180 break; 181 } 182 case kPositionChanged: 183 { 184 int32 position; 185 if (msg->FindInt32(kNotificationPositionName, &position) == B_OK) { 186 fNewPosition = position; 187 SettingsPane::SettingsChanged(true); 188 } 189 break; 190 } 191 default: 192 BView::MessageReceived(msg); 193 break; 194 } 195 } 196 197 198 status_t 199 GeneralView::Load(BMessage& settings) 200 { 201 bool autoStart = settings.GetBool(kAutoStartName, true); 202 fNotificationBox->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF); 203 204 if (settings.FindFloat(kWidthName, &fOriginalWidth) != B_OK 205 || fOriginalWidth > kMaximumWidth 206 || fOriginalWidth < kMinimumWidth) 207 fOriginalWidth = kDefaultWidth; 208 209 if (settings.FindInt32(kTimeoutName, &fOriginalTimeout) != B_OK 210 || fOriginalTimeout > kMaximumTimeout 211 || fOriginalTimeout < kMinimumTimeout) 212 fOriginalTimeout = kDefaultTimeout; 213 // TODO need to save again if values outside of expected range 214 int32 setting; 215 if (settings.FindInt32(kIconSizeName, &setting) != B_OK) 216 fOriginalIconSize = kDefaultIconSize; 217 else 218 fOriginalIconSize = (icon_size)setting; 219 220 int32 position; 221 if (settings.FindInt32(kNotificationPositionName, &position) != B_OK) 222 fOriginalPosition = kDefaultNotificationPosition; 223 else 224 fOriginalPosition = position; 225 226 _EnableControls(); 227 228 return Revert(); 229 } 230 231 232 status_t 233 GeneralView::Save(BMessage& settings) 234 { 235 bool autoStart = (fNotificationBox->Value() == B_CONTROL_ON); 236 settings.AddBool(kAutoStartName, autoStart); 237 238 int32 timeout = fDurationSlider->Value(); 239 settings.AddInt32(kTimeoutName, timeout); 240 241 float width = fWidthSlider->Value() * 50; 242 settings.AddFloat(kWidthName, width); 243 244 icon_size iconSize = B_LARGE_ICON; 245 settings.AddInt32(kIconSizeName, (int32)iconSize); 246 247 settings.AddInt32(kNotificationPositionName, (int32)fNewPosition); 248 249 return B_OK; 250 } 251 252 253 status_t 254 GeneralView::Revert() 255 { 256 fDurationSlider->SetValue(fOriginalTimeout); 257 _SetTimeoutLabel(fOriginalTimeout); 258 259 fWidthSlider->SetValue(fOriginalWidth / 50); 260 _SetWidthLabel(fOriginalWidth); 261 262 fNewPosition = fOriginalPosition; 263 BMenuItem* item = fPositionMenu->ItemAt( 264 notification_position_to_index(fNewPosition)); 265 if (item != NULL) 266 item->SetMarked(true); 267 268 return B_OK; 269 } 270 271 272 bool 273 GeneralView::RevertPossible() 274 { 275 int32 timeout = fDurationSlider->Value(); 276 if (fOriginalTimeout != timeout) 277 return true; 278 279 int32 width = fWidthSlider->Value() * 50; 280 if (fOriginalWidth != width) 281 return true; 282 283 if (fOriginalPosition != fNewPosition) 284 return true; 285 286 return false; 287 } 288 289 290 status_t 291 GeneralView::Defaults() 292 { 293 fDurationSlider->SetValue(kDefaultTimeout); 294 _SetTimeoutLabel(kDefaultTimeout); 295 296 fWidthSlider->SetValue(kDefaultWidth / 50); 297 _SetWidthLabel(kDefaultWidth); 298 299 fNewPosition = kDefaultNotificationPosition; 300 BMenuItem* item = fPositionMenu->ItemAt( 301 notification_position_to_index(fNewPosition)); 302 if (item != NULL) 303 item->SetMarked(true); 304 305 return B_OK; 306 } 307 308 309 bool 310 GeneralView::DefaultsPossible() 311 { 312 int32 timeout = fDurationSlider->Value(); 313 if (kDefaultTimeout != timeout) 314 return true; 315 316 int32 width = fWidthSlider->Value() * 50; 317 if (kDefaultWidth != width) 318 return true; 319 320 if (kDefaultNotificationPosition != fNewPosition) 321 return true; 322 323 return false; 324 } 325 326 327 bool 328 GeneralView::UseDefaultRevertButtons() 329 { 330 return true; 331 } 332 333 334 void 335 GeneralView::_EnableControls() 336 { 337 bool enabled = fNotificationBox->Value() == B_CONTROL_ON; 338 fWidthSlider->SetEnabled(enabled); 339 fDurationSlider->SetEnabled(enabled); 340 BMenuItem* item = fPositionMenu->ItemAt( 341 notification_position_to_index(fOriginalPosition)); 342 if (item != NULL) 343 item->SetMarked(true); 344 } 345 346 347 void 348 GeneralView::_SetTimeoutLabel(int32 value) 349 { 350 BString label(B_TRANSLATE("Timeout:")); 351 label.Append(" "); 352 label << value; 353 label.Append(" ").Append(B_TRANSLATE("seconds")); 354 fDurationSlider->SetLabel(label.String()); 355 } 356 357 358 void 359 GeneralView::_SetWidthLabel(int32 value) 360 { 361 BString label(B_TRANSLATE("Width:")); 362 label.Append(" "); 363 label << value; 364 label.Append(" ").Append(B_TRANSLATE("pixels")); 365 fWidthSlider->SetLabel(label.String()); 366 } 367 368 369 bool 370 GeneralView::_IsServerRunning() 371 { 372 return be_roster->IsRunning(kNotificationServerSignature); 373 } 374