xref: /haiku/src/apps/cortex/RouteApp/RouteWindow.cpp (revision 6c4a44e36ba846c54467103f884d65dfa13e7fcb)
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 		{
376 			BAlert* alert = new BAlert("About", g_aboutText, "OK");
377 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
378 			alert->Go();
379 			break;
380 		}
381 		case MediaRoutingView::M_GROUP_SELECTED:
382 			_handleGroupSelected(pMsg);
383 			break;
384 
385 		case MediaRoutingView::M_SHOW_ERROR_MESSAGE:
386 			_handleShowErrorMessage(pMsg);
387 			break;
388 
389 		case M_TOGGLE_TRANSPORT_WINDOW:
390 			_toggleTransportWindow();
391 			break;
392 
393 		case M_REFRESH_TRANSPORT_SETTINGS:
394 			_refreshTransportSettings(pMsg);
395 			break;
396 
397 		case M_TOGGLE_PULLING_PALETTES:
398 			_togglePullPalettes();
399 			break;
400 
401 		case M_TOGGLE_DORMANT_NODE_WINDOW:
402 			_toggleDormantNodeWindow();
403 			break;
404 
405 		case M_TOGGLE_GROUP_ROLLING:
406 			_toggleGroupRolling();
407 			break;
408 
409 		default:
410 			_inherited::MessageReceived(pMsg);
411 			break;
412 	}
413 }
414 
415 
416 //	#pragma mark - IStateArchivable
417 
418 
419 status_t
420 RouteWindow::importState(const BMessage* archive)
421 {
422 	status_t err;
423 
424 	// frame rect
425 	BRect r;
426 	err = archive->FindRect("frame", &r);
427 	if(err == B_OK) {
428 		MoveTo(r.LeftTop());
429 		ResizeTo(r.Width(), r.Height());
430 		m_lastFramePosition = r.LeftTop();
431 	}
432 
433 	// status view width
434 	int32 i;
435 	err = archive->FindInt32("statusViewWidth", &i);
436 	if (err == B_OK) {
437 		float diff = i - m_statusView->Bounds().IntegerWidth();
438 		m_statusView->ResizeBy(diff, 0.0);
439 		m_hScrollBar->ResizeBy(-diff, 0.0);
440 		m_hScrollBar->MoveBy(diff, 0.0);
441 	}
442 
443 	// settings
444 	bool b;
445 	err = archive->FindBool("pullPalettes", &b);
446 	if(err == B_OK)
447 		m_pullPalettesItem->SetMarked(b);
448 
449 //	const char* p;
450 //	err = archive->FindString("saveDir", &p);
451 //	if(err == B_OK) {
452 //		m_openPanel.SetPanelDirectory(p);
453 //		m_savePanel.SetPanelDirectory(p);
454 //	}
455 //
456 	// dormant-node window
457 	err = archive->FindRect("addonPaletteFrame", &r);
458 	if (err == B_OK)
459 		m_dormantNodeWindowFrame = r;
460 	err = archive->FindBool("addonPaletteVisible", &b);
461 	if (err == B_OK && (b != (m_dormantNodeWindow != 0))) {
462 		_toggleDormantNodeWindow();
463 		if(!m_dormantNodeWindow)
464 			m_dormantNodeWindowFrame = r;
465 	}
466 
467 	if (m_dormantNodeWindow) {
468 		m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
469 		m_dormantNodeWindow->ResizeTo(
470 			m_dormantNodeWindowFrame.Width(),
471 			m_dormantNodeWindowFrame.Height());
472 	}
473 
474 	// transport window
475 	err = archive->FindRect("transportFrame", &r);
476 	if (err == B_OK)
477 		m_transportWindowFrame = r;
478 	err = archive->FindBool("transportVisible", &b);
479 	if (err == B_OK && (b != (m_transportWindow != 0))) {
480 		_toggleTransportWindow();
481 		if (!m_transportWindow)
482 			m_transportWindowFrame = r;
483 	}
484 
485 	if (m_transportWindow) {
486 		m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
487 		m_transportWindow->ResizeTo(
488 			m_transportWindowFrame.Width(),
489 			m_transportWindowFrame.Height());
490 	}
491 
492 	return B_OK;
493 }
494 
495 
496 status_t
497 RouteWindow::exportState(BMessage* archive) const
498 {
499 	BRect r = Frame();
500 	archive->AddRect("frame", r);
501 	archive->AddBool("pullPalettes", m_pullPalettesItem->IsMarked());
502 
503 	bool b = (m_dormantNodeWindow != 0);
504 	r = b ? m_dormantNodeWindow->Frame() : m_dormantNodeWindowFrame;
505 	archive->AddRect("addonPaletteFrame", r);
506 	archive->AddBool("addonPaletteVisible", b);
507 
508 	b = (m_transportWindow != 0);
509 	r = b ? m_transportWindow->Frame() : m_transportWindowFrame;
510 
511 	archive->AddRect("transportFrame", r);
512 	archive->AddBool("transportVisible", b);
513 
514 	// [c.lenz 23may00] remember status view width
515 	int i = m_statusView->Bounds().IntegerWidth();
516 	archive->AddInt32("statusViewWidth", i);
517 
518 //	entry_ref saveRef;
519 //	m_savePanel.GetPanelDirectory(&saveRef);
520 //	BEntry saveEntry(&saveRef);
521 //	if(saveEntry.InitCheck() == B_OK) {
522 //		BPath p;
523 //		saveEntry.GetPath(&p);
524 //		archive->AddString("saveDir", p.Path());
525 //	}
526 
527 	return B_OK;
528 }
529 
530 
531 //	#pragma mark - implementation
532 
533 
534 void
535 RouteWindow::_constrainToScreen()
536 {
537 	D_INTERNAL(("RouteWindow::_constrainToScreen()\n"));
538 
539 	BScreen screen(this);
540 	BRect screenRect = screen.Frame();
541 	BRect windowRect = Frame();
542 
543 	// if the window is outside the screen rect
544 	// move it to the default position
545 	if (!screenRect.Intersects(windowRect)) {
546 		windowRect.OffsetTo(screenRect.LeftTop());
547 		MoveTo(windowRect.LeftTop());
548 		windowRect = Frame();
549 	}
550 
551 	// if the window is larger than the screen rect
552 	// resize it to fit at each side
553 	if (!screenRect.Contains(windowRect)) {
554 		if (windowRect.left < screenRect.left) {
555 			windowRect.left = screenRect.left + 5.0;
556 			MoveTo(windowRect.LeftTop());
557 			windowRect = Frame();
558 		}
559 		if (windowRect.top < screenRect.top) {
560 			windowRect.top = screenRect.top + 5.0;
561 			MoveTo(windowRect.LeftTop());
562 			windowRect = Frame();
563 		}
564 		if (windowRect.right > screenRect.right) {
565 			windowRect.right = screenRect.right - 5.0;
566 		}
567 		if (windowRect.bottom > screenRect.bottom) {
568 			windowRect.bottom = screenRect.bottom - 5.0;
569 		}
570 		ResizeTo(windowRect.Width(), windowRect.Height());
571 	}
572 }
573 
574 
575 void
576 RouteWindow::_toggleTransportWindow()
577 {
578 	if (m_transportWindow) {
579 		m_transportWindowFrame = m_transportWindow->Frame();
580 		m_transportWindow->Lock();
581 		m_transportWindow->Quit();
582 		m_transportWindow = 0;
583 		m_transportWindowItem->SetMarked(false);
584 	} else {
585 		m_transportWindow = new TransportWindow(m_routingView->manager,
586 			this, "Transport");
587 
588 		// ask for a selection update
589 		BMessenger(m_routingView).SendMessage(
590 			MediaRoutingView::M_BROADCAST_SELECTION);
591 
592 		// place & display the window
593 		if (m_transportWindowFrame.IsValid()) {
594 			m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
595 			m_transportWindow->ResizeTo(m_transportWindowFrame.Width(),
596 				m_transportWindowFrame.Height());
597 		}
598 
599 		m_transportWindow->Show();
600 		m_transportWindowItem->SetMarked(true);
601 	}
602 }
603 
604 
605 void
606 RouteWindow::_togglePullPalettes()
607 {
608 	m_pullPalettesItem->SetMarked(!m_pullPalettesItem->IsMarked());
609 }
610 
611 
612 void
613 RouteWindow::_toggleDormantNodeWindow()
614 {
615 	if (m_dormantNodeWindow) {
616 		m_dormantNodeWindowFrame = m_dormantNodeWindow->Frame();
617 		m_dormantNodeWindow->Lock();
618 		m_dormantNodeWindow->Quit();
619 		m_dormantNodeWindow = 0;
620 		m_dormantNodeWindowItem->SetMarked(false);
621 	} else {
622 		m_dormantNodeWindow = new DormantNodeWindow(this);
623 		if (m_dormantNodeWindowFrame.IsValid()) {
624 			m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
625 			m_dormantNodeWindow->ResizeTo(m_dormantNodeWindowFrame.Width(),
626 				m_dormantNodeWindowFrame.Height());
627 		}
628 		m_dormantNodeWindow->Show();
629 		m_dormantNodeWindowItem->SetMarked(true);
630 	}
631 }
632 
633 
634 void
635 RouteWindow::_handleGroupSelected(BMessage* message)
636 {
637 	status_t err;
638 	uint32 groupID;
639 
640 	err = message->FindInt32("groupID", (int32*)&groupID);
641 	if (err < B_OK) {
642 		PRINT((
643 			"! RouteWindow::_handleGroupSelected(): no groupID in message!\n"));
644 		return;
645 	}
646 
647 	if (!m_transportWindow)
648 		return;
649 
650 	BMessage m(TransportWindow::M_SELECT_GROUP);
651 	m.AddInt32("groupID", groupID);
652 	BMessenger(m_transportWindow).SendMessage(&m);
653 
654 	m_selectedGroupID = groupID;
655 }
656 
657 
658 void
659 RouteWindow::_handleShowErrorMessage(BMessage* message)
660 {
661 	status_t err;
662 	BString text;
663 
664 	err = message->FindString("text", &text);
665 	if (err < B_OK) {
666 		PRINT((
667 			"! RouteWindow::_handleShowErrorMessage(): no text in message!\n"));
668 		return;
669 	}
670 
671 	m_statusView->setErrorMessage(text.String(), message->HasBool("error"));
672 }
673 
674 
675 //! Refresh the transport window for the given group, if any
676 void
677 RouteWindow::_refreshTransportSettings(BMessage* message)
678 {
679 	status_t err;
680 	uint32 groupID;
681 
682 	err = message->FindInt32("groupID", (int32*)&groupID);
683 	if (err < B_OK) {
684 		PRINT((
685 			"! RouteWindow::_refreshTransportSettings(): no groupID in message!\n"));
686 		return;
687 	}
688 
689 	if(m_transportWindow) {
690 		// relay the message
691 		BMessenger(m_transportWindow).SendMessage(message);
692 	}
693 }
694 
695 
696 void
697 RouteWindow::_closePalettes()
698 {
699 	BAutolock _l(this);
700 
701 	if (m_transportWindow) {
702 		m_transportWindow->Lock();
703 		m_transportWindow->Quit();
704 		m_transportWindow = 0;
705 	}
706 }
707 
708 
709 //!	Move all palette windows by the specified amounts
710 void RouteWindow::_movePalettesBy(float xDelta, float yDelta)
711 {
712 	if (m_transportWindow)
713 		m_transportWindow->MoveBy(xDelta, yDelta);
714 
715 	if (m_dormantNodeWindow)
716 		m_dormantNodeWindow->MoveBy(xDelta, yDelta);
717 }
718 
719 
720 //!	Toggle group playback
721 void
722 RouteWindow::_toggleGroupRolling()
723 {
724 	if (!m_selectedGroupID)
725 		return;
726 
727 	NodeGroup* g;
728 	status_t err = m_routingView->manager->findGroup(m_selectedGroupID, &g);
729 	if (err < B_OK)
730 		return;
731 
732 	Autolock _l(g);
733 	uint32 startAction = (g->runMode() == BMediaNode::B_OFFLINE)
734 		? NodeGroup::M_ROLL : NodeGroup::M_START;
735 
736 	BMessenger m(g);
737 	switch (g->transportState()) {
738 		case NodeGroup::TRANSPORT_STOPPED:
739 			m.SendMessage(startAction);
740 			break;
741 
742 		case NodeGroup::TRANSPORT_RUNNING:
743 		case NodeGroup::TRANSPORT_ROLLING:
744 			m.SendMessage(NodeGroup::M_STOP);
745 			break;
746 
747 		default:
748 			break;
749 	}
750 }
751