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