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