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
87float	MediaRoutingView::M_CLEANUP_H_GAP			= 25.0;
88float	MediaRoutingView::M_CLEANUP_V_GAP			= 10.0;
89float	MediaRoutingView::M_CLEANUP_H_MARGIN 		= 15.0;
90float	MediaRoutingView::M_CLEANUP_V_MARGIN 		= 10.0;
91
92// ---------------------------------------------------------------- //
93// m_inactiveNodeState content
94// ---------------------------------------------------------------- //
95
96struct _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
110MediaRoutingView::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
130MediaRoutingView::~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
146void MediaRoutingView::connectionAborted(
147	DiagramEndPoint *fromWhich)
148{
149	D_METHOD(("MediaRoutingView::connectionAborted()\n"));
150
151	be_app->SetCursor(B_HAND_CURSOR);
152}
153
154void 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
163DiagramWire *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
212DiagramWire *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
233void
234MediaRoutingView::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
247void
248MediaRoutingView::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
310void
311MediaRoutingView::SelectionChanged()
312{
313	D_METHOD(("MediaRoutingView::selectionChanged()\n"));
314	_broadcastSelection();
315}
316
317// ---------------------------------------------------------------- //
318// *** BView impl.
319// ---------------------------------------------------------------- //
320
321void 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
340void MediaRoutingView::AllAttached()
341{
342	D_METHOD(("MediaRoutingView::AllAttached()\n"));
343	_inherited::AllAttached();
344
345	_adjustScrollBars();
346
347	// grab keyboard events
348	MakeFocus();
349}
350
351void 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
371void 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
407void MediaRoutingView::Pulse()
408{
409	// do the animation
410}
411
412// ---------------------------------------------------------------- //
413// BHandler impl
414// ---------------------------------------------------------------- //
415
416void 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
725BPoint 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
802void 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
909void 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
935void 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
973void 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
1002status_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 +++++
1090status_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
1141status_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
1208status_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
1242status_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
1280status_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
1301status_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
1320status_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
1371status_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
1389status_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
1429void 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
1445void 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
1510void 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
1556void 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
1576void 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
1596void 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
1624void 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
1644void 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
1664void 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
1703void 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
1768void 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
1795void 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
1806void
1807MediaRoutingView::_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
1828void
1829MediaRoutingView::_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
1856status_t
1857MediaRoutingView::_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
1880void
1881MediaRoutingView::_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