3 * Implements the main PatchBay view class.
5 * Copyright 2013, Haiku, Inc. All rights reserved.
6 * Distributed under the terms of the MIT License.
8 * Revisions by Pete Goodeve
10 * Copyright 1999, Be Incorporated. All Rights Reserved.
11 * This file may be used under the terms of the Be Sample Code License.
14 #include "PatchView.h"
16 #include <Application.h>
20 #include <IconUtils.h>
21 #include <InterfaceDefs.h>
23 #include <Messenger.h>
24 #include <MidiRoster.h>
27 #include "EndpointInfo.h"
29 #include "UnknownDeviceIcons.h"
32 #define B_TRANSLATION_CONTEXT "Patch Bay"
35 PatchView::PatchView(BRect rect
)
37 BView(rect
, "PatchView", B_FOLLOW_ALL
, B_WILL_DRAW
),
38 fUnknownDeviceIcon(NULL
)
40 SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
42 BRect
iconRect(0, 0, LARGE_ICON_SIZE
- 1, LARGE_ICON_SIZE
- 1);
43 fUnknownDeviceIcon
= new BBitmap(iconRect
, B_RGBA32
);
44 if (BIconUtils::GetVectorIcon(
45 UnknownDevice::kVectorIcon
,
46 sizeof(UnknownDevice::kVectorIcon
),
47 fUnknownDeviceIcon
) == B_OK
)
49 delete fUnknownDeviceIcon
;
53 PatchView::~PatchView()
55 delete fUnknownDeviceIcon
;
60 PatchView::AttachedToWindow()
62 BMidiRoster
* roster
= BMidiRoster::MidiRoster();
64 PRINT(("Couldn't get MIDI roster\n"));
65 be_app
->PostMessage(B_QUIT_REQUESTED
);
69 BMessenger
msgr(this);
70 roster
->StartWatching(&msgr
);
71 SetHighUIColor(B_PANEL_TEXT_COLOR
);
76 PatchView::MessageReceived(BMessage
* msg
)
83 BView::MessageReceived(msg
);
90 PatchView::GetToolTipAt(BPoint point
, BToolTip
** tip
)
94 endpoint_itor begin
, end
;
95 int32 size
= fConsumers
.size();
96 for (int32 i
= 0; !found
&& i
< size
; i
++) {
97 BRect r
= ColumnIconFrameAt(i
);
98 if (r
.Contains(point
)) {
99 begin
= fConsumers
.begin();
100 end
= fConsumers
.end();
105 size
= fProducers
.size();
106 for (int32 i
= 0; !found
&& i
< size
; i
++) {
107 BRect r
= RowIconFrameAt(i
);
108 if (r
.Contains(point
)) {
109 begin
= fProducers
.begin();
110 end
= fProducers
.end();
120 for (itor
= begin
; itor
!= end
; itor
++, index
--)
127 BMidiRoster
* roster
= BMidiRoster::MidiRoster();
130 BMidiEndpoint
* obj
= roster
->FindEndpoint(itor
->ID());
135 str
<< "<" << obj
->ID() << ">: " << obj
->Name();
138 SetToolTip(str
.String());
147 PatchView::Draw(BRect
/* updateRect */)
149 // draw producer icons
150 SetDrawingMode(B_OP_OVER
);
152 for (list
<EndpointInfo
>::const_iterator i
= fProducers
.begin();
153 i
!= fProducers
.end(); i
++) {
154 const BBitmap
* bitmap
= (i
->Icon()) ? i
->Icon() : fUnknownDeviceIcon
;
155 DrawBitmapAsync(bitmap
, RowIconFrameAt(index
++).LeftTop());
158 // draw consumer icons
160 for (list
<EndpointInfo
>::const_iterator i
= fConsumers
.begin();
161 i
!= fConsumers
.end(); i
++) {
162 const BBitmap
* bitmap
= (i
->Icon()) ? i
->Icon() : fUnknownDeviceIcon
;
163 DrawBitmapAsync(bitmap
, ColumnIconFrameAt(index2
++).LeftTop());
166 if (index
== 0 && index2
== 0) {
167 const char* message
= B_TRANSLATE("No MIDI devices found!");
168 float width
= StringWidth(message
);
169 BRect rect
= Bounds();
171 rect
.top
= rect
.top
+ rect
.bottom
/ 2;
172 rect
.left
= rect
.left
+ rect
.right
/ 2;
173 rect
.left
-= width
/ 2;
175 DrawString(message
, rect
.LeftTop());
177 // Since the message is centered, we need to redraw the whole view in
179 SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE
);
181 SetFlags(Flags() & ~B_FULL_UPDATE_ON_RESIZE
);
186 PatchView::ColumnIconFrameAt(int32 index
) const
189 rect
.left
= ROW_LEFT
+ METER_PADDING
+ index
* COLUMN_WIDTH
;
191 rect
.right
= rect
.left
+ 31;
192 rect
.bottom
= rect
.top
+ 31;
198 PatchView::RowIconFrameAt(int32 index
) const
202 rect
.top
= ROW_TOP
+ index
* ROW_HEIGHT
;
203 rect
.right
= rect
.left
+ 31;
204 rect
.bottom
= rect
.top
+ 31;
210 PatchView::HandleMidiEvent(BMessage
* msg
)
212 SET_DEBUG_ENABLED(true);
215 if (msg
->FindInt32("be:op", &op
) != B_OK
) {
216 PRINT(("PatchView::HandleMidiEvent: \"op\" field not found\n"));
221 case B_MIDI_REGISTERED
:
224 if (msg
->FindInt32("be:id", &id
) != B_OK
) {
225 PRINT(("PatchView::HandleMidiEvent: \"be:id\""
226 " field not found in B_MIDI_REGISTERED event\n"));
231 if (msg
->FindString("be:type", &type
) != B_OK
) {
232 PRINT(("PatchView::HandleMidiEvent: \"be:type\""
233 " field not found in B_MIDI_REGISTERED event\n"));
237 PRINT(("MIDI Roster Event B_MIDI_REGISTERED: id=%" B_PRId32
238 ", type=%s\n", id
, type
));
239 if (strcmp(type
, "producer") == 0)
241 else if (strcmp(type
, "consumer") == 0)
245 case B_MIDI_UNREGISTERED
:
248 if (msg
->FindInt32("be:id", &id
) != B_OK
) {
249 PRINT(("PatchView::HandleMidiEvent: \"be:id\""
250 " field not found in B_MIDI_UNREGISTERED\n"));
255 if (msg
->FindString("be:type", &type
) != B_OK
) {
256 PRINT(("PatchView::HandleMidiEvent: \"be:type\""
257 " field not found in B_MIDI_UNREGISTERED\n"));
261 PRINT(("MIDI Roster Event B_MIDI_UNREGISTERED: id=%" B_PRId32
262 ", type=%s\n", id
, type
));
263 if (strcmp(type
, "producer") == 0)
265 else if (strcmp(type
, "consumer") == 0)
269 case B_MIDI_CHANGED_PROPERTIES
:
272 if (msg
->FindInt32("be:id", &id
) != B_OK
) {
273 PRINT(("PatchView::HandleMidiEvent: \"be:id\""
274 " field not found in B_MIDI_CHANGED_PROPERTIES\n"));
279 if (msg
->FindString("be:type", &type
) != B_OK
) {
280 PRINT(("PatchView::HandleMidiEvent: \"be:type\""
281 " field not found in B_MIDI_CHANGED_PROPERTIES\n"));
286 if (msg
->FindMessage("be:properties", &props
) != B_OK
) {
287 PRINT(("PatchView::HandleMidiEvent: \"be:properties\""
288 " field not found in B_MIDI_CHANGED_PROPERTIES\n"));
292 PRINT(("MIDI Roster Event B_MIDI_CHANGED_PROPERTIES: id=%" B_PRId32
293 ", type=%s\n", id
, type
));
294 if (strcmp(type
, "producer") == 0)
295 UpdateProducerProps(id
, &props
);
296 else if (strcmp(type
, "consumer") == 0)
297 UpdateConsumerProps(id
, &props
);
301 case B_MIDI_CHANGED_NAME
:
302 case B_MIDI_CHANGED_LATENCY
:
303 // we don't care about these
305 case B_MIDI_CONNECTED
:
308 if (msg
->FindInt32("be:producer", &prod
) != B_OK
) {
309 PRINT(("PatchView::HandleMidiEvent: \"be:producer\""
310 " field not found in B_MIDI_CONNECTED\n"));
315 if (msg
->FindInt32("be:consumer", &cons
) != B_OK
) {
316 PRINT(("PatchView::HandleMidiEvent: \"be:consumer\""
317 " field not found in B_MIDI_CONNECTED\n"));
320 PRINT(("MIDI Roster Event B_MIDI_CONNECTED: producer=%" B_PRId32
321 ", consumer=%" B_PRId32
"\n", prod
, cons
));
325 case B_MIDI_DISCONNECTED
:
328 if (msg
->FindInt32("be:producer", &prod
) != B_OK
) {
329 PRINT(("PatchView::HandleMidiEvent: \"be:producer\""
330 " field not found in B_MIDI_DISCONNECTED\n"));
335 if (msg
->FindInt32("be:consumer", &cons
) != B_OK
) {
336 PRINT(("PatchView::HandleMidiEvent: \"be:consumer\""
337 " field not found in B_MIDI_DISCONNECTED\n"));
340 PRINT(("MIDI Roster Event B_MIDI_DISCONNECTED: producer=%" B_PRId32
341 ", consumer=%" B_PRId32
"\n", prod
, cons
));
342 Disconnect(prod
, cons
);
346 PRINT(("PatchView::HandleMidiEvent: unknown opcode %" B_PRId32
"\n",
354 PatchView::AddProducer(int32 id
)
356 EndpointInfo
info(id
);
357 fProducers
.push_back(info
);
359 Window()->BeginViewTransaction();
360 PatchRow
* row
= new PatchRow(id
);
361 fPatchRows
.push_back(row
);
362 BPoint p1
= CalcRowOrigin(fPatchRows
.size() - 1);
363 BPoint p2
= CalcRowSize();
365 row
->ResizeTo(p2
.x
, p2
.y
);
366 for (list
<EndpointInfo
>::const_iterator i
= fConsumers
.begin();
367 i
!= fConsumers
.end(); i
++)
368 row
->AddColumn(i
->ID());
371 Window()->EndViewTransaction();
376 PatchView::AddConsumer(int32 id
)
378 EndpointInfo
info(id
);
379 fConsumers
.push_back(info
);
381 Window()->BeginViewTransaction();
382 BPoint newSize
= CalcRowSize();
383 for (row_itor i
= fPatchRows
.begin(); i
!= fPatchRows
.end(); i
++) {
385 (*i
)->ResizeTo(newSize
.x
, newSize
.y
- 1);
388 Window()->EndViewTransaction();
393 PatchView::RemoveProducer(int32 id
)
395 for (endpoint_itor i
= fProducers
.begin(); i
!= fProducers
.end(); i
++) {
402 Window()->BeginViewTransaction();
403 for (row_itor i
= fPatchRows
.begin(); i
!= fPatchRows
.end(); i
++) {
404 if ((*i
)->ID() == id
) {
406 i
= fPatchRows
.erase(i
);
409 float moveBy
= -1 * CalcRowSize().y
;
410 while (i
!= fPatchRows
.end()) {
411 (*i
++)->MoveBy(0, moveBy
);
417 Window()->EndViewTransaction();
422 PatchView::RemoveConsumer(int32 id
)
424 Window()->BeginViewTransaction();
425 for (endpoint_itor i
= fConsumers
.begin(); i
!= fConsumers
.end(); i
++) {
432 BPoint newSize
= CalcRowSize();
433 for (row_itor i
= fPatchRows
.begin(); i
!= fPatchRows
.end(); i
++) {
434 (*i
)->RemoveColumn(id
);
435 (*i
)->ResizeTo(newSize
.x
, newSize
.y
- 1);
438 Window()->EndViewTransaction();
443 PatchView::UpdateProducerProps(int32 id
, const BMessage
* props
)
445 for (endpoint_itor i
= fProducers
.begin(); i
!= fProducers
.end(); i
++) {
447 i
->UpdateProperties(props
);
456 PatchView::UpdateConsumerProps(int32 id
, const BMessage
* props
)
458 for (endpoint_itor i
= fConsumers
.begin(); i
!= fConsumers
.end(); i
++) {
460 i
->UpdateProperties(props
);
469 PatchView::Connect(int32 prod
, int32 cons
)
471 for (row_itor i
= fPatchRows
.begin(); i
!= fPatchRows
.end(); i
++) {
472 if ((*i
)->ID() == prod
) {
481 PatchView::Disconnect(int32 prod
, int32 cons
)
483 for (row_itor i
= fPatchRows
.begin(); i
!= fPatchRows
.end(); i
++) {
484 if ((*i
)->ID() == prod
) {
485 (*i
)->Disconnect(cons
);
493 PatchView::CalcRowOrigin(int32 rowIndex
) const
497 point
.y
= ROW_TOP
+ rowIndex
* ROW_HEIGHT
;
503 PatchView::CalcRowSize() const
506 point
.x
= METER_PADDING
+ fConsumers
.size()*COLUMN_WIDTH
;
507 point
.y
= ROW_HEIGHT
- 1;