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