xref: /haiku/src/apps/cortex/RouteApp/RouteWindow.cpp (revision 857b0c2bef2de29d8f02f6daf7c8379bf782dccb)
1 // RouteWindow.cpp
2 // e.moon 14may99
3 
4 #include "RouteApp.h"
5 #include "RouteWindow.h"
6 #include "MediaRoutingView.h"
7 #include "StatusView.h"
8 
9 #include "DormantNodeWindow.h"
10 #include "TransportWindow.h"
11 
12 #include "RouteAppNodeManager.h"
13 #include "NodeGroup.h"
14 #include "TipManager.h"
15 
16 #include <Alert.h>
17 #include <Autolock.h>
18 #include <Debug.h>
19 #include <Font.h>
20 #include <MenuBar.h>
21 #include <Menu.h>
22 #include <MenuItem.h>
23 #include <Message.h>
24 #include <Messenger.h>
25 #include <Roster.h>
26 #include <Screen.h>
27 #include <ScrollView.h>
28 #include <StringView.h>
29 
30 #include <algorithm>
31 
32 #include "debug_tools.h"
33 #define D_HOOK(x) //PRINT (x)
34 #define D_INTERNAL(x) //PRINT (x)
35 
36 __USE_CORTEX_NAMESPACE
37 
38 
39 const char* const RouteWindow::s_windowName = "Cortex";
40 
41 const BRect RouteWindow::s_initFrame(100,100,700,550);
42 
43 const char* const g_aboutText =
44 	"Cortex/Route 2.1.2\n\n"
45 	"Copyright 1999-2000 Eric Moon\n"
46 	"All rights reserved.\n\n"
47 	"The Cortex Team:\n\n"
48 	"Christopher Lenz: UI\n"
49 	"Eric Moon: UI, back-end\n\n"
50 	"Thanks to:\nJohn Ashmun\nJon Watte\nDoug Wright\n<your name here>\n\n"
51 	"Certain icons used herein are the property of\n"
52 	"Be, Inc. and are used by permission.";
53 
54 
55 RouteWindow::~RouteWindow()
56 {
57 }
58 
59 
60 RouteWindow::RouteWindow(RouteAppNodeManager* manager)
61 	:
62 	BWindow(s_initFrame, s_windowName, B_DOCUMENT_WINDOW, 0),
63 	m_hScrollBar(0),
64 	m_vScrollBar(0),
65 	m_transportWindow(0),
66 	m_dormantNodeWindow(0),
67 	m_selectedGroupID(0),
68 	m_zoomed(false),
69 	m_zooming(false)
70 {
71 	BRect b = Bounds();
72 
73 	// initialize the menu bar: add all menus that target this window
74 	BMenuBar* pMenuBar = new BMenuBar(b, "menuBar");
75 	BMenu* pFileMenu = new BMenu("File");
76 	BMenuItem* item = new BMenuItem("Open" B_UTF8_ELLIPSIS,
77 		new BMessage(RouteApp::M_SHOW_OPEN_PANEL), 'O');
78 	item->SetTarget(be_app);
79 	pFileMenu->AddItem(item);
80 	pFileMenu->AddItem(new BSeparatorItem());
81 	item = new BMenuItem("Save Nodes" B_UTF8_ELLIPSIS,
82 		new BMessage(RouteApp::M_SHOW_SAVE_PANEL), 'S');
83 	item->SetTarget(be_app);
84 	pFileMenu->AddItem(item);
85 	pFileMenu->AddItem(new BSeparatorItem());
86 	pFileMenu->AddItem(new BMenuItem("About Cortex/Route" B_UTF8_ELLIPSIS,
87 		new BMessage(B_ABOUT_REQUESTED)));
88 	pFileMenu->AddItem(new BSeparatorItem());
89 	pFileMenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED)));
90 	pMenuBar->AddItem(pFileMenu);
91 	AddChild(pMenuBar);
92 
93 	// build the routing view
94 	BRect rvBounds = b;
95 	rvBounds.top = pMenuBar->Frame().bottom+1;
96 	rvBounds.right -= B_V_SCROLL_BAR_WIDTH;
97 	rvBounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
98 	m_routingView = new MediaRoutingView(manager, rvBounds, "routingView");
99 
100 	BRect hsBounds = rvBounds;
101 	hsBounds.left = rvBounds.left + 199;
102 	hsBounds.top = hsBounds.bottom + 1;
103 	hsBounds.right++;
104 	hsBounds.bottom = b.bottom + 1;
105 
106 	m_hScrollBar = new BScrollBar(hsBounds, "hScrollBar", m_routingView,
107 		0, 0, B_HORIZONTAL);
108 	AddChild(m_hScrollBar);
109 
110 	BRect vsBounds = rvBounds;
111 	vsBounds.left = vsBounds.right + 1;
112 	vsBounds.top--;
113 	vsBounds.right = b.right + 1;
114 	vsBounds.bottom++;
115 
116 	m_vScrollBar = new BScrollBar(vsBounds, "vScrollBar", m_routingView,
117 		0, 0, B_VERTICAL);
118 	AddChild(m_vScrollBar);
119 
120 	BRect svBounds = rvBounds;
121 	svBounds.left -= 1;
122 	svBounds.right = hsBounds.left - 1;
123 	svBounds.top = svBounds.bottom + 1;
124 	svBounds.bottom = b.bottom + 1;
125 
126 	m_statusView = new StatusView(svBounds, manager, m_hScrollBar);
127 	AddChild(m_statusView);
128 
129 	AddChild(m_routingView);
130 
131 	float minWidth, maxWidth, minHeight, maxHeight;
132 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
133 	minWidth = m_statusView->Frame().Width() + 6 * B_V_SCROLL_BAR_WIDTH;
134 	minHeight = 6 * B_H_SCROLL_BAR_HEIGHT;
135 	SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
136 
137 	// construct the Window menu
138 	BMenu* windowMenu = new BMenu("Window");
139 	m_transportWindowItem = new BMenuItem(
140 		"Show Transport",
141 		new BMessage(M_TOGGLE_TRANSPORT_WINDOW));
142 	windowMenu->AddItem(m_transportWindowItem);
143 
144 	m_dormantNodeWindowItem = new BMenuItem(
145 		"Show Add-Ons",
146 		new BMessage(M_TOGGLE_DORMANT_NODE_WINDOW));
147 	windowMenu->AddItem(m_dormantNodeWindowItem);
148 
149 	windowMenu->AddItem(new BSeparatorItem());
150 
151 	m_pullPalettesItem = new BMenuItem(
152 		"Pull Palettes",
153 		new BMessage(M_TOGGLE_PULLING_PALETTES));
154 	windowMenu->AddItem(m_pullPalettesItem);
155 
156 	pMenuBar->AddItem(windowMenu);
157 
158 	// create the dormant-nodes palette
159 	_toggleDormantNodeWindow();
160 
161 	// display group inspector
162 	_toggleTransportWindow();
163 }
164 
165 
166 //	#pragma mark - operations
167 
168 
169 /*!	Enable/disable palette position-locking (when the main
170 	window is moved, all palettes follow).
171 */
172 bool
173 RouteWindow::isPullPalettes() const
174 {
175 	return m_pullPalettesItem->IsMarked();
176 }
177 
178 
179 void
180 RouteWindow::setPullPalettes(bool enabled)
181 {
182 	m_pullPalettesItem->SetMarked(enabled);
183 }
184 
185 
186 void
187 RouteWindow::constrainToScreen()
188 {
189 	BScreen screen(this);
190 
191 	const BRect sr = screen.Frame();
192 
193 	// [c.lenz 1mar2000] this should be handled by every window
194 	// itself. will probably change soon ;-)
195 	_constrainToScreen();
196 /*	// main window
197 	BRect r = Frame();
198 	BPoint offset(0.0, 0.0);
199 	if(r.left < 0.0)
200 		offset.x = -r.left;
201 	if(r.top < 0.0)
202 		offset.y = -r.top;
203 	if(r.left >= (sr.right - 20.0))
204 		offset.x -= (r.left - (sr.Width()/2));
205 	if(r.top >= (sr.bottom - 20.0))
206 		offset.y -= (r.top - (sr.Height()/2));
207 	if(offset.x != 0.0 || offset.y != 0.0) {
208 		setPullPalettes(false);
209 		MoveBy(offset.x, offset.y);
210 	}*/
211 
212 	// transport window
213 	BPoint offset = BPoint(0.0, 0.0);
214 	BRect r = (m_transportWindow) ?
215 			   m_transportWindow->Frame() :
216 			   m_transportWindowFrame;
217 	if(r.left < 0.0)
218 		offset.x = (sr.Width()*.75) - r.left;
219 	if(r.top < 0.0)
220 		offset.y = (sr.Height()*.25) - r.top;
221 	if(r.left >= (sr.right - 20.0))
222 		offset.x -= (r.left - (sr.Width()/2));
223 	if(r.top >= (sr.bottom - 20.0))
224 		offset.y -= (r.top - (sr.Height()/2));
225 
226 	if(offset.x != 0.0 || offset.y != 0.0) {
227 		if(m_transportWindow)
228 			m_transportWindow->MoveBy(offset.x, offset.y);
229 		else
230 			m_transportWindowFrame.OffsetBy(offset.x, offset.y);
231 	}
232 
233 	// addon palette
234 	offset = BPoint(0.0, 0.0);
235 	r = (m_dormantNodeWindow) ?
236 		m_dormantNodeWindow->Frame() :
237 		m_dormantNodeWindowFrame;
238 	if(r.left < 0.0)
239 		offset.x = (sr.Width()*.25) - r.left;
240 	if(r.top < 0.0)
241 		offset.y = (sr.Height()*.125) - r.top;
242 	if(r.left >= (sr.right - 20.0))
243 		offset.x -= (r.left - (sr.Width()/2));
244 	if(r.top >= (sr.bottom - 20.0))
245 		offset.y -= (r.top - (sr.Height()/2));
246 
247 	if(offset.x != 0.0 || offset.y != 0.0) {
248 		if(m_dormantNodeWindow)
249 			m_dormantNodeWindow->MoveBy(offset.x, offset.y);
250 		else
251 			m_dormantNodeWindowFrame.OffsetBy(offset.x, offset.y);
252 	}
253 
254 }
255 
256 
257 //	#pragma mark - BWindow implementation
258 
259 
260 void
261 RouteWindow::FrameMoved(BPoint point)
262 {
263 	// ignore notification if the window isn't yet visible
264 	if(IsHidden())
265 		return;
266 
267 	BPoint delta = point - m_lastFramePosition;
268 	m_lastFramePosition = point;
269 
270 
271 	if (m_pullPalettesItem->IsMarked())
272 		_movePalettesBy(delta.x, delta.y);
273 }
274 
275 
276 void
277 RouteWindow::FrameResized(float width, float height)
278 {
279 	D_HOOK(("RouteWindow::FrameResized()\n"));
280 
281 	if (!m_zooming) {
282 		m_zoomed = false;
283 	}
284 	else {
285 		m_zooming = false;
286 	}
287 }
288 
289 
290 bool
291 RouteWindow::QuitRequested()
292 {
293 	be_app->PostMessage(B_QUIT_REQUESTED);
294 	return false; // [e.moon 20oct99] app now quits window
295 }
296 
297 
298 void
299 RouteWindow::Zoom(BPoint origin, float width, float height)
300 {
301 	D_HOOK(("RouteWindow::Zoom()\n"));
302 
303 	m_zooming = true;
304 
305 	BScreen screen(this);
306 	if (!screen.Frame().Contains(Frame())) {
307 		m_zoomed = false;
308 	}
309 
310 	if (!m_zoomed) {
311 		// resize to the ideal size
312 		m_manualSize = Bounds();
313 		float width, height;
314 		m_routingView->GetPreferredSize(&width, &height);
315 		width += B_V_SCROLL_BAR_WIDTH;
316 		height += B_H_SCROLL_BAR_HEIGHT;
317 		if (KeyMenuBar()) {
318 			height += KeyMenuBar()->Frame().Height();
319 		}
320 		ResizeTo(width, height);
321 		_constrainToScreen();
322 		m_zoomed = true;
323 	}
324 	else {
325 		// resize to the most recent manual size
326 		ResizeTo(m_manualSize.Width(), m_manualSize.Height());
327 		m_zoomed = false;
328 	}
329 }
330 
331 
332 //	#pragma mark - BHandler implemenation
333 
334 
335 void
336 RouteWindow::MessageReceived(BMessage* pMsg)
337 {
338 //	PRINT((
339 //		"RouteWindow::MessageReceived()\n"));
340 //	pMsg->PrintToStream();
341 //
342 	switch (pMsg->what) {
343 		case B_ABOUT_REQUESTED:
344 			(new BAlert("About", g_aboutText, "Ok"))->Go();
345 			break;
346 
347 		case MediaRoutingView::M_GROUP_SELECTED:
348 			_handleGroupSelected(pMsg);
349 			break;
350 
351 		case MediaRoutingView::M_SHOW_ERROR_MESSAGE:
352 			_handleShowErrorMessage(pMsg);
353 			break;
354 
355 		case M_TOGGLE_TRANSPORT_WINDOW:
356 			_toggleTransportWindow();
357 			break;
358 
359 		case M_REFRESH_TRANSPORT_SETTINGS:
360 			_refreshTransportSettings(pMsg);
361 			break;
362 
363 		case M_TOGGLE_PULLING_PALETTES:
364 			_togglePullPalettes();
365 			break;
366 
367 		case M_TOGGLE_DORMANT_NODE_WINDOW:
368 			_toggleDormantNodeWindow();
369 			break;
370 
371 		case M_TOGGLE_GROUP_ROLLING:
372 			_toggleGroupRolling();
373 			break;
374 
375 		default:
376 			_inherited::MessageReceived(pMsg);
377 			break;
378 	}
379 }
380 
381 
382 //	#pragma mark - IStateArchivable
383 
384 
385 status_t
386 RouteWindow::importState(const BMessage* archive)
387 {
388 	status_t err;
389 
390 	// frame rect
391 	BRect r;
392 	err = archive->FindRect("frame", &r);
393 	if(err == B_OK) {
394 		MoveTo(r.LeftTop());
395 		ResizeTo(r.Width(), r.Height());
396 		m_lastFramePosition = r.LeftTop();
397 	}
398 
399 	// status view width
400 	int32 i;
401 	err = archive->FindInt32("statusViewWidth", &i);
402 	if (err == B_OK) {
403 		float diff = i - m_statusView->Bounds().IntegerWidth();
404 		m_statusView->ResizeBy(diff, 0.0);
405 		m_hScrollBar->ResizeBy(-diff, 0.0);
406 		m_hScrollBar->MoveBy(diff, 0.0);
407 	}
408 
409 	// settings
410 	bool b;
411 	err = archive->FindBool("pullPalettes", &b);
412 	if(err == B_OK)
413 		m_pullPalettesItem->SetMarked(b);
414 
415 //	const char* p;
416 //	err = archive->FindString("saveDir", &p);
417 //	if(err == B_OK) {
418 //		m_openPanel.SetPanelDirectory(p);
419 //		m_savePanel.SetPanelDirectory(p);
420 //	}
421 //
422 	// dormant-node window
423 	err = archive->FindRect("addonPaletteFrame", &r);
424 	if (err == B_OK)
425 		m_dormantNodeWindowFrame = r;
426 	err = archive->FindBool("addonPaletteVisible", &b);
427 	if (err == B_OK && (b != (m_dormantNodeWindow != 0))) {
428 		_toggleDormantNodeWindow();
429 		if(!m_dormantNodeWindow)
430 			m_dormantNodeWindowFrame = r;
431 	}
432 
433 	if (m_dormantNodeWindow) {
434 		m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
435 		m_dormantNodeWindow->ResizeTo(
436 			m_dormantNodeWindowFrame.Width(),
437 			m_dormantNodeWindowFrame.Height());
438 	}
439 
440 	// transport window
441 	err = archive->FindRect("transportFrame", &r);
442 	if (err == B_OK)
443 		m_transportWindowFrame = r;
444 	err = archive->FindBool("transportVisible", &b);
445 	if (err == B_OK && (b != (m_transportWindow != 0))) {
446 		_toggleTransportWindow();
447 		if (!m_transportWindow)
448 			m_transportWindowFrame = r;
449 	}
450 
451 	if (m_transportWindow) {
452 		m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
453 		m_transportWindow->ResizeTo(
454 			m_transportWindowFrame.Width(),
455 			m_transportWindowFrame.Height());
456 	}
457 
458 	return B_OK;
459 }
460 
461 
462 status_t
463 RouteWindow::exportState(BMessage* archive) const
464 {
465 	BRect r = Frame();
466 	archive->AddRect("frame", r);
467 	archive->AddBool("pullPalettes", m_pullPalettesItem->IsMarked());
468 
469 	bool b = (m_dormantNodeWindow != 0);
470 	r = b ? m_dormantNodeWindow->Frame() : m_dormantNodeWindowFrame;
471 	archive->AddRect("addonPaletteFrame", r);
472 	archive->AddBool("addonPaletteVisible", b);
473 
474 	b = (m_transportWindow != 0);
475 	r = b ? m_transportWindow->Frame() : m_transportWindowFrame;
476 
477 	archive->AddRect("transportFrame", r);
478 	archive->AddBool("transportVisible", b);
479 
480 	// [c.lenz 23may00] remember status view width
481 	int i = m_statusView->Bounds().IntegerWidth();
482 	archive->AddInt32("statusViewWidth", i);
483 
484 //	entry_ref saveRef;
485 //	m_savePanel.GetPanelDirectory(&saveRef);
486 //	BEntry saveEntry(&saveRef);
487 //	if(saveEntry.InitCheck() == B_OK) {
488 //		BPath p;
489 //		saveEntry.GetPath(&p);
490 //		archive->AddString("saveDir", p.Path());
491 //	}
492 
493 	return B_OK;
494 }
495 
496 
497 //	#pragma mark - implementation
498 
499 
500 void
501 RouteWindow::_constrainToScreen()
502 {
503 	D_INTERNAL(("RouteWindow::_constrainToScreen()\n"));
504 
505 	BScreen screen(this);
506 	BRect screenRect = screen.Frame();
507 	BRect windowRect = Frame();
508 
509 	// if the window is outside the screen rect
510 	// move it to the default position
511 	if (!screenRect.Intersects(windowRect)) {
512 		windowRect.OffsetTo(screenRect.LeftTop());
513 		MoveTo(windowRect.LeftTop());
514 		windowRect = Frame();
515 	}
516 
517 	// if the window is larger than the screen rect
518 	// resize it to fit at each side
519 	if (!screenRect.Contains(windowRect)) {
520 		if (windowRect.left < screenRect.left) {
521 			windowRect.left = screenRect.left + 5.0;
522 			MoveTo(windowRect.LeftTop());
523 			windowRect = Frame();
524 		}
525 		if (windowRect.top < screenRect.top) {
526 			windowRect.top = screenRect.top + 5.0;
527 			MoveTo(windowRect.LeftTop());
528 			windowRect = Frame();
529 		}
530 		if (windowRect.right > screenRect.right) {
531 			windowRect.right = screenRect.right - 5.0;
532 		}
533 		if (windowRect.bottom > screenRect.bottom) {
534 			windowRect.bottom = screenRect.bottom - 5.0;
535 		}
536 		ResizeTo(windowRect.Width(), windowRect.Height());
537 	}
538 }
539 
540 
541 void
542 RouteWindow::_toggleTransportWindow()
543 {
544 	if (m_transportWindow) {
545 		m_transportWindowFrame = m_transportWindow->Frame();
546 		m_transportWindow->Lock();
547 		m_transportWindow->Quit();
548 		m_transportWindow = 0;
549 		m_transportWindowItem->SetMarked(false);
550 	} else {
551 		m_transportWindow = new TransportWindow(m_routingView->manager,
552 			this, "Transport");
553 
554 		// ask for a selection update
555 		BMessenger(m_routingView).SendMessage(
556 			MediaRoutingView::M_BROADCAST_SELECTION);
557 
558 		// place & display the window
559 		if (m_transportWindowFrame.IsValid()) {
560 			m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
561 			m_transportWindow->ResizeTo(m_transportWindowFrame.Width(),
562 				m_transportWindowFrame.Height());
563 		}
564 
565 		m_transportWindow->Show();
566 		m_transportWindowItem->SetMarked(true);
567 	}
568 }
569 
570 
571 void
572 RouteWindow::_togglePullPalettes()
573 {
574 	m_pullPalettesItem->SetMarked(!m_pullPalettesItem->IsMarked());
575 }
576 
577 
578 void
579 RouteWindow::_toggleDormantNodeWindow()
580 {
581 	if (m_dormantNodeWindow) {
582 		m_dormantNodeWindowFrame = m_dormantNodeWindow->Frame();
583 		m_dormantNodeWindow->Lock();
584 		m_dormantNodeWindow->Quit();
585 		m_dormantNodeWindow = 0;
586 		m_dormantNodeWindowItem->SetMarked(false);
587 	} else {
588 		m_dormantNodeWindow = new DormantNodeWindow(this);
589 		if (m_dormantNodeWindowFrame.IsValid()) {
590 			m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
591 			m_dormantNodeWindow->ResizeTo(m_dormantNodeWindowFrame.Width(),
592 				m_dormantNodeWindowFrame.Height());
593 		}
594 		m_dormantNodeWindow->Show();
595 		m_dormantNodeWindowItem->SetMarked(true);
596 	}
597 }
598 
599 
600 void
601 RouteWindow::_handleGroupSelected(BMessage* message)
602 {
603 	status_t err;
604 	uint32 groupID;
605 
606 	err = message->FindInt32("groupID", (int32*)&groupID);
607 	if (err < B_OK) {
608 		PRINT((
609 			"! RouteWindow::_handleGroupSelected(): no groupID in message!\n"));
610 		return;
611 	}
612 
613 	if (!m_transportWindow)
614 		return;
615 
616 	BMessage m(TransportWindow::M_SELECT_GROUP);
617 	m.AddInt32("groupID", groupID);
618 	BMessenger(m_transportWindow).SendMessage(&m);
619 
620 	m_selectedGroupID = groupID;
621 }
622 
623 
624 void
625 RouteWindow::_handleShowErrorMessage(BMessage* message)
626 {
627 	status_t err;
628 	BString text;
629 
630 	err = message->FindString("text", &text);
631 	if (err < B_OK) {
632 		PRINT((
633 			"! RouteWindow::_handleShowErrorMessage(): no text in message!\n"));
634 		return;
635 	}
636 
637 	m_statusView->setErrorMessage(text.String(), message->HasBool("error"));
638 }
639 
640 
641 //! Refresh the transport window for the given group, if any
642 void
643 RouteWindow::_refreshTransportSettings(BMessage* message)
644 {
645 	status_t err;
646 	uint32 groupID;
647 
648 	err = message->FindInt32("groupID", (int32*)&groupID);
649 	if (err < B_OK) {
650 		PRINT((
651 			"! RouteWindow::_refreshTransportSettings(): no groupID in message!\n"));
652 		return;
653 	}
654 
655 	if(m_transportWindow) {
656 		// relay the message
657 		BMessenger(m_transportWindow).SendMessage(message);
658 	}
659 }
660 
661 
662 void
663 RouteWindow::_closePalettes()
664 {
665 	BAutolock _l(this);
666 
667 	if (m_transportWindow) {
668 		m_transportWindow->Lock();
669 		m_transportWindow->Quit();
670 		m_transportWindow = 0;
671 	}
672 }
673 
674 
675 //!	Move all palette windows by the specified amounts
676 void RouteWindow::_movePalettesBy(float xDelta, float yDelta)
677 {
678 	if (m_transportWindow)
679 		m_transportWindow->MoveBy(xDelta, yDelta);
680 
681 	if (m_dormantNodeWindow)
682 		m_dormantNodeWindow->MoveBy(xDelta, yDelta);
683 }
684 
685 
686 //!	Toggle group playback
687 void
688 RouteWindow::_toggleGroupRolling()
689 {
690 	if (!m_selectedGroupID)
691 		return;
692 
693 	NodeGroup* g;
694 	status_t err = m_routingView->manager->findGroup(m_selectedGroupID, &g);
695 	if (err < B_OK)
696 		return;
697 
698 	Autolock _l(g);
699 	uint32 startAction = (g->runMode() == BMediaNode::B_OFFLINE)
700 		? NodeGroup::M_ROLL : NodeGroup::M_START;
701 
702 	BMessenger m(g);
703 	switch (g->transportState()) {
704 		case NodeGroup::TRANSPORT_STOPPED:
705 			m.SendMessage(startAction);
706 			break;
707 
708 		case NodeGroup::TRANSPORT_RUNNING:
709 		case NodeGroup::TRANSPORT_ROLLING:
710 			m.SendMessage(NodeGroup::M_STOP);
711 			break;
712 
713 		default:
714 			break;
715 	}
716 }
717