1 /* PatchView.cpp 2 * ------------- 3 * Implements the main PatchBay view class. 4 * 5 * Copyright 2013, Haiku, Inc. All rights reserved. 6 * Distributed under the terms of the MIT License. 7 * 8 * Revisions by Pete Goodeve 9 * 10 * Copyright 1999, Be Incorporated. All Rights Reserved. 11 * This file may be used under the terms of the Be Sample Code License. 12 */ 13 14 #include "PatchView.h" 15 16 #include <Application.h> 17 #include <Bitmap.h> 18 #include <Debug.h> 19 #include <IconUtils.h> 20 #include <InterfaceDefs.h> 21 #include <Message.h> 22 #include <Messenger.h> 23 #include <MidiRoster.h> 24 #include <Window.h> 25 26 #include "EndpointInfo.h" 27 #include "PatchRow.h" 28 #include "UnknownDeviceIcons.h" 29 30 31 PatchView::PatchView(BRect rect) 32 : 33 BView(rect, "PatchView", B_FOLLOW_ALL, B_WILL_DRAW), 34 fUnknownDeviceIcon(NULL) 35 { 36 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 37 38 BRect iconRect(0, 0, LARGE_ICON_SIZE - 1, LARGE_ICON_SIZE - 1); 39 fUnknownDeviceIcon = new BBitmap(iconRect, B_RGBA32); 40 if (BIconUtils::GetVectorIcon( 41 UnknownDevice::kVectorIcon, 42 sizeof(UnknownDevice::kVectorIcon), 43 fUnknownDeviceIcon) == B_OK) 44 return; 45 delete fUnknownDeviceIcon; 46 } 47 48 49 PatchView::~PatchView() 50 { 51 delete fUnknownDeviceIcon; 52 } 53 54 55 void 56 PatchView::AttachedToWindow() 57 { 58 BMidiRoster* roster = BMidiRoster::MidiRoster(); 59 if (roster == NULL) { 60 PRINT(("Couldn't get MIDI roster\n")); 61 be_app->PostMessage(B_QUIT_REQUESTED); 62 return; 63 } 64 65 BMessenger msgr(this); 66 roster->StartWatching(&msgr); 67 } 68 69 70 void 71 PatchView::MessageReceived(BMessage* msg) 72 { 73 switch (msg->what) { 74 case B_MIDI_EVENT: 75 HandleMidiEvent(msg); 76 break; 77 default: 78 BView::MessageReceived(msg); 79 break; 80 } 81 } 82 83 84 bool 85 PatchView::GetToolTipAt(BPoint point, BToolTip** tip) 86 { 87 bool found = false; 88 int32 index = 0; 89 endpoint_itor begin, end; 90 int32 size = fConsumers.size(); 91 for (int32 i = 0; !found && i < size; i++) { 92 BRect r = ColumnIconFrameAt(i); 93 if (r.Contains(point)) { 94 begin = fConsumers.begin(); 95 end = fConsumers.end(); 96 found = true; 97 index = i; 98 } 99 } 100 size = fProducers.size(); 101 for (int32 i = 0; !found && i < size; i++) { 102 BRect r = RowIconFrameAt(i); 103 if (r.Contains(point)) { 104 begin = fProducers.begin(); 105 end = fProducers.end(); 106 found = true; 107 index = i; 108 } 109 } 110 111 if (!found) 112 return false; 113 114 endpoint_itor itor; 115 for (itor = begin; itor != end; itor++, index--) 116 if (index <= 0) 117 break; 118 119 if (itor == end) 120 return false; 121 122 BMidiRoster* roster = BMidiRoster::MidiRoster(); 123 if (roster == NULL) 124 return false; 125 BMidiEndpoint* obj = roster->FindEndpoint(itor->ID()); 126 if (obj == NULL) 127 return false; 128 129 BString str; 130 str << "<" << obj->ID() << ">: " << obj->Name(); 131 obj->Release(); 132 133 SetToolTip(str.String()); 134 135 *tip = ToolTip(); 136 137 return true; 138 } 139 140 141 void 142 PatchView::Draw(BRect /* updateRect */) 143 { 144 // draw producer icons 145 SetDrawingMode(B_OP_OVER); 146 int32 index = 0; 147 for (list<EndpointInfo>::const_iterator i = fProducers.begin(); 148 i != fProducers.end(); i++) { 149 const BBitmap* bitmap = (i->Icon()) ? i->Icon() : fUnknownDeviceIcon; 150 DrawBitmapAsync(bitmap, RowIconFrameAt(index++).LeftTop()); 151 } 152 153 // draw consumer icons 154 index = 0; 155 for (list<EndpointInfo>::const_iterator i = fConsumers.begin(); 156 i != fConsumers.end(); i++) { 157 const BBitmap* bitmap = (i->Icon()) ? i->Icon() : fUnknownDeviceIcon; 158 DrawBitmapAsync(bitmap, ColumnIconFrameAt(index++).LeftTop()); 159 } 160 } 161 162 163 BRect 164 PatchView::ColumnIconFrameAt(int32 index) const 165 { 166 BRect rect; 167 rect.left = ROW_LEFT + METER_PADDING + index * COLUMN_WIDTH; 168 rect.top = 10; 169 rect.right = rect.left + 31; 170 rect.bottom = rect.top + 31; 171 return rect; 172 } 173 174 175 BRect 176 PatchView::RowIconFrameAt(int32 index) const 177 { 178 BRect rect; 179 rect.left = 10; 180 rect.top = ROW_TOP + index * ROW_HEIGHT; 181 rect.right = rect.left + 31; 182 rect.bottom = rect.top + 31; 183 return rect; 184 } 185 186 187 void 188 PatchView::HandleMidiEvent(BMessage* msg) 189 { 190 SET_DEBUG_ENABLED(true); 191 192 int32 op; 193 if (msg->FindInt32("be:op", &op) != B_OK) { 194 PRINT(("PatchView::HandleMidiEvent: \"op\" field not found\n")); 195 return; 196 } 197 198 switch (op) { 199 case B_MIDI_REGISTERED: 200 { 201 int32 id; 202 if (msg->FindInt32("be:id", &id) != B_OK) { 203 PRINT(("PatchView::HandleMidiEvent: \"be:id\"" 204 " field not found in B_MIDI_REGISTERED event\n")); 205 break; 206 } 207 208 const char* type; 209 if (msg->FindString("be:type", &type) != B_OK) { 210 PRINT(("PatchView::HandleMidiEvent: \"be:type\"" 211 " field not found in B_MIDI_REGISTERED event\n")); 212 break; 213 } 214 215 PRINT(("MIDI Roster Event B_MIDI_REGISTERED: id=%ld, type=%s\n", 216 id, type)); 217 if (strcmp(type, "producer") == 0) 218 AddProducer(id); 219 else if (strcmp(type, "consumer") == 0) 220 AddConsumer(id); 221 } 222 break; 223 case B_MIDI_UNREGISTERED: 224 { 225 int32 id; 226 if (msg->FindInt32("be:id", &id) != B_OK) { 227 PRINT(("PatchView::HandleMidiEvent: \"be:id\"" 228 " field not found in B_MIDI_UNREGISTERED\n")); 229 break; 230 } 231 232 const char* type; 233 if (msg->FindString("be:type", &type) != B_OK) { 234 PRINT(("PatchView::HandleMidiEvent: \"be:type\"" 235 " field not found in B_MIDI_UNREGISTERED\n")); 236 break; 237 } 238 239 PRINT(("MIDI Roster Event B_MIDI_UNREGISTERED: id=%ld, type=%s\n", 240 id, type)); 241 if (strcmp(type, "producer") == 0) 242 RemoveProducer(id); 243 else if (strcmp(type, "consumer") == 0) 244 RemoveConsumer(id); 245 } 246 break; 247 case B_MIDI_CHANGED_PROPERTIES: 248 { 249 int32 id; 250 if (msg->FindInt32("be:id", &id) != B_OK) { 251 PRINT(("PatchView::HandleMidiEvent: \"be:id\"" 252 " field not found in B_MIDI_CHANGED_PROPERTIES\n")); 253 break; 254 } 255 256 const char* type; 257 if (msg->FindString("be:type", &type) != B_OK) { 258 PRINT(("PatchView::HandleMidiEvent: \"be:type\"" 259 " field not found in B_MIDI_CHANGED_PROPERTIES\n")); 260 break; 261 } 262 263 BMessage props; 264 if (msg->FindMessage("be:properties", &props) != B_OK) { 265 PRINT(("PatchView::HandleMidiEvent: \"be:properties\"" 266 " field not found in B_MIDI_CHANGED_PROPERTIES\n")); 267 break; 268 } 269 270 PRINT(("MIDI Roster Event B_MIDI_CHANGED_PROPERTIES: id=%ld, type=%s\n", 271 id, type)); 272 if (strcmp(type, "producer") == 0) 273 UpdateProducerProps(id, &props); 274 else if (strcmp(type, "consumer") == 0) 275 UpdateConsumerProps(id, &props); 276 277 } 278 break; 279 case B_MIDI_CHANGED_NAME: 280 case B_MIDI_CHANGED_LATENCY: 281 // we don't care about these 282 break; 283 case B_MIDI_CONNECTED: 284 { 285 int32 prod; 286 if (msg->FindInt32("be:producer", &prod) != B_OK) { 287 PRINT(("PatchView::HandleMidiEvent: \"be:producer\"" 288 " field not found in B_MIDI_CONNECTED\n")); 289 break; 290 } 291 292 int32 cons; 293 if (msg->FindInt32("be:consumer", &cons) != B_OK) { 294 PRINT(("PatchView::HandleMidiEvent: \"be:consumer\"" 295 " field not found in B_MIDI_CONNECTED\n")); 296 break; 297 } 298 PRINT(("MIDI Roster Event B_MIDI_CONNECTED: producer=%ld, consumer=%ld\n", 299 prod, cons)); 300 Connect(prod, cons); 301 } 302 break; 303 case B_MIDI_DISCONNECTED: 304 { 305 int32 prod; 306 if (msg->FindInt32("be:producer", &prod) != B_OK) { 307 PRINT(("PatchView::HandleMidiEvent: \"be:producer\"" 308 " field not found in B_MIDI_DISCONNECTED\n")); 309 break; 310 } 311 312 int32 cons; 313 if (msg->FindInt32("be:consumer", &cons) != B_OK) { 314 PRINT(("PatchView::HandleMidiEvent: \"be:consumer\"" 315 " field not found in B_MIDI_DISCONNECTED\n")); 316 break; 317 } 318 PRINT(("MIDI Roster Event B_MIDI_DISCONNECTED: producer=%ld, consumer=%ld\n", 319 prod, cons)); 320 Disconnect(prod, cons); 321 } 322 break; 323 default: 324 PRINT(("PatchView::HandleMidiEvent: unknown opcode %ld\n", op)); 325 break; 326 } 327 } 328 329 330 void 331 PatchView::AddProducer(int32 id) 332 { 333 EndpointInfo info(id); 334 fProducers.push_back(info); 335 336 Window()->BeginViewTransaction(); 337 PatchRow* row = new PatchRow(id); 338 fPatchRows.push_back(row); 339 BPoint p1 = CalcRowOrigin(fPatchRows.size() - 1); 340 BPoint p2 = CalcRowSize(); 341 row->MoveTo(p1); 342 row->ResizeTo(p2.x, p2.y); 343 for (list<EndpointInfo>::const_iterator i = fConsumers.begin(); 344 i != fConsumers.end(); i++) 345 row->AddColumn(i->ID()); 346 AddChild(row); 347 Invalidate(); 348 Window()->EndViewTransaction(); 349 } 350 351 352 void 353 PatchView::AddConsumer(int32 id) 354 { 355 EndpointInfo info(id); 356 fConsumers.push_back(info); 357 358 Window()->BeginViewTransaction(); 359 BPoint newSize = CalcRowSize(); 360 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 361 (*i)->AddColumn(id); 362 (*i)->ResizeTo(newSize.x, newSize.y - 1); 363 } 364 Invalidate(); 365 Window()->EndViewTransaction(); 366 } 367 368 369 void 370 PatchView::RemoveProducer(int32 id) 371 { 372 for (endpoint_itor i = fProducers.begin(); i != fProducers.end(); i++) { 373 if (i->ID() == id) { 374 fProducers.erase(i); 375 break; 376 } 377 } 378 379 Window()->BeginViewTransaction(); 380 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 381 if ((*i)->ID() == id) { 382 PatchRow* row = *i; 383 i = fPatchRows.erase(i); 384 RemoveChild(row); 385 delete row; 386 float moveBy = -1 * CalcRowSize().y; 387 while (i != fPatchRows.end()) { 388 (*i++)->MoveBy(0, moveBy); 389 } 390 break; 391 } 392 } 393 Invalidate(); 394 Window()->EndViewTransaction(); 395 } 396 397 398 void 399 PatchView::RemoveConsumer(int32 id) 400 { 401 Window()->BeginViewTransaction(); 402 for (endpoint_itor i = fConsumers.begin(); i != fConsumers.end(); i++) { 403 if (i->ID() == id) { 404 fConsumers.erase(i); 405 break; 406 } 407 } 408 409 BPoint newSize = CalcRowSize(); 410 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 411 (*i)->RemoveColumn(id); 412 (*i)->ResizeTo(newSize.x, newSize.y - 1); 413 } 414 Invalidate(); 415 Window()->EndViewTransaction(); 416 } 417 418 419 void 420 PatchView::UpdateProducerProps(int32 id, const BMessage* props) 421 { 422 for (endpoint_itor i = fProducers.begin(); i != fProducers.end(); i++) { 423 if (i->ID() == id) { 424 i->UpdateProperties(props); 425 Invalidate(); 426 break; 427 } 428 } 429 } 430 431 432 void 433 PatchView::UpdateConsumerProps(int32 id, const BMessage* props) 434 { 435 for (endpoint_itor i = fConsumers.begin(); i != fConsumers.end(); i++) { 436 if (i->ID() == id) { 437 i->UpdateProperties(props); 438 Invalidate(); 439 break; 440 } 441 } 442 } 443 444 445 void 446 PatchView::Connect(int32 prod, int32 cons) 447 { 448 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 449 if ((*i)->ID() == prod) { 450 (*i)->Connect(cons); 451 break; 452 } 453 } 454 } 455 456 457 void 458 PatchView::Disconnect(int32 prod, int32 cons) 459 { 460 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 461 if ((*i)->ID() == prod) { 462 (*i)->Disconnect(cons); 463 break; 464 } 465 } 466 } 467 468 469 BPoint 470 PatchView::CalcRowOrigin(int32 rowIndex) const 471 { 472 BPoint point; 473 point.x = ROW_LEFT; 474 point.y = ROW_TOP + rowIndex * ROW_HEIGHT; 475 return point; 476 } 477 478 479 BPoint 480 PatchView::CalcRowSize() const 481 { 482 BPoint point; 483 point.x = METER_PADDING + fConsumers.size()*COLUMN_WIDTH; 484 point.y = ROW_HEIGHT - 1; 485 return point; 486 } 487