* 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.
*/
#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 <Debug.h>
#include <OS.h>
#include <Roster.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <NodeInfo.h>
#include <Path.h>
#include <Entry.h>
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";
RouteApp::~RouteApp() {
ASSERT(manager);
thread_id id = manager->Thread();
manager->release();
if(id >= B_OK) {
status_t err;
while(wait_for_thread(id, &err) == B_INTERRUPTED) {
PRINT((" * RouteApp::~RouteApp(): B_INTERRUPTED\n"));
}
}
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) {
_InitMimeTypes();
RouteWindow*& r = const_cast<RouteWindow*&>(routeWindow);
r = new RouteWindow(manager);
_readSettings();
routeWindow->constrainToScreen();
routeWindow->Show();
}
bool
RouteApp::QuitRequested()
{
_writeSettings();
routeWindow->_closePalettes();
routeWindow->Lock();
routeWindow->Quit();
RouteWindow*& r = const_cast<RouteWindow*&>(routeWindow);
r = 0;
TipManager::QuitInstance();
return true;
}
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);
}
}
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);
}
}
void RouteApp::xmlExportBegin(
ExportContext& context) const {
context.beginElement(s_rootElement);
}
void RouteApp::xmlExportAttributes(
ExportContext& context) const {}
void RouteApp::xmlExportContent(
ExportContext& context) const {
context.beginContent();
{
BMessage m;
exportState(&m);
MessageIO io(&m);
status_t err __attribute__((unused)) = context.writeObject(&io);
ASSERT(err == B_OK);
}
if(routeWindow) {
context.beginElement(s_routeWindowElement);
context.beginContent();
BMessage m;
if (routeWindow->Lock()) {
routeWindow->exportState(&m);
routeWindow->Unlock();
}
MessageIO io(&m);
context.writeObject(&io);
context.endElement();
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();
}
void RouteApp::xmlImportBegin(
ImportContext& context) {
m_readState = _READ_ROOT;
}
void RouteApp::xmlImportAttribute(
const char* key,
const char* value,
ImportContext& context) {}
void RouteApp::xmlImportContent(
const char* data,
uint32 length,
ImportContext& context) {}
void RouteApp::xmlImportChild(
IPersistent* child,
ImportContext& context) {
MessageIO* io = dynamic_cast<MessageIO*>(child);
if(io) {
ASSERT(io->message());
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) {}
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;
}
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;
}
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() {
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;
BFile file(&entry, B_READ_ONLY);
if(file.InitCheck() != B_OK)
return file.InitCheck();
list<BString> errors;
err = XML::Read(
&file,
this,
m_settingsDocType,
&errors);
if(errors.size()) {
fputs("!!! RouteApp::_readSettings():", stderr);
for(list<BString>::iterator it = errors.begin();
it != errors.end(); ++it)
fputs((*it).String(), stderr);
}
return err;
}
status_t RouteApp::_writeSettings() {
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()) {
err = baseDirectory.CreateDirectory(s_settingsDirectory, &settingsDirectory);
ASSERT(err == B_OK);
}
else
settingsDirectory.SetTo(&folderEntry);
BFile file(
&settingsDirectory,
s_settingsFile,
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
err = file.InitCheck();
if(err < B_OK)
return err;
const char* header = "<?xml version=\"1.0\"?>\n";
file.Write((const void*)header, strlen(header));
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<BString>& errors,
MediaRoutingView* routingView) :
ImportContext(errors),
m_routingView(routingView) {}
public:
virtual void importUIState(
const BMessage* archive) {
PRINT((
"### importUIState\n"));
if(m_routingView) {
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();
}
}
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<BString> 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<BString>::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:
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;
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<MediaNodePanel*>(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();
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;
const char* header = "<?xml version=\"1.0\"?>\n";
file.Write((const void*)header, strlen(header));
context.stream = &file;
err = context.writeObject(manager);
if(err < B_OK) {
PRINT((
"!!! RouteApp::_writeSelectedNodeSet(): error:\n"
" %s\n", context.errorText()));
}
BNodeInfo* fileInfo = new BNodeInfo(&file);
fileInfo->SetType(s_nodeSetType.Type());
fileInfo->SetPreferredApp(s_appSignature);
delete fileInfo;
return B_OK;
}
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;
}
int main(int argc, char** argv) {
RouteApp app;
app.Run();
return 0;
}