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