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