Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / preferences / media / MediaWindow.cpp
blob30ab05126e971e4460a6e85569a1d50e0eea7a68
1 /*
2 * Copyright 2003-2012, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Sikosis, Jérôme Duval
7 * yourpalal, Alex Wilson
8 */
11 #include "MediaWindow.h"
13 #include <stdio.h>
15 #include <Alert.h>
16 #include <Application.h>
17 #include <Autolock.h>
18 #include <Button.h>
19 #include <CardLayout.h>
20 #include <Catalog.h>
21 #include <Debug.h>
22 #include <Deskbar.h>
23 #include <IconUtils.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>
38 #include "Media.h"
39 #include "MediaIcons.h"
40 #include "MidiSettingsView.h"
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "Media Window"
46 const uint32 ML_SELECTED_NODE = 'MlSN';
47 const uint32 ML_RESTART_THREAD_FINISHED = 'MlRF';
50 class NodeListItemUpdater : public MediaListItem::Visitor {
51 public:
52 typedef void (NodeListItem::*UpdateMethod)(bool);
54 NodeListItemUpdater(NodeListItem* target, UpdateMethod action)
56 fComparator(target),
57 fAction(action)
62 virtual void Visit(AudioMixerListItem*){}
63 virtual void Visit(DeviceListItem*){}
64 virtual void Visit(MidiListItem*){}
65 virtual void Visit(NodeListItem* item)
67 item->Accept(fComparator);
68 (item->*(fAction))(fComparator.result == 0);
71 private:
73 NodeListItem::Comparator fComparator;
74 UpdateMethod fAction;
78 MediaWindow::SmartNode::SmartNode(const BMessenger& notifyHandler)
80 fNode(NULL),
81 fMessenger(notifyHandler)
86 MediaWindow::SmartNode::~SmartNode()
88 _FreeNode();
92 void
93 MediaWindow::SmartNode::SetTo(const dormant_node_info* info)
95 _FreeNode();
96 if (!info)
97 return;
99 fNode = new media_node();
100 BMediaRoster* roster = BMediaRoster::Roster();
102 status_t status = B_OK;
103 media_node_id node_id;
104 if (roster->GetInstancesFor(info->addon, info->flavor_id, &node_id) == B_OK)
105 status = roster->GetNodeFor(node_id, fNode);
106 else
107 status = roster->InstantiateDormantNode(*info, fNode, B_FLAVOR_IS_GLOBAL);
109 if (status != B_OK) {
110 fprintf(stderr, "SmartNode::SetTo error with node %" B_PRId32
111 ": %s\n", fNode->node, strerror(status));
114 status = roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD);
115 if (status != B_OK) {
116 fprintf(stderr, "SmartNode::SetTo can't start watching for"
117 " node %" B_PRId32 "\n", fNode->node);
122 void
123 MediaWindow::SmartNode::SetTo(const media_node& node)
125 _FreeNode();
126 fNode = new media_node(node);
127 BMediaRoster* roster = BMediaRoster::Roster();
128 roster->StartWatching(fMessenger, *fNode, B_MEDIA_WILDCARD);
132 bool
133 MediaWindow::SmartNode::IsSet()
135 return fNode != NULL;
139 MediaWindow::SmartNode::operator media_node()
141 if (fNode)
142 return *fNode;
143 media_node node;
144 return node;
148 void
149 MediaWindow::SmartNode::_FreeNode()
151 if (!IsSet())
152 return;
154 BMediaRoster* roster = BMediaRoster::Roster();
155 if (roster != NULL) {
156 status_t status = roster->StopWatching(fMessenger,
157 *fNode, B_MEDIA_WILDCARD);
158 if (status != B_OK) {
159 fprintf(stderr, "SmartNode::_FreeNode can't unwatch"
160 " media services for node %" B_PRId32 "\n", fNode->node);
163 roster->ReleaseNode(*fNode);
164 if (status != B_OK) {
165 fprintf(stderr, "SmartNode::_FreeNode can't release"
166 " node %" B_PRId32 "\n", fNode->node);
169 delete fNode;
170 fNode = NULL;
174 // #pragma mark -
177 MediaWindow::MediaWindow(BRect frame)
179 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Media"), B_TITLED_WINDOW,
180 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
181 fCurrentNode(BMessenger(this)),
182 fParamWeb(NULL),
183 fAudioInputs(5, true),
184 fAudioOutputs(5, true),
185 fVideoInputs(5, true),
186 fVideoOutputs(5, true),
187 fInitCheck(B_OK),
188 fRestartThread(-1)
190 _InitWindow();
192 BMediaRoster* roster = BMediaRoster::Roster();
193 roster->StartWatching(BMessenger(this, this),
194 B_MEDIA_SERVER_STARTED);
195 roster->StartWatching(BMessenger(this, this),
196 B_MEDIA_SERVER_QUIT);
200 MediaWindow::~MediaWindow()
202 _EmptyNodeLists();
203 _ClearParamView();
205 char buffer[512];
206 BRect rect = Frame();
207 PRINT_OBJECT(rect);
208 snprintf(buffer, 512, "# MediaPrefs Settings\n rect = %i,%i,%i,%i\n",
209 int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
211 BPath path;
212 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
213 path.Append(SETTINGS_FILE);
214 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
215 if (file.InitCheck() == B_OK)
216 file.Write(buffer, strlen(buffer));
219 BMediaRoster* roster = BMediaRoster::CurrentRoster();
220 roster->StopWatching(BMessenger(this, this),
221 B_MEDIA_SERVER_STARTED);
222 roster->StartWatching(BMessenger(this, this),
223 B_MEDIA_SERVER_QUIT);
227 status_t
228 MediaWindow::InitCheck()
230 return fInitCheck;
234 void
235 MediaWindow::SelectNode(const dormant_node_info* node)
237 fCurrentNode.SetTo(node);
238 _MakeParamView();
239 fTitleView->SetLabel(node->name);
243 void
244 MediaWindow::SelectAudioSettings(const char* title)
246 fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fAudioView));
247 fTitleView->SetLabel(title);
251 void
252 MediaWindow::SelectVideoSettings(const char* title)
254 fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fVideoView));
255 fTitleView->SetLabel(title);
259 void
260 MediaWindow::SelectAudioMixer(const char* title)
262 media_node mixerNode;
263 BMediaRoster* roster = BMediaRoster::Roster();
264 roster->GetAudioMixer(&mixerNode);
265 fCurrentNode.SetTo(mixerNode);
266 _MakeParamView();
267 fTitleView->SetLabel(title);
271 void
272 MediaWindow::SelectMidiSettings(const char* title)
274 fContentLayout->SetVisibleItem(fContentLayout->IndexOfView(fMidiView));
275 fTitleView->SetLabel(title);
279 void
280 MediaWindow::UpdateInputListItem(MediaListItem::media_type type,
281 const dormant_node_info* node)
283 NodeListItem compareTo(node, type);
284 NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultInput);
285 for (int32 i = 0; i < fListView->CountItems(); i++) {
286 MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
287 item->Accept(updater);
289 fListView->Invalidate();
293 void
294 MediaWindow::UpdateOutputListItem(MediaListItem::media_type type,
295 const dormant_node_info* node)
297 NodeListItem compareTo(node, type);
298 NodeListItemUpdater updater(&compareTo, &NodeListItem::SetDefaultOutput);
299 for (int32 i = 0; i < fListView->CountItems(); i++) {
300 MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
301 item->Accept(updater);
303 fListView->Invalidate();
307 bool
308 MediaWindow::QuitRequested()
310 status_t exit = B_OK;
311 if (fRestartThread > 0) {
312 wait_for_thread(fRestartThread, &exit);
313 if (exit != B_OK) {
314 fprintf(stderr, "MediaWindow::QuitRequested wait_for_thread"
315 " returned with an error: %s\n", strerror(exit));
318 // Stop watching the MediaRoster
319 fCurrentNode.SetTo(NULL);
320 be_app->PostMessage(B_QUIT_REQUESTED);
321 return true;
325 void
326 MediaWindow::MessageReceived(BMessage* message)
328 switch (message->what) {
329 case ML_RESTART_THREAD_FINISHED:
330 fRestartThread = -1;
331 _InitMedia(false);
332 break;
334 case ML_RESTART_MEDIA_SERVER:
336 fRestartThread = spawn_thread(&MediaWindow::_RestartMediaServices,
337 "restart_thread", B_NORMAL_PRIORITY, this);
338 if (fRestartThread < 0)
339 fprintf(stderr, "couldn't create restart thread\n");
340 else
341 resume_thread(fRestartThread);
342 break;
345 case B_MEDIA_WEB_CHANGED:
346 case ML_SELECTED_NODE:
348 PRINT_OBJECT(*message);
350 MediaListItem* item = static_cast<MediaListItem*>(
351 fListView->ItemAt(fListView->CurrentSelection()));
352 if (item == NULL)
353 break;
355 fCurrentNode.SetTo(NULL);
356 _ClearParamView();
357 item->AlterWindow(this);
358 break;
361 case B_MEDIA_SERVER_STARTED:
362 case B_MEDIA_SERVER_QUIT:
364 PRINT_OBJECT(*message);
365 _InitMedia(false);
366 break;
369 default:
370 BWindow::MessageReceived(message);
371 break;
376 // #pragma mark - private
379 void
380 MediaWindow::_InitWindow()
382 fListView = new BListView("media_list_view");
383 fListView->SetSelectionMessage(new BMessage(ML_SELECTED_NODE));
384 fListView->SetExplicitMinSize(BSize(140, B_SIZE_UNSET));
386 // Add ScrollView to Media Menu for pretty border
387 BScrollView* scrollView = new BScrollView("listscroller",
388 fListView, 0, false, false, B_FANCY_BORDER);
390 // Create the Views
391 fTitleView = new BSeparatorView(B_HORIZONTAL, B_FANCY_BORDER);
392 fTitleView->SetLabel(B_TRANSLATE("Audio settings"));
393 fTitleView->SetFont(be_bold_font);
395 fContentLayout = new BCardLayout();
396 new BView("content view", 0, fContentLayout);
397 fContentLayout->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
398 fContentLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
400 fAudioView = new AudioSettingsView();
401 fContentLayout->AddView(fAudioView);
403 fVideoView = new VideoSettingsView();
404 fContentLayout->AddView(fVideoView);
406 fMidiView = new MidiSettingsView();
407 fContentLayout->AddView(fMidiView);
409 // Layout all views
410 BLayoutBuilder::Group<>(this, B_HORIZONTAL)
411 .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
412 B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
413 .Add(scrollView, 0.0f)
414 .AddGroup(B_VERTICAL)
415 .SetInsets(0, 0, 0, 0)
416 .Add(fTitleView)
417 .Add(fContentLayout);
419 // Start the window
420 fInitCheck = _InitMedia(true);
421 if (fInitCheck != B_OK)
422 PostMessage(B_QUIT_REQUESTED);
423 else if (IsHidden())
424 Show();
428 status_t
429 MediaWindow::_InitMedia(bool first)
431 status_t err = B_OK;
432 BMediaRoster* roster = BMediaRoster::Roster(&err);
434 if (first && err != B_OK) {
435 BAlert* alert = new BAlert("start_media_server",
436 B_TRANSLATE("Could not connect to the media server.\n"
437 "Would you like to start it ?"),
438 B_TRANSLATE("Quit"),
439 B_TRANSLATE("Start media server"), NULL,
440 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
441 alert->SetShortcut(0, B_ESCAPE);
442 if (alert->Go() == 0)
443 return B_ERROR;
445 Show();
447 launch_media_server();
450 Lock();
452 bool isVideoSelected = true;
453 if (!first && fListView->ItemAt(0) != NULL
454 && fListView->ItemAt(0)->IsSelected())
455 isVideoSelected = false;
457 while (fListView->CountItems() > 0)
458 delete fListView->RemoveItem((int32)0);
459 _EmptyNodeLists();
461 // Grab Media Info
462 _FindNodes();
464 // Add video nodes first. They might have an additional audio
465 // output or input, but still should be listed as video node.
466 _AddNodeItems(fVideoOutputs, MediaListItem::VIDEO_TYPE);
467 _AddNodeItems(fVideoInputs, MediaListItem::VIDEO_TYPE);
468 _AddNodeItems(fAudioOutputs, MediaListItem::AUDIO_TYPE);
469 _AddNodeItems(fAudioInputs, MediaListItem::AUDIO_TYPE);
471 fAudioView->AddOutputNodes(fAudioOutputs);
472 fAudioView->AddInputNodes(fAudioInputs);
473 fVideoView->AddOutputNodes(fVideoOutputs);
474 fVideoView->AddInputNodes(fVideoInputs);
476 // build our list view
477 DeviceListItem* audio = new DeviceListItem(B_TRANSLATE("Audio settings"),
478 MediaListItem::AUDIO_TYPE);
479 fListView->AddItem(audio);
481 MidiListItem* midi = new MidiListItem(B_TRANSLATE("MIDI Settings"));
482 fListView->AddItem(midi);
484 MediaListItem* video = new DeviceListItem(B_TRANSLATE("Video settings"),
485 MediaListItem::VIDEO_TYPE);
486 fListView->AddItem(video);
488 MediaListItem* mixer = new AudioMixerListItem(B_TRANSLATE("Audio mixer"));
489 fListView->AddItem(mixer);
491 fListView->SortItems(&MediaListItem::Compare);
492 _UpdateListViewMinWidth();
494 // Set default nodes for our setting views
495 media_node defaultNode;
496 dormant_node_info nodeInfo;
497 int32 outputID;
498 BString outputName;
500 if (roster->GetAudioInput(&defaultNode) == B_OK) {
501 roster->GetDormantNodeFor(defaultNode, &nodeInfo);
502 fAudioView->SetDefaultInput(&nodeInfo);
503 // this causes our listview to be updated as well
506 if (roster->GetAudioOutput(&defaultNode, &outputID, &outputName) == B_OK) {
507 roster->GetDormantNodeFor(defaultNode, &nodeInfo);
508 fAudioView->SetDefaultOutput(&nodeInfo);
509 fAudioView->SetDefaultChannel(outputID);
510 // this causes our listview to be updated as well
513 if (roster->GetVideoInput(&defaultNode) == B_OK) {
514 roster->GetDormantNodeFor(defaultNode, &nodeInfo);
515 fVideoView->SetDefaultInput(&nodeInfo);
516 // this causes our listview to be updated as well
519 if (roster->GetVideoOutput(&defaultNode) == B_OK) {
520 roster->GetDormantNodeFor(defaultNode, &nodeInfo);
521 fVideoView->SetDefaultOutput(&nodeInfo);
522 // this causes our listview to be updated as well
525 if (first)
526 fListView->Select(fListView->IndexOf(mixer));
527 else if (isVideoSelected)
528 fListView->Select(fListView->IndexOf(video));
529 else
530 fListView->Select(fListView->IndexOf(audio));
532 Unlock();
534 return B_OK;
538 void
539 MediaWindow::_FindNodes()
541 _FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
542 _FindNodes(B_MEDIA_RAW_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
543 _FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_OUTPUT, fAudioOutputs);
544 _FindNodes(B_MEDIA_ENCODED_AUDIO, B_PHYSICAL_INPUT, fAudioInputs);
545 _FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
546 _FindNodes(B_MEDIA_RAW_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
547 _FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_OUTPUT, fVideoOutputs);
548 _FindNodes(B_MEDIA_ENCODED_VIDEO, B_PHYSICAL_INPUT, fVideoInputs);
552 void
553 MediaWindow::_FindNodes(media_type type, uint64 kind, NodeList& into)
555 dormant_node_info nodeInfo[64];
556 int32 nodeInfoCount = 64;
558 media_format format;
559 media_format* nodeInputFormat = NULL;
560 media_format* nodeOutputFormat = NULL;
561 format.type = type;
563 // output nodes must be BBufferConsumers => they have an input format
564 // input nodes must be BBufferProducers => they have an output format
565 if ((kind & B_PHYSICAL_OUTPUT) != 0)
566 nodeInputFormat = &format;
567 else if ((kind & B_PHYSICAL_INPUT) != 0)
568 nodeOutputFormat = &format;
569 else
570 return;
572 BMediaRoster* roster = BMediaRoster::Roster();
574 if (roster->GetDormantNodes(nodeInfo, &nodeInfoCount, nodeInputFormat,
575 nodeOutputFormat, NULL, kind) != B_OK) {
576 // TODO: better error reporting!
577 fprintf(stderr, "error\n");
578 return;
581 for (int32 i = 0; i < nodeInfoCount; i++) {
582 PRINT(("node : %s, media_addon %i, flavor_id %i\n",
583 nodeInfo[i].name, (int)nodeInfo[i].addon,
584 (int)nodeInfo[i].flavor_id));
586 dormant_node_info* info = new dormant_node_info();
587 strncpy(info->name, nodeInfo[i].name, B_MEDIA_NAME_LENGTH);
588 info->flavor_id = nodeInfo[i].flavor_id;
589 info->addon = nodeInfo[i].addon;
590 into.AddItem(info);
595 void
596 MediaWindow::_AddNodeItems(NodeList& list, MediaListItem::media_type type)
598 int32 count = list.CountItems();
599 for (int32 i = 0; i < count; i++) {
600 dormant_node_info* info = list.ItemAt(i);
601 if (_FindNodeListItem(info) == NULL)
602 fListView->AddItem(new NodeListItem(info, type));
607 void
608 MediaWindow::_EmptyNodeLists()
610 fAudioOutputs.MakeEmpty();
611 fAudioInputs.MakeEmpty();
612 fVideoOutputs.MakeEmpty();
613 fVideoInputs.MakeEmpty();
617 NodeListItem*
618 MediaWindow::_FindNodeListItem(dormant_node_info* info)
620 NodeListItem audioItem(info, MediaListItem::AUDIO_TYPE);
621 NodeListItem videoItem(info, MediaListItem::VIDEO_TYPE);
623 NodeListItem::Comparator audioComparator(&audioItem);
624 NodeListItem::Comparator videoComparator(&videoItem);
626 for (int32 i = 0; i < fListView->CountItems(); i++) {
627 MediaListItem* item = static_cast<MediaListItem*>(fListView->ItemAt(i));
628 item->Accept(audioComparator);
629 if (audioComparator.result == 0)
630 return static_cast<NodeListItem*>(item);
632 item->Accept(videoComparator);
633 if (videoComparator.result == 0)
634 return static_cast<NodeListItem*>(item);
636 return NULL;
640 void
641 MediaWindow::_UpdateListViewMinWidth()
643 float width = 0;
644 for (int32 i = 0; i < fListView->CountItems(); i++) {
645 BListItem* item = fListView->ItemAt(i);
646 width = max_c(width, item->Width());
648 fListView->SetExplicitMinSize(BSize(width, B_SIZE_UNSET));
649 fListView->InvalidateLayout();
653 status_t
654 MediaWindow::_RestartMediaServices(void* data)
656 MediaWindow* window = (MediaWindow*)data;
658 shutdown_media_server();
659 launch_media_server();
661 return window->PostMessage(ML_RESTART_THREAD_FINISHED);
665 void
666 MediaWindow::_ClearParamView()
668 BLayoutItem* item = fContentLayout->VisibleItem();
669 if (!item)
670 return;
672 BView* view = item->View();
673 if (view != fVideoView && view != fAudioView && view != fMidiView) {
674 fContentLayout->RemoveItem(item);
675 delete item;
676 delete view;
677 delete fParamWeb;
678 fParamWeb = NULL;
683 void
684 MediaWindow::_MakeParamView()
686 if (!fCurrentNode.IsSet())
687 return;
689 fParamWeb = NULL;
690 BMediaRoster* roster = BMediaRoster::Roster();
691 if (roster->GetParameterWebFor(fCurrentNode, &fParamWeb) == B_OK) {
692 BRect hint(fContentLayout->Frame());
693 BView* paramView = BMediaTheme::ViewFor(fParamWeb, &hint);
694 if (paramView) {
695 fContentLayout->AddView(paramView);
696 fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
697 return;
701 _MakeEmptyParamView();
705 void
706 MediaWindow::_MakeEmptyParamView()
708 fParamWeb = NULL;
710 BStringView* stringView = new BStringView("noControls",
711 B_TRANSLATE("This hardware has no controls."));
713 BSize unlimited(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
714 stringView->SetExplicitMaxSize(unlimited);
716 BAlignment centered(B_ALIGN_HORIZONTAL_CENTER,
717 B_ALIGN_VERTICAL_CENTER);
718 stringView->SetExplicitAlignment(centered);
719 stringView->SetAlignment(B_ALIGN_CENTER);
721 fContentLayout->AddView(stringView);
722 fContentLayout->SetVisibleItem(fContentLayout->CountItems() - 1);
724 rgb_color panel = stringView->LowColor();
725 stringView->SetHighColor(tint_color(panel,
726 B_DISABLED_LABEL_TINT));