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