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=%" B_PRId32 216 ", type=%s\n", 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=%" B_PRId32 240 ", type=%s\n", 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=%" B_PRId32 271 ", type=%s\n", 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=%" B_PRId32 299 ", consumer=%" B_PRId32 "\n", 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=%" B_PRId32 319 ", consumer=%" B_PRId32 "\n", prod, cons)); 320 Disconnect(prod, cons); 321 } 322 break; 323 default: 324 PRINT(("PatchView::HandleMidiEvent: unknown opcode %" B_PRId32 "\n", 325 op)); 326 break; 327 } 328 } 329 330 331 void 332 PatchView::AddProducer(int32 id) 333 { 334 EndpointInfo info(id); 335 fProducers.push_back(info); 336 337 Window()->BeginViewTransaction(); 338 PatchRow* row = new PatchRow(id); 339 fPatchRows.push_back(row); 340 BPoint p1 = CalcRowOrigin(fPatchRows.size() - 1); 341 BPoint p2 = CalcRowSize(); 342 row->MoveTo(p1); 343 row->ResizeTo(p2.x, p2.y); 344 for (list<EndpointInfo>::const_iterator i = fConsumers.begin(); 345 i != fConsumers.end(); i++) 346 row->AddColumn(i->ID()); 347 AddChild(row); 348 Invalidate(); 349 Window()->EndViewTransaction(); 350 } 351 352 353 void 354 PatchView::AddConsumer(int32 id) 355 { 356 EndpointInfo info(id); 357 fConsumers.push_back(info); 358 359 Window()->BeginViewTransaction(); 360 BPoint newSize = CalcRowSize(); 361 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 362 (*i)->AddColumn(id); 363 (*i)->ResizeTo(newSize.x, newSize.y - 1); 364 } 365 Invalidate(); 366 Window()->EndViewTransaction(); 367 } 368 369 370 void 371 PatchView::RemoveProducer(int32 id) 372 { 373 for (endpoint_itor i = fProducers.begin(); i != fProducers.end(); i++) { 374 if (i->ID() == id) { 375 fProducers.erase(i); 376 break; 377 } 378 } 379 380 Window()->BeginViewTransaction(); 381 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 382 if ((*i)->ID() == id) { 383 PatchRow* row = *i; 384 i = fPatchRows.erase(i); 385 RemoveChild(row); 386 delete row; 387 float moveBy = -1 * CalcRowSize().y; 388 while (i != fPatchRows.end()) { 389 (*i++)->MoveBy(0, moveBy); 390 } 391 break; 392 } 393 } 394 Invalidate(); 395 Window()->EndViewTransaction(); 396 } 397 398 399 void 400 PatchView::RemoveConsumer(int32 id) 401 { 402 Window()->BeginViewTransaction(); 403 for (endpoint_itor i = fConsumers.begin(); i != fConsumers.end(); i++) { 404 if (i->ID() == id) { 405 fConsumers.erase(i); 406 break; 407 } 408 } 409 410 BPoint newSize = CalcRowSize(); 411 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 412 (*i)->RemoveColumn(id); 413 (*i)->ResizeTo(newSize.x, newSize.y - 1); 414 } 415 Invalidate(); 416 Window()->EndViewTransaction(); 417 } 418 419 420 void 421 PatchView::UpdateProducerProps(int32 id, const BMessage* props) 422 { 423 for (endpoint_itor i = fProducers.begin(); i != fProducers.end(); i++) { 424 if (i->ID() == id) { 425 i->UpdateProperties(props); 426 Invalidate(); 427 break; 428 } 429 } 430 } 431 432 433 void 434 PatchView::UpdateConsumerProps(int32 id, const BMessage* props) 435 { 436 for (endpoint_itor i = fConsumers.begin(); i != fConsumers.end(); i++) { 437 if (i->ID() == id) { 438 i->UpdateProperties(props); 439 Invalidate(); 440 break; 441 } 442 } 443 } 444 445 446 void 447 PatchView::Connect(int32 prod, int32 cons) 448 { 449 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 450 if ((*i)->ID() == prod) { 451 (*i)->Connect(cons); 452 break; 453 } 454 } 455 } 456 457 458 void 459 PatchView::Disconnect(int32 prod, int32 cons) 460 { 461 for (row_itor i = fPatchRows.begin(); i != fPatchRows.end(); i++) { 462 if ((*i)->ID() == prod) { 463 (*i)->Disconnect(cons); 464 break; 465 } 466 } 467 } 468 469 470 BPoint 471 PatchView::CalcRowOrigin(int32 rowIndex) const 472 { 473 BPoint point; 474 point.x = ROW_LEFT; 475 point.y = ROW_TOP + rowIndex * ROW_HEIGHT; 476 return point; 477 } 478 479 480 BPoint 481 PatchView::CalcRowSize() const 482 { 483 BPoint point; 484 point.x = METER_PADDING + fConsumers.size()*COLUMN_WIDTH; 485 point.y = ROW_HEIGHT - 1; 486 return point; 487 } 488