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