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
_GroupInfoView(BRect frame,TransportView * parent,const char * name,uint32 resizeMode=B_FOLLOW_LEFT|B_FOLLOW_TOP,uint32 flags=B_WILL_DRAW|B_FRAME_EVENTS)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
FrameResized(float width,float height)103 virtual void FrameResized(
104 float width,
105 float height) {
106
107 _inherited::FrameResized(width, height);
108 _updateLayout();
109 Invalidate();
110 }
111
GetPreferredSize(float * width,float * height)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
Draw(BRect updateRect)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
MouseDown(BPoint point)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
_initViews()207 void _initViews() {
208 // +++++
209 }
210
_initColors()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
_updateLayout()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
TransportView(NodeManager * manager,const char * name)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
AttachedToWindow()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
AllAttached()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
DetachedFromWindow()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
FrameResized(float width,float height)320 void TransportView::FrameResized(
321 float width,
322 float height) {
323
324 _inherited::FrameResized(width, height);
325 // _updateLayout();
326 }
327
KeyDown(const char * bytes,int32 count)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
MouseDown(BPoint where)347 void TransportView::MouseDown(
348 BPoint where) {
349
350 MakeFocus(true);
351 }
352
353
354 // -------------------------------------------------------- //
355 // *** BHandler
356 // -------------------------------------------------------- //
357
MessageReceived(BMessage * message)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
_handleSelectGroup(BMessage * message)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)
_selectGroup(uint32 groupID)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
_observeGroup()557 void TransportView::_observeGroup() {
558 ASSERT(m_group);
559
560 add_observer(this, m_group);
561 }
562
_releaseGroup()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
_constructControls()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]
_scalePosition(double value)718 bigtime_t TransportView::_scalePosition(
719 double value) {
720
721 return bigtime_t(value * 1000000.0);
722 }
723
_populateRunModeMenu(BMenu * menu)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
_populateTimeSourceMenu(BMenu * menu)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
_addLocalTarget(BInvoker * invoker)835 void TransportView::_addLocalTarget(
836 BInvoker* invoker) {
837
838 m_localTargets.push_back(invoker);
839 if(Window())
840 invoker->SetTarget(this);
841 }
842
_addGroupTarget(BInvoker * invoker)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
_refreshTransportSettings()851 void TransportView::_refreshTransportSettings() {
852 if(m_group)
853 _updateTransportButtons();
854 }
855
856
_disableControls()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
_enableControls()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
_updateTransportButtons()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
_updateTimeSource()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 }
_updateRunMode()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]
_timeSourceCreated(BMessage * message)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 // +++++
_timeSourceDeleted(BMessage * message)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:
_layout_state()1207 _layout_state() {}
1208
1209 // +++++
1210 };
1211
_menu_width(const BMenu * menu,const BFont * font)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
_initLayout()1231 void TransportView::_initLayout() {
1232 m_layout = new _layout_state();
1233 }
1234
1235
_updateLayout()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
~TransportView()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