1 /* 2 * Copyright 2003-2011, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Sikosis 7 * Jérôme Duval 8 */ 9 10 11 #include "MediaViews.h" 12 13 #include <AutoDeleter.h> 14 #include <Box.h> 15 #include <Button.h> 16 #include <Catalog.h> 17 #include <CheckBox.h> 18 #include <Deskbar.h> 19 #include <Entry.h> 20 #include <LayoutBuilder.h> 21 #include <Locale.h> 22 #include <MediaAddOn.h> 23 #include <MediaRoster.h> 24 #include <MenuField.h> 25 #include <PopUpMenu.h> 26 #include <String.h> 27 #include <StringView.h> 28 #include <TextView.h> 29 30 #include <assert.h> 31 #include <stdio.h> 32 33 #include "MediaWindow.h" 34 35 36 #undef B_TRANSLATION_CONTEXT 37 #define B_TRANSLATION_CONTEXT "Media views" 38 39 #define MEDIA_DEFAULT_INPUT_CHANGE 'dich' 40 #define MEDIA_DEFAULT_OUTPUT_CHANGE 'doch' 41 #define MEDIA_SHOW_HIDE_VOLUME_CONTROL 'shvc' 42 43 44 SettingsView::SettingsView() 45 : 46 BGroupView(B_VERTICAL, B_USE_DEFAULT_SPACING), 47 fInputMenu(NULL), 48 fOutputMenu(NULL) 49 { 50 // input menu 51 fInputMenu = new BPopUpMenu(B_TRANSLATE("<none>")); 52 fInputMenu->SetLabelFromMarked(true); 53 54 // input menu 55 fOutputMenu = new BPopUpMenu(B_TRANSLATE("<none>")); 56 fOutputMenu->SetLabelFromMarked(true); 57 } 58 59 60 BButton* 61 SettingsView::MakeRestartButton() 62 { 63 return new BButton("restartButton", 64 B_TRANSLATE("Restart media services"), 65 new BMessage(ML_RESTART_MEDIA_SERVER)); 66 } 67 68 69 70 void 71 SettingsView::AddInputNodes(NodeList& list) 72 { 73 _EmptyMenu(fInputMenu); 74 75 BMessage message(MEDIA_DEFAULT_INPUT_CHANGE); 76 _PopulateMenu(fInputMenu, list, message); 77 } 78 79 80 void 81 SettingsView::AddOutputNodes(NodeList& list) 82 { 83 _EmptyMenu(fOutputMenu); 84 85 BMessage message(MEDIA_DEFAULT_OUTPUT_CHANGE); 86 _PopulateMenu(fOutputMenu, list, message); 87 } 88 89 90 void 91 SettingsView::SetDefaultInput(const dormant_node_info* info) 92 { 93 _ClearMenuSelection(fInputMenu); 94 NodeMenuItem* item = _FindNodeItem(fInputMenu, info); 95 if (item) 96 item->SetMarked(true); 97 } 98 99 100 void 101 SettingsView::SetDefaultOutput(const dormant_node_info* info) 102 { 103 _ClearMenuSelection(fOutputMenu); 104 NodeMenuItem* item = _FindNodeItem(fOutputMenu, info); 105 if (item) 106 item->SetMarked(true); 107 } 108 109 110 void 111 SettingsView::MessageReceived(BMessage* message) 112 { 113 switch (message->what) { 114 case MEDIA_DEFAULT_INPUT_CHANGE: 115 { 116 int32 index; 117 if (message->FindInt32("index", &index)!=B_OK) 118 break; 119 NodeMenuItem* item 120 = static_cast<NodeMenuItem*>(fInputMenu->ItemAt(index)); 121 SetDefaultInput(item->NodeInfo()); 122 break; 123 } 124 case MEDIA_DEFAULT_OUTPUT_CHANGE: 125 { 126 int32 index; 127 if (message->FindInt32("index", &index)!=B_OK) 128 break; 129 NodeMenuItem* item 130 = static_cast<NodeMenuItem*>(fOutputMenu->ItemAt(index)); 131 SetDefaultOutput(item->NodeInfo()); 132 break; 133 } 134 default: 135 BGroupView::MessageReceived(message); 136 } 137 } 138 139 140 void 141 SettingsView::AttachedToWindow() 142 { 143 BMessenger thisMessenger(this); 144 fInputMenu->SetTargetForItems(thisMessenger); 145 fOutputMenu->SetTargetForItems(thisMessenger); 146 } 147 148 149 MediaWindow* 150 SettingsView::_MediaWindow() const 151 { 152 return static_cast<MediaWindow*>(Window()); 153 } 154 155 156 void 157 SettingsView::_EmptyMenu(BMenu* menu) 158 { 159 while (menu->CountItems() > 0) 160 delete menu->RemoveItem((int32)0); 161 } 162 163 164 void 165 SettingsView::_PopulateMenu(BMenu* menu, NodeList& nodes, 166 const BMessage& message) 167 { 168 for (int32 i = 0; i < nodes.CountItems(); i++) { 169 dormant_node_info* info = nodes.ItemAt(i); 170 menu->AddItem(new NodeMenuItem(info, new BMessage(message))); 171 } 172 173 if (Window() != NULL) 174 menu->SetTargetForItems(BMessenger(this)); 175 } 176 177 178 NodeMenuItem* 179 SettingsView::_FindNodeItem(BMenu* menu, const dormant_node_info* nodeInfo) 180 { 181 for (int32 i = 0; i < menu->CountItems(); i++) { 182 NodeMenuItem* item = static_cast<NodeMenuItem*>(menu->ItemAt(i)); 183 const dormant_node_info* itemInfo = item->NodeInfo(); 184 if (itemInfo && itemInfo->addon == nodeInfo->addon 185 && itemInfo->flavor_id == nodeInfo->flavor_id) { 186 return item; 187 } 188 } 189 return NULL; 190 } 191 192 193 void 194 SettingsView::_ClearMenuSelection(BMenu* menu) 195 { 196 for (int32 i = 0; i < menu->CountItems(); i++) { 197 BMenuItem* item = menu->ItemAt(i); 198 item->SetMarked(false); 199 } 200 } 201 202 203 NodeMenuItem::NodeMenuItem(const dormant_node_info* info, BMessage* message, 204 char shortcut, uint32 modifiers) 205 : 206 BMenuItem(info->name, message, shortcut, modifiers), 207 fInfo(info) 208 { 209 210 } 211 212 213 status_t 214 NodeMenuItem::Invoke(BMessage* message) 215 { 216 if (IsMarked()) 217 return B_OK; 218 return BMenuItem::Invoke(message); 219 } 220 221 222 ChannelMenuItem::ChannelMenuItem(media_input* input, BMessage* message, 223 char shortcut, uint32 modifiers) 224 : 225 BMenuItem(input->name, message, shortcut, modifiers), 226 fInput(input) 227 { 228 } 229 230 231 ChannelMenuItem::~ChannelMenuItem() 232 { 233 delete fInput; 234 } 235 236 237 int32 238 ChannelMenuItem::DestinationID() 239 { 240 return fInput->destination.id; 241 } 242 243 244 media_input* 245 ChannelMenuItem::Input() 246 { 247 return fInput; 248 } 249 250 251 status_t 252 ChannelMenuItem::Invoke(BMessage* message) 253 { 254 if (IsMarked()) 255 return B_OK; 256 return BMenuItem::Invoke(message); 257 } 258 259 260 AudioSettingsView::AudioSettingsView() 261 { 262 BBox* defaultsBox = new BBox("defaults"); 263 defaultsBox->SetLabel(B_TRANSLATE("Defaults")); 264 BGridView* defaultsGridView = new BGridView(); 265 266 BMenuField* inputMenuField = new BMenuField("inputMenuField", 267 B_TRANSLATE("Audio input:"), InputMenu()); 268 269 BMenuField* outputMenuField = new BMenuField("outputMenuField", 270 B_TRANSLATE("Audio output:"), OutputMenu()); 271 272 BLayoutBuilder::Grid<>(defaultsGridView) 273 .SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING, 274 B_USE_DEFAULT_SPACING) 275 .AddMenuField(inputMenuField, 0, 0, B_ALIGN_HORIZONTAL_UNSET, 1, 3, 1) 276 .AddMenuField(outputMenuField, 0, 1) 277 .AddMenuField(_MakeChannelMenu(), 2, 1); 278 279 defaultsBox->AddChild(defaultsGridView); 280 281 BLayoutBuilder::Group<>(this) 282 .SetInsets(0, 0, 0, 0) 283 .Add(defaultsBox) 284 .AddGroup(B_HORIZONTAL) 285 .Add(_MakeVolumeCheckBox()) 286 .Add(MakeRestartButton()) 287 .End() 288 .AddGlue(); 289 } 290 291 292 void 293 AudioSettingsView::SetDefaultChannel(int32 channelID) 294 { 295 for (int32 i = 0; i < fChannelMenu->CountItems(); i++) { 296 ChannelMenuItem* item = _ChannelMenuItemAt(i); 297 item->SetMarked(item->DestinationID() == channelID); 298 } 299 } 300 301 302 void 303 AudioSettingsView::AttachedToWindow() 304 { 305 SettingsView::AttachedToWindow(); 306 307 BMessenger thisMessenger(this); 308 fChannelMenu->SetTargetForItems(thisMessenger); 309 fVolumeCheckBox->SetTarget(thisMessenger); 310 } 311 312 313 void 314 AudioSettingsView::MessageReceived(BMessage* message) 315 { 316 switch (message->what) { 317 case ML_DEFAULT_CHANNEL_CHANGED: 318 { 319 int32 index; 320 if (message->FindInt32("index", &index) != B_OK) 321 break; 322 ChannelMenuItem* item = _ChannelMenuItemAt(index); 323 324 if (item) { 325 BMediaRoster* roster = BMediaRoster::Roster(); 326 roster->SetAudioOutput(*item->Input()); 327 } else 328 fprintf(stderr, "ChannelMenuItem not found\n"); 329 } 330 break; 331 case MEDIA_SHOW_HIDE_VOLUME_CONTROL: 332 { 333 if (fVolumeCheckBox->Value() == B_CONTROL_ON) 334 _ShowDeskbarVolumeControl(); 335 else 336 _HideDeskbarVolumeControl(); 337 break; 338 } 339 340 default: 341 SettingsView::MessageReceived(message); 342 } 343 } 344 345 346 void 347 AudioSettingsView::SetDefaultInput(const dormant_node_info* info) 348 { 349 SettingsView::SetDefaultInput(info); 350 _MediaWindow()->UpdateInputListItem(MediaListItem::AUDIO_TYPE, info); 351 BMediaRoster::Roster()->SetAudioInput(*info); 352 } 353 354 355 void 356 AudioSettingsView::SetDefaultOutput(const dormant_node_info* info) 357 { 358 SettingsView::SetDefaultOutput(info); 359 _MediaWindow()->UpdateOutputListItem(MediaListItem::AUDIO_TYPE, info); 360 _FillChannelMenu(info); 361 BMediaRoster::Roster()->SetAudioOutput(*info); 362 } 363 364 365 BMenuField* 366 AudioSettingsView::_MakeChannelMenu() 367 { 368 fChannelMenu = new BPopUpMenu(B_TRANSLATE("<none>")); 369 fChannelMenu->SetLabelFromMarked(true); 370 BMenuField* channelMenuField = new BMenuField("channelMenuField", 371 B_TRANSLATE("Channel:"), fChannelMenu); 372 return channelMenuField; 373 } 374 375 376 BCheckBox* 377 AudioSettingsView::_MakeVolumeCheckBox() 378 { 379 fVolumeCheckBox = new BCheckBox("volumeCheckBox", 380 B_TRANSLATE("Show volume control on Deskbar"), 381 new BMessage(MEDIA_SHOW_HIDE_VOLUME_CONTROL)); 382 383 if (BDeskbar().HasItem("MediaReplicant")) 384 fVolumeCheckBox->SetValue(B_CONTROL_ON); 385 386 return fVolumeCheckBox; 387 } 388 389 390 void 391 AudioSettingsView::_FillChannelMenu(const dormant_node_info* nodeInfo) 392 { 393 _EmptyMenu(fChannelMenu); 394 395 BMediaRoster* roster = BMediaRoster::Roster(); 396 media_node node; 397 media_node_id node_id; 398 399 status_t err = roster->GetInstancesFor(nodeInfo->addon, 400 nodeInfo->flavor_id, &node_id); 401 if (err != B_OK) { 402 err = roster->InstantiateDormantNode(*nodeInfo, &node, 403 B_FLAVOR_IS_GLOBAL); 404 } else { 405 err = roster->GetNodeFor(node_id, &node); 406 } 407 408 if (err == B_OK) { 409 int32 inputCount = 4; 410 media_input* inputs = new media_input[inputCount]; 411 BPrivate::ArrayDeleter<media_input> inputDeleter(inputs); 412 413 while (true) { 414 int32 realInputCount = 0; 415 err = roster->GetAllInputsFor(node, inputs, 416 inputCount, &realInputCount); 417 if (realInputCount > inputCount) { 418 inputCount *= 2; 419 inputs = new media_input[inputCount]; 420 inputDeleter.SetTo(inputs); 421 } else { 422 inputCount = realInputCount; 423 break; 424 } 425 } 426 427 if (err == B_OK) { 428 BMessage message(ML_DEFAULT_CHANNEL_CHANGED); 429 430 for (int32 i = 0; i < inputCount; i++) { 431 media_input* input = new media_input(); 432 memcpy(input, &inputs[i], sizeof(*input)); 433 ChannelMenuItem* channelItem = new ChannelMenuItem(input, 434 new BMessage(message)); 435 fChannelMenu->AddItem(channelItem); 436 437 if (channelItem->DestinationID() == 0) 438 channelItem->SetMarked(true); 439 } 440 } 441 } 442 443 if (Window()) 444 fChannelMenu->SetTargetForItems(BMessenger(this)); 445 } 446 447 448 void 449 AudioSettingsView::_ShowDeskbarVolumeControl() 450 { 451 BDeskbar deskbar; 452 BEntry entry("/bin/desklink", true); 453 int32 id; 454 entry_ref ref; 455 status_t status = entry.GetRef(&ref); 456 if (status == B_OK) 457 status = deskbar.AddItem(&ref, &id); 458 459 if (status != B_OK) { 460 fprintf(stderr, B_TRANSLATE( 461 "Couldn't add volume control in Deskbar: %s\n"), 462 strerror(status)); 463 } 464 } 465 466 467 void 468 AudioSettingsView::_HideDeskbarVolumeControl() 469 { 470 BDeskbar deskbar; 471 status_t status = deskbar.RemoveItem("MediaReplicant"); 472 if (status != B_OK) { 473 fprintf(stderr, B_TRANSLATE( 474 "Couldn't remove volume control in Deskbar: %s\n"), 475 strerror(status)); 476 } 477 } 478 479 480 ChannelMenuItem* 481 AudioSettingsView::_ChannelMenuItemAt(int32 index) 482 { 483 return static_cast<ChannelMenuItem*>(fChannelMenu->ItemAt(index)); 484 } 485 486 487 VideoSettingsView::VideoSettingsView() 488 { 489 BBox* defaultsBox = new BBox("defaults"); 490 defaultsBox->SetLabel(B_TRANSLATE("Defaults")); 491 BGridView* defaultsGridView = new BGridView(); 492 493 BMenuField* inputMenuField = new BMenuField("inputMenuField", 494 B_TRANSLATE("Video input:"), InputMenu()); 495 496 BMenuField* outputMenuField = new BMenuField("outputMenuField", 497 B_TRANSLATE("Video output:"), OutputMenu()); 498 499 BLayoutBuilder::Grid<>(defaultsGridView) 500 .SetInsets(B_USE_DEFAULT_SPACING, 0, B_USE_DEFAULT_SPACING, 501 B_USE_DEFAULT_SPACING) 502 .AddMenuField(inputMenuField, 0, 0) 503 .AddMenuField(outputMenuField, 0, 1); 504 505 defaultsBox->AddChild(defaultsGridView); 506 507 BLayoutBuilder::Group<>(this) 508 .SetInsets(0, 0, 0, 0) 509 .Add(defaultsBox) 510 .AddGroup(B_HORIZONTAL) 511 .AddGlue() 512 .Add(MakeRestartButton()) 513 .End() 514 .AddGlue(); 515 } 516 517 518 void 519 VideoSettingsView::SetDefaultInput(const dormant_node_info* info) 520 { 521 SettingsView::SetDefaultInput(info); 522 _MediaWindow()->UpdateInputListItem(MediaListItem::VIDEO_TYPE, info); 523 BMediaRoster::Roster()->SetVideoInput(*info); 524 } 525 526 527 void 528 VideoSettingsView::SetDefaultOutput(const dormant_node_info* info) 529 { 530 SettingsView::SetDefaultOutput(info); 531 _MediaWindow()->UpdateOutputListItem(MediaListItem::VIDEO_TYPE, info); 532 BMediaRoster::Roster()->SetVideoOutput(*info); 533 } 534