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