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