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