1 // RouteApp.cpp 2 // e.moon 14may99 3 4 #include "RouteApp.h" 5 #include "RouteWindow.h" 6 #include "DormantNodeWindow.h" 7 #include "MediaRoutingView.h" 8 #include "MediaNodePanel.h" 9 10 #include "RouteAppNodeManager.h" 11 #include "NodeRef.h" 12 13 #include "TipManager.h" 14 15 #include "AddOnHost.h" 16 17 #include "route_app_io.h" 18 #include "XML.h" 19 #include "MessageIO.h" 20 #include "NodeSetIOContext.h" 21 22 #include <Debug.h> 23 #include <OS.h> 24 #include <Roster.h> 25 #include <Directory.h> 26 #include <FindDirectory.h> 27 #include <NodeInfo.h> 28 #include <Path.h> 29 #include <Entry.h> 30 31 extern "C" void SetNewLeakChecking(bool); 32 extern "C" void SetMallocLeakChecking(bool); 33 34 __USE_CORTEX_NAMESPACE 35 36 const char* const RouteApp::s_settingsDirectory = "Cortex"; 37 const char* const RouteApp::s_settingsFile = "cortex_settings"; 38 39 const char* const RouteApp::s_appSignature = "application/x-vnd.Cortex.Route"; 40 41 BMimeType RouteApp::s_nodeSetType("text/x-vnd.Cortex.NodeSet"); 42 43 const char* const RouteApp::s_rootElement = "cortex_settings"; 44 const char* const RouteApp::s_mediaRoutingViewElement = "MediaRoutingView"; 45 const char* const RouteApp::s_routeWindowElement = "RouteWindow"; 46 47 // -------------------------------------------------------- // 48 // ctor/dtor 49 // -------------------------------------------------------- // 50 51 RouteApp::~RouteApp() { 52 // PRINT(( 53 // "RouteApp::~RouteApp()\n")); 54 55 ASSERT(manager); 56 thread_id id = manager->Thread(); 57 manager->release(); 58 59 // PRINT(( 60 // "- waiting for manager to die\n")); 61 if(id >= B_OK) { 62 status_t err; 63 while(wait_for_thread(id, &err) == B_INTERRUPTED) { 64 PRINT((" * RouteApp::~RouteApp(): B_INTERRUPTED\n")); 65 } 66 } 67 // PRINT(( 68 // "- RouteApp done.\n")); 69 70 // [e.moon 6nov99] kill off the AddOnHost app, if any 71 AddOnHost::Kill(); 72 73 if(m_settingsDocType) 74 delete m_settingsDocType; 75 76 if(m_nodeSetDocType) 77 delete m_nodeSetDocType; 78 } 79 80 RouteApp::RouteApp() : 81 BApplication(s_appSignature), 82 manager(new RouteAppNodeManager(true)), 83 routeWindow(0), 84 m_settingsDocType(_createSettingsDocType()), 85 m_nodeSetDocType(_createNodeSetDocType()), 86 m_openPanel(B_OPEN_PANEL), 87 m_savePanel(B_SAVE_PANEL) { 88 89 // register MIME type(s) 90 _InitMimeTypes(); 91 92 // create the window hierarchy 93 RouteWindow*& r = const_cast<RouteWindow*&>(routeWindow); 94 r = new RouteWindow(manager); 95 96 // restore settings 97 _readSettings(); 98 99 // fit windows to screen 100 routeWindow->constrainToScreen(); 101 102 // show main window & palettes 103 routeWindow->Show(); 104 } 105 106 bool RouteApp::QuitRequested() { 107 108 // [e.moon 20oct99] make sure the main window is dead before quitting 109 110 // store window positions & other settings 111 app_info ai; 112 status_t err = GetAppInfo(&ai); 113 ASSERT(err == B_OK); 114 115 BFile appFile(&ai.ref, B_READ_WRITE); 116 117 // write settings file 118 _writeSettings(); 119 120 routeWindow->_closePalettes(); 121 routeWindow->Lock(); 122 routeWindow->Quit(); 123 RouteWindow*& r = const_cast<RouteWindow*&>(routeWindow); 124 r = 0; 125 126 // clean up the TipManager [e.moon 19oct99] 127 TipManager::QuitInstance(); 128 129 return true; 130 } 131 132 // -------------------------------------------------------- // 133 // *** BHandler 134 // -------------------------------------------------------- // 135 136 void RouteApp::MessageReceived( 137 BMessage* message) { 138 139 status_t err; 140 141 entry_ref ref; 142 const char* name; 143 144 switch(message->what) { 145 146 case M_SHOW_OPEN_PANEL: 147 m_openPanel.Show(); 148 break; 149 150 case M_SHOW_SAVE_PANEL: 151 m_savePanel.Show(); 152 break; 153 154 case B_SAVE_REQUESTED: { 155 err = message->FindRef("directory", &ref); 156 if(err < B_OK) 157 break; 158 err = message->FindString("name", &name); 159 if(err < B_OK) 160 break; 161 162 _writeSelectedNodeSet(&ref, name); 163 164 m_savePanel.GetPanelDirectory(&ref); 165 BEntry e(&ref); 166 m_lastIODir.SetTo(&e); 167 break; 168 } 169 170 default: 171 _inherited::MessageReceived(message); 172 } 173 } 174 175 // -------------------------------------------------------- // 176 // *** BApplication 177 // -------------------------------------------------------- // 178 179 void RouteApp::RefsReceived( 180 BMessage* message) { 181 182 PRINT(("### RefsReceived\n")); 183 184 status_t err; 185 186 entry_ref ref; 187 188 for(int32 n = 0; ; ++n) { 189 err = message->FindRef("refs", n, &ref); 190 if(err < B_OK) 191 break; 192 193 _readNodeSet(&ref); 194 195 m_openPanel.GetPanelDirectory(&ref); 196 BEntry e(&ref); 197 m_lastIODir.SetTo(&e); 198 } 199 } 200 201 // -------------------------------------------------------- // 202 // *** IPersistent 203 // -------------------------------------------------------- // 204 205 // EXPORT 206 207 void RouteApp::xmlExportBegin( 208 ExportContext& context) const { 209 context.beginElement(s_rootElement); 210 } 211 212 void RouteApp::xmlExportAttributes( 213 ExportContext& context) const {} //nyi: write version info +++++ 214 215 // +++++ 216 void RouteApp::xmlExportContent( 217 ExportContext& context) const { 218 219 status_t err; 220 context.beginContent(); 221 222 // export app settings 223 { 224 BMessage m; 225 exportState(&m); 226 MessageIO io(&m); 227 err = context.writeObject(&io); 228 ASSERT(err == B_OK); 229 } 230 231 if(routeWindow) { 232 // export main routing window (frame/palette) settings 233 context.beginElement(s_routeWindowElement); 234 context.beginContent(); 235 BMessage m; 236 if (routeWindow->Lock()) { 237 routeWindow->exportState(&m); 238 routeWindow->Unlock(); 239 } 240 MessageIO io(&m); 241 context.writeObject(&io); 242 context.endElement(); 243 244 // export routing view (content) settings 245 m.MakeEmpty(); 246 ASSERT(routeWindow->m_routingView); 247 context.beginElement(s_mediaRoutingViewElement); 248 context.beginContent(); 249 routeWindow->m_routingView->exportState(&m); 250 context.writeObject(&io); 251 context.endElement(); 252 } 253 } 254 255 void RouteApp::xmlExportEnd( 256 ExportContext& context) const { 257 context.endElement(); 258 } 259 260 // IMPORT 261 262 void RouteApp::xmlImportBegin( 263 ImportContext& context) { 264 265 m_readState = _READ_ROOT; 266 } 267 268 void RouteApp::xmlImportAttribute( 269 const char* key, 270 const char* value, 271 ImportContext& context) {} //nyi 272 273 void RouteApp::xmlImportContent( 274 const char* data, 275 uint32 length, 276 ImportContext& context) {} //nyi 277 278 void RouteApp::xmlImportChild( 279 IPersistent* child, 280 ImportContext& context) { 281 282 MessageIO* io = dynamic_cast<MessageIO*>(child); 283 if(io) { 284 ASSERT(io->message()); 285 // PRINT(("* RouteApp::xmlImportChild() [flat message]:\n")); 286 // io->message()->PrintToStream(); 287 288 switch(m_readState) { 289 case _READ_ROOT: 290 importState(io->message()); 291 break; 292 293 case _READ_ROUTE_WINDOW: 294 ASSERT(routeWindow); 295 routeWindow->importState(io->message()); 296 break; 297 298 case _READ_MEDIA_ROUTING_VIEW: 299 ASSERT(routeWindow); 300 ASSERT(routeWindow->m_routingView); 301 routeWindow->m_routingView->importState(io->message()); 302 break; 303 304 default: 305 PRINT(("! RouteApp::xmlImportChild(): unimplemented target\n")); 306 break; 307 } 308 } 309 } 310 311 void RouteApp::xmlImportComplete( 312 ImportContext& context) {} //nyi 313 314 void RouteApp::xmlImportChildBegin( 315 const char* name, 316 ImportContext& context) { 317 318 if(m_readState != _READ_ROOT) { 319 context.reportError("RouteApp import: invalid nested element"); 320 return; 321 } 322 323 if(!strcmp(name, s_routeWindowElement)) { 324 m_readState = _READ_ROUTE_WINDOW; 325 } 326 else if(!strcmp(name, s_mediaRoutingViewElement)) { 327 m_readState = _READ_MEDIA_ROUTING_VIEW; 328 } 329 else { 330 context.reportError("RouteApp import: unknown child element"); 331 } 332 } 333 334 void RouteApp::xmlImportChildComplete( 335 const char* name, 336 ImportContext& context) { 337 338 if(m_readState == _READ_ROOT) { 339 context.reportError("RouteApp import: garbled state"); 340 return; 341 } 342 m_readState = _READ_ROOT; 343 } 344 345 // -------------------------------------------------------- // 346 // *** IStateArchivable 347 // -------------------------------------------------------- // 348 349 status_t RouteApp::importState( 350 const BMessage* archive) { 351 352 const char* last; 353 if(archive->FindString("lastDir", &last) == B_OK) { 354 m_lastIODir.SetTo(last); 355 m_openPanel.SetPanelDirectory(last); 356 m_savePanel.SetPanelDirectory(last); 357 } 358 359 return B_OK; 360 } 361 362 status_t RouteApp::exportState( 363 BMessage* archive) const { 364 365 if(m_lastIODir.InitCheck() == B_OK) 366 archive->AddString("lastDir", m_lastIODir.Path()); 367 368 return B_OK; 369 } 370 371 // -------------------------------------------------------- // 372 // implementation 373 // -------------------------------------------------------- // 374 375 XML::DocumentType* RouteApp::_createSettingsDocType() { 376 377 XML::DocumentType* docType = new XML::DocumentType( 378 s_rootElement); 379 MessageIO::AddTo(docType); 380 381 return docType; 382 } 383 384 XML::DocumentType* RouteApp::_createNodeSetDocType() { 385 386 XML::DocumentType* docType = new XML::DocumentType( 387 _NODE_SET_ELEMENT); 388 RouteAppNodeManager::AddTo(docType); 389 390 return docType; 391 } 392 393 status_t RouteApp::_readSettings() { 394 395 // figure path 396 BPath path; 397 status_t err = find_directory( 398 B_USER_SETTINGS_DIRECTORY, 399 &path); 400 ASSERT(err == B_OK); 401 402 path.Append(s_settingsDirectory); 403 BEntry entry(path.Path()); 404 if(!entry.Exists()) 405 return B_ENTRY_NOT_FOUND; 406 407 path.Append(s_settingsFile); 408 entry.SetTo(path.Path()); 409 if(!entry.Exists()) 410 return B_ENTRY_NOT_FOUND; 411 412 // open the settings file 413 BFile file(&entry, B_READ_ONLY); 414 if(file.InitCheck() != B_OK) 415 return file.InitCheck(); 416 417 // read it: 418 list<BString> errors; 419 err = XML::Read( 420 &file, 421 this, 422 m_settingsDocType, 423 &errors); 424 425 if(errors.size()) { 426 fputs("!!! RouteApp::_readSettings():", stderr); 427 for(list<BString>::iterator it = errors.begin(); 428 it != errors.end(); ++it) 429 fputs((*it).String(), stderr); 430 } 431 return err; 432 } 433 434 status_t RouteApp::_writeSettings() { 435 // figure path, creating settings folder if necessary 436 BPath path; 437 status_t err = find_directory( 438 B_USER_SETTINGS_DIRECTORY, 439 &path); 440 ASSERT(err == B_OK); 441 442 BDirectory baseDirectory, settingsDirectory; 443 444 err = baseDirectory.SetTo(path.Path()); 445 if(err < B_OK) 446 return err; 447 448 path.Append(s_settingsDirectory); 449 450 BEntry folderEntry(path.Path()); 451 if(!folderEntry.Exists()) { 452 // create folder 453 err = baseDirectory.CreateDirectory(s_settingsDirectory, &settingsDirectory); 454 ASSERT(err == B_OK); 455 } 456 else 457 settingsDirectory.SetTo(&folderEntry); 458 459 // open/clobber file 460 BFile file( 461 &settingsDirectory, 462 s_settingsFile, 463 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 464 err = file.InitCheck(); 465 if(err < B_OK) 466 return err; 467 468 // write document header 469 const char* header = "<?xml version=\"1.0\"?>\n"; 470 file.Write((const void*)header, strlen(header)); 471 472 // write content 473 BString errorText; 474 err = XML::Write( 475 &file, 476 this, 477 &errorText); 478 479 if(err < B_OK) { 480 fprintf(stderr, 481 "!!! RouteApp::_writeSettings() failed: %s\n", 482 errorText.String()); 483 } 484 485 return err; 486 } 487 488 // -------------------------------------------------------- // 489 490 class _RouteAppImportContext : 491 public ImportContext, 492 public NodeSetIOContext { 493 494 public: 495 _RouteAppImportContext( 496 list<BString>& errors, 497 MediaRoutingView* routingView) : 498 ImportContext(errors), 499 m_routingView(routingView) {} 500 501 public: // *** hooks 502 virtual void importUIState( 503 const BMessage* archive) { 504 505 PRINT(( 506 "### importUIState\n")); 507 508 if(m_routingView) { 509 // m_routingView->LockLooper(); 510 m_routingView->deselectAll(); 511 status_t err = m_routingView->importStateFor( 512 this, 513 archive); 514 if(err < B_OK) { 515 PRINT(( 516 "!!! _RouteAppImportContext::importStateFor() failed:\n" 517 " %s\n", strerror(err))); 518 } 519 m_routingView->Invalidate(); // +++++ not particularly clean 520 // m_routingView->UnlockLooper(); 521 } 522 } 523 524 MediaRoutingView* m_routingView; 525 }; 526 527 status_t RouteApp::_readNodeSet( 528 entry_ref* ref) { 529 530 BFile file(ref, B_READ_ONLY); 531 status_t err = file.InitCheck(); 532 if(err < B_OK) 533 return err; 534 535 routeWindow->Lock(); 536 537 list<BString> errors; 538 539 err = XML::Read( 540 &file, 541 manager, 542 m_nodeSetDocType, 543 new _RouteAppImportContext(errors, routeWindow->m_routingView)); 544 545 routeWindow->Unlock(); 546 547 if(errors.size()) { 548 fputs("!!! RouteApp::_readNodeSet():", stderr); 549 for(list<BString>::iterator it = errors.begin(); 550 it != errors.end(); ++it) 551 fputs((*it).String(), stderr); 552 } 553 return err; 554 } 555 556 // -------------------------------------------------------- // 557 558 class _RouteAppExportContext : 559 public ExportContext, 560 public NodeSetIOContext { 561 562 public: 563 _RouteAppExportContext( 564 MediaRoutingView* routingView) : 565 m_routingView(routingView) {} 566 567 public: // *** hooks 568 virtual void exportUIState( 569 BMessage* archive) { 570 571 PRINT(( 572 "### exportUIState\n")); 573 574 if(m_routingView) { 575 m_routingView->LockLooper(); 576 m_routingView->exportStateFor( 577 this, 578 archive); 579 m_routingView->UnlockLooper(); 580 } 581 } 582 583 MediaRoutingView* m_routingView; 584 }; 585 586 status_t RouteApp::_writeSelectedNodeSet( 587 entry_ref* dirRef, 588 const char* filename) { 589 590 status_t err; 591 592 593 // sanity-check & fetch the selection 594 routeWindow->Lock(); 595 596 MediaRoutingView* v = routeWindow->m_routingView; 597 ASSERT(v); 598 599 if( 600 v->countSelectedItems() < 0 || 601 v->selectedType() != DiagramItem::M_BOX) { 602 PRINT(( 603 "!!! RouteApp::_writeSelectedNodeSet():\n" 604 " Invalid selection!\n")); 605 606 routeWindow->Unlock(); 607 return B_NOT_ALLOWED; 608 } 609 610 _RouteAppExportContext context(v); 611 612 for(uint32 i = 0; i < v->countSelectedItems(); ++i) { 613 MediaNodePanel* panel = dynamic_cast<MediaNodePanel*>(v->selectedItemAt(i)); 614 if(!panel) 615 continue; 616 err = context.addNode(panel->ref->id()); 617 if(err < B_OK) { 618 PRINT(( 619 "!!! context.addNode() failed: '%s\n", strerror(err))); 620 } 621 } 622 routeWindow->Unlock(); 623 624 // open/clobber file 625 BDirectory dir(dirRef); 626 err = dir.InitCheck(); 627 if(err < B_OK) 628 return err; 629 630 BFile file( 631 &dir, 632 filename, 633 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 634 err = file.InitCheck(); 635 if(err < B_OK) 636 return err; 637 638 // write document header 639 const char* header = "<?xml version=\"1.0\"?>\n"; 640 file.Write((const void*)header, strlen(header)); 641 642 // export nodes 643 context.stream = &file; 644 err = context.writeObject(manager); 645 if(err < B_OK) { 646 PRINT(( 647 "!!! RouteApp::_writeSelectedNodeSet(): error:\n" 648 " %s\n", context.errorText())); 649 650 // +++++ delete the malformed file 651 652 } 653 654 655 // write MIME type 656 BNodeInfo* fileInfo = new BNodeInfo(&file); 657 fileInfo->SetType(s_nodeSetType.Type()); 658 fileInfo->SetPreferredApp(s_appSignature); 659 delete fileInfo; 660 661 return B_OK; 662 } 663 664 /*static*/ 665 status_t RouteApp::_InitMimeTypes() { 666 667 status_t err; 668 669 ASSERT(s_nodeSetType.IsValid()); 670 671 if(!s_nodeSetType.IsInstalled()) { 672 err = s_nodeSetType.Install(); 673 if(err < B_OK) { 674 PRINT(( 675 "!!! RouteApp::_InitMimeTypes(): Install():\n" 676 " %s\n", strerror(err))); 677 return err; 678 } 679 680 err = s_nodeSetType.SetPreferredApp(s_appSignature); 681 if(err < B_OK) { 682 PRINT(( 683 "!!! RouteApp::_InitMimeTypes(): SetPreferredApp():\n" 684 " %s\n", strerror(err))); 685 return err; 686 } 687 } 688 689 return B_OK; 690 } 691 692 // -------------------------------------------------------- // 693 // main() stub 694 // -------------------------------------------------------- // 695 696 int main(int argc, char** argv) { 697 // SetNewLeakChecking(true); 698 // SetMallocLeakChecking(true); 699 700 RouteApp app; 701 app.Run(); 702 703 return 0; 704 } 705 706 // END -- RouteApp.cpp -- 707