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