xref: /haiku/src/apps/cortex/MediaRoutingView/MediaNodePanel.cpp (revision a127b88ecbfab58f64944c98aa47722a18e363b2)
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 // MediaNodePanel.cpp
33 // c.lenz 10oct99
34 
35 #include "MediaNodePanel.h"
36 // InfoWindow
37 #include "InfoWindowManager.h"
38 // MediaRoutingView
39 #include "MediaRoutingView.h"
40 #include "MediaWire.h"
41 #include "RouteAppNodeManager.h"
42 // NodeManager
43 #include "NodeRef.h"
44 #include "NodeGroup.h"
45 // ParameterWindow
46 #include "ParameterWindow.h"
47 // Support
48 #include "cortex_ui.h"
49 #include "MediaIcon.h"
50 #include "MediaString.h"
51 // RouteApp
52 #include "RouteWindow.h"
53 // TipManager
54 #include "TipManager.h"
55 
56 // App Kit
57 #include <Application.h>
58 #include <Roster.h>
59 // Interface Kit
60 #include <MenuItem.h>
61 #include <PopUpMenu.h>
62 // Media Kit
63 #include <MediaDefs.h>
64 #include <MediaRoster.h>
65 // Locale Kit
66 #undef B_CATALOG
67 #define B_CATALOG (&sCatalog)
68 #include <Catalog.h>
69 
70 #undef B_TRANSLATION_CONTEXT
71 #define B_TRANSLATION_CONTEXT "MediaNodePanel"
72 
73 using namespace std;
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_DRAW(x) //PRINT (x)
81 
82 static BCatalog sCatalog("x-vnd.Cortex.MediaRoutingView");
83 
84 // -------------------------------------------------------- //
85 // constants
86 // -------------------------------------------------------- //
87 
88 float	MediaNodePanel::M_DEFAULT_WIDTH		= 90.0;
89 float	MediaNodePanel::M_DEFAULT_HEIGHT	= 60.0;
90 float	MediaNodePanel::M_LABEL_H_MARGIN	= 3.0;
91 float	MediaNodePanel::M_LABEL_V_MARGIN	= 3.0;
92 float	MediaNodePanel::M_BODY_H_MARGIN		= 5.0;
93 float	MediaNodePanel::M_BODY_V_MARGIN		= 5.0;
94 
95 // [e.moon 7dec99]
96 const BPoint MediaNodePanel::s_invalidPosition(-200.0, -200.0);
97 
98 // -------------------------------------------------------- //
99 // *** ctor/dtor
100 // -------------------------------------------------------- //
101 
102 MediaNodePanel::MediaNodePanel(
103 	BPoint position,
104 	NodeRef *nodeRef)
105 	: DiagramBox(BRect(position, position + BPoint(M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT))),
106 	  BHandler(nodeRef->name()),
107 	  ref(nodeRef),
108 	  m_bitmap(0),
109 	  m_icon(0),
110 	  m_alternatePosition(s_invalidPosition)
111 {
112 	D_METHOD(("MediaNodePanel::MediaNodePanel()\n"));
113 	ASSERT(ref);
114 }
115 
116 MediaNodePanel::~MediaNodePanel()
117 {
118 	D_METHOD(("MediaNodePanel::~MediaNodePanel()\n"));
119 	if (m_icon)
120 	{
121 		delete m_icon;
122 	}
123 	if (m_bitmap)
124 	{
125 		delete m_bitmap;
126 	}
127 }
128 
129 // -------------------------------------------------------- //
130 // *** derived from DiagramBox
131 // -------------------------------------------------------- //
132 
133 void MediaNodePanel::attachedToDiagram()
134 {
135 	D_METHOD(("MediaNodePanel::attachedToDiagram()\n"));
136 
137 	resizeTo(M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT);
138 	_updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout());
139 	_prepareLabel();
140 	populateInit();
141 	arrangeIOJacks();
142 
143 	view()->Looper()->AddHandler(this);
144 }
145 
146 void MediaNodePanel::detachedFromDiagram()
147 {
148 	D_METHOD(("MediaNodePanel::detachedFromDiagram()\n"));
149 
150 	BRect labelRect = m_labelRect.OffsetByCopy(Frame().LeftTop());
151 	if (m_mouseOverLabel && m_labelTruncated)
152 	{
153 		TipManager *tips = TipManager::Instance();
154 		tips->hideTip(view()->ConvertToScreen(labelRect));
155 	}
156 
157 	view()->Looper()->RemoveHandler(this);
158 }
159 
160 void MediaNodePanel::DrawBox()
161 {
162 	D_DRAW(("MediaNodePanel::DrawBox()\n"));
163 	if (m_bitmap)
164 	{
165 		view()->DrawBitmap(m_bitmap, Frame().LeftTop());
166 	}
167 }
168 
169 void MediaNodePanel::MouseDown(
170 	BPoint point,
171 	uint32 buttons,
172 	uint32 clicks)
173 {
174 	D_METHOD(("MediaNodePanel::MouseDown()\n"));
175 
176 	_inherited::MouseDown(point, buttons, clicks);
177 
178 	// +++ REALLY BAD WORKAROUND
179 	MediaJack *jack = dynamic_cast<MediaJack *>(_LastItemUnder());
180 	if (jack && jack->Frame().Contains(point))
181 	{
182 		return;
183 	}
184 
185 	switch (buttons) {
186 		case B_PRIMARY_MOUSE_BUTTON:
187 		{
188 			if (clicks == 2) {
189 				if (ref->kind() & B_CONTROLLABLE) {
190 					BMessage message(MediaRoutingView::M_NODE_TWEAK_PARAMETERS);
191 					DiagramView* v = view();
192 					BMessenger(v).SendMessage(&message);
193 				}
194 			}
195 			break;
196 		}
197 		case B_SECONDARY_MOUSE_BUTTON:
198 		{
199 			if (clicks == 1) {
200 				showContextMenu(point);
201 			}
202 			break;
203 		}
204 	}
205 }
206 
207 void MediaNodePanel::MouseOver(
208 	BPoint point,
209 	uint32 transit)
210 {
211 	D_METHOD(("MediaNodePanel::MouseOver()\n"));
212 	_inherited::MouseOver(point, transit);
213 
214 	switch (transit)
215 	{
216 		case B_ENTERED_VIEW:
217 		{
218 			break;
219 		}
220 		case B_INSIDE_VIEW:
221 		{
222 			BRect labelRect = m_labelRect.OffsetByCopy(Frame().LeftTop());
223 			if (labelRect.Contains(point))
224 			{
225 				if (!m_mouseOverLabel && m_labelTruncated)
226 				{
227 					TipManager *tips = TipManager::Instance();
228 					tips->showTip(m_fullLabel.String(), view()->ConvertToScreen(labelRect));
229 					m_mouseOverLabel = true;
230 				}
231 			}
232 			else
233 			{
234 				m_mouseOverLabel = false;
235 			}
236 			break;
237 		}
238 		case B_EXITED_VIEW:
239 		{
240 			m_mouseOverLabel = false;
241 			break;
242 		}
243 	}
244 }
245 
246 void MediaNodePanel::MessageDropped(
247 	BPoint point,
248 	BMessage *message)
249 {
250 	D_METHOD(("MediaNodePanel::MessageDropped()\n"));
251 
252 	// +++ REALLY BAD WORKAROUND
253 	MediaJack *jack = dynamic_cast<MediaJack *>(ItemUnder(point));
254 	if (jack)
255 	{
256 		jack->MessageDropped(point, message);
257 		return;
258 	}
259 	else
260 	{
261 		be_app->SetCursor(B_HAND_CURSOR);
262 	}
263 }
264 
265 void MediaNodePanel::selected()
266 {
267 	D_METHOD(("MediaNodePanel::selected()\n"));
268 	_updateBitmap();
269 }
270 
271 void MediaNodePanel::deselected()
272 {
273 	D_METHOD(("MediaNodePanel::deselected()\n"));
274 	_updateBitmap();
275 }
276 
277 // ---------------------------------------------------------------- //
278 // *** updating
279 // ---------------------------------------------------------------- //
280 
281 void MediaNodePanel::layoutChanged(
282 	int32 layout)
283 {
284 	D_METHOD(("MediaNodePanel::layoutChanged()\n"));
285 
286 	BPoint p = Frame().LeftTop();
287 	if (m_alternatePosition == s_invalidPosition)
288 	{
289 		m_alternatePosition = dynamic_cast<MediaRoutingView *>
290 							  (view())->findFreePositionFor(this);
291 	}
292 	moveTo(m_alternatePosition);
293 	m_alternatePosition = p;
294 
295 	resizeTo(M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT);
296 	for (uint32 i = 0; i < CountItems(); i++)
297 	{
298 		MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i));
299 		jack->layoutChanged(layout);
300 	}
301 	_updateIcon(layout);
302 	_prepareLabel();
303 	arrangeIOJacks();
304 	_updateBitmap();
305 }
306 
307 void MediaNodePanel::populateInit()
308 {
309 	D_METHOD(("MediaNodePanel::populateInit()\n"));
310 	if (ref->kind() & B_BUFFER_CONSUMER)
311 	{
312 		vector<media_input> freeInputs;
313 		ref->getFreeInputs(freeInputs);
314 		for (uint32 i = 0; i < freeInputs.size(); i++)
315 		{
316 			AddItem(new MediaJack(freeInputs[i]));
317 		}
318 	}
319 	if (ref->kind() & B_BUFFER_PRODUCER)
320 	{
321 		vector<media_output> freeOutputs;
322 		ref->getFreeOutputs(freeOutputs);
323 		for (uint32 i = 0; i < freeOutputs.size(); i++)
324 		{
325 			AddItem(new MediaJack(freeOutputs[i]));
326 		}
327 	}
328 }
329 
330 void MediaNodePanel::updateIOJacks()
331 {
332 	D_METHOD(("MediaNodePanel::updateIOJacks()\n"));
333 
334 	// remove all free inputs/outputs, they may be outdated
335 	for (uint32 i = 0; i < CountItems(); i++)
336 	{
337 		MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i));
338 		if (jack && !jack->isConnected())
339 		{
340 			RemoveItem(jack);
341 			delete jack;
342 			i--; // account for reindexing in the BList
343 		}
344 	}
345 
346 	// add free inputs
347 	if (ref->kind() & B_BUFFER_CONSUMER)
348 	{
349 		vector<media_input> freeInputs;
350 		ref->getFreeInputs(freeInputs);
351 		for (uint32 i = 0; i < freeInputs.size(); i++)
352 		{
353 			AddItem(new MediaJack(freeInputs[i]));
354 		}
355 	}
356 
357 	// add free outputs
358 	if (ref->kind() & B_BUFFER_PRODUCER)
359 	{
360 		vector<media_output> freeOutputs;
361 		ref->getFreeOutputs(freeOutputs);
362 		for (uint32 i = 0; i < freeOutputs.size(); i++)
363 		{
364 			AddItem(new MediaJack(freeOutputs[i]));
365 		}
366 	}
367 
368 	// the supported media types might have changed -> this could
369 	// require changing the icon
370 	_updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout());
371 }
372 
373 void MediaNodePanel::arrangeIOJacks()
374 {
375 	D_METHOD(("MediaNodePanel::arrangeIOJacks()\n"));
376 	SortItems(DiagramItem::M_ENDPOINT, &compareTypeAndID);
377 
378 	switch (dynamic_cast<MediaRoutingView *>(view())->getLayout())
379 	{
380 		case MediaRoutingView::M_ICON_VIEW:
381 		{
382 			BRegion updateRegion;
383 			float align = 1.0;
384 			view()->GetItemAlignment(0, &align);
385 
386 			// adjust this panel's size
387 			int32 numInputs = 0, numOutputs = 0;
388 			for (uint32 i = 0; i < CountItems(); i++)
389 			{
390 				MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i));
391 				if (jack)
392 				{
393 					if (jack->isInput())
394 					{
395 						numInputs++;
396 					}
397 					if (jack->isOutput())
398 					{
399 						numOutputs++;
400 					}
401 				}
402 			}
403 			float minHeight = MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP;
404 			minHeight *= numInputs > numOutputs ? numInputs : numOutputs;
405 			minHeight += m_labelRect.Height();
406 			minHeight += 2 * MediaJack::M_DEFAULT_GAP;
407 			minHeight = ((int)minHeight / (int)align) * align + align;
408 			if ((Frame().Height() < minHeight)
409 			 || ((Frame().Height() > minHeight)
410 			 && (minHeight >= MediaNodePanel::M_DEFAULT_HEIGHT)))
411 			{
412 				updateRegion.Include(Frame());
413 				resizeTo(Frame().Width(), minHeight);
414 				updateRegion.Include(Frame());
415 				_prepareLabel();
416 			}
417 
418 			// adjust the placement of the jacks
419 			BRect r = m_bodyRect;
420 			r.bottom -= M_BODY_V_MARGIN;
421 			float inputOffset = 0.0, outputOffset = 0.0;
422 			float center = Frame().top + r.top + (r.Height() / 2.0);
423 			center += MediaJack::M_DEFAULT_GAP - (MediaJack::M_DEFAULT_HEIGHT / 2.0);
424 			center = ((int)center / (int)align) * align;
425 			if (numInputs)
426 			{
427 				if (numInputs % 2) // odd number of inputs
428 				{
429 					inputOffset = center - (numInputs / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP);
430 				}
431 				else // even number of inputs
432 				{
433 					inputOffset = center - ((numInputs + 1) / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP);
434 				}
435 			}
436 			if (numOutputs)
437 			{
438 				if (numOutputs % 2) // odd number of outputs
439 				{
440 					outputOffset = center - (numOutputs / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP);
441 				}
442 				else // even number of outputs
443 				{
444 					outputOffset = center - ((numOutputs + 1) / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP);
445 				}
446 			}
447 			for (uint32 i = 0; i < CountItems(); i++)
448 			{
449 				MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i));
450 				if (jack)
451 				{
452 					if (jack->isInput())
453 					{
454 						jack->setPosition(inputOffset, Frame().left, Frame().right, &updateRegion);
455 						inputOffset += jack->Frame().Height() + MediaJack::M_DEFAULT_GAP;
456 					}
457 					if (jack->isOutput())
458 					{
459 						jack->setPosition(outputOffset, Frame().left, Frame().right, &updateRegion);
460 						outputOffset += jack->Frame().Height() + MediaJack::M_DEFAULT_GAP;
461 					}
462 				}
463 			}
464 			for (int32 i = 0; i < updateRegion.CountRects(); i++)
465 			{
466 				view()->Invalidate(updateRegion.RectAt(i));
467 			}
468 			break;
469 		}
470 		case MediaRoutingView::M_MINI_ICON_VIEW:
471 		{
472 			BRegion updateRegion;
473 			float align = 1.0;
474 			view()->GetItemAlignment(&align, 0);
475 
476 			// adjust this panel's size
477 			int32 numInputs = 0, numOutputs = 0;
478 			for (uint32 i = 0; i < CountItems(); i++)
479 			{
480 				MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i));
481 				if (jack)
482 				{
483 					if (jack->isInput())
484 					{
485 						numInputs++;
486 					}
487 					if (jack->isOutput())
488 					{
489 						numOutputs++;
490 					}
491 				}
492 			}
493 			float minWidth = MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP;
494 			minWidth *= numInputs > numOutputs ? numInputs : numOutputs;
495 			minWidth += m_bodyRect.Width();
496 			minWidth += 2 * MediaJack::M_DEFAULT_GAP;
497 			minWidth = ((int)minWidth / (int)align) * align + align;
498 			if ((Frame().Width() < minWidth)
499 			 || ((Frame().Width() > minWidth)
500 			 && (minWidth >= MediaNodePanel::M_DEFAULT_WIDTH)))
501 			{
502 				updateRegion.Include(Frame());
503 				resizeTo(minWidth, Frame().Height());
504 				updateRegion.Include(Frame());
505 				_prepareLabel();
506 			}
507 			// adjust the placement of the jacks
508 			float inputOffset = 0.0, outputOffset = 0.0;
509 			float center = Frame().left + m_labelRect.left + (m_labelRect.Width() / 2.0);
510 			center += MediaJack::M_DEFAULT_GAP - (MediaJack::M_DEFAULT_WIDTH / 2.0);
511 			center = ((int)center / (int)align) * align;
512 			if (numInputs)
513 			{
514 				if (numInputs % 2) // odd number of inputs
515 				{
516 					inputOffset = center - (numInputs / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP);
517 				}
518 				else // even number of inputs
519 				{
520 					inputOffset = center - ((numInputs + 1) / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP);
521 				}
522 			}
523 			if (numOutputs)
524 			{
525 				if (numOutputs % 2) // odd number of outputs
526 				{
527 					outputOffset = center - (numOutputs / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP);
528 				}
529 				else // even number of outputs
530 				{
531 					outputOffset = center - ((numOutputs + 1) / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP);
532 				}
533 			}
534 			for (uint32 i = 0; i < CountItems(); i++)
535 			{
536 				MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i));
537 				if (jack)
538 				{
539 					if (jack->isInput())
540 					{
541 						jack->setPosition(inputOffset, Frame().top, Frame().bottom, &updateRegion);
542 						inputOffset += jack->Frame().Width() + MediaJack::M_DEFAULT_GAP;
543 					}
544 					if (jack->isOutput())
545 					{
546 						jack->setPosition(outputOffset, Frame().top, Frame().bottom, &updateRegion);
547 						outputOffset += jack->Frame().Width() + MediaJack::M_DEFAULT_GAP;
548 					}
549 				}
550 			}
551 			for (int32 i = 0; i < updateRegion.CountRects(); i++)
552 			{
553 				view()->Invalidate(updateRegion.RectAt(i));
554 			}
555 			break;
556 		}
557 	}
558 	_updateBitmap();
559 }
560 
561 void MediaNodePanel::showContextMenu(
562 	BPoint point)
563 {
564 	D_METHOD(("MediaNodePanel::showContextMenu()\n"));
565 
566 	BPopUpMenu *menu = new BPopUpMenu("MediaNodePanel PopUp", false, false, B_ITEMS_IN_COLUMN);
567 	menu->SetFont(be_plain_font);
568 
569 	BMenuItem *item;
570 	BMessage *message;
571 
572 	// add the "Tweak Parameters" item
573 	message = new BMessage(MediaRoutingView::M_NODE_TWEAK_PARAMETERS);
574 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Tweak parameters"),
575 		message, 'P'));
576 	if (!(ref->kind() & B_CONTROLLABLE))
577 	{
578 		item->SetEnabled(false);
579 	}
580 
581 	message = new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED);
582 	message->AddInt32("nodeID", ref->id());
583 	menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), message, 'I'));
584 	menu->AddSeparatorItem();
585 
586 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Release"),
587 		new BMessage(MediaRoutingView::M_DELETE_SELECTION), 'T'));
588 	if (!ref->isInternal())
589 	{
590 		item->SetEnabled(false);
591 	}
592 	menu->AddSeparatorItem();
593 
594 	// add the "Cycle" item
595 	message = new BMessage(MediaRoutingView::M_NODE_CHANGE_CYCLING);
596 	message->AddBool("cycle", !ref->isCycling());
597 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Cycle"), message));
598 	item->SetMarked(ref->isCycling());
599 	if (ref->flags() & NodeRef::NO_SEEK)
600 	{
601 		item->SetEnabled(false);
602 	}
603 
604 	// add the "Run Mode" sub menu
605 	BMenu *subMenu = new BMenu(B_TRANSLATE("Run mode"));
606 	subMenu->SetFont(be_plain_font);
607 	for (uint32 runMode = 1; runMode <= BMediaNode::B_RECORDING; runMode++)
608 	{
609 		BString itemName = MediaString::getStringFor(static_cast<BMediaNode::run_mode>
610 													 (runMode));
611 		message = new BMessage(MediaRoutingView::M_NODE_CHANGE_RUN_MODE);
612 		message->AddInt32("run_mode", runMode);
613 		subMenu->AddItem(item = new BMenuItem(itemName.String(), message));
614 		if (ref->runMode() == runMode)
615 		{
616 			item->SetMarked(true);
617 		}
618 		else if ((ref->runMode() == 0)
619 			  && (ref->group()) && (ref->group()->runMode() == BMediaNode::run_mode(runMode)))
620 		{
621 			item->SetMarked(true);
622 		}
623 	}
624 	subMenu->AddSeparatorItem();
625 	message = new BMessage(MediaRoutingView::M_NODE_CHANGE_RUN_MODE);
626 	message->AddInt32("run_mode", 0);
627 	subMenu->AddItem(
628 		item = new BMenuItem(B_TRANSLATE("(same as group)"), message));
629 	if (ref->group() == 0)
630 	{
631 		item->SetEnabled(false);
632 	}
633 	else if ((ref->runMode() < 1) && (ref->group()->runMode() > 0))
634 	{
635 		item->SetMarked(true);
636 	}
637 	menu->AddItem(subMenu);
638 	subMenu->SetTargetForItems(view());
639 
640 	// [c.lenz 24dec99] hide rarely used commands in a 'Advanced' submenu
641 	subMenu = new BMenu(B_TRANSLATE("Advanced"));
642 	subMenu->SetFont(be_plain_font);
643 	// [e.moon 5dec99] ad-hoc timesource support
644 	if(ref->kind() & B_TIME_SOURCE) {
645 		message = new BMessage(MediaRoutingView::M_NODE_START_TIME_SOURCE);
646 		message->AddInt32("nodeID", ref->id());
647 		subMenu->AddItem(new BMenuItem(
648 			B_TRANSLATE("Start time source"),
649 			message));
650 		message = new BMessage(MediaRoutingView::M_NODE_START_TIME_SOURCE);
651 		message->AddInt32("nodeID", ref->id());
652 		subMenu->AddItem(new BMenuItem(
653 			B_TRANSLATE("Stop time source"),
654 			message));
655 	}
656 	// [c.lenz 24dec99] support for BControllable::StartControlPanel()
657 	if(ref->kind() & B_CONTROLLABLE) {
658 		if (subMenu->CountItems() > 0)
659 			subMenu->AddSeparatorItem();
660 		message = new BMessage(MediaRoutingView::M_NODE_START_CONTROL_PANEL);
661 		subMenu->AddItem(new BMenuItem(B_TRANSLATE("Start control panel"),
662 			message, 'P', B_COMMAND_KEY | B_SHIFT_KEY));
663 	}
664 	// [em 1feb00] group tweaks
665 	if(ref->group())
666 	{
667 		message = new BMessage(MediaRoutingView::M_GROUP_SET_LOCKED);
668 		message->AddInt32("groupID", ref->group()->id());
669 		bool isLocked = (ref->group()->groupFlags() & NodeGroup::GROUP_LOCKED);
670 		message->AddBool("locked", !isLocked);
671 		if (subMenu->CountItems() > 0)
672 			subMenu->AddSeparatorItem();
673 		subMenu->AddItem(
674 			new BMenuItem(
675 				isLocked ? B_TRANSLATE("Unlock group")
676 						 : B_TRANSLATE("Lock group"), message));
677 	}
678 
679 	if (subMenu->CountItems() > 0)
680 	{
681 		menu->AddItem(subMenu);
682 		subMenu->SetTargetForItems(view());
683 	}
684 
685 	menu->SetTargetForItems(view());
686 	view()->ConvertToScreen(&point);
687 	point -= BPoint(1.0, 1.0);
688 	menu->Go(point, true, true, true);
689 }
690 
691 // ---------------------------------------------------------------- //
692 // BHandler impl
693 // ---------------------------------------------------------------- //
694 
695 void MediaNodePanel::MessageReceived(
696 	BMessage *message)
697 {
698 	D_METHOD(("MediaNodePanel::MessageReceived()\n"));
699 	switch (message->what)
700 	{
701 		case NodeRef::M_INPUTS_CHANGED:
702 		{
703 			D_MESSAGE(("MediaNodePanel::MessageReceived(NodeRef::M_INPUTS_CHANGED)\n"));
704 			_updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout());
705 			break;
706 		}
707 		case NodeRef::M_OUTPUTS_CHANGED:
708 		{
709 			D_MESSAGE(("MediaNodePanel::MessageReceived(NodeRef::M_OUTPUTS_CHANGED)\n"));
710 			_updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout());
711 			break;
712 		}
713 		default:
714 		{
715 			BHandler::MessageReceived(message);
716 			break;
717 		}
718 	}
719 }
720 
721 // -------------------------------------------------------- //
722 // *** IStateArchivable
723 // -------------------------------------------------------- //
724 
725 status_t MediaNodePanel::importState(
726 	const BMessage*						archive) {
727 
728 	BPoint iconPos(s_invalidPosition);
729 	BPoint miniIconPos(s_invalidPosition);
730 
731 	MediaRoutingView* v = dynamic_cast<MediaRoutingView*>(view());
732 	ASSERT(v);
733 	MediaRoutingView::layout_t layoutMode = v->getLayout();
734 	archive->FindPoint("iconPos", &iconPos);
735 	archive->FindPoint("miniIconPos", &miniIconPos);
736 
737 	switch(layoutMode) {
738 		case MediaRoutingView::M_ICON_VIEW:
739 			if(iconPos != s_invalidPosition)
740 				moveTo(iconPos);
741 			m_alternatePosition = miniIconPos;
742 			break;
743 
744 		case MediaRoutingView::M_MINI_ICON_VIEW:
745 			if(miniIconPos != s_invalidPosition)
746 				moveTo(miniIconPos);
747 			m_alternatePosition = iconPos;
748 			break;
749 	}
750 
751 	return B_OK;
752 }
753 
754 status_t MediaNodePanel::exportState(
755 	BMessage*									archive) const {
756 
757 	BPoint iconPos, miniIconPos;
758 
759 	MediaRoutingView* v = dynamic_cast<MediaRoutingView*>(view());
760 	ASSERT(v);
761 	MediaRoutingView::layout_t layoutMode = v->getLayout();
762 	switch(layoutMode) {
763 		case MediaRoutingView::M_ICON_VIEW:
764 			iconPos = Frame().LeftTop();
765 			miniIconPos = m_alternatePosition;
766 			break;
767 
768 		case MediaRoutingView::M_MINI_ICON_VIEW:
769 			miniIconPos = Frame().LeftTop();
770 			iconPos = m_alternatePosition;
771 			break;
772 	}
773 
774 	if(iconPos != s_invalidPosition)
775 		archive->AddPoint("iconPos", iconPos);
776 	if(miniIconPos != s_invalidPosition)
777 		archive->AddPoint("miniIconPos", miniIconPos);
778 
779 	// determine if I'm a 'system' node
780 	port_info portInfo;
781 	app_info appInfo;
782 
783 	if ((get_port_info(ref->node().port, &portInfo) == B_OK)
784 		&& (be_roster->GetRunningAppInfo(portInfo.team, &appInfo) == B_OK)) {
785 		BEntry appEntry(&appInfo.ref);
786 		char appName[B_FILE_NAME_LENGTH];
787 		if(
788 			appEntry.InitCheck() == B_OK &&
789 			appEntry.GetName(appName) == B_OK &&
790 			(!strcmp(appName, "media_addon_server") ||
791 			 !strcmp(appName, "audio_server"))) {
792 
793 			archive->AddBool("sysOwned", true);
794 		}
795 	}
796 
797 	return B_OK;
798 }
799 
800 // ---------------------------------------------------------------- //
801 // *** internal operations
802 // ---------------------------------------------------------------- //
803 
804 void MediaNodePanel::_prepareLabel()
805 {
806 	// find out if its a file node first
807 	if (ref->kind() & B_FILE_INTERFACE)
808 	{
809 		entry_ref nodeFile;
810 		status_t error = BMediaRoster::Roster()->GetRefFor(ref->node(),	&nodeFile);
811 		if (error)
812 		{
813 			m_fullLabel = B_TRANSLATE("%refname% (no file)");
814 			m_fullLabel.ReplaceFirst("%refname%", ref->name());
815 		}
816 		else
817 		{
818 			BEntry entry(&nodeFile);
819 			char fileName[B_FILE_NAME_LENGTH];
820 			entry.GetName(fileName);
821 			m_fullLabel = fileName;
822 		}
823 	}
824 	else
825 	{
826 		m_fullLabel = ref->name();
827 	}
828 
829 	int32 layout = dynamic_cast<MediaRoutingView *>(view())->getLayout();
830 
831 	// Construct labelRect
832 	font_height fh;
833 	be_plain_font->GetHeight(&fh);
834 	switch (layout)
835 	{
836 		case MediaRoutingView::M_ICON_VIEW:
837 		{
838 			m_labelRect = Frame();
839 			m_labelRect.OffsetTo(0.0, 0.0);
840 			m_labelRect.bottom = 2 * M_LABEL_V_MARGIN + (fh.ascent + fh.descent + fh.leading) + 1.0;
841 			break;
842 		}
843 		case MediaRoutingView::M_MINI_ICON_VIEW:
844 		{
845 			m_labelRect = Frame();
846 			m_labelRect.OffsetTo(0.0, 0.0);
847 			m_labelRect.left = M_BODY_H_MARGIN + B_MINI_ICON;
848 			m_labelRect.top += MediaJack::M_DEFAULT_HEIGHT;
849 			m_labelRect.bottom -= MediaJack::M_DEFAULT_HEIGHT;
850 			break;
851 		}
852 	}
853 
854 	// truncate the label to fit in the panel
855 	m_label = m_fullLabel;
856 	float maxWidth = m_labelRect.Width() - (2.0 * M_LABEL_H_MARGIN) - 2.0;
857 	if (be_plain_font->StringWidth(m_fullLabel.String()) > maxWidth)
858 	{
859 		be_plain_font->TruncateString(&m_label, B_TRUNCATE_END, maxWidth);
860 		m_labelTruncated = true;
861 	}
862 
863 	// Construct labelOffset
864 	float fw = be_plain_font->StringWidth(m_label.String());
865 	m_labelOffset.x = m_labelRect.left + m_labelRect.Width() / 2.0 - fw / 2.0;
866 	m_labelOffset.y = m_labelRect.bottom - M_LABEL_V_MARGIN - fh.descent - (fh.leading / 2.0) - 1.0;
867 
868 	// Construct bodyRect
869 	switch (layout)
870 	{
871 		case MediaRoutingView::M_ICON_VIEW:
872 		{
873 			m_bodyRect = Frame();
874 			m_bodyRect.OffsetTo(0.0, 0.0);
875 			m_bodyRect.top = m_labelRect.bottom;
876 			break;
877 		}
878 		case MediaRoutingView::M_MINI_ICON_VIEW:
879 		{
880 			m_bodyRect = Frame();
881 			m_bodyRect.OffsetTo(0.0, 0.0);
882 			m_bodyRect.right = m_labelRect.left;
883 			break;
884 		}
885 	}
886 }
887 
888 void MediaNodePanel::_updateBitmap()
889 {
890 	if (m_bitmap)
891 	{
892 		delete m_bitmap;
893 	}
894 	BBitmap *tempBitmap = new BBitmap(Frame().OffsetToCopy(0.0, 0.0),
895 		B_RGBA32, true);
896 	tempBitmap->Lock();
897 	{
898 		BView *tempView = new BView(tempBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
899 		tempBitmap->AddChild(tempView);
900 		tempView->SetOrigin(0.0, 0.0);
901 
902 		int32 layout = dynamic_cast<MediaRoutingView *>(view())->getLayout();
903 		_drawInto(tempView, tempView->Bounds(), layout);
904 
905 		tempView->Sync();
906 		tempBitmap->RemoveChild(tempView);
907 		delete tempView;
908 	}
909 	tempBitmap->Unlock();
910 	m_bitmap = new BBitmap(tempBitmap);
911 	delete tempBitmap;
912 }
913 
914 void MediaNodePanel::_drawInto(
915 	BView *target,
916 	BRect targetRect,
917 	int32 layout)
918 {
919 	switch (layout)
920 	{
921 		case MediaRoutingView::M_ICON_VIEW:
922 		{
923 			BRect r;
924 			BPoint p;
925 
926 			// Draw borders
927 			r = targetRect;
928 			target->BeginLineArray(16);
929 				target->AddLine(r.LeftTop(), r.RightTop(), M_DARK_GRAY_COLOR);
930 				target->AddLine(r.RightTop(), r.RightBottom(), M_DARK_GRAY_COLOR);
931 				target->AddLine(r.RightBottom(), r.LeftBottom(), M_DARK_GRAY_COLOR);
932 				target->AddLine(r.LeftBottom(), r.LeftTop(), M_DARK_GRAY_COLOR);
933 				r.InsetBy(1.0, 1.0);
934 				target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR);
935 				target->AddLine(r.RightTop(), r.RightBottom(), M_MED_GRAY_COLOR);
936 				target->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR);
937 				target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR);
938 			target->EndLineArray();
939 
940 			// Fill background
941 			r.InsetBy(1.0, 1.0);
942 			target->SetLowColor(M_GRAY_COLOR);
943 			target->FillRect(r, B_SOLID_LOW);
944 
945 			// Draw icon
946 			if (m_icon)
947 			{
948 				p.x = m_bodyRect.left + m_bodyRect.Width() / 2.0 - B_LARGE_ICON / 2.0;
949 				p.y = m_labelRect.bottom + m_bodyRect.Height() / 2.0 - B_LARGE_ICON / 2.0;
950 				target->SetDrawingMode(B_OP_OVER);
951 				target->DrawBitmapAsync(m_icon, p);
952 			}
953 
954 			// Draw label
955 			if (isSelected())
956 			{
957 				r = m_labelRect;
958 				r.InsetBy(M_LABEL_H_MARGIN, M_LABEL_V_MARGIN);
959 				target->BeginLineArray(4);
960 					target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_BLUE_COLOR);
961 					target->AddLine(r.RightTop(), r.RightBottom(), M_LIGHT_BLUE_COLOR);
962 					target->AddLine(r.RightBottom(), r.LeftBottom(), M_LIGHT_BLUE_COLOR);
963 					target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_BLUE_COLOR);
964 				target->EndLineArray();
965 				r.InsetBy(1.0, 1.0);
966 				target->SetHighColor(M_DARK_BLUE_COLOR);
967 				target->FillRect(r, B_SOLID_HIGH);
968 			}
969 			target->SetDrawingMode(B_OP_OVER);
970 			target->SetHighColor(isSelected() ? M_WHITE_COLOR : M_BLACK_COLOR);
971 			target->DrawString(m_label.String(), m_labelOffset);
972 			break;
973 		}
974 		case MediaRoutingView::M_MINI_ICON_VIEW:
975 		{
976 			BRect r;
977 			BPoint p;
978 
979 			// Draw borders
980 			r = targetRect;
981 			target->BeginLineArray(16);
982 				target->AddLine(r.LeftTop(), r.RightTop(), M_DARK_GRAY_COLOR);
983 				target->AddLine(r.RightTop(), r.RightBottom(), M_DARK_GRAY_COLOR);
984 				target->AddLine(r.RightBottom(), r.LeftBottom(), M_DARK_GRAY_COLOR);
985 				target->AddLine(r.LeftBottom(), r.LeftTop(), M_DARK_GRAY_COLOR);
986 				r.InsetBy(1.0, 1.0);
987 				target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR);
988 				target->AddLine(r.RightTop(), r.RightBottom(), M_MED_GRAY_COLOR);
989 				target->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR);
990 				target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR);
991 			target->EndLineArray();
992 
993 			// Fill background
994 			r.InsetBy(1.0, 1.0);
995 			target->SetLowColor(M_GRAY_COLOR);
996 			target->FillRect(r, B_SOLID_LOW);
997 
998 			// Draw icon
999 			if (m_icon)
1000 			{
1001 				p.x = m_bodyRect.left + M_BODY_H_MARGIN;
1002 				p.y = m_bodyRect.top + (m_bodyRect.Height() / 2.0) - (B_MINI_ICON / 2.0);
1003 				target->SetDrawingMode(B_OP_OVER);
1004 				target->DrawBitmapAsync(m_icon, p);
1005 			}
1006 
1007 			// Draw label
1008 			if (isSelected())
1009 			{
1010 				r = m_labelRect;
1011 				r.InsetBy(M_LABEL_H_MARGIN, M_LABEL_V_MARGIN);
1012 				target->BeginLineArray(4);
1013 					target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_BLUE_COLOR);
1014 					target->AddLine(r.RightTop(), r.RightBottom(), M_LIGHT_BLUE_COLOR);
1015 					target->AddLine(r.RightBottom(), r.LeftBottom(), M_LIGHT_BLUE_COLOR);
1016 					target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_BLUE_COLOR);
1017 				target->EndLineArray();
1018 				r.InsetBy(1.0, 1.0);
1019 				target->SetHighColor(M_DARK_BLUE_COLOR);
1020 				target->FillRect(r, B_SOLID_HIGH);
1021 			}
1022 			target->SetDrawingMode(B_OP_OVER);
1023 			target->SetHighColor(isSelected() ? M_WHITE_COLOR : M_BLACK_COLOR);
1024 			target->DrawString(m_label.String(), m_labelOffset);
1025 			break;
1026 		}
1027 	}
1028 }
1029 
1030 void MediaNodePanel::_updateIcon(
1031 	int32 layout)
1032 {
1033 	D_METHOD(("MediaNodePanel::_updateIcon()\n"));
1034 
1035 	if (m_icon)
1036 	{
1037 		delete m_icon;
1038 		m_icon = 0;
1039 	}
1040 	RouteAppNodeManager *manager;
1041 	manager = dynamic_cast<MediaRoutingView *>(view())->manager;
1042 	switch (layout)
1043 	{
1044 		case MediaRoutingView::M_ICON_VIEW:
1045 		{
1046 			const MediaIcon *icon = manager->mediaIconFor(ref->id(), B_LARGE_ICON);
1047 			m_icon = new BBitmap(dynamic_cast<const BBitmap *>(icon));
1048 			break;
1049 		}
1050 		case MediaRoutingView::M_MINI_ICON_VIEW:
1051 		{
1052 			const MediaIcon *icon = manager->mediaIconFor(ref->id(), B_MINI_ICON);
1053 			m_icon = new BBitmap(dynamic_cast<const BBitmap *>(icon));
1054 			break;
1055 		}
1056 	}
1057 }
1058 
1059 // -------------------------------------------------------- //
1060 // *** sorting methods (friend)
1061 // -------------------------------------------------------- //
1062 
1063 int __CORTEX_NAMESPACE__ compareID(
1064 	const void *lValue,
1065 	const void *rValue)
1066 {
1067 	int retValue = 0;
1068 	const MediaNodePanel *lPanel = *(reinterpret_cast<MediaNodePanel * const*>(reinterpret_cast<void * const*>(lValue)));
1069 	const MediaNodePanel *rPanel = *(reinterpret_cast<MediaNodePanel * const*>(reinterpret_cast<void * const*>(rValue)));
1070 	if (lPanel && rPanel)
1071 	{
1072 		if (lPanel->ref->id() < rPanel->ref->id())
1073 		{
1074 			retValue = -1;
1075 		}
1076 		else if (lPanel->ref->id() > rPanel->ref->id())
1077 		{
1078 			retValue = 1;
1079 		}
1080 	}
1081 	return retValue;
1082 }
1083 
1084 // END -- MediaNodePanel.cpp --
1085