1 /* 2 * Copyright 2003-2012, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Sikosis, Jérôme Duval 7 * yourpalal, Alex Wilson 8 */ 9 10 11 #include "MediaWindow.h" 12 13 #include <stdio.h> 14 15 #include <Alert.h> 16 #include <Application.h> 17 #include <Autolock.h> 18 #include <Bitmap.h> 19 #include <Button.h> 20 #include <CardLayout.h> 21 #include <Catalog.h> 22 #include <Debug.h> 23 #include <Deskbar.h> 24 #include <LayoutBuilder.h> 25 #include <Locale.h> 26 #include <MediaRoster.h> 27 #include <MediaTheme.h> 28 #include <Resources.h> 29 #include <Roster.h> 30 #include <Screen.h> 31 #include <ScrollView.h> 32 #include <SeparatorView.h> 33 #include <SpaceLayoutItem.h> 34 #include <StorageKit.h> 35 #include <String.h> 36 #include <TextView.h> 37 38 #include "MediaIcons.h" 39 #include "MidiSettingsView.h" 40 41 #undef B_TRANSLATION_CONTEXT 42 #define B_TRANSLATION_CONTEXT "Media Window" 43 44 45 const uint32 ML_SELECTED_NODE = 'MlSN'; 46 const uint32 ML_INIT_MEDIA = 'MlIM'; 47 48 49 class NodeListItemUpdater : public MediaListItem::Visitor { 50 public: 51 typedef void (NodeListItem::*UpdateMethod)(bool); 52 53 NodeListItemUpdater(NodeListItem* target, UpdateMethod action) 54 : 55 fComparator(target), 56 fAction(action) 57 { 58 } 59 60 61 virtual void Visit(AudioMixerListItem*){} 62 virtual void Visit(DeviceListItem*){} 63 virtual void Visit(MidiListItem*){} 64 virtual void Visit(NodeListItem* item) 65 { 66 item->Accept(fComparator); 67 (item->*(fAction))(fComparator.result == 0); 68 } 69 70 private: 71 72 NodeListItem::Comparator fComparator; 73 UpdateMethod fAction; 74 }; 75 76 77 MediaWindow::SmartNode::SmartNode(const BMessenger& notifyHandler) 78 : 79 fNode(NULL), 80 fMessenger(notifyHandler) 81 { 82 } 83 84 85 MediaWindow::SmartNode::~SmartNode() 86 { 87 _FreeNode(); 88 } 89 90 91 void 92 MediaWindow::SmartNode::SetTo(const dormant_node_info* info) 93 { 94 _FreeNode(); 95 if (!info) 96 return; 97 98 fNode = new media_node(); 99 BMediaRoster* roster = BMediaRoster::Roster(); 100 101 // TODO: error codes 102 media_node_id node_id; 103 if (roster->GetInstancesFor(info->addon, info->flavor_id, &node_id) == B_OK) 104 roster->GetNodeFor(node_id, fNode); 105 else 106 roster->InstantiateDormantNode(*info, fNode, B_FLAVOR_IS_GLOBAL); 107 roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD); 108 } 109 110 111 void 112 MediaWindow::SmartNode::SetTo(const media_node& node) 113 { 114 _FreeNode(); 115 fNode = new media_node(node); 116 BMediaRoster* roster = BMediaRoster::Roster(); 117 roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD); 118 } 119 120 121 bool 122 MediaWindow::SmartNode::IsSet() 123 { 124 return fNode != NULL; 125 } 126 127 128 MediaWindow::SmartNode::operator media_node() 129 { 130 if (fNode) 131 return *fNode; 132 media_node node; 133 return node; 134 } 135 136 137 void 138 MediaWindow::SmartNode::_FreeNode() 139 { 140 if (!IsSet()) 141 return; 142 // TODO: check error codes 143 BMediaRoster* roster = BMediaRoster::Roster(); 144 roster->StopWatching(fMessenger, *fNode, B_MEDIA_WILDCARD); 145 roster->ReleaseNode(*fNode); 146 delete fNode; 147 fNode = NULL; 148 } 149 150 151 // #pragma mark - 152 153 154 MediaWindow::MediaWindow(BRect frame) 155 : 156 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Media"), B_TITLED_WINDOW, 157 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 158 fCurrentNode(BMessenger(this)), 159 fParamWeb(NULL), 160 fAudioInputs(5, true), 161 fAudioOutputs(5, true), 162 fVideoInputs(5, true), 163 fVideoOutputs(5, true), 164 fAlert(NULL), 165 fInitCheck(B_OK) 166 { 167 _InitWindow(); 168 } 169 170 171 MediaWindow::~MediaWindow() 172 { 173 _EmptyNodeLists(); 174 _ClearParamView(); 175 176 char buffer[512]; 177 BRect rect = Frame(); 178 PRINT_OBJECT(rect); 179 snprintf(buffer, 512, "# MediaPrefs Settings\n rect = %i,%i,%i,%i\n", 180 int(rect.left), int(rect.top), int(rect.right), int(rect.bottom)); 181 182 BPath path; 183 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 184 path.Append(SETTINGS_FILE); 185 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 186 if (file.InitCheck() == B_OK) 187 file.Write(buffer, strlen(buffer)); 188 } 189 } 190 191 192 status_t 193 MediaWindow::InitCheck() 194 { 195 return fInitCheck; 196 } 197 198 199 void 200 MediaWindow::SelectNode(const dormant_node_info* node) 201 { 202 fCurrentNode.SetTo(node); 203 _MakeParamView(); 204 fTitleView->SetLabel(node->name); 205 } 206 207 208 void 209 MediaWindow::SelectAudioSettings(const char* title) 210 { 211 fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fAudioView)); 212 fTitleView->SetLabel(title); 213 } 214 215 216 void 217 MediaWindow::SelectVideoSettings(const char* title) 218 { 219 fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fVideoView)); 220 fTitleView->SetLabel(title); 221 } 222 223 224 void 225 MediaWindow::SelectAudioMixer(const char* title) 226 { 227 media_node mixerNode; 228 BMediaRoster* roster = BMediaRoster::Roster(); 229 roster->GetAudioMixer(&mixerNode); 230 fCurrentNode.SetTo(mixerNode); 231 _MakeParamView(); 232 fTitleView->SetLabel(title); 233 } 234 235 236 void 237 MediaWindow::SelectMidiSettings(const char* title) 238 { 239 fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fMidiView)); 240 fTitleView->SetLabel(title); 241 } 242 243 244 void 245 MediaWindow::UpdateInputListItem(MediaListItem::media_type type, 246 const dormant_node_info* node) 247 { 248 NodeListItem compareTo(node, type); 249 NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultInput); 250 for (int32 i = 0; i < fListView->CountItems(); i++) { 251 MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i)); 252 item->Accept(updater); 253 } 254 fListView->Invalidate(); 255 } 256 257 258 void 259 MediaWindow::UpdateOutputListItem(MediaListItem::media_type type, 260 const dormant_node_info* node) 261 { 262 NodeListItem compareTo(node, type); 263 NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultOutput); 264 for (int32 i = 0; i < fListView->CountItems(); i++) { 265 MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i)); 266 item->Accept(updater); 267 } 268 fListView->Invalidate(); 269 } 270 271 272 bool 273 MediaWindow::QuitRequested() 274 { 275 // stop watching the MediaRoster 276 fCurrentNode.SetTo(NULL); 277 be_app->PostMessage(B_QUIT_REQUESTED); 278 return true; 279 } 280 281 282 void 283 MediaWindow::MessageReceived(BMessage* message) 284 { 285 switch (message->what) { 286 case ML_INIT_MEDIA: 287 _InitMedia(false); 288 break; 289 case ML_RESTART_MEDIA_SERVER: 290 { 291 thread_id thread = spawn_thread(&MediaWindow::_RestartMediaServices, 292 "restart_thread", B_NORMAL_PRIORITY, this); 293 if (thread < 0) 294 fprintf(stderr, "couldn't create restart thread\n"); 295 else 296 resume_thread(thread); 297 break; 298 } 299 case B_MEDIA_WEB_CHANGED: 300 case ML_SELECTED_NODE: 301 { 302 PRINT_OBJECT(*message); 303 304 MediaListItem* item = static_cast<MediaListItem*>( 305 fListView->ItemAt(fListView->CurrentSelection())); 306 if (item == NULL) 307 break; 308 309 fCurrentNode.SetTo(NULL); 310 _ClearParamView(); 311 item->AlterWindow(this); 312 break; 313 } 314 case B_SOME_APP_LAUNCHED: 315 { 316 PRINT_OBJECT(*message); 317 if (fAlert == NULL) 318 break; 319 320 BString mimeSig; 321 if (message->FindString("be:signature", &mimeSig) == B_OK 322 && (mimeSig == "application/x-vnd.Be.addon-host" 323 || mimeSig == "application/x-vnd.Be.media-server")) { 324 fAlert->Lock(); 325 fAlert->TextView()->SetText( 326 B_TRANSLATE("Starting media server" B_UTF8_ELLIPSIS)); 327 fAlert->Unlock(); 328 } 329 break; 330 } 331 case B_SOME_APP_QUIT: 332 { 333 PRINT_OBJECT(*message); 334 BString mimeSig; 335 if (message->FindString("be:signature", &mimeSig) == B_OK) { 336 if (mimeSig == "application/x-vnd.Be.addon-host" 337 || mimeSig == "application/x-vnd.Be.media-server") { 338 BMediaRoster* roster = BMediaRoster::CurrentRoster(); 339 if (roster != NULL && roster->Lock()) 340 roster->Quit(); 341 } 342 } 343 break; 344 } 345 default: 346 BWindow::MessageReceived(message); 347 break; 348 } 349 } 350 351 352 // #pragma mark - private 353 354 355 void 356 MediaWindow::_InitWindow() 357 { 358 fListView = new BListView("media_list_view"); 359 fListView->SetSelectionMessage(new BMessage(ML_SELECTED_NODE)); 360 fListView->SetExplicitMinSize(BSize(140, B_SIZE_UNSET)); 361 362 // Add ScrollView to Media Menu for pretty border 363 BScrollView* scrollView = new BScrollView("listscroller", 364 fListView, 0, false, false, B_FANCY_BORDER); 365 366 // Create the Views 367 fTitleView = new BSeparatorView(B_HORIZONTAL, B_FANCY_BORDER); 368 fTitleView->SetLabel(B_TRANSLATE("Audio settings")); 369 fTitleView->SetFont(be_bold_font); 370 371 fContentLayout = new BCardLayout(); 372 new BView("content view", 0, fContentLayout); 373 fContentLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 374 fContentLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 375 376 fAudioView = new AudioSettingsView(); 377 fContentLayout->AddView(fAudioView); 378 379 fVideoView = new VideoSettingsView(); 380 fContentLayout->AddView(fVideoView); 381 382 fMidiView = new MidiSettingsView(); 383 fContentLayout->AddView(fMidiView); 384 385 // Layout all views 386 BLayoutBuilder::Group<>(this, B_HORIZONTAL) 387 .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 388 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) 389 .Add(scrollView, 0.0f) 390 .AddGroup(B_VERTICAL) 391 .SetInsets(0, 0, 0, 0) 392 .Add(fTitleView) 393 .Add(fContentLayout); 394 395 // Start the window 396 fInitCheck = _InitMedia(true); 397 if (fInitCheck != B_OK) 398 PostMessage(B_QUIT_REQUESTED); 399 else if (IsHidden()) 400 Show(); 401 } 402 403 404 status_t 405 MediaWindow::_InitMedia(bool first) 406 { 407 status_t err = B_OK; 408 BMediaRoster* roster = BMediaRoster::Roster(&err); 409 410 if (first && err != B_OK) { 411 BAlert* alert = new BAlert("start_media_server", 412 B_TRANSLATE("Could not connect to the media server.\n" 413 "Would you like to start it ?"), 414 B_TRANSLATE("Quit"), 415 B_TRANSLATE("Start media server"), NULL, 416 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 417 alert->SetShortcut(0, B_ESCAPE); 418 if (alert->Go() == 0) 419 return B_ERROR; 420 421 fAlert = new MediaAlert(BRect(0, 0, 300, 60), "restart_alert", 422 B_TRANSLATE("Restarting media services\nStarting media server" 423 B_UTF8_ELLIPSIS "\n")); 424 fAlert->Show(); 425 426 Show(); 427 428 launch_media_server(); 429 } 430 431 Lock(); 432 433 bool isVideoSelected = true; 434 if (!first && fListView->ItemAt(0) != NULL 435 && fListView->ItemAt(0)->IsSelected()) 436 isVideoSelected = false; 437 438 if ((!first || (first && err) ) && fAlert) { 439 BAutolock locker(fAlert); 440 if (locker.IsLocked()) 441 fAlert->TextView()->SetText( 442 B_TRANSLATE("Ready for use" B_UTF8_ELLIPSIS)); 443 } 444 445 while (fListView->CountItems() > 0) 446 delete fListView->RemoveItem((int32)0); 447 _EmptyNodeLists(); 448 449 // Grab Media Info 450 _FindNodes(); 451 452 // Add video nodes first. They might have an additional audio 453 // output or input, but still should be listed as video node. 454 _AddNodeItems(fVideoOutputs, MediaListItem::VIDEO_TYPE); 455 _AddNodeItems(fVideoInputs, MediaListItem::VIDEO_TYPE); 456 _AddNodeItems(fAudioOutputs, MediaListItem::AUDIO_TYPE); 457 _AddNodeItems(fAudioInputs, MediaListItem::AUDIO_TYPE); 458 459 fAudioView->AddOutputNodes(fAudioOutputs); 460 fAudioView->AddInputNodes(fAudioInputs); 461 fVideoView->AddOutputNodes(fVideoOutputs); 462 fVideoView->AddInputNodes(fVideoInputs); 463 464 // build our list view 465 DeviceListItem* audio = new DeviceListItem(B_TRANSLATE("Audio settings"), 466 MediaListItem::AUDIO_TYPE); 467 fListView->AddItem(audio); 468 469 MidiListItem* midi = new MidiListItem(B_TRANSLATE("MIDI Settings")); 470 fListView->AddItem(midi); 471 472 MediaListItem* video = new DeviceListItem(B_TRANSLATE("Video settings"), 473 MediaListItem::VIDEO_TYPE); 474 fListView->AddItem(video); 475 476 MediaListItem* mixer = new AudioMixerListItem(B_TRANSLATE("Audio mixer")); 477 fListView->AddItem(mixer); 478 479 fListView->SortItems(&MediaListItem::Compare); 480 _UpdateListViewMinWidth(); 481 482 // Set default nodes for our setting views 483 media_node defaultNode; 484 dormant_node_info nodeInfo; 485 int32 outputID; 486 BString outputName; 487 488 if (roster->GetAudioInput(&defaultNode) == B_OK) { 489 roster->GetDormantNodeFor(defaultNode, &nodeInfo); 490 fAudioView->SetDefaultInput(&nodeInfo); 491 // this causes our listview to be updated as well 492 } 493 494 if (roster->GetAudioOutput(&defaultNode, &outputID, &outputName) == B_OK) { 495 roster->GetDormantNodeFor(defaultNode, &nodeInfo); 496 fAudioView->SetDefaultOutput(&nodeInfo); 497 fAudioView->SetDefaultChannel(outputID); 498 // this causes our listview to be updated as well 499 } 500 501 if (roster->GetVideoInput(&defaultNode) == B_OK) { 502 roster->GetDormantNodeFor(defaultNode, &nodeInfo); 503 fVideoView->SetDefaultInput(&nodeInfo); 504 // this causes our listview to be updated as well 505 } 506 507 if (roster->GetVideoOutput(&defaultNode) == B_OK) { 508 roster->GetDormantNodeFor(defaultNode, &nodeInfo); 509 fVideoView->SetDefaultOutput(&nodeInfo); 510 // this causes our listview to be updated as well 511 } 512 513 if (first) 514 fListView->Select(fListView->IndexOf(mixer)); 515 else if (isVideoSelected) 516 fListView->Select(fListView->IndexOf(video)); 517 else 518 fListView->Select(fListView->IndexOf(audio)); 519 520 if (fAlert != NULL) { 521 snooze(800000); 522 fAlert->PostMessage(B_QUIT_REQUESTED); 523 } 524 fAlert = NULL; 525 526 Unlock(); 527 528 return B_OK; 529 } 530 531 532 void 533 MediaWindow::_FindNodes() 534 { 535 _FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs); 536 _FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_INPUT, fAudioInputs); 537 _FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs); 538 _FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_INPUT, fAudioInputs); 539 _FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs); 540 _FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_INPUT, fVideoInputs); 541 _FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs); 542 _FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_INPUT, fVideoInputs); 543 } 544 545 546 void 547 MediaWindow::_FindNodes(media_type type, uint64 kind, NodeList& into) 548 { 549 dormant_node_info nodeInfo[64]; 550 int32 nodeInfoCount = 64; 551 552 media_format format; 553 media_format* nodeInputFormat = NULL; 554 media_format* nodeOutputFormat = NULL; 555 format.type = type; 556 557 // output nodes must be BBufferConsumers => they have an input format 558 // input nodes must be BBufferProducers => they have an output format 559 if ((kind & B_PHYSICAL_OUTPUT) != 0) 560 nodeInputFormat = &format; 561 else if ((kind & B_PHYSICAL_INPUT) != 0) 562 nodeOutputFormat = &format; 563 else 564 return; 565 566 BMediaRoster* roster = BMediaRoster::Roster(); 567 568 if (roster->GetDormantNodes(nodeInfo, &nodeInfoCount, nodeInputFormat, 569 nodeOutputFormat, NULL, kind) != B_OK) { 570 // TODO: better error reporting! 571 fprintf(stderr, "error\n"); 572 return; 573 } 574 575 for (int32 i = 0; i < nodeInfoCount; i++) { 576 PRINT(("node : %s, media_addon %i, flavor_id %i\n", 577 nodeInfo[i].name, (int)nodeInfo[i].addon, 578 (int)nodeInfo[i].flavor_id)); 579 580 dormant_node_info* info = new dormant_node_info(); 581 strncpy(info->name, nodeInfo[i].name, B_MEDIA_NAME_LENGTH); 582 info->flavor_id = nodeInfo[i].flavor_id; 583 info->addon = nodeInfo[i].addon; 584 into.AddItem(info); 585 } 586 } 587 588 589 void 590 MediaWindow::_AddNodeItems(NodeList& list, MediaListItem::media_type type) 591 { 592 int32 count = list.CountItems(); 593 for (int32 i = 0; i < count; i++) { 594 dormant_node_info* info = list.ItemAt(i); 595 if (_FindNodeListItem(info) == NULL) 596 fListView->AddItem(new NodeListItem(info, type)); 597 } 598 } 599 600 601 void 602 MediaWindow::_EmptyNodeLists() 603 { 604 fAudioOutputs.MakeEmpty(); 605 fAudioInputs.MakeEmpty(); 606 fVideoOutputs.MakeEmpty(); 607 fVideoInputs.MakeEmpty(); 608 } 609 610 611 NodeListItem* 612 MediaWindow::_FindNodeListItem(dormant_node_info* info) 613 { 614 NodeListItem audioItem(info, MediaListItem::AUDIO_TYPE); 615 NodeListItem videoItem(info, MediaListItem::VIDEO_TYPE); 616 617 NodeListItem::Comparator audioComparator(&audioItem); 618 NodeListItem::Comparator videoComparator(&videoItem); 619 620 for (int32 i = 0; i < fListView->CountItems(); i++) { 621 MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i)); 622 item->Accept(audioComparator); 623 if (audioComparator.result == 0) 624 return static_cast<NodeListItem*>(item); 625 626 item->Accept(videoComparator); 627 if (videoComparator.result == 0) 628 return static_cast<NodeListItem*>(item); 629 } 630 return NULL; 631 } 632 633 634 void 635 MediaWindow::_UpdateListViewMinWidth() 636 { 637 float width = 0; 638 for (int32 i = 0; i < fListView->CountItems(); i++) { 639 BListItem* item = fListView->ItemAt(i); 640 width = max_c(width, item->Width()); 641 } 642 fListView->SetExplicitMinSize(BSize(width, B_SIZE_UNSET)); 643 fListView->InvalidateLayout(); 644 } 645 646 647 status_t 648 MediaWindow::_RestartMediaServices(void* data) 649 { 650 MediaWindow* window = (MediaWindow*)data; 651 window->fAlert = new MediaAlert(BRect(0, 0, 300, 60), 652 "restart_alert", B_TRANSLATE( 653 "Restarting media services\nShutting down media server\n")); 654 655 window->fAlert->Show(); 656 657 shutdown_media_server(B_INFINITE_TIMEOUT, MediaWindow::_UpdateProgress, 658 window->fAlert); 659 660 { 661 BAutolock locker(window->fAlert); 662 if (locker.IsLocked()) 663 window->fAlert->TextView()->SetText( 664 B_TRANSLATE("Starting media server" B_UTF8_ELLIPSIS)); 665 } 666 launch_media_server(); 667 668 return window->PostMessage(ML_INIT_MEDIA); 669 } 670 671 672 bool 673 MediaWindow::_UpdateProgress(int stage, const char* message, void* cookie) 674 { 675 MediaAlert* alert = static_cast<MediaAlert*>(cookie); 676 PRINT(("stage : %i\n", stage)); 677 const char* string = "Unknown stage"; 678 switch (stage) { 679 case 10: 680 string = B_TRANSLATE("Stopping media server" B_UTF8_ELLIPSIS); 681 break; 682 case 20: 683 string = B_TRANSLATE("Telling media_addon_server to quit."); 684 break; 685 case 40: 686 string = B_TRANSLATE("Waiting for media_server to quit."); 687 break; 688 case 70: 689 string = B_TRANSLATE("Cleaning up."); 690 break; 691 case 100: 692 string = B_TRANSLATE("Done shutting down."); 693 break; 694 } 695 696 BAutolock locker(alert); 697 if (locker.IsLocked()) 698 alert->TextView()->SetText(string); 699 return true; 700 } 701 702 703 void 704 MediaWindow::_ClearParamView() 705 { 706 BLayoutItem* item = fContentLayout->VisibleItem(); 707 if (!item) 708 return; 709 710 BView* view = item->View(); 711 if (view != fVideoView && view != fAudioView) { 712 fContentLayout->RemoveItem(item); 713 delete item; 714 delete view; 715 delete fParamWeb; 716 fParamWeb = NULL; 717 } 718 } 719 720 721 void 722 MediaWindow::_MakeParamView() 723 { 724 if (!fCurrentNode.IsSet()) 725 return; 726 727 fParamWeb = NULL; 728 BMediaRoster* roster = BMediaRoster::Roster(); 729 if (roster->GetParameterWebFor(fCurrentNode, &fParamWeb) == B_OK) { 730 BRect hint(fContentLayout->Frame()); 731 BView* paramView = BMediaTheme::ViewFor(fParamWeb, &hint); 732 if (paramView) { 733 fContentLayout->AddView(paramView); 734 fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1); 735 return; 736 } 737 } 738 739 _MakeEmptyParamView(); 740 } 741 742 743 void 744 MediaWindow::_MakeEmptyParamView() 745 { 746 fParamWeb = NULL; 747 748 BStringView* stringView = new BStringView("noControls", 749 B_TRANSLATE("This hardware has no controls.")); 750 751 BSize unlimited(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 752 stringView->SetExplicitMaxSize(unlimited); 753 754 BAlignment centered(B_ALIGN_HORIZONTAL_CENTER, 755 B_ALIGN_VERTICAL_CENTER); 756 stringView->SetExplicitAlignment(centered); 757 stringView->SetAlignment(B_ALIGN_CENTER); 758 759 fContentLayout->AddView(stringView); 760 fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1); 761 762 rgb_color panel = stringView->LowColor(); 763 stringView->SetHighColor(tint_color(panel, 764 B_DISABLED_LABEL_TINT)); 765 } 766 767