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