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