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