xref: /haiku/src/apps/cortex/MediaRoutingView/MediaRoutingView.cpp (revision 899e0ef82b5624ace2ccfa5f5a58c8ebee54aaef)
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 // MediaRoutingView.cpp
33 
34 #include "MediaRoutingView.h"
35 
36 // RouteApp
37 #include "RouteApp.h"
38 #include "RouteAppNodeManager.h"
39 #include "RouteWindow.h"
40 #include "NodeSetIOContext.h"
41 // DormantNodeView
42 #include "DormantNodeView.h"
43 // InfoWindow
44 #include "InfoWindowManager.h"
45 // TransportWindow
46 #include "TransportWindow.h"
47 // MediaRoutingView
48 #include "MediaNodePanel.h"
49 #include "MediaWire.h"
50 // NodeManager
51 #include "NodeGroup.h"
52 #include "NodeRef.h"
53 #include "Connection.h"
54 // ParameterWindow
55 #include "ParameterWindowManager.h"
56 
57 // Application Kit
58 #include <Application.h>
59 // Interface Kit
60 #include <Alert.h>
61 #include <Bitmap.h>
62 #include <MenuItem.h>
63 #include <PopUpMenu.h>
64 #include <Window.h>
65 // Locale Kit
66 #undef B_CATALOG
67 #define B_CATALOG (&sCatalog)
68 #include <Catalog.h>
69 // Media Kit
70 #include <MediaRoster.h>
71 // Storage Kit
72 #include <File.h>
73 #include <NodeInfo.h>
74 #include <Path.h>
75 // Translation Kit
76 #include <BitmapStream.h>
77 #include <TranslatorRoster.h>
78 
79 #undef B_TRANSLATION_CONTEXT
80 #define B_TRANSLATION_CONTEXT "MediaRoutingView"
81 
82 __USE_CORTEX_NAMESPACE
83 
84 #include <Debug.h>
85 #define D_METHOD(x) //PRINT (x)
86 #define D_MESSAGE(x) //PRINT (x)
87 #define D_MOUSE(x) //PRINT (x)
88 #define D_KEY(x) //PRINT (x)
89 
90 static BCatalog sCatalog("x-vnd.Cortex.MediaRoutingView");
91 
92 // ---------------------------------------------------------------- //
93 // constants
94 // ---------------------------------------------------------------- //
95 
96 float	MediaRoutingView::M_CLEANUP_H_GAP			= 25.0;
97 float	MediaRoutingView::M_CLEANUP_V_GAP			= 10.0;
98 float	MediaRoutingView::M_CLEANUP_H_MARGIN 		= 15.0;
99 float	MediaRoutingView::M_CLEANUP_V_MARGIN 		= 10.0;
100 
101 // ---------------------------------------------------------------- //
102 // m_inactiveNodeState content
103 // ---------------------------------------------------------------- //
104 
105 struct _inactive_node_state_entry {
106 	_inactive_node_state_entry(
107 		const char* _name, int32 _kind, const BMessage& _state) :
108 		name(_name), kind(_kind), state(_state) {}
109 
110 	BString name;
111 	uint32 kind;
112 	BMessage state;
113 };
114 
115 // ---------------------------------------------------------------- //
116 // ctor/dtor
117 // ---------------------------------------------------------------- //
118 
119 MediaRoutingView::MediaRoutingView(
120 	RouteAppNodeManager *nodeManager,
121 	BRect frame,
122 	const char *name,
123 	uint32 resizeMode)
124 	: DiagramView(frame, "MediaRoutingView", true, B_FOLLOW_ALL_SIDES),
125 	  manager(nodeManager),
126 	  m_layout(M_ICON_VIEW),
127 	  m_nextGroupNumber(1),
128 	  m_lastDroppedNode(0),
129 	  m_draggedWire(0)
130 {
131 	D_METHOD(("MediaRoutingView::MediaRoutingView()\n"));
132 	ASSERT(manager);
133 
134 	setBackgroundColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_2_TINT));
135 	SetItemAlignment(5.0, 5.0);
136 	_initLayout();
137 }
138 
139 MediaRoutingView::~MediaRoutingView() {
140 	D_METHOD(("MediaRoutingView::~MediaRoutingView()\n"));
141 
142 	_emptyInactiveNodeState();
143 
144 	// quit ParameterWindowManager if necessary
145 	ParameterWindowManager::shutDown();
146 
147 	// quit InfoWindowManager if necessary
148 	InfoWindowManager::shutDown();
149 }
150 
151 // -------------------------------------------------------- //
152 // *** derived from DiagramView
153 // -------------------------------------------------------- //
154 
155 void MediaRoutingView::connectionAborted(
156 	DiagramEndPoint *fromWhich)
157 {
158 	D_METHOD(("MediaRoutingView::connectionAborted()\n"));
159 
160 	be_app->SetCursor(B_HAND_CURSOR);
161 }
162 
163 void MediaRoutingView::connectionEstablished(
164 	DiagramEndPoint *fromWhich,
165 	DiagramEndPoint *toWhich)
166 {
167 	D_METHOD(("MediaRoutingView::connectionEstablished()\n"));
168 
169 	be_app->SetCursor(B_HAND_CURSOR);
170 }
171 
172 DiagramWire *MediaRoutingView::createWire(
173 	DiagramEndPoint *fromWhich,
174 	DiagramEndPoint *toWhich)
175 {
176 	D_METHOD(("MediaRoutingView::createWire()\n"));
177 
178 	MediaJack *outputJack, *inputJack;
179 	MediaJack *jack = dynamic_cast<MediaJack *>(fromWhich);
180 
181 	if (jack == NULL)
182 		return 0;
183 
184 	if (jack->isOutput())
185 	{
186 		outputJack = jack;
187 		inputJack = dynamic_cast<MediaJack *>(toWhich);
188 		if (!inputJack || !inputJack->isInput())
189 		{
190 			return 0;
191 		}
192 	}
193 	else
194 	{
195 		inputJack = jack;
196 		outputJack = dynamic_cast<MediaJack *>(toWhich);
197 		if (!outputJack || !outputJack->isOutput())
198 		{
199 			return 0;
200 		}
201 	}
202 	if (!outputJack->isConnected() && !inputJack->isConnected())
203 	{
204 		media_output output;
205 		media_input input;
206 		if ((outputJack->getOutput(&output) == B_OK)
207 		 && (inputJack->getInput(&input) == B_OK))
208 		{
209 			status_t error;
210 			Connection connection;
211 			error = manager->connect(output, input, &connection);
212 			if (error)
213 			{
214 				showErrorMessage(B_TRANSLATE("Could not connect"), error);
215 			}
216 		}
217 	}
218 	return 0;
219 }
220 
221 DiagramWire *MediaRoutingView::createWire(
222 	DiagramEndPoint *fromWhich)
223 {
224 	D_METHOD(("MediaRoutingView::createWire(temp)\n"));
225 
226 	MediaJack *jack = dynamic_cast<MediaJack *>(fromWhich);
227 	if (jack)
228 	{
229 		if (jack->isOutput()) // this is the start point
230 		{
231 			return new MediaWire(jack, true);
232 		}
233 		else
234 		{
235 			return new MediaWire(jack, false);
236 		}
237 	}
238 	return 0;
239 }
240 
241 
242 void
243 MediaRoutingView::BackgroundMouseDown(BPoint point, uint32 buttons,
244 	uint32 clicks)
245 {
246 	D_MOUSE(("MediaRoutingView::BackgroundMouseDown()\n"));
247 
248 	if ((buttons == B_SECONDARY_MOUSE_BUTTON)
249 	 || (modifiers() & B_CONTROL_KEY)) {
250 		EndRectTracking();
251 		showContextMenu(point);
252 	}
253 }
254 
255 
256 void
257 MediaRoutingView::MessageDropped(BPoint point, BMessage *message)
258 {
259 	D_METHOD(("MediaRoutingView::MessageDropped()\n"));
260 
261 	switch (message->what) {
262 		case DormantNodeView::M_INSTANTIATE_NODE:
263 		{
264 			D_MESSAGE(("MediaRoutingView::MessageDropped(DormantNodeView::M_INSTANTIATE_NODE)\n"));
265 			type_code type;
266 			int32 count;
267 			if (message->GetInfo("which", &type, &count) == B_OK) {
268 				for (int32 n = 0; n < count; n++) {
269 					dormant_node_info info;
270 					const void *data;
271 					ssize_t dataSize;
272 					if (message->FindData("which", B_RAW_TYPE, &data, &dataSize) == B_OK) {
273 						memcpy(reinterpret_cast<void *>(&info), data, dataSize);
274 						NodeRef* droppedNode;
275 						status_t error;
276 						error = manager->instantiate(info, &droppedNode);
277 						if (!error) {
278 							m_lastDroppedNode = droppedNode->id();
279 							BPoint dropPoint, dropOffset;
280 							dropPoint = message->DropPoint(&dropOffset);
281 							m_lastDropPoint = Align(ConvertFromScreen(dropPoint - dropOffset));
282 						} else {
283 							BString s = B_TRANSLATE(
284 								"Could not instantiate '%infoname%'");
285 							s.ReplaceFirst("%infoname%", info.name);
286 							showErrorMessage(s, error);
287 						}
288 					}
289 				}
290 			}
291 			break;
292 		}
293 
294 		case B_SIMPLE_DATA:
295 		{
296 			D_MESSAGE(("MediaRoutingView::MessageDropped(B_SIMPLE_DATA)\n"));
297 			entry_ref fileRef;
298 			if (message->FindRef("refs", &fileRef) == B_OK)
299 				_checkDroppedFile(&fileRef, ConvertFromScreen(message->DropPoint()));
300 			break;
301 		}
302 
303 		case B_PASTE:
304 		{
305 			D_MESSAGE(("MediaRoutingView::MessageDropped(B_PASTE)\n"));
306 			ssize_t size;
307 			const rgb_color *color; // [e.moon 21nov99] fixed const error
308 			if (message->FindData("RGBColor", B_RGB_COLOR_TYPE,
309 				reinterpret_cast<const void **>(&color), &size) == B_OK)
310 				_changeBackground(*color);
311 			break;
312 		}
313 
314 		default:
315 			DiagramView::MessageDropped(point, message);
316 	}
317 }
318 
319 
320 void
321 MediaRoutingView::SelectionChanged()
322 {
323 	D_METHOD(("MediaRoutingView::selectionChanged()\n"));
324 	_broadcastSelection();
325 }
326 
327 // ---------------------------------------------------------------- //
328 // *** BView impl.
329 // ---------------------------------------------------------------- //
330 
331 void MediaRoutingView::AttachedToWindow()
332 {
333 	D_METHOD(("MediaRoutingView::AttachedToWindow()\n"));
334 	_inherited::AttachedToWindow();
335 
336 	// attach to manager
337 	ASSERT(manager);
338 	add_observer(this, manager);
339 
340 	// add the context-menu shortcuts to the window
341 	_addShortcuts();
342 
343 	// populate with existing nodes & connections
344 	_initContent();
345 
346 	// [e.moon 29nov99] moved from AllAttached()
347 	cleanUp();
348 }
349 
350 void MediaRoutingView::AllAttached()
351 {
352 	D_METHOD(("MediaRoutingView::AllAttached()\n"));
353 	_inherited::AllAttached();
354 
355 	_adjustScrollBars();
356 
357 	// grab keyboard events
358 	MakeFocus();
359 }
360 
361 void MediaRoutingView::DetachedFromWindow()
362 {
363 	D_METHOD(("MediaRoutingView::DetachedFromWindow()\n"));
364 	_inherited::DetachedFromWindow();
365 
366 	// detach from manager
367 	if (manager)
368 	{
369 		Autolock lock(manager);
370 		void *cookie = 0;
371 		NodeRef *ref;
372 		while (manager->getNextRef(&ref, &cookie) == B_OK)
373 		{
374 			remove_observer(this, ref);
375 		}
376 		remove_observer(this, manager);
377 		const_cast<RouteAppNodeManager *&>(manager) = 0;
378 	}
379 }
380 
381 void MediaRoutingView::KeyDown(
382 	const char *bytes,
383 	int32 numBytes)
384 {
385 	D_METHOD(("MediaRoutingView::KeyDown()\n"));
386 
387 	switch (bytes[0])
388 	{
389 		case B_ENTER:
390 		{
391 			D_KEY(("MediaRoutingView::KeyDown(B_ENTER)\n"));
392 			_openParameterWindowsForSelection();
393 			break;
394 		}
395 		case B_DELETE:
396 		{
397 			D_KEY(("MediaRoutingView::KeyDown(B_DELETE)\n"));
398 			_deleteSelection();
399 			break;
400 		}
401 		case B_SPACE:
402 		{
403 			// [e.moon 1dec99]
404 			BWindow* w = Window();
405 			ASSERT(w);
406 			BMessenger(w).SendMessage(RouteWindow::M_TOGGLE_GROUP_ROLLING);
407 			break;
408 		}
409 		default:
410 		{
411 			DiagramView::KeyDown(bytes, numBytes);
412 			break;
413 		}
414 	}
415 }
416 
417 void MediaRoutingView::Pulse()
418 {
419 	// do the animation
420 }
421 
422 // ---------------------------------------------------------------- //
423 // BHandler impl
424 // ---------------------------------------------------------------- //
425 
426 void MediaRoutingView::MessageReceived(
427 	BMessage* message)
428 {
429 	D_METHOD(("MediaRoutingView::MessageReceived()\n"));
430 
431 	switch (message->what)
432 	{
433 		case B_MEDIA_NODE_CREATED:
434 		{
435 			D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_NODE_CREATED)\n"));
436 			type_code type;
437 			int32 count;
438 			if (message->GetInfo("media_node_id", &type, &count) == B_OK)
439 			{
440 				for(int32 n = 0; n < count; n++)
441 				{
442 					int32 id;
443 					if (message->FindInt32("media_node_id", n, &id) == B_OK)
444 					{
445 						// [e.moon 8dec99] allow for existing panel
446 						MediaNodePanel* panel;
447 						if(_findPanelFor(id, &panel) < B_OK)
448 							_addPanelFor(id, BPoint(5.0, 5.0));
449 					}
450 				}
451 			}
452 			break;
453 		}
454 		case B_MEDIA_NODE_DELETED:
455 		{
456 			D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_NODE_DELETED)\n"));
457 			type_code type;
458 			int32 count;
459 			if (message->GetInfo("media_node_id", &type, &count) == B_OK)
460 			{
461 				for (int32 n = 0; n < count; n++)
462 				{
463 					int32 id;
464 					if (message->FindInt32("media_node_id", n, &id) == B_OK)
465 					{
466 						_removePanelFor(id);
467 					}
468 				}
469 			}
470 			break;
471 		}
472 		case B_MEDIA_CONNECTION_MADE:
473 		{
474 			D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_CONNECTION_MADE)\n"));
475 			type_code type;
476 			int32 count;
477 			if (message->GetInfo("output", &type, &count) == B_OK)
478 			{
479 				for (int32 n = 0; n < count; n++)
480 				{
481 					media_output output;
482 					const void *data;
483 					ssize_t dataSize;
484 					if (message->FindData("output", B_RAW_TYPE, n, &data, &dataSize) == B_OK)
485 					{
486 						output = *reinterpret_cast<const media_output *>(data);
487 						Connection connection;
488 						if (manager->findConnection(output.node.node, output.source, &connection) == B_OK)
489 						{
490 							_addWireFor(connection);
491 						}
492 					}
493 				}
494 			}
495 			break;
496 		}
497 		case B_MEDIA_CONNECTION_BROKEN:
498 		{
499 			D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_CONNECTION_BROKEN)\n"));
500 			type_code type;
501 			int32 count;
502 			if (message->GetInfo("__connection_id", &type, &count) == B_OK)
503 			{
504 				for (int32 n = 0; n < count; n++)
505 				{
506 					int32 id;
507 					if (message->FindInt32("__connection_id", n, &id) == B_OK)
508 					{
509 						_removeWireFor(id);
510 					}
511 				}
512 			}
513 			break;
514 		}
515 		case B_MEDIA_FORMAT_CHANGED:
516 		{
517 			D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_FORMAT_CHANGED)\n"));
518 
519 			media_node_id nodeID;
520 			if(message->FindInt32("__source_node_id", &nodeID) < B_OK)
521 				break;
522 
523 			uint32 connectionID;
524 			if(message->FindInt32("__connection_id", (int32*)&connectionID) < B_OK)
525 				break;
526 
527 			media_source* source;
528 			ssize_t dataSize;
529 			if(message->FindData("be:source", B_RAW_TYPE, (const void**)&source, &dataSize) < B_OK)
530 				break;
531 
532 			MediaWire* wire;
533 			if(_findWireFor(connectionID, &wire) == B_OK) {
534 				// copy new connection data
535 				manager->findConnection(nodeID, *source, &wire->connection);
536 			}
537 			break;
538 		}
539 		case M_CLEANUP_REQUESTED:
540 		{
541 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_M_CLEANUP_REQUESTED)\n"));
542 			cleanUp();
543 			break;
544 		}
545 		case M_SELECT_ALL:
546 		{
547 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_SELECT_ALL)\n"));
548 			SelectAll(DiagramItem::M_BOX);
549 			break;
550 		}
551 		case M_DELETE_SELECTION:
552 		{
553 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_DELETE_SELECTION)\n"));
554 			_deleteSelection();
555 			break;
556 		}
557 		case M_NODE_CHANGE_CYCLING:
558 		{
559 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_CYCLING_CHANGED)\n"));
560 			bool cycle;
561 			if (message->FindBool("cycle", &cycle) == B_OK)
562 			{
563 				_changeCyclingForSelection(cycle);
564 			}
565 			break;
566 		}
567 		case M_NODE_CHANGE_RUN_MODE:
568 		{
569 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_RUNMODE_CHANGED)\n"));
570 			int32 mode;
571 			if (message->FindInt32("run_mode", &mode) == B_OK)
572 			{
573 				_changeRunModeForSelection(static_cast<uint32>(mode));
574 			}
575 			break;
576 		}
577 		case M_LAYOUT_CHANGED:
578 		{
579 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_LAYOUT_CHANGED)\n"));
580 			layout_t layout;
581 			if (message->FindInt32("layout", (int32*)&layout) == B_OK)
582 			{
583 				if (layout != m_layout)
584 				{
585 					layoutChanged(layout);
586 					updateDataRect();
587 					Invalidate();
588 				}
589 			}
590 			break;
591 		}
592 		case M_NODE_START_TIME_SOURCE:
593 		{
594 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_START_TIME_SOURCE)\n"));
595 			int32 id;
596 			if(message->FindInt32("nodeID", &id) < B_OK)
597 				break;
598 			NodeRef* ref;
599 			if(manager->getNodeRef(id, &ref) < B_OK)
600 				break;
601 
602 			bigtime_t when = system_time();
603 			status_t err = manager->roster->StartTimeSource(ref->node(), when);
604 			if(err < B_OK) {
605 				PRINT((
606 					"! StartTimeSource(%" B_PRId32 "): '%s'\n",
607 					ref->id(), strerror(err)));
608 			}
609 			break;
610 		}
611 		case M_NODE_STOP_TIME_SOURCE:
612 		{
613 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_STOP_TIME_SOURCE)\n"));
614 			int32 id;
615 			if(message->FindInt32("nodeID", &id) < B_OK)
616 				break;
617 			NodeRef* ref;
618 			if(manager->getNodeRef(id, &ref) < B_OK)
619 				break;
620 
621 			bigtime_t when = system_time();
622 			status_t err = manager->roster->StopTimeSource(ref->node(), when);
623 			if(err < B_OK) {
624 				PRINT((
625 					"! StopTimeSource(%" B_PRId32 "): '%s'\n",
626 					ref->id(), strerror(err)));
627 			}
628 			break;
629 		}
630 		case M_NODE_TWEAK_PARAMETERS: {
631 			D_MESSAGE((" -> M_NODE_TWEAK_PARAMETERS\n"));
632 			_openParameterWindowsForSelection();
633 			break;
634 		}
635 		case M_NODE_START_CONTROL_PANEL: {
636 			D_MESSAGE((" -> M_NODE_START_CONTROL_PANEL\n"));
637 			_startControlPanelsForSelection();
638 			break;
639 		}
640 		case M_GROUP_SET_LOCKED:
641 		{
642 			D_MESSAGE(("MediaRoutingView::MessageReceived(M_GROUP_SET_LOCKED)\n"));
643 			int32 groupID;
644 			if(message->FindInt32("groupID", &groupID) < B_OK)
645 				break;
646 			bool locked;
647 			if(message->FindBool("locked", &locked) < B_OK)
648 				break;
649 			NodeGroup* group;
650 			if(manager->findGroup(groupID, &group) < B_OK)
651 				break;
652 			uint32 f = locked ?
653 				group->groupFlags() | NodeGroup::GROUP_LOCKED :
654 				group->groupFlags() & ~NodeGroup::GROUP_LOCKED;
655 			group->setGroupFlags(f);
656 			break;
657 		}
658 		case M_BROADCAST_SELECTION: {
659 			D_MESSAGE((" -> M_BROADCAST_SELECTION\n"));
660 			_broadcastSelection();
661 			break;
662 		}
663 		case InfoWindowManager::M_INFO_WINDOW_REQUESTED:
664 		{
665 			D_MESSAGE(("MediaRoutingView::MessageReceived(InfoView::M_INFO_WINDOW_REQUESTED)\n"));
666 			type_code type;
667 			int32 count;
668 			if (message->GetInfo("input", &type, &count) == B_OK)
669 			{
670 				for (int32 i = 0; i < count; i++)
671 				{
672 					media_input input;
673 					const void *data;
674 					ssize_t dataSize;
675 					if (message->FindData("input", B_RAW_TYPE, i, &data, &dataSize) == B_OK)
676 					{
677 						input = *reinterpret_cast<const media_input *>(data);
678 						InfoWindowManager *manager = InfoWindowManager::Instance();
679 						if (manager && manager->Lock()) {
680 							manager->openWindowFor(input);
681 							manager->Unlock();
682 						}
683 					}
684 				}
685 			}
686 			else if (message->GetInfo("output", &type, &count) == B_OK)
687 			{
688 				for (int32 i = 0; i < count; i++)
689 				{
690 					media_output output;
691 					const void *data;
692 					ssize_t dataSize;
693 					if (message->FindData("output", B_RAW_TYPE, i, &data, &dataSize) == B_OK)
694 					{
695 						output = *reinterpret_cast<const media_output *>(data);
696 						InfoWindowManager *manager = InfoWindowManager::Instance();
697 						if (manager && manager->Lock()) {
698 							manager->openWindowFor(output);
699 							manager->Unlock();
700 						}
701 					}
702 				}
703 			}
704 			else
705 			{
706 				_openInfoWindowsForSelection();
707 			}
708 			break;
709 		}
710 		case NodeManager::M_RELEASED:
711 		{
712 			D_MESSAGE(("MediaRoutingView::MessageReceived(NodeManager::M_RELEASED)\n"));
713 			remove_observer(this, manager);
714 			const_cast<RouteAppNodeManager*&>(manager) = 0;
715 			// +++++ disable view!
716 			break;
717 		}
718 		case NodeRef::M_RELEASED:
719 		{
720 			D_MESSAGE(("MediaRoutingView::MessageReceived(NodeRef::M_RELEASED)\n"));
721 			// only relevant on shutdown; do nothing
722 			break;
723 		}
724 		default:
725 		{
726 			DiagramView::MessageReceived(message);
727 		}
728 	}
729 }
730 
731 // ---------------------------------------------------------------- //
732 // *** operations (public)
733 // ---------------------------------------------------------------- //
734 
735 BPoint MediaRoutingView::findFreePositionFor(
736 	const MediaNodePanel* panel) const
737 {
738 	D_METHOD(("MediaRoutingView::_findFreeSpotFor()\n"));
739 
740 	BPoint p(M_CLEANUP_H_MARGIN, M_CLEANUP_V_MARGIN);
741 	if (panel)
742 	{
743 		switch (m_layout)
744 		{
745 			case M_ICON_VIEW:
746 			{
747 				// find the target column by node_kind
748 				p.x += M_CLEANUP_H_GAP + MediaNodePanel::M_DEFAULT_WIDTH;
749 				if (panel->ref->kind() & B_BUFFER_PRODUCER)
750 				{
751 					p.x -= M_CLEANUP_H_GAP + MediaNodePanel::M_DEFAULT_WIDTH;
752 				}
753 				if (panel->ref->kind() & B_BUFFER_CONSUMER)
754 				{
755 					p.x += M_CLEANUP_H_GAP + MediaNodePanel::M_DEFAULT_WIDTH;
756 				}
757 				// find the bottom item in the column
758 				float bottom = 0.0;
759 				for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
760 				{
761 					BRect r = ItemAt(i, DiagramItem::M_BOX)->Frame();
762 					if ((r.left >= p.x)
763 					 && (r.left <= p.x + MediaNodePanel::M_DEFAULT_WIDTH))
764 					{
765 						bottom = (r.bottom > bottom) ? r.bottom : bottom;
766 					}
767 				}
768 				if (bottom >= p.y)
769 				{
770 					p.y = bottom + M_CLEANUP_V_GAP;
771 				}
772 				break;
773 			}
774 			case M_MINI_ICON_VIEW:
775 			{
776 				// find the target row by node_kind
777 				p.y += M_CLEANUP_V_GAP + MediaNodePanel::M_DEFAULT_HEIGHT;
778 				if (panel->ref->kind() & B_BUFFER_PRODUCER)
779 				{
780 					p.y -= M_CLEANUP_V_GAP + MediaNodePanel::M_DEFAULT_HEIGHT;
781 				}
782 				if (panel->ref->kind() & B_BUFFER_CONSUMER)
783 				{
784 					p.y += M_CLEANUP_V_GAP + MediaNodePanel::M_DEFAULT_HEIGHT;
785 				}
786 				// find the right-most item in the row
787 				float right = 0.0;
788 				for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
789 				{
790 					BRect r = ItemAt(i, DiagramItem::M_BOX)->Frame();
791 					if ((r.top >= p.y)
792 					 && (r.top <= p.y + MediaNodePanel::M_DEFAULT_HEIGHT))
793 					{
794 						right = (r.right > right) ? r.right : right;
795 					}
796 				}
797 				if (right >= p.x)
798 				{
799 					p.x = right + M_CLEANUP_H_GAP;
800 				}
801 				break;
802 			}
803 		}
804 	}
805 	return p;
806 }
807 
808 // ---------------------------------------------------------------- //
809 // *** operations (protected)
810 // ---------------------------------------------------------------- //
811 
812 void MediaRoutingView::layoutChanged(
813 	layout_t layout)
814 {
815 	D_METHOD(("MediaRoutingView::layoutChanged()\n"));
816 
817 	switch (layout)
818 	{
819 		case M_ICON_VIEW:
820 		{
821 			float temp;
822 
823 			// swap the cleanup defaults
824 			temp = M_CLEANUP_H_GAP;
825 			M_CLEANUP_H_GAP = M_CLEANUP_V_GAP;
826 			M_CLEANUP_V_GAP = temp;
827 			temp = M_CLEANUP_H_MARGIN;
828 			M_CLEANUP_H_MARGIN = M_CLEANUP_V_MARGIN;
829 			M_CLEANUP_V_MARGIN = temp;
830 
831 			// swap the default dimensions for MediaJacks
832 			temp = MediaJack::M_DEFAULT_WIDTH;
833 			MediaJack::M_DEFAULT_WIDTH = MediaJack::M_DEFAULT_HEIGHT;
834 			MediaJack::M_DEFAULT_HEIGHT = temp;
835 
836 			// Add space for the 3-letter i/o-abbreviation
837 			BFont font(be_plain_font);
838 			font.SetSize(font.Size() - 2.0);
839 			for (int i = 0; i < MediaJack::M_MAX_ABBR_LENGTH; i++)
840 			{
841 				MediaJack::M_DEFAULT_WIDTH += font.StringWidth("M");
842 			}
843 			MediaJack::M_DEFAULT_WIDTH += 2.0; // add some padding
844 
845 			// Adjust the default size for MediaNodePanels
846 			float labelWidth, bodyWidth;
847 			float labelHeight, bodyHeight;
848 			font_height fh;
849 			be_plain_font->GetHeight(&fh);
850 			labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
851 						 + be_plain_font->StringWidth(" Be Audio Mixer ");
852 			bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_LARGE_ICON
853 						+ 2 * MediaJack::M_DEFAULT_WIDTH;
854 			labelHeight = 2 * MediaNodePanel::M_LABEL_V_MARGIN
855 						  + fh.ascent + fh.descent + fh.leading + 1.0;
856 			bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_LARGE_ICON;
857 			MediaNodePanel::M_DEFAULT_WIDTH = labelWidth > bodyWidth ? labelWidth : bodyWidth;
858 			MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight + bodyHeight;
859 			Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);
860 			break;
861 		}
862 		case M_MINI_ICON_VIEW:
863 		{
864 			float temp;
865 
866 			// Swap the cleanup defaults
867 			temp = M_CLEANUP_H_GAP;
868 			M_CLEANUP_H_GAP = M_CLEANUP_V_GAP;
869 			M_CLEANUP_V_GAP = temp;
870 			temp = M_CLEANUP_H_MARGIN;
871 			M_CLEANUP_H_MARGIN = M_CLEANUP_V_MARGIN;
872 			M_CLEANUP_V_MARGIN = temp;
873 
874 			// Subtract space for the 3-letter i/o-abbreviation
875 			BFont font(be_plain_font);
876 			font.SetSize(font.Size() - 2.0);
877 			for (int i = 0; i < MediaJack::M_MAX_ABBR_LENGTH; i++)
878 			{
879 				MediaJack::M_DEFAULT_WIDTH -= font.StringWidth("M");
880 			}
881 			MediaJack::M_DEFAULT_WIDTH -= 2.0; // substract the padding
882 
883 			// Swap the default dimensions for MediaJacks
884 			temp = MediaJack::M_DEFAULT_WIDTH;
885 			MediaJack::M_DEFAULT_WIDTH = MediaJack::M_DEFAULT_HEIGHT;
886 			MediaJack::M_DEFAULT_HEIGHT = temp;
887 
888 			// Adjust the default size for MediaNodePanels
889 			float labelWidth, bodyWidth;
890 			float labelHeight, bodyHeight;
891 			font_height fh;
892 			be_plain_font->GetHeight(&fh);
893 			labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
894 						 + be_plain_font->StringWidth(" Be Audio Mixer ");
895 			bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_MINI_ICON;
896 			labelHeight = 3 * MediaNodePanel::M_LABEL_V_MARGIN
897 						  + fh.ascent + fh.descent + fh.leading
898 						  + 2 * MediaJack::M_DEFAULT_HEIGHT;
899 			bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_MINI_ICON;
900 			MediaNodePanel::M_DEFAULT_WIDTH = labelWidth + bodyWidth;
901 			MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight > bodyHeight ? labelHeight : bodyHeight;
902 			Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);
903 			break;
904 		}
905 	}
906 	m_layout = layout;
907 	for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
908 	{
909 		MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(ItemAt(i, DiagramItem::M_BOX));
910 		if (panel)
911 		{
912 			panel->layoutChanged(layout);
913 		}
914 	}
915 
916 	_adjustScrollBars();
917 }
918 
919 void MediaRoutingView::cleanUp()
920 {
921 	D_METHOD(("MediaRoutingView::cleanUp()\n"));
922 
923 	SortItems(DiagramItem::M_BOX, compareID);
924 
925 	// move all the panels offscreen
926 	for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
927 	{
928 		ItemAt(i, DiagramItem::M_BOX)->moveTo(BPoint(-200.0, -200.0));
929 	}
930 
931 	// move all panels to their 'ideal' position
932 	for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
933 	{
934 		MediaNodePanel *panel;
935 		panel = dynamic_cast<MediaNodePanel *>(ItemAt(i, DiagramItem::M_BOX));
936 		BPoint p = findFreePositionFor(panel);
937 		panel->moveTo(p);
938 	}
939 
940 	SortItems(DiagramItem::M_BOX, compareSelectionTime);
941 	Invalidate();
942 	updateDataRect();
943 }
944 
945 void MediaRoutingView::showContextMenu(
946 	BPoint point)
947 {
948 	D_METHOD(("MediaRoutingView::showContextMenu()\n"));
949 
950 	BPopUpMenu *menu = new BPopUpMenu("MediaRoutingView PopUp", false, false, B_ITEMS_IN_COLUMN);
951 	menu->SetFont(be_plain_font);
952 
953 	// add layout options
954 	BMenuItem *item;
955 	BMessage *message = new BMessage(M_LAYOUT_CHANGED);
956 	message->AddInt32("layout", M_ICON_VIEW);
957 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Icon view"), message));
958 	if (m_layout == M_ICON_VIEW)
959 	{
960 		item->SetMarked(true);
961 	}
962 	message = new BMessage(M_LAYOUT_CHANGED);
963 	message->AddInt32("layout", M_MINI_ICON_VIEW);
964 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Mini icon view"), message));
965 	if (m_layout == M_MINI_ICON_VIEW)
966 	{
967 		item->SetMarked(true);
968 	}
969 	menu->AddSeparatorItem();
970 
971 	// add 'CleanUp' command
972 	menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"),
973 		new BMessage(M_CLEANUP_REQUESTED), 'K'));
974 
975 	// add 'Select All' command
976 	menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
977 		new BMessage(M_SELECT_ALL), 'A'));
978 
979 	menu->SetTargetForItems(this);
980 	ConvertToScreen(&point);
981 	point -= BPoint(1.0, 1.0);
982 	menu->Go(point, true, true, true);
983 }
984 
985 void MediaRoutingView::showErrorMessage(
986 	BString text,
987 	status_t error)
988 {
989 	D_METHOD(("MediaRoutingView::showErrorMessage()\n"));
990 
991 	if (error) {
992 		text << " (" << strerror(error) << ")";
993 	}
994 
995 	BMessage message(M_SHOW_ERROR_MESSAGE);
996 	message.AddString("text", text.String());
997 	if (error) {
998 		message.AddBool("error", true);
999 	}
1000 	BMessenger messenger(0, Window());
1001 	if (!messenger.IsValid()
1002 	 || (messenger.SendMessage(&message) != B_OK)) {
1003 		BAlert *alert = new BAlert(B_TRANSLATE("Error"), text.String(),
1004 			B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1005 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1006 		alert->Go();
1007 	}
1008 }
1009 
1010 // -------------------------------------------------------- //
1011 // *** IStateArchivable
1012 // -------------------------------------------------------- //
1013 
1014 status_t MediaRoutingView::importState(
1015 	const BMessage*						archive) {
1016 
1017 	status_t err;
1018 
1019 	_emptyInactiveNodeState();
1020 
1021 	layout_t layout;
1022 	err = archive->FindInt32("layout", (int32*)&layout);
1023 	if(err == B_OK && layout != m_layout) {
1024 		layoutChanged(layout);
1025 	}
1026 
1027 	const char* path;
1028 	err = archive->FindString("bgBitmap", &path);
1029 	if(err == B_OK) {
1030 		BEntry entry(path);
1031 		entry_ref ref;
1032 		err = entry.GetRef(&ref);
1033 		if(err == B_OK)
1034 			_changeBackground(&ref);
1035 	}
1036 	else {
1037 		rgb_color color;
1038 		color.alpha = 255;
1039 		if(
1040 			archive->FindInt8("bgRed", (int8*)&color.red) == B_OK &&
1041 			archive->FindInt8("bgGreen", (int8*)&color.green) == B_OK &&
1042 			archive->FindInt8("bgBlue", (int8*)&color.blue) == B_OK)
1043 				_changeBackground(color);
1044 	}
1045 
1046 	for(int32 n = 0; ; ++n) {
1047 
1048 		// find panel state info; stop when exhausted
1049 		BMessage m;
1050 		err = archive->FindMessage("panel", n, &m);
1051 		if(err < B_OK)
1052 			break;
1053 
1054 		const char* nodeName;
1055 		err = archive->FindString("nodeName", n, &nodeName);
1056 		if(err < B_OK)
1057 			break;
1058 
1059 		uint32 nodeKind;
1060 		err = archive->FindInt32("nodeKind", n, (int32*)&nodeKind);
1061 		if(err < B_OK)
1062 			break;
1063 
1064 		// look up matching panel +++++ SLOW +++++
1065 		uint32 panelIndex;
1066 		uint32 items = CountItems(DiagramItem::M_BOX);
1067 		for(
1068 			panelIndex = 0;
1069 			panelIndex < items;
1070 			++panelIndex) {
1071 
1072 			MediaNodePanel* panel = dynamic_cast<MediaNodePanel*>(
1073 				ItemAt(panelIndex, DiagramItem::M_BOX));
1074 
1075 			if(panel &&
1076 				!strcmp(panel->ref->name(), nodeName) &&
1077 				panel->ref->kind() == nodeKind) {
1078 
1079 				// found match; hand message to panel
1080 				panel->importState(&m);
1081 				break;
1082 			}
1083 		}
1084 		if(panelIndex == items) {
1085 			// no panel found
1086 			// if a "system node" hang onto (and re-export) state info
1087 			bool sysOwned;
1088 			if(m.FindBool("sysOwned", &sysOwned) == B_OK && sysOwned) {
1089 				m_inactiveNodeState.AddItem(
1090 					new _inactive_node_state_entry(
1091 						nodeName, nodeKind, m));
1092 			}
1093 		}
1094 	}
1095 
1096 	updateDataRect();
1097 
1098 	return B_OK;
1099 }
1100 
1101 // +++++ export state info for currently inactive system nodes +++++
1102 status_t MediaRoutingView::exportState(
1103 	BMessage*									archive) const {
1104 
1105 	// store layout mode
1106 	archive->AddInt32("layout", m_layout);
1107 
1108 	// store background settings
1109 	if(m_backgroundBitmapEntry.InitCheck() == B_OK) {
1110 		BPath path;
1111 		m_backgroundBitmapEntry.GetPath(&path);
1112 		archive->AddString("bgBitmap", path.Path());
1113 	} else {
1114 		rgb_color c = backgroundColor();
1115 		archive->AddInt8("bgRed", c.red);
1116 		archive->AddInt8("bgGreen", c.green);
1117 		archive->AddInt8("bgBlue", c.blue);
1118 	}
1119 
1120 	// store panel positions w/ node names & signatures
1121 	for(uint32 n = 0; n < CountItems(DiagramItem::M_BOX); ++n) {
1122 		MediaNodePanel* panel = dynamic_cast<MediaNodePanel*>(
1123 			ItemAt(n, DiagramItem::M_BOX));
1124 		if(!panel)
1125 			continue;
1126 
1127 		if(panel->ref->isInternal())
1128 			// skip internal nodes
1129 			continue;
1130 
1131 		BMessage m;
1132 		panel->exportState(&m);
1133 		archive->AddString("nodeName", panel->ref->name());
1134 		archive->AddInt32("nodeKind", panel->ref->kind());
1135 		archive->AddMessage("panel", &m);
1136 	}
1137 
1138 	// copy inactive node state info
1139 	for(int32 n = 0; n < m_inactiveNodeState.CountItems(); ++n) {
1140 		_inactive_node_state_entry* e = reinterpret_cast<_inactive_node_state_entry*>(
1141 			m_inactiveNodeState.ItemAt(n));
1142 
1143 		archive->AddString("nodeName", e->name.String());
1144 		archive->AddInt32("nodeKind", e->kind);
1145 		archive->AddMessage("panel", &e->state);
1146 	}
1147 
1148 	return B_OK;
1149 }
1150 
1151 // [e.moon 8dec99] subset support
1152 
1153 status_t MediaRoutingView::importStateFor(
1154 	const NodeSetIOContext*		context,
1155 	const BMessage*						archive) {
1156 
1157 	status_t err;
1158 
1159 	for(int32 archiveIndex = 0;; ++archiveIndex) {
1160 
1161 		// fetch archived key & panel data
1162 		const char* key;
1163 		err = archive->FindString("nodeKey", archiveIndex, &key);
1164 		if(err < B_OK)
1165 			break;
1166 
1167 		BMessage m;
1168 		err = archive->FindMessage("panel", archiveIndex, &m);
1169 		if(err < B_OK) {
1170 			PRINT((
1171 				"!!! MediaRoutingView::importStateFor(): missing panel %"
1172 					B_PRId32 "\n", archiveIndex));
1173 			continue;
1174 		}
1175 
1176 		// find corresponding node
1177 		media_node_id id;
1178 		err = context->getNodeFor(key, &id);
1179 		if(err < B_OK) {
1180 			PRINT((
1181 				"!!! MediaRoutingView::importStateFor(): missing node '%s'\n",
1182 				key));
1183 			continue;
1184 		}
1185 
1186 		// look for panel, create it if necessary
1187 		MediaNodePanel* panel;
1188 		err = _findPanelFor(id,	&panel);
1189 		if(err < B_OK) {
1190 			// create it
1191 			err = _addPanelFor(
1192 				id,
1193 				BPoint(5.0, 5.0));
1194 			if(err < B_OK) {
1195 				PRINT((
1196 					"!!! MediaRoutingView::importStateFor(): _addPanelFor():\n"
1197 					"    %s\n", strerror(err)));
1198 				continue;
1199 			}
1200 
1201 			err = _findPanelFor(id,	&panel);
1202 			if(err < B_OK) {
1203 				PRINT((
1204 					"!!! MediaRoutingView::importStateFor(): _findPanelFor():\n"
1205 					"    %s\n", strerror(err)));
1206 				continue;
1207 			}
1208 		}
1209 
1210 		// pass state data along
1211 		panel->importState(&m);
1212 
1213 		// select the panel
1214 		SelectItem(panel, false);
1215 	}
1216 
1217 	return B_OK;
1218 }
1219 
1220 status_t MediaRoutingView::exportStateFor(
1221 	const NodeSetIOContext*		context,
1222 	BMessage*									archive) const {
1223 
1224 	status_t err;
1225 
1226 	for(uint32 n = 0; n < context->countNodes(); ++n) {
1227 		MediaNodePanel* panel;
1228 		err = _findPanelFor(
1229 			context->nodeAt(n),
1230 			&panel);
1231 		if(err < B_OK) {
1232 			PRINT((
1233 				"!!! MediaRoutingView::exportStateFor():\n"
1234 				"    no panel for node %" B_PRId32 "\n",
1235 				context->nodeAt(n)));
1236 			return B_BAD_VALUE;
1237 		}
1238 
1239 		const char* key = context->keyAt(n);
1240 
1241 		archive->AddString("nodeKey", key);
1242 		BMessage m;
1243 		panel->exportState(&m);
1244 		archive->AddMessage("panel", &m);
1245 	}
1246 
1247 	return B_OK;
1248 }
1249 
1250 // -------------------------------------------------------- //
1251 // *** children management
1252 // -------------------------------------------------------- //
1253 
1254 status_t MediaRoutingView::_addPanelFor(
1255 	media_node_id id,
1256 	BPoint atPoint)
1257 {
1258 	D_METHOD(("MediaRoutingView::_addPanelFor()\n"));
1259 
1260 	manager->lock();
1261 	NodeRef *ref;
1262 	status_t error = manager->getNodeRef(id, &ref);
1263 	manager->unlock();
1264 	if (!error)
1265 	{
1266 		add_observer(this, ref);
1267 		MediaNodePanel *panel = 0;
1268 		if (id == m_lastDroppedNode) // this was instantiated thru drag & drop
1269 		{
1270 			AddItem(panel = new MediaNodePanel(m_lastDropPoint, ref));
1271 			SelectItem(panel, true);
1272 			m_lastDroppedNode = 0;
1273 		}
1274 		else // this was an externally created node, must find a nice position first
1275 		{
1276 			panel = new MediaNodePanel(BPoint(0.0, 0.0), ref);
1277 			AddItem(panel);
1278 			BMessage state;
1279 			if(_fetchInactiveNodeState(panel, &state) == B_OK)
1280 				panel->importState(&state);
1281 			else {
1282 				BPoint p = findFreePositionFor(panel);
1283 				panel->moveTo(p);
1284 			}
1285 			Invalidate(panel->Frame());
1286 		}
1287 	}
1288 	updateDataRect();
1289 	return error;
1290 }
1291 
1292 status_t MediaRoutingView::_findPanelFor(
1293 	media_node_id id,
1294 	MediaNodePanel **outPanel) const
1295 {
1296 	D_METHOD(("MediaRoutingView::_findPanelFor()\n"));
1297 
1298 	for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
1299 	{
1300 		MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(ItemAt(i, DiagramItem::M_BOX));
1301 		if (panel)
1302 		{
1303 			if (panel->ref->id() == id)
1304 			{
1305 				*outPanel = panel;
1306 				return B_OK;
1307 			}
1308 		}
1309 	}
1310 	return B_ERROR;
1311 }
1312 
1313 status_t MediaRoutingView::_removePanelFor(
1314 	media_node_id id)
1315 {
1316 	D_METHOD(("MediaRoutingView::_removePanelFor()\n"));
1317 
1318 	MediaNodePanel *panel;
1319 	if (_findPanelFor(id, &panel) == B_OK)
1320 	{
1321 		if (RemoveItem(panel))
1322 		{
1323 			remove_observer(this, panel->ref);
1324 			Invalidate(panel->Frame());
1325 			delete panel;
1326 			return B_OK;
1327 		}
1328 	}
1329 	return B_ERROR;
1330 }
1331 
1332 status_t MediaRoutingView::_addWireFor(
1333 	Connection& connection)
1334 {
1335 	D_METHOD(("MediaRoutingView::_addWireFor()\n"));
1336 
1337 	MediaNodePanel *source, *destination;
1338 	if ((_findPanelFor(connection.sourceNode(), &source) == B_OK)
1339 	 && (_findPanelFor(connection.destinationNode(), &destination) == B_OK))
1340 	{
1341 		status_t error;
1342 
1343 		media_output output;
1344 		error = connection.getOutput(&output);
1345 		if (error)
1346 		{
1347 			return error;
1348 		}
1349 		MediaJack *outputJack = new MediaJack(output);
1350 		source->AddItem(outputJack);
1351 
1352 		media_input input;
1353 		error = connection.getInput(&input);
1354 		if (error)
1355 		{
1356 			return error;
1357 		}
1358 		MediaJack *inputJack =  new MediaJack(input);
1359 		destination->AddItem(inputJack);
1360 
1361 		MediaWire *wire = new MediaWire(connection, outputJack, inputJack);
1362 		AddItem(wire);
1363 		source->updateIOJacks();
1364 		source->arrangeIOJacks();
1365 		destination->updateIOJacks();
1366 		destination->arrangeIOJacks();
1367 		updateDataRect();
1368 
1369 		// [e.moon 21nov99] group creation/merging now performed by
1370 		// RouteAppNodeManager
1371 
1372 		Invalidate(source->Frame());
1373 		Invalidate(destination->Frame());
1374 		Invalidate(wire->Frame());
1375 		return B_OK;
1376 	}
1377 	else
1378 	{
1379 		return B_ERROR;
1380 	}
1381 }
1382 
1383 status_t MediaRoutingView::_findWireFor(
1384 	uint32 connectionID,
1385 	MediaWire **outWire) const
1386 {
1387 	D_METHOD(("MediaRoutingView::_findWireFor()\n"));
1388 
1389 	for (uint32 i = 0; i < CountItems(DiagramItem::M_WIRE); i++)
1390 	{
1391 		MediaWire *wire = dynamic_cast<MediaWire *>(ItemAt(i, DiagramItem::M_WIRE));
1392 		if (wire && wire->connection.id() == connectionID)
1393 		{
1394 			*outWire = wire;
1395 			return B_OK;
1396 		}
1397 	}
1398 	return B_ERROR;
1399 }
1400 
1401 status_t MediaRoutingView::_removeWireFor(
1402 	uint32 connectionID)
1403 {
1404 	D_METHOD(("MediaRoutingView::_removeWireFor()\n"));
1405 
1406 	MediaWire *wire;
1407 	if (_findWireFor(connectionID, &wire) == B_OK)
1408 	{
1409 		MediaNodePanel *source, *destination;
1410 		_findPanelFor(wire->connection.sourceNode(), &source);
1411 		_findPanelFor(wire->connection.destinationNode(), &destination);
1412 		RemoveItem(wire);
1413 		Invalidate(wire->Frame());
1414 		delete wire;
1415 		if (source)
1416 		{
1417 			source->updateIOJacks();
1418 			source->arrangeIOJacks();
1419 			Invalidate(source->Frame());
1420 		}
1421 		if (destination)
1422 		{
1423 			destination->updateIOJacks();
1424 			destination->arrangeIOJacks();
1425 			Invalidate(destination->Frame());
1426 		}
1427 
1428 		// [e.moon 21nov99] group split/remove now performed by
1429 		// RouteAppNodeManager
1430 
1431 		updateDataRect();
1432 		return B_OK;
1433 	}
1434 	return B_ERROR;
1435 }
1436 
1437 // -------------------------------------------------------- //
1438 // *** internal methods
1439 // -------------------------------------------------------- //
1440 
1441 void MediaRoutingView::_addShortcuts()
1442 {
1443 	Window()->AddShortcut('A', B_COMMAND_KEY,
1444 						  new BMessage(M_SELECT_ALL), this);
1445 	Window()->AddShortcut('K', B_COMMAND_KEY,
1446 						  new BMessage(M_CLEANUP_REQUESTED), this);
1447 	Window()->AddShortcut('T', B_COMMAND_KEY,
1448 						  new BMessage(M_DELETE_SELECTION), this);
1449 	Window()->AddShortcut('P', B_COMMAND_KEY,
1450 						  new BMessage(M_NODE_TWEAK_PARAMETERS), this);
1451 	Window()->AddShortcut('P', B_COMMAND_KEY | B_SHIFT_KEY,
1452 						  new BMessage(M_NODE_START_CONTROL_PANEL), this);
1453 	Window()->AddShortcut('I', B_COMMAND_KEY,
1454 						  new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED), this);
1455 }
1456 
1457 void MediaRoutingView::_initLayout()
1458 {
1459 	D_METHOD(("MediaRoutingView::_initLayout()\n"));
1460 
1461 	BString measure;
1462 	measure << " " << B_TRANSLATE("Be Audio Mixer") << " ";
1463 
1464 	switch (m_layout)
1465 	{
1466 		case M_ICON_VIEW:
1467 		{
1468 			// Adjust the jack width for displaying the abbreviated
1469 			// input/output name
1470 			BFont font(be_plain_font);
1471 			font.SetSize(font.Size() - 2.0);
1472 			for (int i = 0; i < MediaJack::M_MAX_ABBR_LENGTH; i++)
1473 			{
1474 				MediaJack::M_DEFAULT_WIDTH += font.StringWidth("M");
1475 			}
1476 			MediaJack::M_DEFAULT_WIDTH += 2.0; // add some padding
1477 
1478 			// Adjust the default size for MediaNodePanels to fit the
1479 			// size of be_plain_font
1480 			float labelWidth, bodyWidth;
1481 			float labelHeight, bodyHeight;
1482 			font_height fh;
1483 			be_plain_font->GetHeight(&fh);
1484 			labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
1485 						 + be_plain_font->StringWidth(measure.String());
1486 			bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_LARGE_ICON
1487 						+ 2 * MediaJack::M_DEFAULT_WIDTH;
1488 			labelHeight = 2 * MediaNodePanel::M_LABEL_V_MARGIN
1489 						  + fh.ascent + fh.descent + fh.leading + 1.0;
1490 			bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_LARGE_ICON;
1491 			MediaNodePanel::M_DEFAULT_WIDTH = labelWidth > bodyWidth ? labelWidth : bodyWidth;
1492 			MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight + bodyHeight;
1493 			Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);
1494 
1495 			// Adjust the cleanup settings
1496 			M_CLEANUP_H_GAP += MediaNodePanel::M_DEFAULT_WIDTH;
1497 			break;
1498 		}
1499 		case M_MINI_ICON_VIEW:
1500 		{
1501 			// Adjust the default size for MediaNodePanels to fit the
1502 			// size of be_plain_font
1503 			float labelWidth, bodyWidth;
1504 			float labelHeight, bodyHeight;
1505 			font_height fh;
1506 			be_plain_font->GetHeight(&fh);
1507 			labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
1508 						 + be_plain_font->StringWidth(measure.String());
1509 			bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_MINI_ICON;
1510 			labelHeight = 3 * MediaNodePanel::M_LABEL_V_MARGIN
1511 						  + fh.ascent + fh.descent + fh.leading
1512 						  + 2 * MediaJack::M_DEFAULT_HEIGHT;
1513 			bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_MINI_ICON;
1514 			MediaNodePanel::M_DEFAULT_WIDTH = labelWidth + bodyWidth;
1515 			MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight > bodyHeight ? labelHeight : bodyHeight;
1516 			Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);
1517 
1518 			// Adjust the cleanup settings
1519 			M_CLEANUP_V_GAP += MediaNodePanel::M_DEFAULT_HEIGHT;
1520 			break;
1521 		}
1522 	}
1523 }
1524 
1525 void MediaRoutingView::_initContent()
1526 {
1527 	D_METHOD(("MediaRoutingView::_initContent()\n"));
1528 
1529 	Autolock lock(manager);
1530 
1531 	void *cookie = 0;
1532 	NodeRef *ref;
1533 	while (manager->getNextRef(&ref, &cookie) == B_OK)
1534 	{
1535 		// add self as observer
1536 		add_observer(this, ref);
1537 		// create & place node view (+++++ defer until observer status confirmed!)
1538 		_addPanelFor(ref->id(), BPoint(M_CLEANUP_H_MARGIN, M_CLEANUP_V_MARGIN));
1539 	}
1540 	cookie = 0;
1541 	Connection connection;
1542 	while (manager->getNextConnection(&connection, &cookie) == B_OK)
1543 	{
1544 		_addWireFor(connection);
1545 	}
1546 
1547 	// create default groups
1548 	NodeGroup* group;
1549 	NodeRef* videoIn = manager->videoInputNode();
1550 	if (videoIn)
1551 	{
1552 		group = manager->createGroup(B_TRANSLATE("Video input"));
1553 		group->setRunMode(BMediaNode::B_RECORDING);
1554 		group->addNode(videoIn);
1555 	}
1556 	NodeRef* audioIn = manager->audioInputNode();
1557 	if (audioIn)
1558 	{
1559 		group = manager->createGroup(B_TRANSLATE("Audio input"));
1560 		group->setRunMode(BMediaNode::B_RECORDING);
1561 		group->addNode(audioIn);
1562 	}
1563 	NodeRef* videoOut = manager->videoOutputNode();
1564 	if (videoOut)
1565 	{
1566 		group = manager->createGroup(B_TRANSLATE("Video output"));
1567 		group->addNode(videoOut);
1568 	}
1569 }
1570 
1571 void MediaRoutingView::_changeCyclingForSelection(
1572 	bool cycle)
1573 {
1574 	D_METHOD(("MediaRoutingView::_changeCyclingForSelection()\n"));
1575 
1576 	if (SelectedType() == DiagramItem::M_BOX)
1577 	{
1578 		manager->lock();
1579 		for (uint32 i = 0; i < CountSelectedItems(); i++)
1580 		{
1581 			MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
1582 			if (panel && (panel->ref->isCycling() != cycle))
1583 			{
1584 				panel->ref->setCycling(cycle);
1585 			}
1586 		}
1587 		manager->unlock();
1588 	}
1589 }
1590 
1591 void MediaRoutingView::_changeRunModeForSelection(
1592 	uint32 mode)
1593 {
1594 	D_METHOD(("MediaRoutingView::_changeRunModeForSelection()\n"));
1595 
1596 	if (SelectedType() == DiagramItem::M_BOX)
1597 	{
1598 		manager->lock();
1599 		for (uint32 i = 0; i < CountSelectedItems(); i++)
1600 		{
1601 			MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
1602 			if (panel && (panel->ref->runMode() != mode))
1603 			{
1604 				panel->ref->setRunMode(mode);
1605 			}
1606 		}
1607 		manager->unlock();
1608 	}
1609 }
1610 
1611 void MediaRoutingView::_openInfoWindowsForSelection() {
1612 	D_METHOD(("MediaRoutingView::_openInfoWindowsForSelection()\n"));
1613 
1614 	InfoWindowManager *manager = InfoWindowManager::Instance();
1615 	if (!manager) {
1616 		return;
1617 	}
1618 
1619 	if (SelectedType() == DiagramItem::M_BOX) {
1620 		for (uint32 i = 0; i < CountSelectedItems(); i++) {
1621 			MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
1622 			if (panel && manager->Lock()) {
1623 				manager->openWindowFor(panel->ref);
1624 				manager->Unlock();
1625 			}
1626 		}
1627 	}
1628 	else if (SelectedType() == DiagramItem::M_WIRE) {
1629 		for (uint32 i = 0; i < CountSelectedItems(); i++) {
1630 			MediaWire *wire = dynamic_cast<MediaWire *>(SelectedItemAt(i));
1631 			if (wire && manager->Lock()) {
1632 				manager->openWindowFor(wire->connection);
1633 				manager->Unlock();
1634 			}
1635 		}
1636 	}
1637 }
1638 
1639 void MediaRoutingView::_openParameterWindowsForSelection() {
1640 	D_METHOD(("MediaRoutingView::_openParameterWindowsForSelection()\n"));
1641 
1642 	if (SelectedType() != DiagramItem::M_BOX) {
1643 		// can only open parameter window for nodes
1644 		return;
1645 	}
1646 
1647 	for (uint32 i = 0; i < CountSelectedItems(); i++) {
1648 		MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
1649 		if (panel && (panel->ref->kind() & B_CONTROLLABLE)) {
1650 			ParameterWindowManager *paramMgr= ParameterWindowManager::Instance();
1651 			if (paramMgr && paramMgr->Lock()) {
1652 				paramMgr->openWindowFor(panel->ref);
1653 				paramMgr->Unlock();
1654 			}
1655 		}
1656 	}
1657 }
1658 
1659 void MediaRoutingView::_startControlPanelsForSelection() {
1660 	D_METHOD(("MediaRoutingView::_startControlPanelsForSelection()\n"));
1661 
1662 	if (SelectedType() != DiagramItem::M_BOX) {
1663 		// can only start control panel for nodes
1664 		return;
1665 	}
1666 
1667 	for (uint32 i = 0; i < CountSelectedItems(); i++) {
1668 		MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
1669 		if (panel && (panel->ref->kind() & B_CONTROLLABLE)) {
1670 			ParameterWindowManager *paramMgr= ParameterWindowManager::Instance();
1671 			if (paramMgr && paramMgr->Lock()) {
1672 				paramMgr->startControlPanelFor(panel->ref);
1673 				paramMgr->Unlock();
1674 			}
1675 		}
1676 	}
1677 }
1678 
1679 void MediaRoutingView::_deleteSelection()
1680 {
1681 	D_METHOD(("MediaRoutingView::_deleteSelection()\n"));
1682 	if (SelectedType() == DiagramItem::M_BOX)
1683 	{
1684 		for (uint32 i = 0; i < CountSelectedItems(); i++)
1685 		{
1686 			MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
1687 			if (panel && panel->ref->isInternal())
1688 			{
1689 				status_t error = panel->ref->releaseNode();
1690 				if (error)
1691 				{
1692 					BString s = B_TRANSLATE("Could not release '%refname%'");
1693 					s.ReplaceFirst("%rename%", panel->ref->name());
1694 					showErrorMessage(s, error);
1695 				}
1696 			}
1697 		}
1698 	}
1699 	else if (SelectedType() == DiagramItem::M_WIRE)
1700 	{
1701 		for (uint32 i = 0; i < CountSelectedItems(); i++)
1702 		{
1703 			MediaWire *wire = dynamic_cast<MediaWire *>(SelectedItemAt(i));
1704 			if (wire && !(wire->connection.flags() & Connection::LOCKED))
1705 			{
1706 				status_t error = manager->disconnect(wire->connection);
1707 				if (error)
1708 				{
1709 					showErrorMessage(
1710 						B_TRANSLATE("Could not disconnect"), error);
1711 				}
1712 			}
1713 		}
1714 	}
1715 	// make sure none of the deleted items is still displaying its mouse cursor !
1716 	be_app->SetCursor(B_HAND_CURSOR);
1717 }
1718 
1719 void MediaRoutingView::_checkDroppedFile(
1720 	entry_ref *ref,
1721 	BPoint dropPoint)
1722 {
1723 	D_METHOD(("MediaRoutingView::_checkDroppedFile()\n"));
1724 
1725 	// [cell 26apr00] traverse links
1726 	BEntry entry(ref, true);
1727 	entry.GetRef(ref);
1728 
1729 	BNode node(ref);
1730 	if (node.InitCheck() == B_OK)
1731 	{
1732 		BNodeInfo nodeInfo(&node);
1733 		if (nodeInfo.InitCheck() == B_OK)
1734 		{
1735 			char mimeString[B_MIME_TYPE_LENGTH];
1736 			if (nodeInfo.GetType(mimeString) == B_OK)
1737 			{
1738 				BMimeType mimeType(mimeString);
1739 				BMimeType superType;
1740 
1741 				// [e.moon 22dec99] handle dropped node-set files
1742 				if(mimeType == RouteApp::s_nodeSetType) {
1743 					BMessage m(B_REFS_RECEIVED);
1744 					m.AddRef("refs", ref);
1745 					be_app_messenger.SendMessage(&m);
1746 				}
1747 				else if (mimeType.GetSupertype(&superType) == B_OK)
1748 				{
1749 					if (superType == "image")
1750 					{
1751 						_changeBackground(ref);
1752 					}
1753 					else if ((superType == "audio") || (superType == "video"))
1754 					{
1755 						NodeRef* droppedNode;
1756 						status_t error;
1757 						error = manager->instantiate(*ref, B_BUFFER_PRODUCER, &droppedNode);
1758 						if (!error)
1759 						{
1760 							media_output encVideoOutput;
1761 							if (droppedNode->findFreeOutput(&encVideoOutput, B_MEDIA_ENCODED_VIDEO) == B_OK)
1762 							{
1763 								droppedNode->setFlags(droppedNode->flags() | NodeRef::NO_POSITION_REPORTING);
1764 							}
1765 							m_lastDroppedNode = droppedNode->id();
1766 							m_lastDropPoint = Align(dropPoint);
1767 						}
1768 						else
1769 						{
1770 							char fileName[B_FILE_NAME_LENGTH];
1771 							BEntry entry(ref);
1772 							entry.GetName(fileName);
1773 							BString s = B_TRANSLATE(
1774 								"Could not load '%filename%'");
1775 							s.ReplaceFirst("%filename%", fileName);
1776 							showErrorMessage(s, error);
1777 						}
1778 					}
1779 				}
1780 			}
1781 		}
1782 	}
1783 }
1784 
1785 void MediaRoutingView::_changeBackground(
1786 	entry_ref *ref)
1787 {
1788 	D_METHOD(("MediaRoutingView::_changeBackground()\n"));
1789 
1790 	status_t error;
1791 	BBitmap *background = 0;
1792 	BFile file(ref, B_READ_ONLY);
1793 	error = file.InitCheck();
1794 	if (!error)
1795 	{
1796 		BTranslatorRoster *roster = BTranslatorRoster::Default();
1797 		BBitmapStream stream;
1798 		error = roster->Translate(&file, NULL, NULL, &stream, B_TRANSLATOR_BITMAP);
1799 		if (!error)
1800 		{
1801 			stream.DetachBitmap(&background);
1802 			setBackgroundBitmap(background);
1803 			Invalidate();
1804 
1805 			// [e.moon 1dec99] persistence, yay
1806 			m_backgroundBitmapEntry.SetTo(ref);
1807 		}
1808 	}
1809 	delete background;
1810 }
1811 
1812 void MediaRoutingView::_changeBackground(
1813 	rgb_color color)
1814 {
1815 	D_METHOD(("MediaRoutingView::_changeBackground()\n"));
1816 	setBackgroundColor(color);
1817 	Invalidate();
1818 
1819 	// [e.moon 1dec99] persistence, yay
1820 	m_backgroundBitmapEntry.Unset();
1821 }
1822 
1823 void
1824 MediaRoutingView::_adjustScrollBars()
1825 {
1826 	D_METHOD(("MediaRoutingView::_adjustScrollBars()\n"));
1827 
1828 	BScrollBar *scrollBar;
1829 
1830 	// adjust horizontal scroll bar
1831 	scrollBar = ScrollBar(B_HORIZONTAL);
1832 	if (scrollBar) {
1833 		float bigStep = floor(MediaNodePanel::M_DEFAULT_WIDTH + M_CLEANUP_H_GAP);
1834 		scrollBar->SetSteps(floor(bigStep / 10.0), bigStep);
1835 	}
1836 
1837 	// adjust vertical scroll bar
1838 	scrollBar = ScrollBar(B_VERTICAL);
1839 	if (scrollBar) {
1840 		float bigStep = floor(MediaNodePanel::M_DEFAULT_HEIGHT + M_CLEANUP_V_GAP);
1841 		scrollBar->SetSteps(floor(bigStep / 10.0), bigStep);
1842 	}
1843 }
1844 
1845 void
1846 MediaRoutingView::_broadcastSelection() const
1847 {
1848 	int32 selectedGroup = 0;
1849 
1850 	if (SelectedType() == DiagramItem::M_BOX) {
1851 		// iterate thru the list of selected node panels and make the
1852 		// first group we find the selected group
1853 		for (uint32 i = 0; i < CountSelectedItems(); i++) {
1854 			MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
1855 			if (panel && panel->ref->group()) {
1856 				selectedGroup = panel->ref->group()->id();
1857 				BMessenger messenger(Window());
1858 				BMessage groupMsg(M_GROUP_SELECTED);
1859 				groupMsg.AddInt32("groupID", selectedGroup);
1860 				messenger.SendMessage(&groupMsg);
1861 				return;
1862 			}
1863 		}
1864 	}
1865 
1866 	// currently no group is selected
1867 	BMessenger messenger(Window());
1868 	BMessage groupMsg(M_GROUP_SELECTED);
1869 	groupMsg.AddInt32("groupID", selectedGroup);
1870 	messenger.SendMessage(&groupMsg);
1871 }
1872 
1873 status_t
1874 MediaRoutingView::_fetchInactiveNodeState(MediaNodePanel *forPanel, BMessage *outMessage)
1875 {
1876 	// copy inactive node state info
1877 	int32 c = m_inactiveNodeState.CountItems();
1878 	for(int32 n = 0; n < c; n++) {
1879 		_inactive_node_state_entry* e = reinterpret_cast<_inactive_node_state_entry*>(
1880 			m_inactiveNodeState.ItemAt(n));
1881 		ASSERT(e);
1882 		if(e->name != forPanel->ref->name())
1883 			continue;
1884 
1885 		if(e->kind != forPanel->ref->kind())
1886 			continue;
1887 
1888 		// found match; extract message & remove entry
1889 		*outMessage = e->state;
1890 		m_inactiveNodeState.RemoveItem(n);
1891 		return B_OK;
1892 	}
1893 
1894 	return B_BAD_VALUE;
1895 }
1896 
1897 void
1898 MediaRoutingView::_emptyInactiveNodeState()
1899 {
1900 	int32 c = m_inactiveNodeState.CountItems();
1901 	for(int32 n = 0; n < c; n++) {
1902 		_inactive_node_state_entry* e = reinterpret_cast<_inactive_node_state_entry*>(
1903 			m_inactiveNodeState.ItemAt(n));
1904 		ASSERT(e);
1905 		delete e;
1906 	}
1907 	m_inactiveNodeState.MakeEmpty();
1908 }
1909 
1910 
1911 // END -- MediaRoutingView.cpp --
1912