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