1 /* 2 * Copyright 2003-2010, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Jérôme Duval 7 * François Revol 8 * Marcus Overhagen 9 * Jonas Sundström 10 * Axel Dörfler, axeld@pinc-software.de. 11 * Stephan Aßmus <superstippi@gmx.de> 12 */ 13 14 15 //! Volume control, and media shortcuts in Deskbar 16 17 18 #include <new> 19 #include <stdio.h> 20 21 #include <Alert.h> 22 #include <Bitmap.h> 23 #include <Entry.h> 24 #include <File.h> 25 #include <FindDirectory.h> 26 #include <IconUtils.h> 27 #include <MenuItem.h> 28 #include <Path.h> 29 #include <PopUpMenu.h> 30 #include <Roster.h> 31 #include <String.h> 32 #include <StringView.h> 33 #include <ToolTip.h> 34 #include <ToolTipManager.h> 35 36 #include "desklink.h" 37 #include "iconfile.h" 38 #include "MixerControl.h" 39 #include "VolumeWindow.h" 40 41 42 static const uint32 kMsgOpenMediaSettings = 'mese'; 43 static const uint32 kMsgOpenSoundSettings = 'sose'; 44 static const uint32 kMsgOpenMediaPlayer = 'omep'; 45 static const uint32 kMsgToggleBeep = 'tdbp'; 46 static const uint32 kMsgVolumeWhich = 'svwh'; 47 48 static const char* kReplicantName = "MediaReplicant"; 49 // R5 name needed, Media prefs manel removes by name 50 51 static const char* kSettingsFile = "x-vnd.Haiku-desklink"; 52 53 54 class VolumeToolTip : public BToolTip { 55 public: 56 VolumeToolTip(int32 which = VOLUME_USE_MIXER) 57 : 58 fWhich(which) 59 { 60 fView = new BStringView("", ""); 61 } 62 63 virtual ~VolumeToolTip() 64 { 65 delete fView; 66 } 67 68 virtual BView* View() const 69 { 70 return fView; 71 } 72 73 virtual void AttachedToWindow() 74 { 75 Update(); 76 } 77 78 void SetWhich(int32 which) 79 { 80 fWhich = which; 81 } 82 83 void Update() 84 { 85 if (!Lock()) 86 return; 87 88 MixerControl control; 89 control.Connect(fWhich); 90 91 char text[256]; 92 snprintf(text, sizeof(text), "%g dB", control.Volume()); 93 fView->SetText(text); 94 95 Unlock(); 96 } 97 98 private: 99 BStringView* fView; 100 int32 fWhich; 101 }; 102 103 104 class MediaReplicant : public BView { 105 public: 106 MediaReplicant(BRect frame, const char* name, 107 uint32 resizeMask = B_FOLLOW_ALL, 108 uint32 flags = B_WILL_DRAW | B_NAVIGABLE); 109 MediaReplicant(BMessage* archive); 110 111 virtual ~MediaReplicant(); 112 113 // archiving overrides 114 static MediaReplicant* Instantiate(BMessage* data); 115 virtual status_t Archive(BMessage* data, bool deep = true) const; 116 117 // BView overrides 118 virtual void AttachedToWindow(); 119 virtual void MouseDown(BPoint point); 120 virtual void Draw(BRect updateRect); 121 virtual void MessageReceived(BMessage* message); 122 123 private: 124 status_t _LaunchByPath(const char* path); 125 status_t _LaunchBySignature(const char* signature); 126 void _Launch(const char* prettyName, 127 const char* signature, directory_which base, 128 const char* fileName); 129 void _LoadSettings(); 130 void _SaveSettings(); 131 void _Init(); 132 133 BBitmap* fIcon; 134 VolumeWindow* fVolumeSlider; 135 bool fDontBeep; 136 // don't beep on volume change 137 int32 fVolumeWhich; 138 // which volume parameter to act on (Mixer/Phys.Output) 139 }; 140 141 142 MediaReplicant::MediaReplicant(BRect frame, const char* name, 143 uint32 resizeMask, uint32 flags) 144 : 145 BView(frame, name, resizeMask, flags), 146 fVolumeSlider(NULL) 147 { 148 _Init(); 149 } 150 151 152 MediaReplicant::MediaReplicant(BMessage* message) 153 : 154 BView(message), 155 fVolumeSlider(NULL) 156 { 157 _Init(); 158 } 159 160 161 MediaReplicant::~MediaReplicant() 162 { 163 delete fIcon; 164 _SaveSettings(); 165 } 166 167 168 MediaReplicant* 169 MediaReplicant::Instantiate(BMessage* data) 170 { 171 if (!validate_instantiation(data, kReplicantName)) 172 return NULL; 173 174 return new(std::nothrow) MediaReplicant(data); 175 } 176 177 178 status_t 179 MediaReplicant::Archive(BMessage* data, bool deep) const 180 { 181 status_t status = BView::Archive(data, deep); 182 if (status < B_OK) 183 return status; 184 185 return data->AddString("add_on", kAppSignature); 186 } 187 188 189 void 190 MediaReplicant::AttachedToWindow() 191 { 192 BView* parent = Parent(); 193 if (parent) 194 SetViewColor(parent->ViewColor()); 195 196 BView::AttachedToWindow(); 197 } 198 199 200 void 201 MediaReplicant::Draw(BRect rect) 202 { 203 SetDrawingMode(B_OP_OVER); 204 DrawBitmap(fIcon); 205 } 206 207 208 void 209 MediaReplicant::MouseDown(BPoint point) 210 { 211 int32 buttons = B_PRIMARY_MOUSE_BUTTON; 212 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) 213 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 214 215 BPoint where = ConvertToScreen(point); 216 217 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 218 BPopUpMenu* menu = new BPopUpMenu("", false, false); 219 menu->SetFont(be_plain_font); 220 221 menu->AddItem(new BMenuItem("Media preferences" B_UTF8_ELLIPSIS, 222 new BMessage(kMsgOpenMediaSettings))); 223 menu->AddItem(new BMenuItem("Sound preferences" B_UTF8_ELLIPSIS, 224 new BMessage(kMsgOpenSoundSettings))); 225 226 menu->AddSeparatorItem(); 227 228 menu->AddItem(new BMenuItem("Open MediaPlayer", 229 new BMessage(kMsgOpenMediaPlayer))); 230 231 menu->AddSeparatorItem(); 232 233 BMenu* subMenu = new BMenu("Options"); 234 menu->AddItem(subMenu); 235 236 BMenuItem* item = new BMenuItem("Control physical output", 237 new BMessage(kMsgVolumeWhich)); 238 item->SetMarked(fVolumeWhich == VOLUME_USE_PHYS_OUTPUT); 239 subMenu->AddItem(item); 240 241 item = new BMenuItem("Beep", new BMessage(kMsgToggleBeep)); 242 item->SetMarked(!fDontBeep); 243 subMenu->AddItem(item); 244 245 menu->AddSeparatorItem(); 246 247 menu->AddItem(new BMenuItem("About" B_UTF8_ELLIPSIS, 248 new BMessage(B_ABOUT_REQUESTED))); 249 250 menu->SetTargetForItems(this); 251 subMenu->SetTargetForItems(this); 252 253 menu->Go(where, true, true, BRect(where - BPoint(4, 4), 254 where + BPoint(4, 4))); 255 } else { 256 // Show VolumeWindow 257 fVolumeSlider = new VolumeWindow(BRect(where.x, where.y, 258 where.x + 207, where.y + 19), fDontBeep, fVolumeWhich); 259 fVolumeSlider->Show(); 260 } 261 } 262 263 264 void 265 MediaReplicant::MessageReceived(BMessage* message) 266 { 267 switch (message->what) { 268 case B_ABOUT_REQUESTED: 269 (new BAlert("About Volume Control", "Volume Control\n" 270 " Brought to you by Jérôme DUVAL.\n\n" 271 "Copyright " B_UTF8_COPYRIGHT "2003-2010, Haiku", 272 "OK"))->Go(NULL); 273 break; 274 275 case kMsgOpenMediaPlayer: 276 _Launch("MediaPlayer", "application/x-vnd.Haiku-MediaPlayer", 277 B_SYSTEM_APPS_DIRECTORY, "MediaPlayer"); 278 break; 279 280 case kMsgOpenMediaSettings: 281 _Launch("Media Preferences", "application/x-vnd.Haiku-Media", 282 B_SYSTEM_PREFERENCES_DIRECTORY, "Media"); 283 break; 284 285 case kMsgOpenSoundSettings: 286 _Launch("Sounds Preferences", "application/x-vnd.Haiku-Sounds", 287 B_SYSTEM_PREFERENCES_DIRECTORY, "Sounds"); 288 break; 289 290 case kMsgToggleBeep: 291 { 292 BMenuItem* item; 293 if (message->FindPointer("source", (void**)&item) != B_OK) 294 return; 295 296 item->SetMarked(!item->IsMarked()); 297 fDontBeep = !item->IsMarked(); 298 break; 299 } 300 301 case kMsgVolumeWhich: 302 { 303 BMenuItem* item; 304 if (message->FindPointer("source", (void**)&item) != B_OK) 305 return; 306 307 item->SetMarked(!item->IsMarked()); 308 fVolumeWhich = item->IsMarked() 309 ? VOLUME_USE_PHYS_OUTPUT : VOLUME_USE_MIXER; 310 311 if (VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip())) 312 tip->SetWhich(fVolumeWhich); 313 break; 314 } 315 316 case B_MOUSE_WHEEL_CHANGED: 317 { 318 float deltaY; 319 if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK 320 && deltaY != 0.0) { 321 MixerControl mixerControl; 322 mixerControl.Connect(fVolumeWhich); 323 mixerControl.ChangeVolumeBy(deltaY < 0 ? 6 : -6); 324 325 VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip()); 326 if (tip != NULL) { 327 tip->Update(); 328 ShowToolTip(tip); 329 } 330 } 331 break; 332 } 333 334 default: 335 BView::MessageReceived(message); 336 break; 337 } 338 } 339 340 341 status_t 342 MediaReplicant::_LaunchByPath(const char* path) 343 { 344 entry_ref ref; 345 status_t status = get_ref_for_path(path, &ref); 346 if (status != B_OK) 347 return status; 348 349 status = be_roster->Launch(&ref); 350 if (status != B_ALREADY_RUNNING) 351 return status; 352 353 // The application runs already, bring it to front 354 355 app_info appInfo; 356 status = be_roster->GetAppInfo(&ref, &appInfo); 357 if (status != B_OK) 358 return status; 359 360 return be_roster->ActivateApp(appInfo.team); 361 } 362 363 364 status_t 365 MediaReplicant::_LaunchBySignature(const char* signature) 366 { 367 status_t status = be_roster->Launch(signature); 368 if (status != B_ALREADY_RUNNING) 369 return status; 370 371 // The application runs already, bring it to front 372 373 app_info appInfo; 374 status = be_roster->GetAppInfo(signature, &appInfo); 375 if (status != B_OK) 376 return status; 377 378 return be_roster->ActivateApp(appInfo.team); 379 } 380 381 382 void 383 MediaReplicant::_Launch(const char* prettyName, const char* signature, 384 directory_which base, const char* fileName) 385 { 386 BPath path; 387 status_t status = find_directory(base, &path); 388 if (status == B_OK) 389 path.Append(fileName); 390 391 // launch the application 392 if (_LaunchBySignature(signature) != B_OK 393 && _LaunchByPath(path.Path()) != B_OK) { 394 BString message = "Couldn't launch "; 395 message << prettyName; 396 397 (new BAlert("desklink", message.String(), "OK"))->Go(); 398 } 399 } 400 401 402 void 403 MediaReplicant::_LoadSettings() 404 { 405 fDontBeep = false; 406 fVolumeWhich = VOLUME_USE_MIXER; 407 408 BPath path; 409 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK) 410 return; 411 412 path.Append(kSettingsFile); 413 414 BFile settings(path.Path(), B_READ_ONLY); 415 if (settings.InitCheck() < B_OK) 416 return; 417 418 BMessage msg; 419 if (msg.Unflatten(&settings) < B_OK) 420 return; 421 422 msg.FindInt32("volwhich", &fVolumeWhich); 423 msg.FindBool("dontbeep", &fDontBeep); 424 } 425 426 427 void 428 MediaReplicant::_SaveSettings() 429 { 430 BPath path; 431 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK) 432 return; 433 434 path.Append(kSettingsFile); 435 436 BFile settings(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 437 if (settings.InitCheck() < B_OK) 438 return; 439 440 BMessage msg('CNFG'); 441 msg.AddInt32("volwhich", fVolumeWhich); 442 msg.AddBool("dontbeep", fDontBeep); 443 444 ssize_t size = 0; 445 msg.Flatten(&settings, &size); 446 } 447 448 449 void 450 MediaReplicant::_Init() 451 { 452 fIcon = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1), 453 B_RGBA32); 454 BIconUtils::GetVectorIcon(kSpeakerIcon, sizeof(kSpeakerIcon), fIcon); 455 456 _LoadSettings(); 457 458 SetToolTip(new VolumeToolTip(fVolumeWhich)); 459 } 460 461 462 // #pragma mark - 463 464 465 extern "C" BView* 466 instantiate_deskbar_item(void) 467 { 468 return new MediaReplicant(BRect(0, 0, 16, 16), kReplicantName); 469 } 470 471