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