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