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