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_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 62 BButton* 63 SettingsView::MakeRestartButton() 64 { 65 return new BButton("restartButton", 66 B_TRANSLATE("Restart media services"), 67 new BMessage(ML_RESTART_MEDIA_SERVER)); 68 } 69 70 71 72 void 73 SettingsView::AddInputNodes(NodeList& list) 74 { 75 _EmptyMenu(fInputMenu); 76 77 BMessage message(MEDIA_DEFAULT_INPUT_CHANGE); 78 _PopulateMenu(fInputMenu, list, message); 79 } 80 81 82 void 83 SettingsView::AddOutputNodes(NodeList& list) 84 { 85 _EmptyMenu(fOutputMenu); 86 87 BMessage message(MEDIA_DEFAULT_OUTPUT_CHANGE); 88 _PopulateMenu(fOutputMenu, list, message); 89 } 90 91 92 void 93 SettingsView::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 102 void 103 SettingsView::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 112 void 113 SettingsView::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 142 void 143 SettingsView::AttachedToWindow() 144 { 145 BMessenger thisMessenger(this); 146 fInputMenu->SetTargetForItems(thisMessenger); 147 fOutputMenu->SetTargetForItems(thisMessenger); 148 } 149 150 151 MediaWindow* 152 SettingsView::_MediaWindow() const 153 { 154 return static_cast<MediaWindow*>(Window()); 155 } 156 157 158 void 159 SettingsView::_EmptyMenu(BMenu* menu) 160 { 161 while (menu->CountItems() > 0) 162 delete menu->RemoveItem((int32)0); 163 } 164 165 166 void 167 SettingsView::_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 180 NodeMenuItem* 181 SettingsView::_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 195 void 196 SettingsView::_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 205 NodeMenuItem::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 215 status_t 216 NodeMenuItem::Invoke(BMessage* message) 217 { 218 if (IsMarked()) 219 return B_OK; 220 return BMenuItem::Invoke(message); 221 } 222 223 224 ChannelMenuItem::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 233 ChannelMenuItem::~ChannelMenuItem() 234 { 235 delete fInput; 236 } 237 238 239 int32 240 ChannelMenuItem::DestinationID() 241 { 242 return fInput->destination.id; 243 } 244 245 246 media_input* 247 ChannelMenuItem::Input() 248 { 249 return fInput; 250 } 251 252 253 status_t 254 ChannelMenuItem::Invoke(BMessage* message) 255 { 256 if (IsMarked()) 257 return B_OK; 258 return BMenuItem::Invoke(message); 259 } 260 261 262 AudioSettingsView::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 295 void 296 AudioSettingsView::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 305 void 306 AudioSettingsView::AttachedToWindow() 307 { 308 SettingsView::AttachedToWindow(); 309 310 BMessenger thisMessenger(this); 311 fChannelMenu->SetTargetForItems(thisMessenger); 312 fVolumeCheckBox->SetTarget(thisMessenger); 313 } 314 315 316 void 317 AudioSettingsView::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 349 void 350 AudioSettingsView::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 358 void 359 AudioSettingsView::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 368 BMenuField* 369 AudioSettingsView::_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 379 BCheckBox* 380 AudioSettingsView::_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 393 void 394 AudioSettingsView::_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 451 void 452 AudioSettingsView::_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 470 void 471 AudioSettingsView::_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 483 ChannelMenuItem* 484 AudioSettingsView::_ChannelMenuItemAt(int32 index) 485 { 486 return static_cast<ChannelMenuItem*>(fChannelMenu->ItemAt(index)); 487 } 488 489 490 VideoSettingsView::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 521 void 522 VideoSettingsView::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 530 void 531 VideoSettingsView::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