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