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