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