1 // RouteWindow.cpp 2 // e.moon 14may99 3 4 #include "RouteApp.h" 5 #include "RouteWindow.h" 6 #include "MediaRoutingView.h" 7 #include "StatusView.h" 8 9 #include "DormantNodeWindow.h" 10 #include "TransportWindow.h" 11 12 #include "RouteAppNodeManager.h" 13 #include "NodeGroup.h" 14 #include "TipManager.h" 15 16 #include <Alert.h> 17 #include <Autolock.h> 18 #include <Debug.h> 19 #include <Font.h> 20 #include <MenuBar.h> 21 #include <Menu.h> 22 #include <MenuItem.h> 23 #include <Message.h> 24 #include <Messenger.h> 25 #include <Roster.h> 26 #include <Screen.h> 27 #include <ScrollView.h> 28 #include <StringView.h> 29 30 #include <algorithm> 31 32 #include "debug_tools.h" 33 #define D_HOOK(x) //PRINT (x) 34 #define D_INTERNAL(x) //PRINT (x) 35 36 __USE_CORTEX_NAMESPACE 37 38 39 const char* const RouteWindow::s_windowName = "Cortex"; 40 41 const BRect RouteWindow::s_initFrame(100,100,700,550); 42 43 const char* const g_aboutText = 44 "Cortex/Route 2.1.2\n\n" 45 "Copyright 1999-2000 Eric Moon\n" 46 "All rights reserved.\n\n" 47 "The Cortex Team:\n\n" 48 "Christopher Lenz: UI\n" 49 "Eric Moon: UI, back-end\n\n" 50 "Thanks to:\nJohn Ashmun\nJon Watte\nDoug Wright\n<your name here>\n\n" 51 "Certain icons used herein are the property of\n" 52 "Be, Inc. and are used by permission."; 53 54 55 RouteWindow::~RouteWindow() 56 { 57 } 58 59 60 RouteWindow::RouteWindow(RouteAppNodeManager* manager) 61 : 62 BWindow(s_initFrame, s_windowName, B_DOCUMENT_WINDOW, 0), 63 m_hScrollBar(0), 64 m_vScrollBar(0), 65 m_transportWindow(0), 66 m_dormantNodeWindow(0), 67 m_selectedGroupID(0), 68 m_zoomed(false), 69 m_zooming(false) 70 { 71 BRect b = Bounds(); 72 73 // initialize the menu bar: add all menus that target this window 74 BMenuBar* pMenuBar = new BMenuBar(b, "menuBar"); 75 BMenu* pFileMenu = new BMenu("File"); 76 BMenuItem* item = new BMenuItem("Open" B_UTF8_ELLIPSIS, 77 new BMessage(RouteApp::M_SHOW_OPEN_PANEL), 'O'); 78 item->SetTarget(be_app); 79 pFileMenu->AddItem(item); 80 pFileMenu->AddItem(new BSeparatorItem()); 81 item = new BMenuItem("Save Nodes" B_UTF8_ELLIPSIS, 82 new BMessage(RouteApp::M_SHOW_SAVE_PANEL), 'S'); 83 item->SetTarget(be_app); 84 pFileMenu->AddItem(item); 85 pFileMenu->AddItem(new BSeparatorItem()); 86 pFileMenu->AddItem(new BMenuItem("About Cortex/Route" B_UTF8_ELLIPSIS, 87 new BMessage(B_ABOUT_REQUESTED))); 88 pFileMenu->AddItem(new BSeparatorItem()); 89 pFileMenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED))); 90 pMenuBar->AddItem(pFileMenu); 91 AddChild(pMenuBar); 92 93 // build the routing view 94 BRect rvBounds = b; 95 rvBounds.top = pMenuBar->Frame().bottom+1; 96 rvBounds.right -= B_V_SCROLL_BAR_WIDTH; 97 rvBounds.bottom -= B_H_SCROLL_BAR_HEIGHT; 98 m_routingView = new MediaRoutingView(manager, rvBounds, "routingView"); 99 100 BRect hsBounds = rvBounds; 101 hsBounds.left = rvBounds.left + 199; 102 hsBounds.top = hsBounds.bottom + 1; 103 hsBounds.right++; 104 hsBounds.bottom = b.bottom + 1; 105 106 m_hScrollBar = new BScrollBar(hsBounds, "hScrollBar", m_routingView, 107 0, 0, B_HORIZONTAL); 108 AddChild(m_hScrollBar); 109 110 BRect vsBounds = rvBounds; 111 vsBounds.left = vsBounds.right + 1; 112 vsBounds.top--; 113 vsBounds.right = b.right + 1; 114 vsBounds.bottom++; 115 116 m_vScrollBar = new BScrollBar(vsBounds, "vScrollBar", m_routingView, 117 0, 0, B_VERTICAL); 118 AddChild(m_vScrollBar); 119 120 BRect svBounds = rvBounds; 121 svBounds.left -= 1; 122 svBounds.right = hsBounds.left - 1; 123 svBounds.top = svBounds.bottom + 1; 124 svBounds.bottom = b.bottom + 1; 125 126 m_statusView = new StatusView(svBounds, manager, m_hScrollBar); 127 AddChild(m_statusView); 128 129 AddChild(m_routingView); 130 131 float minWidth, maxWidth, minHeight, maxHeight; 132 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 133 minWidth = m_statusView->Frame().Width() + 6 * B_V_SCROLL_BAR_WIDTH; 134 minHeight = 6 * B_H_SCROLL_BAR_HEIGHT; 135 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 136 137 // construct the Window menu 138 BMenu* windowMenu = new BMenu("Window"); 139 m_transportWindowItem = new BMenuItem( 140 "Show Transport", 141 new BMessage(M_TOGGLE_TRANSPORT_WINDOW)); 142 windowMenu->AddItem(m_transportWindowItem); 143 144 m_dormantNodeWindowItem = new BMenuItem( 145 "Show Add-Ons", 146 new BMessage(M_TOGGLE_DORMANT_NODE_WINDOW)); 147 windowMenu->AddItem(m_dormantNodeWindowItem); 148 149 windowMenu->AddItem(new BSeparatorItem()); 150 151 m_pullPalettesItem = new BMenuItem( 152 "Pull Palettes", 153 new BMessage(M_TOGGLE_PULLING_PALETTES)); 154 windowMenu->AddItem(m_pullPalettesItem); 155 156 pMenuBar->AddItem(windowMenu); 157 158 // create the dormant-nodes palette 159 _toggleDormantNodeWindow(); 160 161 // display group inspector 162 _toggleTransportWindow(); 163 } 164 165 166 // #pragma mark - operations 167 168 169 /*! Enable/disable palette position-locking (when the main 170 window is moved, all palettes follow). 171 */ 172 bool 173 RouteWindow::isPullPalettes() const 174 { 175 return m_pullPalettesItem->IsMarked(); 176 } 177 178 179 void 180 RouteWindow::setPullPalettes(bool enabled) 181 { 182 m_pullPalettesItem->SetMarked(enabled); 183 } 184 185 186 void 187 RouteWindow::constrainToScreen() 188 { 189 BScreen screen(this); 190 191 const BRect sr = screen.Frame(); 192 193 // [c.lenz 1mar2000] this should be handled by every window 194 // itself. will probably change soon ;-) 195 _constrainToScreen(); 196 /* // main window 197 BRect r = Frame(); 198 BPoint offset(0.0, 0.0); 199 if(r.left < 0.0) 200 offset.x = -r.left; 201 if(r.top < 0.0) 202 offset.y = -r.top; 203 if(r.left >= (sr.right - 20.0)) 204 offset.x -= (r.left - (sr.Width()/2)); 205 if(r.top >= (sr.bottom - 20.0)) 206 offset.y -= (r.top - (sr.Height()/2)); 207 if(offset.x != 0.0 || offset.y != 0.0) { 208 setPullPalettes(false); 209 MoveBy(offset.x, offset.y); 210 }*/ 211 212 // transport window 213 BPoint offset = BPoint(0.0, 0.0); 214 BRect r = (m_transportWindow) ? 215 m_transportWindow->Frame() : 216 m_transportWindowFrame; 217 if(r.left < 0.0) 218 offset.x = (sr.Width()*.75) - r.left; 219 if(r.top < 0.0) 220 offset.y = (sr.Height()*.25) - r.top; 221 if(r.left >= (sr.right - 20.0)) 222 offset.x -= (r.left - (sr.Width()/2)); 223 if(r.top >= (sr.bottom - 20.0)) 224 offset.y -= (r.top - (sr.Height()/2)); 225 226 if(offset.x != 0.0 || offset.y != 0.0) { 227 if(m_transportWindow) 228 m_transportWindow->MoveBy(offset.x, offset.y); 229 else 230 m_transportWindowFrame.OffsetBy(offset.x, offset.y); 231 } 232 233 // addon palette 234 offset = BPoint(0.0, 0.0); 235 r = (m_dormantNodeWindow) ? 236 m_dormantNodeWindow->Frame() : 237 m_dormantNodeWindowFrame; 238 if(r.left < 0.0) 239 offset.x = (sr.Width()*.25) - r.left; 240 if(r.top < 0.0) 241 offset.y = (sr.Height()*.125) - r.top; 242 if(r.left >= (sr.right - 20.0)) 243 offset.x -= (r.left - (sr.Width()/2)); 244 if(r.top >= (sr.bottom - 20.0)) 245 offset.y -= (r.top - (sr.Height()/2)); 246 247 if(offset.x != 0.0 || offset.y != 0.0) { 248 if(m_dormantNodeWindow) 249 m_dormantNodeWindow->MoveBy(offset.x, offset.y); 250 else 251 m_dormantNodeWindowFrame.OffsetBy(offset.x, offset.y); 252 } 253 254 } 255 256 257 // #pragma mark - BWindow implementation 258 259 260 void 261 RouteWindow::FrameMoved(BPoint point) 262 { 263 // ignore notification if the window isn't yet visible 264 if(IsHidden()) 265 return; 266 267 BPoint delta = point - m_lastFramePosition; 268 m_lastFramePosition = point; 269 270 271 if (m_pullPalettesItem->IsMarked()) 272 _movePalettesBy(delta.x, delta.y); 273 } 274 275 276 void 277 RouteWindow::FrameResized(float width, float height) 278 { 279 D_HOOK(("RouteWindow::FrameResized()\n")); 280 281 if (!m_zooming) { 282 m_zoomed = false; 283 } 284 else { 285 m_zooming = false; 286 } 287 } 288 289 290 bool 291 RouteWindow::QuitRequested() 292 { 293 be_app->PostMessage(B_QUIT_REQUESTED); 294 return false; // [e.moon 20oct99] app now quits window 295 } 296 297 298 void 299 RouteWindow::Zoom(BPoint origin, float width, float height) 300 { 301 D_HOOK(("RouteWindow::Zoom()\n")); 302 303 m_zooming = true; 304 305 BScreen screen(this); 306 if (!screen.Frame().Contains(Frame())) { 307 m_zoomed = false; 308 } 309 310 if (!m_zoomed) { 311 // resize to the ideal size 312 m_manualSize = Bounds(); 313 float width, height; 314 m_routingView->GetPreferredSize(&width, &height); 315 width += B_V_SCROLL_BAR_WIDTH; 316 height += B_H_SCROLL_BAR_HEIGHT; 317 if (KeyMenuBar()) { 318 height += KeyMenuBar()->Frame().Height(); 319 } 320 ResizeTo(width, height); 321 _constrainToScreen(); 322 m_zoomed = true; 323 } 324 else { 325 // resize to the most recent manual size 326 ResizeTo(m_manualSize.Width(), m_manualSize.Height()); 327 m_zoomed = false; 328 } 329 } 330 331 332 // #pragma mark - BHandler implemenation 333 334 335 void 336 RouteWindow::MessageReceived(BMessage* pMsg) 337 { 338 // PRINT(( 339 // "RouteWindow::MessageReceived()\n")); 340 // pMsg->PrintToStream(); 341 // 342 switch (pMsg->what) { 343 case B_ABOUT_REQUESTED: 344 (new BAlert("About", g_aboutText, "Ok"))->Go(); 345 break; 346 347 case MediaRoutingView::M_GROUP_SELECTED: 348 _handleGroupSelected(pMsg); 349 break; 350 351 case MediaRoutingView::M_SHOW_ERROR_MESSAGE: 352 _handleShowErrorMessage(pMsg); 353 break; 354 355 case M_TOGGLE_TRANSPORT_WINDOW: 356 _toggleTransportWindow(); 357 break; 358 359 case M_REFRESH_TRANSPORT_SETTINGS: 360 _refreshTransportSettings(pMsg); 361 break; 362 363 case M_TOGGLE_PULLING_PALETTES: 364 _togglePullPalettes(); 365 break; 366 367 case M_TOGGLE_DORMANT_NODE_WINDOW: 368 _toggleDormantNodeWindow(); 369 break; 370 371 case M_TOGGLE_GROUP_ROLLING: 372 _toggleGroupRolling(); 373 break; 374 375 default: 376 _inherited::MessageReceived(pMsg); 377 break; 378 } 379 } 380 381 382 // #pragma mark - IStateArchivable 383 384 385 status_t 386 RouteWindow::importState(const BMessage* archive) 387 { 388 status_t err; 389 390 // frame rect 391 BRect r; 392 err = archive->FindRect("frame", &r); 393 if(err == B_OK) { 394 MoveTo(r.LeftTop()); 395 ResizeTo(r.Width(), r.Height()); 396 m_lastFramePosition = r.LeftTop(); 397 } 398 399 // status view width 400 int32 i; 401 err = archive->FindInt32("statusViewWidth", &i); 402 if (err == B_OK) { 403 float diff = i - m_statusView->Bounds().IntegerWidth(); 404 m_statusView->ResizeBy(diff, 0.0); 405 m_hScrollBar->ResizeBy(-diff, 0.0); 406 m_hScrollBar->MoveBy(diff, 0.0); 407 } 408 409 // settings 410 bool b; 411 err = archive->FindBool("pullPalettes", &b); 412 if(err == B_OK) 413 m_pullPalettesItem->SetMarked(b); 414 415 // const char* p; 416 // err = archive->FindString("saveDir", &p); 417 // if(err == B_OK) { 418 // m_openPanel.SetPanelDirectory(p); 419 // m_savePanel.SetPanelDirectory(p); 420 // } 421 // 422 // dormant-node window 423 err = archive->FindRect("addonPaletteFrame", &r); 424 if (err == B_OK) 425 m_dormantNodeWindowFrame = r; 426 err = archive->FindBool("addonPaletteVisible", &b); 427 if (err == B_OK && (b != (m_dormantNodeWindow != 0))) { 428 _toggleDormantNodeWindow(); 429 if(!m_dormantNodeWindow) 430 m_dormantNodeWindowFrame = r; 431 } 432 433 if (m_dormantNodeWindow) { 434 m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop()); 435 m_dormantNodeWindow->ResizeTo( 436 m_dormantNodeWindowFrame.Width(), 437 m_dormantNodeWindowFrame.Height()); 438 } 439 440 // transport window 441 err = archive->FindRect("transportFrame", &r); 442 if (err == B_OK) 443 m_transportWindowFrame = r; 444 err = archive->FindBool("transportVisible", &b); 445 if (err == B_OK && (b != (m_transportWindow != 0))) { 446 _toggleTransportWindow(); 447 if (!m_transportWindow) 448 m_transportWindowFrame = r; 449 } 450 451 if (m_transportWindow) { 452 m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop()); 453 m_transportWindow->ResizeTo( 454 m_transportWindowFrame.Width(), 455 m_transportWindowFrame.Height()); 456 } 457 458 return B_OK; 459 } 460 461 462 status_t 463 RouteWindow::exportState(BMessage* archive) const 464 { 465 BRect r = Frame(); 466 archive->AddRect("frame", r); 467 archive->AddBool("pullPalettes", m_pullPalettesItem->IsMarked()); 468 469 bool b = (m_dormantNodeWindow != 0); 470 r = b ? m_dormantNodeWindow->Frame() : m_dormantNodeWindowFrame; 471 archive->AddRect("addonPaletteFrame", r); 472 archive->AddBool("addonPaletteVisible", b); 473 474 b = (m_transportWindow != 0); 475 r = b ? m_transportWindow->Frame() : m_transportWindowFrame; 476 477 archive->AddRect("transportFrame", r); 478 archive->AddBool("transportVisible", b); 479 480 // [c.lenz 23may00] remember status view width 481 int i = m_statusView->Bounds().IntegerWidth(); 482 archive->AddInt32("statusViewWidth", i); 483 484 // entry_ref saveRef; 485 // m_savePanel.GetPanelDirectory(&saveRef); 486 // BEntry saveEntry(&saveRef); 487 // if(saveEntry.InitCheck() == B_OK) { 488 // BPath p; 489 // saveEntry.GetPath(&p); 490 // archive->AddString("saveDir", p.Path()); 491 // } 492 493 return B_OK; 494 } 495 496 497 // #pragma mark - implementation 498 499 500 void 501 RouteWindow::_constrainToScreen() 502 { 503 D_INTERNAL(("RouteWindow::_constrainToScreen()\n")); 504 505 BScreen screen(this); 506 BRect screenRect = screen.Frame(); 507 BRect windowRect = Frame(); 508 509 // if the window is outside the screen rect 510 // move it to the default position 511 if (!screenRect.Intersects(windowRect)) { 512 windowRect.OffsetTo(screenRect.LeftTop()); 513 MoveTo(windowRect.LeftTop()); 514 windowRect = Frame(); 515 } 516 517 // if the window is larger than the screen rect 518 // resize it to fit at each side 519 if (!screenRect.Contains(windowRect)) { 520 if (windowRect.left < screenRect.left) { 521 windowRect.left = screenRect.left + 5.0; 522 MoveTo(windowRect.LeftTop()); 523 windowRect = Frame(); 524 } 525 if (windowRect.top < screenRect.top) { 526 windowRect.top = screenRect.top + 5.0; 527 MoveTo(windowRect.LeftTop()); 528 windowRect = Frame(); 529 } 530 if (windowRect.right > screenRect.right) { 531 windowRect.right = screenRect.right - 5.0; 532 } 533 if (windowRect.bottom > screenRect.bottom) { 534 windowRect.bottom = screenRect.bottom - 5.0; 535 } 536 ResizeTo(windowRect.Width(), windowRect.Height()); 537 } 538 } 539 540 541 void 542 RouteWindow::_toggleTransportWindow() 543 { 544 if (m_transportWindow) { 545 m_transportWindowFrame = m_transportWindow->Frame(); 546 m_transportWindow->Lock(); 547 m_transportWindow->Quit(); 548 m_transportWindow = 0; 549 m_transportWindowItem->SetMarked(false); 550 } else { 551 m_transportWindow = new TransportWindow(m_routingView->manager, 552 this, "Transport"); 553 554 // ask for a selection update 555 BMessenger(m_routingView).SendMessage( 556 MediaRoutingView::M_BROADCAST_SELECTION); 557 558 // place & display the window 559 if (m_transportWindowFrame.IsValid()) { 560 m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop()); 561 m_transportWindow->ResizeTo(m_transportWindowFrame.Width(), 562 m_transportWindowFrame.Height()); 563 } 564 565 m_transportWindow->Show(); 566 m_transportWindowItem->SetMarked(true); 567 } 568 } 569 570 571 void 572 RouteWindow::_togglePullPalettes() 573 { 574 m_pullPalettesItem->SetMarked(!m_pullPalettesItem->IsMarked()); 575 } 576 577 578 void 579 RouteWindow::_toggleDormantNodeWindow() 580 { 581 if (m_dormantNodeWindow) { 582 m_dormantNodeWindowFrame = m_dormantNodeWindow->Frame(); 583 m_dormantNodeWindow->Lock(); 584 m_dormantNodeWindow->Quit(); 585 m_dormantNodeWindow = 0; 586 m_dormantNodeWindowItem->SetMarked(false); 587 } else { 588 m_dormantNodeWindow = new DormantNodeWindow(this); 589 if (m_dormantNodeWindowFrame.IsValid()) { 590 m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop()); 591 m_dormantNodeWindow->ResizeTo(m_dormantNodeWindowFrame.Width(), 592 m_dormantNodeWindowFrame.Height()); 593 } 594 m_dormantNodeWindow->Show(); 595 m_dormantNodeWindowItem->SetMarked(true); 596 } 597 } 598 599 600 void 601 RouteWindow::_handleGroupSelected(BMessage* message) 602 { 603 status_t err; 604 uint32 groupID; 605 606 err = message->FindInt32("groupID", (int32*)&groupID); 607 if (err < B_OK) { 608 PRINT(( 609 "! RouteWindow::_handleGroupSelected(): no groupID in message!\n")); 610 return; 611 } 612 613 if (!m_transportWindow) 614 return; 615 616 BMessage m(TransportWindow::M_SELECT_GROUP); 617 m.AddInt32("groupID", groupID); 618 BMessenger(m_transportWindow).SendMessage(&m); 619 620 m_selectedGroupID = groupID; 621 } 622 623 624 void 625 RouteWindow::_handleShowErrorMessage(BMessage* message) 626 { 627 status_t err; 628 BString text; 629 630 err = message->FindString("text", &text); 631 if (err < B_OK) { 632 PRINT(( 633 "! RouteWindow::_handleShowErrorMessage(): no text in message!\n")); 634 return; 635 } 636 637 m_statusView->setErrorMessage(text.String(), message->HasBool("error")); 638 } 639 640 641 //! Refresh the transport window for the given group, if any 642 void 643 RouteWindow::_refreshTransportSettings(BMessage* message) 644 { 645 status_t err; 646 uint32 groupID; 647 648 err = message->FindInt32("groupID", (int32*)&groupID); 649 if (err < B_OK) { 650 PRINT(( 651 "! RouteWindow::_refreshTransportSettings(): no groupID in message!\n")); 652 return; 653 } 654 655 if(m_transportWindow) { 656 // relay the message 657 BMessenger(m_transportWindow).SendMessage(message); 658 } 659 } 660 661 662 void 663 RouteWindow::_closePalettes() 664 { 665 BAutolock _l(this); 666 667 if (m_transportWindow) { 668 m_transportWindow->Lock(); 669 m_transportWindow->Quit(); 670 m_transportWindow = 0; 671 } 672 } 673 674 675 //! Move all palette windows by the specified amounts 676 void RouteWindow::_movePalettesBy(float xDelta, float yDelta) 677 { 678 if (m_transportWindow) 679 m_transportWindow->MoveBy(xDelta, yDelta); 680 681 if (m_dormantNodeWindow) 682 m_dormantNodeWindow->MoveBy(xDelta, yDelta); 683 } 684 685 686 //! Toggle group playback 687 void 688 RouteWindow::_toggleGroupRolling() 689 { 690 if (!m_selectedGroupID) 691 return; 692 693 NodeGroup* g; 694 status_t err = m_routingView->manager->findGroup(m_selectedGroupID, &g); 695 if (err < B_OK) 696 return; 697 698 Autolock _l(g); 699 uint32 startAction = (g->runMode() == BMediaNode::B_OFFLINE) 700 ? NodeGroup::M_ROLL : NodeGroup::M_START; 701 702 BMessenger m(g); 703 switch (g->transportState()) { 704 case NodeGroup::TRANSPORT_STOPPED: 705 m.SendMessage(startAction); 706 break; 707 708 case NodeGroup::TRANSPORT_RUNNING: 709 case NodeGroup::TRANSPORT_ROLLING: 710 m.SendMessage(NodeGroup::M_STOP); 711 break; 712 713 default: 714 break; 715 } 716 } 717