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