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