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 SetDrawingMode(B_OP_OVER); 203 DrawBitmap(fIcon); 204 } 205 206 207 void 208 MediaReplicant::MouseDown(BPoint point) 209 { 210 int32 buttons = B_PRIMARY_MOUSE_BUTTON; 211 if (Looper() != NULL && Looper()->CurrentMessage() != NULL) 212 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 213 214 BPoint where = ConvertToScreen(point); 215 216 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 217 BPopUpMenu* menu = new BPopUpMenu("", false, false); 218 menu->SetFont(be_plain_font); 219 220 menu->AddItem(new BMenuItem("Media preferences" B_UTF8_ELLIPSIS, 221 new BMessage(kMsgOpenMediaSettings))); 222 menu->AddItem(new BMenuItem("Sound preferences" B_UTF8_ELLIPSIS, 223 new BMessage(kMsgOpenSoundSettings))); 224 225 menu->AddSeparatorItem(); 226 227 menu->AddItem(new BMenuItem("Open MediaPlayer", 228 new BMessage(kMsgOpenMediaPlayer))); 229 230 menu->AddSeparatorItem(); 231 232 BMenu* subMenu = new BMenu("Options"); 233 menu->AddItem(subMenu); 234 235 BMenuItem* item = new BMenuItem("Control physical output", 236 new BMessage(kMsgVolumeWhich)); 237 item->SetMarked(fVolumeWhich == VOLUME_USE_PHYS_OUTPUT); 238 subMenu->AddItem(item); 239 240 item = new BMenuItem("Beep", new BMessage(kMsgToggleBeep)); 241 item->SetMarked(!fDontBeep); 242 subMenu->AddItem(item); 243 244 menu->AddSeparatorItem(); 245 246 menu->AddItem(new BMenuItem("About" B_UTF8_ELLIPSIS, 247 new BMessage(B_ABOUT_REQUESTED))); 248 249 menu->SetTargetForItems(this); 250 subMenu->SetTargetForItems(this); 251 252 menu->Go(where, true, true, BRect(where - BPoint(4, 4), 253 where + BPoint(4, 4))); 254 } else { 255 // Show VolumeWindow 256 fVolumeSlider = new VolumeWindow(BRect(where.x, where.y, 257 where.x + 207, where.y + 19), fDontBeep, fVolumeWhich); 258 fVolumeSlider->Show(); 259 } 260 } 261 262 263 void 264 MediaReplicant::MessageReceived(BMessage* message) 265 { 266 switch (message->what) { 267 case B_ABOUT_REQUESTED: 268 (new BAlert("About Volume Control", "Volume Control\n" 269 " Brought to you by Jérôme DUVAL.\n\n" 270 "Copyright " B_UTF8_COPYRIGHT "2003-2009, Haiku", 271 "OK"))->Go(NULL); 272 break; 273 274 case kMsgOpenMediaPlayer: 275 _Launch("MediaPlayer", "application/x-vnd.Haiku-MediaPlayer", 276 B_SYSTEM_APPS_DIRECTORY, "MediaPlayer"); 277 break; 278 279 case kMsgOpenMediaSettings: 280 _Launch("Media Preferences", "application/x-vnd.Haiku-Media", 281 B_SYSTEM_PREFERENCES_DIRECTORY, "Media"); 282 break; 283 284 case kMsgOpenSoundSettings: 285 _Launch("Sounds Preferences", "application/x-vnd.Haiku-Sounds", 286 B_SYSTEM_PREFERENCES_DIRECTORY, "Sounds"); 287 break; 288 289 case kMsgToggleBeep: 290 { 291 BMenuItem* item; 292 if (message->FindPointer("source", (void**)&item) != B_OK) 293 return; 294 295 item->SetMarked(!item->IsMarked()); 296 fDontBeep = !item->IsMarked(); 297 break; 298 } 299 300 case kMsgVolumeWhich: 301 { 302 BMenuItem* item; 303 if (message->FindPointer("source", (void**)&item) != B_OK) 304 return; 305 306 item->SetMarked(!item->IsMarked()); 307 fVolumeWhich = item->IsMarked() 308 ? VOLUME_USE_PHYS_OUTPUT : VOLUME_USE_MIXER; 309 310 if (VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip())) 311 tip->SetWhich(fVolumeWhich); 312 break; 313 } 314 315 case B_MOUSE_WHEEL_CHANGED: 316 { 317 float deltaY; 318 if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK 319 && deltaY != 0.0) { 320 MixerControl mixerControl; 321 mixerControl.Connect(fVolumeWhich); 322 mixerControl.ChangeVolumeBy(deltaY < 0 ? 6 : -6); 323 324 VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip()); 325 if (tip != NULL) { 326 tip->Update(); 327 ShowToolTip(tip); 328 } 329 } 330 break; 331 } 332 333 default: 334 BView::MessageReceived(message); 335 break; 336 } 337 } 338 339 340 status_t 341 MediaReplicant::_LaunchByPath(const char* path) 342 { 343 entry_ref ref; 344 status_t status = get_ref_for_path(path, &ref); 345 if (status != B_OK) 346 return status; 347 348 status = be_roster->Launch(&ref); 349 if (status != B_ALREADY_RUNNING) 350 return status; 351 352 // The application runs already, bring it to front 353 354 app_info appInfo; 355 status = be_roster->GetAppInfo(&ref, &appInfo); 356 if (status != B_OK) 357 return status; 358 359 return be_roster->ActivateApp(appInfo.team); 360 } 361 362 363 status_t 364 MediaReplicant::_LaunchBySignature(const char* signature) 365 { 366 status_t status = be_roster->Launch(signature); 367 if (status != B_ALREADY_RUNNING) 368 return status; 369 370 // The application runs already, bring it to front 371 372 app_info appInfo; 373 status = be_roster->GetAppInfo(signature, &appInfo); 374 if (status != B_OK) 375 return status; 376 377 return be_roster->ActivateApp(appInfo.team); 378 } 379 380 381 void 382 MediaReplicant::_Launch(const char* prettyName, const char* signature, 383 directory_which base, const char* fileName) 384 { 385 BPath path; 386 status_t status = find_directory(base, &path); 387 if (status == B_OK) 388 path.Append(fileName); 389 390 // launch the application 391 if (_LaunchBySignature(signature) != B_OK 392 && _LaunchByPath(path.Path()) != B_OK) { 393 BString message = "Couldn't launch "; 394 message << prettyName; 395 396 (new BAlert("desklink", message.String(), "OK"))->Go(); 397 } 398 } 399 400 401 void 402 MediaReplicant::_LoadSettings() 403 { 404 fDontBeep = false; 405 fVolumeWhich = VOLUME_USE_MIXER; 406 407 BPath path; 408 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK) 409 return; 410 411 path.Append(kSettingsFile); 412 413 BFile settings(path.Path(), B_READ_ONLY); 414 if (settings.InitCheck() < B_OK) 415 return; 416 417 BMessage msg; 418 if (msg.Unflatten(&settings) < B_OK) 419 return; 420 421 msg.FindInt32("volwhich", &fVolumeWhich); 422 msg.FindBool("dontbeep", &fDontBeep); 423 } 424 425 426 void 427 MediaReplicant::_SaveSettings() 428 { 429 BPath path; 430 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK) 431 return; 432 433 path.Append(kSettingsFile); 434 435 BFile settings(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 436 if (settings.InitCheck() < B_OK) 437 return; 438 439 BMessage msg('CNFG'); 440 msg.AddInt32("volwhich", fVolumeWhich); 441 msg.AddBool("dontbeep", fDontBeep); 442 443 ssize_t size = 0; 444 msg.Flatten(&settings, &size); 445 } 446 447 448 void 449 MediaReplicant::_Init() 450 { 451 fIcon = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1), 452 B_CMAP8); 453 fIcon->SetBits(kSpeakerBits, kSpeakerWidth * kSpeakerHeight, 0, B_CMAP8); 454 455 _LoadSettings(); 456 457 SetToolTip(new VolumeToolTip(fVolumeWhich)); 458 } 459 460 461 // #pragma mark - 462 463 464 extern "C" BView* 465 instantiate_deskbar_item(void) 466 { 467 return new MediaReplicant(BRect(0, 0, 16, 16), kReplicantName); 468 } 469 470