/* * Copyright (c) 1999-2000, Eric Moon. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions, and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // RouteApp.cpp // e.moon 14may99 #include "RouteApp.h" #include "RouteWindow.h" #include "DormantNodeWindow.h" #include "MediaRoutingView.h" #include "MediaNodePanel.h" #include "RouteAppNodeManager.h" #include "NodeRef.h" #include "TipManager.h" #include "AddOnHost.h" #include "route_app_io.h" #include "XML.h" #include "MessageIO.h" #include "NodeSetIOContext.h" #include #include #include #include #include #include #include #include extern "C" void SetNewLeakChecking(bool); extern "C" void SetMallocLeakChecking(bool); using namespace std; __USE_CORTEX_NAMESPACE const char* const RouteApp::s_settingsDirectory = "Cortex"; const char* const RouteApp::s_settingsFile = "cortex_settings"; const char* const RouteApp::s_appSignature = "application/x-vnd.Cortex.Route"; BMimeType RouteApp::s_nodeSetType("text/x-vnd.Cortex.NodeSet"); const char* const RouteApp::s_rootElement = "cortex_settings"; const char* const RouteApp::s_mediaRoutingViewElement = "MediaRoutingView"; const char* const RouteApp::s_routeWindowElement = "RouteWindow"; // -------------------------------------------------------- // // ctor/dtor // -------------------------------------------------------- // RouteApp::~RouteApp() { // PRINT(( // "RouteApp::~RouteApp()\n")); ASSERT(manager); thread_id id = manager->Thread(); manager->release(); // PRINT(( // "- waiting for manager to die\n")); if(id >= B_OK) { status_t err; while(wait_for_thread(id, &err) == B_INTERRUPTED) { PRINT((" * RouteApp::~RouteApp(): B_INTERRUPTED\n")); } } // PRINT(( // "- RouteApp done.\n")); // [e.moon 6nov99] kill off the AddOnHost app, if any AddOnHost::Kill(); if(m_settingsDocType) delete m_settingsDocType; if(m_nodeSetDocType) delete m_nodeSetDocType; } RouteApp::RouteApp() : BApplication(s_appSignature), manager(new RouteAppNodeManager(true)), routeWindow(0), m_settingsDocType(_createSettingsDocType()), m_nodeSetDocType(_createNodeSetDocType()), m_openPanel(B_OPEN_PANEL), m_savePanel(B_SAVE_PANEL) { // register MIME type(s) _InitMimeTypes(); // create the window hierarchy RouteWindow*& r = const_cast(routeWindow); r = new RouteWindow(manager); // restore settings _readSettings(); // fit windows to screen routeWindow->constrainToScreen(); // show main window & palettes routeWindow->Show(); } bool RouteApp::QuitRequested() { // [e.moon 20oct99] make sure the main window is dead before quitting // store window positions & other settings // write settings file _writeSettings(); routeWindow->_closePalettes(); routeWindow->Lock(); routeWindow->Quit(); RouteWindow*& r = const_cast(routeWindow); r = 0; // clean up the TipManager [e.moon 19oct99] TipManager::QuitInstance(); return true; } // -------------------------------------------------------- // // *** BHandler // -------------------------------------------------------- // void RouteApp::MessageReceived( BMessage* message) { status_t err; entry_ref ref; const char* name; switch(message->what) { case M_SHOW_OPEN_PANEL: m_openPanel.Show(); break; case M_SHOW_SAVE_PANEL: m_savePanel.Show(); break; case B_SAVE_REQUESTED: { err = message->FindRef("directory", &ref); if(err < B_OK) break; err = message->FindString("name", &name); if(err < B_OK) break; _writeSelectedNodeSet(&ref, name); m_savePanel.GetPanelDirectory(&ref); BEntry e(&ref); m_lastIODir.SetTo(&e); break; } default: _inherited::MessageReceived(message); } } // -------------------------------------------------------- // // *** BApplication // -------------------------------------------------------- // void RouteApp::RefsReceived( BMessage* message) { PRINT(("### RefsReceived\n")); status_t err; entry_ref ref; for(int32 n = 0; ; ++n) { err = message->FindRef("refs", n, &ref); if(err < B_OK) break; _readNodeSet(&ref); m_openPanel.GetPanelDirectory(&ref); BEntry e(&ref); m_lastIODir.SetTo(&e); } } // -------------------------------------------------------- // // *** IPersistent // -------------------------------------------------------- // // EXPORT void RouteApp::xmlExportBegin( ExportContext& context) const { context.beginElement(s_rootElement); } void RouteApp::xmlExportAttributes( ExportContext& context) const {} //nyi: write version info +++++ // +++++ void RouteApp::xmlExportContent( ExportContext& context) const { context.beginContent(); // export app settings { BMessage m; exportState(&m); MessageIO io(&m); status_t err __attribute__((unused)) = context.writeObject(&io); ASSERT(err == B_OK); } if(routeWindow) { // export main routing window (frame/palette) settings context.beginElement(s_routeWindowElement); context.beginContent(); BMessage m; if (routeWindow->Lock()) { routeWindow->exportState(&m); routeWindow->Unlock(); } MessageIO io(&m); context.writeObject(&io); context.endElement(); // export routing view (content) settings m.MakeEmpty(); ASSERT(routeWindow->m_routingView); context.beginElement(s_mediaRoutingViewElement); context.beginContent(); routeWindow->m_routingView->exportState(&m); context.writeObject(&io); context.endElement(); } } void RouteApp::xmlExportEnd( ExportContext& context) const { context.endElement(); } // IMPORT void RouteApp::xmlImportBegin( ImportContext& context) { m_readState = _READ_ROOT; } void RouteApp::xmlImportAttribute( const char* key, const char* value, ImportContext& context) {} //nyi void RouteApp::xmlImportContent( const char* data, uint32 length, ImportContext& context) {} //nyi void RouteApp::xmlImportChild( IPersistent* child, ImportContext& context) { MessageIO* io = dynamic_cast(child); if(io) { ASSERT(io->message()); // PRINT(("* RouteApp::xmlImportChild() [flat message]:\n")); // io->message()->PrintToStream(); switch(m_readState) { case _READ_ROOT: importState(io->message()); break; case _READ_ROUTE_WINDOW: ASSERT(routeWindow); routeWindow->importState(io->message()); break; case _READ_MEDIA_ROUTING_VIEW: ASSERT(routeWindow); ASSERT(routeWindow->m_routingView); routeWindow->m_routingView->importState(io->message()); break; default: PRINT(("! RouteApp::xmlImportChild(): unimplemented target\n")); break; } } } void RouteApp::xmlImportComplete( ImportContext& context) {} //nyi void RouteApp::xmlImportChildBegin( const char* name, ImportContext& context) { if(m_readState != _READ_ROOT) { context.reportError("RouteApp import: invalid nested element"); return; } if(!strcmp(name, s_routeWindowElement)) { m_readState = _READ_ROUTE_WINDOW; } else if(!strcmp(name, s_mediaRoutingViewElement)) { m_readState = _READ_MEDIA_ROUTING_VIEW; } else { context.reportError("RouteApp import: unknown child element"); } } void RouteApp::xmlImportChildComplete( const char* name, ImportContext& context) { if(m_readState == _READ_ROOT) { context.reportError("RouteApp import: garbled state"); return; } m_readState = _READ_ROOT; } // -------------------------------------------------------- // // *** IStateArchivable // -------------------------------------------------------- // status_t RouteApp::importState( const BMessage* archive) { const char* last; if(archive->FindString("lastDir", &last) == B_OK) { m_lastIODir.SetTo(last); m_openPanel.SetPanelDirectory(last); m_savePanel.SetPanelDirectory(last); } return B_OK; } status_t RouteApp::exportState( BMessage* archive) const { if(m_lastIODir.InitCheck() == B_OK) archive->AddString("lastDir", m_lastIODir.Path()); return B_OK; } // -------------------------------------------------------- // // implementation // -------------------------------------------------------- // XML::DocumentType* RouteApp::_createSettingsDocType() { XML::DocumentType* docType = new XML::DocumentType( s_rootElement); MessageIO::AddTo(docType); return docType; } XML::DocumentType* RouteApp::_createNodeSetDocType() { XML::DocumentType* docType = new XML::DocumentType( _NODE_SET_ELEMENT); RouteAppNodeManager::AddTo(docType); return docType; } status_t RouteApp::_readSettings() { // figure path BPath path; status_t err = find_directory( B_USER_SETTINGS_DIRECTORY, &path); ASSERT(err == B_OK); path.Append(s_settingsDirectory); BEntry entry(path.Path()); if(!entry.Exists()) return B_ENTRY_NOT_FOUND; path.Append(s_settingsFile); entry.SetTo(path.Path()); if(!entry.Exists()) return B_ENTRY_NOT_FOUND; // open the settings file BFile file(&entry, B_READ_ONLY); if(file.InitCheck() != B_OK) return file.InitCheck(); // read it: list errors; err = XML::Read( &file, this, m_settingsDocType, &errors); if(errors.size()) { fputs("!!! RouteApp::_readSettings():", stderr); for(list::iterator it = errors.begin(); it != errors.end(); ++it) fputs((*it).String(), stderr); } return err; } status_t RouteApp::_writeSettings() { // figure path, creating settings folder if necessary BPath path; status_t err = find_directory( B_USER_SETTINGS_DIRECTORY, &path); ASSERT(err == B_OK); BDirectory baseDirectory, settingsDirectory; err = baseDirectory.SetTo(path.Path()); if(err < B_OK) return err; path.Append(s_settingsDirectory); BEntry folderEntry(path.Path()); if(!folderEntry.Exists()) { // create folder err = baseDirectory.CreateDirectory(s_settingsDirectory, &settingsDirectory); ASSERT(err == B_OK); } else settingsDirectory.SetTo(&folderEntry); // open/clobber file BFile file( &settingsDirectory, s_settingsFile, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); err = file.InitCheck(); if(err < B_OK) return err; // write document header const char* header = "\n"; file.Write((const void*)header, strlen(header)); // write content BString errorText; err = XML::Write( &file, this, &errorText); if(err < B_OK) { fprintf(stderr, "!!! RouteApp::_writeSettings() failed: %s\n", errorText.String()); } return err; } // -------------------------------------------------------- // class _RouteAppImportContext : public ImportContext, public NodeSetIOContext { public: _RouteAppImportContext( list& errors, MediaRoutingView* routingView) : ImportContext(errors), m_routingView(routingView) {} public: // *** hooks virtual void importUIState( const BMessage* archive) { PRINT(( "### importUIState\n")); if(m_routingView) { // m_routingView->LockLooper(); m_routingView->DeselectAll(); status_t err = m_routingView->importStateFor( this, archive); if(err < B_OK) { PRINT(( "!!! _RouteAppImportContext::importStateFor() failed:\n" " %s\n", strerror(err))); } m_routingView->Invalidate(); // +++++ not particularly clean // m_routingView->UnlockLooper(); } } MediaRoutingView* m_routingView; }; status_t RouteApp::_readNodeSet( entry_ref* ref) { BFile file(ref, B_READ_ONLY); status_t err = file.InitCheck(); if(err < B_OK) return err; routeWindow->Lock(); list errors; err = XML::Read( &file, manager, m_nodeSetDocType, new _RouteAppImportContext(errors, routeWindow->m_routingView)); routeWindow->Unlock(); if(errors.size()) { fputs("!!! RouteApp::_readNodeSet():", stderr); for(list::iterator it = errors.begin(); it != errors.end(); ++it) fputs((*it).String(), stderr); } return err; } // -------------------------------------------------------- // class _RouteAppExportContext : public ExportContext, public NodeSetIOContext { public: _RouteAppExportContext( MediaRoutingView* routingView) : m_routingView(routingView) {} public: // *** hooks virtual void exportUIState( BMessage* archive) { PRINT(( "### exportUIState\n")); if(m_routingView) { m_routingView->LockLooper(); m_routingView->exportStateFor( this, archive); m_routingView->UnlockLooper(); } } MediaRoutingView* m_routingView; }; status_t RouteApp::_writeSelectedNodeSet( entry_ref* dirRef, const char* filename) { status_t err; // sanity-check & fetch the selection routeWindow->Lock(); MediaRoutingView* v = routeWindow->m_routingView; ASSERT(v); if( v->CountSelectedItems() < 0 || v->SelectedType() != DiagramItem::M_BOX) { PRINT(( "!!! RouteApp::_writeSelectedNodeSet():\n" " Invalid selection!\n")); routeWindow->Unlock(); return B_NOT_ALLOWED; } _RouteAppExportContext context(v); for(uint32 i = 0; i < v->CountSelectedItems(); ++i) { MediaNodePanel* panel = dynamic_cast(v->SelectedItemAt(i)); if(!panel) continue; err = context.addNode(panel->ref->id()); if(err < B_OK) { PRINT(( "!!! context.addNode() failed: '%s\n", strerror(err))); } } routeWindow->Unlock(); // open/clobber file BDirectory dir(dirRef); err = dir.InitCheck(); if(err < B_OK) return err; BFile file( &dir, filename, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); err = file.InitCheck(); if(err < B_OK) return err; // write document header const char* header = "\n"; file.Write((const void*)header, strlen(header)); // export nodes context.stream = &file; err = context.writeObject(manager); if(err < B_OK) { PRINT(( "!!! RouteApp::_writeSelectedNodeSet(): error:\n" " %s\n", context.errorText())); // +++++ delete the malformed file } // write MIME type BNodeInfo* fileInfo = new BNodeInfo(&file); fileInfo->SetType(s_nodeSetType.Type()); fileInfo->SetPreferredApp(s_appSignature); delete fileInfo; return B_OK; } /*static*/ status_t RouteApp::_InitMimeTypes() { status_t err; ASSERT(s_nodeSetType.IsValid()); if(!s_nodeSetType.IsInstalled()) { err = s_nodeSetType.Install(); if(err < B_OK) { PRINT(( "!!! RouteApp::_InitMimeTypes(): Install():\n" " %s\n", strerror(err))); return err; } err = s_nodeSetType.SetPreferredApp(s_appSignature); if(err < B_OK) { PRINT(( "!!! RouteApp::_InitMimeTypes(): SetPreferredApp():\n" " %s\n", strerror(err))); return err; } } return B_OK; } // -------------------------------------------------------- // // main() stub // -------------------------------------------------------- // int main(int argc, char** argv) { // SetNewLeakChecking(true); // SetMallocLeakChecking(true); RouteApp app; app.Run(); return 0; } // END -- RouteApp.cpp --