xref: /haiku/src/apps/cortex/TransportView/TransportView.cpp (revision 3cb015b1ee509d69c643506e8ff573808c86dcfc)
1 // TransportView.cpp
2 
3 #include "TransportView.h"
4 
5 #include "RouteApp.h"
6 #include "RouteWindow.h"
7 
8 #include "RouteAppNodeManager.h"
9 #include "NodeGroup.h"
10 
11 #include "NumericValControl.h"
12 #include "TextControlFloater.h"
13 
14 #include <Button.h>
15 #include <Debug.h>
16 #include <Font.h>
17 #include <Invoker.h>
18 #include <StringView.h>
19 #include <MediaRoster.h>
20 #include <MenuField.h>
21 #include <MenuItem.h>
22 #include <PopUpMenu.h>
23 #include <String.h>
24 #include <TextControl.h>
25 
26 #include <algorithm>
27 #include <functional>
28 
29 using namespace std;
30 
31 __USE_CORTEX_NAMESPACE
32 
33 // -------------------------------------------------------- //
34 // _GroupInfoView
35 // -------------------------------------------------------- //
36 
37 __BEGIN_CORTEX_NAMESPACE
38 class _GroupInfoView :
39 	public	BView {
40 	typedef	BView _inherited;
41 
42 public:												// ctor/dtor
43 	_GroupInfoView(
44 		BRect											frame,
45 		TransportView*						parent,
46 		const char*								name,
47 		uint32										resizeMode =B_FOLLOW_LEFT|B_FOLLOW_TOP,
48 		uint32										flags =B_WILL_DRAW|B_FRAME_EVENTS) :
49 
50 		BView(frame, name, resizeMode, flags),
51 		m_parent(parent),
52 		m_plainFont(be_plain_font),
53 		m_boldFont(be_bold_font) {
54 
55 		_initViews();
56 		_initColors();
57 		_updateLayout();
58 	}
59 
60 public:												// BView
61 	virtual void FrameResized(
62 		float											width,
63 		float											height) {
64 
65 		_inherited::FrameResized(width, height);
66 		_updateLayout();
67 		Invalidate();
68 	}
69 
70 	virtual void GetPreferredSize(
71 		float*										width,
72 		float*										height) {
73 		font_height fh;
74 		m_plainFont.GetHeight(&fh);
75 
76 		*width = 0.0;
77 		*height = (fh.ascent+fh.descent+fh.leading) * 2;
78 		*height += 4.0; //+++++
79 	}
80 
81 
82 	virtual void Draw(
83 		BRect											updateRect) {
84 
85 		NodeGroup* g = m_parent->m_group;
86 		BRect b = Bounds();
87 
88 		// border
89 		rgb_color hi = tint_color(ViewColor(), B_LIGHTEN_2_TINT);
90 		rgb_color lo = tint_color(ViewColor(), B_DARKEN_2_TINT);
91 		SetHighColor(lo);
92 		StrokeLine(
93 			b.LeftTop(), b.RightTop());
94 		StrokeLine(
95 			b.LeftTop(), b.LeftBottom());
96 
97 		SetHighColor(hi);
98 		StrokeLine(
99 			b.LeftBottom(), b.RightBottom());
100 		StrokeLine(
101 			b.RightTop(), b.RightBottom());
102 
103 		SetHighColor(255,255,255,255);
104 
105 		// background +++++
106 
107 		// name
108 		BString name = g ? g->name() : "(no group)";
109 		// +++++ constrain width
110 		SetFont(&m_boldFont);
111 		DrawString(name.String(), m_namePosition);
112 
113 		SetFont(&m_plainFont);
114 
115 		// node count
116 		BString nodeCount;
117 		if(g)
118 			nodeCount << g->countNodes();
119 		else
120 			nodeCount << '0';
121 		nodeCount << ((nodeCount == "1") ? " node." : " nodes.");
122 		// +++++ constrain width
123 		DrawString(nodeCount.String(), m_nodeCountPosition);
124 
125 		// status
126 		BString status = "No errors.";
127 		// +++++ constrain width
128 		DrawString(status.String(), m_statusPosition);
129 	}
130 
131 	virtual void MouseDown(
132 		BPoint										point) {
133 
134 		NodeGroup* g = m_parent->m_group;
135 		if(!g)
136 			return;
137 
138 		font_height fh;
139 		m_boldFont.GetHeight(&fh);
140 
141 		BRect nameBounds(
142 			m_namePosition.x,
143 			m_namePosition.y - fh.ascent,
144 			m_namePosition.x + m_maxNameWidth,
145 			m_namePosition.y + (fh.ascent+fh.leading-4.0));
146 		if(nameBounds.Contains(point)) {
147 			ConvertToScreen(&nameBounds);
148 			nameBounds.OffsetBy(-7.0, -3.0);
149 			new TextControlFloater(
150 				nameBounds,
151 				B_ALIGN_LEFT,
152 				&m_boldFont,
153 				g->name(),
154 				m_parent,
155 				new BMessage(TransportView::M_SET_NAME));
156 		}
157 	}
158 
159 public:												// implementation
160 	void _initViews() {
161 		// +++++
162 	}
163 
164 	void _initColors() {
165 		// +++++ these colors need to be centrally defined
166 		SetViewColor(16, 64, 96, 255);
167 		SetLowColor(16, 64, 96, 255);
168 		SetHighColor(255,255,255,255);
169 	}
170 
171 	void _updateLayout() {
172 		float _edge_pad_x = 3.0;
173 		float _edge_pad_y = 1.0;
174 
175 		BRect b = Bounds();
176 		font_height fh;
177 		m_plainFont.GetHeight(&fh);
178 
179 		float realWidth = b.Width() - (_edge_pad_x * 2);
180 
181 		m_maxNameWidth = realWidth * 0.7;
182 		m_maxNodeCountWidth = realWidth - m_maxNameWidth;
183 		m_namePosition.x = _edge_pad_x;
184 		m_namePosition.y = _edge_pad_x + fh.ascent - 2.0;
185 		m_nodeCountPosition = m_namePosition;
186 		m_nodeCountPosition.x = m_maxNameWidth;
187 
188 		m_maxStatusWidth = realWidth;
189 		m_statusPosition.x = _edge_pad_x;
190 		m_statusPosition.y = b.Height() - (fh.descent + fh.leading + _edge_pad_y);
191 	}
192 
193 private:
194 	TransportView*							m_parent;
195 
196 	BFont												m_plainFont;
197 	BFont												m_boldFont;
198 
199 	BPoint											m_namePosition;
200 	float												m_maxNameWidth;
201 
202 	BPoint											m_nodeCountPosition;
203 	float												m_maxNodeCountWidth;
204 
205 	BPoint											m_statusPosition;
206 	float												m_maxStatusWidth;
207 };
208 __END_CORTEX_NAMESPACE
209 
210 // -------------------------------------------------------- //
211 // *** ctors
212 // -------------------------------------------------------- //
213 
214 TransportView::TransportView(
215 	NodeManager*						manager,
216 	const char*							name) :
217 
218 	BView(
219 		BRect(),
220 		name,
221 		B_FOLLOW_ALL_SIDES,
222 		B_WILL_DRAW|B_FRAME_EVENTS),
223 	m_manager(manager),
224 	m_group(0) {
225 
226 	// initialize
227 	_initLayout();
228 	_constructControls();
229 //	_updateLayout(); deferred until AttachedToWindow(): 24aug99
230 	_disableControls();
231 
232 	SetViewColor(
233 		tint_color(
234 			ui_color(B_PANEL_BACKGROUND_COLOR),
235 			B_LIGHTEN_1_TINT));
236 }
237 
238 // -------------------------------------------------------- //
239 // *** BView
240 // -------------------------------------------------------- //
241 
242 void TransportView::AttachedToWindow() {
243 	_inherited::AttachedToWindow();
244 
245 	// finish layout
246 	_updateLayout();
247 
248 	// watch the node manager (for time-source create/delete notification)
249 	RouteApp* app = dynamic_cast<RouteApp*>(be_app);
250 	ASSERT(app);
251 	add_observer(this, app->manager);
252 }
253 
254 void TransportView::AllAttached() {
255 	_inherited::AllAttached();
256 
257 	// set message targets for view-configuation controls
258 	for(target_set::iterator it = m_localTargets.begin();
259 		it != m_localTargets.end(); ++it) {
260 		ASSERT(*it);
261 		(*it)->SetTarget(this);
262 	}
263 }
264 
265 void TransportView::DetachedFromWindow() {
266 	_inherited::DetachedFromWindow();
267 
268 	RouteApp* app = dynamic_cast<RouteApp*>(be_app);
269 	ASSERT(app);
270 	remove_observer(this, app->manager);
271 }
272 
273 void TransportView::FrameResized(
274 	float										width,
275 	float										height) {
276 
277 	_inherited::FrameResized(width, height);
278 //	_updateLayout();
279 }
280 
281 void TransportView::KeyDown(
282 	const char*							bytes,
283 	int32										count) {
284 
285 	switch(bytes[0]) {
286 		case B_SPACE: {
287 			RouteApp* app = dynamic_cast<RouteApp*>(be_app);
288 			ASSERT(app);
289 			BMessenger(app->routeWindow).SendMessage(
290 				RouteWindow::M_TOGGLE_GROUP_ROLLING);
291 			break;
292 		}
293 
294 		default:
295 			_inherited::KeyDown(bytes, count);
296 	}
297 }
298 
299 
300 void TransportView::MouseDown(
301 	BPoint									where) {
302 
303 	MakeFocus(true);
304 }
305 
306 
307 // -------------------------------------------------------- //
308 // *** BHandler
309 // -------------------------------------------------------- //
310 
311 void TransportView::MessageReceived(
312 	BMessage*								message) {
313 	status_t err;
314 	uint32 groupID;
315 
316 //	PRINT((
317 //		"TransportView::MessageReceived()\n"));
318 //	message->PrintToStream();
319 
320 	switch(message->what) {
321 
322 		case NodeGroup::M_RELEASED:
323 			{
324 				err = message->FindInt32("groupID", (int32*)&groupID);
325 				if(err < B_OK) {
326 					PRINT((
327 						"* TransportView::MessageReceived(NodeGroup::M_RELEASED)\n"
328 						"  no groupID!\n"));
329 				}
330 				if(!m_group || groupID != m_group->id()) {
331 					PRINT((
332 						"* TransportView::MessageReceived(NodeGroup::M_RELEASED)\n"
333 						"  mismatched groupID.\n"));
334 					break;
335 				}
336 
337 				_releaseGroup();
338 //
339 //				BMessage m(M_REMOVE_OBSERVER);
340 //				m.AddMessenger("observer", BMessenger(this));
341 //				message->SendReply(&m);
342 			}
343 			break;
344 
345 		case NodeGroup::M_OBSERVER_ADDED:
346 			err = message->FindInt32("groupID", (int32*)&groupID);
347 			if(err < B_OK) {
348 				PRINT((
349 					"* TransportView::MessageReceived(NodeGroup::M_OBSERVER_ADDED)\n"
350 					"  no groupID!\n"));
351 				break;
352 			}
353 			if(!m_group || groupID != m_group->id()) {
354 				PRINT((
355 					"* TransportView::MessageReceived(NodeGroup::M_OBSERVER_ADDED)\n"
356 					"  mismatched groupID; ignoring.\n"));
357 				break;
358 			}
359 
360 			_enableControls();
361 			break;
362 
363 		case NodeGroup::M_TRANSPORT_STATE_CHANGED:
364 			uint32 groupID;
365 			err = message->FindInt32("groupID", (int32*)&groupID);
366 			if(err < B_OK) {
367 				PRINT((
368 					"* TransportView::MessageReceived(NodeGroup::M_TRANSPORT_STATE_CHANGED)\n"
369 					"  no groupID!\n"));
370 				break;
371 			}
372 			if(!m_group || groupID != m_group->id()) {
373 				PRINT((
374 					"* TransportView::MessageReceived(NodeGroup::M_TRANSPORT_STATE_CHANGED)\n"
375 					"  mismatched groupID; ignoring.\n"));
376 				break;
377 			}
378 
379 			_updateTransportButtons();
380 			break;
381 
382 		case NodeGroup::M_TIME_SOURCE_CHANGED:
383 			//_updateTimeSource(); +++++ check group?
384 			break;
385 
386 		case NodeGroup::M_RUN_MODE_CHANGED:
387 			//_updateRunMode(); +++++ check group?
388 			break;
389 
390 		// * CONTROL PROCESSING
391 
392 		case NodeGroup::M_SET_START_POSITION: {
393 
394 			if(!m_group)
395 				break;
396 
397 			bigtime_t position = _scalePosition(
398 				m_regionStartView->value());
399 			message->AddInt64("position", position);
400 			BMessenger(m_group).SendMessage(message);
401 
402 			// update start-button duty/label [e.moon 11oct99]
403 			if(m_group->transportState() == NodeGroup::TRANSPORT_STOPPED)
404 				_updateTransportButtons();
405 
406 			break;
407 		}
408 
409 		case NodeGroup::M_SET_END_POSITION: {
410 
411 			if(!m_group)
412 				break;
413 
414 			bigtime_t position = _scalePosition(
415 				m_regionEndView->value());
416 			message->AddInt64("position", position);
417 			BMessenger(m_group).SendMessage(message);
418 
419 			// update start-button duty/label [e.moon 11oct99]
420 			if(m_group->transportState() == NodeGroup::TRANSPORT_STOPPED)
421 				_updateTransportButtons();
422 
423 			break;
424 		}
425 
426 		case M_SET_NAME:
427 			{
428 				const char* name;
429 				err = message->FindString("_value", &name);
430 				if(err < B_OK) {
431 					PRINT((
432 						"! TransportView::MessageReceived(M_SET_NAME): no _value!\n"));
433 					break;
434 				}
435 				if(m_group) {
436 					m_group->setName(name);
437 					m_infoView->Invalidate();
438 				}
439 			}
440 			break;
441 
442 		case RouteAppNodeManager::M_TIME_SOURCE_CREATED:
443 			_timeSourceCreated(message);
444 			break;
445 
446 		case RouteAppNodeManager::M_TIME_SOURCE_DELETED:
447 			_timeSourceDeleted(message);
448 			break;
449 
450 		default:
451 			_inherited::MessageReceived(message);
452 			break;
453 	}
454 }
455 
456 // -------------------------------------------------------- //
457 // *** BHandler impl.
458 // -------------------------------------------------------- //
459 
460 void TransportView::_handleSelectGroup(
461 	BMessage*								message) {
462 
463 	uint32 groupID;
464 	status_t err = message->FindInt32("groupID", (int32*)&groupID);
465 	if(err < B_OK) {
466 		PRINT((
467 			"* TransportView::_handleSelectGroup(): no groupID\n"));
468 		return;
469 	}
470 
471 	if(m_group && groupID != m_group->id())
472 		_releaseGroup();
473 
474 	_selectGroup(groupID);
475 
476 	Invalidate();
477 }
478 
479 // -------------------------------------------------------- //
480 // *** internal operations
481 // -------------------------------------------------------- //
482 
483 // select the given group; initialize controls
484 // (if 0, gray out all controls)
485 void TransportView::_selectGroup(
486 	uint32									groupID) {
487 	status_t err;
488 
489 	if(m_group)
490 		_releaseGroup();
491 
492 	if(!groupID) {
493 		_disableControls();
494 		return;
495 	}
496 
497 	err = m_manager->findGroup(groupID, &m_group);
498 	if(err < B_OK) {
499 		PRINT((
500 			"* TransportView::_selectGroup(%ld): findGroup() failed:\n"
501 			"  %s\n",
502 			groupID,
503 			strerror(err)));
504 		return;
505 	}
506 
507 	_observeGroup();
508 }
509 
510 void TransportView::_observeGroup() {
511 	ASSERT(m_group);
512 
513 	add_observer(this, m_group);
514 }
515 
516 void TransportView::_releaseGroup() {
517 	ASSERT(m_group);
518 
519 	remove_observer(this, m_group);
520 	m_group = 0;
521 }
522 // -------------------------------------------------------- //
523 // *** CONTROLS
524 // -------------------------------------------------------- //
525 
526 const char _run_mode_strings[][20] = {
527 	"Offline",
528 	"Decrease Precision",
529 	"Increase Latency",
530 	"Drop Data",
531 	"Recording"
532 };
533 const int _run_modes = 5;
534 
535 //const char _time_source_strings[][20] = {
536 //	"DAC Time Source",
537 //	"System Clock"
538 //};
539 //const int _time_sources = 2;
540 
541 const char* _region_start_label = "From:";
542 const char* _region_end_label = "To:";
543 
544 void TransportView::_constructControls() {
545 
546 	BMessage* m;
547 
548 	// * create and populate, but don't position, the views:
549 
550 //	// create and populate, but don't position, the views:
551 //	m_nameView = new BStringView(
552 //		BRect(),
553 //		"nameView",
554 //		"Group Name",
555 //		B_FOLLOW_NONE);
556 //	AddChild(m_nameView);
557 
558 	m_infoView = new _GroupInfoView(
559 		BRect(),
560 		this,
561 		"infoView");
562 	AddChild(m_infoView);
563 
564 	m_runModeView = new BMenuField(
565 		BRect(),
566 		"runModeView",
567 		"Run Mode:",
568 		new BPopUpMenu("runModeMenu"));
569 	_populateRunModeMenu(
570 		m_runModeView->Menu());
571 	AddChild(m_runModeView);
572 
573 	m_timeSourceView = new BMenuField(
574 		BRect(),
575 		"timeSourceView",
576 		"Time Source:",
577 		new BPopUpMenu("timeSourceMenu"));
578 	_populateTimeSourceMenu(
579 		m_timeSourceView->Menu());
580 	AddChild(m_timeSourceView);
581 
582 
583 	m_fromLabel = new BStringView(BRect(), 0, "Roll from");
584 	AddChild(m_fromLabel);
585 
586 
587 	m = new BMessage(NodeGroup::M_SET_START_POSITION);
588 	m_regionStartView = new NumericValControl(
589 		BRect(),
590 		"regionStartView",
591 		m,
592 		2, 4, // * DIGITS
593 		false, ValControl::ALIGN_FLUSH_LEFT);
594 
595 	// redirect view back to self.  this gives me the chance to
596 	// augment the message with the position before sending
597 	_addLocalTarget(m_regionStartView);
598 	AddChild(m_regionStartView);
599 
600 	m_toLabel = new BStringView(BRect(), 0, "to");
601 	AddChild(m_toLabel);
602 
603 	m = new BMessage(NodeGroup::M_SET_END_POSITION);
604 	m_regionEndView = new NumericValControl(
605 		BRect(),
606 		"regionEndView",
607 		m,
608 		2, 4, // * DIGITS
609 		false, ValControl::ALIGN_FLUSH_LEFT);
610 
611 	// redirect view back to self.  this gives me the chance to
612 	// augment the message with the position before sending
613 	_addLocalTarget(m_regionEndView);
614 	AddChild(m_regionEndView);
615 
616 //	m = new BMessage(NodeGroup::M_SET_START_POSITION);
617 //	m_regionStartView = new BTextControl(
618 //		BRect(),
619 //		"regionStartView",
620 //		_region_start_label,
621 //		"0",
622 //		m);
623 //
624 //	_addGroupTarget(m_regionStartView);
625 ////	m_regionStartView->TextView()->SetAlignment(B_ALIGN_RIGHT);
626 //
627 //	AddChild(m_regionStartView);
628 //
629 //	m = new BMessage(NodeGroup::M_SET_END_POSITION);
630 //	m_regionEndView = new BTextControl(
631 //		BRect(),
632 //		"regionEndView",
633 //		_region_end_label,
634 //		"0",
635 //		m);
636 //
637 //	_addGroupTarget(m_regionEndView);
638 ////	m_regionEndView->TextView()->SetAlignment(B_ALIGN_RIGHT);
639 //	AddChild(m_regionEndView);
640 
641 	m = new BMessage(NodeGroup::M_START);
642 	m_startButton = new BButton(
643 		BRect(),
644 		"startButton",
645 		"Start",
646 		m);
647 	_addGroupTarget(m_startButton);
648 	AddChild(m_startButton);
649 
650 	m = new BMessage(NodeGroup::M_STOP);
651 	m_stopButton = new BButton(
652 		BRect(),
653 		"stopButton",
654 		"Stop",
655 		m);
656 	_addGroupTarget(m_stopButton);
657 	AddChild(m_stopButton);
658 
659 	m = new BMessage(NodeGroup::M_PREROLL);
660 	m_prerollButton = new BButton(
661 		BRect(),
662 		"prerollButton",
663 		"Preroll",
664 		m);
665 	_addGroupTarget(m_prerollButton);
666 	AddChild(m_prerollButton);
667 }
668 
669 // convert a position control's value to bigtime_t
670 // [e.moon 11oct99]
671 bigtime_t TransportView::_scalePosition(
672 	double									value) {
673 
674 	return bigtime_t(value * 1000000.0);
675 }
676 
677 void TransportView::_populateRunModeMenu(
678 	BMenu*									menu) {
679 
680 	BMessage* m;
681 	for(int n = 0; n < _run_modes; ++n) {
682 		m = new BMessage(NodeGroup::M_SET_RUN_MODE);
683 		m->AddInt32("runMode", n+1);
684 
685 		BMenuItem* i = new BMenuItem(
686 			_run_mode_strings[n], m);
687 		menu->AddItem(i);
688 		_addGroupTarget(i);
689 	}
690 }
691 
692 void TransportView::_populateTimeSourceMenu(
693 	BMenu*									menu) {
694 
695 	// find the standard time sources
696 	status_t err;
697 	media_node dacTimeSource, systemTimeSource;
698 	BMessage* m;
699 	BMenuItem* i;
700 	err = m_manager->roster->GetTimeSource(&dacTimeSource);
701 	if(err == B_OK) {
702 		m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
703 		m->AddData(
704 			"timeSourceNode",
705 			B_RAW_TYPE,
706 			&dacTimeSource,
707 			sizeof(media_node));
708 		i = new BMenuItem(
709 			"DAC Time Source",
710 			m);
711 		menu->AddItem(i);
712 		_addGroupTarget(i);
713 	}
714 
715 	err = m_manager->roster->GetSystemTimeSource(&systemTimeSource);
716 	if(err == B_OK) {
717 		m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
718 		m->AddData(
719 			"timeSourceNode",
720 			B_RAW_TYPE,
721 			&systemTimeSource,
722 			sizeof(media_node));
723 		i = new BMenuItem(
724 			"System Clock",
725 			m);
726 		menu->AddItem(i);
727 		_addGroupTarget(i);
728 	}
729 
730 	// find other time source nodes
731 
732 	Autolock _l(m_manager);
733 	void* cookie = 0;
734 	NodeRef* r;
735 	char nameBuffer[B_MEDIA_NAME_LENGTH+16];
736 	bool needSeparator = (menu->CountItems() > 0);
737 	while(m_manager->getNextRef(&r, &cookie) == B_OK) {
738 		if(!(r->kind() & B_TIME_SOURCE))
739 			continue;
740 		if(r->id() == dacTimeSource.node)
741 			continue;
742 		if(r->id() == systemTimeSource.node)
743 			continue;
744 
745 		if(needSeparator) {
746 			needSeparator = false;
747 			menu->AddItem(new BSeparatorItem());
748 		}
749 
750 		m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
751 		m->AddData(
752 			"timeSourceNode",
753 			B_RAW_TYPE,
754 			&r->node(),
755 			sizeof(media_node));
756 		sprintf(nameBuffer, "%s: %ld",
757 			r->name(),
758 			r->id());
759 		i = new BMenuItem(
760 			nameBuffer,
761 			m);
762 		menu->AddItem(i);
763 		_addGroupTarget(i);
764 	}
765 
766 //	BMessage* m;
767 //	for(int n = 0; n < _time_sources; ++n) {
768 //		m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
769 //		m->AddData(
770 //			"timeSourceNode",
771 //			B_RAW_TYPE,
772 //			&m_timeSourcePresets[n],
773 //			sizeof(media_node));
774 //		// +++++ copy media_node of associated time source!
775 ////		m->AddInt32("timeSource", n);
776 //
777 //		BMenuItem* i = new BMenuItem(
778 //				_time_source_strings[n], m);
779 //		menu->AddItem(i);
780 //		_addGroupTarget(i);
781 //	}
782 }
783 
784 // add the given invoker to be retargeted to this
785 // view (used for controls whose messages need a bit more
786 // processing before being forwarded to the NodeManager.)
787 
788 void TransportView::_addLocalTarget(
789 	BInvoker*								invoker) {
790 
791 	m_localTargets.push_back(invoker);
792 	if(Window())
793 		invoker->SetTarget(this);
794 }
795 
796 void TransportView::_addGroupTarget(
797 	BInvoker*								invoker) {
798 
799 	m_groupTargets.push_back(invoker);
800 	if(m_group)
801 		invoker->SetTarget(m_group);
802 }
803 
804 void TransportView::_refreshTransportSettings() {
805 	if(m_group)
806 		_updateTransportButtons();
807 }
808 
809 
810 void TransportView::_disableControls() {
811 
812 //	PRINT((
813 //		"*** _disableControls()\n"));
814 
815 	BWindow* w = Window();
816 	if(w)
817 		w->BeginViewTransaction();
818 
819 //	m_nameView->SetText("(no group)");
820 	m_infoView->Invalidate();
821 
822 	m_runModeView->SetEnabled(false);
823 	m_runModeView->Menu()->Superitem()->SetLabel("(none)");
824 
825 	m_timeSourceView->SetEnabled(false);
826 	m_timeSourceView->Menu()->Superitem()->SetLabel("(none)");
827 
828 	m_regionStartView->SetEnabled(false);
829 	m_regionStartView->setValue(0);
830 
831 	m_regionEndView->SetEnabled(false);
832 	m_regionEndView->setValue(0);
833 
834 	m_startButton->SetEnabled(false);
835 	m_stopButton->SetEnabled(false);
836 	m_prerollButton->SetEnabled(false);
837 
838 	if(w)
839 		w->EndViewTransaction();
840 
841 
842 //	PRINT((
843 //		"*** DONE: _disableControls()\n"));
844 }
845 
846 void TransportView::_enableControls() {
847 
848 //	PRINT((
849 //		"*** _enableControls()\n"));
850 //
851 	ASSERT(m_group);
852 	Autolock _l(m_group); // +++++ why?
853 	BWindow* w = Window();
854 	if(w) {
855 		w->BeginViewTransaction();
856 
857 		// clear focused view
858 		// 19sep99: TransportWindow is currently a B_AVOID_FOCUS window; the
859 		// only way views get focused is if they ask to be (which ValControl
860 		// currently does.)
861 		BView* focused = w->CurrentFocus();
862 		if(focused)
863 			focused->MakeFocus(false);
864 	}
865 
866 //	BString nameViewText = m_group->name();
867 //	nameViewText << ": " << m_group->countNodes() << " nodes.";
868 //	m_nameView->SetText(nameViewText.String());
869 
870 	m_infoView->Invalidate();
871 
872 	m_runModeView->SetEnabled(true);
873 	_updateRunMode();
874 
875 	m_timeSourceView->SetEnabled(true);
876 	_updateTimeSource();
877 
878 	_updateTransportButtons();
879 
880 
881 	// +++++ ick. hard-coded scaling factors make me queasy
882 
883 	m_regionStartView->SetEnabled(true);
884 	m_regionStartView->setValue(
885 		((double)m_group->startPosition()) / 1000000.0);
886 
887 	m_regionEndView->SetEnabled(true);
888 	m_regionEndView->setValue(
889 		((double)m_group->endPosition()) / 1000000.0);
890 
891 	// target controls on group
892 	for(target_set::iterator it = m_groupTargets.begin();
893 		it != m_groupTargets.end(); ++it) {
894 		ASSERT(*it);
895 		(*it)->SetTarget(m_group);
896 	}
897 
898 	if(w) {
899 		w->EndViewTransaction();
900 	}
901 
902 //	PRINT((
903 //		"*** DONE: _enableControls()\n"));
904 }
905 
906 void TransportView::_updateTransportButtons() {
907 
908 	ASSERT(m_group);
909 
910 	switch(m_group->transportState()) {
911 		case NodeGroup::TRANSPORT_INVALID:
912 		case NodeGroup::TRANSPORT_STARTING:
913 		case NodeGroup::TRANSPORT_STOPPING:
914 			m_startButton->SetEnabled(false);
915 			m_stopButton->SetEnabled(false);
916 			m_prerollButton->SetEnabled(false);
917 			break;
918 
919 		case NodeGroup::TRANSPORT_STOPPED:
920 			m_startButton->SetEnabled(true);
921 			m_stopButton->SetEnabled(false);
922 			m_prerollButton->SetEnabled(true);
923 			break;
924 
925 		case NodeGroup::TRANSPORT_RUNNING:
926 		case NodeGroup::TRANSPORT_ROLLING:
927 			m_startButton->SetEnabled(false);
928 			m_stopButton->SetEnabled(true);
929 			m_prerollButton->SetEnabled(false);
930 			break;
931 	}
932 
933 	// based on group settings, set the start button to do either
934 	// a simple start or a roll (atomic start/stop.) [e.moon 11oct99]
935 	ASSERT(m_startButton->Message());
936 
937 	// get current region settings
938 	bigtime_t startPosition = _scalePosition(m_regionStartView->value());
939 	bigtime_t endPosition = _scalePosition(m_regionEndView->value());
940 
941 	// get current run-mode setting
942 	uint32 runMode = 0;
943 	BMenuItem* i = m_runModeView->Menu()->FindMarked();
944 	ASSERT(i);
945 	runMode = m_runModeView->Menu()->IndexOf(i) + 1;
946 
947 	if(
948 		endPosition > startPosition &&
949 		(runMode == BMediaNode::B_OFFLINE ||
950 			!m_group->canCycle())) {
951 
952 		m_startButton->SetLabel("Roll");
953 		m_startButton->Message()->what = NodeGroup::M_ROLL;
954 
955 	} else {
956 		m_startButton->SetLabel("Start");
957 		m_startButton->Message()->what = NodeGroup::M_START;
958 	}
959 }
960 
961 void TransportView::_updateTimeSource() {
962 	ASSERT(m_group);
963 
964 	media_node tsNode;
965 	status_t err = m_group->getTimeSource(&tsNode);
966 	if(err < B_OK) {
967 		PRINT((
968 			"! TransportView::_updateTimeSource(): m_group->getTimeSource():\n"
969 			"  %s\n",
970 			strerror(err)));
971 		return;
972 	}
973 
974 	BMenu* menu = m_timeSourceView->Menu();
975 	ASSERT(menu);
976 	if(tsNode == media_node::null) {
977 		menu->Superitem()->SetLabel("(none)");
978 		return;
979 	}
980 
981 	// find menu item
982 	int32 n;
983 	for(n = menu->CountItems()-1; n >= 0; --n) {
984 		const void* data;
985 		media_node itemNode;
986 		BMessage* message = menu->ItemAt(n)->Message();
987 		if(!message)
988 			continue;
989 
990 		ssize_t size = sizeof(media_node);
991 		err = message->FindData(
992 			"timeSourceNode",
993 			B_RAW_TYPE,
994 			&data,
995 			&size);
996 		if(err < B_OK)
997 			continue;
998 
999 		memcpy(&itemNode, data, sizeof(media_node));
1000 		if(itemNode != tsNode)
1001 			continue;
1002 
1003 		// found it
1004 		PRINT((
1005 			"### _updateTimeSource: %s\n",
1006 			menu->ItemAt(n)->Label()));
1007 		menu->ItemAt(n)->SetMarked(true);
1008 		break;
1009 	}
1010 //	ASSERT(m_timeSourcePresets);
1011 //	int n;
1012 //	for(n = 0; n < _time_sources; ++n) {
1013 //		if(m_timeSourcePresets[n] == tsNode) {
1014 //			BMenuItem* i = m_timeSourceView->Menu()->ItemAt(n);
1015 //			ASSERT(i);
1016 //			i->SetMarked(true);
1017 //			break;
1018 //		}
1019 //	}
1020 	if(n < 0)
1021 		menu->Superitem()->SetLabel("(???)");
1022 
1023 }
1024 void TransportView::_updateRunMode() {
1025 	ASSERT(m_group);
1026 
1027 	BMenu* menu = m_runModeView->Menu();
1028 	uint32 runMode = m_group->runMode() - 1; // real run mode starts at 1
1029 	ASSERT(menu->CountItems() > runMode);
1030 	menu->ItemAt(runMode)->SetMarked(true);
1031 }
1032 
1033 //void TransportView::_initTimeSources() {
1034 //
1035 //	status_t err;
1036 //	media_node node;
1037 //	err = m_manager->roster->GetTimeSource(&node);
1038 //	if(err == B_OK) {
1039 //		m_timeSources.push_back(node.node);
1040 //	}
1041 //
1042 //	err = m_manager->roster->GetSystemTimeSource(&m_timeSourcePresets[1]);
1043 //	if(err < B_OK) {
1044 //		PRINT((
1045 //			"* TransportView::_initTimeSources():\n"
1046 //			"  GetSystemTimeSource() failed: %s\n", strerror(err)));
1047 //	}
1048 //}
1049 
1050 // [e.moon 2dec99]
1051 void TransportView::_timeSourceCreated(
1052 	BMessage*								message) {
1053 
1054 	status_t err;
1055 	media_node_id id;
1056 	err = message->FindInt32("nodeID", &id);
1057 	if(err < B_OK)
1058 		return;
1059 
1060 //	PRINT(("### _timeSourceCreated(): %ld\n", id));
1061 	NodeRef* ref;
1062 	err = m_manager->getNodeRef(id, &ref);
1063 	if(err < B_OK) {
1064 		PRINT((
1065 			"!!! TransportView::_timeSourceCreated(): node %ld doesn't exist\n",
1066 			id));
1067 		return;
1068 	}
1069 
1070 	char nameBuffer[B_MEDIA_NAME_LENGTH+16];
1071 	BMessage* m = new BMessage(NodeGroup::M_SET_TIME_SOURCE);
1072 	m->AddData(
1073 		"timeSourceNode",
1074 		B_RAW_TYPE,
1075 		&ref->node(),
1076 		sizeof(media_node));
1077 	sprintf(nameBuffer, "%s: %ld",
1078 		ref->name(),
1079 		ref->id());
1080 	BMenuItem* i = new BMenuItem(
1081 		nameBuffer,
1082 		m);
1083 	BMenu* menu = m_timeSourceView->Menu();
1084 	menu->AddItem(i);
1085 	_addGroupTarget(i);
1086 }
1087 
1088 // +++++
1089 void TransportView::_timeSourceDeleted(
1090 	BMessage*								message) {
1091 
1092 	status_t err;
1093 	media_node_id id;
1094 	err = message->FindInt32("nodeID", &id);
1095 	if(err < B_OK)
1096 		return;
1097 
1098 //	PRINT(("### _timeSourceDeleted(): %ld\n", id));
1099 
1100 	BMenu* menu = m_timeSourceView->Menu();
1101 	ASSERT(menu);
1102 
1103 	// find menu item
1104 	int32 n;
1105 	for(n = menu->CountItems()-1; n >= 0; --n) {
1106 		const void* data;
1107 		media_node itemNode;
1108 		BMessage* message = menu->ItemAt(n)->Message();
1109 		if(!message)
1110 			continue;
1111 
1112 		ssize_t size = sizeof(media_node);
1113 		err = message->FindData(
1114 			"timeSourceNode",
1115 			B_RAW_TYPE,
1116 			&data,
1117 			&size);
1118 		if(err < B_OK)
1119 			continue;
1120 
1121 		memcpy(&itemNode, data, sizeof(media_node));
1122 		if(itemNode.node != id)
1123 			continue;
1124 
1125 		// found it; remove the item
1126 		menu->RemoveItem(n);
1127 		break;
1128 	}
1129 }
1130 
1131 // -------------------------------------------------------- //
1132 // *** LAYOUT ***
1133 // -------------------------------------------------------- //
1134 
1135 const float _edge_pad_x 								= 3.0;
1136 const float _edge_pad_y 								= 3.0;
1137 
1138 const float _column_pad_x 							= 4.0;
1139 
1140 const float _field_pad_x 								= 20.0;
1141 
1142 const float _text_view_pad_x						= 10.0;
1143 
1144 const float _control_pad_x 							= 5.0;
1145 const float _control_pad_y 							= 10.0;
1146 const float _menu_field_pad_x 					= 30.0;
1147 
1148 const float _label_pad_x 								= 8.0;
1149 
1150 const float _transport_pad_y 						= 4.0;
1151 const float _transport_button_width			= 60.0;
1152 const float _transport_button_height		= 22.0;
1153 const float _transport_button_pad_x			= 4.0;
1154 
1155 const float _lock_button_width					= 42.0;
1156 const float _lock_button_height					= 16.0;
1157 
1158 class TransportView::_layout_state {
1159 public:
1160 	_layout_state() {}
1161 
1162 	// +++++
1163 };
1164 
1165 inline float _menu_width(
1166 	const BMenu* menu,
1167 	const BFont* font) {
1168 
1169 	float max = 0.0;
1170 
1171 	int total = menu->CountItems();
1172 	for(int n = 0; n < total; ++n) {
1173 		float w = font->StringWidth(
1174 			menu->ItemAt(n)->Label());
1175 		if(w > max)
1176 			max = w;
1177 	}
1178 
1179 	return max;
1180 }
1181 
1182 // -------------------------------------------------------- //
1183 
1184 void TransportView::_initLayout() {
1185 	m_layout = new _layout_state();
1186 }
1187 
1188 
1189 void TransportView::_updateLayout() // +++++
1190 {
1191 	BWindow* window = Window();
1192 	if(window)
1193 		window->BeginViewTransaction();
1194 
1195 	// calculate the ideal size of the view
1196 	// * max label width
1197 	float maxLabelWidth = 0.0;
1198 	float w;
1199 
1200 	maxLabelWidth = be_bold_font->StringWidth(
1201 		m_runModeView->Label());
1202 
1203 	w = be_bold_font->StringWidth(
1204 		m_timeSourceView->Label());
1205 	if(w > maxLabelWidth)
1206 		maxLabelWidth = w;
1207 
1208 //	w = be_bold_font->StringWidth(
1209 //		m_regionStartView->Label());
1210 //	if(w > maxLabelWidth)
1211 //		maxLabelWidth = w;
1212 //
1213 //	w = be_bold_font->StringWidth(
1214 //		m_regionEndView->Label());
1215 //	if(w > maxLabelWidth)
1216 //		maxLabelWidth = w;
1217 
1218 	// * max field width
1219 	float maxFieldWidth = 0.0;
1220 	maxFieldWidth = _menu_width(
1221 		m_runModeView->Menu(), be_plain_font);
1222 
1223 	w = _menu_width(
1224 		m_timeSourceView->Menu(), be_plain_font);
1225 	if(w > maxFieldWidth)
1226 		maxFieldWidth = w;
1227 
1228 	// * column width
1229 	float columnWidth =
1230 		maxLabelWidth +
1231 		maxFieldWidth + _label_pad_x + _field_pad_x;
1232 
1233 	// figure columns
1234 	float column1_x = _edge_pad_x;
1235 	float column2_x = column1_x + columnWidth + _column_pad_x;
1236 
1237 	// * sum to figure view width
1238 	float viewWidth =
1239 		column2_x + columnWidth + _edge_pad_x;
1240 
1241 	// make room for buttons
1242 	float buttonSpan =
1243 		(_transport_button_width*3) +
1244 		(_transport_button_pad_x*2);
1245 	if(columnWidth < buttonSpan)
1246 		viewWidth += (buttonSpan - columnWidth);
1247 
1248 //	float insideWidth = viewWidth - (_edge_pad_x*2);
1249 
1250 	// * figure view height a row at a time
1251 	font_height fh;
1252 	be_plain_font->GetHeight(&fh);
1253 	float lineHeight = fh.ascent + fh.descent + fh.leading;
1254 
1255 	float prefInfoWidth, prefInfoHeight;
1256 	m_infoView->GetPreferredSize(&prefInfoWidth, &prefInfoHeight);
1257 	float row_1_height = max(prefInfoHeight, _transport_button_height);
1258 
1259 	float row1_y = _edge_pad_y;
1260 	float row2_y = row1_y + row_1_height + _transport_pad_y;
1261 	float row3_y = row2_y + lineHeight + _control_pad_y;
1262 //	float row4_y = row3_y + lineHeight + _control_pad_y + _transport_pad_y;
1263 //	float row5_y = row4_y + lineHeight + _control_pad_y;
1264 	float viewHeight = row3_y + lineHeight + _control_pad_y + _edge_pad_y;
1265 
1266 	// Place controls
1267 	m_infoView->MoveTo(
1268 		column1_x+1.0, row1_y+1.0);
1269 	m_infoView->ResizeTo(
1270 		columnWidth, prefInfoHeight);
1271 
1272 	BRect br(
1273 		column2_x, row1_y,
1274 		column2_x+_transport_button_width,
1275 		row1_y+_transport_button_height);
1276 	if(prefInfoHeight > _transport_button_height)
1277 		br.OffsetBy(0.0, (prefInfoHeight - _transport_button_height)/2);
1278 
1279 	m_startButton->MoveTo(br.LeftTop());
1280 	m_startButton->ResizeTo(br.Width(), br.Height());
1281 	br.OffsetBy(_transport_button_width + _transport_button_pad_x, 0.0);
1282 
1283 	m_stopButton->MoveTo(br.LeftTop());
1284 	m_stopButton->ResizeTo(br.Width(), br.Height());
1285 	br.OffsetBy(_transport_button_width + _transport_button_pad_x, 0.0);
1286 
1287 	m_prerollButton->MoveTo(br.LeftTop());
1288 	m_prerollButton->ResizeTo(br.Width(), br.Height());
1289 
1290 	m_runModeView->MoveTo(
1291 		column2_x, row2_y);
1292 	m_runModeView->ResizeTo(
1293 		columnWidth, lineHeight);
1294 	m_runModeView->SetDivider(
1295 		maxLabelWidth+_label_pad_x);
1296 	m_runModeView->SetAlignment(
1297 		B_ALIGN_LEFT);
1298 
1299 	m_timeSourceView->MoveTo(
1300 		column2_x, row3_y);
1301 	m_timeSourceView->ResizeTo(
1302 		columnWidth, lineHeight);
1303 	m_timeSourceView->SetDivider(
1304 		maxLabelWidth+_label_pad_x);
1305 	m_timeSourceView->SetAlignment(
1306 		B_ALIGN_LEFT);
1307 
1308 //	float regionControlWidth = columnWidth;
1309 //	float regionControlHeight = lineHeight + 4.0;
1310 
1311 //	m_regionStartView->TextView()->SetResizingMode(
1312 //		B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP);
1313 
1314 	// "FROM"
1315 
1316 	BPoint rtLeftTop(column1_x, row2_y + 5.0);
1317 	BPoint rtRightBottom;
1318 
1319 	m_fromLabel->MoveTo(rtLeftTop);
1320 	m_fromLabel->ResizeToPreferred();
1321 	rtRightBottom = rtLeftTop + BPoint(
1322 		m_fromLabel->Bounds().Width(),
1323 		m_fromLabel->Bounds().Height());
1324 
1325 
1326 	// (region-start)
1327 
1328 	rtLeftTop.x = rtRightBottom.x+4;
1329 
1330 	m_regionStartView->MoveTo(rtLeftTop + BPoint(0.0, 2.0));
1331 	m_regionStartView->ResizeToPreferred();
1332 	rtRightBottom = rtLeftTop + BPoint(
1333 		m_regionStartView->Bounds().Width(),
1334 		m_regionStartView->Bounds().Height());
1335 
1336 //	m_regionStartView->SetDivider(
1337 //		maxLabelWidth);
1338 //	m_regionStartView->TextView()->ResizeTo(
1339 //		regionControlWidth-(maxLabelWidth+_text_view_pad_x),
1340 //		regionControlHeight-4.0);
1341 
1342 	// "TO"
1343 
1344 	rtLeftTop.x = rtRightBottom.x + 6;
1345 
1346 	m_toLabel->MoveTo(rtLeftTop);
1347 	m_toLabel->ResizeToPreferred();
1348 	rtRightBottom = rtLeftTop + BPoint(
1349 		m_toLabel->Bounds().Width(),
1350 		m_toLabel->Bounds().Height());
1351 
1352 	// (region-end)
1353 
1354 	rtLeftTop.x = rtRightBottom.x + 4;
1355 
1356 	m_regionEndView->MoveTo(rtLeftTop + BPoint(0.0, 2.0));
1357 	m_regionEndView->ResizeToPreferred();
1358 //	m_regionEndView->SetDivider(
1359 //		maxLabelWidth);
1360 //	m_regionEndView->TextView()->ResizeTo(
1361 //		regionControlWidth-(maxLabelWidth+_text_view_pad_x),
1362 //		regionControlHeight-4.0);
1363 
1364 
1365 	BRect b = Bounds();
1366 	float targetWidth = (b.Width() < viewWidth) ?
1367 		viewWidth :
1368 		b.Width();
1369 	float targetHeight = (b.Height() < viewHeight) ?
1370 		viewHeight :
1371 		b.Height();
1372 
1373 	// Resize view to fit contents
1374 	ResizeTo(targetWidth, targetHeight);
1375 
1376 	if(window) {
1377 		window->ResizeTo(targetWidth, targetHeight);
1378 	}
1379 
1380 //	// +++++ testing NumericValControl [23aug99]
1381 //	float valWidth, valHeight;
1382 //	m_valView->GetPreferredSize(&valWidth, &valHeight);
1383 //	PRINT((
1384 //		"\n\nm_valView preferred size: %.1f x %.1f\n\n",
1385 //		valWidth, valHeight));
1386 //
1387 	if(window)
1388 		window->EndViewTransaction();
1389 }
1390 
1391 // -------------------------------------------------------- //
1392 // *** dtor
1393 // -------------------------------------------------------- //
1394 
1395 TransportView::~TransportView() {
1396 	if(m_group)
1397 		_releaseGroup();
1398 	if(m_layout)
1399 		delete m_layout;
1400 }
1401 
1402 
1403 // END -- TransportView.cpp --
1404