imported Cortex 2.1.2 source
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16643 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
24159a0c7d
commit
a0795c6fe3
151
src/apps/cortex/AddOnHost/AddOnHostApp.cpp
Normal file
151
src/apps/cortex/AddOnHost/AddOnHostApp.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
// AddOnHostApp.cpp
|
||||
|
||||
#include "AddOnHostApp.h"
|
||||
#include "AddOnHostProtocol.h"
|
||||
|
||||
#include <Alert.h>
|
||||
#include <Debug.h>
|
||||
#include <MediaRoster.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
using namespace addon_host;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** implementation
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
App app;
|
||||
if(argc < 2 || strcmp(argv[1], "--addon-host") != 0)
|
||||
{
|
||||
int32 response = (new BAlert(
|
||||
"Cortex AddOnHost",
|
||||
"This program runs in the background, and is started automatically "
|
||||
"by Cortex when necessary. You probably don't want to start it manually.",
|
||||
"Continue", "Quit"))->Go();
|
||||
if(response == 1)
|
||||
return 0;
|
||||
}
|
||||
app.Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
App::~App() {}
|
||||
App::App() :
|
||||
BApplication(g_appSignature) {}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BLooper
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool App::QuitRequested() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BHandler
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void App::MessageReceived(
|
||||
BMessage* message) {
|
||||
|
||||
status_t err;
|
||||
|
||||
// message->PrintToStream();
|
||||
|
||||
switch(message->what) {
|
||||
case M_INSTANTIATE: {
|
||||
// fetch node info
|
||||
dormant_node_info info;
|
||||
const void *data;
|
||||
ssize_t dataSize;
|
||||
err = message->FindData("info", B_RAW_TYPE, &data, &dataSize);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! App::MessageReceived(M_INSTANTIATE):\n"
|
||||
" missing 'info'\n"));
|
||||
break;
|
||||
}
|
||||
if(dataSize != sizeof(info)) {
|
||||
PRINT((
|
||||
"* App::MessageReceived(M_INSTANTIATE):\n"
|
||||
" warning: 'info' size mismatch\n"));
|
||||
if(dataSize > ssize_t(sizeof(info)))
|
||||
dataSize = sizeof(info);
|
||||
}
|
||||
memcpy(reinterpret_cast<void *>(&info), data, dataSize);
|
||||
|
||||
// attempt to instantiate
|
||||
BMediaRoster* r = BMediaRoster::Roster();
|
||||
media_node node;
|
||||
err = r->InstantiateDormantNode(
|
||||
info,
|
||||
&node);
|
||||
|
||||
// if(err == B_OK)
|
||||
// // reference it
|
||||
// err = r->GetNodeFor(node.node, &node);
|
||||
|
||||
// send status
|
||||
if(err == B_OK) {
|
||||
BMessage m(M_INSTANTIATE_COMPLETE);
|
||||
m.AddInt32("node_id", node.node);
|
||||
message->SendReply(&m);
|
||||
}
|
||||
else {
|
||||
BMessage m(M_INSTANTIATE_FAILED);
|
||||
m.AddInt32("error", err);
|
||||
message->SendReply(&m);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case M_RELEASE: {
|
||||
// fetch node info
|
||||
live_node_info info;
|
||||
const void *data;
|
||||
ssize_t dataSize;
|
||||
err = message->FindData("info", B_RAW_TYPE, &data, &dataSize);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! App::MessageReceived(M_RELEASE):\n"
|
||||
" missing 'info'\n"));
|
||||
break;
|
||||
}
|
||||
if(dataSize != sizeof(info)) {
|
||||
PRINT((
|
||||
"* App::MessageReceived(M_RELEASE):\n"
|
||||
" warning: 'info' size mismatch\n"));
|
||||
if(dataSize > ssize_t(sizeof(info)))
|
||||
dataSize = sizeof(info);
|
||||
}
|
||||
memcpy(reinterpret_cast<void *>(&info), data, dataSize);
|
||||
|
||||
// attempt to release
|
||||
BMediaRoster* r = BMediaRoster::Roster();
|
||||
media_node node;
|
||||
err = r->ReleaseNode(info.node);
|
||||
|
||||
// send status
|
||||
if(err == B_OK) {
|
||||
BMessage m(M_RELEASE_COMPLETE);
|
||||
m.AddInt32("node_id", info.node.node);
|
||||
message->SendReply(&m);
|
||||
}
|
||||
else {
|
||||
BMessage m(M_RELEASE_FAILED);
|
||||
m.AddInt32("error", err);
|
||||
message->SendReply(&m);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
_inherited::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
// END -- AddOnHostApp.cpp --
|
44
src/apps/cortex/AddOnHost/AddOnHostApp.h
Normal file
44
src/apps/cortex/AddOnHost/AddOnHostApp.h
Normal file
@ -0,0 +1,44 @@
|
||||
// cortex::NodeManager::AddOnHostApp.h
|
||||
// * PURPOSE
|
||||
// Definition of (and provisions for communication with)
|
||||
// a separate BApplication whose single responsibility is
|
||||
// to launch nodes. NodeManager-launched nodes run in
|
||||
// another team, helping to lower the likelihood of a
|
||||
// socially maladjusted young node taking you out.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 6nov99
|
||||
|
||||
#ifndef __NodeManager_AddOnHostApp_H__
|
||||
#define __NodeManager_AddOnHostApp_H__
|
||||
|
||||
#include <Application.h>
|
||||
#include <MediaAddOn.h>
|
||||
#include <MediaDefs.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
namespace addon_host {
|
||||
|
||||
class App :
|
||||
public BApplication {
|
||||
typedef BApplication _inherited;
|
||||
|
||||
public: // *** implementation
|
||||
~App();
|
||||
App();
|
||||
|
||||
public: // *** BLooper
|
||||
bool QuitRequested();
|
||||
|
||||
public: // *** BHandler
|
||||
void MessageReceived(
|
||||
BMessage* message);
|
||||
|
||||
private: // implementation
|
||||
|
||||
};
|
||||
|
||||
}; // addon_host
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__NodeManager_AddOnHostApp_H__*/
|
BIN
src/apps/cortex/AddOnHost/AddOnHost_Resource.rsrc
Normal file
BIN
src/apps/cortex/AddOnHost/AddOnHost_Resource.rsrc
Normal file
Binary file not shown.
68
src/apps/cortex/AddOnHost/makefile
Normal file
68
src/apps/cortex/AddOnHost/makefile
Normal file
@ -0,0 +1,68 @@
|
||||
NAME= CortexAddOnHost
|
||||
TYPE= APP
|
||||
|
||||
#%{
|
||||
# @src->@
|
||||
|
||||
SRCS= \
|
||||
AddOnHostApp.cpp \
|
||||
../support/debug_tools.cpp
|
||||
|
||||
RSRCS= AddOnHost_Resource.rsrc
|
||||
|
||||
# @<-src@
|
||||
#%}
|
||||
|
||||
LIBS= be media
|
||||
X86_LIBS= stdc++.r4
|
||||
PPC_LIBS= mslcpp_4_0
|
||||
|
||||
# specify additional paths to directories following the standard
|
||||
# libXXX.so or libXXX.a naming scheme. You can specify full paths
|
||||
# or paths relative to the makefile. The paths included may not
|
||||
# be recursive, so include all of the paths where libraries can
|
||||
# be found. Directories where source files are found are
|
||||
# automatically included.
|
||||
LIBPATHS=
|
||||
|
||||
# additional paths to look for system headers
|
||||
# thes use the form: #include <header>
|
||||
# source file directories are NOT auto-included here
|
||||
SYSTEM_INCLUDE_PATHS =
|
||||
|
||||
# additional paths to look for local headers
|
||||
# thes use the form: #include "header"
|
||||
# source file directories are automatically included
|
||||
LOCAL_INCLUDE_PATHS = ..
|
||||
|
||||
# specify the level of optimization that you desire
|
||||
# NONE, SOME, FULL
|
||||
OPTIMIZE= FULL
|
||||
|
||||
DEFINES= #DEBUG=1 NDEBUG=0
|
||||
DEBUGGER = #TRUE
|
||||
|
||||
# specify special warning levels
|
||||
# if unspecified default warnings will be used
|
||||
# NONE = supress all warnings
|
||||
# ALL = enable all warnings
|
||||
WARNINGS = ALL
|
||||
|
||||
# specify whether image symbols will be created
|
||||
# so that stack crawls in the debugger are meaningful
|
||||
# if TRUE symbols will be created
|
||||
SYMBOLS =
|
||||
|
||||
# if TRUE, all symbols will be removed
|
||||
STRIP_SYMBOLS = TRUE
|
||||
|
||||
# specify additional compiler flags for all files
|
||||
COMPILER_FLAGS = -DCORTEX_NAMESPACE=cortex
|
||||
|
||||
# specify additional linker flags
|
||||
LINKER_FLAGS =
|
||||
|
||||
|
||||
## include local makefile-engine
|
||||
include ../makefile-engine
|
||||
|
325
src/apps/cortex/DiagramView/DiagramBox.cpp
Normal file
325
src/apps/cortex/DiagramView/DiagramBox.cpp
Normal file
@ -0,0 +1,325 @@
|
||||
// DiagramBox.cpp
|
||||
|
||||
#include "DiagramBox.h"
|
||||
#include "DiagramDefs.h"
|
||||
#include "DiagramEndPoint.h"
|
||||
#include "DiagramView.h"
|
||||
|
||||
#include <Message.h>
|
||||
#include <Messenger.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
#define D_MOUSE(x) //PRINT (x)
|
||||
#define D_DRAW(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DiagramBox::DiagramBox(
|
||||
BRect frame,
|
||||
uint32 flags)
|
||||
: DiagramItem(DiagramItem::M_BOX),
|
||||
DiagramItemGroup(DiagramItem::M_ENDPOINT),
|
||||
m_frame(frame),
|
||||
m_flags(flags)
|
||||
{
|
||||
D_METHOD(("DiagramBox::DiagramBox()\n"));
|
||||
makeDraggable(true);
|
||||
}
|
||||
|
||||
DiagramBox::~DiagramBox()
|
||||
{
|
||||
D_METHOD(("DiagramBox::~DiagramBox()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from DiagramItemGroup (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool DiagramBox::addItem(
|
||||
DiagramItem *item)
|
||||
{
|
||||
D_METHOD(("DiagramBox::addItem()\n"));
|
||||
if (item)
|
||||
{
|
||||
if (DiagramItemGroup::addItem(item))
|
||||
{
|
||||
if (m_view)
|
||||
{
|
||||
item->_setOwner(m_view);
|
||||
item->attachedToDiagram();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiagramBox::removeItem(
|
||||
DiagramItem *item)
|
||||
{
|
||||
D_METHOD(("DiagramBox::removeItem()\n"));
|
||||
if (item)
|
||||
{
|
||||
item->detachedFromDiagram();
|
||||
if (DiagramItemGroup::removeItem(item))
|
||||
{
|
||||
item->_setOwner(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from DiagramItem (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramBox::draw(
|
||||
BRect updateRect)
|
||||
{
|
||||
D_DRAW(("DiagramBox::draw()\n"));
|
||||
if (view())
|
||||
{
|
||||
view()->PushState();
|
||||
{
|
||||
if (m_flags & M_DRAW_UNDER_ENDPOINTS)
|
||||
{
|
||||
BRegion region, clipping;
|
||||
region.Include(frame());
|
||||
if (group()->getClippingAbove(this, &clipping))
|
||||
region.Exclude(&clipping);
|
||||
view()->ConstrainClippingRegion(®ion);
|
||||
drawBox();
|
||||
for (int32 i = 0; i < countItems(); i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i);
|
||||
if (region.Intersects(item->frame()))
|
||||
{
|
||||
item->draw(item->frame());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BRegion region, clipping;
|
||||
region.Include(frame());
|
||||
if (view()->getClippingAbove(this, &clipping))
|
||||
region.Exclude(&clipping);
|
||||
for (int32 i = 0; i < countItems(); i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i);
|
||||
BRect r;
|
||||
if (region.Intersects(r = item->frame()))
|
||||
{
|
||||
item->draw(r);
|
||||
region.Exclude(r);
|
||||
}
|
||||
}
|
||||
view()->ConstrainClippingRegion(®ion);
|
||||
drawBox();
|
||||
}
|
||||
}
|
||||
view()->PopState();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramBox::mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks)
|
||||
{
|
||||
D_MOUSE(("DiagramBox::mouseDown()\n"));
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
item->mouseDown(point, buttons, clicks);
|
||||
}
|
||||
else if (clicks == 1)
|
||||
{
|
||||
if (isSelectable())
|
||||
{
|
||||
BMessage selectMsg(M_SELECTION_CHANGED);
|
||||
if (modifiers() & B_SHIFT_KEY)
|
||||
{
|
||||
selectMsg.AddBool("replace", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectMsg.AddBool("replace", true);
|
||||
}
|
||||
selectMsg.AddPointer("item", reinterpret_cast<void *>(this));
|
||||
DiagramView* v = view();
|
||||
BMessenger(v).SendMessage(&selectMsg);
|
||||
}
|
||||
if (isDraggable() && (buttons == B_PRIMARY_MOUSE_BUTTON))
|
||||
{
|
||||
BMessage dragMsg(M_BOX_DRAGGED);
|
||||
dragMsg.AddPointer("item", static_cast<void *>(this));
|
||||
dragMsg.AddPoint("offset", point - frame().LeftTop());
|
||||
view()->DragMessage(&dragMsg, BRect(0.0, 0.0, -1.0, -1.0), view());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramBox::mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit)
|
||||
{
|
||||
D_MOUSE(("DiagramBox::mouseOver()\n"));
|
||||
DiagramItem *last = lastItemUnder();
|
||||
if (last && (transit == B_EXITED_VIEW))
|
||||
{
|
||||
last->mouseOver(point, B_EXITED_VIEW);
|
||||
resetItemUnder();
|
||||
}
|
||||
else
|
||||
{
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
if (item != last)
|
||||
{
|
||||
if (last)
|
||||
last->mouseOver(point, B_EXITED_VIEW);
|
||||
item->mouseOver(point, B_ENTERED_VIEW);
|
||||
}
|
||||
else
|
||||
{
|
||||
item->mouseOver(point, B_INSIDE_VIEW);
|
||||
}
|
||||
}
|
||||
else if (last)
|
||||
{
|
||||
last->mouseOver(point, B_EXITED_VIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramBox::messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message)
|
||||
{
|
||||
D_MOUSE(("DiagramBox::messageDragged()\n"));
|
||||
DiagramItem *last = lastItemUnder();
|
||||
if (last && (transit == B_EXITED_VIEW))
|
||||
{
|
||||
last->messageDragged(point, B_EXITED_VIEW, message);
|
||||
resetItemUnder();
|
||||
}
|
||||
else
|
||||
{
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
if (item != last)
|
||||
{
|
||||
if (last)
|
||||
{
|
||||
last->messageDragged(point, B_EXITED_VIEW, message);
|
||||
}
|
||||
item->messageDragged(point, B_ENTERED_VIEW, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
item->messageDragged(point, B_INSIDE_VIEW, message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (last)
|
||||
{
|
||||
last->messageDragged(point, B_EXITED_VIEW, message);
|
||||
}
|
||||
if (message->what == M_WIRE_DRAGGED)
|
||||
{
|
||||
view()->trackWire(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramBox::messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message)
|
||||
{
|
||||
D_METHOD(("DiagramBox::messageDropped()\n"));
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
item->messageDropped(point, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramBox::moveBy(
|
||||
float x,
|
||||
float y,
|
||||
BRegion *wireRegion)
|
||||
{
|
||||
D_METHOD(("DiagramBox::moveBy()\n"));
|
||||
if (view())
|
||||
{
|
||||
view()->PushState();
|
||||
{
|
||||
for (int32 i = 0; i < countItems(); i++)
|
||||
{
|
||||
DiagramEndPoint *endPoint = dynamic_cast<DiagramEndPoint *>(itemAt(i));
|
||||
if (endPoint)
|
||||
{
|
||||
endPoint->moveBy(x, y, wireRegion);
|
||||
}
|
||||
}
|
||||
if (wireRegion)
|
||||
{
|
||||
wireRegion->Include(m_frame);
|
||||
m_frame.OffsetBy(x, y);
|
||||
wireRegion->Include(m_frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frame.OffsetBy(x, y);
|
||||
}
|
||||
}
|
||||
view()->PopState();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramBox::resizeBy(
|
||||
float horizontal,
|
||||
float vertical)
|
||||
{
|
||||
D_METHOD(("DiagramBox::resizeBy()\n"));
|
||||
m_frame.right += horizontal;
|
||||
m_frame.bottom += vertical;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations (private)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramBox::_setOwner(
|
||||
DiagramView *owner)
|
||||
{
|
||||
D_METHOD(("DiagramBox::_setOwner()\n"));
|
||||
m_view = owner;
|
||||
for (int32 i = 0; i < countItems(DiagramItem::M_ENDPOINT); i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i);
|
||||
item->_setOwner(m_view);
|
||||
if (m_view)
|
||||
{
|
||||
item->attachedToDiagram();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END -- DiagramBox.cpp --
|
130
src/apps/cortex/DiagramView/DiagramBox.h
Normal file
130
src/apps/cortex/DiagramView/DiagramBox.h
Normal file
@ -0,0 +1,130 @@
|
||||
// DiagramBox.h (Cortex/DiagramView.h)
|
||||
//
|
||||
// * PURPOSE
|
||||
// DiagramItem subclass providing a basic framework for
|
||||
// boxes in diagrams, i.e. objects that can contain various
|
||||
// endpoints
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 25sep99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DiagramBox_H__
|
||||
#define __DiagramBox_H__
|
||||
|
||||
#include <Region.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "DiagramItem.h"
|
||||
#include "DiagramItemGroup.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DiagramBox : public DiagramItem,
|
||||
public DiagramItemGroup
|
||||
{
|
||||
|
||||
public: // *** flags
|
||||
|
||||
enum flag_t
|
||||
{
|
||||
M_DRAW_UNDER_ENDPOINTS = 0x1
|
||||
};
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DiagramBox(
|
||||
BRect frame,
|
||||
uint32 flags = 0);
|
||||
|
||||
virtual ~DiagramBox();
|
||||
|
||||
public: // *** hook functions
|
||||
|
||||
// is called from draw() to do the actual drawing
|
||||
virtual void drawBox() = 0;
|
||||
|
||||
public: // ** derived from DiagramItemGroup
|
||||
|
||||
// extends the DiagramItemGroup implementation by setting
|
||||
// the items owner and calling the attachedToDiagram() hook
|
||||
// on it
|
||||
virtual bool addItem(
|
||||
DiagramItem *item);
|
||||
|
||||
// extends the DiagramItemGroup implementation by calling
|
||||
// the detachedToDiagram() hook on the item
|
||||
virtual bool removeItem(
|
||||
DiagramItem *item);
|
||||
|
||||
public: // *** derived from DiagramItem
|
||||
|
||||
// returns the Boxes frame rectangle
|
||||
BRect frame() const
|
||||
{ return m_frame; }
|
||||
|
||||
// prepares the drawing stack and clipping region, then
|
||||
// calls drawBox
|
||||
void draw(
|
||||
BRect updateRect);
|
||||
|
||||
// is called from the parent DiagramViews MouseDown() implementation
|
||||
// if the Box was hit; this version initiates selection and dragging
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks);
|
||||
|
||||
// is called from the DiagramViews MouseMoved() when no message is being
|
||||
// dragged, but the mouse has moved above the box
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit);
|
||||
|
||||
// is called from the DiagramViews MouseMoved() when a message is being
|
||||
// dragged over the DiagramBox
|
||||
virtual void messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message);
|
||||
|
||||
// is called from the DiagramViews MessageReceived() function when a
|
||||
// message has been dropped on the DiagramBox
|
||||
virtual void messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message);
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// moves the box by a given amount, and returns in updateRegion the
|
||||
// frames of wires impacted by the move
|
||||
void moveBy(
|
||||
float x,
|
||||
float y,
|
||||
BRegion *updateRegion);
|
||||
|
||||
// resizes the boxes frame without doing any updating
|
||||
virtual void resizeBy(
|
||||
float horizontal,
|
||||
float vertical);
|
||||
|
||||
private: // *** internal methods
|
||||
|
||||
// is called by the DiagramView when added
|
||||
void _setOwner(
|
||||
DiagramView *owner);
|
||||
|
||||
private: // *** data
|
||||
|
||||
// the boxes' frame rectangle
|
||||
BRect m_frame;
|
||||
|
||||
// flags:
|
||||
// M_DRAW_UNDER_ENDPOINTS - don't remove EndPoint frames from
|
||||
// the clipping region
|
||||
uint32 m_flags;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __DiagramBox_H__ */
|
46
src/apps/cortex/DiagramView/DiagramDefs.h
Normal file
46
src/apps/cortex/DiagramView/DiagramDefs.h
Normal file
@ -0,0 +1,46 @@
|
||||
// DiagramDefs.h (Cortex/DiagramView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Contains some global constants used by most of the
|
||||
// DiagramView classes, currently only message "what"s
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 25sep99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DiagramDefs_H__
|
||||
#define __DiagramDefs_H__
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
// Message constants
|
||||
enum message_t
|
||||
{
|
||||
// Selection changed
|
||||
// - (DiagramItem *) "item"
|
||||
// - (bool) "replace"
|
||||
M_SELECTION_CHANGED = DiagramView_message_base,
|
||||
|
||||
// Diagram Box dragged
|
||||
// - (DiagramBox *) "item"
|
||||
// - (BPoint) "offset"
|
||||
M_BOX_DRAGGED,
|
||||
|
||||
// Diagram Wire dragged
|
||||
// - (DiagramEndPoint *) "from"
|
||||
M_WIRE_DRAGGED,
|
||||
|
||||
// Diagram Wire dropped
|
||||
// - (DiagramEndPoint *) "from"
|
||||
// - (DiagramEndPoint *) "to"
|
||||
// - (bool) "success"
|
||||
M_WIRE_DROPPED,
|
||||
|
||||
// Diagram View 'Rect Tracking'
|
||||
// - (BPoint) "origin"
|
||||
M_RECT_TRACKING
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __DiagramDefs_H__ */
|
295
src/apps/cortex/DiagramView/DiagramEndPoint.cpp
Normal file
295
src/apps/cortex/DiagramView/DiagramEndPoint.cpp
Normal file
@ -0,0 +1,295 @@
|
||||
// DiagramEndPoint.cpp
|
||||
|
||||
#include "DiagramEndPoint.h"
|
||||
#include "DiagramDefs.h"
|
||||
#include "DiagramWire.h"
|
||||
#include "DiagramView.h"
|
||||
|
||||
#include <Message.h>
|
||||
#include <Messenger.h>
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
#define D_MOUSE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DiagramEndPoint::DiagramEndPoint(
|
||||
BRect frame)
|
||||
: DiagramItem(DiagramItem::M_ENDPOINT),
|
||||
m_frame(frame),
|
||||
m_wire(0),
|
||||
m_connected(false),
|
||||
m_connecting(false)
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::DiagramEndPoint()\n"));
|
||||
makeDraggable(true);
|
||||
}
|
||||
|
||||
DiagramEndPoint::~DiagramEndPoint()
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::~DiagramEndPoint()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** hook functions
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
BPoint DiagramEndPoint::connectionPoint() const
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::connectionPoint()\n"));
|
||||
return BPoint(frame().left + frame().Height() / 2.0, frame().top + frame().Width() / 2.0);
|
||||
}
|
||||
|
||||
bool DiagramEndPoint::connectionRequested(
|
||||
DiagramEndPoint *fromWhich)
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::connectionRequested()\n"));
|
||||
if (!isConnected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from DiagramItem
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramEndPoint::draw(
|
||||
BRect updateRect)
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::draw()\n"));
|
||||
if (view())
|
||||
{
|
||||
view()->PushState();
|
||||
{
|
||||
BRegion region;
|
||||
region.Include(frame() & updateRect);
|
||||
view()->ConstrainClippingRegion(®ion);
|
||||
drawEndPoint();
|
||||
}
|
||||
view()->PopState();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramEndPoint::mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks)
|
||||
{
|
||||
D_MOUSE(("DiagramEndPoint::mouseDown()\n"));
|
||||
if (clicks == 1)
|
||||
{
|
||||
if (isSelectable())
|
||||
{
|
||||
BMessage selectMsg(M_SELECTION_CHANGED);
|
||||
if (modifiers() & B_SHIFT_KEY)
|
||||
{
|
||||
selectMsg.AddBool("replace", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectMsg.AddBool("replace", true);
|
||||
}
|
||||
selectMsg.AddPointer("item", reinterpret_cast<void *>(this));
|
||||
DiagramView* v = view();
|
||||
BMessenger(v).SendMessage(&selectMsg);
|
||||
}
|
||||
if (isDraggable() && (buttons == B_PRIMARY_MOUSE_BUTTON))
|
||||
{
|
||||
BMessage dragMsg(M_WIRE_DRAGGED);
|
||||
dragMsg.AddPointer("from", static_cast<void *>(this));
|
||||
view()->DragMessage(&dragMsg, BRect(0.0, 0.0, -1.0, -1.0), view());
|
||||
view()->messageDragged(point, B_INSIDE_VIEW, &dragMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramEndPoint::messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message)
|
||||
{
|
||||
D_MOUSE(("DiagramEndPoint::messageDragged()\n"));
|
||||
switch (message->what)
|
||||
{
|
||||
case M_WIRE_DRAGGED:
|
||||
{
|
||||
D_MESSAGE(("DiagramEndPoint::messageDragged(M_WIRE_DRAGGED)\n"));
|
||||
switch (transit)
|
||||
{
|
||||
case B_INSIDE_VIEW:
|
||||
{
|
||||
//PRINT((" -> transit: B_INSIDE_VIEW\n"));
|
||||
// this is a WORK-AROUND caused by the unreliability
|
||||
// of BViews DragMessage() routine !!
|
||||
if (isConnecting())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (isConnected())
|
||||
{
|
||||
view()->trackWire(point);
|
||||
}
|
||||
/* this should be enough in theory:
|
||||
if (!isConnecting())
|
||||
{
|
||||
view()->trackWire(point);
|
||||
}
|
||||
//break;*/
|
||||
}
|
||||
case B_ENTERED_VIEW:
|
||||
{
|
||||
//PRINT((" -> transit: B_ENTERED_VIEW\n"));
|
||||
DiagramEndPoint *endPoint;
|
||||
if ((message->FindPointer("from", reinterpret_cast<void **>(&endPoint)) == B_OK)
|
||||
&& (endPoint != this))
|
||||
{
|
||||
if (connectionRequested(endPoint))
|
||||
{
|
||||
view()->trackWire(connectionPoint());
|
||||
if (!isConnecting())
|
||||
{
|
||||
m_connecting = true;
|
||||
connected();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
view()->trackWire(point);
|
||||
if (isConnecting())
|
||||
{
|
||||
m_connecting = false;
|
||||
disconnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case B_EXITED_VIEW:
|
||||
{
|
||||
//PRINT((" -> transit: B_EXITED_VIEW\n"));
|
||||
if (isConnecting())
|
||||
{
|
||||
m_connecting = false;
|
||||
disconnected();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
DiagramItem::messageDragged(point, transit, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramEndPoint::messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message)
|
||||
{
|
||||
D_MESSAGE(("DiagramEndPoint::messageDropped()\n"));
|
||||
switch (message->what)
|
||||
{
|
||||
case M_WIRE_DRAGGED:
|
||||
{
|
||||
D_MESSAGE(("DiagramEndPoint::messageDropped(M_WIRE_DRAGGED)\n"));
|
||||
DiagramEndPoint *endPoint;
|
||||
if ((message->FindPointer("from", reinterpret_cast<void **>(&endPoint)) == B_OK)
|
||||
&& (endPoint != this))
|
||||
{
|
||||
bool success = connectionRequested(endPoint);
|
||||
BMessage dropMsg(M_WIRE_DROPPED);
|
||||
dropMsg.AddPointer("from", reinterpret_cast<void *>(endPoint));
|
||||
dropMsg.AddPointer("to", reinterpret_cast<void *>(this));
|
||||
dropMsg.AddBool("success", success);
|
||||
DiagramView* v = view();
|
||||
BMessenger(v).SendMessage(&dropMsg);
|
||||
if (isConnecting())
|
||||
{
|
||||
m_connecting = false;
|
||||
disconnected();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
DiagramItem::messageDropped(point, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** frame related operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramEndPoint::moveBy(
|
||||
float x,
|
||||
float y,
|
||||
BRegion *updateRegion)
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::moveBy()\n"));
|
||||
if (isConnected() && m_wire && updateRegion)
|
||||
{
|
||||
updateRegion->Include(m_wire->frame());
|
||||
m_frame.OffsetBy(x, y);
|
||||
m_wire->endPointMoved(this);
|
||||
updateRegion->Include(m_wire->frame());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frame.OffsetBy(x, y);
|
||||
if (m_wire)
|
||||
{
|
||||
m_wire->endPointMoved(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramEndPoint::resizeBy(
|
||||
float horizontal,
|
||||
float vertical)
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::resizeBy()\n"));
|
||||
m_frame.right += horizontal;
|
||||
m_frame.bottom += vertical;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** connection methods
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramEndPoint::connect(
|
||||
DiagramWire *wire)
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::connect()\n"));
|
||||
if (!m_connected && wire)
|
||||
{
|
||||
m_connected = true;
|
||||
m_wire = wire;
|
||||
makeDraggable(false);
|
||||
connected();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramEndPoint::disconnect()
|
||||
{
|
||||
D_METHOD(("DiagramEndPoint::disconnect()\n"));
|
||||
if (m_connected)
|
||||
{
|
||||
m_connected = false;
|
||||
m_wire = 0;
|
||||
makeDraggable(true);
|
||||
disconnected();
|
||||
}
|
||||
}
|
||||
|
||||
// END -- DiagramEndPoint.cpp --
|
156
src/apps/cortex/DiagramView/DiagramEndPoint.h
Normal file
156
src/apps/cortex/DiagramView/DiagramEndPoint.h
Normal file
@ -0,0 +1,156 @@
|
||||
// DiagramItem.h (Cortex/DiagramView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// DiagramItem subclass providing a basic implementation
|
||||
// of 'connectable' points inside a DiagramBox
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 25sep99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DiagramEndPoint_H__
|
||||
#define __DiagramEndPoint_H__
|
||||
|
||||
#include <Looper.h>
|
||||
#include <Region.h>
|
||||
|
||||
#include "DiagramItem.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DiagramWire;
|
||||
|
||||
class DiagramEndPoint : public DiagramItem
|
||||
{
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DiagramEndPoint(
|
||||
BRect frame);
|
||||
|
||||
virtual ~DiagramEndPoint();
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
DiagramWire *wire() const
|
||||
{ return m_wire; }
|
||||
|
||||
public: // *** hook functions
|
||||
|
||||
// is called from draw() to do the actual drawing
|
||||
virtual void drawEndPoint() = 0;
|
||||
|
||||
// should return the BPoint that a connected wire uses as
|
||||
// a drawing offset; this implementation returns the center
|
||||
// point of the EndPoint
|
||||
virtual BPoint connectionPoint() const;
|
||||
|
||||
// is called from messageDragged() and messageDropped() to
|
||||
// let you verify that a connection is okay
|
||||
virtual bool connectionRequested(
|
||||
DiagramEndPoint *fromWhich);
|
||||
|
||||
// is called whenever a connection has been made or a
|
||||
// temporary wire drag has been accepted
|
||||
virtual void connected()
|
||||
{ /* does nothing */ }
|
||||
|
||||
// is called whenever a connection has been broken or a
|
||||
// temporary wire drag has finished
|
||||
virtual void disconnected()
|
||||
{ /* does nothing */ }
|
||||
|
||||
public: // *** derived from DiagramItem
|
||||
|
||||
// returns the EndPoints frame rectangle
|
||||
BRect frame() const
|
||||
{ return m_frame; }
|
||||
|
||||
// prepares the drawing stack and clipping region, then
|
||||
// calls drawEndPoint
|
||||
void draw(
|
||||
BRect updateRect);
|
||||
|
||||
// is called from the parent DiagramBox's mouseDown()
|
||||
// implementation if the EndPoint was hit; this version
|
||||
// initiates selection and dragging (i.e. connecting)
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks);
|
||||
|
||||
// is called from the parent DiagramBox's mouseOver()
|
||||
// implementation if the mouse is over the EndPoint
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit)
|
||||
{ /* does nothing */ }
|
||||
|
||||
// is called from the parent DiagramBox's messageDragged()
|
||||
// implementation of a message is being dragged of the
|
||||
// EndPoint; this implementation handles only wire drags
|
||||
// (i.e. M_WIRE_DRAGGED messages)
|
||||
virtual void messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message);
|
||||
|
||||
// is called from the parent DiagramBox's messageDropped()
|
||||
// implementation if the message was dropped on the EndPoint;
|
||||
// this version handles wire dropping (connecting)
|
||||
virtual void messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message);
|
||||
|
||||
// moves the EndPoint by the specified amount and returns in
|
||||
// updateRegion the frames of connected wires if there are any
|
||||
// NOTE: no drawing/refreshing is done in this method, that's
|
||||
// up to the parent
|
||||
void moveBy(
|
||||
float x,
|
||||
float y,
|
||||
BRegion *updateRegion);
|
||||
|
||||
// resize the EndPoints frame rectangle without redraw
|
||||
virtual void resizeBy(
|
||||
float horizontal,
|
||||
float vertical);
|
||||
|
||||
public: // *** connection methods
|
||||
|
||||
// connect/disconnect and set the wire pointer accordingly
|
||||
void connect(
|
||||
DiagramWire *wire);
|
||||
void disconnect();
|
||||
|
||||
// returns true if the EndPoint is currently connected
|
||||
bool isConnected() const
|
||||
{ return m_connected; }
|
||||
|
||||
// returns true if the EndPoint is currently in a connection
|
||||
// process (i.e. it is either being dragged somewhere, or a
|
||||
// M_WIRE_DRAGGED message is being dragged over it
|
||||
bool isConnecting() const
|
||||
{ return m_connecting; }
|
||||
|
||||
private: // *** data
|
||||
|
||||
// the endpoints' frame rectangle
|
||||
BRect m_frame;
|
||||
|
||||
// pointer to the wire object this endpoint might be
|
||||
// connected to
|
||||
DiagramWire *m_wire;
|
||||
|
||||
// indicates if the endpoint is currently connected
|
||||
// to another endpoint
|
||||
bool m_connected;
|
||||
|
||||
// indicates that a connection is currently being made
|
||||
// but has not been confirmed yet
|
||||
bool m_connecting;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif // __DiagramEndPoint_H__
|
118
src/apps/cortex/DiagramView/DiagramItem.cpp
Normal file
118
src/apps/cortex/DiagramView/DiagramItem.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
// DiagramItem.cpp
|
||||
|
||||
#include "DiagramItem.h"
|
||||
#include "DiagramView.h"
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT(x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** static member initialization
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bigtime_t DiagramItem::m_lastSelectionTime = 0;
|
||||
int32 DiagramItem::m_countSelected = 0;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DiagramItem::DiagramItem(
|
||||
uint32 itemType)
|
||||
: m_type(itemType),
|
||||
m_view(0),
|
||||
m_group(0),
|
||||
m_draggable(false),
|
||||
m_selectable(true),
|
||||
m_selected(false),
|
||||
m_selectionTime(system_time())
|
||||
{
|
||||
D_METHOD(("DiagramItem::DiagramItem()\n"));
|
||||
}
|
||||
|
||||
DiagramItem::~DiagramItem()
|
||||
{
|
||||
D_METHOD(("DiagramItem::~DiagramItem()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramItem::select()
|
||||
{
|
||||
D_METHOD(("DiagramItem::select()\n"));
|
||||
if (!m_selected)
|
||||
{
|
||||
m_selected = true;
|
||||
m_lastSelectionTime = m_selectionTime = system_time();
|
||||
m_countSelected = 1;
|
||||
selected();
|
||||
view()->Invalidate(frame());
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramItem::selectAdding()
|
||||
{
|
||||
D_METHOD(("DiagramItem::selectAdding()\n"));
|
||||
if (!m_selected)
|
||||
{
|
||||
m_selected = true;
|
||||
m_selectionTime = m_lastSelectionTime - m_countSelected++;
|
||||
selected();
|
||||
view()->Invalidate(frame());
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramItem::deselect()
|
||||
{
|
||||
D_METHOD(("DiagramItem::deselect()\n"));
|
||||
if (m_selected)
|
||||
{
|
||||
m_selected = false;
|
||||
deselected();
|
||||
view()->Invalidate(frame());
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** hook functions (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
float DiagramItem::howCloseTo(
|
||||
BPoint point) const
|
||||
{
|
||||
D_METHOD(("DiagramItem::howCloseTo()\n"));
|
||||
if (frame().Contains(point))
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** compare functions (friend)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
int __CORTEX_NAMESPACE__ compareSelectionTime(
|
||||
const void *lValue,
|
||||
const void *rValue)
|
||||
{
|
||||
D_METHOD(("compareSelectionTime()\n"));
|
||||
int returnValue = 0;
|
||||
const DiagramItem *lItem = *(reinterpret_cast<const DiagramItem* const*>(reinterpret_cast<const void* const*>(lValue)));
|
||||
const DiagramItem *rItem = *(reinterpret_cast<const DiagramItem* const*>(reinterpret_cast<const void* const*>(rValue)));
|
||||
if (lItem->m_selectionTime < rItem->m_selectionTime)
|
||||
{
|
||||
returnValue = 1;
|
||||
}
|
||||
else if (lItem->m_selectionTime > rItem->m_selectionTime)
|
||||
{
|
||||
returnValue = -1;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// END -- DiagramItem.cpp --
|
254
src/apps/cortex/DiagramView/DiagramItem.h
Normal file
254
src/apps/cortex/DiagramView/DiagramItem.h
Normal file
@ -0,0 +1,254 @@
|
||||
// DiagramItem.h (Cortex/DiagramView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Provides a base class for all items that can be handled
|
||||
// by the DiagramView implementation. A basic interface with
|
||||
// some common implementation is defined, with methods related
|
||||
// to drawing, mouse handling, selecting, dragging, comparison
|
||||
// for sorting, and access to the drawing context which is the
|
||||
// DiagramView instance.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 25sep99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DiagramItem_H__
|
||||
#define __DiagramItem_H__
|
||||
|
||||
#include <OS.h>
|
||||
#include <InterfaceDefs.h>
|
||||
#include <Region.h>
|
||||
|
||||
class BView;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DiagramItemGroup;
|
||||
class DiagramView;
|
||||
class DiagramBox;
|
||||
|
||||
class DiagramItem
|
||||
{
|
||||
friend DiagramItemGroup;
|
||||
friend DiagramView;
|
||||
friend DiagramBox;
|
||||
|
||||
public: // *** types
|
||||
|
||||
enum diagram_item_t {
|
||||
M_BOX = 0x1,
|
||||
M_WIRE = 0x2,
|
||||
M_ENDPOINT = 0x4,
|
||||
M_ANY = 0x7
|
||||
};
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DiagramItem(
|
||||
uint32 itemType);
|
||||
|
||||
virtual ~DiagramItem();
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
// returns the item type assigned in the ctor
|
||||
uint32 type() const
|
||||
{ return m_type; }
|
||||
|
||||
// returns pointer to the drawing context of the DiagramView
|
||||
// object
|
||||
DiagramView *view() const
|
||||
{ return m_view; }
|
||||
|
||||
// returns pointer to the DiagramItemGroup the item belongs to
|
||||
DiagramItemGroup *group() const
|
||||
{ return m_group; }
|
||||
|
||||
// returns true if the item is currently selected
|
||||
bool isSelected() const
|
||||
{ return m_selected; }
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// changes the selection state of the item, and updates the
|
||||
// m_selectionTime member in the process to ensure proper
|
||||
// sorting; calls the selected() hook if the state has
|
||||
// actually changed
|
||||
void select();
|
||||
|
||||
// sets the item to selected without changing m_selectionTime
|
||||
// to the time of selection but prior to the last "replacing"
|
||||
// selection (i.e. thru select()) use this method for additive
|
||||
// selecting; still calls the selected() hook
|
||||
void selectAdding();
|
||||
|
||||
// deselects the item; calls the deselected() hook if the
|
||||
// state has actually changed
|
||||
void deselect();
|
||||
|
||||
// moves the items frame to a given point by calling moveBy with the
|
||||
// absolute coords translated into relative shift amount
|
||||
void moveTo(
|
||||
BPoint point,
|
||||
BRegion *updateRegion = 0)
|
||||
{ moveBy(point.x - frame().left, point.y - frame().top, updateRegion); }
|
||||
|
||||
// resizes the items frame to given dimensions; simply calls the resizeBy
|
||||
// implementation
|
||||
void resizeTo(
|
||||
float width,
|
||||
float height)
|
||||
{ resizeBy(width - frame().Width(), height - frame().Height()); }
|
||||
|
||||
public: // *** hook functions
|
||||
|
||||
// is called when the item has been attached to the DiagramView
|
||||
// and the view() pointer is valid
|
||||
virtual void attachedToDiagram()
|
||||
{ /* does nothing */ }
|
||||
|
||||
// is called just before the item is being detached from the
|
||||
// the DiagramView
|
||||
virtual void detachedFromDiagram()
|
||||
{ /* does nothing */ }
|
||||
|
||||
// is called from the DiagramViews MouseDown() function after
|
||||
// finding out the mouse buttons and clicks quantity.
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks)
|
||||
{/* does nothing */}
|
||||
|
||||
// is called from the DiagramViews MouseMoved() when *no* message is being
|
||||
// dragged, i.e. the mouse is simply floating above the item
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit)
|
||||
{/* does nothing */}
|
||||
|
||||
// is called from the DiagramViews MouseMoved() when a message is being
|
||||
// dragged; always call the base class version when overriding!
|
||||
virtual void messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message)
|
||||
{/* does nothing */}
|
||||
|
||||
// is called from the DiagramViews MessageReceived() function when an
|
||||
// message has been received through Drag&Drop; always call the base
|
||||
// class version when overriding!
|
||||
virtual void messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message)
|
||||
{/* does nothing */}
|
||||
|
||||
// is called when the item has been selected or deselected in some way
|
||||
virtual void selected()
|
||||
{ /* does nothing */ }
|
||||
virtual void deselected()
|
||||
{ /* does nothing */ }
|
||||
|
||||
public: // *** interface definition
|
||||
|
||||
// this function must be implemented by derived classes to return the
|
||||
// items frame rectangle in the DiagramViews coordinates
|
||||
virtual BRect frame() const = 0;
|
||||
|
||||
// this function should be implemented for non-rectangular subclasses
|
||||
// (like wires) to estimate how close a given point is to the object;
|
||||
// the default implementation returns 1.0 when the point lies within
|
||||
// the Frame() rect and 0.0 if not
|
||||
virtual float howCloseTo(
|
||||
BPoint point) const;
|
||||
|
||||
// this is the hook function called by DiagramView when it's time to
|
||||
// draw the object
|
||||
virtual void draw(
|
||||
BRect updateRect) = 0;
|
||||
|
||||
// should move the items frame by the specified amount and do the
|
||||
// necessary drawing instructions to update the display; if the
|
||||
// caller supplied a BRegion pointer in updateRegion, this method
|
||||
// should add other areas affected by the move to it (e.g. wire
|
||||
// frames)
|
||||
virtual void moveBy(
|
||||
float x,
|
||||
float y,
|
||||
BRegion *updateRegion = 0)
|
||||
{ /* does nothing */ }
|
||||
|
||||
// should resize the items frame by the specified amount
|
||||
virtual void resizeBy(
|
||||
float horizontal,
|
||||
float vertical)
|
||||
{ /* does nothing */ }
|
||||
|
||||
protected: // *** selecting/dragging
|
||||
|
||||
// turn on/off the built-in selection handling
|
||||
void makeSelectable(
|
||||
bool selectable)
|
||||
{ m_selectable = selectable; }
|
||||
bool isSelectable() const
|
||||
{ return m_selectable; }
|
||||
|
||||
// turn on/off the built-in drag & drop handling
|
||||
void makeDraggable(
|
||||
bool draggable)
|
||||
{ m_draggable = draggable; }
|
||||
bool isDraggable() const
|
||||
{ return m_draggable; }
|
||||
|
||||
protected: // *** compare functions
|
||||
|
||||
// compares the time when each item was last selected and
|
||||
// returns -1 for the most recent.
|
||||
friend int compareSelectionTime(
|
||||
const void *lValue,
|
||||
const void *rValue);
|
||||
|
||||
protected: // *** internal methods
|
||||
|
||||
// called only by DiagramItemGroup objects in the method
|
||||
// addItem()
|
||||
virtual void _setOwner(
|
||||
DiagramView *owner)
|
||||
{ m_view = owner; }
|
||||
|
||||
private: // *** data members
|
||||
|
||||
// the items type (M_BOX, M_WIRE or M_ENDPOINT)
|
||||
uint32 m_type;
|
||||
|
||||
// a pointer to the drawing context (the DiagramView instance)
|
||||
DiagramView *m_view;
|
||||
|
||||
// a pointer to the DiagramItemGroup the item belongs to
|
||||
DiagramItemGroup *m_group;
|
||||
|
||||
// can the object be dragged
|
||||
bool m_draggable;
|
||||
|
||||
// can the object be selected
|
||||
bool m_selectable;
|
||||
|
||||
// is the object currently selected
|
||||
bool m_selected;
|
||||
|
||||
// when was the object selected the last time or added (used
|
||||
// for drawing order)
|
||||
bigtime_t m_selectionTime;
|
||||
|
||||
// stores the most recent time a item was selected thru
|
||||
// the select() method
|
||||
static bigtime_t m_lastSelectionTime;
|
||||
|
||||
// counts the number of selections thru selectAdding()
|
||||
// since the last call to select()
|
||||
static int32 m_countSelected;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __DiagramItem_H__ */
|
642
src/apps/cortex/DiagramView/DiagramItemGroup.cpp
Normal file
642
src/apps/cortex/DiagramView/DiagramItemGroup.cpp
Normal file
@ -0,0 +1,642 @@
|
||||
// DiagramItemGroup.cpp
|
||||
|
||||
#include "DiagramItemGroup.h"
|
||||
#include "DiagramItem.h"
|
||||
|
||||
#include <Region.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DiagramItemGroup::DiagramItemGroup(
|
||||
uint32 acceptedTypes,
|
||||
bool multiSelection)
|
||||
: m_boxes(0),
|
||||
m_wires(0),
|
||||
m_endPoints(0),
|
||||
m_selection(0),
|
||||
m_types(acceptedTypes),
|
||||
m_itemAlignment(1.0, 1.0),
|
||||
m_multiSelection(multiSelection),
|
||||
m_lastItemUnder(0)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::DiagramItemGroup()\n"));
|
||||
m_selection = new BList(1);
|
||||
}
|
||||
|
||||
DiagramItemGroup::~DiagramItemGroup()
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::~DiagramItemGroup()\n"));
|
||||
if (m_selection)
|
||||
{
|
||||
m_selection->MakeEmpty();
|
||||
delete m_selection;
|
||||
}
|
||||
if (m_boxes && (m_types & DiagramItem::M_BOX))
|
||||
{
|
||||
while (countItems(DiagramItem::M_BOX) > 0)
|
||||
{
|
||||
DiagramItem *item = itemAt(0, DiagramItem::M_BOX);
|
||||
if (removeItem(item))
|
||||
delete item;
|
||||
}
|
||||
delete m_boxes;
|
||||
}
|
||||
if (m_wires && (m_types & DiagramItem::M_WIRE))
|
||||
{
|
||||
while (countItems(DiagramItem::M_WIRE) > 0)
|
||||
{
|
||||
DiagramItem *item = itemAt(0, DiagramItem::M_WIRE);
|
||||
if (removeItem(item))
|
||||
delete item;
|
||||
}
|
||||
delete m_wires;
|
||||
}
|
||||
if (m_endPoints && (m_types & DiagramItem::M_ENDPOINT))
|
||||
{
|
||||
while (countItems(DiagramItem::M_ENDPOINT) > 0)
|
||||
{
|
||||
DiagramItem *item = itemAt(0, DiagramItem::M_ENDPOINT);
|
||||
if (removeItem(item))
|
||||
delete item;
|
||||
}
|
||||
delete m_endPoints;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** item accessors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
uint32 DiagramItemGroup::countItems(
|
||||
uint32 whichType) const
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::countItems()\n"));
|
||||
uint32 count = 0;
|
||||
if (whichType & m_types)
|
||||
{
|
||||
if (whichType & DiagramItem::M_BOX)
|
||||
{
|
||||
if (m_boxes)
|
||||
{
|
||||
count += m_boxes->CountItems();
|
||||
}
|
||||
}
|
||||
if (whichType & DiagramItem::M_WIRE)
|
||||
{
|
||||
if (m_wires)
|
||||
{
|
||||
count += m_wires->CountItems();
|
||||
}
|
||||
}
|
||||
if (whichType & DiagramItem::M_ENDPOINT)
|
||||
{
|
||||
if (m_endPoints)
|
||||
{
|
||||
count += m_endPoints->CountItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
DiagramItem *DiagramItemGroup::itemAt(
|
||||
uint32 index,
|
||||
uint32 whichType) const
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::itemAt()\n"));
|
||||
if (m_types & whichType)
|
||||
{
|
||||
if (whichType & DiagramItem::M_BOX)
|
||||
{
|
||||
if (m_boxes && (index < countItems(DiagramItem::M_BOX)))
|
||||
{
|
||||
return static_cast<DiagramItem *>(m_boxes->ItemAt(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
index -= countItems(DiagramItem::M_BOX);
|
||||
}
|
||||
}
|
||||
if (whichType & DiagramItem::M_WIRE)
|
||||
{
|
||||
if (m_wires && (index < countItems(DiagramItem::M_WIRE)))
|
||||
{
|
||||
return static_cast<DiagramItem *>(m_wires->ItemAt(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
index -= countItems(DiagramItem::M_WIRE);
|
||||
}
|
||||
}
|
||||
if (whichType & DiagramItem::M_ENDPOINT)
|
||||
{
|
||||
if (m_endPoints && (index < countItems(DiagramItem::M_ENDPOINT)))
|
||||
{
|
||||
return static_cast<DiagramItem *>(m_endPoints->ItemAt(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This function returns the first box or endpoint found that
|
||||
// contains the given point. For connections it looks at all
|
||||
// wires that 'might' contain the point and calls their method
|
||||
// howCloseTo() to find the one closest to the point.
|
||||
// The lists should be sorted by selection time for proper results!
|
||||
DiagramItem *DiagramItemGroup::itemUnder(
|
||||
BPoint point)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::itemUnder()\n"));
|
||||
if (m_types & DiagramItem::M_BOX)
|
||||
{
|
||||
for (uint32 i = 0; i < countItems(DiagramItem::M_BOX); i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
|
||||
if (item->frame().Contains(point) && (item->howCloseTo(point) == 1.0))
|
||||
{
|
||||
// DiagramItemGroup *group = dynamic_cast<DiagramItemGroup *>(item);
|
||||
return (m_lastItemUnder = item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_types & DiagramItem::M_WIRE)
|
||||
{
|
||||
float closest = 0.0;
|
||||
DiagramItem *closestItem = 0;
|
||||
for (uint32 i = 0; i < countItems(DiagramItem::M_WIRE); i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_WIRE);
|
||||
if (item->frame().Contains(point))
|
||||
{
|
||||
float howClose = item->howCloseTo(point);
|
||||
if (howClose > closest)
|
||||
{
|
||||
closestItem = item;
|
||||
if (howClose == 1.0)
|
||||
return (m_lastItemUnder = item);
|
||||
closest = howClose;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closest > 0.5)
|
||||
{
|
||||
return (m_lastItemUnder = closestItem);
|
||||
}
|
||||
}
|
||||
if (m_types & DiagramItem::M_ENDPOINT)
|
||||
{
|
||||
for (uint32 i = 0; i < countItems(DiagramItem::M_ENDPOINT); i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_ENDPOINT);
|
||||
if (item->frame().Contains(point) && (item->howCloseTo(point) == 1.0))
|
||||
{
|
||||
return (m_lastItemUnder = item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (m_lastItemUnder = 0); // no item was found!
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** item operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool DiagramItemGroup::addItem(
|
||||
DiagramItem *item)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::addItem()\n"));
|
||||
if (item && (m_types & item->type()))
|
||||
{
|
||||
switch (item->type())
|
||||
{
|
||||
case DiagramItem::M_BOX:
|
||||
{
|
||||
if (!m_boxes)
|
||||
{
|
||||
m_boxes = new BList();
|
||||
}
|
||||
item->m_group = this;
|
||||
return m_boxes->AddItem(static_cast<void *>(item));
|
||||
}
|
||||
case DiagramItem::M_WIRE:
|
||||
{
|
||||
if (!m_wires)
|
||||
{
|
||||
m_wires = new BList();
|
||||
}
|
||||
item->m_group = this;
|
||||
return m_wires->AddItem(static_cast<void *>(item));
|
||||
}
|
||||
case DiagramItem::M_ENDPOINT:
|
||||
{
|
||||
if (!m_endPoints)
|
||||
{
|
||||
m_endPoints = new BList();
|
||||
}
|
||||
item->m_group = this;
|
||||
return m_endPoints->AddItem(static_cast<void *>(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiagramItemGroup::removeItem(
|
||||
DiagramItem *item)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::removeItem()\n"));
|
||||
if (item && (m_types & item->type()))
|
||||
{
|
||||
// reset the lastItemUnder-pointer if it pointed to this item
|
||||
if (m_lastItemUnder == item)
|
||||
{
|
||||
m_lastItemUnder = 0;
|
||||
}
|
||||
// remove it from the selection list if it was selected
|
||||
if (item->isSelected())
|
||||
{
|
||||
m_selection->RemoveItem(static_cast<void *>(item));
|
||||
}
|
||||
// try to remove the item from its list
|
||||
switch (item->type())
|
||||
{
|
||||
case DiagramItem::M_BOX:
|
||||
{
|
||||
if (m_boxes)
|
||||
{
|
||||
item->m_group = 0;
|
||||
return m_boxes->RemoveItem(static_cast<void *>(item));
|
||||
}
|
||||
}
|
||||
case DiagramItem::M_WIRE:
|
||||
{
|
||||
if (m_wires)
|
||||
{
|
||||
item->m_group = 0;
|
||||
return m_wires->RemoveItem(static_cast<void *>(item));
|
||||
}
|
||||
}
|
||||
case DiagramItem::M_ENDPOINT:
|
||||
{
|
||||
if (m_endPoints)
|
||||
{
|
||||
item->m_group = 0;
|
||||
return m_endPoints->RemoveItem(static_cast<void *>(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DiagramItemGroup::sortItems(
|
||||
uint32 whichType,
|
||||
int (*compareFunc)(const void *, const void *))
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::sortItems()\n"));
|
||||
if ((whichType != DiagramItem::M_ANY) && (m_types & whichType))
|
||||
{
|
||||
switch (whichType)
|
||||
{
|
||||
case DiagramItem::M_BOX:
|
||||
{
|
||||
if (m_boxes)
|
||||
{
|
||||
m_boxes->SortItems(compareFunc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DiagramItem::M_WIRE:
|
||||
{
|
||||
if (m_wires)
|
||||
{
|
||||
m_wires->SortItems(compareFunc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DiagramItem::M_ENDPOINT:
|
||||
{
|
||||
if (m_endPoints)
|
||||
{
|
||||
m_endPoints->SortItems(compareFunc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// items are drawn in reverse order; they should be sorted by
|
||||
// selection time before this function gets called, so that
|
||||
// the more recently selected item are drawn above others
|
||||
void DiagramItemGroup::drawItems(
|
||||
BRect updateRect,
|
||||
uint32 whichType,
|
||||
BRegion *updateRegion)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::drawItems()\n"));
|
||||
if (whichType & DiagramItem::M_WIRE)
|
||||
{
|
||||
for (int32 i = countItems(DiagramItem::M_WIRE) - 1; i >= 0; i--)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_WIRE);
|
||||
if (item->frame().Intersects(updateRect))
|
||||
{
|
||||
item->draw(updateRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (whichType & DiagramItem::M_BOX)
|
||||
{
|
||||
for (int32 i = countItems(DiagramItem::M_BOX) - 1; i >= 0; i--)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
|
||||
if (item && item->frame().Intersects(updateRect))
|
||||
{
|
||||
item->draw(updateRect);
|
||||
if (updateRegion)
|
||||
updateRegion->Exclude(item->frame());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (whichType & DiagramItem::M_ENDPOINT)
|
||||
{
|
||||
for (int32 i = countItems(DiagramItem::M_ENDPOINT) - 1; i >= 0; i--)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_ENDPOINT);
|
||||
if (item && item->frame().Intersects(updateRect))
|
||||
{
|
||||
item->draw(updateRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DiagramItemGroup::getClippingAbove(
|
||||
DiagramItem *which,
|
||||
BRegion *region)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::getClippingAbove()\n"));
|
||||
bool found = false;
|
||||
if (which && region)
|
||||
{
|
||||
switch (which->type())
|
||||
{
|
||||
case DiagramItem::M_BOX:
|
||||
{
|
||||
int32 index = m_boxes->IndexOf(which);
|
||||
if (index >= 0) // the item was found
|
||||
{
|
||||
BRect r = which->frame();
|
||||
for (int32 i = 0; i < index; i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
|
||||
if (item && item->frame().Intersects(r))
|
||||
{
|
||||
region->Include(item->frame() & r);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DiagramItem::M_WIRE:
|
||||
{
|
||||
BRect r = which->frame();
|
||||
for (uint32 i = 0; i < countItems(DiagramItem::M_BOX); i++)
|
||||
{
|
||||
DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
|
||||
if (item && item->frame().Intersects(r))
|
||||
{
|
||||
region->Include(item->frame() & r);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** selection accessors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
uint32 DiagramItemGroup::selectedType() const
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::selectedType()\n"));
|
||||
if (countSelectedItems() > 0)
|
||||
{
|
||||
return selectedItemAt(0)->type();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 DiagramItemGroup::countSelectedItems() const
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::countSelectedItems()\n"));
|
||||
if (m_selection)
|
||||
{
|
||||
return m_selection->CountItems();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DiagramItem *DiagramItemGroup::selectedItemAt(
|
||||
uint32 index) const
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::selectedItemAt()\n"));
|
||||
if (m_selection)
|
||||
{
|
||||
return static_cast<DiagramItem *>(m_selection->ItemAt(index));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** selection related operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// selects an item, optionally replacing the complete former
|
||||
// selection. If the type of the item to be selected differs
|
||||
// from the type of items currently selected, this methods
|
||||
// automatically replaces the former selection
|
||||
bool DiagramItemGroup::selectItem(
|
||||
DiagramItem *which,
|
||||
bool deselectOthers)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::selectItem()\n"));
|
||||
bool selectionChanged = false;
|
||||
if (which && !which->isSelected() && which->isSelectable())
|
||||
{
|
||||
// check if the item's type is the same as of the other
|
||||
// selected items
|
||||
if (m_multiSelection)
|
||||
{
|
||||
if (which->type() != selectedType())
|
||||
{
|
||||
deselectOthers = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check if the former selection has to be deselected
|
||||
if (deselectOthers || !m_multiSelection)
|
||||
{
|
||||
while (countSelectedItems() > 0)
|
||||
{
|
||||
deselectItem(selectedItemAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
// select the item
|
||||
if (deselectOthers || countSelectedItems() == 0)
|
||||
{
|
||||
which->select();
|
||||
}
|
||||
else
|
||||
{
|
||||
which->selectAdding();
|
||||
}
|
||||
m_selection->AddItem(which);
|
||||
selectionChanged = true;
|
||||
}
|
||||
|
||||
// resort the lists if necessary
|
||||
if (selectionChanged)
|
||||
{
|
||||
sortItems(which->type(), compareSelectionTime);
|
||||
sortSelectedItems(compareSelectionTime);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiagramItemGroup::deselectItem(
|
||||
DiagramItem *which)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::deselectItem()\n"));
|
||||
if (which && which->isSelected())
|
||||
{
|
||||
m_selection->RemoveItem(which);
|
||||
which->deselect();
|
||||
sortItems(which->type(), compareSelectionTime);
|
||||
sortSelectedItems(compareSelectionTime);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiagramItemGroup::selectAll(
|
||||
uint32 itemType)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::selectAll()\n"));
|
||||
bool selectionChanged = false;
|
||||
if (m_types & itemType)
|
||||
{
|
||||
for (int32 i = 0; i < countItems(itemType); i++)
|
||||
{
|
||||
if (selectItem(itemAt(i, itemType), false))
|
||||
selectionChanged = true;
|
||||
}
|
||||
}
|
||||
return selectionChanged;
|
||||
}
|
||||
|
||||
bool DiagramItemGroup::deselectAll(
|
||||
uint32 itemType)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::deselectAll()\n"));
|
||||
bool selectionChanged = false;
|
||||
if (m_types & itemType)
|
||||
{
|
||||
for (int32 i = 0; i < countItems(itemType); i++)
|
||||
{
|
||||
if (deselectItem(itemAt(i, itemType)))
|
||||
selectionChanged = true;
|
||||
}
|
||||
}
|
||||
return selectionChanged;
|
||||
}
|
||||
|
||||
void DiagramItemGroup::sortSelectedItems(
|
||||
int (*compareFunc)(const void *, const void *))
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::sortSelectedItems()\n"));
|
||||
m_selection->SortItems(compareFunc);
|
||||
}
|
||||
|
||||
void DiagramItemGroup::dragSelectionBy(
|
||||
float x,
|
||||
float y,
|
||||
BRegion *updateRegion)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::dragSelectionBy()\n"));
|
||||
if (selectedType() == DiagramItem::M_BOX)
|
||||
{
|
||||
align(&x, &y);
|
||||
if ((x != 0) || (y != 0))
|
||||
{
|
||||
for (int32 i = countSelectedItems() - 1; i >= 0; i--)
|
||||
{
|
||||
DiagramItem *item = dynamic_cast<DiagramItem *>(selectedItemAt(i));
|
||||
if (item->isDraggable())
|
||||
{
|
||||
item->moveBy(x, y, updateRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramItemGroup::removeSelection()
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::removeSelection()\n"));
|
||||
for (int32 i = 0; i < countSelectedItems(); i++)
|
||||
{
|
||||
removeItem(selectedItemAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** alignment related accessors & operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramItemGroup::getItemAlignment(
|
||||
float *horizontal,
|
||||
float *vertical)
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::getItemAlignment()\n"));
|
||||
if (horizontal)
|
||||
*horizontal = m_itemAlignment.x;
|
||||
if (vertical)
|
||||
*vertical = m_itemAlignment.y;
|
||||
}
|
||||
|
||||
void DiagramItemGroup::align(
|
||||
float *x,
|
||||
float *y) const
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::align()\n"));
|
||||
*x = ((int)*x / (int)m_itemAlignment.x) * m_itemAlignment.x;
|
||||
*y = ((int)*y / (int)m_itemAlignment.y) * m_itemAlignment.y;
|
||||
}
|
||||
|
||||
BPoint DiagramItemGroup::align(
|
||||
BPoint point) const
|
||||
{
|
||||
D_METHOD(("DiagramItemGroup::align()\n"));
|
||||
float x = point.x, y = point.y;
|
||||
align(&x, &y);
|
||||
return BPoint(x, y);
|
||||
}
|
||||
|
||||
// END -- DiagramItemGroup.cpp --
|
203
src/apps/cortex/DiagramView/DiagramItemGroup.h
Normal file
203
src/apps/cortex/DiagramView/DiagramItemGroup.h
Normal file
@ -0,0 +1,203 @@
|
||||
// DiagramItemGroup.h (Cortex/DiagramView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Basic class for managing and accessing DiagramItem objects.
|
||||
// Objects of this class can manage one or more of the DiagramItem
|
||||
// type M_BOX, M_WIRE and M_ENDPOINT. Many methods let you specify
|
||||
// which type of item you want to deal with.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 28sep99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DiagramItemGroup_H__
|
||||
#define __DiagramItemGroup_H__
|
||||
|
||||
#include "DiagramItem.h"
|
||||
|
||||
#include <List.h>
|
||||
#include <Point.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DiagramItemGroup
|
||||
{
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DiagramItemGroup(
|
||||
uint32 acceptedTypes,
|
||||
bool multiSelection = true);
|
||||
|
||||
virtual ~DiagramItemGroup();
|
||||
|
||||
public: // *** hook functions
|
||||
|
||||
// is called whenever the current selection has changed
|
||||
virtual void selectionChanged()
|
||||
{ /*does nothing */ }
|
||||
|
||||
public: // *** item accessors
|
||||
|
||||
// returns the number of items in the group (optionally only those
|
||||
// of the given type)
|
||||
uint32 countItems(
|
||||
uint32 whichType = DiagramItem::M_ANY) const;
|
||||
|
||||
// returns a pointer to the item in the lists which is
|
||||
// at the given index; if none is found, this function
|
||||
// returns 0
|
||||
DiagramItem *itemAt(
|
||||
uint32 index,
|
||||
uint32 whichType = DiagramItem::M_ANY) const;
|
||||
|
||||
// returns a pointer to the item that is currently under
|
||||
// the given point, and 0 if there is none
|
||||
DiagramItem *itemUnder(
|
||||
BPoint point);
|
||||
|
||||
public: // *** item operations
|
||||
|
||||
// adds an item to the group; returns true on success
|
||||
virtual bool addItem(
|
||||
DiagramItem *item);
|
||||
|
||||
// removes an item from the group; returns true on success
|
||||
bool removeItem(
|
||||
DiagramItem *item);
|
||||
|
||||
// performs a quicksort on a list of items with the provided
|
||||
// compare function (one is already defined in the DiagramItem
|
||||
// implementation); can't handle more than one item type at a
|
||||
// time!
|
||||
void sortItems(
|
||||
uint32 itemType,
|
||||
int (*compareFunc)(const void *, const void *));
|
||||
|
||||
// fires a draw() command at all items of a specific type that
|
||||
// intersect with the updateRect
|
||||
void drawItems(
|
||||
BRect updateRect,
|
||||
uint32 whichType = DiagramItem::M_ANY,
|
||||
BRegion *updateRegion = 0);
|
||||
|
||||
// returns in outRegion the region of items that lay "over" the given
|
||||
// DiagramItem in which; returns false if no items are above or the item
|
||||
// doesn't exist
|
||||
bool getClippingAbove(
|
||||
DiagramItem *which,
|
||||
BRegion *outRegion);
|
||||
|
||||
public: // *** selection accessors
|
||||
|
||||
// returns the type of DiagramItems in the current selection
|
||||
// (currently only one type at a time is supported!)
|
||||
uint32 selectedType() const;
|
||||
|
||||
// returns the number of items in the current selection
|
||||
uint32 countSelectedItems() const;
|
||||
|
||||
// returns a pointer to the item in the list which is
|
||||
// at the given index; if none is found, this function
|
||||
// returns 0
|
||||
DiagramItem *selectedItemAt(
|
||||
uint32 index) const;
|
||||
|
||||
// returns the ability of the group to handle multiple selections
|
||||
// as set in the constructor
|
||||
bool multipleSelection() const
|
||||
{ return m_multiSelection; }
|
||||
|
||||
public: // *** selection related operations
|
||||
|
||||
// selects the given item and optionally deselects all others
|
||||
// (thereby replacing the former selection)
|
||||
bool selectItem(
|
||||
DiagramItem *which,
|
||||
bool deselectOthers = true);
|
||||
|
||||
// simply deselects one item
|
||||
bool deselectItem(
|
||||
DiagramItem *which);
|
||||
|
||||
// performs a quicksort on the list of selected items with the
|
||||
// provided compare function (one is already defined in the DiagramItem
|
||||
// implementation)
|
||||
void sortSelectedItems(
|
||||
int (*compareFunc)(const void *, const void *));
|
||||
|
||||
// select all items of the given type
|
||||
bool selectAll(
|
||||
uint32 itemType = DiagramItem::M_ANY);
|
||||
|
||||
// deselect all items of the given type
|
||||
bool deselectAll(
|
||||
uint32 itemType = DiagramItem::M_ANY);
|
||||
|
||||
// moves all selected items by a given amount, taking
|
||||
// item alignment into account; in updateRegion the areas
|
||||
// that still require updating by the caller are returned
|
||||
void dragSelectionBy(
|
||||
float x,
|
||||
float y,
|
||||
BRegion *updateRegion);
|
||||
|
||||
// removes all selected items from the group
|
||||
void removeSelection();
|
||||
|
||||
public: // *** alignment related
|
||||
|
||||
// set/get the 'item alignment' grid size; this is used when
|
||||
// items are being dragged and inserted
|
||||
void setItemAlignment(
|
||||
float horizontal,
|
||||
float vertical)
|
||||
{ m_itemAlignment.Set(horizontal, vertical); }
|
||||
void getItemAlignment(
|
||||
float *horizontal,
|
||||
float *vertical);
|
||||
|
||||
// align a given point to the current grid
|
||||
void align(
|
||||
float *x,
|
||||
float *y) const;
|
||||
BPoint align(
|
||||
BPoint point) const;
|
||||
|
||||
protected: // *** accessors
|
||||
|
||||
// returns a pointer to the last item returned by the itemUnder()
|
||||
// function; this can be used by deriving classes to implement a
|
||||
// transit system
|
||||
DiagramItem *lastItemUnder()
|
||||
{ return m_lastItemUnder; }
|
||||
|
||||
void resetItemUnder()
|
||||
{ m_lastItemUnder = 0; }
|
||||
|
||||
private: // *** data members
|
||||
|
||||
// pointers to the item-lists (one list for each item type)
|
||||
BList *m_boxes;
|
||||
BList *m_wires;
|
||||
BList *m_endPoints;
|
||||
|
||||
// pointer to the list containing the current selection
|
||||
BList *m_selection;
|
||||
|
||||
// the DiagramItem type(s) of items this group will accept
|
||||
uint32 m_types;
|
||||
|
||||
// specifies the "grid"-size for the frames of items
|
||||
BPoint m_itemAlignment;
|
||||
|
||||
// can multiple items be selected at once ?
|
||||
bool m_multiSelection;
|
||||
|
||||
// cached pointer to the item that was found in itemUnder()
|
||||
DiagramItem *m_lastItemUnder;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __DiagramItemGroup_H__ */
|
761
src/apps/cortex/DiagramView/DiagramView.cpp
Normal file
761
src/apps/cortex/DiagramView/DiagramView.cpp
Normal file
@ -0,0 +1,761 @@
|
||||
// DiagramView.cpp
|
||||
|
||||
#include "DiagramView.h"
|
||||
#include "DiagramDefs.h"
|
||||
#include "DiagramBox.h"
|
||||
#include "DiagramEndPoint.h"
|
||||
#include "DiagramWire.h"
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <Message.h>
|
||||
#include <ScrollBar.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_HOOK(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
#define D_MOUSE(x) //PRINT (x)
|
||||
#define D_DRAW(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DiagramView::DiagramView(
|
||||
BRect frame,
|
||||
const char *name,
|
||||
bool multiSelection,
|
||||
uint32 resizingMode,
|
||||
uint32 flags)
|
||||
: BView(frame, name, resizingMode, B_WILL_DRAW | B_FRAME_EVENTS | flags),
|
||||
DiagramItemGroup(DiagramItem::M_BOX | DiagramItem::M_WIRE),
|
||||
m_lastButton(0),
|
||||
m_clickCount(0),
|
||||
m_lastClickPoint(-1.0, -1.0),
|
||||
m_pressedButton(0),
|
||||
m_draggedWire(0),
|
||||
m_useBackgroundBitmap(false),
|
||||
m_backgroundBitmap(0)
|
||||
{
|
||||
D_METHOD(("DiagramView::DiagramView()\n"));
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
m_dataRect = Bounds();
|
||||
}
|
||||
|
||||
DiagramView::~DiagramView()
|
||||
{
|
||||
D_METHOD(("DiagramView::~DiagramView()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** hook functions
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramView::messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message)
|
||||
{
|
||||
D_METHOD(("DiagramView::messageDragged()\n"));
|
||||
switch (message->what)
|
||||
{
|
||||
case M_WIRE_DRAGGED:
|
||||
{
|
||||
D_MESSAGE(("DiagramView::messageDragged(M_WIRE_DROPPED)\n"));
|
||||
if (!m_draggedWire)
|
||||
{
|
||||
DiagramEndPoint *fromEndPoint;
|
||||
if (message->FindPointer("from", reinterpret_cast<void **>(&fromEndPoint)) == B_OK)
|
||||
{
|
||||
_beginWireTracking(fromEndPoint);
|
||||
}
|
||||
}
|
||||
trackWire(point);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message)
|
||||
{
|
||||
D_METHOD(("DiagramView::messageDropped()\n"));
|
||||
switch (message->what)
|
||||
{
|
||||
case M_WIRE_DRAGGED:
|
||||
{
|
||||
D_MESSAGE(("DiagramView::messageDropped(M_WIRE_DROPPED)\n"));
|
||||
DiagramEndPoint *fromWhich = 0;
|
||||
if (message->FindPointer("from", reinterpret_cast<void **>(&fromWhich)) == B_OK)
|
||||
{
|
||||
connectionAborted(fromWhich);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from BView
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// initial scrollbar update [e.moon 16nov99]
|
||||
void DiagramView::AttachedToWindow()
|
||||
{
|
||||
D_METHOD(("DiagramView::AttachedToWindow()\n"));
|
||||
_updateScrollBars();
|
||||
}
|
||||
|
||||
void DiagramView::Draw(
|
||||
BRect updateRect)
|
||||
{
|
||||
D_METHOD(("DiagramView::Draw()\n"));
|
||||
drawBackground(updateRect);
|
||||
drawItems(updateRect, DiagramItem::M_WIRE);
|
||||
drawItems(updateRect, DiagramItem::M_BOX);
|
||||
}
|
||||
|
||||
void DiagramView::FrameResized(
|
||||
float width,
|
||||
float height)
|
||||
{
|
||||
D_METHOD(("DiagramView::FrameResized()\n"));
|
||||
_updateScrollBars();
|
||||
}
|
||||
|
||||
void DiagramView::GetPreferredSize(
|
||||
float *width,
|
||||
float *height) {
|
||||
D_HOOK(("DiagramView::GetPreferredSize()\n"));
|
||||
|
||||
*width = m_dataRect.Width() + 10.0;
|
||||
*height = m_dataRect.Height() + 10.0;
|
||||
}
|
||||
|
||||
void DiagramView::MessageReceived(
|
||||
BMessage *message)
|
||||
{
|
||||
D_METHOD(("DiagramView::MessageReceived()\n"));
|
||||
switch (message->what)
|
||||
{
|
||||
case M_SELECTION_CHANGED:
|
||||
{
|
||||
D_MESSAGE(("DiagramView::MessageReceived(M_SELECTION_CHANGED)\n"));
|
||||
DiagramItem *item;
|
||||
if (message->FindPointer("item", reinterpret_cast<void **>(&item)) == B_OK)
|
||||
{
|
||||
bool deselectOthers = true;
|
||||
message->FindBool("replace", &deselectOthers);
|
||||
if (item->isSelected() && !deselectOthers && multipleSelection())
|
||||
{
|
||||
if (deselectItem(item))
|
||||
{
|
||||
selectionChanged();
|
||||
}
|
||||
}
|
||||
else if (selectItem(item, deselectOthers))
|
||||
{
|
||||
sortItems(item->type(), &compareSelectionTime);
|
||||
selectionChanged();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case M_WIRE_DROPPED:
|
||||
{
|
||||
D_MESSAGE(("DiagramView::MessageReceived(M_WIRE_DROPPED)\n"));
|
||||
DiagramEndPoint *fromWhich = 0;
|
||||
DiagramEndPoint *toWhich = 0;
|
||||
bool success = false;
|
||||
if (message->FindPointer("from", reinterpret_cast<void **>(&fromWhich)) == B_OK)
|
||||
{
|
||||
if ((message->FindPointer("to", reinterpret_cast<void **>(&toWhich)) == B_OK)
|
||||
&& (message->FindBool("success", &success) == B_OK))
|
||||
{
|
||||
if (success && fromWhich && toWhich)
|
||||
{
|
||||
_endWireTracking();
|
||||
DiagramWire *wire = createWire(fromWhich, toWhich);
|
||||
if (wire && addItem(wire))
|
||||
{
|
||||
connectionEstablished(fromWhich, toWhich);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
connectionAborted(fromWhich);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (message->WasDropped())
|
||||
{
|
||||
BPoint point = ConvertFromScreen(message->DropPoint());
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
item->messageDropped(point, message);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
messageDropped(point, message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BView::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::KeyDown(
|
||||
const char *bytes,
|
||||
int32 numBytes)
|
||||
{
|
||||
D_METHOD(("DiagramView::KeyDown()\n"));
|
||||
switch (bytes[0])
|
||||
{
|
||||
case B_LEFT_ARROW:
|
||||
{
|
||||
float x;
|
||||
getItemAlignment(&x, 0);
|
||||
BRegion updateRegion;
|
||||
dragSelectionBy(-x, 0.0, &updateRegion);
|
||||
for (int32 i = 0; i < updateRegion.CountRects(); i++)
|
||||
Invalidate(updateRegion.RectAt(i));
|
||||
updateDataRect();
|
||||
break;
|
||||
}
|
||||
case B_RIGHT_ARROW:
|
||||
{
|
||||
float x;
|
||||
getItemAlignment(&x, 0);
|
||||
BRegion updateRegion;
|
||||
dragSelectionBy(x, 0.0, &updateRegion);
|
||||
for (int32 i = 0; i < updateRegion.CountRects(); i++)
|
||||
Invalidate(updateRegion.RectAt(i));
|
||||
updateDataRect();
|
||||
break;
|
||||
}
|
||||
case B_UP_ARROW:
|
||||
{
|
||||
float y;
|
||||
getItemAlignment(0, &y);
|
||||
BRegion updateRegion;
|
||||
dragSelectionBy(0.0, -y, &updateRegion);
|
||||
for (int32 i = 0; i < updateRegion.CountRects(); i++)
|
||||
Invalidate(updateRegion.RectAt(i));
|
||||
updateDataRect();
|
||||
break;
|
||||
}
|
||||
case B_DOWN_ARROW:
|
||||
{
|
||||
float y;
|
||||
getItemAlignment(0, &y);
|
||||
BRegion updateRegion;
|
||||
dragSelectionBy(0.0, y, &updateRegion);
|
||||
for (int32 i = 0; i < updateRegion.CountRects(); i++)
|
||||
Invalidate(updateRegion.RectAt(i));
|
||||
updateDataRect();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
BView::KeyDown(bytes, numBytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::MouseDown(
|
||||
BPoint point)
|
||||
{
|
||||
D_METHOD(("DiagramView::MouseDown()\n"));
|
||||
|
||||
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
|
||||
|
||||
// update click count
|
||||
BMessage* message = Window()->CurrentMessage();
|
||||
int32 clicks = message->FindInt32("clicks");
|
||||
int32 buttons = message->FindInt32("buttons");
|
||||
|
||||
bool moved = (fabs(point.x - m_lastClickPoint.x) > 2.0 || fabs(point.y - m_lastClickPoint.y) > 2.0);
|
||||
if (!moved && (buttons == m_lastButton) && (clicks > 1))
|
||||
{
|
||||
m_clickCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_clickCount = 1;
|
||||
}
|
||||
m_lastButton = buttons;
|
||||
m_lastClickPoint = point;
|
||||
m_lastDragPoint = ConvertToScreen(point);
|
||||
|
||||
// [e.moon 16nov99] scroll on 3rd button
|
||||
m_pressedButton = buttons;
|
||||
if(m_pressedButton == B_TERTIARY_MOUSE_BUTTON) {
|
||||
return;
|
||||
}
|
||||
|
||||
// was an item clicked ?
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
item->mouseDown(point, m_lastButton, m_clickCount);
|
||||
}
|
||||
else // no, the background was clicked
|
||||
{
|
||||
if (!(modifiers() & B_SHIFT_KEY) && deselectAll(DiagramItem::M_ANY))
|
||||
selectionChanged();
|
||||
if (multipleSelection() && (m_lastButton == B_PRIMARY_MOUSE_BUTTON) && !(modifiers() & B_CONTROL_KEY))
|
||||
_beginRectTracking(point);
|
||||
mouseDown(point, m_lastButton, m_clickCount);
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::MouseMoved(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message)
|
||||
{
|
||||
D_METHOD(("DiagramView::MouseMoved()\n"));
|
||||
|
||||
|
||||
// [e.moon 16nov99] 3rd-button scrolling
|
||||
if(m_pressedButton == B_TERTIARY_MOUSE_BUTTON) {
|
||||
|
||||
// fetch/store screen point; calculate distance travelled
|
||||
ConvertToScreen(&point);
|
||||
float xDelta = m_lastDragPoint.x - point.x;
|
||||
float yDelta = m_lastDragPoint.y - point.y;
|
||||
m_lastDragPoint = point;
|
||||
|
||||
// constrain to scrollbar limits
|
||||
BScrollBar* hScroll = ScrollBar(B_HORIZONTAL);
|
||||
if(xDelta && hScroll) {
|
||||
float val = hScroll->Value();
|
||||
float min, max;
|
||||
hScroll->GetRange(&min, &max);
|
||||
|
||||
if(val + xDelta < min)
|
||||
xDelta = 0;
|
||||
|
||||
if(val + xDelta > max)
|
||||
xDelta = 0;
|
||||
}
|
||||
|
||||
BScrollBar* vScroll = ScrollBar(B_VERTICAL);
|
||||
if(yDelta && vScroll) {
|
||||
float val = vScroll->Value();
|
||||
float min, max;
|
||||
vScroll->GetRange(&min, &max);
|
||||
|
||||
if(val + yDelta < min)
|
||||
yDelta = 0;
|
||||
|
||||
if(val + yDelta > max)
|
||||
yDelta = 0;
|
||||
}
|
||||
|
||||
// scroll
|
||||
if(xDelta == 0.0 && yDelta == 0.0)
|
||||
return;
|
||||
|
||||
ScrollBy(xDelta, yDelta);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message)
|
||||
{
|
||||
switch (message->what)
|
||||
{
|
||||
case M_RECT_TRACKING:
|
||||
{
|
||||
BPoint origin;
|
||||
if (message->FindPoint("origin", &origin) == B_OK)
|
||||
{
|
||||
_trackRect(origin, point);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case M_BOX_DRAGGED:
|
||||
{
|
||||
DiagramBox *box;
|
||||
BPoint offset;
|
||||
if ((message->FindPointer("item", reinterpret_cast<void **>(&box)) == B_OK)
|
||||
&& (message->FindPoint("offset", &offset) == B_OK))
|
||||
{
|
||||
if (box)
|
||||
{
|
||||
BRegion updateRegion;
|
||||
dragSelectionBy(point.x - box->frame().left - offset.x,
|
||||
point.y - box->frame().top - offset.y,
|
||||
&updateRegion);
|
||||
updateDataRect();
|
||||
for (int32 i = 0; i < updateRegion.CountRects(); i++)
|
||||
{
|
||||
Invalidate(updateRegion.RectAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: // unkwown message -> redirect to messageDragged()
|
||||
{
|
||||
DiagramItem *last = lastItemUnder();
|
||||
if (transit == B_EXITED_VIEW)
|
||||
{
|
||||
if (last)
|
||||
{
|
||||
last->messageDragged(point, B_EXITED_VIEW, message);
|
||||
messageDragged(point, B_EXITED_VIEW, message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
if (item != last)
|
||||
{
|
||||
if (last)
|
||||
last->messageDragged(point, B_EXITED_VIEW, message);
|
||||
item->messageDragged(point, B_ENTERED_VIEW, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
item->messageDragged(point, B_INSIDE_VIEW, message);
|
||||
}
|
||||
}
|
||||
else if (last)
|
||||
{
|
||||
last->messageDragged(point, B_EXITED_VIEW, message);
|
||||
messageDragged(point, B_ENTERED_VIEW, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageDragged(point, transit, message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // no message at all -> redirect to mouseOver()
|
||||
{
|
||||
DiagramItem *last = lastItemUnder();
|
||||
if ((transit == B_EXITED_VIEW) || (transit == B_OUTSIDE_VIEW))
|
||||
{
|
||||
if (last)
|
||||
{
|
||||
last->mouseOver(point, B_EXITED_VIEW);
|
||||
resetItemUnder();
|
||||
mouseOver(point, B_EXITED_VIEW);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DiagramItem *item = itemUnder(point);
|
||||
if (item)
|
||||
{
|
||||
if (item != last)
|
||||
{
|
||||
if (last)
|
||||
last->mouseOver(point, B_EXITED_VIEW);
|
||||
item->mouseOver(point, B_ENTERED_VIEW);
|
||||
}
|
||||
else
|
||||
{
|
||||
item->mouseOver(point, B_INSIDE_VIEW);
|
||||
}
|
||||
}
|
||||
else if (last)
|
||||
{
|
||||
last->mouseOver(point, B_EXITED_VIEW);
|
||||
mouseOver(point, B_ENTERED_VIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::MouseUp(
|
||||
BPoint point)
|
||||
{
|
||||
D_METHOD(("DiagramView::MouseUp()\n"));
|
||||
if (multipleSelection())
|
||||
EndRectTracking();
|
||||
_endWireTracking();
|
||||
|
||||
// [e.moon 16nov99] mark no button as down
|
||||
m_pressedButton = 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from DiagramItemGroup (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool DiagramView::addItem(
|
||||
DiagramItem *item)
|
||||
{
|
||||
D_METHOD(("DiagramBox::addItem()\n"));
|
||||
if (item)
|
||||
{
|
||||
if (DiagramItemGroup::addItem(item))
|
||||
{
|
||||
item->_setOwner(this);
|
||||
item->attachedToDiagram();
|
||||
if (item->type() == DiagramItem::M_BOX)
|
||||
{
|
||||
updateDataRect();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiagramView::removeItem(
|
||||
DiagramItem *item)
|
||||
{
|
||||
D_METHOD(("DiagramBox::removeItem()\n"));
|
||||
if (item)
|
||||
{
|
||||
item->detachedFromDiagram();
|
||||
if (DiagramItemGroup::removeItem(item))
|
||||
{
|
||||
item->_setOwner(0);
|
||||
if (item->type() == DiagramItem::M_BOX)
|
||||
{
|
||||
updateDataRect();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramView::trackWire(
|
||||
BPoint point)
|
||||
{
|
||||
D_MOUSE(("DiagramView::trackWire()\n"));
|
||||
if (m_draggedWire)
|
||||
{
|
||||
BRegion region;
|
||||
region.Include(m_draggedWire->frame());
|
||||
m_draggedWire->m_dragEndPoint = point;
|
||||
m_draggedWire->endPointMoved();
|
||||
region.Include(m_draggedWire->frame());
|
||||
region.Exclude(&m_boxRegion);
|
||||
for (int32 i = 0; i < region.CountRects(); i++)
|
||||
{
|
||||
Invalidate(region.RectAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations (protected)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramView::drawBackground(
|
||||
BRect updateRect)
|
||||
{
|
||||
D_METHOD(("DiagramView::drawBackground()\n"));
|
||||
if (m_useBackgroundBitmap)
|
||||
{
|
||||
BRegion region;
|
||||
region.Include(updateRect);
|
||||
region.Exclude(&m_boxRegion);
|
||||
BRect bounds = Bounds();
|
||||
PushState();
|
||||
{
|
||||
ConstrainClippingRegion(®ion);
|
||||
for (float y = 0; y < bounds.bottom; y += m_backgroundBitmap->Bounds().Height())
|
||||
{
|
||||
for (float x = 0; x < bounds.right; x += m_backgroundBitmap->Bounds().Width())
|
||||
{
|
||||
DrawBitmapAsync(m_backgroundBitmap, BPoint(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
PopState();
|
||||
}
|
||||
else
|
||||
{
|
||||
BRegion region;
|
||||
region.Include(updateRect);
|
||||
region.Exclude(&m_boxRegion);
|
||||
PushState();
|
||||
{
|
||||
SetLowColor(m_backgroundColor);
|
||||
FillRegion(®ion, B_SOLID_LOW);
|
||||
}
|
||||
PopState();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::setBackgroundColor(
|
||||
rgb_color color)
|
||||
{
|
||||
D_METHOD(("DiagramView::setBackgroundColor()\n"));
|
||||
m_backgroundColor = color;
|
||||
m_useBackgroundBitmap = false;
|
||||
}
|
||||
|
||||
void DiagramView::setBackgroundBitmap(
|
||||
BBitmap *bitmap)
|
||||
{
|
||||
D_METHOD(("DiagramView::setBackgroundBitmap()\n"));
|
||||
if (m_backgroundBitmap)
|
||||
{
|
||||
delete m_backgroundBitmap;
|
||||
}
|
||||
m_backgroundBitmap = new BBitmap(bitmap);
|
||||
m_useBackgroundBitmap = true;
|
||||
}
|
||||
|
||||
void DiagramView::updateDataRect()
|
||||
{
|
||||
D_METHOD(("DiagramView::updateDataRect()\n"));
|
||||
// calculate the area in which boxes display
|
||||
m_boxRegion.MakeEmpty();
|
||||
for (int32 i = 0; i < countItems(DiagramItem::M_BOX); i++)
|
||||
{
|
||||
m_boxRegion.Include(itemAt(i, DiagramItem::M_BOX)->frame());
|
||||
}
|
||||
// adapt the data rect to the new region of boxes
|
||||
BRect boxRect = m_boxRegion.Frame();
|
||||
m_dataRect.right = boxRect.right;
|
||||
m_dataRect.bottom = boxRect.bottom;
|
||||
// update the scroll bars
|
||||
_updateScrollBars();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations (private)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DiagramView::_beginWireTracking(
|
||||
DiagramEndPoint *startPoint)
|
||||
{
|
||||
D_METHOD(("DiagramView::beginWireTracking()\n"));
|
||||
m_draggedWire = createWire(startPoint);
|
||||
addItem(m_draggedWire);
|
||||
selectItem(m_draggedWire, true);
|
||||
Invalidate(startPoint->frame());
|
||||
}
|
||||
|
||||
void DiagramView::_endWireTracking()
|
||||
{
|
||||
D_METHOD(("DiagramView::endWireTracking()\n"));
|
||||
if (m_draggedWire)
|
||||
{
|
||||
removeItem(m_draggedWire);
|
||||
Invalidate(m_draggedWire->frame());
|
||||
DiagramEndPoint *endPoint = m_draggedWire->startPoint();
|
||||
if (!endPoint)
|
||||
{
|
||||
endPoint = m_draggedWire->endPoint();
|
||||
}
|
||||
if (endPoint)
|
||||
{
|
||||
Invalidate(endPoint->frame());
|
||||
}
|
||||
delete m_draggedWire;
|
||||
m_draggedWire = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::_beginRectTracking(
|
||||
BPoint origin)
|
||||
{
|
||||
D_METHOD(("DiagramView::beginRectTracking()\n"));
|
||||
BMessage message(M_RECT_TRACKING);
|
||||
message.AddPoint("origin", origin);
|
||||
DragMessage(&message, BRect(0.0, 0.0, -1.0, -1.0));
|
||||
BView::BeginRectTracking(BRect(origin, origin), B_TRACK_RECT_CORNER);
|
||||
}
|
||||
|
||||
void DiagramView::_trackRect(
|
||||
BPoint origin,
|
||||
BPoint current)
|
||||
{
|
||||
D_METHOD(("DiagramView::trackRect()\n"));
|
||||
bool changed = false;
|
||||
BRect rect;
|
||||
rect.left = origin.x < current.x ? origin.x : current.x;
|
||||
rect.top = origin.y < current.y ? origin.y : current.y;
|
||||
rect.right = origin.x < current.x ? current.x : origin.x;
|
||||
rect.bottom = origin.y < current.y ? current.y : origin.y;
|
||||
for (int32 i = 0; i < countItems(DiagramItem::M_BOX); i++)
|
||||
{
|
||||
DiagramBox *box = dynamic_cast<DiagramBox *>(itemAt(i, DiagramItem::M_BOX));
|
||||
if (box)
|
||||
{
|
||||
if (rect.Intersects(box->frame()))
|
||||
{
|
||||
changed |= selectItem(box, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
changed |= deselectItem(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
sortItems(DiagramItem::M_BOX, &compareSelectionTime);
|
||||
selectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramView::_updateScrollBars()
|
||||
{
|
||||
D_METHOD(("DiagramView::_updateScrollBars()\n"));
|
||||
// fetch the vertical ScrollBar
|
||||
BScrollBar *scrollBar = ScrollBar(B_VERTICAL);
|
||||
if (scrollBar)
|
||||
{
|
||||
// Disable the ScrollBar if the view is large enough to display
|
||||
// the entire data-rect
|
||||
if (Bounds().Height() > m_dataRect.Height())
|
||||
{
|
||||
scrollBar->SetRange(0.0, 0.0);
|
||||
scrollBar->SetProportion(1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
scrollBar->SetRange(m_dataRect.top, m_dataRect.bottom - Bounds().Height());
|
||||
scrollBar->SetProportion(Bounds().Height() / m_dataRect.Height());
|
||||
}
|
||||
}
|
||||
scrollBar = ScrollBar(B_HORIZONTAL);
|
||||
if (scrollBar)
|
||||
{
|
||||
// Disable the ScrollBar if the view is large enough to display
|
||||
// the entire data-rect
|
||||
if (Bounds().Width() > m_dataRect.Width())
|
||||
{
|
||||
scrollBar->SetRange(0.0, 0.0);
|
||||
scrollBar->SetProportion(1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
scrollBar->SetRange(m_dataRect.left, m_dataRect.right - Bounds().Width());
|
||||
scrollBar->SetProportion(Bounds().Width() / m_dataRect.Width());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END -- DiagramView.cpp --
|
257
src/apps/cortex/DiagramView/DiagramView.h
Normal file
257
src/apps/cortex/DiagramView/DiagramView.h
Normal file
@ -0,0 +1,257 @@
|
||||
// DiagramView.h (Cortex/DiagramView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// BView and DiagramItemGroup derived class providing the
|
||||
// one and only drawing context all child DiagramItems will
|
||||
// use.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 25sep99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DiagramView_H__
|
||||
#define __DiagramView_H__
|
||||
|
||||
#include "DiagramItemGroup.h"
|
||||
|
||||
#include <Region.h>
|
||||
#include <View.h>
|
||||
|
||||
//class BBitmap;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
//class DiagramBox;
|
||||
class DiagramWire;
|
||||
class DiagramEndPoint;
|
||||
|
||||
class DiagramView : public BView,
|
||||
public DiagramItemGroup
|
||||
{
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DiagramView(
|
||||
BRect frame,
|
||||
const char *name,
|
||||
bool multiSelection,
|
||||
uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP,
|
||||
uint32 flags = B_WILL_DRAW);
|
||||
|
||||
virtual ~DiagramView();
|
||||
|
||||
public: // *** hook functions
|
||||
|
||||
// is called from MessageReceived() if a wire has been dropped
|
||||
// on the background or on an incompatible endpoint
|
||||
virtual void connectionAborted(
|
||||
DiagramEndPoint *fromWhich)
|
||||
{ /* does nothing */ }
|
||||
|
||||
// is called from MessageReceived() if an endpoint has accepted
|
||||
// a wire-drop (i.e. connection)
|
||||
virtual void connectionEstablished(
|
||||
DiagramEndPoint *fromWhich,
|
||||
DiagramEndPoint *toWhich)
|
||||
{ /* does nothing */ }
|
||||
|
||||
// must be implemented to return an instance of the DiagramWire
|
||||
// derived class
|
||||
virtual DiagramWire *createWire(
|
||||
DiagramEndPoint *fromWhich,
|
||||
DiagramEndPoint *woWhich) = 0;
|
||||
|
||||
// must be implemented to return an instance of the DiagramWire
|
||||
// derived class; this version is for temporary wires used in
|
||||
// drag & drop connecting
|
||||
virtual DiagramWire *createWire(
|
||||
DiagramEndPoint *fromWhich) = 0;
|
||||
|
||||
// hook called from MouseDown() if the background was hit
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks)
|
||||
{ /* does nothing */ }
|
||||
|
||||
// hook called from MouseMoved() if the mouse is floating over
|
||||
// the background (i.e. with no message attached)
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit)
|
||||
{ /* does nothing */ }
|
||||
|
||||
// hook called from MouseMoved() if a message is being dragged
|
||||
// over the background
|
||||
virtual void messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message);
|
||||
|
||||
// hook called from MessageReceived() if a message has been
|
||||
// dropped over the background
|
||||
virtual void messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message);
|
||||
|
||||
public: // derived from BView
|
||||
|
||||
// initial scrollbar update [e.moon 16nov99]
|
||||
virtual void AttachedToWindow();
|
||||
|
||||
// draw the background and all items
|
||||
virtual void Draw(
|
||||
BRect updateRect);
|
||||
|
||||
// updates the scrollbars
|
||||
virtual void FrameResized(
|
||||
float width,
|
||||
float height);
|
||||
|
||||
// return data rect [c.lenz 1mar2000]
|
||||
virtual void GetPreferredSize(
|
||||
float *width,
|
||||
float *height);
|
||||
|
||||
// handles the messages M_SELECTION_CHANGED and M_WIRE_DROPPED
|
||||
// and passes a dropped message on to the item it was dropped on
|
||||
virtual void MessageReceived(
|
||||
BMessage *message);
|
||||
|
||||
// handles the arrow keys for moving DiagramBoxes
|
||||
virtual void KeyDown(
|
||||
const char *bytes,
|
||||
int32 numBytes);
|
||||
|
||||
// if an item is located at the click point, this function calls
|
||||
// that items mouseDown() method; else a deselectAll() command is
|
||||
// made and rect-tracking is initiated
|
||||
virtual void MouseDown(
|
||||
BPoint point);
|
||||
|
||||
// if an item is located under the given point, this function
|
||||
// calls that items messageDragged() hook if a message is being
|
||||
// dragged, and mouseOver() if not
|
||||
virtual void MouseMoved(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message);
|
||||
|
||||
// ends rect-tracking and wire-tracking
|
||||
virtual void MouseUp(
|
||||
BPoint point);
|
||||
|
||||
public: // *** derived from DiagramItemGroup
|
||||
|
||||
// extends the DiagramItemGroup implementation by setting
|
||||
// the items owner and calling the attachedToDiagram() hook
|
||||
// on it
|
||||
virtual bool addItem(
|
||||
DiagramItem *item);
|
||||
|
||||
// extends the DiagramItemGroup implementation by calling
|
||||
// the detachedToDiagram() hook on the item
|
||||
virtual bool removeItem(
|
||||
DiagramItem *item);
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// update the temporary wire to follow the mouse cursor
|
||||
void trackWire(
|
||||
BPoint point);
|
||||
|
||||
bool isWireTracking() const
|
||||
{ return m_draggedWire; }
|
||||
|
||||
protected: // *** internal operations
|
||||
|
||||
// do the actual background drawing
|
||||
void drawBackground(
|
||||
BRect updateRect);
|
||||
|
||||
// returns the current background color
|
||||
rgb_color backgroundColor() const
|
||||
{ return m_backgroundColor; }
|
||||
|
||||
// set the background color; does not refresh the display
|
||||
void setBackgroundColor(
|
||||
rgb_color color);
|
||||
|
||||
// set the background bitmap; does not refresh the display
|
||||
void setBackgroundBitmap(
|
||||
BBitmap *bitmap);
|
||||
|
||||
// updates the region containing the rects of all boxes in
|
||||
// the view (and thereby the "data-rect") and then adapts
|
||||
// the scrollbars if necessary
|
||||
void updateDataRect();
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// setup a temporary wire for "live" dragging and attaches
|
||||
// a message to the mouse
|
||||
void _beginWireTracking(
|
||||
DiagramEndPoint *fromEndPoint);
|
||||
|
||||
// delete the temporary dragged wire and invalidate display
|
||||
void _endWireTracking();
|
||||
|
||||
// setups rect-tracking to additionally drag a message for
|
||||
// easier identification in MouseMoved()
|
||||
void _beginRectTracking(
|
||||
BPoint origin);
|
||||
|
||||
// takes care of actually selecting/deselecting boxes when
|
||||
// they intersect with the tracked rect
|
||||
void _trackRect(
|
||||
BPoint origin,
|
||||
BPoint current);
|
||||
|
||||
// updates the scrollbars (if there are any) to represent
|
||||
// the current data-rect
|
||||
void _updateScrollBars();
|
||||
|
||||
private: // *** data
|
||||
|
||||
// the button pressed at the last mouse event
|
||||
int32 m_lastButton;
|
||||
|
||||
// the number of clicks with the last button
|
||||
int32 m_clickCount;
|
||||
|
||||
// the point last clicked in this view
|
||||
BPoint m_lastClickPoint;
|
||||
|
||||
// the button currently pressed (reset to 0 on mouse-up)
|
||||
// [e.moon 16nov99]
|
||||
int32 m_pressedButton;
|
||||
|
||||
// last mouse position in screen coordinates [e.moon 16nov99]
|
||||
// only valid while m_pressedButton != 0
|
||||
BPoint m_lastDragPoint;
|
||||
|
||||
// a pointer to the temporary wire used for wire
|
||||
// tracking
|
||||
DiagramWire *m_draggedWire;
|
||||
|
||||
// contains the rects of all DiagramBoxes in this view
|
||||
BRegion m_boxRegion;
|
||||
|
||||
// contains the rect of the view actually containing something
|
||||
// (i.e. DiagramBoxes) and all free space left/above of that
|
||||
BRect m_dataRect;
|
||||
|
||||
// true if a bitmap is used for the background; false
|
||||
// if a color is used
|
||||
bool m_useBackgroundBitmap;
|
||||
|
||||
// the background color of the view
|
||||
rgb_color m_backgroundColor;
|
||||
|
||||
// the background bitmap of the view
|
||||
BBitmap *m_backgroundBitmap;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif // __DiagramView_H__
|
186
src/apps/cortex/DiagramView/DiagramWire.cpp
Normal file
186
src/apps/cortex/DiagramView/DiagramWire.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
// DiagramWire.cpp
|
||||
|
||||
#include "DiagramWire.h"
|
||||
#include "DiagramDefs.h"
|
||||
#include "DiagramEndPoint.h"
|
||||
#include "DiagramView.h"
|
||||
|
||||
#include <Message.h>
|
||||
#include <Messenger.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DiagramWire::DiagramWire(
|
||||
DiagramEndPoint *fromWhich,
|
||||
DiagramEndPoint *toWhich)
|
||||
: DiagramItem(DiagramItem::M_WIRE),
|
||||
m_fromEndPoint(fromWhich),
|
||||
m_toEndPoint(toWhich),
|
||||
m_temporary(false)
|
||||
{
|
||||
D_METHOD(("DiagramWire::DiagramWire()\n"));
|
||||
makeDraggable(false);
|
||||
ASSERT(m_fromEndPoint);
|
||||
m_fromEndPoint->connect(this);
|
||||
ASSERT(m_toEndPoint);
|
||||
m_toEndPoint->connect(this);
|
||||
}
|
||||
|
||||
DiagramWire::DiagramWire(
|
||||
DiagramEndPoint *fromWhich,
|
||||
bool isStartPoint)
|
||||
: DiagramItem(DiagramItem::M_WIRE),
|
||||
m_fromEndPoint(isStartPoint ? fromWhich : 0),
|
||||
m_toEndPoint(isStartPoint ? 0 : fromWhich),
|
||||
m_temporary(true),
|
||||
m_dragEndPoint(fromWhich->connectionPoint())
|
||||
{
|
||||
D_METHOD(("DiagramWire::DiagramWire(temp)\n"));
|
||||
makeDraggable(false);
|
||||
ASSERT(fromWhich);
|
||||
if (m_fromEndPoint)
|
||||
{
|
||||
m_fromEndPoint->connect(this);
|
||||
}
|
||||
else if (m_toEndPoint)
|
||||
{
|
||||
m_toEndPoint->connect(this);
|
||||
}
|
||||
}
|
||||
|
||||
DiagramWire::~DiagramWire()
|
||||
{
|
||||
D_METHOD(("DiagramWire::~DiagramWire()\n"));
|
||||
if (m_fromEndPoint)
|
||||
{
|
||||
m_fromEndPoint->disconnect();
|
||||
}
|
||||
if (m_toEndPoint)
|
||||
{
|
||||
m_toEndPoint->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** accessors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
BPoint DiagramWire::startConnectionPoint() const
|
||||
{
|
||||
D_METHOD(("DiagramWire::startConnectionPoint()\n"));
|
||||
if (m_fromEndPoint)
|
||||
{
|
||||
return m_fromEndPoint->connectionPoint();
|
||||
}
|
||||
else if (m_temporary)
|
||||
{
|
||||
return m_dragEndPoint;
|
||||
}
|
||||
return BPoint(-1.0, -1.0);
|
||||
}
|
||||
|
||||
BPoint DiagramWire::endConnectionPoint() const
|
||||
{
|
||||
D_METHOD(("DiagramWire::endConnectionPoint()\n"));
|
||||
if (m_toEndPoint)
|
||||
{
|
||||
return m_toEndPoint->connectionPoint();
|
||||
}
|
||||
else if (m_temporary)
|
||||
{
|
||||
return m_dragEndPoint;
|
||||
}
|
||||
return BPoint(-1.0, -1.0);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from DiagramItem
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
float DiagramWire::howCloseTo(
|
||||
BPoint point) const
|
||||
{
|
||||
D_METHOD(("DiagramWire::howCloseTo()\n"));
|
||||
BPoint a = startConnectionPoint();
|
||||
BPoint b = endConnectionPoint();
|
||||
float length, result;
|
||||
length = sqrt(pow(b.x - a.x, 2) + pow(b.y - a.y, 2));
|
||||
result = ((a.y - point.y) * (b.x - a.x)) - ((a.x - point.x) * (b.y - a.y));
|
||||
result = 3.0 - fabs(result / length);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DiagramWire::draw(
|
||||
BRect updateRect)
|
||||
{
|
||||
D_METHOD(("DiagramWire::draw()\n"));
|
||||
if (view())
|
||||
{
|
||||
view()->PushState();
|
||||
{
|
||||
BRegion region, clipping;
|
||||
region.Include(frame() & updateRect);
|
||||
if (view()->getClippingAbove(this, &clipping))
|
||||
region.Exclude(&clipping);
|
||||
view()->ConstrainClippingRegion(®ion);
|
||||
drawWire();
|
||||
}
|
||||
view()->PopState();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramWire::mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks)
|
||||
{
|
||||
D_METHOD(("DiagramWire::mouseDown()\n"));
|
||||
if (clicks == 1)
|
||||
{
|
||||
if (isSelectable())
|
||||
{
|
||||
BMessage selectMsg(M_SELECTION_CHANGED);
|
||||
if (modifiers() & B_SHIFT_KEY)
|
||||
{
|
||||
selectMsg.AddBool("replace", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectMsg.AddBool("replace", true);
|
||||
}
|
||||
selectMsg.AddPointer("item", reinterpret_cast<void *>(this));
|
||||
DiagramView* v = view();
|
||||
BMessenger(v).SendMessage(&selectMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagramWire::messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message)
|
||||
{
|
||||
D_METHOD(("DiagramWire::messageDragged()\n"));
|
||||
switch (message->what)
|
||||
{
|
||||
case M_WIRE_DRAGGED:
|
||||
{
|
||||
view()->trackWire(point);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
DiagramItem::messageDragged(point, transit, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END -- DiagramWire.cpp --
|
140
src/apps/cortex/DiagramView/DiagramWire.h
Normal file
140
src/apps/cortex/DiagramView/DiagramWire.h
Normal file
@ -0,0 +1,140 @@
|
||||
// DiagramWire.h (Cortex/DiagramView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// DiagramItem subclass serving as a graphical connection
|
||||
// between two DiagramEndPoints
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 25sep99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DiagramWire_H__
|
||||
#define __DiagramWire_H__
|
||||
|
||||
#include "DiagramItem.h"
|
||||
|
||||
#include <Region.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DiagramView;
|
||||
class DiagramEndPoint;
|
||||
|
||||
class DiagramWire : public DiagramItem
|
||||
{
|
||||
friend DiagramView;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// both endpoints are set connected by this constructor
|
||||
// so be careful to only pass in valid pointers
|
||||
DiagramWire(
|
||||
DiagramEndPoint *fromWhich,
|
||||
DiagramEndPoint *toWhich);
|
||||
|
||||
// special constructor used only in drag&drop sessions for
|
||||
// temporary visual indication of the connection process
|
||||
// the isStartPoint lets you specify if the given EndPoint
|
||||
// is supposed to be treated as start or end point
|
||||
DiagramWire(
|
||||
DiagramEndPoint *fromWhich,
|
||||
bool isStartPoint = true);
|
||||
|
||||
virtual ~DiagramWire();
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
bool isDragging() const
|
||||
{ return m_temporary; }
|
||||
|
||||
// returns a pointer to the "start/source" endpoint
|
||||
DiagramEndPoint *startPoint() const
|
||||
{ return m_fromEndPoint; }
|
||||
|
||||
// returns the point from the m_fromEndPoints connectionPoint() method
|
||||
BPoint startConnectionPoint() const;
|
||||
|
||||
// returns a pointer the "end/target" endpoint
|
||||
DiagramEndPoint *endPoint() const
|
||||
{ return m_toEndPoint; }
|
||||
|
||||
// returns the point from the m_toEndPoints connectionPoint() method
|
||||
BPoint endConnectionPoint() const;
|
||||
|
||||
public: // *** hook functions
|
||||
|
||||
// is called from draw() to do the actual drawing
|
||||
virtual void drawWire() = 0;
|
||||
|
||||
// is called by the connected diagramEndPoints whenever one is moved
|
||||
// if the argument is NULL then both endpoints should be evaluated
|
||||
virtual void endPointMoved(
|
||||
DiagramEndPoint *which = 0)
|
||||
{ /* does nothing */ }
|
||||
|
||||
public: // *** derived from DiagramItem
|
||||
|
||||
// returns the area in which the wire displays
|
||||
virtual BRect frame() const
|
||||
{ return BRect(startConnectionPoint(), endConnectionPoint()); }
|
||||
|
||||
// returns how close a given point is to the wire; the current
|
||||
// implementation returns a wide array of values, those above
|
||||
// approx. 0.5 are quite close to the wire
|
||||
float howCloseTo(
|
||||
BPoint point) const;
|
||||
|
||||
// prepares the drawing stack and clipping region, then
|
||||
// calls drawWire
|
||||
void draw(
|
||||
BRect updateRect);
|
||||
|
||||
// is called from the parent DiagramViews MouseDown() implementation
|
||||
// if the Wire was hit; this version initiates selection and dragging
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks);
|
||||
|
||||
// is called from the DiagramViews MouseMoved() when no message is being
|
||||
// dragged, but the mouse has moved above the wire
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit)
|
||||
{ /* does nothing */ }
|
||||
|
||||
|
||||
// is called from the DiagramViews MouseMoved() when a message is being
|
||||
// dragged over the DiagramWire
|
||||
virtual void messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message);
|
||||
|
||||
// is called from the DiagramViews MessageReceived() function when a
|
||||
// message has been dropped on the DiagramWire
|
||||
virtual void messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message)
|
||||
{ /* does nothing */ }
|
||||
|
||||
private: // *** data
|
||||
|
||||
// pointer to the "from" EndPoint as assigned in the ctor
|
||||
DiagramEndPoint *m_fromEndPoint;
|
||||
|
||||
// pointer to the "to" EndPoint as assigned in the ctor
|
||||
DiagramEndPoint *m_toEndPoint;
|
||||
|
||||
// indicates that this is a connection used in a drag&drop session
|
||||
// and will be deleted after that
|
||||
bool m_temporary;
|
||||
|
||||
// contains a BPoint for "temporary connections"
|
||||
BPoint m_dragEndPoint;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif // __DiagramWire_H__
|
293
src/apps/cortex/DormantNodeView/DormantNodeListItem.cpp
Normal file
293
src/apps/cortex/DormantNodeView/DormantNodeListItem.cpp
Normal file
@ -0,0 +1,293 @@
|
||||
// DormantNodeListItem.cpp
|
||||
|
||||
#include "DormantNodeListItem.h"
|
||||
// InfoWindow
|
||||
#include "InfoWindowManager.h"
|
||||
// Support
|
||||
#include "MediaIcon.h"
|
||||
// TipManager
|
||||
#include "TipManager.h"
|
||||
|
||||
// Application Kit
|
||||
#include <Application.h>
|
||||
// Interface Kit
|
||||
#include <PopUpMenu.h>
|
||||
#include <MenuItem.h>
|
||||
// Media Kit
|
||||
#include <MediaRoster.h>
|
||||
#include <MediaAddOn.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ALLOC(x) //PRINT (x) // ctor/dtor
|
||||
#define D_HOOK(x) //PRINT (x) // BListItem impl.
|
||||
#define D_OPERATION(x) //PRINT (x) // operations
|
||||
#define D_COMPARE(x) //PRINT (x) // compare functions
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// constants
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const float DormantNodeListItem::M_ICON_H_MARGIN = 3.0;
|
||||
const float DormantNodeListItem::M_ICON_V_MARGIN = 1.0;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DormantNodeListItem::DormantNodeListItem(
|
||||
const dormant_node_info &nodeInfo)
|
||||
: m_info(nodeInfo),
|
||||
m_icon(0) {
|
||||
D_ALLOC(("DormantNodeListItem::DormantNodeListItem()\n"));
|
||||
|
||||
m_icon = new MediaIcon(nodeInfo, B_MINI_ICON);
|
||||
}
|
||||
|
||||
DormantNodeListItem::~DormantNodeListItem() {
|
||||
D_ALLOC(("DormantNodeListItem::~DormantNodeListItem()\n"));
|
||||
|
||||
delete m_icon;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// BListItem impl.
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DormantNodeListItem::DrawItem(
|
||||
BView *owner,
|
||||
BRect frame,
|
||||
bool drawEverything) {
|
||||
D_HOOK(("DormantNodeListItem::DrawItem()\n"));
|
||||
|
||||
// Draw icon
|
||||
if (m_icon) {
|
||||
BRect r = frame;
|
||||
r.left += M_ICON_H_MARGIN;
|
||||
r.top += (frame.Height() / 2.0) - (B_MINI_ICON / 2.0);
|
||||
r.right = r.left + B_MINI_ICON - 1.0;
|
||||
r.bottom = r.top + B_MINI_ICON - 1.0;
|
||||
if (IsSelected()) {
|
||||
owner->SetHighColor(255.0, 255.0, 255.0, 255.0);
|
||||
owner->FillRect(r);
|
||||
owner->SetDrawingMode(B_OP_INVERT);
|
||||
owner->DrawBitmap(m_icon, r.LeftTop());
|
||||
owner->SetDrawingMode(B_OP_ALPHA);
|
||||
owner->SetHighColor(0.0, 0.0, 0.0, 180.0);
|
||||
owner->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
|
||||
owner->DrawBitmap(m_icon, r.LeftTop());
|
||||
owner->SetDrawingMode(B_OP_OVER);
|
||||
}
|
||||
else {
|
||||
owner->SetDrawingMode(B_OP_OVER);
|
||||
owner->DrawBitmap(m_icon, r.LeftTop());
|
||||
}
|
||||
}
|
||||
|
||||
// Draw label
|
||||
BRect r = frame;
|
||||
r.left += 2 * M_ICON_H_MARGIN + B_MINI_ICON;
|
||||
r.top += (frame.Height() / 2.0) - (m_fontHeight.ascent / 2.0);
|
||||
r.right = r.left + Width();
|
||||
r.bottom = r.top + m_fontHeight.ascent + m_fontHeight.descent;
|
||||
if (IsSelected() || drawEverything) {
|
||||
if (IsSelected()) {
|
||||
owner->SetHighColor(16.0, 64.0, 96.0, 255.0);
|
||||
}
|
||||
else {
|
||||
owner->SetHighColor(owner->ViewColor());
|
||||
}
|
||||
owner->FillRect(r);
|
||||
}
|
||||
if (IsSelected()) {
|
||||
owner->SetHighColor(255.0, 255.0, 255.0, 255.0);
|
||||
}
|
||||
else {
|
||||
owner->SetHighColor(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
BPoint labelOffset(r.left + 1.0, r.bottom - m_fontHeight.descent);
|
||||
owner->DrawString(m_info.name, labelOffset);
|
||||
|
||||
// cache the frame rect
|
||||
m_frame = frame;
|
||||
}
|
||||
|
||||
void DormantNodeListItem::Update(
|
||||
BView *owner,
|
||||
const BFont *font) {
|
||||
D_HOOK(("DormantNodeListItem::Update()\n"));
|
||||
BListItem::Update(owner, font);
|
||||
|
||||
SetWidth(font->StringWidth(m_info.name));
|
||||
owner->GetFontHeight(&m_fontHeight);
|
||||
float newHeight = m_fontHeight.ascent + m_fontHeight.descent + m_fontHeight.leading;
|
||||
if (newHeight < B_MINI_ICON) {
|
||||
newHeight = B_MINI_ICON;
|
||||
}
|
||||
newHeight += 2 * M_ICON_V_MARGIN;
|
||||
if (Height() < newHeight) {
|
||||
SetHeight(newHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// BListItem impl.
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DormantNodeListItem::mouseOver(
|
||||
BView *owner,
|
||||
BPoint point,
|
||||
uint32 transit) {
|
||||
D_OPERATION(("DormantNodeListItem::mouseOver()\n"));
|
||||
|
||||
switch (transit) {
|
||||
case B_ENTERED_VIEW: {
|
||||
TipManager *tips = TipManager::Instance();
|
||||
dormant_flavor_info flavorInfo;
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (roster && roster->GetDormantFlavorInfoFor(info(), &flavorInfo) == B_OK) {
|
||||
BString tip = flavorInfo.info;
|
||||
if (tip == "") {
|
||||
tip = m_info.name;
|
||||
}
|
||||
tips->showTip(tip.String(), owner->ConvertToScreen(m_frame));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case B_EXITED_VIEW: {
|
||||
TipManager *tips = TipManager::Instance();
|
||||
tips->hideTip(owner->ConvertToScreen(m_frame));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BRect DormantNodeListItem::getRealFrame(
|
||||
const BFont *font) const {
|
||||
D_OPERATION(("DormantNodeListItem::getRealFrame()\n"));
|
||||
|
||||
BRect r(0.0, 0.0, -1.0, -1.0);
|
||||
if (m_frame.IsValid()) {
|
||||
r = m_frame;
|
||||
r.right = r.left + B_MINI_ICON + 2 * M_ICON_H_MARGIN;
|
||||
r.right += font->StringWidth(m_info.name);
|
||||
}
|
||||
else {
|
||||
r.right = font->StringWidth(m_info.name);
|
||||
r.right += B_MINI_ICON + 2 * M_ICON_H_MARGIN + 5.0;
|
||||
font_height fh;
|
||||
font->GetHeight(&fh);
|
||||
r.bottom = fh.ascent + fh.descent + fh.leading;
|
||||
if (r.bottom < B_MINI_ICON) {
|
||||
r.bottom = B_MINI_ICON;
|
||||
}
|
||||
r.bottom += 2 * M_ICON_V_MARGIN;
|
||||
if (Height() > r.bottom) {
|
||||
r.bottom = Height();
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
BBitmap *DormantNodeListItem::getDragBitmap() {
|
||||
D_OPERATION(("DormantNodeListItem::getDragBitmap()\n"));
|
||||
|
||||
// Prepare Bitmap for Drag & Drop
|
||||
BBitmap *dragBitmap = new BBitmap(m_frame.OffsetToCopy(BPoint(0.0, 0.0)), B_RGBA32, true);
|
||||
dragBitmap->Lock(); {
|
||||
BView *dragView = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
|
||||
dragBitmap->AddChild(dragView);
|
||||
dragView->SetOrigin(0.0, 0.0);
|
||||
dragView->SetHighColor(0.0, 0.0, 0.0, 0.0);
|
||||
dragView->FillRect(dragView->Bounds());
|
||||
dragView->SetDrawingMode(B_OP_ALPHA);
|
||||
dragView->SetHighColor(0.0, 0.0, 0.0, 128.0);
|
||||
dragView->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
|
||||
// Drawing code back again
|
||||
if (m_icon) {
|
||||
BRect r = dragView->Bounds();
|
||||
r.left += M_ICON_H_MARGIN;
|
||||
r.top += (m_frame.Height() / 2.0) - (B_MINI_ICON / 2.0);
|
||||
r.right = r.left + B_MINI_ICON - 1.0;
|
||||
r.bottom = r.top + B_MINI_ICON - 1.0;
|
||||
dragView->DrawBitmap(m_icon, r.LeftTop());
|
||||
}
|
||||
BRect r = dragView->Bounds();
|
||||
r.left += 2 * M_ICON_H_MARGIN + B_MINI_ICON;
|
||||
r.top += (m_frame.Height() / 2.0) - (m_fontHeight.ascent / 2.0);
|
||||
r.right = r.left + Width();
|
||||
r.bottom = r.top + m_fontHeight.ascent + m_fontHeight.descent;
|
||||
BPoint labelOffset(r.left + 1.0, r.bottom - m_fontHeight.descent);
|
||||
dragView->DrawString(m_info.name, labelOffset);
|
||||
dragView->Sync();
|
||||
}
|
||||
dragBitmap->Unlock();
|
||||
return dragBitmap;
|
||||
}
|
||||
|
||||
void DormantNodeListItem::showContextMenu(
|
||||
BPoint point,
|
||||
BView *owner) {
|
||||
D_OPERATION(("DormantNodeListItem::showContextMenu()\n"));
|
||||
|
||||
BPopUpMenu *menu = new BPopUpMenu("DormantNodeListItem PopUp", false, false, B_ITEMS_IN_COLUMN);
|
||||
menu->SetFont(be_plain_font);
|
||||
|
||||
// Add the "Get Info" item
|
||||
BMessage *message = new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED);
|
||||
menu->AddItem(new BMenuItem("Get Info", message));
|
||||
|
||||
menu->SetTargetForItems(owner);
|
||||
owner->ConvertToScreen(&point);
|
||||
point -= BPoint(1.0, 1.0);
|
||||
menu->Go(point, true, true, true);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** compare functions (friend)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
int __CORTEX_NAMESPACE__ compareName(
|
||||
const void *lValue,
|
||||
const void *rValue) {
|
||||
D_COMPARE(("compareSelectionTime()\n"));
|
||||
|
||||
int returnValue = 0;
|
||||
const DormantNodeListItem *lItem = *(reinterpret_cast<const DormantNodeListItem* const*>
|
||||
(reinterpret_cast<const void* const*>(lValue)));
|
||||
const DormantNodeListItem *rItem = *(reinterpret_cast<const DormantNodeListItem* const*>
|
||||
(reinterpret_cast<const void* const*>(rValue)));
|
||||
BString lString = lItem->info().name;
|
||||
lString.ToUpper();
|
||||
BString rString = rItem->info().name;
|
||||
rString.ToUpper();
|
||||
if (lString < rString) {
|
||||
returnValue = -1;
|
||||
}
|
||||
else if (lString > rString) {
|
||||
returnValue = 1;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
int __CORTEX_NAMESPACE__ compareAddOnID(
|
||||
const void *lValue,
|
||||
const void *rValue) {
|
||||
D_COMPARE(("compareAddOnID()\n"));
|
||||
|
||||
int returnValue = 0;
|
||||
const DormantNodeListItem *lItem = *(reinterpret_cast<const DormantNodeListItem* const*>
|
||||
(reinterpret_cast<const void* const*>(lValue)));
|
||||
const DormantNodeListItem *rItem = *(reinterpret_cast<const DormantNodeListItem* const*>
|
||||
(reinterpret_cast<const void* const*>(rValue)));
|
||||
if (lItem->info().addon < rItem->info().addon) {
|
||||
returnValue = -1;
|
||||
}
|
||||
else if (lItem->info().addon > rItem->info().addon) {
|
||||
returnValue = 1;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// END -- DormantNodeListItem.cpp --
|
100
src/apps/cortex/DormantNodeView/DormantNodeListItem.h
Normal file
100
src/apps/cortex/DormantNodeView/DormantNodeListItem.h
Normal file
@ -0,0 +1,100 @@
|
||||
// DormantNodeListItem.h
|
||||
// e.moon 2jun99
|
||||
//
|
||||
// * PURPOSE
|
||||
// - represent a single dormant (add-on) media node in a
|
||||
// list view.
|
||||
//
|
||||
// * HISTORY
|
||||
// 22oct99 c.lenz complete rewrite
|
||||
// 27oct99 c.lenz added tooltip support
|
||||
|
||||
#ifndef __DormantNodeListItem_H__
|
||||
#define __DormantNodeListItem_H__
|
||||
|
||||
// Interface Kit
|
||||
#include <Bitmap.h>
|
||||
#include <Font.h>
|
||||
#include <ListItem.h>
|
||||
// Media Kit
|
||||
#include <MediaAddOn.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DormantNodeView;
|
||||
class MediaIcon;
|
||||
|
||||
class DormantNodeListItem :
|
||||
public BListItem {
|
||||
typedef BListItem _inherited;
|
||||
|
||||
public: // *** constants
|
||||
|
||||
// [e.moon 26oct99] moved definition to DormantNodeListItem.cpp
|
||||
// to appease the Metrowerks compiler.
|
||||
static const float M_ICON_H_MARGIN;
|
||||
static const float M_ICON_V_MARGIN;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DormantNodeListItem(
|
||||
const dormant_node_info &nodeInfo);
|
||||
|
||||
virtual ~DormantNodeListItem();
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
const dormant_node_info &info() const
|
||||
{ return m_info; }
|
||||
|
||||
public: // *** operations
|
||||
|
||||
void mouseOver(
|
||||
BView *owner,
|
||||
BPoint point,
|
||||
uint32 transit);
|
||||
|
||||
BRect getRealFrame(
|
||||
const BFont *font) const;
|
||||
|
||||
BBitmap *getDragBitmap();
|
||||
|
||||
void showContextMenu(
|
||||
BPoint point,
|
||||
BView *owner);
|
||||
|
||||
public: // *** BListItem impl.
|
||||
|
||||
virtual void DrawItem(
|
||||
BView *owner,
|
||||
BRect frame,
|
||||
bool drawEverything = false);
|
||||
|
||||
virtual void Update(
|
||||
BView *owner,
|
||||
const BFont *fFont);
|
||||
|
||||
protected: // *** compare functions
|
||||
|
||||
friend int compareName(
|
||||
const void *lValue,
|
||||
const void *rValue);
|
||||
|
||||
friend int compareAddOnID(
|
||||
const void *lValue,
|
||||
const void *rValue);
|
||||
|
||||
private: // *** data
|
||||
|
||||
dormant_node_info m_info;
|
||||
|
||||
BRect m_frame;
|
||||
|
||||
font_height m_fontHeight;
|
||||
|
||||
MediaIcon *m_icon;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__DormantNodeListItem_H__*/
|
321
src/apps/cortex/DormantNodeView/DormantNodeView.cpp
Normal file
321
src/apps/cortex/DormantNodeView/DormantNodeView.cpp
Normal file
@ -0,0 +1,321 @@
|
||||
// DormantNodeView.cpp
|
||||
|
||||
#include "DormantNodeView.h"
|
||||
// DormantNodeView
|
||||
#include "DormantNodeWindow.h"
|
||||
#include "DormantNodeListItem.h"
|
||||
// InfoWindow
|
||||
#include "InfoWindowManager.h"
|
||||
|
||||
// Interface Kit
|
||||
#include <Deskbar.h>
|
||||
#include <Region.h>
|
||||
#include <Screen.h>
|
||||
#include <ScrollBar.h>
|
||||
// Media Kit
|
||||
#include <MediaRoster.h>
|
||||
// Storage Kit
|
||||
#include <Mime.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ALLOC(x) //PRINT (x) // ctor/dtor
|
||||
#define D_HOOK(x) //PRINT (x) // BListView impl.
|
||||
#define D_MESSAGE(x) //PRINT (x) // MessageReceived()
|
||||
#define D_INTERNAL(x) //PRINT (x) // internal operations
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DormantNodeView::DormantNodeView(
|
||||
BRect frame,
|
||||
const char *name,
|
||||
uint32 resizeMode)
|
||||
: BListView(frame, name, B_SINGLE_SELECTION_LIST, resizeMode),
|
||||
m_lastItemUnder(0) {
|
||||
D_ALLOC(("DormantNodeView::DormantNodeView()\n"));
|
||||
|
||||
}
|
||||
|
||||
DormantNodeView::~DormantNodeView() {
|
||||
D_ALLOC(("DormantNodeView::~DormantNodeView()\n"));
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// BListView impl. (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DormantNodeView::AttachedToWindow() {
|
||||
D_HOOK(("DormantNodeView::AttachedToWindow()\n"));
|
||||
|
||||
// populate the list
|
||||
_populateList();
|
||||
|
||||
// Start watching the MediaRoster for flavor changes
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (roster) {
|
||||
BMessenger messenger(this, Window());
|
||||
roster->StartWatching(messenger, B_MEDIA_FLAVORS_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
void DormantNodeView::DetachedFromWindow() {
|
||||
D_HOOK(("DormantNodeView::DetachedFromWindow()\n"));
|
||||
|
||||
// delete the lists contents
|
||||
_freeList();
|
||||
|
||||
// Stop watching the MediaRoster for flavor changes
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (roster) {
|
||||
BMessenger messenger(this, Window());
|
||||
roster->StopWatching(messenger, B_MEDIA_FLAVORS_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
void DormantNodeView::GetPreferredSize(
|
||||
float* width,
|
||||
float* height) {
|
||||
D_HOOK(("DormantNodeView::GetPreferredSize()\n"));
|
||||
|
||||
// calculate the accumulated size of all list items
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
for (int32 i = 0; i < CountItems(); i++) {
|
||||
DormantNodeListItem *item;
|
||||
item = dynamic_cast<DormantNodeListItem *>(ItemAt(i));
|
||||
if (item) {
|
||||
BRect r = item->getRealFrame(be_plain_font);
|
||||
if (r.Width() > *width) {
|
||||
*width = r.Width();
|
||||
}
|
||||
*height += r.Height() + 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DormantNodeView::MessageReceived(
|
||||
BMessage *message) {
|
||||
D_MESSAGE(("DormantNodeView::MessageReceived()\n"));
|
||||
|
||||
switch (message->what) {
|
||||
case B_MEDIA_FLAVORS_CHANGED: {
|
||||
D_MESSAGE((" -> B_MEDIA_FLAVORS_CHANGED\n"));
|
||||
|
||||
// init & re-populate the list
|
||||
int32 addOnID = 0;
|
||||
if (message->FindInt32("be:addon_id", &addOnID) != B_OK) {
|
||||
D_MESSAGE((" -> messages doesn't contain 'be:addon_id'!\n"));
|
||||
return;
|
||||
}
|
||||
_updateList(addOnID);
|
||||
break;
|
||||
}
|
||||
case InfoWindowManager::M_INFO_WINDOW_REQUESTED: {
|
||||
D_MESSAGE((" -> InfoWindowManager::M_INFO_WINDOW_REQUESTED)\n"));
|
||||
|
||||
DormantNodeListItem *item;
|
||||
item = dynamic_cast<DormantNodeListItem *>(ItemAt(CurrentSelection()));
|
||||
if (item) {
|
||||
InfoWindowManager *manager = InfoWindowManager::Instance();
|
||||
if (manager && manager->Lock()) {
|
||||
manager->openWindowFor(item->info());
|
||||
manager->Unlock();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
_inherited::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DormantNodeView::MouseDown(
|
||||
BPoint point) {
|
||||
D_HOOK(("DormantNodeView::MouseDown()\n"));
|
||||
|
||||
BMessage* message = Window()->CurrentMessage();
|
||||
int32 buttons = message->FindInt32("buttons");
|
||||
if (buttons == B_SECONDARY_MOUSE_BUTTON) {
|
||||
int32 index;
|
||||
if ((index = IndexOf(point)) >= 0) {
|
||||
DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(index));
|
||||
if (item) {
|
||||
Select(index);
|
||||
BRect r = item->getRealFrame(be_plain_font);
|
||||
if (r.Contains(point)) {
|
||||
item->showContextMenu(point, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_inherited::MouseDown(point);
|
||||
}
|
||||
|
||||
void DormantNodeView::MouseMoved(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message) {
|
||||
D_HOOK(("DormantNodeView::MouseMoved()\n"));
|
||||
|
||||
int32 index;
|
||||
if (!message && ((index = IndexOf(point)) >= 0)) {
|
||||
DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(index));
|
||||
DormantNodeListItem *last = dynamic_cast<DormantNodeListItem *>(m_lastItemUnder);
|
||||
BRect r = item->getRealFrame(be_plain_font);
|
||||
if (item && r.Contains(point)) {
|
||||
if (item != last) {
|
||||
if (last)
|
||||
last->mouseOver(this, point, B_EXITED_VIEW);
|
||||
item->mouseOver(this, point, B_ENTERED_VIEW);
|
||||
m_lastItemUnder = item;
|
||||
}
|
||||
else {
|
||||
item->mouseOver(this, point, B_INSIDE_VIEW);
|
||||
}
|
||||
}
|
||||
else if (last) {
|
||||
last->mouseOver(this, point, B_EXITED_VIEW);
|
||||
}
|
||||
}
|
||||
|
||||
_inherited::MouseMoved(point, transit, message);
|
||||
}
|
||||
|
||||
bool DormantNodeView::InitiateDrag(
|
||||
BPoint point,
|
||||
int32 index,
|
||||
bool wasSelected) {
|
||||
D_HOOK(("DormantNodeView::InitiateDrag()\n"));
|
||||
|
||||
DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(CurrentSelection()));
|
||||
if (item) {
|
||||
BMessage dragMsg(M_INSTANTIATE_NODE);
|
||||
dragMsg.AddData("which", B_RAW_TYPE,
|
||||
reinterpret_cast<const void *>(&item->info()),
|
||||
sizeof(item->info()));
|
||||
point -= ItemFrame(index).LeftTop();
|
||||
DragMessage(&dragMsg, item->getDragBitmap(), B_OP_ALPHA, point);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal operations (private)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DormantNodeView::_populateList() {
|
||||
D_INTERNAL(("DormantNodeView::_populateList()\n"));
|
||||
|
||||
// init the resizable node-info buffer
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
const int32 bufferInc = 64;
|
||||
int32 bufferSize = bufferInc;
|
||||
dormant_node_info *infoBuffer = new dormant_node_info[bufferSize];
|
||||
int32 numNodes;
|
||||
|
||||
// fill the buffer
|
||||
while (true) {
|
||||
numNodes = bufferSize;
|
||||
status_t error = roster->GetDormantNodes(infoBuffer, &numNodes);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
if (numNodes < bufferSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
// reallocate buffer & try again
|
||||
delete [] infoBuffer;
|
||||
bufferSize += bufferInc;
|
||||
infoBuffer = new dormant_node_info[bufferSize];
|
||||
}
|
||||
|
||||
// populate the list
|
||||
for (int32 i = 0; i < numNodes; i++) {
|
||||
DormantNodeListItem *item = new DormantNodeListItem(infoBuffer[i]);
|
||||
AddItem(item);
|
||||
}
|
||||
SortItems(compareName);
|
||||
}
|
||||
|
||||
void DormantNodeView::_freeList() {
|
||||
D_HOOK(("DormantNodeView::_freeList()\n"));
|
||||
|
||||
// remove and delete all items in the list
|
||||
while (CountItems() > 0) {
|
||||
BListItem *item = ItemAt(0);
|
||||
if (RemoveItem(item)) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DormantNodeView::_updateList(
|
||||
int32 addOnID) {
|
||||
D_INTERNAL(("DormantNodeView::_updateList(%ld)\n", addOnID));
|
||||
|
||||
// init the resizable node-info buffer
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
const int32 bufferInc = 64;
|
||||
int32 bufferSize = bufferInc;
|
||||
dormant_node_info *infoBuffer = new dormant_node_info[bufferSize];
|
||||
int32 numNodes;
|
||||
|
||||
// fill the buffer
|
||||
while (true) {
|
||||
numNodes = bufferSize;
|
||||
status_t error = roster->GetDormantNodes(infoBuffer, &numNodes);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
if (numNodes < bufferSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
// reallocate buffer & try again
|
||||
delete [] infoBuffer;
|
||||
bufferSize += bufferInc;
|
||||
infoBuffer = new dormant_node_info[bufferSize];
|
||||
}
|
||||
|
||||
// sort the list by add-on id to avoid multiple searches through
|
||||
// the list
|
||||
SortItems(compareAddOnID);
|
||||
|
||||
// Remove all nodes managed by this add-on
|
||||
int32 start;
|
||||
for (start = 0; start < CountItems(); start++) {
|
||||
DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(start));
|
||||
if (item && (item->info().addon == addOnID)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int32 count = 0;
|
||||
for (int32 i = start; start < CountItems(); i++) {
|
||||
DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(i));
|
||||
if (!item || (item->info().addon != addOnID)) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
RemoveItems(start, count);
|
||||
|
||||
// add the items
|
||||
for (int32 i = 0; i < numNodes; i++) {
|
||||
if (infoBuffer[i].addon != addOnID) {
|
||||
continue;
|
||||
}
|
||||
AddItem(new DormantNodeListItem(infoBuffer[i]));
|
||||
}
|
||||
|
||||
SortItems(compareName);
|
||||
}
|
||||
|
||||
// END -- DormantNodeView.cpp --
|
83
src/apps/cortex/DormantNodeView/DormantNodeView.h
Normal file
83
src/apps/cortex/DormantNodeView/DormantNodeView.h
Normal file
@ -0,0 +1,83 @@
|
||||
// DormantNodeView.h
|
||||
// c.lenz 22oct99
|
||||
//
|
||||
// RESPONSIBILITIES
|
||||
// - simple extension of BListView to support
|
||||
// drag & drop
|
||||
//
|
||||
// HISTORY
|
||||
// c.lenz 22oct99 Begun
|
||||
// c.lenz 27oct99 Added ToolTip support
|
||||
|
||||
#ifndef __DormantNodeView_H__
|
||||
#define __DormantNodeView_H__
|
||||
|
||||
// Interface Kit
|
||||
#include <ListView.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DormantNodeView :
|
||||
public BListView {
|
||||
typedef BListView _inherited;
|
||||
|
||||
public: // *** messages
|
||||
|
||||
enum message_t {
|
||||
// OUTBOUND:
|
||||
// B_RAW_TYPE "which" dormant_node_info
|
||||
M_INSTANTIATE_NODE = 'dNV0'
|
||||
};
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DormantNodeView(
|
||||
BRect frame,
|
||||
const char *name,
|
||||
uint32 resizeMode);
|
||||
|
||||
virtual ~DormantNodeView();
|
||||
|
||||
public: // *** BListView impl.
|
||||
|
||||
virtual void AttachedToWindow();
|
||||
|
||||
virtual void DetachedFromWindow();
|
||||
|
||||
virtual void GetPreferredSize(
|
||||
float* width,
|
||||
float* height);
|
||||
|
||||
virtual void MessageReceived(
|
||||
BMessage *message);
|
||||
|
||||
virtual void MouseDown(
|
||||
BPoint point);
|
||||
|
||||
virtual void MouseMoved(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message);
|
||||
|
||||
virtual bool InitiateDrag(
|
||||
BPoint point,
|
||||
int32 index,
|
||||
bool wasSelected);
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
void _populateList();
|
||||
|
||||
void _freeList();
|
||||
|
||||
void _updateList(
|
||||
int32 addOnID);
|
||||
|
||||
private: // *** data
|
||||
|
||||
BListItem *m_lastItemUnder;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__DormantNodeView_H__*/
|
154
src/apps/cortex/DormantNodeView/DormantNodeWindow.cpp
Normal file
154
src/apps/cortex/DormantNodeView/DormantNodeWindow.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
// DormantNodeWindow.cpp
|
||||
// e.moon 2jun99
|
||||
|
||||
#include "DormantNodeWindow.h"
|
||||
// DormantNodeView
|
||||
#include "DormantNodeView.h"
|
||||
|
||||
#include "RouteWindow.h"
|
||||
|
||||
// Application Kit
|
||||
#include <Application.h>
|
||||
// Interface Kit
|
||||
#include <Screen.h>
|
||||
#include <ScrollBar.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ALLOC(x) //PRINT (x) // ctor/dtor
|
||||
#define D_HOOK(x) //PRINT (x) // BWindow impl.
|
||||
#define D_MESSAGE(x) //PRINT (x) // MessageReceived()
|
||||
#define D_INTERNAL(x) //PRINT (x) // internal operations
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// constants
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// this should be a bit more sophisticated :)
|
||||
const BRect DormantNodeWindow::s_initFrame(500.0, 350.0, 640.0, 480.0);
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DormantNodeWindow::DormantNodeWindow(
|
||||
BWindow* parent)
|
||||
: BWindow(s_initFrame, "Media Add-Ons",
|
||||
B_FLOATING_WINDOW_LOOK,
|
||||
B_FLOATING_SUBSET_WINDOW_FEEL,
|
||||
B_WILL_ACCEPT_FIRST_CLICK|B_AVOID_FOCUS|B_ASYNCHRONOUS_CONTROLS),
|
||||
m_parent(parent),
|
||||
m_zoomed(false),
|
||||
m_zooming(false) {
|
||||
D_ALLOC(("DormantNodeWindow::DormantNodeWindow()\n"));
|
||||
|
||||
ASSERT(m_parent);
|
||||
AddToSubset(m_parent);
|
||||
|
||||
// Create the ListView
|
||||
BRect r = Bounds();
|
||||
r.right -= B_V_SCROLL_BAR_WIDTH;
|
||||
m_listView = new DormantNodeView(r, "Dormant Node ListView", B_FOLLOW_ALL_SIDES);
|
||||
|
||||
// Add the vertical ScrollBar
|
||||
r.left = r.right + 1.0;
|
||||
r.right = r.left + B_V_SCROLL_BAR_WIDTH;
|
||||
r.InsetBy(0.0, -1.0);
|
||||
BScrollBar *scrollBar;
|
||||
AddChild(scrollBar = new BScrollBar(r, "", m_listView, 0.0, 0.0, B_VERTICAL));
|
||||
|
||||
// Add the ListView
|
||||
AddChild(m_listView);
|
||||
_constrainToScreen();
|
||||
}
|
||||
|
||||
DormantNodeWindow::~DormantNodeWindow() {
|
||||
D_ALLOC(("DormantNodeWindow::~DormantNodeWindow()\n"));
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// BWindow impl.
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool DormantNodeWindow::QuitRequested() {
|
||||
D_HOOK(("DormantNodeWindow::QuitRequested()\n"));
|
||||
|
||||
// [e.moon 29nov99] the RouteWindow is now responsible for
|
||||
// closing me
|
||||
m_parent->PostMessage(RouteWindow::M_TOGGLE_DORMANT_NODE_WINDOW);
|
||||
return false;
|
||||
}
|
||||
|
||||
void DormantNodeWindow::Zoom(
|
||||
BPoint origin,
|
||||
float width,
|
||||
float height) {
|
||||
D_HOOK(("DormantNodeWindow::Zoom()\n"));
|
||||
|
||||
m_zooming = true;
|
||||
|
||||
BScreen screen(this);
|
||||
if (!screen.Frame().Contains(Frame())) {
|
||||
m_zoomed = false;
|
||||
}
|
||||
|
||||
if (!m_zoomed) {
|
||||
// resize to the ideal size
|
||||
m_manualSize = Bounds();
|
||||
m_listView->GetPreferredSize(&width, &height);
|
||||
ResizeTo(width + B_V_SCROLL_BAR_WIDTH, height);
|
||||
m_zoomed = true;
|
||||
_constrainToScreen();
|
||||
}
|
||||
else {
|
||||
// resize to the most recent manual size
|
||||
ResizeTo(m_manualSize.Width(), m_manualSize.Height());
|
||||
m_zoomed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DormantNodeWindow::_constrainToScreen() {
|
||||
D_INTERNAL(("DormantNodeWindow::_constrainToScreen()\n"));
|
||||
|
||||
BScreen screen(this);
|
||||
BRect screenRect = screen.Frame();
|
||||
BRect windowRect = Frame();
|
||||
|
||||
// if the window is outside the screen rect
|
||||
// move it to the default position
|
||||
if (!screenRect.Intersects(windowRect)) {
|
||||
windowRect.OffsetTo(screenRect.LeftTop());
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
|
||||
// if the window is larger than the screen rect
|
||||
// resize it to fit at each side
|
||||
if (!screenRect.Contains(windowRect)) {
|
||||
if (windowRect.left < screenRect.left) {
|
||||
windowRect.left = screenRect.left + 5.0;
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
if (windowRect.top < screenRect.top) {
|
||||
windowRect.top = screenRect.top + 5.0;
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
if (windowRect.right > screenRect.right) {
|
||||
windowRect.right = screenRect.right - 5.0;
|
||||
}
|
||||
if (windowRect.bottom > screenRect.bottom) {
|
||||
windowRect.bottom = screenRect.bottom - 5.0;
|
||||
}
|
||||
ResizeTo(windowRect.Width(), windowRect.Height());
|
||||
}
|
||||
}
|
||||
|
||||
// END -- DormantNodeWindow.cpp --
|
57
src/apps/cortex/DormantNodeView/DormantNodeWindow.h
Normal file
57
src/apps/cortex/DormantNodeView/DormantNodeWindow.h
Normal file
@ -0,0 +1,57 @@
|
||||
// DormantNodeWindow.h
|
||||
// e.moon 2jun99
|
||||
|
||||
#ifndef __DormantNodeWindow_H__
|
||||
#define __DormantNodeWindow_H__
|
||||
|
||||
// Interface Kit
|
||||
#include <Window.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class DormantNodeView;
|
||||
|
||||
class DormantNodeWindow :
|
||||
public BWindow {
|
||||
typedef BWindow _inherited;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
DormantNodeWindow(
|
||||
BWindow* parent);
|
||||
|
||||
virtual ~DormantNodeWindow();
|
||||
|
||||
public: // *** BWindow impl.
|
||||
|
||||
bool QuitRequested();
|
||||
|
||||
void Zoom(
|
||||
BPoint origin,
|
||||
float width,
|
||||
float height);
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
void _constrainToScreen();
|
||||
|
||||
private: // *** data
|
||||
|
||||
BWindow* m_parent;
|
||||
|
||||
DormantNodeView* m_listView;
|
||||
|
||||
BRect m_manualSize;
|
||||
|
||||
bool m_zoomed;
|
||||
|
||||
bool m_zooming;
|
||||
|
||||
private: // *** internal constants
|
||||
|
||||
static const BRect s_initFrame;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__DormantNodeWindow_H__*/
|
78
src/apps/cortex/InfoView/AppNodeInfoView.cpp
Normal file
78
src/apps/cortex/InfoView/AppNodeInfoView.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
// AppNodeInfoView.cpp
|
||||
|
||||
#include "AppNodeInfoView.h"
|
||||
// NodeManager
|
||||
#include "NodeRef.h"
|
||||
// Support
|
||||
#include "MediaIcon.h"
|
||||
#include "MediaString.h"
|
||||
|
||||
// Application Kit
|
||||
#include <Roster.h>
|
||||
// Media Kit
|
||||
#include <MediaNode.h>
|
||||
#include <MediaRoster.h>
|
||||
// Storage Kit
|
||||
#include <AppFileInfo.h>
|
||||
#include <Entry.h>
|
||||
#include <File.h>
|
||||
#include <Path.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
AppNodeInfoView::AppNodeInfoView(
|
||||
const NodeRef *ref)
|
||||
: LiveNodeInfoView(ref)
|
||||
{
|
||||
D_METHOD(("AppNodeInfoView::AppNodeInfoView()\n"));
|
||||
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" File Format ") + 2 * InfoView::M_H_MARGIN);
|
||||
setSubTitle("Application-Owned Node");
|
||||
|
||||
// add separator
|
||||
addField("", "");
|
||||
|
||||
port_info portInfo;
|
||||
app_info appInfo;
|
||||
|
||||
if ((get_port_info(ref->node().port, &portInfo) == B_OK)
|
||||
&& (be_roster->GetRunningAppInfo(portInfo.team, &appInfo) == B_OK))
|
||||
{
|
||||
BEntry appEntry(&appInfo.ref);
|
||||
char appName[B_FILE_NAME_LENGTH];
|
||||
if ((appEntry.InitCheck() == B_OK)
|
||||
&& (appEntry.GetName(appName) == B_OK))
|
||||
{
|
||||
addField("Application", appName);
|
||||
}
|
||||
BFile appFile(&appInfo.ref, B_READ_ONLY);
|
||||
if (appFile.InitCheck() == B_OK)
|
||||
{
|
||||
BAppFileInfo appFileInfo(&appFile);
|
||||
if (appFileInfo.InitCheck() == B_OK)
|
||||
{
|
||||
version_info appVersion;
|
||||
if (appFileInfo.GetVersionInfo(&appVersion, B_APP_VERSION_KIND) == B_OK)
|
||||
{
|
||||
addField("Version", appVersion.long_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
addField("Signature", appInfo.signature);
|
||||
}
|
||||
}
|
||||
|
||||
AppNodeInfoView::~AppNodeInfoView()
|
||||
{
|
||||
D_METHOD(("AppNodeInfoView::~AppNodeInfoView()\n"));
|
||||
}
|
||||
|
||||
// END -- AppNodeInfoView.cpp --
|
36
src/apps/cortex/InfoView/AppNodeInfoView.h
Normal file
36
src/apps/cortex/InfoView/AppNodeInfoView.h
Normal file
@ -0,0 +1,36 @@
|
||||
// AppNodeInfoView.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// In addition to the fields defined by its base class
|
||||
// LiveNodeInfoView, this class defines fields about
|
||||
// the application in which the node 'lives'
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 3dec99 Begun
|
||||
//
|
||||
|
||||
#ifndef __AppNodeInfoView_H__
|
||||
#define __AppNodeInfoView_H__
|
||||
|
||||
#include "LiveNodeInfoView.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeRef;
|
||||
|
||||
class AppNodeInfoView : public LiveNodeInfoView
|
||||
{
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// add app related fields to the view by looking at
|
||||
// the nodes port and querying the application roster
|
||||
AppNodeInfoView(
|
||||
const NodeRef *ref);
|
||||
|
||||
virtual ~AppNodeInfoView();
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __AppNodeInfoView_H__ */
|
213
src/apps/cortex/InfoView/ConnectionInfoView.cpp
Normal file
213
src/apps/cortex/InfoView/ConnectionInfoView.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
// ConnectionInfoView.cpp
|
||||
|
||||
#include "ConnectionInfoView.h"
|
||||
// InfoView
|
||||
#include "InfoWindowManager.h"
|
||||
// Support
|
||||
#include "MediaIcon.h"
|
||||
#include "MediaString.h"
|
||||
// NodeManager
|
||||
#include "Connection.h"
|
||||
|
||||
// MediaKit
|
||||
#include <MediaDefs.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
ConnectionInfoView::ConnectionInfoView(
|
||||
const Connection &connection)
|
||||
: InfoView("Connection", "", 0),
|
||||
m_source(connection.source()),
|
||||
m_destination(connection.destination())
|
||||
{
|
||||
D_METHOD(("ConnectionInfoView::ConnectionInfoView()\n"));
|
||||
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Destination ")
|
||||
+ 2 * InfoView::M_H_MARGIN);
|
||||
media_input input;
|
||||
media_output output;
|
||||
if (connection.getOutput(&output) == B_OK) {
|
||||
// add "Source" field
|
||||
BString s;
|
||||
s << output.name;
|
||||
if (s.Length() > 0)
|
||||
s << " ";
|
||||
s << "(" << MediaString::getStringFor(output.source) << ")";
|
||||
addField("Source", s);
|
||||
}
|
||||
if (connection.getInput(&input) == B_OK) {
|
||||
// add "Destination" field
|
||||
BString s;
|
||||
s << input.name;
|
||||
if (s.Length() > 0)
|
||||
s << " ";
|
||||
s << "(" << MediaString::getStringFor(input.destination) << ")";
|
||||
addField("Destination", s);
|
||||
}
|
||||
|
||||
// add a separator field
|
||||
addField("", "");
|
||||
|
||||
// add "Media Type" field
|
||||
addField("Media Type", MediaString::getStringFor(connection.format().type));
|
||||
|
||||
// add the format fields
|
||||
_addFormatFields(connection.format());
|
||||
}
|
||||
|
||||
ConnectionInfoView::~ConnectionInfoView()
|
||||
{
|
||||
D_METHOD(("ConnectionInfoView::~ConnectionInfoView()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations (private)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ConnectionInfoView::_addFormatFields(
|
||||
const media_format &format) {
|
||||
D_METHOD(("ConnectionInfoView::_addFormatFields()\n"));
|
||||
|
||||
switch (format.type) {
|
||||
case B_MEDIA_RAW_AUDIO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Sample Rate ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add "Format" field
|
||||
s = MediaString::forAudioFormat(format.u.raw_audio.format,
|
||||
format.u.raw_audio.valid_bits);
|
||||
addField("Format", s);
|
||||
// add "Sample Rate" field
|
||||
s = MediaString::forAudioFrameRate(format.u.raw_audio.frame_rate);
|
||||
addField("Sample Rate", s);
|
||||
// add "Channels" field
|
||||
s = MediaString::forAudioChannelCount(format.u.raw_audio.channel_count);
|
||||
addField("Channels", s);
|
||||
// add "Channel Mask" field
|
||||
s = MediaString::forAudioChannelMask(format.u.raw_audio.channel_mask);
|
||||
addField("Channel Mask", s);
|
||||
// add "Matrix Mask" field
|
||||
s = MediaString::forAudioMatrixMask(format.u.raw_audio.matrix_mask);
|
||||
addField("Matrix Mask", s);
|
||||
// add the "Byte Order" field
|
||||
s = MediaString::forAudioByteOrder(format.u.raw_audio.byte_order);
|
||||
addField("Byte Order", s);
|
||||
// add the "Buffer Size" field
|
||||
s = MediaString::forAudioBufferSize(format.u.raw_audio.buffer_size);
|
||||
addField("Buffer Size", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_RAW_VIDEO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Video Data Between ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Format" field
|
||||
s = MediaString::forVideoFormat(format.u.raw_video.display.format);
|
||||
addField("Format", s);
|
||||
// add the "Resolution" field
|
||||
s = MediaString::forVideoResolution(format.u.raw_video.display.line_width,
|
||||
format.u.raw_video.display.line_count);
|
||||
addField("Resolution", s);
|
||||
// add the "Field Rate" field
|
||||
s = MediaString::forVideoFieldRate(format.u.raw_video.field_rate,
|
||||
format.u.raw_video.interlace);
|
||||
addField("Field Rate", s);
|
||||
// add the "Orientation" field
|
||||
s = MediaString::forVideoOrientation(format.u.raw_video.orientation);
|
||||
addField("Orientation", s);
|
||||
// add the "Aspect Ratio" field
|
||||
s = MediaString::forVideoAspectRatio(format.u.raw_video.pixel_width_aspect,
|
||||
format.u.raw_video.pixel_height_aspect);
|
||||
addField("Aspect Ratio", s);
|
||||
// add the "Active Lines" field
|
||||
s = MediaString::forVideoActiveLines(format.u.raw_video.first_active,
|
||||
format.u.raw_video.last_active);
|
||||
addField("Active Lines", s);
|
||||
// add the "Offset" field
|
||||
s = MediaString::forVideoOffset(format.u.raw_video.display.pixel_offset,
|
||||
format.u.raw_video.display.line_offset);
|
||||
addField("Offset", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_ENCODED_AUDIO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Frame Size ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Bit Rate" field
|
||||
s = MediaString::forAudioBitRate(format.u.encoded_audio.bit_rate);
|
||||
addField("Bit Rate", s);
|
||||
// add the "Frame Size" field
|
||||
s = MediaString::forAudioFrameSize(format.u.encoded_audio.frame_size);
|
||||
addField("Frame Size", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_ENCODED_VIDEO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Frame Size ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Bit Rate" field
|
||||
s = MediaString::forVideoBitRate(format.u.encoded_video.avg_bit_rate,
|
||||
format.u.encoded_video.max_bit_rate);
|
||||
addField("Bit Rate", s);
|
||||
// add the "Frame Size" field
|
||||
s = MediaString::forVideoFrameSize(format.u.encoded_video.frame_size);
|
||||
addField("Frame Size", s);
|
||||
// add the "History" field
|
||||
s = MediaString::forVideoHistory(format.u.encoded_video.forward_history,
|
||||
format.u.encoded_video.backward_history);
|
||||
addField("History", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_MULTISTREAM: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Chunk Size ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Format" field
|
||||
s = MediaString::forMultistreamFormat(format.u.multistream.format);
|
||||
addField("Format", s);
|
||||
// add the "Bit Rate" field
|
||||
s = MediaString::forMultistreamBitRate(format.u.multistream.avg_bit_rate,
|
||||
format.u.multistream.max_bit_rate);
|
||||
addField("Bit Rate", s);
|
||||
// add the "Chunk Size" field
|
||||
s = MediaString::forMultistreamChunkSize(format.u.multistream.avg_chunk_size,
|
||||
format.u.multistream.max_chunk_size);
|
||||
addField("Chunk Size", s);
|
||||
// add the "Flags" field
|
||||
s = MediaString::forMultistreamFlags(format.u.multistream.flags);
|
||||
addField("Flags", s);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// add no fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BView implementation (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ConnectionInfoView::DetachedFromWindow() {
|
||||
D_METHOD(("ConnectionInfoView::DetachedFromWindow()\n"));
|
||||
|
||||
InfoWindowManager *manager = InfoWindowManager::Instance();
|
||||
if (manager) {
|
||||
BMessage message(InfoWindowManager::M_CONNECTION_WINDOW_CLOSED);
|
||||
message.AddInt32("source_port", m_source.port);
|
||||
message.AddInt32("source_id", m_source.id);
|
||||
message.AddInt32("destination_port", m_destination.port);
|
||||
message.AddInt32("destination_id", m_destination.id);
|
||||
manager->PostMessage(&message);
|
||||
}
|
||||
}
|
||||
|
||||
// END -- ConnectionInfoView.cpp --
|
54
src/apps/cortex/InfoView/ConnectionInfoView.h
Normal file
54
src/apps/cortex/InfoView/ConnectionInfoView.h
Normal file
@ -0,0 +1,54 @@
|
||||
// ConnectionInfoView.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Display connection-specific info in the InfoWindow.
|
||||
// This is: source, destination and format.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 5nov99 Begun
|
||||
//
|
||||
|
||||
#ifndef __ConnectionInfoView_H__
|
||||
#define __ConnectionInfoView_H__
|
||||
|
||||
#include "InfoView.h"
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
// NodeManager
|
||||
class Connection;
|
||||
|
||||
class ConnectionInfoView : public InfoView
|
||||
{
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
ConnectionInfoView(
|
||||
const Connection &connection);
|
||||
|
||||
virtual ~ConnectionInfoView();
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// adds media_format related fields to the view
|
||||
// (wildcard-aware)
|
||||
void _addFormatFields(
|
||||
const media_format &format);
|
||||
|
||||
public: // *** BView impl
|
||||
|
||||
// notify InfoWindowManager
|
||||
virtual void DetachedFromWindow();
|
||||
|
||||
private: // *** data members
|
||||
|
||||
media_source m_source;
|
||||
|
||||
media_destination m_destination;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __ConnectionInfoView_H__ */
|
137
src/apps/cortex/InfoView/DormantNodeInfoView.cpp
Normal file
137
src/apps/cortex/InfoView/DormantNodeInfoView.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
// DormantNodeInfoView.cpp
|
||||
|
||||
#include "DormantNodeInfoView.h"
|
||||
// InfoView
|
||||
#include "InfoWindowManager.h"
|
||||
// Support
|
||||
#include "MediaIcon.h"
|
||||
#include "MediaString.h"
|
||||
|
||||
// Media Kit
|
||||
#include <MediaAddOn.h>
|
||||
#include <MediaRoster.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DormantNodeInfoView::DormantNodeInfoView(
|
||||
const dormant_node_info &info)
|
||||
: InfoView(info.name, "Dormant Media Node",
|
||||
new MediaIcon(info, B_LARGE_ICON)),
|
||||
m_addOnID(info.addon),
|
||||
m_flavorID(info.flavor_id)
|
||||
{
|
||||
D_METHOD(("DormantNodeInfoView::DormantNodeInfoView()\n"));
|
||||
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Output Formats ") + 2 * InfoView::M_H_MARGIN);
|
||||
|
||||
BString s;
|
||||
|
||||
// add the "AddOn ID" field
|
||||
s = "";
|
||||
s << info.addon;
|
||||
addField("AddOn ID", s);
|
||||
|
||||
// add the "Flavor ID" field
|
||||
s = "";
|
||||
s << info.flavor_id;
|
||||
addField("Flavor ID", s);
|
||||
|
||||
// add separator field
|
||||
addField("", "");
|
||||
|
||||
dormant_flavor_info flavorInfo;
|
||||
BMediaRoster *roster = BMediaRoster::Roster();
|
||||
if (roster && (roster->GetDormantFlavorInfoFor(info, &flavorInfo) == B_OK))
|
||||
{
|
||||
// add the "Description" field
|
||||
s = flavorInfo.info;
|
||||
addField("Description", s);
|
||||
|
||||
// add "Kinds" field
|
||||
addField("Kinds", MediaString::getStringFor(static_cast<node_kind>(flavorInfo.kinds)));
|
||||
|
||||
// add "Flavor Flags" field
|
||||
addField("Flavor Flags", "?");
|
||||
|
||||
// add "Max Instances" field
|
||||
if (flavorInfo.possible_count > 0)
|
||||
{
|
||||
s = "";
|
||||
s << flavorInfo.possible_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = "Any number";
|
||||
}
|
||||
addField("Max Instances", s);
|
||||
|
||||
// add "Input Formats" field
|
||||
if (flavorInfo.in_format_count > 0)
|
||||
{
|
||||
if (flavorInfo.in_format_count == 1)
|
||||
{
|
||||
addField("Input Format", MediaString::getStringFor(flavorInfo.in_formats[0], false));
|
||||
}
|
||||
else
|
||||
{
|
||||
addField("Input Formats", "");
|
||||
for (int32 i = 0; i < flavorInfo.in_format_count; i++)
|
||||
{
|
||||
s = "";
|
||||
s << "(" << i + 1 << ")";
|
||||
addField(s, MediaString::getStringFor(flavorInfo.in_formats[i], false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add "Output Formats" field
|
||||
if (flavorInfo.out_format_count > 0)
|
||||
{
|
||||
if (flavorInfo.out_format_count == 1)
|
||||
{
|
||||
addField("Output Format", MediaString::getStringFor(flavorInfo.out_formats[0], false));
|
||||
}
|
||||
else
|
||||
{
|
||||
addField("Output Formats", "");
|
||||
for (int32 i = 0; i < flavorInfo.out_format_count; i++)
|
||||
{
|
||||
s = "";
|
||||
s << "(" << i + 1 << ")";
|
||||
addField(s, MediaString::getStringFor(flavorInfo.out_formats[i], false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DormantNodeInfoView::~DormantNodeInfoView()
|
||||
{
|
||||
D_METHOD(("DormantNodeInfoView::~DormantNodeInfoView()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BView implementation (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DormantNodeInfoView::DetachedFromWindow() {
|
||||
D_METHOD(("DormantNodeInfoView::DetachedFromWindow()\n"));
|
||||
|
||||
InfoWindowManager *manager = InfoWindowManager::Instance();
|
||||
if (manager) {
|
||||
BMessage message(InfoWindowManager::M_DORMANT_NODE_WINDOW_CLOSED);
|
||||
message.AddInt32("addOnID", m_addOnID);
|
||||
message.AddInt32("flavorID", m_flavorID);
|
||||
manager->PostMessage(&message);
|
||||
}
|
||||
}
|
||||
|
||||
// END -- DormantNodeInfoView.cpp --
|
48
src/apps/cortex/InfoView/DormantNodeInfoView.h
Normal file
48
src/apps/cortex/InfoView/DormantNodeInfoView.h
Normal file
@ -0,0 +1,48 @@
|
||||
// DormantNodeInfoView.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// An InfoView variant for dormant MediaNodes. Accepts
|
||||
// a dormant_node_info struct in the constructor and
|
||||
// tries to aquire the corresponding dormant_flavor_info
|
||||
// from the BMediaRoster.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 5nov99 Begun
|
||||
//
|
||||
|
||||
#ifndef __DormantNodeInfoView_H__
|
||||
#define __DormantNodeInfoView_H__
|
||||
|
||||
#include "InfoView.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeRef;
|
||||
|
||||
class DormantNodeInfoView :
|
||||
public InfoView {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// add fields relevant for dormant MediaNodes (like
|
||||
// AddOn-ID, input/output formats etc.)
|
||||
DormantNodeInfoView(
|
||||
const dormant_node_info &info);
|
||||
|
||||
virtual ~DormantNodeInfoView();
|
||||
|
||||
public: // *** BView impl
|
||||
|
||||
// notify InfoWindowManager
|
||||
virtual void DetachedFromWindow();
|
||||
|
||||
private: // *** data members
|
||||
|
||||
int32 m_addOnID;
|
||||
|
||||
int32 m_flavorID;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __DormantNodeInfoView_H__ */
|
225
src/apps/cortex/InfoView/EndPointInfoView.cpp
Normal file
225
src/apps/cortex/InfoView/EndPointInfoView.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
// EndPointInfoView.cpp
|
||||
|
||||
#include "EndPointInfoView.h"
|
||||
// InfoView
|
||||
#include "InfoWindowManager.h"
|
||||
// Support
|
||||
#include "MediaIcon.h"
|
||||
#include "MediaString.h"
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
EndPointInfoView::EndPointInfoView(
|
||||
const media_input &input)
|
||||
: InfoView(input.name, "Media Input", 0),
|
||||
m_output(false),
|
||||
m_port(input.destination.port),
|
||||
m_id(input.destination.id) {
|
||||
D_METHOD(("EndPointInfoView::EndPointInfoView(input)\n"));
|
||||
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Destination ")
|
||||
+ 2 * InfoView::M_H_MARGIN);
|
||||
|
||||
// add "Source" field
|
||||
addField("Source", MediaString::getStringFor(input.source));
|
||||
|
||||
// add "Destination" field
|
||||
addField("Destination", MediaString::getStringFor(input.destination));
|
||||
|
||||
// add a separator field
|
||||
addField("", "");
|
||||
|
||||
// add "Media Type" field
|
||||
addField("Media Type", MediaString::getStringFor(input.format.type));
|
||||
|
||||
_addFormatFields(input.format);
|
||||
}
|
||||
|
||||
EndPointInfoView::EndPointInfoView(
|
||||
const media_output &output)
|
||||
: InfoView(output.name, "Media Output", 0),
|
||||
m_output(true),
|
||||
m_port(output.source.port),
|
||||
m_id(output.source.id) {
|
||||
D_METHOD(("EndPointInfoView::EndPointInfoView(output)\n"));
|
||||
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Destination ")
|
||||
+ 2 * InfoView::M_H_MARGIN);
|
||||
|
||||
// add "Source" field
|
||||
addField("Source", MediaString::getStringFor(output.source));
|
||||
|
||||
// add "Destination" field
|
||||
addField("Destination", MediaString::getStringFor(output.destination));
|
||||
|
||||
// add a separator field
|
||||
addField("", "");
|
||||
|
||||
// add "Media Type" field
|
||||
addField("Media Type", MediaString::getStringFor(output.format.type));
|
||||
|
||||
_addFormatFields(output.format);
|
||||
}
|
||||
|
||||
EndPointInfoView::~EndPointInfoView()
|
||||
{
|
||||
D_METHOD(("EndPointInfoView::~EndPointInfoView()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BView implementation (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void EndPointInfoView::DetachedFromWindow() {
|
||||
D_METHOD(("EndPointInfoView::DetachedFromWindow()\n"));
|
||||
|
||||
InfoWindowManager *manager = InfoWindowManager::Instance();
|
||||
if (manager) {
|
||||
if (m_output) {
|
||||
BMessage message(InfoWindowManager::M_OUTPUT_WINDOW_CLOSED);
|
||||
message.AddInt32("source_port", m_port);
|
||||
message.AddInt32("source_id", m_id);
|
||||
manager->PostMessage(&message);
|
||||
}
|
||||
else {
|
||||
BMessage message(InfoWindowManager::M_INPUT_WINDOW_CLOSED);
|
||||
message.AddInt32("destination_port", m_port);
|
||||
message.AddInt32("destination_id", m_id);
|
||||
manager->PostMessage(&message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations (private)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void EndPointInfoView::_addFormatFields(
|
||||
const media_format &format)
|
||||
{
|
||||
D_METHOD(("EndPointInfoView::_addFormatFields()\n"));
|
||||
|
||||
switch (format.type) {
|
||||
case B_MEDIA_RAW_AUDIO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Sample Rate ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add "Format" field
|
||||
s = MediaString::forAudioFormat(format.u.raw_audio.format,
|
||||
format.u.raw_audio.valid_bits);
|
||||
addField("Format", s);
|
||||
// add "Sample Rate" field
|
||||
s = MediaString::forAudioFrameRate(format.u.raw_audio.frame_rate);
|
||||
addField("Sample Rate", s);
|
||||
// add "Channels" field
|
||||
s = MediaString::forAudioChannelCount(format.u.raw_audio.channel_count);
|
||||
addField("Channels", s);
|
||||
// add "Channel Mask" field
|
||||
s = MediaString::forAudioChannelMask(format.u.raw_audio.channel_mask);
|
||||
addField("Channel Mask", s);
|
||||
// add "Matrix Mask" field
|
||||
s = MediaString::forAudioMatrixMask(format.u.raw_audio.matrix_mask);
|
||||
addField("Matrix Mask", s);
|
||||
// add the "Byte Order" field
|
||||
s = MediaString::forAudioByteOrder(format.u.raw_audio.byte_order);
|
||||
addField("Byte Order", s);
|
||||
// add the "Buffer Size" field
|
||||
s = MediaString::forAudioBufferSize(format.u.raw_audio.buffer_size);
|
||||
addField("Buffer Size", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_RAW_VIDEO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Video Data Between ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Format" field
|
||||
s = MediaString::forVideoFormat(format.u.raw_video.display.format);
|
||||
addField("Format", s);
|
||||
// add the "Resolution" field
|
||||
s = MediaString::forVideoResolution(format.u.raw_video.display.line_width,
|
||||
format.u.raw_video.display.line_count);
|
||||
addField("Resolution", s);
|
||||
// add the "Field Rate" field
|
||||
s = MediaString::forVideoFieldRate(format.u.raw_video.field_rate,
|
||||
format.u.raw_video.interlace);
|
||||
addField("Field Rate", s);
|
||||
// add the "Orientation" field
|
||||
s = MediaString::forVideoOrientation(format.u.raw_video.orientation);
|
||||
addField("Orientation", s);
|
||||
// add the "Aspect Ratio" field
|
||||
s = MediaString::forVideoAspectRatio(format.u.raw_video.pixel_width_aspect,
|
||||
format.u.raw_video.pixel_height_aspect);
|
||||
addField("Aspect Ratio", s);
|
||||
// add the "Active Lines" field
|
||||
s = MediaString::forVideoActiveLines(format.u.raw_video.first_active,
|
||||
format.u.raw_video.last_active);
|
||||
addField("Active Lines", s);
|
||||
// add the "Offset" field
|
||||
s = MediaString::forVideoOffset(format.u.raw_video.display.pixel_offset,
|
||||
format.u.raw_video.display.line_offset);
|
||||
addField("Offset", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_ENCODED_AUDIO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Frame Size ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Bit Rate" field
|
||||
s = MediaString::forAudioBitRate(format.u.encoded_audio.bit_rate);
|
||||
addField("Bit Rate", s);
|
||||
// add the "Frame Size" field
|
||||
s = MediaString::forAudioFrameSize(format.u.encoded_audio.frame_size);
|
||||
addField("Frame Size", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_ENCODED_VIDEO: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Frame Size ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Bit Rate" field
|
||||
s = MediaString::forVideoBitRate(format.u.encoded_video.avg_bit_rate,
|
||||
format.u.encoded_video.max_bit_rate);
|
||||
addField("Bit Rate", s);
|
||||
// add the "Frame Size" field
|
||||
s = MediaString::forVideoFrameSize(format.u.encoded_video.frame_size);
|
||||
addField("Frame Size", s);
|
||||
// add the "History" field
|
||||
s = MediaString::forVideoHistory(format.u.encoded_video.forward_history,
|
||||
format.u.encoded_video.backward_history);
|
||||
addField("History", s);
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_MULTISTREAM: {
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Chunk Size ") + 2 * InfoView::M_H_MARGIN);
|
||||
BString s;
|
||||
// add the "Format" field
|
||||
s = MediaString::forMultistreamFormat(format.u.multistream.format);
|
||||
addField("Format", s);
|
||||
// add the "Bit Rate" field
|
||||
s = MediaString::forMultistreamBitRate(format.u.multistream.avg_bit_rate,
|
||||
format.u.multistream.max_bit_rate);
|
||||
addField("Bit Rate", s);
|
||||
// add the "Chunk Size" field
|
||||
s = MediaString::forMultistreamChunkSize(format.u.multistream.avg_chunk_size,
|
||||
format.u.multistream.max_chunk_size);
|
||||
addField("Chunk Size", s);
|
||||
// add the "Flags" field
|
||||
s = MediaString::forMultistreamFlags(format.u.multistream.flags);
|
||||
addField("Flags", s);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// add no fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END -- EndPointInfoView.cpp --
|
64
src/apps/cortex/InfoView/EndPointInfoView.h
Normal file
64
src/apps/cortex/InfoView/EndPointInfoView.h
Normal file
@ -0,0 +1,64 @@
|
||||
// EndPointInfoView.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Display input/output related info in the InfoWindow.
|
||||
// Very similar to ConnectionInfoView, only that this
|
||||
// views is prepared to handle wildcard values in the
|
||||
// media_format (a connection should never have to!)
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 14nov99 Begun
|
||||
//
|
||||
|
||||
#ifndef __EndPointInfoView_H__
|
||||
#define __EndPointInfoView_H__
|
||||
|
||||
#include "InfoView.h"
|
||||
|
||||
// Media Kit
|
||||
#include <MediaDefs.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class EndPointInfoView :
|
||||
public InfoView {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// creates an InfoView for a media_input. adds source
|
||||
// and destination fields, then calls _addFormatFields
|
||||
EndPointInfoView(
|
||||
const media_input &input);
|
||||
|
||||
// creates an InfoView for a media_output. adds source
|
||||
// and destination fields, then calls _addFormatFields
|
||||
EndPointInfoView(
|
||||
const media_output &output);
|
||||
|
||||
virtual ~EndPointInfoView();
|
||||
|
||||
public: // *** BView impl
|
||||
|
||||
// notify InfoWindowManager
|
||||
virtual void DetachedFromWindow();
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// adds media_format related fields to the view
|
||||
// (wildcard-aware)
|
||||
void _addFormatFields(
|
||||
const media_format &format);
|
||||
|
||||
private: // *** data members
|
||||
|
||||
// true if media_output
|
||||
bool m_output;
|
||||
|
||||
int32 m_port;
|
||||
|
||||
int32 m_id;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __EndPointInfoView_H__ */
|
135
src/apps/cortex/InfoView/FileNodeInfoView.cpp
Normal file
135
src/apps/cortex/InfoView/FileNodeInfoView.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
// FileNodeInfoView.cpp
|
||||
|
||||
#include "FileNodeInfoView.h"
|
||||
#include "MediaIcon.h"
|
||||
#include "MediaString.h"
|
||||
#include "NodeRef.h"
|
||||
|
||||
#include <MediaFile.h>
|
||||
#include <MediaNode.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <MediaTrack.h>
|
||||
#include <TimeCode.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
FileNodeInfoView::FileNodeInfoView(
|
||||
const NodeRef *ref)
|
||||
: LiveNodeInfoView(ref)
|
||||
{
|
||||
D_METHOD(("FileNodeInfoView::FileNodeInfoView()\n"));
|
||||
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" File Format ") + 2 * InfoView::M_H_MARGIN);
|
||||
setSubTitle("Live File-Interface Node");
|
||||
|
||||
// if a ref is set for this file-interface display some info
|
||||
// thru MediaFile and set the title appropriatly
|
||||
BString title;
|
||||
BString s;
|
||||
entry_ref nodeFile;
|
||||
status_t error;
|
||||
error = BMediaRoster::Roster()->GetRefFor(ref->node(), &nodeFile);
|
||||
if (!error)
|
||||
{
|
||||
BMediaFile file(&nodeFile);
|
||||
if (file.InitCheck() == B_OK)
|
||||
{
|
||||
// add separator field
|
||||
addField("", "");
|
||||
|
||||
// add "File Format" fields
|
||||
media_file_format format;
|
||||
if (file.GetFileFormatInfo(&format) == B_OK)
|
||||
{
|
||||
s = "";
|
||||
s << format.pretty_name << " (" << format.mime_type << ")";
|
||||
addField("File Format", s);
|
||||
}
|
||||
|
||||
// add "Copyright" field
|
||||
const char *copyRight = file.Copyright();
|
||||
if (copyRight)
|
||||
{
|
||||
s = copyRight;
|
||||
addField("Copyright", s);
|
||||
}
|
||||
|
||||
// add "Tracks" list
|
||||
if (file.CountTracks() > 0)
|
||||
{
|
||||
addField("Tracks", "");
|
||||
for (int32 i = 0; i < file.CountTracks(); i++)
|
||||
{
|
||||
BString label;
|
||||
label << "(" << i + 1 << ")";
|
||||
BMediaTrack *track = file.TrackAt(i);
|
||||
|
||||
// add format
|
||||
media_format format;
|
||||
if (track->EncodedFormat(&format) == B_OK)
|
||||
{
|
||||
s = MediaString::getStringFor(format, false);
|
||||
}
|
||||
|
||||
if ((format.type == B_MEDIA_ENCODED_AUDIO)
|
||||
|| (format.type == B_MEDIA_ENCODED_VIDEO))
|
||||
{
|
||||
// add codec
|
||||
media_codec_info codec;
|
||||
if (track->GetCodecInfo(&codec) == B_OK)
|
||||
{
|
||||
s << "\n- Codec: " << codec.pretty_name;
|
||||
if (codec.id > 0)
|
||||
{
|
||||
s << " (ID: " << codec.id << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add duration
|
||||
bigtime_t duration = track->Duration();
|
||||
int hours, minutes, seconds, frames;
|
||||
us_to_timecode(duration, &hours, &minutes, &seconds, &frames);
|
||||
char buffer[64];
|
||||
sprintf(buffer, "%02d:%02d:%02d:%02d", hours, minutes, seconds, frames);
|
||||
s << "\n- Duration: " << buffer;
|
||||
|
||||
// add quality
|
||||
float quality;
|
||||
if (track->GetQuality(&quality) == B_OK)
|
||||
{
|
||||
s << "\n- Quality: " << quality;
|
||||
}
|
||||
addField(label, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
// set title
|
||||
BEntry entry(&nodeFile);
|
||||
char fileName[B_FILE_NAME_LENGTH];
|
||||
entry.GetName(fileName);
|
||||
title = fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// set title
|
||||
title = ref->name();
|
||||
title += " (no file)";
|
||||
}
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
FileNodeInfoView::~FileNodeInfoView()
|
||||
{
|
||||
D_METHOD(("FileNodeInfoView::~FileNodeInfoView()\n"));
|
||||
}
|
||||
|
||||
// END -- FileNodeInfoView.cpp --
|
36
src/apps/cortex/InfoView/FileNodeInfoView.h
Normal file
36
src/apps/cortex/InfoView/FileNodeInfoView.h
Normal file
@ -0,0 +1,36 @@
|
||||
// FileNodeInfoView.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// In addition to the fields defined by its base class
|
||||
// LiveNodeInfoView, this class defines media-file specific
|
||||
// fields, line number of tracks, codecs, formats, etc.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 13nov99 Begun
|
||||
//
|
||||
|
||||
#ifndef __FileNodeInfoView_H__
|
||||
#define __FileNodeInfoView_H__
|
||||
|
||||
#include "LiveNodeInfoView.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeRef;
|
||||
|
||||
class FileNodeInfoView : public LiveNodeInfoView
|
||||
{
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// add file related fields to the view by creating
|
||||
// and querying an instance of BMediaFile/BMediaTrack
|
||||
FileNodeInfoView(
|
||||
const NodeRef *ref);
|
||||
|
||||
virtual ~FileNodeInfoView();
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __FileNodeInfoView_H__ */
|
586
src/apps/cortex/InfoView/InfoView.cpp
Normal file
586
src/apps/cortex/InfoView/InfoView.cpp
Normal file
@ -0,0 +1,586 @@
|
||||
// InfoView.cpp
|
||||
|
||||
#include "InfoView.h"
|
||||
#include "cortex_ui.h"
|
||||
|
||||
#include "array_delete.h"
|
||||
|
||||
// Interface Kit
|
||||
#include <Bitmap.h>
|
||||
#include <Region.h>
|
||||
#include <ScrollBar.h>
|
||||
#include <StringView.h>
|
||||
#include <TextView.h>
|
||||
#include <Window.h>
|
||||
// Storage Kit
|
||||
#include <Mime.h>
|
||||
// Support Kit
|
||||
#include <List.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ALLOC(X) //PRINT (x) // ctor/dtor
|
||||
#define D_HOOK(X) //PRINT (x) // BView impl.
|
||||
#define D_ACCESS(X) //PRINT (x) // Accessors
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal class: _InfoTextField
|
||||
//
|
||||
// * PURPOSE:
|
||||
// store the label & text for each field, and provide methods
|
||||
// for linewrapping and drawing
|
||||
//
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
class _InfoTextField
|
||||
{
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
_InfoTextField(
|
||||
BString label,
|
||||
BString text,
|
||||
InfoView *parent);
|
||||
|
||||
~_InfoTextField();
|
||||
|
||||
public: // *** operations
|
||||
|
||||
void drawField(
|
||||
BPoint position);
|
||||
|
||||
void updateLineWrapping(
|
||||
bool *wrappingChanged = 0,
|
||||
bool *heightChanged = 0);
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
float getHeight() const;
|
||||
|
||||
float getWidth() const;
|
||||
|
||||
bool isWrapped() const;
|
||||
|
||||
private: // *** static internal methods
|
||||
|
||||
static bool canEndLine(
|
||||
const char c);
|
||||
|
||||
static bool mustEndLine(
|
||||
const char c);
|
||||
|
||||
private: // *** data members
|
||||
|
||||
BString m_label;
|
||||
|
||||
BString m_text;
|
||||
|
||||
BList *m_textLines;
|
||||
|
||||
InfoView *m_parent;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** static member init
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const BRect InfoView::M_DEFAULT_FRAME = BRect(0.0, 0.0, 250.0, 100.0);
|
||||
const float InfoView::M_H_MARGIN = 5.0;
|
||||
const float InfoView::M_V_MARGIN = 5.0;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
InfoView::InfoView(
|
||||
BString title,
|
||||
BString subTitle,
|
||||
BBitmap *icon)
|
||||
: BView(M_DEFAULT_FRAME, "InfoView", B_FOLLOW_ALL_SIDES,
|
||||
B_WILL_DRAW | B_FRAME_EVENTS),
|
||||
m_title(title),
|
||||
m_subTitle(subTitle),
|
||||
m_icon(0),
|
||||
m_fields(0) {
|
||||
D_ALLOC(("InfoView::InfoView()\n"));
|
||||
|
||||
if (icon) {
|
||||
m_icon = new BBitmap(icon);
|
||||
}
|
||||
m_fields = new BList();
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
}
|
||||
|
||||
InfoView::~InfoView() {
|
||||
D_ALLOC(("InfoView::~InfoView()\n"));
|
||||
|
||||
// delete all the fields
|
||||
if (m_fields) {
|
||||
while (m_fields->CountItems() > 0) {
|
||||
_InfoTextField *field = static_cast<_InfoTextField *>
|
||||
(m_fields->RemoveItem((int32)0));
|
||||
if (field) {
|
||||
delete field;
|
||||
}
|
||||
}
|
||||
delete m_fields;
|
||||
m_fields = 0;
|
||||
}
|
||||
|
||||
// delete the icon bitmap
|
||||
if (m_icon) {
|
||||
delete m_icon;
|
||||
m_icon = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BView implementation
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void InfoView::AttachedToWindow() {
|
||||
D_HOOK(("InfoView::AttachedToWindow()\n"));
|
||||
|
||||
// adjust the windows title
|
||||
BString title = m_title;
|
||||
title << " info";
|
||||
Window()->SetTitle(title.String());
|
||||
|
||||
// calculate the area occupied by title, subtitle and icon
|
||||
font_height fh;
|
||||
be_bold_font->GetHeight(&fh);
|
||||
float titleHeight = fh.leading + fh.descent;
|
||||
titleHeight += M_V_MARGIN * 2.0 + B_LARGE_ICON / 2.0;
|
||||
be_plain_font->GetHeight(&fh);
|
||||
titleHeight += fh.leading + fh.ascent + fh.descent;
|
||||
BFont font(be_bold_font);
|
||||
float titleWidth = font.StringWidth(title.String());
|
||||
titleWidth += M_H_MARGIN + B_LARGE_ICON + B_LARGE_ICON / 2.0;
|
||||
|
||||
float width, height;
|
||||
GetPreferredSize(&width, &height);
|
||||
Window()->ResizeTo(width + B_V_SCROLL_BAR_WIDTH, height);
|
||||
ResizeBy(- B_V_SCROLL_BAR_WIDTH, 0.0);
|
||||
|
||||
// add scroll bar
|
||||
BRect scrollRect = Window()->Bounds();
|
||||
scrollRect.left = scrollRect.right - B_V_SCROLL_BAR_WIDTH + 1.0;
|
||||
scrollRect.top -= 1.0;
|
||||
scrollRect.right += 1.0;
|
||||
scrollRect.bottom -= B_H_SCROLL_BAR_HEIGHT - 1.0;
|
||||
Window()->AddChild(new BScrollBar(scrollRect, "ScrollBar", this,
|
||||
0.0, 0.0, B_VERTICAL));
|
||||
ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0);
|
||||
be_plain_font->GetHeight(&fh);
|
||||
float step = fh.ascent + fh.descent + fh.leading + M_V_MARGIN;
|
||||
ScrollBar(B_VERTICAL)->SetSteps(step, step * 5);
|
||||
|
||||
// set window size limits
|
||||
float minWidth, maxWidth, minHeight, maxHeight;
|
||||
Window()->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
|
||||
Window()->SetSizeLimits(titleWidth + B_V_SCROLL_BAR_WIDTH, maxWidth,
|
||||
titleHeight + B_H_SCROLL_BAR_HEIGHT, maxHeight);
|
||||
|
||||
// cache the bounds rect for proper redraw later on...
|
||||
m_oldFrame = Bounds();
|
||||
}
|
||||
|
||||
void InfoView::Draw(
|
||||
BRect updateRect) {
|
||||
D_HOOK(("InfoView::Draw()\n"));
|
||||
|
||||
// Draw side bar
|
||||
SetDrawingMode(B_OP_COPY);
|
||||
BRect r = Bounds();
|
||||
r.right = B_LARGE_ICON - 1.0;
|
||||
SetLowColor(M_LIGHT_BLUE_COLOR);
|
||||
FillRect(r, B_SOLID_LOW);
|
||||
SetHighColor(M_DARK_BLUE_COLOR);
|
||||
r.right += 1.0;
|
||||
StrokeLine(r.RightTop(), r.RightBottom(), B_SOLID_HIGH);
|
||||
|
||||
// Draw background
|
||||
BRegion region;
|
||||
region.Include(updateRect);
|
||||
region.Exclude(r);
|
||||
SetLowColor(M_GRAY_COLOR);
|
||||
FillRegion(®ion, B_SOLID_LOW);
|
||||
|
||||
// Draw title
|
||||
SetDrawingMode(B_OP_OVER);
|
||||
font_height fh;
|
||||
be_bold_font->GetHeight(&fh);
|
||||
SetFont(be_bold_font);
|
||||
BPoint p(M_H_MARGIN + B_LARGE_ICON + B_LARGE_ICON / 2.0,
|
||||
M_V_MARGIN * 2.0 + fh.ascent);
|
||||
SetHighColor(M_BLACK_COLOR);
|
||||
DrawString(m_title.String(), p);
|
||||
|
||||
// Draw sub-title
|
||||
p.y += fh.descent;
|
||||
be_plain_font->GetHeight(&fh);
|
||||
SetFont(be_plain_font);
|
||||
p.y += fh.ascent + fh.leading;
|
||||
SetHighColor(M_DARK_GRAY_COLOR);
|
||||
DrawString(m_subTitle.String(), p);
|
||||
|
||||
// Draw icon
|
||||
p.y = 2 * M_V_MARGIN;
|
||||
if (m_icon) {
|
||||
p.x = B_LARGE_ICON / 2.0;
|
||||
DrawBitmapAsync(m_icon, p);
|
||||
}
|
||||
|
||||
// Draw fields
|
||||
be_plain_font->GetHeight(&fh);
|
||||
p.x = B_LARGE_ICON;
|
||||
p.y += B_LARGE_ICON + 2 * M_V_MARGIN + fh.ascent + 2 * fh.leading;
|
||||
for (int32 i = 0; i < m_fields->CountItems(); i++) {
|
||||
_InfoTextField *field = static_cast<_InfoTextField *>(m_fields->ItemAt(i));
|
||||
field->drawField(p);
|
||||
p.y += field->getHeight() + M_V_MARGIN;
|
||||
}
|
||||
}
|
||||
|
||||
void InfoView::FrameResized(
|
||||
float width,
|
||||
float height) {
|
||||
D_HOOK(("InfoView::FrameResized()\n"));
|
||||
|
||||
BRect newFrame = BRect(0.0, 0.0, width, height);
|
||||
|
||||
// update the each lines' line-wrapping and redraw as necessary
|
||||
font_height fh;
|
||||
BPoint p;
|
||||
be_plain_font->GetHeight(&fh);
|
||||
p.x = B_LARGE_ICON;
|
||||
p.y += B_LARGE_ICON + M_V_MARGIN * 2.0 + fh.ascent + fh.leading * 2.0;
|
||||
bool heightChanged = false;
|
||||
for (int32 i = 0; i < m_fields->CountItems(); i++) {
|
||||
bool wrappingChanged = false;
|
||||
_InfoTextField *field = static_cast<_InfoTextField *>(m_fields->ItemAt(i));
|
||||
field->updateLineWrapping(&wrappingChanged,
|
||||
heightChanged ? 0 : &heightChanged);
|
||||
float fieldHeight = field->getHeight() + M_V_MARGIN;
|
||||
if (heightChanged) {
|
||||
Invalidate(BRect(p.x, p.y, width, p.y + fieldHeight));
|
||||
}
|
||||
else if (wrappingChanged) {
|
||||
Invalidate(BRect(p.x + m_sideBarWidth, p.y, width, p.y + fieldHeight));
|
||||
}
|
||||
p.y += fieldHeight;
|
||||
}
|
||||
|
||||
// clean up the rest of the view
|
||||
BRect updateRect;
|
||||
updateRect.left = B_LARGE_ICON;
|
||||
updateRect.top = p.y < (m_oldFrame.bottom - M_V_MARGIN - 15.0) ?
|
||||
p.y - 15.0 : m_oldFrame.bottom - M_V_MARGIN - 15.0;
|
||||
updateRect.right = width - M_H_MARGIN;
|
||||
updateRect.bottom = m_oldFrame.bottom < newFrame.bottom ?
|
||||
newFrame.bottom : m_oldFrame.bottom;
|
||||
Invalidate(updateRect);
|
||||
|
||||
if (p.y > height) {
|
||||
ScrollBar(B_VERTICAL)->SetRange(0.0, ceil(p.y - height));
|
||||
}
|
||||
else {
|
||||
ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0);
|
||||
}
|
||||
|
||||
// cache the new frame rect for the next time
|
||||
m_oldFrame = newFrame;
|
||||
}
|
||||
|
||||
void
|
||||
InfoView::GetPreferredSize(
|
||||
float *width,
|
||||
float *height) {
|
||||
D_HOOK(("InfoView::GetPreferredSize()\n"));
|
||||
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
|
||||
// calculate the height needed to display everything, avoiding line wrapping
|
||||
font_height fh;
|
||||
be_plain_font->GetHeight(&fh);
|
||||
for (int32 i = 0; i < m_fields->CountItems(); i++) {
|
||||
_InfoTextField *field = static_cast<_InfoTextField *>(m_fields->ItemAt(i));
|
||||
*height += fh.ascent + fh.descent + fh.leading + M_V_MARGIN;
|
||||
float tfw = field->getWidth();
|
||||
if (tfw > *width) {
|
||||
*width = tfw;
|
||||
}
|
||||
}
|
||||
|
||||
*width += B_LARGE_ICON + 2 * M_H_MARGIN;
|
||||
*height += B_LARGE_ICON + 2 * M_V_MARGIN + fh.ascent + 2 * fh.leading;
|
||||
*height += B_H_SCROLL_BAR_HEIGHT;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations (protected)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void InfoView::addField(
|
||||
BString label,
|
||||
BString text) {
|
||||
D_METHOD(("InfoView::addField()\n"));
|
||||
|
||||
m_fields->AddItem(reinterpret_cast<void *>
|
||||
(new _InfoTextField(label, text, this)));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal class: _InfoTextField
|
||||
//
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
_InfoTextField::_InfoTextField(
|
||||
BString label,
|
||||
BString text,
|
||||
InfoView *parent)
|
||||
: m_label(label),
|
||||
m_text(text),
|
||||
m_textLines(0),
|
||||
m_parent(parent) {
|
||||
D_ALLOC(("_InfoTextField::_InfoTextField()\n"));
|
||||
|
||||
if (m_label != "") {
|
||||
m_label << ": ";
|
||||
}
|
||||
m_textLines = new BList();
|
||||
}
|
||||
|
||||
_InfoTextField::~_InfoTextField() {
|
||||
D_ALLOC(("_InfoTextField::~_InfoTextField()\n"));
|
||||
|
||||
// delete every line
|
||||
if (m_textLines) {
|
||||
while (m_textLines->CountItems() > 0) {
|
||||
BString *line = static_cast<BString *>(m_textLines->RemoveItem((int32)0));
|
||||
if (line) {
|
||||
delete line;
|
||||
}
|
||||
}
|
||||
delete m_textLines;
|
||||
m_textLines = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal class: _InfoTextField
|
||||
//
|
||||
// *** operations (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void _InfoTextField::drawField(
|
||||
BPoint position) {
|
||||
D_METHOD(("_InfoTextField::drawField()\n"));
|
||||
|
||||
float sideBarWidth = m_parent->getSideBarWidth();
|
||||
|
||||
// Draw label
|
||||
BPoint p = position;
|
||||
p.x += sideBarWidth - be_plain_font->StringWidth(m_label.String());
|
||||
m_parent->SetHighColor(M_DARK_GRAY_COLOR);
|
||||
m_parent->SetDrawingMode(B_OP_OVER);
|
||||
m_parent->SetFont(be_plain_font);
|
||||
m_parent->DrawString(m_label.String(), p);
|
||||
|
||||
// Draw text
|
||||
font_height fh;
|
||||
be_plain_font->GetHeight(&fh);
|
||||
p.x = position.x + sideBarWidth;// + InfoView::M_H_MARGIN;
|
||||
m_parent->SetHighColor(M_BLACK_COLOR);
|
||||
for (int32 i = 0; i < m_textLines->CountItems(); i++) {
|
||||
BString *line = static_cast<BString *>(m_textLines->ItemAt(i));
|
||||
m_parent->DrawString(line->String(), p);
|
||||
p.y += fh.ascent + fh.descent + fh.leading;
|
||||
}
|
||||
}
|
||||
|
||||
void _InfoTextField::updateLineWrapping(
|
||||
bool *wrappingChanged,
|
||||
bool *heightChanged)
|
||||
{
|
||||
D_METHOD(("_InfoTextField::updateLineWrapping()\n"));
|
||||
|
||||
// clear the current list of lines but remember their number and
|
||||
// the number of characters per line (to know if something changed)
|
||||
int32 numLines = m_textLines->CountItems();
|
||||
int32* numChars = new int32[numLines];
|
||||
array_delete<int32> _d(numChars);
|
||||
|
||||
for (int32 i = 0; i < numLines; i++)
|
||||
{
|
||||
BString *line = static_cast<BString *>(m_textLines->ItemAt(i));
|
||||
numChars[i] = line->CountChars();
|
||||
delete line;
|
||||
}
|
||||
m_textLines->MakeEmpty();
|
||||
|
||||
// calculate the maximum width for a line
|
||||
float maxWidth = m_parent->Bounds().Width();
|
||||
maxWidth -= m_parent->getSideBarWidth() + 3 * InfoView::M_H_MARGIN + B_LARGE_ICON;
|
||||
if (maxWidth <= be_plain_font->StringWidth("M"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate through the text and split into new lines as
|
||||
// necessary
|
||||
BString *currentLine = new BString(m_text);
|
||||
while (currentLine && (currentLine->CountChars() > 0))
|
||||
{
|
||||
int32 lastBreak = 0;
|
||||
for (int32 i = 0; i < currentLine->CountChars(); i++)
|
||||
{
|
||||
if (canEndLine(currentLine->ByteAt(i)))
|
||||
{
|
||||
lastBreak = i + 1;
|
||||
if (mustEndLine(currentLine->ByteAt(i)))
|
||||
{
|
||||
BString *newLine = new BString();
|
||||
currentLine->Remove(i, 1);
|
||||
currentLine->MoveInto(*newLine, i,
|
||||
currentLine->CountChars() - i);
|
||||
m_textLines->AddItem(reinterpret_cast<void *>(currentLine));
|
||||
currentLine = newLine;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == currentLine->CountChars() - 1) // the last char in the text
|
||||
{
|
||||
m_textLines->AddItem(reinterpret_cast<void *>(currentLine));
|
||||
currentLine = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
BString buffer;
|
||||
currentLine->CopyInto(buffer, 0, i);
|
||||
if (be_plain_font->StringWidth(buffer.String()) > maxWidth)
|
||||
{
|
||||
if (lastBreak < 1)
|
||||
{
|
||||
lastBreak = i - 1;
|
||||
}
|
||||
BString *newLine = new BString();
|
||||
currentLine->MoveInto(*newLine, lastBreak,
|
||||
currentLine->CountChars() - lastBreak);
|
||||
m_textLines->AddItem(reinterpret_cast<void *>(currentLine));
|
||||
currentLine = newLine;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// report changes in the fields total height (i.e. if the number
|
||||
// of lines changed)
|
||||
if (heightChanged && (numLines != m_textLines->CountItems()))
|
||||
{
|
||||
*heightChanged = true;
|
||||
}
|
||||
|
||||
// report changes in the wrapping (e.g. if a word slipped into the
|
||||
// next line)
|
||||
else if (wrappingChanged)
|
||||
{
|
||||
for (int32 i = 0; i < m_textLines->CountItems(); i++)
|
||||
{
|
||||
BString *line = static_cast<BString *>(m_textLines->ItemAt(i));
|
||||
if (line->CountChars() != numChars[i])
|
||||
{
|
||||
*wrappingChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal class: _InfoTextField
|
||||
//
|
||||
// *** accessors (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
float
|
||||
_InfoTextField::getHeight() const {
|
||||
D_ACCESS(("_InfoTextField::getHeight()\n"));
|
||||
|
||||
font_height fh;
|
||||
be_plain_font->GetHeight(&fh);
|
||||
|
||||
// calculate the width for an empty line (separator)
|
||||
if (m_textLines->CountItems() == 0)
|
||||
{
|
||||
return fh.ascent + fh.descent + fh.leading;
|
||||
}
|
||||
|
||||
// calculate the total height of the field by counting the
|
||||
else
|
||||
{
|
||||
float height = fh.ascent + fh.descent + fh.leading;
|
||||
height *= m_textLines->CountItems();
|
||||
height += fh.leading;
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
_InfoTextField::getWidth() const {
|
||||
D_ACCESS(("_InfoTextField::getWidth()\n"));
|
||||
|
||||
float width = be_plain_font->StringWidth(m_text.String());
|
||||
width += m_parent->getSideBarWidth();
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
bool
|
||||
_InfoTextField::isWrapped() const {
|
||||
D_ACCESS(("_InfoTextField::isWrapped()\n"));
|
||||
|
||||
return (m_textLines->CountItems() > 1);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal class: _InfoTextField
|
||||
//
|
||||
// *** static internal methods (private)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool _InfoTextField::canEndLine(
|
||||
const char c)
|
||||
{
|
||||
D_METHOD(("_InfoTextField::canEndLine()\n"));
|
||||
|
||||
if ((c == B_SPACE) || (c == B_TAB) || (c == B_ENTER)
|
||||
|| ( c == '-'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _InfoTextField::mustEndLine(
|
||||
const char c)
|
||||
{
|
||||
D_METHOD(("_InfoTextField::mustEndLine()\n"));
|
||||
|
||||
if (c == B_ENTER)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// END -- InfoView.cpp --
|
139
src/apps/cortex/InfoView/InfoView.h
Normal file
139
src/apps/cortex/InfoView/InfoView.h
Normal file
@ -0,0 +1,139 @@
|
||||
// InfoView.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// A base class for displaying info in a list of fields,
|
||||
// where each field has a label and the actual content text.
|
||||
// This class has to be subclassed for providing info on
|
||||
// specific objects, by adding fields to the view in the
|
||||
// constructor of the subclass. InfoView takes care of all
|
||||
// the display details, dynamically rearranging text on resize
|
||||
// if necessary.
|
||||
//
|
||||
// * TODO
|
||||
// Maybe add more field types, e.g. for options (checkboxes) or
|
||||
// dropdown-menus ?
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 5nov99 Begun
|
||||
//
|
||||
|
||||
#ifndef __InfoView_H__
|
||||
#define __InfoView_H__
|
||||
|
||||
// Interface Kit
|
||||
#include <View.h>
|
||||
// Support Kit
|
||||
#include <String.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class InfoView :
|
||||
public BView {
|
||||
|
||||
public: // *** constants
|
||||
|
||||
// the default frame for an InfoView. Is not actually
|
||||
// needed right now, because the frame is set to an
|
||||
// 'ideal' size when the view is attached to the window
|
||||
static const BRect M_DEFAULT_FRAME;
|
||||
|
||||
// defines how much space is used to separate objects
|
||||
// horizontally
|
||||
static const float M_H_MARGIN;
|
||||
|
||||
// defines how much space is used to separate objects
|
||||
// vertically
|
||||
static const float M_V_MARGIN;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// creates a view containing only the title and subtitle
|
||||
// and the icon (if provided). No fields are defined here.
|
||||
InfoView(
|
||||
BString title,
|
||||
BString subTitle,
|
||||
BBitmap *icon);
|
||||
|
||||
virtual ~InfoView();
|
||||
|
||||
public: // *** BView impl.
|
||||
|
||||
// resizes the view to an 'ideal' size and inits linewrapping
|
||||
// for each field
|
||||
virtual void AttachedToWindow();
|
||||
|
||||
// updates every fields linewrapping
|
||||
virtual void FrameResized(
|
||||
float width,
|
||||
float height);
|
||||
|
||||
// returns the ideal size needed to display all text without
|
||||
// wrapping lines
|
||||
virtual void GetPreferredSize(
|
||||
float *width,
|
||||
float *height);
|
||||
|
||||
// draws the title, subtitle, sidebar & icon as well as
|
||||
// every field
|
||||
virtual void Draw(
|
||||
BRect updateRect);
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
// adjusts the sidebars' width
|
||||
void setSideBarWidth(
|
||||
float width)
|
||||
{ m_sideBarWidth = width; }
|
||||
|
||||
// returns the sidebars' width
|
||||
float getSideBarWidth() const
|
||||
{ return m_sideBarWidth; }
|
||||
|
||||
// set the title (also used for window title)
|
||||
void setTitle(
|
||||
BString title)
|
||||
{ m_title = title; }
|
||||
|
||||
// set the string which will be displayed just below
|
||||
// the title
|
||||
void setSubTitle(
|
||||
BString subTitle)
|
||||
{ m_subTitle = subTitle; }
|
||||
|
||||
protected: // *** operations
|
||||
|
||||
// add a field with the given label and 'content'-text.
|
||||
// fields are displayed in the order they are added!
|
||||
// as there is no way to update the fields (yet), these
|
||||
// should always be added in the constructor of the
|
||||
// subclass!
|
||||
void addField(
|
||||
BString label,
|
||||
BString text);
|
||||
|
||||
private: // *** data members
|
||||
|
||||
// the objects title, which will appear at top of the view
|
||||
// and in the windows titlebar
|
||||
BString m_title;
|
||||
|
||||
// a string to be displayed right beneath the title, using a
|
||||
// smaller font
|
||||
BString m_subTitle;
|
||||
|
||||
// an icon representation of the object
|
||||
BBitmap *m_icon;
|
||||
|
||||
// a list of the InfoTextField objects registered thru addField()
|
||||
BList *m_fields;
|
||||
|
||||
// the width of the sidebar holding label strings
|
||||
float m_sideBarWidth;
|
||||
|
||||
// cached frame rect for proper redrawing of the sidebar
|
||||
BRect m_oldFrame;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __InfoView_H__ */
|
139
src/apps/cortex/InfoView/InfoWindow.cpp
Normal file
139
src/apps/cortex/InfoView/InfoWindow.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
// InfoWindow.cpp
|
||||
|
||||
#include "InfoWindow.h"
|
||||
|
||||
// Interface Kit
|
||||
#include <Screen.h>
|
||||
#include <ScrollBar.h>
|
||||
#include <View.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ALLOC(x) //PRINT (x)
|
||||
#define D_HOOK(x) //PRINT (x)
|
||||
#define D_INTERNAL(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
InfoWindow::InfoWindow(
|
||||
BRect frame)
|
||||
: BWindow(frame,
|
||||
"", B_DOCUMENT_WINDOW, 0),
|
||||
m_zoomed(false),
|
||||
m_zooming(false) {
|
||||
D_ALLOC(("InfoWindow::InfoWindow()\n"));
|
||||
|
||||
}
|
||||
|
||||
InfoWindow::~InfoWindow() {
|
||||
D_ALLOC(("InfoWindow::~InfoWindow()\n"));
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// BWindow impl
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void
|
||||
InfoWindow::FrameResized(
|
||||
float width,
|
||||
float height) {
|
||||
D_HOOK(("InfoWindow::FrameResized()\n"));
|
||||
|
||||
if (!m_zooming) {
|
||||
m_zoomed = false;
|
||||
}
|
||||
else {
|
||||
m_zooming = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InfoWindow::Show() {
|
||||
D_HOOK(("InfoWindow::Show()\n"));
|
||||
|
||||
_constrainToScreen();
|
||||
m_manualSize = Bounds().OffsetToCopy(0.0, 0.0);
|
||||
|
||||
BWindow::Show();
|
||||
}
|
||||
|
||||
void
|
||||
InfoWindow::Zoom(
|
||||
BPoint origin,
|
||||
float width,
|
||||
float height) {
|
||||
D_HOOK(("InfoWindow::Zoom()\n"));
|
||||
|
||||
m_zooming = true;
|
||||
|
||||
BScreen screen(this);
|
||||
if (!screen.Frame().Contains(Frame())) {
|
||||
m_zoomed = false;
|
||||
}
|
||||
|
||||
if (!m_zoomed) {
|
||||
// resize to the ideal size
|
||||
m_manualSize = Bounds();
|
||||
float width, height;
|
||||
FindView("InfoView")->GetPreferredSize(&width, &height);
|
||||
width += B_V_SCROLL_BAR_WIDTH;
|
||||
ResizeTo(width, height);
|
||||
_constrainToScreen();
|
||||
m_zoomed = true;
|
||||
}
|
||||
else {
|
||||
// resize to the most recent manual size
|
||||
ResizeTo(m_manualSize.Width(), m_manualSize.Height());
|
||||
m_zoomed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void
|
||||
InfoWindow::_constrainToScreen() {
|
||||
D_INTERNAL(("InfoWindow::_constrainToScreen()\n"));
|
||||
|
||||
BScreen screen(this);
|
||||
BRect screenRect = screen.Frame();
|
||||
BRect windowRect = Frame();
|
||||
|
||||
// if the window is outside the screen rect
|
||||
// move it to the default position
|
||||
if (!screenRect.Intersects(windowRect)) {
|
||||
windowRect.OffsetTo(screenRect.LeftTop());
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
|
||||
// if the window is larger than the screen rect
|
||||
// resize it to fit at each side
|
||||
if (!screenRect.Contains(windowRect)) {
|
||||
if (windowRect.left < screenRect.left) {
|
||||
windowRect.left = screenRect.left + 5.0;
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
if (windowRect.top < screenRect.top) {
|
||||
windowRect.top = screenRect.top + 5.0;
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
if (windowRect.right > screenRect.right) {
|
||||
windowRect.right = screenRect.right - 5.0;
|
||||
}
|
||||
if (windowRect.bottom > screenRect.bottom) {
|
||||
windowRect.bottom = screenRect.bottom - 5.0;
|
||||
}
|
||||
ResizeTo(windowRect.Width(), windowRect.Height());
|
||||
}
|
||||
}
|
||||
|
||||
// END -- InfoWindow.cpp --
|
61
src/apps/cortex/InfoView/InfoWindow.h
Normal file
61
src/apps/cortex/InfoView/InfoWindow.h
Normal file
@ -0,0 +1,61 @@
|
||||
// InfoWindow.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 25may00 Begun
|
||||
//
|
||||
|
||||
#ifndef __InfoWindow_H__
|
||||
#define __InfoWindow_H__
|
||||
|
||||
// Interface Kit
|
||||
#include <Window.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class InfoWindow :
|
||||
public BWindow {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
InfoWindow(
|
||||
BRect frame);
|
||||
|
||||
virtual ~InfoWindow();
|
||||
|
||||
public: // *** BWindow impl
|
||||
|
||||
// remember that frame was changed manually
|
||||
virtual void FrameResized(
|
||||
float width,
|
||||
float height);
|
||||
|
||||
// extends BWindow implementation to constrain to screen
|
||||
// and remember the initial size
|
||||
virtual void Show();
|
||||
|
||||
// extend basic Zoom() functionality to 'minimize' the
|
||||
// window when currently at max size
|
||||
virtual void Zoom(
|
||||
BPoint origin,
|
||||
float width,
|
||||
float height);
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// resizes the window to fit in the current screen rect
|
||||
void _constrainToScreen();
|
||||
|
||||
private: // *** data members
|
||||
|
||||
BRect m_manualSize;
|
||||
|
||||
bool m_zoomed;
|
||||
|
||||
bool m_zooming;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __InfoWindow_H__ */
|
905
src/apps/cortex/InfoView/InfoWindowManager.cpp
Normal file
905
src/apps/cortex/InfoView/InfoWindowManager.cpp
Normal file
@ -0,0 +1,905 @@
|
||||
// InfoWindowManager.cpp
|
||||
|
||||
#include "InfoWindowManager.h"
|
||||
// InfoWindow
|
||||
#include "AppNodeInfoView.h"
|
||||
#include "ConnectionInfoView.h"
|
||||
#include "DormantNodeInfoView.h"
|
||||
#include "EndPointInfoView.h"
|
||||
#include "FileNodeInfoView.h"
|
||||
#include "LiveNodeInfoView.h"
|
||||
#include "InfoWindow.h"
|
||||
// NodeManager
|
||||
#include "AddOnHostProtocol.h"
|
||||
#include "Connection.h"
|
||||
#include "NodeRef.h"
|
||||
|
||||
// Application Kit
|
||||
#include <Application.h>
|
||||
#include <AppDefs.h>
|
||||
#include <Roster.h>
|
||||
// Media Kit
|
||||
#include <MediaAddOn.h>
|
||||
#include <MediaRoster.h>
|
||||
// Support Kit
|
||||
#include <List.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ACCESS(x) //PRINT (x)
|
||||
#define D_ALLOC(x) //PRINT (x)
|
||||
#define D_INTERNAL(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
#define D_WINDOW(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal types
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// used to remember window for live nodes
|
||||
struct live_node_window {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
live_node_window(
|
||||
const NodeRef *ref,
|
||||
BWindow *window)
|
||||
: ref(ref),
|
||||
window(window)
|
||||
{ }
|
||||
|
||||
public: // *** data members
|
||||
|
||||
const NodeRef *ref;
|
||||
|
||||
BWindow *window;
|
||||
};
|
||||
|
||||
// used to remember windows for dormant nodes
|
||||
struct dormant_node_window {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
dormant_node_window(
|
||||
const dormant_node_info &info,
|
||||
BWindow *window)
|
||||
: info(info),
|
||||
window(window)
|
||||
{ }
|
||||
|
||||
public: // *** data members
|
||||
|
||||
const dormant_node_info info;
|
||||
|
||||
BWindow *window;
|
||||
};
|
||||
|
||||
// used to remember windows for connections
|
||||
struct connection_window {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
connection_window(
|
||||
const media_source &source,
|
||||
const media_destination &destination,
|
||||
BWindow *window)
|
||||
: source(source),
|
||||
destination(destination),
|
||||
window(window)
|
||||
{ }
|
||||
|
||||
public: // *** data members
|
||||
|
||||
media_source source;
|
||||
|
||||
media_destination destination;
|
||||
|
||||
BWindow *window;
|
||||
};
|
||||
|
||||
// used to remember windows for media_inputs
|
||||
struct input_window {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
input_window(
|
||||
const media_destination &destination,
|
||||
BWindow *window)
|
||||
: destination(destination),
|
||||
window(window)
|
||||
{ }
|
||||
|
||||
public: // *** data members
|
||||
|
||||
media_destination destination;
|
||||
|
||||
BWindow *window;
|
||||
};
|
||||
|
||||
// used to remember windows for media_outputs
|
||||
struct output_window {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
output_window(
|
||||
const media_source &source,
|
||||
BWindow *window)
|
||||
: source(source),
|
||||
window(window)
|
||||
{ }
|
||||
|
||||
public: // *** data members
|
||||
|
||||
media_source source;
|
||||
|
||||
BWindow *window;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// static member init
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const BPoint InfoWindowManager::M_DEFAULT_OFFSET = BPoint(20.0, 20.0);
|
||||
const BPoint InfoWindowManager::M_INIT_POSITION = BPoint(90.0, 90.0);
|
||||
|
||||
InfoWindowManager *InfoWindowManager::s_instance = 0;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/* hidden */
|
||||
InfoWindowManager::InfoWindowManager()
|
||||
: BLooper("InfoWindowManager",
|
||||
B_NORMAL_PRIORITY),
|
||||
m_liveNodeWindows(0),
|
||||
m_dormantNodeWindows(0),
|
||||
m_connectionWindows(0),
|
||||
m_inputWindows(0),
|
||||
m_outputWindows(0),
|
||||
m_nextWindowPosition(M_INIT_POSITION) {
|
||||
D_ALLOC(("InfoWindowManager::InfoWindowManager()\n"));
|
||||
|
||||
Run();
|
||||
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (roster) {
|
||||
roster->StartWatching(BMessenger(0, this),
|
||||
B_MEDIA_CONNECTION_BROKEN);
|
||||
}
|
||||
}
|
||||
|
||||
InfoWindowManager::~InfoWindowManager() {
|
||||
D_ALLOC(("InfoWindowManager::~InfoWindowManager()\n"));
|
||||
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (roster) {
|
||||
roster->StopWatching(BMessenger(0, this),
|
||||
B_MEDIA_CONNECTION_BROKEN);
|
||||
}
|
||||
|
||||
if (m_liveNodeWindows) {
|
||||
while (m_liveNodeWindows->CountItems() > 0) {
|
||||
live_node_window *entry = static_cast<live_node_window *>
|
||||
(m_liveNodeWindows->ItemAt(0));
|
||||
if (entry && entry->window) {
|
||||
remove_observer(this, entry->ref);
|
||||
BMessenger messenger(0, entry->window);
|
||||
messenger.SendMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
m_liveNodeWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
delete m_liveNodeWindows;
|
||||
m_liveNodeWindows = 0;
|
||||
}
|
||||
|
||||
if (m_dormantNodeWindows) {
|
||||
while (m_dormantNodeWindows->CountItems() > 0) {
|
||||
dormant_node_window *entry = static_cast<dormant_node_window *>
|
||||
(m_dormantNodeWindows->ItemAt(0));
|
||||
if (entry && entry->window) {
|
||||
BMessenger messenger(0, entry->window);
|
||||
messenger.SendMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
m_dormantNodeWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
delete m_dormantNodeWindows;
|
||||
m_dormantNodeWindows = 0;
|
||||
}
|
||||
|
||||
if (m_connectionWindows) {
|
||||
while (m_connectionWindows->CountItems() > 0) {
|
||||
connection_window *entry = static_cast<connection_window *>
|
||||
(m_connectionWindows->ItemAt(0));
|
||||
if (entry && entry->window) {
|
||||
BMessenger messenger(0, entry->window);
|
||||
messenger.SendMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
m_connectionWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
delete m_connectionWindows;
|
||||
m_connectionWindows = 0;
|
||||
}
|
||||
|
||||
if (m_inputWindows) {
|
||||
while (m_inputWindows->CountItems() > 0) {
|
||||
input_window *entry = static_cast<input_window *>
|
||||
(m_inputWindows->ItemAt(0));
|
||||
if (entry && entry->window) {
|
||||
BMessenger messenger(0, entry->window);
|
||||
messenger.SendMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
m_inputWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
delete m_inputWindows;
|
||||
m_inputWindows = 0;
|
||||
}
|
||||
|
||||
if (m_outputWindows) {
|
||||
while (m_outputWindows->CountItems() > 0) {
|
||||
output_window *entry = static_cast<output_window *>
|
||||
(m_outputWindows->ItemAt(0));
|
||||
if (entry && entry->window) {
|
||||
BMessenger messenger(0, entry->window);
|
||||
messenger.SendMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
m_outputWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
delete m_outputWindows;
|
||||
m_outputWindows = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** singleton access
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/*static*/
|
||||
InfoWindowManager *InfoWindowManager::Instance() {
|
||||
D_ACCESS(("InfoWindowManager::Instance()\n"));
|
||||
|
||||
if (!s_instance) {
|
||||
D_ACCESS((" -> create instance\n"));
|
||||
s_instance = new InfoWindowManager();
|
||||
}
|
||||
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void InfoWindowManager::shutDown() {
|
||||
D_WINDOW(("InfoWindowManager::shutDown()\n"));
|
||||
|
||||
if (s_instance) {
|
||||
s_instance->Lock();
|
||||
s_instance->Quit();
|
||||
s_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
status_t InfoWindowManager::openWindowFor(
|
||||
const NodeRef *ref) {
|
||||
D_WINDOW(("InfoWindowManager::openWindowFor(live_node)\n"));
|
||||
|
||||
// make absolutely sure we're locked
|
||||
if (!IsLocked()) {
|
||||
debugger("The looper must be locked !");
|
||||
}
|
||||
|
||||
// make sure the ref is valid
|
||||
if (!ref) {
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
BWindow *window = 0;
|
||||
if (_findWindowFor(ref->id(), &window)) {
|
||||
// window for this node already exists, activate it
|
||||
window->SetWorkspaces(B_CURRENT_WORKSPACE);
|
||||
window->Activate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
BRect frame = InfoView::M_DEFAULT_FRAME;
|
||||
frame.OffsetTo(m_nextWindowPosition);
|
||||
m_nextWindowPosition += M_DEFAULT_OFFSET;
|
||||
window = new InfoWindow(frame);
|
||||
|
||||
if (_addWindowFor(ref, window)) {
|
||||
// find the correct InfoView sub-class
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
dormant_node_info dormantNodeInfo;
|
||||
if (ref->kind() & B_FILE_INTERFACE)
|
||||
{
|
||||
window->AddChild(new FileNodeInfoView(ref));
|
||||
}
|
||||
else if (roster->GetDormantNodeFor(ref->node(), &dormantNodeInfo) != B_OK) {
|
||||
port_info portInfo;
|
||||
app_info appInfo;
|
||||
if ((get_port_info(ref->node().port, &portInfo) == B_OK)
|
||||
&& (be_roster->GetRunningAppInfo(portInfo.team, &appInfo) == B_OK)) {
|
||||
app_info thisAppInfo;
|
||||
if ((be_app->GetAppInfo(&thisAppInfo) != B_OK)
|
||||
|| ((strcmp(appInfo.signature, thisAppInfo.signature) != 0)
|
||||
&& (strcmp(appInfo.signature, addon_host::g_appSignature) != 0))) {
|
||||
window->AddChild(new AppNodeInfoView(ref));
|
||||
}
|
||||
else {
|
||||
window->AddChild(new LiveNodeInfoView(ref));
|
||||
}
|
||||
}
|
||||
else {
|
||||
window->AddChild(new LiveNodeInfoView(ref));
|
||||
}
|
||||
}
|
||||
else {
|
||||
window->AddChild(new LiveNodeInfoView(ref));
|
||||
}
|
||||
// display the window
|
||||
window->Show();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// failed
|
||||
delete window;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t InfoWindowManager::openWindowFor(
|
||||
const dormant_node_info &info) {
|
||||
D_WINDOW(("InfoWindowManager::openWindowFor(dormant_node)\n"));
|
||||
|
||||
// make absolutely sure we're locked
|
||||
if (!IsLocked()) {
|
||||
debugger("The looper must be locked !");
|
||||
}
|
||||
|
||||
BWindow *window = 0;
|
||||
if (_findWindowFor(info, &window)) {
|
||||
// window for this node already exists, activate it
|
||||
window->SetWorkspaces(B_CURRENT_WORKSPACE);
|
||||
window->Activate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
BRect frame = InfoView::M_DEFAULT_FRAME;
|
||||
frame.OffsetTo(m_nextWindowPosition);
|
||||
m_nextWindowPosition += M_DEFAULT_OFFSET;
|
||||
window = new InfoWindow(frame);
|
||||
|
||||
if (_addWindowFor(info, window)) {
|
||||
window->AddChild(new DormantNodeInfoView(info));
|
||||
window->Show();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// failed
|
||||
delete window;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t InfoWindowManager::openWindowFor(
|
||||
const Connection &connection) {
|
||||
D_WINDOW(("InfoWindowManager::openWindowFor(connection)\n"));
|
||||
|
||||
// make absolutely sure we're locked
|
||||
if (!IsLocked()) {
|
||||
debugger("The looper must be locked !");
|
||||
}
|
||||
|
||||
BWindow *window = 0;
|
||||
if (_findWindowFor(connection.source(), connection.destination(), &window)) {
|
||||
// window for this node already exists, activate it
|
||||
window->SetWorkspaces(B_CURRENT_WORKSPACE);
|
||||
window->Activate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
BRect frame = InfoView::M_DEFAULT_FRAME;
|
||||
frame.OffsetTo(m_nextWindowPosition);
|
||||
m_nextWindowPosition += M_DEFAULT_OFFSET;
|
||||
window = new InfoWindow(frame);
|
||||
|
||||
if (_addWindowFor(connection, window)) {
|
||||
window->AddChild(new ConnectionInfoView(connection));
|
||||
window->Show();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// failed
|
||||
delete window;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t InfoWindowManager::openWindowFor(
|
||||
const media_input &input) {
|
||||
D_WINDOW(("InfoWindowManager::openWindowFor(input)\n"));
|
||||
|
||||
// make absolutely sure we're locked
|
||||
if (!IsLocked()) {
|
||||
debugger("The looper must be locked !");
|
||||
}
|
||||
|
||||
BWindow *window = 0;
|
||||
if (_findWindowFor(input.destination, &window)) {
|
||||
// window for this node already exists, activate it
|
||||
window->SetWorkspaces(B_CURRENT_WORKSPACE);
|
||||
window->Activate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
BRect frame = InfoView::M_DEFAULT_FRAME;
|
||||
frame.OffsetTo(m_nextWindowPosition);
|
||||
m_nextWindowPosition += M_DEFAULT_OFFSET;
|
||||
window = new InfoWindow(frame);
|
||||
|
||||
if (_addWindowFor(input, window)) {
|
||||
window->AddChild(new EndPointInfoView(input));
|
||||
window->Show();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// failed
|
||||
delete window;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t InfoWindowManager::openWindowFor(
|
||||
const media_output &output) {
|
||||
D_WINDOW(("InfoWindowManager::openWindowFor(output)\n"));
|
||||
|
||||
// make absolutely sure we're locked
|
||||
if (!IsLocked()) {
|
||||
debugger("The looper must be locked !");
|
||||
}
|
||||
|
||||
BWindow *window = 0;
|
||||
if (_findWindowFor(output.source, &window)) {
|
||||
// window for this node already exists, activate it
|
||||
window->SetWorkspaces(B_CURRENT_WORKSPACE);
|
||||
window->Activate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
BRect frame = InfoView::M_DEFAULT_FRAME;
|
||||
frame.OffsetTo(m_nextWindowPosition);
|
||||
m_nextWindowPosition += M_DEFAULT_OFFSET;
|
||||
window = new BWindow(frame, "", B_DOCUMENT_WINDOW, 0);
|
||||
|
||||
if (_addWindowFor(output, window)) {
|
||||
window->AddChild(new EndPointInfoView(output));
|
||||
window->Show();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// failed
|
||||
delete window;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BLooper impl
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void InfoWindowManager::MessageReceived(
|
||||
BMessage *message) {
|
||||
D_MESSAGE(("InfoWindowManager::MessageReceived()\n"));
|
||||
|
||||
switch (message->what) {
|
||||
case M_LIVE_NODE_WINDOW_CLOSED: {
|
||||
D_MESSAGE((" -> M_LIVE_NODE_WINDOW_CLOSED\n"));
|
||||
int32 nodeID;
|
||||
if (message->FindInt32("nodeID", &nodeID) != B_OK) {
|
||||
return;
|
||||
}
|
||||
_removeWindowFor(nodeID);
|
||||
break;
|
||||
}
|
||||
case M_DORMANT_NODE_WINDOW_CLOSED: {
|
||||
D_MESSAGE((" -> M_DORMANT_NODE_WINDOW_CLOSED\n"));
|
||||
dormant_node_info info;
|
||||
if (message->FindInt32("addOnID", &info.addon) != B_OK) {
|
||||
return;
|
||||
}
|
||||
if (message->FindInt32("flavorID", &info.flavor_id) != B_OK) {
|
||||
return;
|
||||
}
|
||||
_removeWindowFor(info);
|
||||
break;
|
||||
}
|
||||
case M_CONNECTION_WINDOW_CLOSED: {
|
||||
D_MESSAGE((" -> M_CONNECTION_WINDOW_CLOSED\n"));
|
||||
media_source source;
|
||||
if (message->FindInt32("source_port", &source.port) != B_OK) {
|
||||
return;
|
||||
}
|
||||
if (message->FindInt32("source_id", &source.id) != B_OK) {
|
||||
return;
|
||||
}
|
||||
media_destination destination;
|
||||
if (message->FindInt32("destination_port", &destination.port) != B_OK) {
|
||||
return;
|
||||
}
|
||||
if (message->FindInt32("destination_id", &destination.id) != B_OK) {
|
||||
return;
|
||||
}
|
||||
_removeWindowFor(source, destination);
|
||||
break;
|
||||
}
|
||||
case M_INPUT_WINDOW_CLOSED: {
|
||||
D_MESSAGE((" -> M_INPUT_WINDOW_CLOSED\n"));
|
||||
media_destination destination;
|
||||
if (message->FindInt32("destination_port", &destination.port) != B_OK) {
|
||||
return;
|
||||
}
|
||||
if (message->FindInt32("destination_id", &destination.id) != B_OK) {
|
||||
return;
|
||||
}
|
||||
_removeWindowFor(destination);
|
||||
break;
|
||||
}
|
||||
case M_OUTPUT_WINDOW_CLOSED: {
|
||||
D_MESSAGE((" -> M_OUTPUT_WINDOW_CLOSED\n"));
|
||||
media_source source;
|
||||
if (message->FindInt32("source_port", &source.port) != B_OK) {
|
||||
return;
|
||||
}
|
||||
if (message->FindInt32("source_id", &source.id) != B_OK) {
|
||||
return;
|
||||
}
|
||||
_removeWindowFor(source);
|
||||
break;
|
||||
}
|
||||
case NodeRef::M_RELEASED: {
|
||||
D_MESSAGE((" -> NodeRef::M_RELEASED\n"));
|
||||
int32 nodeID;
|
||||
if (message->FindInt32("nodeID", &nodeID) != B_OK) {
|
||||
return;
|
||||
}
|
||||
BWindow *window;
|
||||
if (_findWindowFor(nodeID, &window)) {
|
||||
window->Lock();
|
||||
window->Quit();
|
||||
_removeWindowFor(nodeID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_CONNECTION_BROKEN: {
|
||||
D_MESSAGE((" -> B_MEDIA_CONNECTION_BROKEN\n"));
|
||||
const void *data;
|
||||
ssize_t dataSize;
|
||||
if (message->FindData("source", B_RAW_TYPE, &data, &dataSize) != B_OK) {
|
||||
return;
|
||||
}
|
||||
const media_source source = *reinterpret_cast<const media_source *>(data);
|
||||
if (message->FindData("destination", B_RAW_TYPE, &data, &dataSize) != B_OK) {
|
||||
return;
|
||||
}
|
||||
const media_destination destination = *reinterpret_cast<const media_destination *>(data);
|
||||
BWindow *window;
|
||||
if (_findWindowFor(source, destination, &window)) {
|
||||
window->Lock();
|
||||
window->Quit();
|
||||
_removeWindowFor(source, destination);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLooper::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool InfoWindowManager::_addWindowFor(
|
||||
const NodeRef *ref,
|
||||
BWindow *window) {
|
||||
D_INTERNAL(("InfoWindowManager::_addWindowFor(live_node)\n"));
|
||||
|
||||
if (!m_liveNodeWindows) {
|
||||
m_liveNodeWindows = new BList();
|
||||
}
|
||||
|
||||
live_node_window *entry = new live_node_window(ref, window);
|
||||
if (m_liveNodeWindows->AddItem(reinterpret_cast<void *>(entry))) {
|
||||
add_observer(this, entry->ref);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_findWindowFor(
|
||||
int32 nodeID,
|
||||
BWindow **outWindow) {
|
||||
D_INTERNAL(("InfoWindowManager::_findWindowFor(live_node)\n"));
|
||||
|
||||
if (!m_liveNodeWindows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_liveNodeWindows->CountItems(); i++) {
|
||||
live_node_window *entry = static_cast<live_node_window *>
|
||||
(m_liveNodeWindows->ItemAt(i));
|
||||
if (entry->ref->id() == nodeID) {
|
||||
*outWindow = entry->window;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InfoWindowManager::_removeWindowFor(
|
||||
int32 nodeID) {
|
||||
D_INTERNAL(("InfoWindowManager::_removeWindowFor(live_node)\n"));
|
||||
|
||||
if (!m_liveNodeWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_liveNodeWindows->CountItems(); i++) {
|
||||
live_node_window *entry = static_cast<live_node_window *>
|
||||
(m_liveNodeWindows->ItemAt(i));
|
||||
if (entry->ref->id() == nodeID) {
|
||||
m_liveNodeWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
remove_observer(this, entry->ref);
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_liveNodeWindows->CountItems() <= 0) {
|
||||
delete m_liveNodeWindows;
|
||||
m_liveNodeWindows = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_addWindowFor(
|
||||
const dormant_node_info &info,
|
||||
BWindow *window) {
|
||||
D_INTERNAL(("InfoWindowManager::_addWindowFor(dormant_node)\n"));
|
||||
|
||||
if (!m_dormantNodeWindows) {
|
||||
m_dormantNodeWindows = new BList();
|
||||
}
|
||||
|
||||
dormant_node_window *entry = new dormant_node_window(info, window);
|
||||
return m_dormantNodeWindows->AddItem(reinterpret_cast<void *>(entry));
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_findWindowFor(
|
||||
const dormant_node_info &info,
|
||||
BWindow **outWindow) {
|
||||
D_INTERNAL(("InfoWindowManager::_findWindowFor(dormant_node)\n"));
|
||||
|
||||
if (!m_dormantNodeWindows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_dormantNodeWindows->CountItems(); i++) {
|
||||
dormant_node_window *entry = static_cast<dormant_node_window *>
|
||||
(m_dormantNodeWindows->ItemAt(i));
|
||||
if ((entry->info.addon == info.addon)
|
||||
&& (entry->info.flavor_id == info.flavor_id)) {
|
||||
*outWindow = entry->window;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InfoWindowManager::_removeWindowFor(
|
||||
const dormant_node_info &info) {
|
||||
D_INTERNAL(("InfoWindowManager::_removeWindowFor(dormant_node)\n"));
|
||||
|
||||
if (!m_dormantNodeWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_dormantNodeWindows->CountItems(); i++) {
|
||||
dormant_node_window *entry = static_cast<dormant_node_window *>
|
||||
(m_dormantNodeWindows->ItemAt(i));
|
||||
if ((entry->info.addon == info.addon)
|
||||
&& (entry->info.flavor_id == info.flavor_id)) {
|
||||
m_dormantNodeWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dormantNodeWindows->CountItems() >= 0) {
|
||||
delete m_dormantNodeWindows;
|
||||
m_dormantNodeWindows = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_addWindowFor(
|
||||
const Connection &connection,
|
||||
BWindow *window) {
|
||||
D_INTERNAL(("InfoWindowManager::_addWindowFor(connection)\n"));
|
||||
|
||||
if (!m_connectionWindows) {
|
||||
m_connectionWindows = new BList();
|
||||
}
|
||||
|
||||
connection_window *entry = new connection_window(connection.source(),
|
||||
connection.destination(),
|
||||
window);
|
||||
return m_connectionWindows->AddItem(reinterpret_cast<void *>(entry));
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_findWindowFor(
|
||||
const media_source &source,
|
||||
const media_destination &destination,
|
||||
BWindow **outWindow) {
|
||||
D_INTERNAL(("InfoWindowManager::_findWindowFor(connection)\n"));
|
||||
|
||||
if (!m_connectionWindows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_connectionWindows->CountItems(); i++) {
|
||||
connection_window *entry = static_cast<connection_window *>
|
||||
(m_connectionWindows->ItemAt(i));
|
||||
if ((entry->source == source)
|
||||
&& (entry->destination == destination)) {
|
||||
*outWindow = entry->window;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InfoWindowManager::_removeWindowFor(
|
||||
const media_source &source,
|
||||
const media_destination &destination) {
|
||||
D_INTERNAL(("InfoWindowManager::_removeWindowFor(connection)\n"));
|
||||
|
||||
if (!m_connectionWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_connectionWindows->CountItems(); i++) {
|
||||
connection_window *entry = static_cast<connection_window *>
|
||||
(m_connectionWindows->ItemAt(i));
|
||||
if ((entry->source == source)
|
||||
&& (entry->destination == destination)) {
|
||||
m_connectionWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_connectionWindows->CountItems() >= 0) {
|
||||
delete m_connectionWindows;
|
||||
m_connectionWindows = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_addWindowFor(
|
||||
const media_input &input,
|
||||
BWindow *window) {
|
||||
D_INTERNAL(("InfoWindowManager::_addWindowFor(input)\n"));
|
||||
|
||||
if (!m_inputWindows) {
|
||||
m_inputWindows = new BList();
|
||||
}
|
||||
|
||||
input_window *entry = new input_window(input.destination, window);
|
||||
return m_inputWindows->AddItem(reinterpret_cast<void *>(entry));
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_findWindowFor(
|
||||
const media_destination &destination,
|
||||
BWindow **outWindow) {
|
||||
D_INTERNAL(("InfoWindowManager::_findWindowFor(input)\n"));
|
||||
|
||||
if (!m_inputWindows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_inputWindows->CountItems(); i++) {
|
||||
input_window *entry = static_cast<input_window *>
|
||||
(m_inputWindows->ItemAt(i));
|
||||
if (entry->destination == destination) {
|
||||
*outWindow = entry->window;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InfoWindowManager::_removeWindowFor(
|
||||
const media_destination &destination) {
|
||||
D_INTERNAL(("InfoWindowManager::_removeWindowFor(input)\n"));
|
||||
|
||||
if (!m_inputWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_inputWindows->CountItems(); i++) {
|
||||
input_window *entry = static_cast<input_window *>
|
||||
(m_inputWindows->ItemAt(i));
|
||||
if (entry->destination == destination) {
|
||||
m_inputWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_inputWindows->CountItems() >= 0) {
|
||||
delete m_inputWindows;
|
||||
m_inputWindows = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_addWindowFor(
|
||||
const media_output &output,
|
||||
BWindow *window) {
|
||||
D_INTERNAL(("InfoWindowManager::_addWindowFor(output)\n"));
|
||||
|
||||
if (!m_outputWindows) {
|
||||
m_outputWindows = new BList();
|
||||
}
|
||||
|
||||
output_window *entry = new output_window(output.source, window);
|
||||
return m_outputWindows->AddItem(reinterpret_cast<void *>(entry));
|
||||
}
|
||||
|
||||
bool InfoWindowManager::_findWindowFor(
|
||||
const media_source &source,
|
||||
BWindow **outWindow) {
|
||||
D_INTERNAL(("InfoWindowManager::_findWindowFor(output)\n"));
|
||||
|
||||
if (!m_outputWindows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_outputWindows->CountItems(); i++) {
|
||||
output_window *entry = static_cast<output_window *>
|
||||
(m_outputWindows->ItemAt(i));
|
||||
if (entry->source == source) {
|
||||
*outWindow = entry->window;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InfoWindowManager::_removeWindowFor(
|
||||
const media_source &source) {
|
||||
D_INTERNAL(("InfoWindowManager::_removeWindowFor(output)\n"));
|
||||
|
||||
if (!m_outputWindows) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < m_outputWindows->CountItems(); i++) {
|
||||
output_window *entry = static_cast<output_window *>
|
||||
(m_outputWindows->ItemAt(i));
|
||||
if (entry->source == source) {
|
||||
m_outputWindows->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_outputWindows->CountItems() >= 0) {
|
||||
delete m_outputWindows;
|
||||
m_outputWindows = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// END -- InfoWindowManager.cpp --
|
180
src/apps/cortex/InfoView/InfoWindowManager.h
Normal file
180
src/apps/cortex/InfoView/InfoWindowManager.h
Normal file
@ -0,0 +1,180 @@
|
||||
// InfoWindowManager.h
|
||||
//
|
||||
// * PURPOSE
|
||||
// Manages all the ParameterWindows and control panels.
|
||||
// Will not let you open multiple windows referring to
|
||||
// the same node, and takes care of quitting them all
|
||||
// when shut down.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 17feb2000 Begun
|
||||
//
|
||||
|
||||
#ifndef __InfoWindowManager_H__
|
||||
#define __InfoWindowManager_H__
|
||||
|
||||
// Application Kit
|
||||
#include <Looper.h>
|
||||
// Interface Kit
|
||||
#include <Point.h>
|
||||
|
||||
class BList;
|
||||
class BWindow;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class Connection;
|
||||
class NodeRef;
|
||||
|
||||
class InfoWindowManager :
|
||||
public BLooper {
|
||||
|
||||
public: // *** constants
|
||||
|
||||
// the screen position where the first window should
|
||||
// be displayed
|
||||
static const BPoint M_INIT_POSITION;
|
||||
|
||||
// horizontal/vertical offset by which subsequent
|
||||
// windows positions are shifted
|
||||
static const BPoint M_DEFAULT_OFFSET;
|
||||
|
||||
enum message_t {
|
||||
M_INFO_WINDOW_REQUESTED = InfoView_message_base,
|
||||
|
||||
M_LIVE_NODE_WINDOW_CLOSED,
|
||||
|
||||
M_DORMANT_NODE_WINDOW_CLOSED,
|
||||
|
||||
M_CONNECTION_WINDOW_CLOSED,
|
||||
|
||||
M_INPUT_WINDOW_CLOSED,
|
||||
|
||||
M_OUTPUT_WINDOW_CLOSED
|
||||
};
|
||||
|
||||
private: // *** ctor/dtor
|
||||
|
||||
// hidden ctor; is called only from inside Instance()
|
||||
InfoWindowManager();
|
||||
|
||||
public:
|
||||
|
||||
// quits all registered info windows
|
||||
virtual ~InfoWindowManager();
|
||||
|
||||
public: // *** singleton access
|
||||
|
||||
// access to the one and only instance of this class
|
||||
static InfoWindowManager *Instance();
|
||||
|
||||
// will delete the singleton instance and take down all
|
||||
// open windows
|
||||
static void shutDown();
|
||||
|
||||
public: // *** operations
|
||||
|
||||
status_t openWindowFor(
|
||||
const NodeRef *ref);
|
||||
|
||||
status_t openWindowFor(
|
||||
const dormant_node_info &info);
|
||||
|
||||
status_t openWindowFor(
|
||||
const Connection &connection);
|
||||
|
||||
status_t openWindowFor(
|
||||
const media_input &input);
|
||||
|
||||
status_t openWindowFor(
|
||||
const media_output &output);
|
||||
|
||||
public: // *** BLooper impl
|
||||
|
||||
virtual void MessageReceived(
|
||||
BMessage *message);
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// management of windows for live nodes
|
||||
bool _addWindowFor(
|
||||
const NodeRef *ref,
|
||||
BWindow *window);
|
||||
bool _findWindowFor(
|
||||
int32 nodeID,
|
||||
BWindow **outWindow);
|
||||
void _removeWindowFor(
|
||||
int32 nodeID);
|
||||
|
||||
// management of windows for dormant nodes
|
||||
bool _addWindowFor(
|
||||
const dormant_node_info &info,
|
||||
BWindow *window);
|
||||
bool _findWindowFor(
|
||||
const dormant_node_info &info,
|
||||
BWindow **outWindow);
|
||||
void _removeWindowFor(
|
||||
const dormant_node_info &info);
|
||||
|
||||
// management of windows for connections
|
||||
bool _addWindowFor(
|
||||
const Connection &connection,
|
||||
BWindow *window);
|
||||
bool _findWindowFor(
|
||||
const media_source &source,
|
||||
const media_destination &destination,
|
||||
BWindow **outWindow);
|
||||
void _removeWindowFor(
|
||||
const media_source &source,
|
||||
const media_destination &destination);
|
||||
|
||||
// management of windows for media_inputs
|
||||
bool _addWindowFor(
|
||||
const media_input &input,
|
||||
BWindow *window);
|
||||
bool _findWindowFor(
|
||||
const media_destination &destination,
|
||||
BWindow **outWindow);
|
||||
void _removeWindowFor(
|
||||
const media_destination &destination);
|
||||
|
||||
// management of windows for media_outputs
|
||||
bool _addWindowFor(
|
||||
const media_output &output,
|
||||
BWindow *window);
|
||||
bool _findWindowFor(
|
||||
const media_source &source,
|
||||
BWindow **outWindow);
|
||||
void _removeWindowFor(
|
||||
const media_source &source);
|
||||
|
||||
private: // *** data members
|
||||
|
||||
// list of all currently open windows about live nodes
|
||||
BList *m_liveNodeWindows;
|
||||
|
||||
// list of all currently open windows about dormant nodes
|
||||
BList *m_dormantNodeWindows;
|
||||
|
||||
// list of all currently open windows about connections
|
||||
BList *m_connectionWindows;
|
||||
|
||||
// list of all currently open windows about media_inputs
|
||||
BList *m_inputWindows;
|
||||
|
||||
// list of all currently open windows about media_outputs
|
||||
BList *m_outputWindows;
|
||||
|
||||
// the BPoint at which the last InfoWindow was initially
|
||||
// opened
|
||||
BPoint m_nextWindowPosition;
|
||||
|
||||
private: // *** static members
|
||||
|
||||
// the magic singleton instance
|
||||
static InfoWindowManager *s_instance;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__InfoWindowManager_H__*/
|
110
src/apps/cortex/InfoView/LiveNodeInfoView.cpp
Normal file
110
src/apps/cortex/InfoView/LiveNodeInfoView.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
// LiveNodeInfoView.cpp
|
||||
|
||||
#include "LiveNodeInfoView.h"
|
||||
// InfoView
|
||||
#include "InfoWindowManager.h"
|
||||
// NodeManager
|
||||
#include "NodeGroup.h"
|
||||
#include "NodeRef.h"
|
||||
// Support
|
||||
#include "MediaIcon.h"
|
||||
#include "MediaString.h"
|
||||
|
||||
// Media Kit
|
||||
#include <MediaNode.h>
|
||||
|
||||
// Interface Kit
|
||||
#include <Window.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
LiveNodeInfoView::LiveNodeInfoView(
|
||||
const NodeRef *ref)
|
||||
: InfoView(ref->name(), "Live Media Node",
|
||||
new MediaIcon(ref->nodeInfo(), B_LARGE_ICON)),
|
||||
m_nodeID(ref->id())
|
||||
{
|
||||
D_METHOD(("LiveNodeInfoView::LiveNodeInfoView()\n"));
|
||||
|
||||
// adjust view properties
|
||||
setSideBarWidth(be_plain_font->StringWidth(" Run Mode ") + 2 * InfoView::M_H_MARGIN);
|
||||
|
||||
// add "Node ID" field
|
||||
BString s;
|
||||
s << ref->id();
|
||||
addField("Node ID", s);
|
||||
|
||||
// add "Port" field
|
||||
s = "";
|
||||
s << ref->node().port;
|
||||
port_info portInfo;
|
||||
if (get_port_info(ref->node().port, &portInfo) == B_OK)
|
||||
{
|
||||
s << " (" << portInfo.name << ")";
|
||||
}
|
||||
addField("Port", s);
|
||||
|
||||
// add separator field
|
||||
addField("", "");
|
||||
|
||||
// add "Kinds" field
|
||||
addField("Kinds", MediaString::getStringFor(static_cast<node_kind>(ref->kind())));
|
||||
|
||||
// add "Run Mode" field
|
||||
BMediaNode::run_mode runMode = static_cast<BMediaNode::run_mode>(ref->runMode());
|
||||
if (runMode < 1)
|
||||
{
|
||||
NodeGroup *group = ref->group();
|
||||
if (group)
|
||||
{
|
||||
runMode = group->runMode();
|
||||
}
|
||||
}
|
||||
addField("Run Mode", MediaString::getStringFor(runMode));
|
||||
|
||||
// add "Latency" field
|
||||
bigtime_t latency;
|
||||
if (ref->totalLatency(&latency) == B_OK)
|
||||
{
|
||||
s = "";
|
||||
if (latency > 0)
|
||||
{
|
||||
s << static_cast<float>(latency) / 1000.0f << " ms";
|
||||
}
|
||||
else
|
||||
{
|
||||
s = "?";
|
||||
}
|
||||
addField("Latency", s);
|
||||
}
|
||||
}
|
||||
|
||||
LiveNodeInfoView::~LiveNodeInfoView() {
|
||||
D_METHOD(("LiveNodeInfoView::~LiveNodeInfoView()\n"));
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BView implementation (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void LiveNodeInfoView::DetachedFromWindow() {
|
||||
D_METHOD(("LiveNodeInfoView::DetachedFromWindow()\n"));
|
||||
|
||||
InfoWindowManager *manager = InfoWindowManager::Instance();
|
||||
if (manager) {
|
||||
BMessage message(InfoWindowManager::M_LIVE_NODE_WINDOW_CLOSED);
|
||||
message.AddInt32("nodeID", m_nodeID);
|
||||
manager->PostMessage(&message);
|
||||
}
|
||||
}
|
||||
|
||||
// END -- LiveNodeInfoView.cpp --
|
45
src/apps/cortex/InfoView/LiveNodeInfoView.h
Normal file
45
src/apps/cortex/InfoView/LiveNodeInfoView.h
Normal file
@ -0,0 +1,45 @@
|
||||
// LiveNodeInfoView.h (Cortex/InfoView)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Defines fields to be displayed for all live MediaNodes,
|
||||
// which can be added to for special nodes like file-readers
|
||||
// (see FileNodeInfoView) etc.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 5nov99 Begun
|
||||
//
|
||||
|
||||
#ifndef __LiveNodeInfoView_H__
|
||||
#define __LiveNodeInfoView_H__
|
||||
|
||||
#include "InfoView.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeRef;
|
||||
|
||||
class LiveNodeInfoView :
|
||||
public InfoView {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// adds the live-node relevant fields the the
|
||||
// InfoView
|
||||
LiveNodeInfoView(
|
||||
const NodeRef *ref);
|
||||
|
||||
virtual ~LiveNodeInfoView();
|
||||
|
||||
public: // *** BView impl
|
||||
|
||||
// notify InfoWindowManager
|
||||
virtual void DetachedFromWindow();
|
||||
|
||||
private: // *** data members
|
||||
|
||||
int32 m_nodeID;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __LiveNodeInfoView_H__ */
|
27
src/apps/cortex/LICENSE.Cortex
Normal file
27
src/apps/cortex/LICENSE.Cortex
Normal file
@ -0,0 +1,27 @@
|
||||
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.
|
773
src/apps/cortex/MediaRoutingView/MediaJack.cpp
Normal file
773
src/apps/cortex/MediaRoutingView/MediaJack.cpp
Normal file
@ -0,0 +1,773 @@
|
||||
// MediaJack.cpp
|
||||
// c.lenz 10oct99
|
||||
|
||||
#include "MediaJack.h"
|
||||
// MediaRoutingView
|
||||
#include "MediaRoutingDefs.h"
|
||||
#include "MediaRoutingView.h"
|
||||
#include "MediaWire.h"
|
||||
// InfoWindow
|
||||
#include "InfoWindowManager.h"
|
||||
// Support
|
||||
#include "cortex_ui.h"
|
||||
#include "MediaString.h"
|
||||
// TipManager
|
||||
#include "TipManager.h"
|
||||
|
||||
// Application Kit
|
||||
#include <Application.h>
|
||||
// Interface Kit
|
||||
#include <Bitmap.h>
|
||||
#include <MenuItem.h>
|
||||
#include <PopUpMenu.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_DRAW(x) //PRINT (x)
|
||||
#define D_MOUSE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// constants
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
float MediaJack::M_DEFAULT_WIDTH = 5.0;
|
||||
float MediaJack::M_DEFAULT_HEIGHT = 10.0;
|
||||
const float MediaJack::M_DEFAULT_GAP = 5.0;
|
||||
const int32 MediaJack::M_MAX_ABBR_LENGTH = 3;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
MediaJack::MediaJack(
|
||||
media_input input)
|
||||
: DiagramEndPoint(BRect(0.0, 0.0, M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT)),
|
||||
m_jackType(M_INPUT),
|
||||
m_bitmap(0),
|
||||
m_index(input.destination.id),
|
||||
m_node(input.node),
|
||||
m_source(input.source),
|
||||
m_destination(input.destination),
|
||||
m_format(input.format),
|
||||
m_label(input.name),
|
||||
m_abbreviation("")
|
||||
{
|
||||
D_METHOD(("MediaJack::MediaJack()\n"));
|
||||
makeSelectable(false);
|
||||
if (m_label == "")
|
||||
m_label = "Input";
|
||||
_updateAbbreviation();
|
||||
}
|
||||
|
||||
MediaJack::MediaJack(
|
||||
media_output output)
|
||||
: DiagramEndPoint(BRect(0.0, 0.0, M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT)),
|
||||
m_jackType(M_OUTPUT),
|
||||
m_bitmap(0),
|
||||
m_index(output.source.id),
|
||||
m_node(output.node),
|
||||
m_source(output.source),
|
||||
m_destination(output.destination),
|
||||
m_format(output.format),
|
||||
m_label(output.name),
|
||||
m_abbreviation("")
|
||||
{
|
||||
D_METHOD(("MediaJack::MediaJack()\n"));
|
||||
makeSelectable(false);
|
||||
if (m_label == "")
|
||||
m_label = "Output";
|
||||
_updateAbbreviation();
|
||||
}
|
||||
|
||||
MediaJack::~MediaJack()
|
||||
{
|
||||
D_METHOD(("MediaJack::~MediaJack()\n"));
|
||||
delete m_bitmap;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** accessors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
status_t MediaJack::getInput(
|
||||
media_input *input) const
|
||||
{
|
||||
D_METHOD(("MediaJack::getInput()\n"));
|
||||
if (isInput())
|
||||
{
|
||||
input->node = m_node;
|
||||
input->source = m_source;
|
||||
input->destination = m_destination;
|
||||
input->format = m_format;
|
||||
m_label.CopyInto(input->name, 0, 64);
|
||||
return B_OK;
|
||||
}
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t MediaJack::getOutput(
|
||||
media_output *output) const
|
||||
{
|
||||
D_METHOD(("MediaJack::getOutput()\n"));
|
||||
if (isOutput())
|
||||
{
|
||||
output->node = m_node;
|
||||
output->source = m_source;
|
||||
output->destination = m_destination;
|
||||
output->format = m_format;
|
||||
m_label.CopyInto(output->name, 0, 64);
|
||||
return B_OK;
|
||||
}
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from DiagramEndPoint (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MediaJack::attachedToDiagram()
|
||||
{
|
||||
D_METHOD(("MediaJack::attachedToDiagram()\n"));
|
||||
_updateBitmap();
|
||||
}
|
||||
|
||||
void MediaJack::detachedFromDiagram()
|
||||
{
|
||||
D_METHOD(("MediaJack::detachedFromDiagram()\n"));
|
||||
|
||||
// make sure we're no longer displaying a tooltip
|
||||
TipManager *tips = TipManager::Instance();
|
||||
tips->hideTip(view()->ConvertToScreen(frame()));
|
||||
}
|
||||
|
||||
void MediaJack::drawEndPoint()
|
||||
{
|
||||
D_DRAW(("MediaJack::drawEndPoint()\n"));
|
||||
|
||||
if (m_bitmap)
|
||||
{
|
||||
view()->DrawBitmap(m_bitmap, frame().LeftTop());
|
||||
}
|
||||
}
|
||||
|
||||
BPoint MediaJack::connectionPoint() const
|
||||
{
|
||||
D_METHOD(("MediaJack::connectionPoint()\n"));
|
||||
|
||||
switch (dynamic_cast<MediaRoutingView *>(view())->getLayout())
|
||||
{
|
||||
case MediaRoutingView::M_ICON_VIEW:
|
||||
{
|
||||
if (isInput())
|
||||
{
|
||||
return BPoint(frame().left - 1.0, frame().top + frame().Height() / 2.0);
|
||||
}
|
||||
else if (isOutput())
|
||||
{
|
||||
return BPoint(frame().right + 1.0, frame().top + frame().Height() / 2.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MediaRoutingView::M_MINI_ICON_VIEW:
|
||||
{
|
||||
if (isInput())
|
||||
{
|
||||
return BPoint(frame().left + frame().Width() / 2.0, frame().top - 1.0);
|
||||
}
|
||||
else if (isOutput())
|
||||
{
|
||||
return BPoint(frame().left + frame().Width() / 2.0, frame().bottom + 1.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return BPoint(-1.0, -1.0);
|
||||
}
|
||||
|
||||
bool MediaJack::connectionRequested(
|
||||
DiagramEndPoint *which)
|
||||
{
|
||||
D_METHOD(("MediaJack::connectionRequested()\n"));
|
||||
|
||||
MediaJack *otherJack = dynamic_cast<MediaJack *>(which);
|
||||
if (otherJack && (otherJack->m_jackType != m_jackType) && !isConnected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MediaJack::mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks)
|
||||
{
|
||||
D_MOUSE(("MediaJack::mouseOver()\n"));
|
||||
|
||||
// if connected, redirect to the wire
|
||||
if (isConnected())
|
||||
{
|
||||
dynamic_cast<MediaWire *>(wire())->mouseDown(point, buttons, clicks);
|
||||
return;
|
||||
}
|
||||
|
||||
// else we handle the mouse event ourselves
|
||||
switch (buttons)
|
||||
{
|
||||
case B_SECONDARY_MOUSE_BUTTON:
|
||||
{
|
||||
showContextMenu(point);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
DiagramEndPoint::mouseDown(point, buttons, clicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaJack::mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit)
|
||||
{
|
||||
D_MOUSE(("MediaJack::mouseOver()\n"));
|
||||
switch (transit)
|
||||
{
|
||||
case B_ENTERED_VIEW:
|
||||
{
|
||||
be_app->SetCursor(M_CABLE_CURSOR);
|
||||
TipManager *tips = TipManager::Instance();
|
||||
BString tipText = m_label.String();
|
||||
tipText << " (" << MediaString::getStringFor(m_format.type) << ")";
|
||||
tips->showTip(tipText.String(),view()->ConvertToScreen(frame()),
|
||||
TipManager::LEFT_OFFSET_FROM_POINTER, BPoint(12.0, 8.0));
|
||||
break;
|
||||
}
|
||||
case B_EXITED_VIEW:
|
||||
{
|
||||
if (!view()->isWireTracking())
|
||||
{
|
||||
be_app->SetCursor(B_HAND_CURSOR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaJack::messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message)
|
||||
{
|
||||
D_MOUSE(("MediaJack::messageDragged()\n"));
|
||||
switch (transit)
|
||||
{
|
||||
case B_ENTERED_VIEW:
|
||||
{
|
||||
be_app->SetCursor(M_CABLE_CURSOR);
|
||||
break;
|
||||
}
|
||||
case B_EXITED_VIEW:
|
||||
{
|
||||
if (!view()->isWireTracking())
|
||||
{
|
||||
be_app->SetCursor(B_HAND_CURSOR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
DiagramEndPoint::messageDragged(point, transit, message);
|
||||
}
|
||||
|
||||
void MediaJack::selected()
|
||||
{
|
||||
D_METHOD(("MediaJack::selected()\n"));
|
||||
_updateBitmap();
|
||||
view()->Invalidate(frame());
|
||||
}
|
||||
|
||||
void MediaJack::deselected()
|
||||
{
|
||||
D_METHOD(("MediaJack::deselected()\n"));
|
||||
_updateBitmap();
|
||||
view()->Invalidate(frame());
|
||||
}
|
||||
|
||||
void MediaJack::connected()
|
||||
{
|
||||
D_METHOD(("MediaJack::connected()\n"));
|
||||
_updateBitmap();
|
||||
view()->Invalidate(frame());
|
||||
}
|
||||
|
||||
void MediaJack::disconnected()
|
||||
{
|
||||
D_METHOD(("MediaJack::disconnected()\n"));
|
||||
_updateBitmap();
|
||||
view()->Invalidate(frame());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MediaJack::layoutChanged(
|
||||
int32 layout)
|
||||
{
|
||||
D_METHOD(("MediaJack::layoutChanged\n"));
|
||||
resizeTo(M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT);
|
||||
_updateBitmap();
|
||||
}
|
||||
|
||||
void MediaJack::setPosition(
|
||||
float offset,
|
||||
float leftTopBoundary,
|
||||
float rightBottomBoundary,
|
||||
BRegion *updateRegion)
|
||||
{
|
||||
D_METHOD(("MediaJack::setPosition\n"));
|
||||
switch (dynamic_cast<MediaRoutingView *>(view())->getLayout())
|
||||
{
|
||||
case MediaRoutingView::M_ICON_VIEW:
|
||||
{
|
||||
if (isInput())
|
||||
{
|
||||
moveTo(BPoint(leftTopBoundary, offset), updateRegion);
|
||||
}
|
||||
else if (isOutput())
|
||||
{
|
||||
moveTo(BPoint(rightBottomBoundary - frame().Width(), offset), updateRegion);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MediaRoutingView::M_MINI_ICON_VIEW:
|
||||
{
|
||||
if (isInput())
|
||||
{
|
||||
moveTo(BPoint(offset, leftTopBoundary), updateRegion);
|
||||
}
|
||||
else if (isOutput())
|
||||
{
|
||||
moveTo(BPoint(offset, rightBottomBoundary - frame().Height()), updateRegion);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal methods (private)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MediaJack::_updateBitmap()
|
||||
{
|
||||
D_METHOD(("MediaJack::_updateBitmap()\n"));
|
||||
|
||||
if (m_bitmap)
|
||||
{
|
||||
delete m_bitmap;
|
||||
}
|
||||
BBitmap *tempBitmap = new BBitmap(frame().OffsetToCopy(0.0, 0.0), B_CMAP8, true);
|
||||
tempBitmap->Lock();
|
||||
{
|
||||
BView *tempView = new BView(tempBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
|
||||
tempBitmap->AddChild(tempView);
|
||||
tempView->SetOrigin(0.0, 0.0);
|
||||
|
||||
int32 layout = dynamic_cast<MediaRoutingView *>(view())->getLayout();
|
||||
_drawInto(tempView, tempView->Bounds(), layout);
|
||||
|
||||
tempView->Sync();
|
||||
tempBitmap->RemoveChild(tempView);
|
||||
delete tempView;
|
||||
}
|
||||
tempBitmap->Unlock();
|
||||
m_bitmap = new BBitmap(tempBitmap);
|
||||
delete tempBitmap;
|
||||
}
|
||||
|
||||
void MediaJack::_drawInto(
|
||||
BView *target,
|
||||
BRect targetRect,
|
||||
int32 layout)
|
||||
{
|
||||
D_METHOD(("MediaJack::_drawInto()\n"));
|
||||
|
||||
bool selected = isConnecting() || isSelected();
|
||||
switch (layout)
|
||||
{
|
||||
case MediaRoutingView::M_ICON_VIEW:
|
||||
{
|
||||
if (isInput())
|
||||
{
|
||||
BRect r;
|
||||
BPoint p;
|
||||
|
||||
// fill rect
|
||||
r = targetRect;
|
||||
target->SetLowColor(M_GRAY_COLOR);
|
||||
r.left += 2.0;
|
||||
target->FillRect(r, B_SOLID_LOW);
|
||||
|
||||
// draw connection point
|
||||
r = targetRect;
|
||||
p.Set(0.0, frame().Height() / 2.0 - 2.0);
|
||||
target->BeginLineArray(4);
|
||||
{
|
||||
target->AddLine(r.LeftTop(),
|
||||
p,
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(r.LeftTop() + BPoint(1.0, 0.0),
|
||||
p + BPoint(1.0, 0.0),
|
||||
M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 5.0),
|
||||
r.LeftBottom(),
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 5.0),
|
||||
r.LeftBottom() + BPoint(1.0, 0.0),
|
||||
M_LIGHT_GRAY_COLOR);
|
||||
}
|
||||
target->EndLineArray();
|
||||
|
||||
if (isConnected() || isConnecting())
|
||||
{
|
||||
target->BeginLineArray(11);
|
||||
{
|
||||
target->AddLine(p, p, M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 4.0), p + BPoint(0.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 0.0), p + BPoint(4.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 4.0), p + BPoint(4.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 1.0), p + BPoint(4.0, 3.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 1.0), p + BPoint(2.0, 1.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 1.0), p + BPoint(3.0, 1.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 2.0), p + BPoint(2.0, 2.0), selected ? M_LIGHT_BLUE_COLOR : M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 2.0), p + BPoint(3.0, 2.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 3.0), p + BPoint(2.0, 3.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 3.0), p + BPoint(3.0, 3.0), M_MED_GRAY_COLOR);
|
||||
}
|
||||
target->EndLineArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
target->BeginLineArray(7);
|
||||
{
|
||||
target->AddLine(p, p + BPoint(0.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 0.0), p + BPoint(4.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 4.0), p + BPoint(4.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 1.0), p + BPoint(4.0, 3.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 1.0), p + BPoint(3.0, 1.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 2.0), p + BPoint(3.0, 2.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 3.0), p + BPoint(3.0, 3.0), M_MED_GRAY_COLOR);
|
||||
}
|
||||
target->EndLineArray();
|
||||
}
|
||||
|
||||
// draw abbreviation string
|
||||
BFont font(be_plain_font);
|
||||
font_height fh;
|
||||
font.SetSize(font.Size() - 2.0);
|
||||
font.GetHeight(&fh);
|
||||
p.x += 7.0;
|
||||
p.y = (frame().Height() / 2.0) + (fh.ascent / 2.0);
|
||||
target->SetFont(&font);
|
||||
target->SetDrawingMode(B_OP_OVER);
|
||||
target->SetHighColor((isConnected() || isConnecting()) ?
|
||||
M_MED_GRAY_COLOR :
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->DrawString(m_abbreviation.String(), p);
|
||||
}
|
||||
else if (isOutput())
|
||||
{
|
||||
BRect r;
|
||||
BPoint p;
|
||||
|
||||
// fill rect
|
||||
r = targetRect;
|
||||
target->SetLowColor(M_GRAY_COLOR);
|
||||
r.right -= 2.0;
|
||||
target->FillRect(r, B_SOLID_LOW);
|
||||
|
||||
// draw connection point
|
||||
r = targetRect;
|
||||
p.Set(targetRect.right - 4.0, frame().Height() / 2.0 - 2.0);
|
||||
target->BeginLineArray(4);
|
||||
{
|
||||
target->AddLine(r.RightTop(),
|
||||
p + BPoint(4.0, 0.0),
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(r.RightTop() + BPoint(-1.0, 0.0),
|
||||
p + BPoint(3.0, 0.0),
|
||||
M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 5.0),
|
||||
r.RightBottom(),
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 5.0),
|
||||
r.RightBottom() + BPoint(-1.0, 0.0),
|
||||
M_MED_GRAY_COLOR);
|
||||
}
|
||||
target->EndLineArray();
|
||||
|
||||
if (isConnected() || isConnecting())
|
||||
{
|
||||
target->BeginLineArray(11);
|
||||
target->AddLine(p + BPoint(4.0, 0.0), p + BPoint(4.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 4.0), p + BPoint(4.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p, p + BPoint(3.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 1.0), p + BPoint(0.0, 3.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 4.0), p + BPoint(3.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 1.0), p + BPoint(1.0, 1.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 1.0), p + BPoint(4.0, 1.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 2.0), p + BPoint(1.0, 2.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 2.0), p + BPoint(4.0, 2.0), selected ? M_LIGHT_BLUE_COLOR : M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 3.0), p + BPoint(1.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 3.0), p + BPoint(4.0, 3.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->EndLineArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
target->BeginLineArray(7);
|
||||
target->AddLine(p + BPoint(4.0, 0.0), p + BPoint(4.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p, p + BPoint(3.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 1.0), p + BPoint(0.0, 3.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 4.0), p + BPoint(3.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 1.0), p + BPoint(3.0, 1.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 2.0), p + BPoint(3.0, 2.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 3.0), p + BPoint(3.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->EndLineArray();
|
||||
}
|
||||
|
||||
// draw abbreviation string
|
||||
BFont font(be_plain_font);
|
||||
font_height fh;
|
||||
font.SetSize(font.Size() - 2.0);
|
||||
font.GetHeight(&fh);
|
||||
p.x -= font.StringWidth(m_abbreviation.String()) + 2.0;
|
||||
p.y = (frame().Height() / 2.0) + (fh.ascent / 2.0);
|
||||
target->SetFont(&font);
|
||||
target->SetDrawingMode(B_OP_OVER);
|
||||
target->SetHighColor((isConnected() || isConnecting()) ?
|
||||
M_MED_GRAY_COLOR :
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->DrawString(m_abbreviation.String(), p);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MediaRoutingView::M_MINI_ICON_VIEW:
|
||||
{
|
||||
if (isInput())
|
||||
{
|
||||
BRect r;
|
||||
BPoint p;
|
||||
|
||||
// fill rect
|
||||
r = targetRect;
|
||||
target->SetLowColor(M_GRAY_COLOR);
|
||||
r.top += 2.0;
|
||||
target->FillRect(r, B_SOLID_LOW);
|
||||
|
||||
// draw connection point
|
||||
r = targetRect;
|
||||
p.Set(frame().Width() / 2.0 - 2.0, 0.0);
|
||||
target->BeginLineArray(4);
|
||||
{
|
||||
target->AddLine(r.LeftTop(),
|
||||
p,
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(r.LeftTop() + BPoint(0.0, 1.0),
|
||||
p + BPoint(0.0, 1.0),
|
||||
M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(5.0, 0.0),
|
||||
r.RightTop(),
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(5.0, 1.0),
|
||||
r.RightTop() + BPoint(0.0, 1.0),
|
||||
M_LIGHT_GRAY_COLOR);
|
||||
}
|
||||
target->EndLineArray();
|
||||
if (isConnected() || isConnecting())
|
||||
{
|
||||
target->BeginLineArray(11);
|
||||
target->AddLine(p, p, M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 0.0), p + BPoint(4.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 1.0), p + BPoint(0.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 1.0), p + BPoint(4.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 4.0), p + BPoint(3.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 0.0), p + BPoint(1.0, 2.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 3.0), p + BPoint(1.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 0.0), p + BPoint(2.0, 2.0), selected ? M_LIGHT_BLUE_COLOR : M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 3.0), p + BPoint(2.0, 3.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 0.0), p + BPoint(3.0, 2.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 3.0), p + BPoint(3.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->EndLineArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
target->BeginLineArray(7);
|
||||
target->AddLine(p, p + BPoint(4.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(0.0, 1.0), p + BPoint(0.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 1.0), p + BPoint(4.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 4.0), p + BPoint(3.0, 4.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 1.0), p + BPoint(1.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 1.0), p + BPoint(2.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 1.0), p + BPoint(3.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->EndLineArray();
|
||||
}
|
||||
}
|
||||
else if (isOutput())
|
||||
{
|
||||
BRect r = targetRect;
|
||||
BPoint p;
|
||||
|
||||
// fill rect
|
||||
r = targetRect;
|
||||
target->SetLowColor(M_GRAY_COLOR);
|
||||
r.bottom -= 2.0;
|
||||
target->FillRect(r, B_SOLID_LOW);
|
||||
|
||||
// draw connection point
|
||||
r = targetRect;
|
||||
p.Set(frame().Width() / 2.0 - 2.0, targetRect.bottom - 4.0);
|
||||
target->BeginLineArray(4);
|
||||
{
|
||||
target->AddLine(r.LeftBottom(),
|
||||
p + BPoint(0.0, 4.0),
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(r.LeftBottom() + BPoint(0.0, -1.0),
|
||||
p + BPoint(0.0, 3.0),
|
||||
M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(5.0, 4.0),
|
||||
r.RightBottom(),
|
||||
M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(5.0, 3.0),
|
||||
r.RightBottom() + BPoint(0.0, -1.0),
|
||||
M_MED_GRAY_COLOR);
|
||||
}
|
||||
target->EndLineArray();
|
||||
if (isConnected() || isConnecting())
|
||||
{
|
||||
target->BeginLineArray(11);
|
||||
target->AddLine(p + BPoint(0.0, 4.0), p + BPoint(0.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 4.0), p + BPoint(4.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p, p + BPoint(0.0, 3.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 0.0), p + BPoint(3.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 0.0), p + BPoint(4.0, 3.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 1.0), p + BPoint(1.0, 1.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 2.0), p + BPoint(1.0, 4.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 1.0), p + BPoint(2.0, 1.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 2.0), p + BPoint(2.0, 4.0), selected ? M_LIGHT_BLUE_COLOR : M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 1.0), p + BPoint(3.0, 1.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 2.0), p + BPoint(3.0, 4.0), selected ? M_BLUE_COLOR : M_DARK_GRAY_COLOR);
|
||||
target->EndLineArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
target->BeginLineArray(7);
|
||||
target->AddLine(p + BPoint(0.0, 4.0), p + BPoint(4.0, 4.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p, p + BPoint(0.0, 3.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 0.0), p + BPoint(3.0, 0.0), M_DARK_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(4.0, 0.0), p + BPoint(4.0, 3.0), M_LIGHT_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(1.0, 1.0), p + BPoint(1.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(2.0, 1.0), p + BPoint(2.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->AddLine(p + BPoint(3.0, 1.0), p + BPoint(3.0, 3.0), M_MED_GRAY_COLOR);
|
||||
target->EndLineArray();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaJack::_updateAbbreviation()
|
||||
{
|
||||
D_METHOD(("MediaJack::_updateAbbreviation()\n"));
|
||||
|
||||
int32 offset;
|
||||
m_abbreviation = "";
|
||||
m_abbreviation += m_label[0];
|
||||
offset = m_label.FindFirst(" ") + 1;
|
||||
if ((offset > 1) && (offset < m_label.CountChars() - 1))
|
||||
m_abbreviation += m_label[offset];
|
||||
else
|
||||
m_abbreviation += m_label[1];
|
||||
offset = m_label.CountChars() - 1;
|
||||
m_abbreviation += m_label[offset];
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations (protected)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MediaJack::showContextMenu(
|
||||
BPoint point)
|
||||
{
|
||||
D_METHOD(("MediaJack::showContextMenu()\n"));
|
||||
|
||||
BPopUpMenu *menu = new BPopUpMenu("MediaJack PopUp", false, false, B_ITEMS_IN_COLUMN);
|
||||
menu->SetFont(be_plain_font);
|
||||
BMenuItem *item;
|
||||
|
||||
// add the "Get Info" item
|
||||
if (isInput())
|
||||
{
|
||||
media_input input;
|
||||
getInput(&input);
|
||||
BMessage *message = new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED);
|
||||
message->AddData("input", B_RAW_TYPE,
|
||||
reinterpret_cast<const void *>(&input), sizeof(input));
|
||||
menu->AddItem(item = new BMenuItem("Get Info", message));
|
||||
}
|
||||
else if (isOutput())
|
||||
{
|
||||
media_output output;
|
||||
getOutput(&output);
|
||||
BMessage *message = new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED);
|
||||
message->AddData("output", B_RAW_TYPE,
|
||||
reinterpret_cast<const void *>(&output), sizeof(output));
|
||||
menu->AddItem(item = new BMenuItem("Get Info", message));
|
||||
}
|
||||
|
||||
menu->SetTargetForItems(view());
|
||||
view()->ConvertToScreen(&point);
|
||||
point -= BPoint(1.0, 1.0);
|
||||
menu->Go(point, true, true, true);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** sorting methods (friend)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
int __CORTEX_NAMESPACE__ compareTypeAndID(
|
||||
const void *lValue,
|
||||
const void *rValue)
|
||||
{
|
||||
int retValue = 0;
|
||||
const MediaJack *lJack = *(reinterpret_cast<MediaJack * const*>(reinterpret_cast<void * const*>(lValue)));
|
||||
const MediaJack *rJack = *(reinterpret_cast<MediaJack * const*>(reinterpret_cast<void * const*>(rValue)));
|
||||
if (lJack && rJack)
|
||||
{
|
||||
if (lJack->m_jackType < lJack->m_jackType)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (lJack->m_jackType == lJack->m_jackType)
|
||||
{
|
||||
if (lJack->m_index < rJack->m_index)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (lJack->m_jackType > rJack->m_jackType)
|
||||
{
|
||||
retValue = 1;
|
||||
}
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
// END -- LiveNodeView.cpp --
|
190
src/apps/cortex/MediaRoutingView/MediaJack.h
Normal file
190
src/apps/cortex/MediaRoutingView/MediaJack.h
Normal file
@ -0,0 +1,190 @@
|
||||
// MediaJack.h
|
||||
// c.lenz 10oct99
|
||||
//
|
||||
// * PURPOSE
|
||||
// DiagramEndPoint derived class implementing the drawing
|
||||
// code and
|
||||
//
|
||||
// HISTORY
|
||||
//
|
||||
|
||||
#ifndef __MediaJack_H__
|
||||
#define __MediaJack_H__
|
||||
|
||||
#include "DiagramEndPoint.h"
|
||||
|
||||
// Media Kit
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaNode.h>
|
||||
// Support Kit
|
||||
#include <String.h>
|
||||
|
||||
class BBitmap;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class MediaJack : public DiagramEndPoint
|
||||
{
|
||||
|
||||
public: // *** jack types
|
||||
|
||||
enum jack_t
|
||||
{
|
||||
M_INPUT,
|
||||
M_OUTPUT
|
||||
};
|
||||
|
||||
public: // *** constants
|
||||
|
||||
// [e.moon 26oct99] moved definitions to MediaJack.cpp
|
||||
static float M_DEFAULT_WIDTH;
|
||||
static float M_DEFAULT_HEIGHT;
|
||||
static const float M_DEFAULT_GAP;
|
||||
static const int32 M_MAX_ABBR_LENGTH;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// Constructor for input jacks
|
||||
MediaJack(
|
||||
media_input input);
|
||||
|
||||
// Constructor for output jacks
|
||||
MediaJack(
|
||||
media_output output);
|
||||
|
||||
virtual ~MediaJack();
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
// returns the full name of the input/output
|
||||
BString name() const
|
||||
{ return m_label; }
|
||||
|
||||
// return true if this is an input jack
|
||||
bool isInput() const
|
||||
{ return (m_jackType == M_INPUT); }
|
||||
|
||||
// copies the media_input struct into input; returns
|
||||
// B_ERROR if this isn't an input jack
|
||||
status_t getInput(
|
||||
media_input *input) const;
|
||||
|
||||
// return true if this is an output jack
|
||||
bool isOutput() const
|
||||
{ return (m_jackType == M_OUTPUT); }
|
||||
|
||||
// copies the media_output struct into input; returns
|
||||
// B_ERROR if this isn't an input jack
|
||||
status_t getOutput(
|
||||
media_output *output) const;
|
||||
|
||||
public: // *** derived from DiagramEndPoint/Item
|
||||
|
||||
// is called by the parent DiagramBox after adding endpoint
|
||||
virtual void attachedToDiagram();
|
||||
|
||||
// is called by the parent DiagramBox just before the endpoint
|
||||
// will be removed
|
||||
virtual void detachedFromDiagram();
|
||||
|
||||
// the actual drawing code
|
||||
virtual void drawEndPoint();
|
||||
|
||||
// returns the coordinate at which a connected MediaWire is
|
||||
// supposed to start/end
|
||||
virtual BPoint connectionPoint() const;
|
||||
|
||||
// hook called by the base class; just verifies if the jack
|
||||
// type isn't equal, i.e. not connecting an input to an input
|
||||
virtual bool connectionRequested(
|
||||
DiagramEndPoint *which);
|
||||
|
||||
// displays the context menu for right-clicks
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks);
|
||||
|
||||
// changes the mouse cursor and prepares a tooltip
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit);
|
||||
|
||||
// changes the mouse cursor
|
||||
virtual void messageDragged(
|
||||
BPoint point,
|
||||
uint32 transit,
|
||||
const BMessage *message);
|
||||
|
||||
// updates the offscreen bitmap
|
||||
virtual void selected();
|
||||
|
||||
// updates the offscreen bitmap
|
||||
virtual void deselected();
|
||||
|
||||
// updates the offscreen bitmap
|
||||
virtual void connected();
|
||||
|
||||
// updates the offscreen bitmap
|
||||
virtual void disconnected();
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// updates the jacks bitmap
|
||||
void layoutChanged(
|
||||
int32 layout);
|
||||
|
||||
// special function to be called by the parent MediaNodePanel
|
||||
// for simple positioning; this method only needs to know the
|
||||
// vertical offset of the jack and the left/right frame coords
|
||||
// of the panel
|
||||
void setPosition(
|
||||
float verticalOffset,
|
||||
float leftBoundary,
|
||||
float rightBoundary,
|
||||
BRegion *updateRegion = 0);
|
||||
|
||||
protected: // *** operations
|
||||
|
||||
// display a popup-menu at given point
|
||||
void showContextMenu(
|
||||
BPoint point);
|
||||
|
||||
private: // *** internal methods
|
||||
|
||||
// update the offscreen bitmap
|
||||
void _updateBitmap();
|
||||
|
||||
// draw jack into the specified target view
|
||||
void _drawInto(
|
||||
BView *target,
|
||||
BRect frame,
|
||||
int32 layout);
|
||||
|
||||
// make/update an abbreviation for the jacks name
|
||||
void _updateAbbreviation();
|
||||
|
||||
public: // *** sorting methods
|
||||
|
||||
// used for sorting; will put input jacks before output jacks
|
||||
// and inside those sort by index
|
||||
friend int compareTypeAndID(
|
||||
const void *lValue,
|
||||
const void *rValue);
|
||||
|
||||
private: // *** data
|
||||
|
||||
int32 m_jackType;
|
||||
BBitmap *m_bitmap;
|
||||
int32 m_index;
|
||||
media_node m_node;
|
||||
media_source m_source;
|
||||
media_destination m_destination;
|
||||
media_format m_format;
|
||||
BString m_label;
|
||||
BString m_abbreviation;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __MediaJack_H__ */
|
1077
src/apps/cortex/MediaRoutingView/MediaNodePanel.cpp
Normal file
1077
src/apps/cortex/MediaRoutingView/MediaNodePanel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
172
src/apps/cortex/MediaRoutingView/MediaNodePanel.h
Normal file
172
src/apps/cortex/MediaRoutingView/MediaNodePanel.h
Normal file
@ -0,0 +1,172 @@
|
||||
// MediaNodePanel.h
|
||||
// c.lenz 9oct99
|
||||
//
|
||||
// HISTORY
|
||||
// c.lenz 9oct99 Begun
|
||||
|
||||
#ifndef __MediaNodePanel_H__
|
||||
#define __MediaNodePanel_H__
|
||||
|
||||
// DiagramView
|
||||
#include "DiagramBox.h"
|
||||
// MediaRoutingView
|
||||
#include "MediaJack.h"
|
||||
|
||||
// STL
|
||||
#include <vector>
|
||||
// Support Kit
|
||||
#include <String.h>
|
||||
|
||||
#include "IStateArchivable.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class MediaIcon;
|
||||
class NodeRef;
|
||||
|
||||
class MediaNodePanel : public DiagramBox,
|
||||
public BHandler,
|
||||
public IStateArchivable
|
||||
{
|
||||
typedef DiagramBox _inherited;
|
||||
|
||||
public: // *** constants
|
||||
|
||||
// [e.moon 26oct99] moved definitions to MediaNodePanel.cpp
|
||||
static float M_DEFAULT_WIDTH;
|
||||
static float M_DEFAULT_HEIGHT;
|
||||
static float M_LABEL_H_MARGIN;
|
||||
static float M_LABEL_V_MARGIN;
|
||||
static float M_BODY_H_MARGIN;
|
||||
static float M_BODY_V_MARGIN;
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
NodeRef* const ref;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
MediaNodePanel(
|
||||
BPoint position,
|
||||
NodeRef *nodeRef);
|
||||
|
||||
virtual ~MediaNodePanel();
|
||||
|
||||
public: // *** derived from DiagramItem
|
||||
|
||||
virtual void attachedToDiagram();
|
||||
|
||||
virtual void detachedFromDiagram();
|
||||
|
||||
virtual void drawBox();
|
||||
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks);
|
||||
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit);
|
||||
|
||||
virtual void messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message);
|
||||
|
||||
virtual void selected();
|
||||
|
||||
virtual void deselected();
|
||||
|
||||
public: // *** derived from BHandler
|
||||
|
||||
virtual void MessageReceived(
|
||||
BMessage *message);
|
||||
|
||||
public: // *** updating
|
||||
|
||||
// is called by the MediaRoutingView when the layout
|
||||
// (i.e. icon size, orientation, default sizes) have
|
||||
// changed
|
||||
void layoutChanged(
|
||||
int32 layout);
|
||||
|
||||
// query the NodeManager for all free inputs & outputs
|
||||
// and add a MediaJack instance for each; (connected
|
||||
// inputs are added when the connection is reported or
|
||||
// queried)
|
||||
void populateInit();
|
||||
|
||||
// completely update the list of free input/output jacks
|
||||
void updateIOJacks();
|
||||
|
||||
// arrange the MediaJacks in order of their IDs, resize
|
||||
// the panel if more space is needed
|
||||
void arrangeIOJacks();
|
||||
|
||||
// display popup-menu at the given point
|
||||
void showContextMenu(
|
||||
BPoint point);
|
||||
|
||||
public: // *** sorting methods
|
||||
|
||||
// used for sorting the panels by media_node_id
|
||||
friend int compareID(
|
||||
const void *lValue,
|
||||
const void *rValue);
|
||||
|
||||
public: // *** IStateArchivable
|
||||
|
||||
status_t importState(
|
||||
const BMessage* archive); //nyi
|
||||
|
||||
status_t exportState(
|
||||
BMessage* archive) const; //nyi
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// fetch node name (shortening as necessary to fit)
|
||||
// and update label placement
|
||||
void _prepareLabel();
|
||||
|
||||
// update the offscreen bitmap
|
||||
void _updateBitmap();
|
||||
|
||||
void _drawInto(
|
||||
BView *target,
|
||||
BRect targetRect,
|
||||
int32 layout);
|
||||
|
||||
void _updateIcon(
|
||||
int32 layout);
|
||||
|
||||
private: // *** data
|
||||
|
||||
// a pointer to the panel's offscreen bitmap
|
||||
BBitmap *m_bitmap;
|
||||
|
||||
BBitmap *m_icon;
|
||||
|
||||
BString m_label; // truncated
|
||||
|
||||
BString m_fullLabel; // not truncated
|
||||
|
||||
bool m_labelTruncated;
|
||||
|
||||
BPoint m_labelOffset;
|
||||
|
||||
BRect m_labelRect;
|
||||
|
||||
BRect m_bodyRect;
|
||||
|
||||
// cached position in the "other" layout
|
||||
BPoint m_alternatePosition;
|
||||
|
||||
bool m_mouseOverLabel;
|
||||
|
||||
// [e.moon 7dec99]
|
||||
static const BPoint s_invalidPosition;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __MediaNodePanel_H__ */
|
34
src/apps/cortex/MediaRoutingView/MediaRoutingDefs.h
Normal file
34
src/apps/cortex/MediaRoutingView/MediaRoutingDefs.h
Normal file
@ -0,0 +1,34 @@
|
||||
// MediaRoutingDefs.h
|
||||
// c.lenz 9oct99
|
||||
//
|
||||
// Constants used in MediaRoutingView and friends:
|
||||
// - Colors
|
||||
// - Cursors
|
||||
//
|
||||
// HISTORY
|
||||
// 6oct99 c.lenz Begun
|
||||
//
|
||||
|
||||
#ifndef __MediaRoutingDefs_H__
|
||||
#define __MediaRoutingDefs_H__
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** cursors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const unsigned char M_CABLE_CURSOR [] = {
|
||||
// PREAMBLE
|
||||
16, 1, 0, 0,
|
||||
// BITMAP
|
||||
224, 0, 144, 0, 168, 0, 68, 0, 34, 0, 17, 128, 11, 64, 7, 160,
|
||||
7, 208, 3, 232, 1, 244, 0, 250, 0, 125, 0, 63, 0, 30, 0, 12,
|
||||
// MASK
|
||||
224, 0, 240, 0, 248, 0, 124, 0, 62, 0, 31, 128, 15, 192, 7, 224,
|
||||
7, 240, 3, 248, 1, 252, 0, 254, 0, 127, 0, 63, 0, 30, 0, 12
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __MediaRoutingDefs_H__ */
|
1869
src/apps/cortex/MediaRoutingView/MediaRoutingView.cpp
Normal file
1869
src/apps/cortex/MediaRoutingView/MediaRoutingView.cpp
Normal file
File diff suppressed because it is too large
Load Diff
363
src/apps/cortex/MediaRoutingView/MediaRoutingView.h
Normal file
363
src/apps/cortex/MediaRoutingView/MediaRoutingView.h
Normal file
@ -0,0 +1,363 @@
|
||||
// MediaRoutingView.h
|
||||
// c.lenz 9oct99
|
||||
//
|
||||
// PURPOSE
|
||||
// Provide a simple interface for the BeOS/Genki media system.
|
||||
// Displays all the currently running ('live') media nodes,
|
||||
// and represents the connections between them visually.
|
||||
//
|
||||
// NOTES
|
||||
//
|
||||
// *** 9oct99: replaced grid-based version
|
||||
//
|
||||
// HISTORY
|
||||
// e.moon 6may99: first stab
|
||||
// c.lenz 6oct99: starting change to DiagramView impl
|
||||
|
||||
#ifndef __MediaRoutingView__H__
|
||||
#define __MediaRoutingView__H__
|
||||
|
||||
// DiagramView
|
||||
#include "DiagramView.h"
|
||||
|
||||
// Media Kit
|
||||
#include "MediaDefs.h"
|
||||
|
||||
#include <Entry.h>
|
||||
#include <List.h>
|
||||
#include <Message.h>
|
||||
|
||||
#include "IStateArchivable.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
// MediaRoutingView
|
||||
class MediaNodePanel;
|
||||
class MediaWire;
|
||||
// NodeManager
|
||||
class RouteAppNodeManager;
|
||||
class NodeGroup;
|
||||
class NodeRef;
|
||||
class Connection;
|
||||
// RouteApp
|
||||
class NodeSetIOContext;
|
||||
|
||||
class MediaRoutingView :
|
||||
public DiagramView,
|
||||
public IStateArchivable {
|
||||
typedef DiagramView _inherited;
|
||||
|
||||
public: // *** constants
|
||||
|
||||
// [e.moon 26oct99] moved definitions to MediaRoutingView.cpp
|
||||
static float M_CLEANUP_H_GAP;
|
||||
static float M_CLEANUP_V_GAP;
|
||||
static float M_CLEANUP_H_MARGIN;
|
||||
static float M_CLEANUP_V_MARGIN;
|
||||
|
||||
// [e.moon 7dec99] enum now a named type
|
||||
enum layout_t
|
||||
{
|
||||
M_ICON_VIEW = 1,
|
||||
M_MINI_ICON_VIEW
|
||||
};
|
||||
|
||||
public: // messages
|
||||
|
||||
enum message_t {
|
||||
// INBOUND
|
||||
// "layout" int32: M_ICON_VIEW / M_MINI_ICON_VIEW
|
||||
M_LAYOUT_CHANGED,
|
||||
|
||||
// INBOUND
|
||||
M_CLEANUP_REQUESTED,
|
||||
|
||||
// INBOUND
|
||||
// release/delete node
|
||||
// "nodeID" [u]int32: node to release
|
||||
M_RELEASE_NODE,
|
||||
|
||||
// INBOUND
|
||||
M_SELECT_ALL,
|
||||
|
||||
// INBOUND
|
||||
M_DELETE_SELECTION,
|
||||
|
||||
// OUTBOUND
|
||||
// describes a selected node (sent to owning window)
|
||||
// "nodeID" int32
|
||||
M_NODE_SELECTED,
|
||||
|
||||
// OUTBOUND
|
||||
// describes a selected group (sent to owning window)
|
||||
// "groupID" int32
|
||||
M_GROUP_SELECTED,
|
||||
|
||||
// INBOUND
|
||||
// requests that the currently selected node/group be broadcast
|
||||
// back to the owning window
|
||||
M_BROADCAST_SELECTION,
|
||||
|
||||
// INBOUND
|
||||
// request to change the selected nodes cycling mode (on/off)
|
||||
// "cycle" bool
|
||||
M_NODE_CHANGE_CYCLING,
|
||||
|
||||
// INBOUND
|
||||
// request to change the selected nodes run mode
|
||||
// "run_mode" int32
|
||||
M_NODE_CHANGE_RUN_MODE,
|
||||
|
||||
// INBOUND
|
||||
// request to start/stop the selected node(s) as a time source
|
||||
// instantly
|
||||
// [e.moon 5dec99]
|
||||
M_NODE_START_TIME_SOURCE,
|
||||
M_NODE_STOP_TIME_SOURCE,
|
||||
|
||||
// INBOUND
|
||||
// call BControllable::StartControlPanel for the node specified
|
||||
// in the field "nodeID" (int32)
|
||||
// [c.lenz 24dec99]
|
||||
M_NODE_START_CONTROL_PANEL,
|
||||
|
||||
// INBOUND
|
||||
// set the given group's GROUP_LOCKED flag
|
||||
// [em 1feb00]
|
||||
// "groupID" int32
|
||||
// "locked" bool
|
||||
M_GROUP_SET_LOCKED,
|
||||
|
||||
// INBOUND
|
||||
// open ParameterWindow for selected nodes
|
||||
// [c.lenz 17feb2000]
|
||||
M_NODE_TWEAK_PARAMETERS,
|
||||
|
||||
// INBOUND
|
||||
// sent to the RouteWindow for displaying error
|
||||
// messages in the status bar if available
|
||||
// "text" string
|
||||
// "error" bool (optional)
|
||||
M_SHOW_ERROR_MESSAGE
|
||||
};
|
||||
|
||||
public: // *** members
|
||||
|
||||
RouteAppNodeManager* const manager;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
MediaRoutingView(
|
||||
RouteAppNodeManager *nodeManager,
|
||||
BRect frame,
|
||||
const char *name,
|
||||
uint32 resizeMode = B_FOLLOW_ALL_SIDES);
|
||||
|
||||
virtual ~MediaRoutingView();
|
||||
|
||||
public: // *** DiagramView impl
|
||||
|
||||
virtual void connectionAborted(
|
||||
DiagramEndPoint *fromWhich);
|
||||
|
||||
virtual void connectionEstablished(
|
||||
DiagramEndPoint *fromWhich,
|
||||
DiagramEndPoint *toWhich);
|
||||
|
||||
DiagramWire *createWire(
|
||||
DiagramEndPoint *fromWhich,
|
||||
DiagramEndPoint *woWhich);
|
||||
|
||||
DiagramWire *createWire(
|
||||
DiagramEndPoint *fromWhich);
|
||||
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks);
|
||||
|
||||
virtual void messageDropped(
|
||||
BPoint point,
|
||||
BMessage *message);
|
||||
|
||||
virtual void selectionChanged();
|
||||
|
||||
public: // *** BView impl
|
||||
|
||||
virtual void AttachedToWindow();
|
||||
|
||||
virtual void AllAttached();
|
||||
|
||||
virtual void DetachedFromWindow();
|
||||
|
||||
virtual void KeyDown(
|
||||
const char *bytes,
|
||||
int32 count);
|
||||
|
||||
virtual void Pulse();
|
||||
|
||||
public: // *** BHandler impl
|
||||
|
||||
virtual void MessageReceived(
|
||||
BMessage *message);
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
layout_t getLayout() const
|
||||
{ return m_layout; }
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// returns coordinates for a free area where the panel
|
||||
// could be positioned; uses the M_CLEANUP_* settings
|
||||
// and positions producers at the left, consumer at the
|
||||
// right and filters in the middle
|
||||
BPoint findFreePositionFor(
|
||||
const MediaNodePanel *panel) const;
|
||||
|
||||
public: // *** IStateArchivable
|
||||
|
||||
status_t importState(
|
||||
const BMessage* archive);
|
||||
|
||||
status_t exportState(
|
||||
BMessage* archive) const;
|
||||
|
||||
// [e.moon 8dec99] subset support
|
||||
status_t importStateFor(
|
||||
const NodeSetIOContext* context,
|
||||
const BMessage* archive);
|
||||
|
||||
status_t exportStateFor(
|
||||
const NodeSetIOContext* context,
|
||||
BMessage* archive) const;
|
||||
|
||||
protected: // *** operations
|
||||
|
||||
// adjust the default object sizes to the current
|
||||
// layout and font size and rearrange if necessary
|
||||
void layoutChanged(
|
||||
layout_t layout);
|
||||
|
||||
// aligns the panels on a grid according to their node_kind
|
||||
void cleanUp();
|
||||
|
||||
// displays a context menu at a given position
|
||||
void showContextMenu(
|
||||
BPoint point);
|
||||
|
||||
// will currently display a BAlert; this should change sometime
|
||||
// in the future!
|
||||
void showErrorMessage(
|
||||
BString message,
|
||||
status_t error);
|
||||
|
||||
private: // *** children management
|
||||
|
||||
// adds a panel representation of a live media node to the view
|
||||
status_t _addPanelFor(
|
||||
media_node_id id,
|
||||
BPoint point);
|
||||
|
||||
// tries to find the panel to a given media node
|
||||
status_t _findPanelFor(
|
||||
media_node_id id,
|
||||
MediaNodePanel **outPanel) const;
|
||||
|
||||
// removes the panel of a given media node
|
||||
status_t _removePanelFor(
|
||||
media_node_id);
|
||||
|
||||
// adds a wire represenation of a media kit connection
|
||||
status_t _addWireFor(
|
||||
Connection &connection);
|
||||
|
||||
// finds the ui rep of a given Connection object
|
||||
status_t _findWireFor(
|
||||
uint32 connectionID,
|
||||
MediaWire **wire) const;
|
||||
|
||||
// removes the wire
|
||||
status_t _removeWireFor(
|
||||
uint32 connectionID);
|
||||
|
||||
private: // *** internal methods
|
||||
|
||||
// iterates through all selected MediaNodePanels and sets the
|
||||
// 'cycling' state for each to cycle
|
||||
void _changeCyclingForSelection(
|
||||
bool cycle);
|
||||
|
||||
// iterates through all selected MediaNodePanels and sets the
|
||||
// RunMode for each to mode; 0 is interpreted as '(same as group)'
|
||||
void _changeRunModeForSelection(
|
||||
uint32 mode);
|
||||
|
||||
void _openInfoWindowsForSelection();
|
||||
|
||||
void _openParameterWindowsForSelection();
|
||||
|
||||
void _startControlPanelsForSelection();
|
||||
|
||||
// tries to release every node in the current selection, or to
|
||||
// disconnect wires if those were selected
|
||||
void _deleteSelection();
|
||||
|
||||
void _addShortcuts();
|
||||
|
||||
void _initLayout();
|
||||
|
||||
// populates the view with all nodes currently in the NodeManager
|
||||
void _initContent();
|
||||
|
||||
void _checkDroppedFile(
|
||||
entry_ref *ref,
|
||||
BPoint dropPoint);
|
||||
|
||||
void _changeBackground(
|
||||
entry_ref *ref);
|
||||
|
||||
void _changeBackground(
|
||||
rgb_color color);
|
||||
|
||||
// adjust scroll bar ranges
|
||||
void _adjustScrollBars();
|
||||
|
||||
void _broadcastSelection() const;
|
||||
|
||||
// find & remove an entry in m_inactiveNodeState
|
||||
status_t _fetchInactiveNodeState(
|
||||
MediaNodePanel* forPanel,
|
||||
BMessage* outMessage);
|
||||
|
||||
void _emptyInactiveNodeState();
|
||||
|
||||
private:
|
||||
|
||||
// the current layout
|
||||
layout_t m_layout;
|
||||
|
||||
// current new-group-name index
|
||||
uint32 m_nextGroupNumber;
|
||||
|
||||
// holds the id of the node instantiated last thru d&d
|
||||
media_node_id m_lastDroppedNode;
|
||||
|
||||
// the point at which above node was dropped
|
||||
BPoint m_lastDropPoint;
|
||||
|
||||
// holds a pointer to the currently dragged wire (if any)
|
||||
MediaWire *m_draggedWire;
|
||||
|
||||
// stores location of the background bitmap (invalid if no
|
||||
// background bitmap has been set.)
|
||||
// [e.moon 1dec99]
|
||||
BEntry m_backgroundBitmapEntry;
|
||||
|
||||
// state info for currently inactive nodes (cached from importState())
|
||||
BList m_inactiveNodeState;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __MediaRoutingView_H__ */
|
309
src/apps/cortex/MediaRoutingView/MediaWire.cpp
Normal file
309
src/apps/cortex/MediaRoutingView/MediaWire.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
// MediaWire.cpp
|
||||
|
||||
#include "MediaWire.h"
|
||||
// InfoWindow
|
||||
#include "InfoWindowManager.h"
|
||||
// MediaRoutingView
|
||||
#include "MediaJack.h"
|
||||
#include "MediaRoutingDefs.h"
|
||||
#include "MediaRoutingView.h"
|
||||
// Support
|
||||
#include "cortex_ui.h"
|
||||
#include "MediaString.h"
|
||||
// TipManager
|
||||
#include "TipManager.h"
|
||||
|
||||
// Application Kit
|
||||
#include <Application.h>
|
||||
// Interface Kit
|
||||
#include <MenuItem.h>
|
||||
#include <PopUpMenu.h>
|
||||
// Media Kit
|
||||
#include <MediaDefs.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_METHOD(x) //PRINT (x)
|
||||
#define D_DRAW(x) //PRINT (x)
|
||||
#define D_MOUSE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// constants
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const float MediaWire::M_WIRE_OFFSET = 4.0;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
MediaWire::MediaWire(
|
||||
Connection connection,
|
||||
MediaJack *outputJack,
|
||||
MediaJack *inputJack)
|
||||
: DiagramWire(outputJack, inputJack),
|
||||
connection(connection)
|
||||
{
|
||||
D_METHOD(("MediaWire::MediaWire()\n"));
|
||||
}
|
||||
|
||||
MediaWire::MediaWire(
|
||||
MediaJack *jack,
|
||||
bool isStartPoint)
|
||||
: DiagramWire(jack, isStartPoint)
|
||||
{
|
||||
D_METHOD(("MediaWire::MediaWire(temp)\n"));
|
||||
}
|
||||
|
||||
MediaWire::~MediaWire()
|
||||
{
|
||||
D_METHOD(("MediaWire::~MediaWire()\n"));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** derived from DiagramWire (public)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MediaWire::attachedToDiagram()
|
||||
{
|
||||
D_METHOD(("MediaWire::detachedFromDiagram()\n"));
|
||||
|
||||
endPointMoved(startPoint());
|
||||
endPointMoved(endPoint());
|
||||
}
|
||||
|
||||
void MediaWire::detachedFromDiagram()
|
||||
{
|
||||
D_METHOD(("MediaWire::detachedFromDiagram()\n"));
|
||||
|
||||
// make sure we're no longer displaying a tooltip
|
||||
TipManager *tips = TipManager::Instance();
|
||||
tips->hideTip(view()->ConvertToScreen(frame()));
|
||||
}
|
||||
|
||||
BRect MediaWire::frame() const
|
||||
{
|
||||
D_DRAW(("MediaWire::frame()\n"));
|
||||
return m_frame;
|
||||
}
|
||||
|
||||
float MediaWire::howCloseTo(
|
||||
BPoint point) const
|
||||
{
|
||||
D_MOUSE(("MediaWire::howCloseTo()\n"));
|
||||
if (frame().Contains(point))
|
||||
{
|
||||
BPoint sp = m_startPoint;
|
||||
BPoint ep = m_endPoint;
|
||||
BPoint so = m_startOffset;
|
||||
BPoint eo = m_endOffset;
|
||||
BRect wireFrame, startFrame, endFrame;
|
||||
wireFrame.left = so.x < eo.x ? so.x : eo.x;
|
||||
wireFrame.top = so.y < eo.y ? so.y : eo.y;
|
||||
wireFrame.right = so.x > eo.x ? so.x : eo.x;
|
||||
wireFrame.bottom = so.y > eo.y ? so.y : eo.y;
|
||||
startFrame.Set(sp.x, sp.y, so.x, so.y);
|
||||
endFrame.Set(ep.x, ep.y, eo.x, eo.y);
|
||||
wireFrame.InsetBy(-1.0, -1.0);
|
||||
startFrame.InsetBy(-1.0, -1.0);
|
||||
endFrame.InsetBy(-1.0, -1.0);
|
||||
if ((wireFrame.Width() <= 5.0) || (wireFrame.Height() <= 5.0) || startFrame.Contains(point) || endFrame.Contains(point))
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float length, result;
|
||||
length = sqrt(pow(eo.x - so.x, 2) + pow(eo.y - so.y, 2));
|
||||
result = ((so.y - point.y) * (eo.x - so.x)) - ((so.x - point.x) * (eo.y - so.y));
|
||||
result = 3.0 - fabs(result / length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void MediaWire::drawWire()
|
||||
{
|
||||
D_DRAW(("MediaWire::drawWire()\n"));
|
||||
|
||||
rgb_color border = isSelected() ? M_BLUE_COLOR : M_DARK_GRAY_COLOR;
|
||||
rgb_color fill = isSelected() ? M_LIGHT_BLUE_COLOR : M_LIGHT_GRAY_COLOR;
|
||||
view()->SetPenSize(3.0);
|
||||
view()->BeginLineArray(3);
|
||||
view()->AddLine(m_startPoint, m_startOffset, border);
|
||||
view()->AddLine(m_startOffset, m_endOffset, border);
|
||||
view()->AddLine(m_endOffset, m_endPoint, border);
|
||||
view()->EndLineArray();
|
||||
view()->SetPenSize(1.0);
|
||||
view()->BeginLineArray(3);
|
||||
view()->AddLine(m_startPoint, m_startOffset, fill);
|
||||
view()->AddLine(m_startOffset, m_endOffset, fill);
|
||||
view()->AddLine(m_endOffset, m_endPoint, fill);
|
||||
view()->EndLineArray();
|
||||
}
|
||||
|
||||
void MediaWire::mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks)
|
||||
{
|
||||
D_MOUSE(("MediaWire::mouseDown()\n"));
|
||||
_inherited::mouseDown(point, buttons, clicks);
|
||||
|
||||
switch (buttons)
|
||||
{
|
||||
case B_SECONDARY_MOUSE_BUTTON:
|
||||
{
|
||||
if (clicks == 1)
|
||||
{
|
||||
showContextMenu(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaWire::mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit)
|
||||
{
|
||||
D_MOUSE(("MediaWire::mouseOver()\n"));
|
||||
|
||||
if (isDragging())
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (transit)
|
||||
{
|
||||
case B_ENTERED_VIEW:
|
||||
{
|
||||
TipManager *tips = TipManager::Instance();
|
||||
BString tipText = MediaString::getStringFor(connection.format(), false);
|
||||
tips->showTip(tipText.String(), view()->ConvertToScreen(frame()),
|
||||
TipManager::LEFT_OFFSET_FROM_POINTER, BPoint(12.0, 8.0));
|
||||
be_app->SetCursor(M_CABLE_CURSOR);
|
||||
break;
|
||||
}
|
||||
case B_EXITED_VIEW:
|
||||
{
|
||||
be_app->SetCursor(B_HAND_CURSOR);
|
||||
TipManager *tips = TipManager::Instance();
|
||||
tips->hideTip(view()->ConvertToScreen(frame()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaWire::selected()
|
||||
{
|
||||
D_METHOD(("MediaWire::selected()\n"));
|
||||
if (startPoint())
|
||||
{
|
||||
MediaJack *outputJack = dynamic_cast<MediaJack *>(startPoint());
|
||||
outputJack->select();
|
||||
}
|
||||
if (endPoint())
|
||||
{
|
||||
MediaJack *inputJack = dynamic_cast<MediaJack *>(endPoint());
|
||||
inputJack->select();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaWire::deselected()
|
||||
{
|
||||
D_METHOD(("MediaWire::deselected()\n"));
|
||||
if (startPoint())
|
||||
{
|
||||
MediaJack *outputJack = dynamic_cast<MediaJack *>(startPoint());
|
||||
outputJack->deselect();
|
||||
}
|
||||
if (endPoint())
|
||||
{
|
||||
MediaJack *inputJack = dynamic_cast<MediaJack *>(endPoint());
|
||||
inputJack->deselect();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaWire::endPointMoved(
|
||||
DiagramEndPoint *which)
|
||||
{
|
||||
if (which == startPoint())
|
||||
{
|
||||
m_startPoint = startConnectionPoint();
|
||||
switch (dynamic_cast<MediaRoutingView *>(view())->getLayout())
|
||||
{
|
||||
case MediaRoutingView::M_ICON_VIEW:
|
||||
{
|
||||
m_startOffset = m_startPoint + BPoint(M_WIRE_OFFSET, 0.0);
|
||||
break;
|
||||
}
|
||||
case MediaRoutingView::M_MINI_ICON_VIEW:
|
||||
{
|
||||
m_startOffset = m_startPoint + BPoint(0.0, M_WIRE_OFFSET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_frame.left = m_startPoint.x < m_endOffset.x ? m_startPoint.x - 2.0: m_endOffset.x - 2.0;
|
||||
m_frame.top = m_startPoint.y < m_endOffset.y ? m_startPoint.y - 2.0 : m_endOffset.y - 2.0;
|
||||
m_frame.right = m_startOffset.x > m_endPoint.x ? m_startOffset.x + 2.0 : m_endPoint.x + 2.0;
|
||||
m_frame.bottom = m_startOffset.y > m_endPoint.y ? m_startOffset.y + 2.0 : m_endPoint.y + 2.0;
|
||||
}
|
||||
else if (which == endPoint())
|
||||
{
|
||||
m_endPoint = endConnectionPoint();
|
||||
switch (dynamic_cast<MediaRoutingView *>(view())->getLayout())
|
||||
{
|
||||
case MediaRoutingView::M_ICON_VIEW:
|
||||
{
|
||||
m_endOffset = m_endPoint - BPoint(M_WIRE_OFFSET, 0.0);
|
||||
break;
|
||||
}
|
||||
case MediaRoutingView::M_MINI_ICON_VIEW:
|
||||
{
|
||||
m_endOffset = m_endPoint - BPoint(0.0, M_WIRE_OFFSET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_frame.left = m_startPoint.x < m_endOffset.x ? m_startPoint.x - 2.0: m_endOffset.x - 2.0;
|
||||
m_frame.top = m_startPoint.y < m_endOffset.y ? m_startPoint.y - 2.0 : m_endOffset.y - 2.0;
|
||||
m_frame.right = m_startOffset.x > m_endPoint.x ? m_startOffset.x + 2.0 : m_endPoint.x + 2.0;
|
||||
m_frame.bottom = m_startOffset.y > m_endPoint.y ? m_startOffset.y + 2.0 : m_endPoint.y + 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations (protected)
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MediaWire::showContextMenu(
|
||||
BPoint point)
|
||||
{
|
||||
D_METHOD(("MediaWire::showContextMenu()\n"));
|
||||
|
||||
BPopUpMenu *menu = new BPopUpMenu("MediaWire PopUp", false, false, B_ITEMS_IN_COLUMN);
|
||||
menu->SetFont(be_plain_font);
|
||||
BMenuItem *item;
|
||||
|
||||
// add the "Get Info" item
|
||||
media_output output;
|
||||
connection.getOutput(&output);
|
||||
BMessage *message = new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED);
|
||||
message->AddData("connection", B_RAW_TYPE,
|
||||
reinterpret_cast<const void *>(&output), sizeof(output));
|
||||
menu->AddItem(item = new BMenuItem("Get Info", message, 'I'));
|
||||
|
||||
// add the "Disconnect" item
|
||||
menu->AddItem(item = new BMenuItem("Disconnect", new BMessage(MediaRoutingView::M_DELETE_SELECTION), 'T'));
|
||||
if (connection.flags() & Connection::LOCKED)
|
||||
{
|
||||
item->SetEnabled(false);
|
||||
}
|
||||
|
||||
menu->SetTargetForItems(view());
|
||||
view()->ConvertToScreen(&point);
|
||||
point -= BPoint(1.0, 1.0);
|
||||
menu->Go(point, true, true, true);
|
||||
}
|
||||
|
||||
// END -- MediaWire.cpp --
|
115
src/apps/cortex/MediaRoutingView/MediaWire.h
Normal file
115
src/apps/cortex/MediaRoutingView/MediaWire.h
Normal file
@ -0,0 +1,115 @@
|
||||
// MediaWire.h
|
||||
// c.lenz 10oct99
|
||||
//
|
||||
// HISTORY
|
||||
//
|
||||
|
||||
#ifndef __MediaWire_H__
|
||||
#define __MediaWire_H__
|
||||
|
||||
// DiagramView
|
||||
#include "DiagramWire.h"
|
||||
// NodeManager
|
||||
#include "Connection.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class MediaJack;
|
||||
|
||||
class MediaWire :
|
||||
public DiagramWire {
|
||||
typedef DiagramWire _inherited;
|
||||
|
||||
public: // *** constans
|
||||
|
||||
// [e.moon 26oct99] moved definition to MediaWire.cpp
|
||||
static const float M_WIRE_OFFSET;
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// input and output jack are set connected by this constructor
|
||||
// so be careful to only pass in valid pointers
|
||||
MediaWire(
|
||||
Connection connection,
|
||||
MediaJack *outputJack,
|
||||
MediaJack *inputJack);
|
||||
|
||||
// special constructor used only in drag&drop sessions for
|
||||
// temporary visual indication of the connection process
|
||||
// the isStartPoint specifies if the given EndPoint is
|
||||
// supposed to be treated as start or end point
|
||||
MediaWire(
|
||||
MediaJack *jack,
|
||||
bool isStartPoint);
|
||||
|
||||
virtual ~MediaWire();
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
Connection connection;
|
||||
|
||||
public: // *** derived from DiagramWire/Item
|
||||
|
||||
// init the cached points and the frame rect
|
||||
virtual void attachedToDiagram();
|
||||
|
||||
// make sure no tooltip is being displayed for this wire
|
||||
// +++ DOES NOT WORK (yet)
|
||||
virtual void detachedFromDiagram();
|
||||
|
||||
// calculates and returns the frame rectangle of the wire
|
||||
virtual BRect frame() const;
|
||||
|
||||
// returns a value > 0.5 for points pretty much close to the
|
||||
// wire
|
||||
virtual float howCloseTo(
|
||||
BPoint point) const;
|
||||
|
||||
// does the actual drawing
|
||||
virtual void drawWire();
|
||||
|
||||
// displays the context-menu for right-clicks
|
||||
virtual void mouseDown(
|
||||
BPoint point,
|
||||
uint32 buttons,
|
||||
uint32 clicks);
|
||||
|
||||
// changes the mouse cursor and starts a tooltip
|
||||
virtual void mouseOver(
|
||||
BPoint point,
|
||||
uint32 transit);
|
||||
|
||||
// also selects and invalidates the jacks connected by
|
||||
// this wire
|
||||
virtual void selected();
|
||||
|
||||
// also deselectes and invalidates the jacks connected
|
||||
// by this wire
|
||||
virtual void deselected();
|
||||
|
||||
// updates the cached start & end points and the frame rect
|
||||
virtual void endPointMoved(
|
||||
DiagramEndPoint *which = 0);
|
||||
|
||||
protected: // *** operations
|
||||
|
||||
// display a popup-menu at given point
|
||||
void showContextMenu(
|
||||
BPoint point);
|
||||
|
||||
private: // *** data members
|
||||
|
||||
BPoint m_startPoint;
|
||||
|
||||
BPoint m_endPoint;
|
||||
|
||||
BPoint m_startOffset;
|
||||
|
||||
BPoint m_endOffset;
|
||||
|
||||
BRect m_frame;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __MediaWire_H__ */
|
243
src/apps/cortex/NodeManager/AddOnHost.cpp
Normal file
243
src/apps/cortex/NodeManager/AddOnHost.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
// AddOnHost.cpp
|
||||
|
||||
#include "AddOnHost.h"
|
||||
#include "AddOnHostProtocol.h"
|
||||
|
||||
#include <Application.h>
|
||||
#include <Debug.h>
|
||||
#include <Entry.h>
|
||||
#include <MediaNode.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <Messenger.h>
|
||||
#include <Path.h>
|
||||
#include <Roster.h>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// constants
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
BMessenger AddOnHost::s_messenger;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** static interface
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/*static*/
|
||||
status_t AddOnHost::FindInstance(
|
||||
BMessenger* outMessenger) {
|
||||
|
||||
status_t err;
|
||||
|
||||
// no current app? launch one
|
||||
if(!s_messenger.IsValid()) {
|
||||
s_messenger = BMessenger(
|
||||
addon_host::g_appSignature,
|
||||
-1,
|
||||
&err);
|
||||
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
if(!s_messenger.IsValid())
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
*outMessenger = s_messenger;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
status_t AddOnHost::Kill(
|
||||
bigtime_t timeout) {
|
||||
|
||||
if(!s_messenger.IsValid())
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
status_t err = kill_team(s_messenger.Team());
|
||||
return err;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
status_t AddOnHost::Launch(
|
||||
BMessenger* outMessenger) {
|
||||
|
||||
if(s_messenger.IsValid())
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
status_t err;
|
||||
|
||||
// find it
|
||||
entry_ref appRef;
|
||||
err = be_roster->FindApp(addon_host::g_appSignature, &appRef);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
// start it
|
||||
team_id team;
|
||||
const char* arg = "--addon-host";
|
||||
err = be_roster->Launch(
|
||||
&appRef,
|
||||
1,
|
||||
&arg,
|
||||
&team);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
// fetch messenger to the new app and return it
|
||||
s_messenger = BMessenger(
|
||||
addon_host::g_appSignature,
|
||||
team,
|
||||
&err);
|
||||
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
if(!s_messenger.IsValid())
|
||||
return B_ERROR;
|
||||
|
||||
if(outMessenger)
|
||||
*outMessenger = s_messenger;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
status_t AddOnHost::InstantiateDormantNode(
|
||||
const dormant_node_info& info,
|
||||
media_node* outNode,
|
||||
bigtime_t timeout) {
|
||||
|
||||
status_t err;
|
||||
|
||||
if(!s_messenger.IsValid()) {
|
||||
err = Launch(0);
|
||||
|
||||
if(err < B_OK) {
|
||||
// give up
|
||||
PRINT((
|
||||
"!!! AddOnHost::InstantiateDormantNode(): Launch() failed:\n"
|
||||
" %s\n",
|
||||
strerror(err)));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// do it
|
||||
ASSERT(s_messenger.IsValid());
|
||||
BMessage request(addon_host::M_INSTANTIATE);
|
||||
request.AddData("info", B_RAW_TYPE, &info, sizeof(dormant_node_info));
|
||||
|
||||
BMessage reply(B_NO_REPLY);
|
||||
err = s_messenger.SendMessage(
|
||||
&request,
|
||||
&reply,
|
||||
timeout,
|
||||
timeout);
|
||||
|
||||
// PRINT((
|
||||
// "### SendMessage() returned '%s'\n", strerror(err)));
|
||||
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! AddOnHost::InstantiateDormantNode(): SendMessage() failed:\n"
|
||||
" %s\n",
|
||||
strerror(err)));
|
||||
return err;
|
||||
}
|
||||
|
||||
if(reply.what == B_NO_REPLY) {
|
||||
PRINT((
|
||||
"!!! AddOnHost::InstantiateDormantNode(): no reply.\n"));
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if(reply.what == addon_host::M_INSTANTIATE_COMPLETE) {
|
||||
media_node_id nodeID;
|
||||
|
||||
// fetch node ID
|
||||
err = reply.FindInt32("node_id", &nodeID);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! AddOnHost::InstantiateDormantNode(): 'node_id' missing from reply.\n"));
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// fetch node
|
||||
err = BMediaRoster::Roster()->GetNodeFor(nodeID, outNode);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! AddOnHost::InstantiateDormantNode(): node missing!\n"));
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// // now solely owned by the add-on host team
|
||||
// BMediaRoster::Roster()->ReleaseNode(*outNode);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// failed:
|
||||
return (reply.FindInt32("error", &err) == B_OK) ? err : B_ERROR;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
status_t AddOnHost::ReleaseInternalNode(
|
||||
const live_node_info& info,
|
||||
bigtime_t timeout) {
|
||||
|
||||
status_t err;
|
||||
|
||||
if(!s_messenger.IsValid()) {
|
||||
err = Launch(0);
|
||||
|
||||
if(err < B_OK) {
|
||||
// give up
|
||||
PRINT((
|
||||
"!!! AddOnHost::ReleaseInternalNode(): Launch() failed:\n"
|
||||
" %s\n",
|
||||
strerror(err)));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// do it
|
||||
ASSERT(s_messenger.IsValid());
|
||||
BMessage request(addon_host::M_RELEASE);
|
||||
request.AddData("info", B_RAW_TYPE, &info, sizeof(live_node_info));
|
||||
|
||||
BMessage reply(B_NO_REPLY);
|
||||
err = s_messenger.SendMessage(
|
||||
&request,
|
||||
&reply,
|
||||
timeout,
|
||||
timeout);
|
||||
|
||||
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! AddOnHost::ReleaseInternalNode(): SendMessage() failed:\n"
|
||||
" %s\n",
|
||||
strerror(err)));
|
||||
return err;
|
||||
}
|
||||
|
||||
if(reply.what == B_NO_REPLY) {
|
||||
PRINT((
|
||||
"!!! AddOnHost::InstantiateDormantNode(): no reply.\n"));
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if(reply.what == addon_host::M_RELEASE_COMPLETE) {
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// failed:
|
||||
return (reply.FindInt32("error", &err) == B_OK) ? err : B_ERROR;
|
||||
}
|
||||
|
||||
// END -- AddOnHost.cpp --
|
52
src/apps/cortex/NodeManager/AddOnHost.h
Normal file
52
src/apps/cortex/NodeManager/AddOnHost.h
Normal file
@ -0,0 +1,52 @@
|
||||
// cortex::NodeManager::AddOnHost.h
|
||||
// * PURPOSE
|
||||
// Provides an interface to a separate BApplication whose
|
||||
// single responsibility is to launch nodes.
|
||||
// NodeManager-launched nodes now run in a separate team,
|
||||
// helping to lower the likelihood of a socially maladjusted
|
||||
// young node taking you out.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 6nov99
|
||||
|
||||
#ifndef __NodeManager_AddOnHost_H__
|
||||
#define __NodeManager_AddOnHost_H__
|
||||
|
||||
#include <Application.h>
|
||||
#include <MediaAddOn.h>
|
||||
#include <MediaDefs.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class AddOnHost {
|
||||
|
||||
public: // *** static interface
|
||||
|
||||
static status_t FindInstance(
|
||||
BMessenger* outMessenger);
|
||||
|
||||
static status_t Kill(
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT);
|
||||
|
||||
// returns B_NOT_ALLOWED if an instance has already been launched
|
||||
static status_t Launch(
|
||||
BMessenger* outMessenger=0);
|
||||
|
||||
static status_t InstantiateDormantNode(
|
||||
const dormant_node_info& info,
|
||||
media_node* outNode,
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT);
|
||||
|
||||
static status_t ReleaseInternalNode(
|
||||
const live_node_info& info,
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT);
|
||||
|
||||
static BMessenger s_messenger;
|
||||
|
||||
private: // implementation
|
||||
friend class _AddOnHostApp;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__NodeManager_AddOnHost_H__*/
|
179
src/apps/cortex/NodeManager/Connection.cpp
Normal file
179
src/apps/cortex/NodeManager/Connection.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
// Connection.cpp
|
||||
// e.moon 25jun99
|
||||
|
||||
#include "Connection.h"
|
||||
#include "NodeManager.h"
|
||||
#include "NodeRef.h"
|
||||
|
||||
#if CORTEX_XML
|
||||
#include "ExportContext.h"
|
||||
#include "MediaFormatIO.h"
|
||||
#include "xml_export_utils.h"
|
||||
#endif /*CORTEX_XML*/
|
||||
|
||||
#include <Debug.h>
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
Connection::~Connection() {
|
||||
|
||||
// PRINT(("~Connection(): '%s'->'%s'\n",
|
||||
// outputName(), inputName()));
|
||||
//
|
||||
// deallocate hints
|
||||
if(m_outputHint) delete m_outputHint;
|
||||
if(m_inputHint) delete m_inputHint;
|
||||
}
|
||||
|
||||
Connection::Connection() :
|
||||
m_disconnected(true),
|
||||
m_id(0),
|
||||
m_outputHint(0),
|
||||
m_inputHint(0) {}
|
||||
|
||||
Connection::Connection(
|
||||
uint32 id,
|
||||
media_node srcNode,
|
||||
const media_source& src,
|
||||
const char* outputName,
|
||||
media_node destNode,
|
||||
const media_destination& dest,
|
||||
const char* inputName,
|
||||
const media_format& format,
|
||||
uint32 flags) :
|
||||
|
||||
m_disconnected(false),
|
||||
m_id(id),
|
||||
m_sourceNode(srcNode),
|
||||
m_source(src),
|
||||
m_outputName(outputName),
|
||||
m_outputHint(0),
|
||||
m_destinationNode(destNode),
|
||||
m_destination(dest),
|
||||
m_inputName(inputName),
|
||||
m_inputHint(0),
|
||||
m_format(format),
|
||||
m_flags(flags) {
|
||||
|
||||
ASSERT(id);
|
||||
m_requestedFormat.type = B_MEDIA_NO_TYPE;
|
||||
}
|
||||
|
||||
Connection::Connection(
|
||||
const Connection& clone) {
|
||||
operator=(clone);
|
||||
}
|
||||
|
||||
Connection& Connection::operator=(
|
||||
const Connection& clone) {
|
||||
|
||||
m_disconnected = clone.m_disconnected;
|
||||
m_id = clone.m_id;
|
||||
m_sourceNode = clone.m_sourceNode;
|
||||
m_source = clone.m_source;
|
||||
m_outputName = clone.m_outputName;
|
||||
m_outputHint = (clone.m_outputHint ?
|
||||
new endpoint_hint(
|
||||
clone.m_outputHint->name.String(),
|
||||
clone.m_outputHint->format) :
|
||||
0);
|
||||
m_destinationNode = clone.m_destinationNode;
|
||||
m_destination = clone.m_destination;
|
||||
m_inputName = clone.m_inputName;
|
||||
m_inputHint = (clone.m_inputHint ?
|
||||
new endpoint_hint(
|
||||
clone.m_inputHint->name.String(),
|
||||
clone.m_inputHint->format) :
|
||||
0);
|
||||
m_format = clone.m_format;
|
||||
m_flags = clone.m_flags;
|
||||
m_requestedFormat = clone.m_requestedFormat;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// input/output access [e.moon 14oct99]
|
||||
|
||||
status_t Connection::getInput(
|
||||
media_input* outInput) const {
|
||||
|
||||
if(!isValid())
|
||||
return B_ERROR;
|
||||
|
||||
outInput->node = m_destinationNode;
|
||||
strcpy(outInput->name, m_inputName.String());
|
||||
outInput->format = format();
|
||||
outInput->source = m_source;
|
||||
outInput->destination = m_destination;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t Connection::getOutput(
|
||||
media_output* outOutput) const {
|
||||
|
||||
if(!isValid())
|
||||
return B_ERROR;
|
||||
|
||||
outOutput->node = m_sourceNode;
|
||||
strcpy(outOutput->name, m_outputName.String());
|
||||
outOutput->format = format();
|
||||
outOutput->source = m_source;
|
||||
outOutput->destination = m_destination;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// hint access
|
||||
|
||||
status_t Connection::getOutputHint(
|
||||
const char** outName,
|
||||
media_format* outFormat) const {
|
||||
|
||||
if(!m_outputHint)
|
||||
return B_NOT_ALLOWED;
|
||||
*outName = m_outputHint->name.String();
|
||||
*outFormat = m_outputHint->format;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t Connection::getInputHint(
|
||||
const char** outName,
|
||||
media_format* outFormat) const {
|
||||
|
||||
if(!m_inputHint)
|
||||
return B_NOT_ALLOWED;
|
||||
*outName = m_inputHint->name.String();
|
||||
*outFormat = m_inputHint->format;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void Connection::setOutputHint(
|
||||
const char* origName,
|
||||
const media_format& origFormat) {
|
||||
|
||||
if(m_outputHint) delete m_outputHint;
|
||||
m_outputHint = new endpoint_hint(origName, origFormat);
|
||||
}
|
||||
|
||||
void Connection::setInputHint(
|
||||
const char* origName,
|
||||
const media_format& origFormat) {
|
||||
|
||||
if(m_inputHint) delete m_inputHint;
|
||||
m_inputHint = new endpoint_hint(origName, origFormat);
|
||||
}
|
||||
|
||||
// [e.moon 8dec99]
|
||||
void Connection::setRequestedFormat(
|
||||
const media_format& reqFormat) {
|
||||
m_requestedFormat = reqFormat;
|
||||
}
|
||||
|
||||
// END -- Connection.cpp --
|
188
src/apps/cortex/NodeManager/Connection.h
Normal file
188
src/apps/cortex/NodeManager/Connection.h
Normal file
@ -0,0 +1,188 @@
|
||||
// Connection.h (Cortex)
|
||||
// * PURPOSE
|
||||
// Represents a general connection between two media nodes.
|
||||
//
|
||||
// * NOTES 13aug99
|
||||
// Connection is undergoing massive resimplification(TM).
|
||||
// 1) It's now intended to be stored and passed by value; synchronization
|
||||
// and reference issues are no more.
|
||||
// 2) It now refers to the participatory nodes by ID, not pointer. This
|
||||
// makes the nodes slightly more cumbersome to look up, but makes
|
||||
// Connection completely 'safe': an outdated instance doesn't contain
|
||||
// any dangerous information.
|
||||
//
|
||||
// * NOTES 29jul99
|
||||
// 1) For completeness, a release() or disconnect() method would be nice. Problems?
|
||||
// 2) How will connections between 'external' nodes be denoted? For example,
|
||||
// the audioMixer->audioOutput connection must not be broken EVER. Other external
|
||||
// connections might be manually broken, but should be left alone when the
|
||||
// NodeManager quits (whereas all internal connections need to be removed by
|
||||
// the dtor.) This implies two flags: 'internal' and 'locked'...
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 25jun99 Begun
|
||||
|
||||
#ifndef __Connection_H__
|
||||
#define __Connection_H__
|
||||
|
||||
#include <Debug.h>
|
||||
#include <MediaNode.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeManager;
|
||||
class NodeGroup;
|
||||
class NodeRef;
|
||||
|
||||
class Connection {
|
||||
|
||||
// rather incestuous set of classes we've got here...
|
||||
friend NodeRef;
|
||||
friend NodeManager;
|
||||
|
||||
public: // *** types & constants
|
||||
enum flag_t {
|
||||
// connection should be removed automatically when the NodeManager
|
||||
// is destroyed
|
||||
INTERNAL = 1<<1,
|
||||
// connection must never be removed
|
||||
LOCKED = 1<<2
|
||||
};
|
||||
|
||||
public: // *** dtor/user-level ctors
|
||||
virtual ~Connection();
|
||||
|
||||
Connection();
|
||||
Connection(
|
||||
const Connection& clone); //nyi
|
||||
Connection& operator=(
|
||||
const Connection& clone); //nyi
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
uint32 id() const { return m_id; }
|
||||
|
||||
bool isValid() const { return
|
||||
m_sourceNode != media_node::null &&
|
||||
m_destinationNode != media_node::null &&
|
||||
!m_disconnected; }
|
||||
|
||||
// changed 13aug99
|
||||
media_node_id sourceNode() const { return m_sourceNode.node; }
|
||||
const media_source& source() const { return m_source; }
|
||||
const char* outputName() const { return m_outputName.String(); }
|
||||
|
||||
// changed 13aug99
|
||||
media_node_id destinationNode() const { return m_destinationNode.node; }
|
||||
const media_destination& destination() const { return m_destination; }
|
||||
const char* inputName() const { return m_inputName.String(); }
|
||||
|
||||
const media_format& format() const { return m_format; }
|
||||
|
||||
uint32 flags() const { return m_flags; }
|
||||
|
||||
// input/output access [e.moon 14oct99]
|
||||
status_t getInput(
|
||||
media_input* outInput) const;
|
||||
|
||||
status_t getOutput(
|
||||
media_output* outOutput) const;
|
||||
|
||||
// hint access
|
||||
|
||||
status_t getOutputHint(
|
||||
const char** outName,
|
||||
media_format* outFormat) const;
|
||||
|
||||
status_t getInputHint(
|
||||
const char** outName,
|
||||
media_format* outFormat) const;
|
||||
|
||||
const media_format& requestedFormat() const { return m_requestedFormat; }
|
||||
|
||||
|
||||
protected: // *** general ctor accessible to subclasses &
|
||||
// cortex::NodeManager
|
||||
|
||||
// id must be non-0
|
||||
Connection(
|
||||
uint32 id,
|
||||
media_node srcNode,
|
||||
const media_source& src,
|
||||
const char* outputName,
|
||||
media_node destNode,
|
||||
const media_destination& dest,
|
||||
const char* inputName,
|
||||
const media_format& format,
|
||||
uint32 flags);
|
||||
|
||||
// if any information about the pre-connection (free) output format
|
||||
// is known, call this method. this info may be useful in
|
||||
// finding the output to re-establish the connection later on.
|
||||
|
||||
void setOutputHint(
|
||||
const char* origName,
|
||||
const media_format& origFormat);
|
||||
|
||||
// if any information about the pre-connection (free) input format
|
||||
// is known, call this method. this info may be useful in
|
||||
// finding the output to re-establish the connection later on.
|
||||
|
||||
void setInputHint(
|
||||
const char* origName,
|
||||
const media_format& origFormat);
|
||||
|
||||
// [e.moon 8dec99]
|
||||
void setRequestedFormat(
|
||||
const media_format& reqFormat);
|
||||
|
||||
private: // *** members
|
||||
|
||||
// info that may be useful for reconstituting a particular
|
||||
// connection later on.
|
||||
struct endpoint_hint {
|
||||
endpoint_hint(const char* _name, const media_format& _format) :
|
||||
name(_name), format(_format) {}
|
||||
|
||||
BString name;
|
||||
media_format format;
|
||||
};
|
||||
|
||||
// waiting to die?
|
||||
bool m_disconnected;
|
||||
|
||||
// unique connection ID
|
||||
uint32 m_id;
|
||||
|
||||
// [e.moon 14oct99] now stores media_nodes
|
||||
|
||||
// source/output info
|
||||
media_node m_sourceNode;
|
||||
media_source m_source;
|
||||
BString m_outputName;
|
||||
|
||||
endpoint_hint* m_outputHint;
|
||||
|
||||
// dest/input info
|
||||
media_node m_destinationNode;
|
||||
media_destination m_destination;
|
||||
BString m_inputName;
|
||||
|
||||
endpoint_hint* m_inputHint;
|
||||
|
||||
// connection format
|
||||
media_format m_format;
|
||||
|
||||
// behaviour modification
|
||||
uint32 m_flags;
|
||||
|
||||
// [e.moon 8dec99] initial requested format
|
||||
media_format m_requestedFormat;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
|
||||
#endif /*__Connection_H__*/
|
1683
src/apps/cortex/NodeManager/NodeGroup.cpp
Normal file
1683
src/apps/cortex/NodeManager/NodeGroup.cpp
Normal file
File diff suppressed because it is too large
Load Diff
620
src/apps/cortex/NodeManager/NodeGroup.h
Normal file
620
src/apps/cortex/NodeManager/NodeGroup.h
Normal file
@ -0,0 +1,620 @@
|
||||
// NodeGroup.h (Cortex/NodeManager)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Represents a logical group of media kit nodes.
|
||||
// Provides group-level operations (transport control
|
||||
// and serialization, for example.)
|
||||
//
|
||||
// * NODE SYNC/LOOPING +++++
|
||||
//
|
||||
// 22jul99
|
||||
// ----------------------------
|
||||
// +++++
|
||||
// - cycling support needs to be thoroughly defined; ie. what
|
||||
// can it do, and what can it NOT do.
|
||||
//
|
||||
// For example, a change in node latency will likely confuse
|
||||
// the cycling mechanism. Is there any possible way to avoid
|
||||
// this without explicit help from the node? The roster sends
|
||||
// no notification, and even if it did the message might not
|
||||
// arrive in time. Polling is possible, but ...ick.
|
||||
//
|
||||
// [this is assuming the 'just-in-time' cycling mechanism:
|
||||
// seeks are queued as late as possible; if the latency increases
|
||||
// significantly, the seek will be queued TOO late and the
|
||||
// node will get out of sync.]
|
||||
//
|
||||
// ----------------------------
|
||||
// 14jul99
|
||||
//
|
||||
// How about handling addition of a node to a group while it's
|
||||
// playing? The "effects insert" scenario:
|
||||
//
|
||||
// 1) you have a producer node, followed by (0..*) filters in
|
||||
// series, followed by a consumer
|
||||
// 2) the transport is started
|
||||
// 3) you wish to connect a new filter in the chain with minimal
|
||||
// interruption -- preferably a pause in output, resuming
|
||||
// with media time & perf. time still in sync.
|
||||
//
|
||||
// Process:
|
||||
// - instantiate the filter & do any setup needed
|
||||
// - [tmStart = current media time; tpStart = current perf. time]
|
||||
// - stop the transport; break the connection at the insert
|
||||
// point and connect the new filter
|
||||
// - calculate the new latency
|
||||
// - cue a seek for all nodes:
|
||||
// to tmStart+latency+pad,
|
||||
// at tpStart+latency+pad - 1
|
||||
// - cue a start for all nodes:
|
||||
// at tpStart+latency+pad
|
||||
//
|
||||
// (pad is the estimated amount of time taken to stop, break
|
||||
// & make connections, etc. It can probably be determined in
|
||||
// a test loop.)
|
||||
//
|
||||
// With the current NodeManager grouping behavior, this operation
|
||||
// would split the NodeGroup, then (provided the filter insertion
|
||||
// works) join it again. This is such a common procedure, though,
|
||||
// that an 'insert' operation at the NodeManager level would be
|
||||
// pretty damn handy. So would a 'remove insert' operation (given
|
||||
// a node with a single input and output, connect its input's source
|
||||
// to its output's destination, with the original format.)
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 29sep99 Made thread control variables 'volatile'.
|
||||
// e.moon 6jul99 Begun
|
||||
|
||||
#ifndef __NodeGroup_H__
|
||||
#define __NodeGroup_H__
|
||||
|
||||
#include "ObservableHandler.h"
|
||||
#include "observe.h"
|
||||
#include "ILockable.h"
|
||||
|
||||
// +++++ [e.moon 3dec99] need to include these for calcLatencyFn impl
|
||||
// +++++ YUCK
|
||||
#include "NodeRef.h"
|
||||
#include <MediaRoster.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Locker.h>
|
||||
#include <MediaNode.h>
|
||||
#include <String.h>
|
||||
|
||||
class BTimeSource;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
|
||||
#if CORTEX_XML
|
||||
#include "IPersistent.h"
|
||||
#endif
|
||||
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeManager;
|
||||
class NodeRef;
|
||||
|
||||
class GroupCycleThread;
|
||||
|
||||
class NodeGroup :
|
||||
public ObservableHandler,
|
||||
public ILockable {
|
||||
|
||||
typedef ObservableHandler _inherited;
|
||||
|
||||
friend NodeManager;
|
||||
friend NodeRef;
|
||||
|
||||
public: // *** messages
|
||||
enum message_t {
|
||||
// groupID: int32
|
||||
// target: BMessenger
|
||||
M_OBSERVER_ADDED =NodeGroup_message_base,
|
||||
M_OBSERVER_REMOVED,
|
||||
M_RELEASED,
|
||||
|
||||
// groupID: int32
|
||||
// nodeID: int32
|
||||
M_NODE_ADDED,
|
||||
M_NODE_REMOVED,
|
||||
|
||||
// groupID: int32
|
||||
// transportState: int32
|
||||
// ++++++ include the following?
|
||||
// runMode: int32
|
||||
// [mediaStart: bigtime_t] only sent if changed
|
||||
// [mediaEnd: bigtime_t] only sent if changed
|
||||
M_TRANSPORT_STATE_CHANGED,
|
||||
|
||||
// groupID: int32
|
||||
// timeSourceID: int32 +++++ should be a node?
|
||||
M_TIME_SOURCE_CHANGED,
|
||||
|
||||
// groupID: int32
|
||||
// runMode: int32
|
||||
M_RUN_MODE_CHANGED,
|
||||
|
||||
// Set a new time source:
|
||||
// timeSourceNode: media_node
|
||||
M_SET_TIME_SOURCE,
|
||||
|
||||
// Set a run mode
|
||||
// runMode: int32 (must be a valid run mode -- not 0!)
|
||||
M_SET_RUN_MODE,
|
||||
|
||||
// Set new start/end position:
|
||||
// position: bigtime_t (int64)
|
||||
M_SET_START_POSITION, //K
|
||||
M_SET_END_POSITION, //L
|
||||
|
||||
// Transport controls:
|
||||
M_PREROLL,
|
||||
M_START,
|
||||
M_STOP,
|
||||
M_ROLL // [e.moon 11oct99]
|
||||
};
|
||||
|
||||
public: // *** types
|
||||
|
||||
// transport state
|
||||
enum transport_state_t {
|
||||
TRANSPORT_INVALID,
|
||||
TRANSPORT_STOPPED,
|
||||
TRANSPORT_STARTING,
|
||||
TRANSPORT_RUNNING,
|
||||
TRANSPORT_ROLLING, // [e.moon 11oct99]
|
||||
TRANSPORT_STOPPING
|
||||
};
|
||||
|
||||
// [em 1feb00] flags
|
||||
enum flag_t {
|
||||
// no new nodes may be added
|
||||
GROUP_LOCKED = 1
|
||||
};
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
// free the group, including all nodes within it
|
||||
// (this call will result in the eventual deletion of the object.)
|
||||
// returns B_OK on success; B_NOT_ALLOWED if release() has
|
||||
// already been called; other error codes if the Media Roster
|
||||
// call fails.
|
||||
|
||||
status_t release();
|
||||
|
||||
// call release() rather than deleting NodeGroup objects
|
||||
virtual ~NodeGroup();
|
||||
|
||||
public: // *** const accessors
|
||||
// [e.moon 13oct99] moved method definition here to keep inline
|
||||
// in the face of a balky PPC compiler
|
||||
inline uint32 id() const { return m_id; }
|
||||
|
||||
public: // *** content accessors
|
||||
|
||||
// name access
|
||||
const char* name() const;
|
||||
status_t setName(const char* name);
|
||||
|
||||
// node access
|
||||
// - you can write-lock the group during sets of calls to these methods;
|
||||
// this ensures that the node set won't change. The methods do lock
|
||||
// the group internally, so locking isn't explicitly required.
|
||||
uint32 countNodes() const;
|
||||
NodeRef* nodeAt(
|
||||
uint32 index) const;
|
||||
|
||||
// add/remove nodes:
|
||||
// - you may only add a node with no current group.
|
||||
// - nodes added during playback will be started;
|
||||
// nodes removed during playback will be stopped (unless
|
||||
// the NO_START_STOP transport restriction flag is set
|
||||
// for a given node.)
|
||||
|
||||
status_t addNode(
|
||||
NodeRef* node);
|
||||
|
||||
status_t removeNode(
|
||||
NodeRef* node);
|
||||
|
||||
status_t removeNode(
|
||||
uint32 index);
|
||||
|
||||
// group flag access
|
||||
|
||||
uint32 groupFlags() const;
|
||||
|
||||
status_t setGroupFlags(
|
||||
uint32 flags);
|
||||
|
||||
// returns true if one or more nodes in the group have cycling
|
||||
// enabled, and the start- and end-positions are valid
|
||||
bool canCycle() const;
|
||||
|
||||
public: // *** TRANSPORT POSITIONING
|
||||
|
||||
// Fetch the current transport state
|
||||
|
||||
transport_state_t transportState() const;
|
||||
|
||||
// Set the starting media time:
|
||||
// This is the point at which playback will begin in any media
|
||||
// files/documents being played by the nodes in this group.
|
||||
// When cycle mode is enabled, this is the point to which each
|
||||
// node will be seek'd at the end of each cycle (loop).
|
||||
//
|
||||
// The starting time can't be changed in the B_OFFLINE run mode
|
||||
// (this call will return an error.)
|
||||
|
||||
status_t setStartPosition(
|
||||
bigtime_t start); //nyi
|
||||
|
||||
// Fetch the starting position:
|
||||
|
||||
bigtime_t startPosition() const; //nyi
|
||||
|
||||
// Set the ending media time:
|
||||
// This is the point at which playback will end relative to
|
||||
// media documents begin played by the nodes in this group;
|
||||
// in cycle mode, this specifies the loop point. If the
|
||||
// ending time is less than or equal to the starting time,
|
||||
// the transport will continue until stopped manually.
|
||||
// If the end position is changed while the transport is playing,
|
||||
// it must take effect retroactively (if it's before the current
|
||||
// position and looping is enabled, all nodes must 'warp' to
|
||||
// the proper post-loop position.) +++++ echk!
|
||||
//
|
||||
// The ending time can't be changed if run mode is B_OFFLINE and
|
||||
// the transport is running (this call will return an error.)
|
||||
|
||||
status_t setEndPosition(
|
||||
bigtime_t end); //nyi
|
||||
|
||||
// Fetch the end position:
|
||||
// Note that if the end position is less than or equal to the start
|
||||
// position, it's ignored.
|
||||
|
||||
bigtime_t endPosition() const; //nyi
|
||||
|
||||
public: // *** TRANSPORT OPERATIONS
|
||||
|
||||
// Preroll the group:
|
||||
// Seeks, then prerolls, each node in the group (honoring the
|
||||
// NO_SEEK and NO_PREROLL flags.) This ensures that the group
|
||||
// can start as quickly as possible.
|
||||
//
|
||||
// Returns B_NOT_ALLOWED if the transport is running.
|
||||
|
||||
status_t preroll();
|
||||
|
||||
// Start all nodes in the group:
|
||||
// Nodes with the NO_START_STOP flag aren't molested.
|
||||
|
||||
status_t start();
|
||||
|
||||
// Stop all nodes in the group:
|
||||
// Nodes with the NO_START_STOP flag aren't molested.
|
||||
|
||||
status_t stop();
|
||||
|
||||
// Roll all nodes in the group:
|
||||
// Queues a start and stop atomically (via BMediaRoster::RollNode()).
|
||||
// Returns B_NOT_ALLOWED if endPosition <= startPosition.
|
||||
|
||||
status_t roll();
|
||||
|
||||
public: // *** TIME SOURCE & RUN-MODE OPERATIONS
|
||||
|
||||
// getTimeSource():
|
||||
// returns B_ERROR if no time source has been set; otherwise,
|
||||
// returns the node ID of the current time source for all
|
||||
// nodes in the group.
|
||||
//
|
||||
// setTimeSource():
|
||||
// Calls SetTimeSourceFor() on every node in the group.
|
||||
// The group must be stopped; B_NOT_ALLOWED will be returned
|
||||
// if the state is TRANSPORT_RUNNING or TRANSPORT_ROLLING.
|
||||
|
||||
status_t getTimeSource(
|
||||
media_node* outTimeSource) const;
|
||||
|
||||
status_t setTimeSource(
|
||||
const media_node& timeSource);
|
||||
|
||||
// run mode access:
|
||||
// Sets the default run mode for the group. This will be
|
||||
// applied to every node with a wildcard (0) run mode.
|
||||
//
|
||||
// Special case: if the run mode is B_OFFLINE, it will be
|
||||
// applied to all nodes in the group.
|
||||
|
||||
status_t setRunMode(
|
||||
BMediaNode::run_mode mode); //nyi
|
||||
|
||||
BMediaNode::run_mode runMode() const; //nyi
|
||||
|
||||
public: // *** BHandler
|
||||
virtual void MessageReceived(
|
||||
BMessage* message);
|
||||
|
||||
#if CORTEX_XML
|
||||
public: // *** IPersistent
|
||||
// +++++
|
||||
|
||||
// Default constructor
|
||||
NodeGroup();
|
||||
|
||||
#endif /*CORTEX_XML*/
|
||||
|
||||
public: // *** IObservable: [19aug99]
|
||||
virtual void observerAdded(
|
||||
const BMessenger& observer);
|
||||
|
||||
virtual void observerRemoved(
|
||||
const BMessenger& observer);
|
||||
|
||||
virtual void notifyRelease();
|
||||
|
||||
virtual void releaseComplete();
|
||||
|
||||
public: // *** ILockable: [21jul99]
|
||||
// Each NodeGroup has a semaphore (BLocker).
|
||||
// Only WRITE locking is allowed!
|
||||
|
||||
bool lock(
|
||||
lock_t type=WRITE,
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT);
|
||||
bool unlock(
|
||||
lock_t type=WRITE);
|
||||
bool isLocked(
|
||||
lock_t type=WRITE) const;
|
||||
|
||||
protected: // *** ctor (accessible to NodeManager)
|
||||
NodeGroup(
|
||||
const char* name,
|
||||
NodeManager* manager,
|
||||
BMediaNode::run_mode runMode=BMediaNode::B_INCREASE_LATENCY);
|
||||
|
||||
protected: // *** internal operations
|
||||
|
||||
static uint32 NextID();
|
||||
|
||||
protected: // *** ref->group communication (LOCK REQUIRED)
|
||||
|
||||
// When a NodeRef's cycle state (ie. looping or not looping)
|
||||
// changes, it must pass that information on via this method.
|
||||
// +++++ group cycle thread
|
||||
void _refCycleChanged(
|
||||
NodeRef* ref);
|
||||
|
||||
// when a cycling node's latency changes, call this method.
|
||||
// +++++ shouldn't there be a general latency-change hook?
|
||||
void _refLatencyChanged(
|
||||
NodeRef* ref);
|
||||
|
||||
// when a NodeRef receives notification that it has been stopped,
|
||||
// but is labeled as still running, it must call this method.
|
||||
// [e.moon 11oct99: roll/B_OFFLINE support]
|
||||
void _refStopped(
|
||||
NodeRef* ref);
|
||||
|
||||
private: // *** transport helpers (LOCK REQUIRED)
|
||||
|
||||
// Preroll all nodes in the group; this is the implementation
|
||||
// of preroll().
|
||||
// *** this method should not be called from the transport thread
|
||||
// (since preroll operations can block for a relatively long time.)
|
||||
|
||||
status_t _preroll();
|
||||
|
||||
// Start all nodes in the group; this is the implementation of
|
||||
// start().
|
||||
//
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _start();
|
||||
|
||||
// Stop all nodes in the group; this is the implementation of
|
||||
// stop(). Fails if the run mode is B_OFFLINE; use _roll() instead
|
||||
// in that case.
|
||||
//
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _stop();
|
||||
|
||||
// Roll all nodes in the group; this is the implementation of
|
||||
// roll().
|
||||
//
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _roll(); //nyi [11oct99 e.moon]
|
||||
|
||||
// State transition; notify listeners
|
||||
inline void _changeState(
|
||||
transport_state_t to);
|
||||
|
||||
// Enforce a state transition, and notify listeners
|
||||
inline void _changeState(
|
||||
transport_state_t from,
|
||||
transport_state_t to);
|
||||
|
||||
|
||||
private: // *** transport thread guts
|
||||
// void _initPort();
|
||||
// void _initThread();
|
||||
//
|
||||
// static status_t _TransportThread(void* user);
|
||||
// void _transportThread();
|
||||
|
||||
// functor: calculates latency of each node it's handed, caching
|
||||
// the largest one found; includes initial latency if nodes report it.
|
||||
class calcLatencyFn { public:
|
||||
bigtime_t& maxLatency;
|
||||
calcLatencyFn(bigtime_t& _m) : maxLatency(_m) {}
|
||||
void operator()(NodeRef* r) {
|
||||
ASSERT(r);
|
||||
if(!(r->node().kind & B_BUFFER_PRODUCER)) {
|
||||
// node can't incur latency
|
||||
return;
|
||||
}
|
||||
|
||||
bigtime_t latency;
|
||||
status_t err =
|
||||
BMediaRoster::Roster()->GetLatencyFor(
|
||||
r->node(),
|
||||
&latency);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"* calcLatencyFn: GetLatencyFor() failed: %s\n",
|
||||
strerror(err)));
|
||||
return;
|
||||
}
|
||||
bigtime_t add;
|
||||
err = BMediaRoster::Roster()->GetInitialLatencyFor(
|
||||
r->node(),
|
||||
&add);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"* calcLatencyFn: GetInitialLatencyFor() failed: %s\n",
|
||||
strerror(err)));
|
||||
}
|
||||
else
|
||||
latency += add;
|
||||
if(latency > maxLatency)
|
||||
maxLatency = latency;
|
||||
}
|
||||
};
|
||||
|
||||
friend class calcLatencyFn;
|
||||
|
||||
protected: // *** cycle thread & helpers (LOCK REQUIRED)
|
||||
|
||||
// set up the cycle thread (including its kernel port)
|
||||
status_t _initCycleThread();
|
||||
|
||||
// shut down the cycle thread/port
|
||||
status_t _destroyCycleThread();
|
||||
|
||||
// 1) do the current positions specify a valid cycle region?
|
||||
// 2) are any nodes in the group cycle-enabled?
|
||||
bool _cycleValid();
|
||||
|
||||
// initialize the next cycle
|
||||
void _cycleInit(
|
||||
bigtime_t startTime);
|
||||
|
||||
// add a ref to the cycle set (in proper order, based on latency)
|
||||
void _cycleAddRef(
|
||||
NodeRef* ref);
|
||||
|
||||
// remove a ref from the cycle set
|
||||
void _cycleRemoveRef(
|
||||
NodeRef* ref);
|
||||
|
||||
// fetches the next cycle boundary (performance time of next loop)
|
||||
bigtime_t _cycleBoundary() const;
|
||||
|
||||
// cycle thread impl.
|
||||
static status_t _CycleThread(void* user);
|
||||
void _cycleThread();
|
||||
|
||||
// cycle service: seek all nodes & initiate next cycle
|
||||
void _handleCycleService();
|
||||
|
||||
private: // *** members
|
||||
|
||||
// lock
|
||||
mutable BLocker m_lock;
|
||||
|
||||
// parent object
|
||||
NodeManager* m_manager;
|
||||
|
||||
// unique group ID (non-0)
|
||||
const uint32 m_id;
|
||||
static uint32 s_nextID;
|
||||
|
||||
// group name
|
||||
BString m_name;
|
||||
|
||||
// group contents
|
||||
typedef vector<NodeRef*> node_set;
|
||||
node_set m_nodes;
|
||||
|
||||
// flags & state
|
||||
uint32 m_flags;
|
||||
transport_state_t m_transportState;
|
||||
|
||||
// default run mode applied to all nodes with a wildcard (0)
|
||||
// run mode.
|
||||
BMediaNode::run_mode m_runMode;
|
||||
|
||||
// current time source
|
||||
media_node m_timeSource;
|
||||
BTimeSource* m_timeSourceObj;
|
||||
|
||||
// slated to die?
|
||||
bool m_released;
|
||||
|
||||
// ---------------------------
|
||||
// 10aug99
|
||||
// cycle thread implementation
|
||||
// ---------------------------
|
||||
|
||||
enum cycle_thread_msg_t {
|
||||
_CYCLE_STOP,
|
||||
_CYCLE_END_CHANGED,
|
||||
_CYCLE_LATENCY_CHANGED
|
||||
};
|
||||
|
||||
thread_id m_cycleThread;
|
||||
port_id m_cyclePort;
|
||||
bool m_cycleThreadDone;
|
||||
|
||||
// when did the current cycle begin?
|
||||
bigtime_t m_cycleStart;
|
||||
|
||||
// performance time at which the current cycle settings will
|
||||
// be applied (ie. the first seek should be queued)
|
||||
bigtime_t m_cycleDeadline;
|
||||
|
||||
// performance time at which the next cycle begins
|
||||
bigtime_t m_cycleBoundary;
|
||||
|
||||
// the set of nodes currently in cycle mode
|
||||
// ordered by latency (largest first)
|
||||
node_set m_cycleNodes;
|
||||
|
||||
// count of nodes that have completed this cycle
|
||||
// (once complete, deadline and boundary are reset, and any
|
||||
// deferred start/end positions are applied.)
|
||||
uint32 m_cycleNodesComplete;
|
||||
|
||||
// max latency of any cycling node
|
||||
bigtime_t m_cycleMaxLatency;
|
||||
|
||||
// the minimum allowed loop time
|
||||
static const bigtime_t s_minCyclePeriod = 1000LL;
|
||||
|
||||
// the amount of time to allow for Media Roster calls
|
||||
// (StartNode, SeekNode, etc) to be handled
|
||||
static const bigtime_t s_rosterLatency = 1000LL; // +++++ probably high
|
||||
|
||||
// position state
|
||||
volatile bigtime_t m_startPosition;
|
||||
volatile bigtime_t m_endPosition;
|
||||
|
||||
// changed positions deferred in cycle mode
|
||||
volatile bool m_newStart;
|
||||
volatile bigtime_t m_newStartPosition;
|
||||
volatile bool m_newEnd;
|
||||
volatile bigtime_t m_newEndPosition;
|
||||
};
|
||||
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__NodeGroup_H__*/
|
2519
src/apps/cortex/NodeManager/NodeManager.cpp
Normal file
2519
src/apps/cortex/NodeManager/NodeManager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
558
src/apps/cortex/NodeManager/NodeManager.h
Normal file
558
src/apps/cortex/NodeManager/NodeManager.h
Normal file
@ -0,0 +1,558 @@
|
||||
// NodeManager.h (Cortex)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Provides a Media Kit application with a straightforward
|
||||
// way to keep track of media nodes and the connections
|
||||
// between them. Nodes are collected into sets via the
|
||||
// NodeGroup class; these sets can be controlled in tandem.
|
||||
//
|
||||
// * GROUPING NOTES
|
||||
// A new group is created with the following information:
|
||||
// - time source (defaults to the DAC time source)
|
||||
// - a user-provided name
|
||||
//
|
||||
// New nodes can be added to a group via NodeGroup methods. When a
|
||||
// node is added to a group, it will automatically be assigned the
|
||||
// group's time source. Unless the node has a run mode set, it will
|
||||
// also be assigned the group's run mode. (If the group is in B_OFFLINE
|
||||
// mode, this will be assigned to all nodes even if they specify something
|
||||
// else.) If a node is added to a group whose transport is running, it
|
||||
// will automatically be seeked and started (unless one or both of those
|
||||
// operations has been disabled.)
|
||||
//
|
||||
// * SYNCHRONIZATION NOTES
|
||||
// Each NodeManager object, including all the NodeGroup and NodeRef
|
||||
// objects in its care, is synchronized by a single semaphore.
|
||||
// Most operations in these three classes require that the object
|
||||
// be locked.
|
||||
//
|
||||
// * UI HOOKS
|
||||
// NodeManager resends any Media Roster messages to all observers
|
||||
// *after* processing them: the NodeRef corresponding to a newly-
|
||||
// created node, for example, must exist by the time that a
|
||||
// NodeManager observer receives B_MEDIA_NODE_CREATED.
|
||||
//
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 7nov99 1) added hooks for Media Roster message processing
|
||||
// 2) improved NodeGroup handling
|
||||
// e.moon 6nov99 safe node instantiation (via addon-host
|
||||
// application)
|
||||
// e.moon 11aug99 Expanded findConnection() methods.
|
||||
// e.moon 6jul99 Begun
|
||||
|
||||
#ifndef __NodeManager_H__
|
||||
#define __NodeManager_H__
|
||||
|
||||
#include "ILockable.h"
|
||||
#include "ObservableLooper.h"
|
||||
#include "observe.h"
|
||||
|
||||
#include <Looper.h>
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaNode.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class BMediaRoster;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class Connection;
|
||||
class NodeGroup;
|
||||
class NodeRef;
|
||||
|
||||
class NodeManager :
|
||||
public ObservableLooper,
|
||||
public ILockable {
|
||||
|
||||
// primary parent class:
|
||||
typedef ObservableLooper _inherited;
|
||||
|
||||
friend NodeGroup;
|
||||
friend NodeRef;
|
||||
friend Connection;
|
||||
|
||||
public: // *** messages
|
||||
// [13aug99]
|
||||
// NodeManager retransmits Media Roster messages to its listeners,
|
||||
// after processing each message.
|
||||
//
|
||||
/// B_MEDIA_CONNECTION_BROKEN
|
||||
// This message, as sent by the Media Roster, contains only
|
||||
// source/destination information. NodeManager adds these fields
|
||||
// to the message:
|
||||
// __connection_id: (uint32) id of the Connection; 0 if no
|
||||
// matching Connection was found.
|
||||
// __source_node_id: media_node_id of the node corresponding to
|
||||
// the source; 0 if no matching Connection was
|
||||
// found.
|
||||
// __destination_node_id: media_node_id of the node corresponding to
|
||||
// the source; 0 if no matching Connection was
|
||||
// found.
|
||||
//
|
||||
// B_MEDIA_FORMAT_CHANGED
|
||||
// NodeManager add these fields as above:
|
||||
// __connection_id
|
||||
// __source_node_id
|
||||
// __destination_node_id
|
||||
|
||||
enum outbound_message_t {
|
||||
M_OBSERVER_ADDED =NodeManager_message_base,
|
||||
M_OBSERVER_REMOVED,
|
||||
M_RELEASED,
|
||||
|
||||
// groupID: int32
|
||||
M_GROUP_CREATED,
|
||||
M_GROUP_DELETED,
|
||||
|
||||
// groupID: int32 x2 (the first is the original)
|
||||
M_GROUP_SPLIT,
|
||||
|
||||
// groupID: int32 x2
|
||||
M_GROUPS_MERGED
|
||||
};
|
||||
|
||||
public: // *** default group names
|
||||
|
||||
static const char* const s_defaultGroupPrefix;
|
||||
static const char* const s_timeSourceGroup;
|
||||
static const char* const s_audioInputGroup;
|
||||
static const char* const s_videoInputGroup;
|
||||
static const char* const s_audioMixerGroup;
|
||||
static const char* const s_videoOutputGroup;
|
||||
|
||||
public: // *** hooks
|
||||
|
||||
// [e.moon 7nov99] these hooks are called during processing of
|
||||
// BMediaRoster messages, before any notification is sent to
|
||||
// observers. For example, if a B_MEDIA_NODES_CREATED message
|
||||
// were received describing 3 new nodes, nodeCreated() would be
|
||||
// called 3 times before the notification was sent.
|
||||
|
||||
virtual void nodeCreated(
|
||||
NodeRef* ref);
|
||||
|
||||
virtual void nodeDeleted(
|
||||
const NodeRef* ref);
|
||||
|
||||
virtual void connectionMade(
|
||||
Connection* connection);
|
||||
|
||||
virtual void connectionBroken(
|
||||
const Connection* connection);
|
||||
|
||||
virtual void connectionFailed(
|
||||
const media_output & output,
|
||||
const media_input & input,
|
||||
const media_format & format,
|
||||
status_t error);
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
NodeManager(
|
||||
bool useAddOnHost=false);
|
||||
|
||||
// don't directly delete NodeManager;
|
||||
// use IObservable::release()
|
||||
virtual ~NodeManager();
|
||||
|
||||
public: // *** const members
|
||||
|
||||
// cached roster pointer
|
||||
::BMediaRoster* const roster;
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// * ACCESS
|
||||
|
||||
// fetches NodeRef corresponding to a given ID; returns
|
||||
// B_BAD_VALUE if no matching entry was found (and writes
|
||||
// a 0 into the provided pointer.)
|
||||
|
||||
status_t getNodeRef(
|
||||
media_node_id id,
|
||||
NodeRef** outRef) const;
|
||||
|
||||
// [13aug99]
|
||||
// fetches Connection corresponding to a given source/destination
|
||||
// on a given node. Returns an invalid connection and B_BAD_VALUE
|
||||
// if no matching connection was found.
|
||||
|
||||
status_t findConnection(
|
||||
media_node_id node,
|
||||
const media_source& source,
|
||||
Connection* outConnection) const;
|
||||
|
||||
status_t findConnection(
|
||||
media_node_id node,
|
||||
const media_destination& destination,
|
||||
Connection* outConnection) const;
|
||||
|
||||
// [e.moon 28sep99]
|
||||
// fetches a Connection matching the given source and destination
|
||||
// nodes. Returns an invalid connection and B_BAD_VALUE if
|
||||
// no matching connection was found
|
||||
|
||||
status_t findConnection(
|
||||
media_node_id sourceNode,
|
||||
media_node_id destinationNode,
|
||||
Connection* outConnection) const;
|
||||
|
||||
// [e.moon 28sep99]
|
||||
// tries to find a route from 'nodeA' to 'nodeB'; returns
|
||||
// true if one exists, false if not. If nodeA and nodeB
|
||||
// are the same node, only returns true if it's actually
|
||||
// connected to itself.
|
||||
|
||||
bool findRoute(
|
||||
media_node_id nodeA,
|
||||
media_node_id nodeB);
|
||||
|
||||
private:
|
||||
// implementation of above
|
||||
class _find_route_state;
|
||||
bool _find_route_recurse(
|
||||
NodeRef* origin,
|
||||
media_node_id target,
|
||||
_find_route_state* state);
|
||||
|
||||
public:
|
||||
// fetches Connection corresponding to a given source or
|
||||
// destination; Returns an invalid connection and B_BAD_VALUE if
|
||||
// none found.
|
||||
// * Note: this is the slowest possible way to look up a
|
||||
// connection. Since the low-level source/destination
|
||||
// structures don't include a node ID, and a destination
|
||||
// port can differ from its node's control port, a linear
|
||||
// search of all known connections is performed. Only
|
||||
// use these methods if you have no clue what node the
|
||||
// connection corresponds to.
|
||||
|
||||
status_t findConnection(
|
||||
const media_source& source,
|
||||
Connection* outConnection) const;
|
||||
|
||||
status_t findConnection(
|
||||
const media_destination& destination,
|
||||
Connection* outConnection) const;
|
||||
|
||||
// fetch NodeRefs for system nodes (if a particular node doesn't
|
||||
// exist, these methods return 0)
|
||||
|
||||
NodeRef* audioInputNode() const;
|
||||
NodeRef* videoInputNode() const;
|
||||
NodeRef* audioMixerNode() const;
|
||||
NodeRef* audioOutputNode() const;
|
||||
NodeRef* videoOutputNode() const;
|
||||
|
||||
// * GROUP CREATION
|
||||
|
||||
NodeGroup* createGroup(
|
||||
const char* name,
|
||||
BMediaNode::run_mode runMode=BMediaNode::B_INCREASE_LATENCY);
|
||||
|
||||
// fetch groups by index
|
||||
// - you can write-lock the manager during sets of calls to these methods;
|
||||
// this ensures that the group set won't change. The methods do lock
|
||||
// the group internally, so locking isn't explicitly required.
|
||||
|
||||
uint32 countGroups() const;
|
||||
NodeGroup* groupAt(
|
||||
uint32 index) const;
|
||||
|
||||
// look up a group by unique ID; returns B_BAD_VALUE if no
|
||||
// matching group was found
|
||||
|
||||
status_t findGroup(
|
||||
uint32 id,
|
||||
NodeGroup** outGroup) const;
|
||||
|
||||
// look up a group by name; returns B_NAME_NOT_FOUND if
|
||||
// no group matching the name was found.
|
||||
|
||||
status_t findGroup(
|
||||
const char* name,
|
||||
NodeGroup** outGroup) const;
|
||||
|
||||
// merge the given source group to the given destination;
|
||||
// empties and releases the source group
|
||||
|
||||
status_t mergeGroups(
|
||||
NodeGroup* sourceGroup,
|
||||
NodeGroup* destinationGroup);
|
||||
|
||||
// [e.moon 28sep99]
|
||||
// split group: given two nodes currently in the same group
|
||||
// that are not connected (directly OR indirectly),
|
||||
// this method removes outsideNode, and all nodes connected
|
||||
// to outsideNode, from the common group. These nodes are
|
||||
// then added to a new group, returned in 'outGroup'. The
|
||||
// new group has " split" appended to the end of the original
|
||||
// group's name.
|
||||
//
|
||||
// Returns B_NOT_ALLOWED if any of the above conditions aren't
|
||||
// met (ie. the nodes are in different groups or an indirect
|
||||
// route exists from one to the other), or B_OK if the group
|
||||
// was split successfully.
|
||||
|
||||
status_t splitGroup(
|
||||
NodeRef* insideNode,
|
||||
NodeRef* outsideNode,
|
||||
NodeGroup** outGroup);
|
||||
|
||||
// * INSTANTIATION & CONNECTION
|
||||
// Use these calls rather than the associated BMediaRoster()
|
||||
// methods to assure that the nodes and connections you set up
|
||||
// can be properly serialized & reconstituted.
|
||||
|
||||
// basic BMediaRoster::InstantiateDormantNode() wrapper
|
||||
// - writes a 0 into *outRef if the instantiation fails
|
||||
// - [e.moon 23oct99]
|
||||
// returns B_BAD_INDEX if InstantiateDormantNode() returns
|
||||
// success, but doesn't hand back a viable media_node
|
||||
// - [e.moon 6nov99] +++++ 'distributed' instantiate:
|
||||
// wait for an external app to create the node; allow for
|
||||
// failure.
|
||||
|
||||
status_t instantiate(
|
||||
const dormant_node_info& info,
|
||||
NodeRef** outRef=0,
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT,
|
||||
uint32 nodeFlags=0);
|
||||
|
||||
// SniffRef/Instantiate.../SetRefFor: a one-call interface
|
||||
// to create a node capable of playing a given media file.
|
||||
// - writes a 0 into *outRef if the instantiation fails; on the
|
||||
// other hand, if instantiation succeeds, but SetRefFor() fails,
|
||||
// a NodeRef will still be returned.
|
||||
|
||||
status_t instantiate(
|
||||
const entry_ref& file,
|
||||
uint64 requireNodeKinds,
|
||||
NodeRef** outRef,
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT,
|
||||
uint32 nodeFlags=0,
|
||||
bigtime_t* outDuration=0);
|
||||
|
||||
// use this method to reference nodes created within your
|
||||
// application. These nodes can't be automatically reconstituted
|
||||
// by the cortex serializer yet.
|
||||
|
||||
status_t reference(
|
||||
BMediaNode* node,
|
||||
NodeRef** outRef,
|
||||
uint32 nodeFlags=0);
|
||||
|
||||
// the most flexible form of connect(): set the template
|
||||
// format as you would for BMediaRoster::Connect().
|
||||
|
||||
status_t connect(
|
||||
const media_output& output,
|
||||
const media_input& input,
|
||||
const media_format& templateFormat,
|
||||
Connection* outConnection=0);
|
||||
|
||||
// format-guessing form of connect(): tries to find
|
||||
// a common format between output & input before connection;
|
||||
// returns B_MEDIA_BAD_FORMAT if no common format type found.
|
||||
//
|
||||
// NOTE: the specifics of the input and output formats are ignored;
|
||||
// this method only looks at the format type, and properly
|
||||
// handles wildcards at that level (B_MEDIA_NO_TYPE).
|
||||
|
||||
status_t connect(
|
||||
const media_output& output,
|
||||
const media_input& input,
|
||||
Connection* outConnection=0);
|
||||
|
||||
// disconnects the connection represented by the provided
|
||||
// Connection object. if successful, returns B_OK.
|
||||
|
||||
status_t disconnect(
|
||||
const Connection& connection);
|
||||
|
||||
public: // *** node/connection iteration
|
||||
// *** MUST BE LOCKED for any of these calls
|
||||
|
||||
// usage:
|
||||
// For the first call, pass 'cookie' a pointer to a void* set to 0.
|
||||
// Returns B_BAD_INDEX when the set of nodes has been exhausted (and
|
||||
// invalidates the cookie, so don't try to use it after this point.)
|
||||
|
||||
status_t getNextRef(
|
||||
NodeRef** outRef,
|
||||
void** cookie);
|
||||
|
||||
// if you want to stop iterating, call this method to avoid leaking
|
||||
// memory
|
||||
void disposeRefCookie(
|
||||
void** cookie);
|
||||
|
||||
status_t getNextConnection(
|
||||
Connection* outConnection,
|
||||
void** cookie);
|
||||
|
||||
void disposeConnectionCookie(
|
||||
void** cookie);
|
||||
|
||||
public: // *** BHandler impl
|
||||
void MessageReceived(BMessage* message); //nyi
|
||||
|
||||
public: // *** IObservable hooks
|
||||
virtual void observerAdded(
|
||||
const BMessenger& observer);
|
||||
|
||||
virtual void observerRemoved(
|
||||
const BMessenger& observer);
|
||||
|
||||
virtual void notifyRelease();
|
||||
|
||||
virtual void releaseComplete();
|
||||
|
||||
|
||||
public: // *** ILockable impl.
|
||||
virtual bool lock(
|
||||
lock_t type=WRITE,
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT);
|
||||
|
||||
virtual bool unlock(
|
||||
lock_t type=WRITE);
|
||||
|
||||
virtual bool isLocked(
|
||||
lock_t type=WRITE) const;
|
||||
|
||||
|
||||
protected: // *** internal operations (LOCK REQUIRED)
|
||||
|
||||
|
||||
void _initCommonNodes();
|
||||
|
||||
void _addRef(
|
||||
NodeRef* ref);
|
||||
void _removeRef(
|
||||
media_node_id id);
|
||||
|
||||
// create, add, return a NodeRef for the given external node;
|
||||
// must not already exist
|
||||
NodeRef* _addRefFor(
|
||||
const media_node& node,
|
||||
uint32 nodeFlags,
|
||||
uint32 nodeImplFlags=0);
|
||||
|
||||
void _addConnection(
|
||||
Connection* connection);
|
||||
void _removeConnection(
|
||||
const Connection& connection);
|
||||
|
||||
void _addGroup(
|
||||
NodeGroup* group);
|
||||
void _removeGroup(
|
||||
NodeGroup* group);
|
||||
|
||||
// dtor helpers
|
||||
inline void _clearGroup(
|
||||
NodeGroup* group);
|
||||
|
||||
inline void _freeConnection(
|
||||
Connection* connection);
|
||||
|
||||
// message handlers
|
||||
|
||||
// now returns B_OK iff the message should be relayed to observers
|
||||
// [e.moon 11oct99]
|
||||
inline status_t _handleNodesCreated(
|
||||
BMessage* message);
|
||||
|
||||
inline void _handleNodesDeleted(
|
||||
BMessage* message);
|
||||
|
||||
inline void _handleConnectionMade(
|
||||
BMessage* message);
|
||||
|
||||
inline void _handleConnectionBroken(
|
||||
BMessage* message);
|
||||
|
||||
inline void _handleFormatChanged(
|
||||
BMessage* message);
|
||||
// return flags appropriate for an external
|
||||
// node with the given 'kind'
|
||||
|
||||
inline uint32 _userFlagsForKind(
|
||||
uint32 kind);
|
||||
|
||||
inline uint32 _implFlagsForKind(
|
||||
uint32 kind);
|
||||
|
||||
// [e.moon 28sep99] latency updating
|
||||
// These methods must set the recording-mode delay for
|
||||
// any B_RECORDING nodes they handle.
|
||||
|
||||
// +++++ abstract to 'for each' and 'for each from'
|
||||
// methods (template or callback?)
|
||||
|
||||
|
||||
// refresh cached latency for every node in the given group
|
||||
// (or all nodes if no group given.)
|
||||
|
||||
inline void _updateLatencies(
|
||||
NodeGroup* group =0);
|
||||
|
||||
// refresh cached latency for every node attached to
|
||||
// AND INCLUDING the given origin node.
|
||||
// if 'recurse' is true, affects indirectly attached
|
||||
// nodes as well.
|
||||
|
||||
inline void _updateLatenciesFrom(
|
||||
NodeRef* origin,
|
||||
bool recurse =false);
|
||||
|
||||
// a bit of unpleasantness [e.moon 13oct99]
|
||||
inline void _lockAllGroups();
|
||||
inline void _unlockAllGroups();
|
||||
|
||||
private: // *** internal messages
|
||||
enum int_message_t {
|
||||
// groupID: int32
|
||||
_M_GROUP_RELEASED = NodeManager_int_message_base
|
||||
};
|
||||
|
||||
private: // *** members
|
||||
// the main NodeRef store
|
||||
typedef map<media_node_id, NodeRef*> node_ref_map;
|
||||
node_ref_map m_nodeRefMap;
|
||||
|
||||
// the Connection stores (connections are indexed by both
|
||||
// source and destination node ID)
|
||||
typedef multimap<media_node_id, Connection*> con_map;
|
||||
con_map m_conSourceMap;
|
||||
con_map m_conDestinationMap;
|
||||
|
||||
// the NodeGroup store
|
||||
typedef vector<NodeGroup*> node_group_set;
|
||||
node_group_set m_nodeGroupSet;
|
||||
|
||||
// common system nodes
|
||||
NodeRef* m_audioInputNode;
|
||||
NodeRef* m_videoInputNode;
|
||||
NodeRef* m_audioMixerNode;
|
||||
NodeRef* m_audioOutputNode;
|
||||
NodeRef* m_videoOutputNode;
|
||||
|
||||
// next unique connection ID
|
||||
uint32 m_nextConID;
|
||||
|
||||
// false until the first 'nodes created' message is
|
||||
// received from the Media Roster, and pre-existing connection
|
||||
// info is filled in:
|
||||
bool m_existingNodesInit;
|
||||
|
||||
// true if nodes should be launched via an external application
|
||||
// (using AddOnHost)
|
||||
bool m_useAddOnHost;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__NodeManager_H__*/
|
2233
src/apps/cortex/NodeManager/NodeRef.cpp
Normal file
2233
src/apps/cortex/NodeManager/NodeRef.cpp
Normal file
File diff suppressed because it is too large
Load Diff
656
src/apps/cortex/NodeManager/NodeRef.h
Normal file
656
src/apps/cortex/NodeManager/NodeRef.h
Normal file
@ -0,0 +1,656 @@
|
||||
// NodeRef.h (Cortex/NodeManager)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Represents a NodeManager reference to a live Media Kit Node.
|
||||
//
|
||||
// * FEATURES UNDER CONSIDERATION +++++
|
||||
// - lazy referencing: m_released becomes m_referenced; only reference
|
||||
// externally created nodes once they've been grouped or connected.
|
||||
// +++++ or disconnected? keep a'thinkin...
|
||||
// - expose dormant_node_info
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 11aug99 Added kind().
|
||||
// e.moon 9aug99 Moved position & cycle threads into NodeRef;
|
||||
// no more 'group transport thread'.
|
||||
// e.moon 25jun99 Begun
|
||||
|
||||
#ifndef __NodeRef_H__
|
||||
#define __NodeRef_H__
|
||||
|
||||
#include <MediaAddOn.h>
|
||||
#include <MediaNode.h>
|
||||
#include <String.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ILockable.h"
|
||||
#include "MultiInvoker.h"
|
||||
#include "ObservableHandler.h"
|
||||
#include "observe.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class Connection;
|
||||
class NodeManager;
|
||||
class NodeGroup;
|
||||
class NodeSyncThread;
|
||||
|
||||
class NodeRef :
|
||||
public ObservableHandler,
|
||||
protected ILockable {
|
||||
|
||||
typedef ObservableHandler _inherited;
|
||||
|
||||
friend NodeManager;
|
||||
friend NodeGroup;
|
||||
|
||||
public: // *** messages
|
||||
enum message_t {
|
||||
// OUTBOUND
|
||||
// nodeID: int32
|
||||
// target: BMessenger
|
||||
M_OBSERVER_ADDED =NodeRef_message_base,
|
||||
M_OBSERVER_REMOVED,
|
||||
M_RELEASED,
|
||||
|
||||
// OUTBOUND
|
||||
// nodeID: int32
|
||||
// groupID: int32
|
||||
M_GROUP_CHANGED,
|
||||
|
||||
// nodeID: int32
|
||||
// runMode: int32
|
||||
M_RUN_MODE_CHANGED,
|
||||
|
||||
// +++++
|
||||
M_INPUTS_CHANGED, //nyi
|
||||
M_OUTPUTS_CHANGED, //nyi
|
||||
|
||||
// OUTBOUND
|
||||
// * only sent to position listeners
|
||||
// nodeID: int32
|
||||
// when: int64
|
||||
// position: int64
|
||||
M_POSITION,
|
||||
|
||||
// INBOUND
|
||||
// runMode: int32
|
||||
// delay: int64 (bigtime_t; applies only to B_RECORDING mode)
|
||||
M_SET_RUN_MODE,
|
||||
|
||||
// INBOUND
|
||||
M_PREROLL,
|
||||
|
||||
// INBOUND
|
||||
// "cycling"; bool (OR "be:value"; int32)
|
||||
M_SET_CYCLING,
|
||||
|
||||
// OUTBOUND
|
||||
// "cycling"; bool
|
||||
M_CYCLING_CHANGED
|
||||
};
|
||||
|
||||
public: // *** flags
|
||||
enum flag_t {
|
||||
// node-level transport restrictions
|
||||
NO_START_STOP = 1<<1,
|
||||
NO_SEEK = 1<<2,
|
||||
NO_PREROLL = 1<<3,
|
||||
|
||||
// [e.moon 28sep99] useful for misbehaved nodes
|
||||
NO_STOP = 1<<4,
|
||||
|
||||
// [e.moon 11oct99]
|
||||
// Disables media-roster connection (which currently
|
||||
// only makes use of B_MEDIA_NODE_STOPPED.)
|
||||
// This flag may become deprecated as the Media Kit
|
||||
// evolves (ie. more node-level notifications come
|
||||
// along.)
|
||||
NO_ROSTER_WATCH = 1<<5,
|
||||
|
||||
// [e.moon 14oct99]
|
||||
// Disables position reporting (via BMediaRoster::SyncToNode().)
|
||||
// Some system-provided nodes tend to explode when SyncToNode() is
|
||||
// called on them (like the video-file player in BeOS R4.5.2).
|
||||
NO_POSITION_REPORTING = 1<<6
|
||||
};
|
||||
|
||||
public: // *** dtor
|
||||
|
||||
// free the node (this call will result in the eventual
|
||||
// deletion of the object.)
|
||||
// returns B_OK on success; B_NOT_ALLOWED if release() has
|
||||
// already been called; other error codes if the Media Roster
|
||||
// call fails.
|
||||
|
||||
status_t release();
|
||||
|
||||
// call release() rather than deleting NodeRef objects
|
||||
virtual ~NodeRef();
|
||||
|
||||
public: // *** const accessors
|
||||
// [e.moon 13oct99] moved method definitions here to keep inline
|
||||
// in the face of a balky PPC compiler
|
||||
inline const media_node& node() const { return m_info.node; }
|
||||
inline uint32 kind() const { return m_info.node.kind; }
|
||||
inline const live_node_info& nodeInfo() const { return m_info; }
|
||||
inline const char* name() const { return m_info.name; }
|
||||
inline media_node_id id() const { return m_info.node.node; }
|
||||
|
||||
public: // *** member access
|
||||
|
||||
// turn cycle mode (looping) on or off
|
||||
void setCycling(
|
||||
bool cycle);
|
||||
bool isCycling() const;
|
||||
|
||||
// is the node running?
|
||||
bool isRunning() const;
|
||||
|
||||
// was the node created via NodeManager::instantiate()?
|
||||
bool isInternal() const;
|
||||
|
||||
// fetch the group, or 0 if the node is ungrouped.
|
||||
NodeGroup* group() const;
|
||||
|
||||
// flag access
|
||||
uint32 flags() const;
|
||||
void setFlags(
|
||||
uint32 flags);
|
||||
|
||||
// [e.moon 29sep99]
|
||||
// access addon-hint info
|
||||
// - returns B_BAD_VALUE if not an add-on node created by this NodeManager
|
||||
|
||||
status_t getDormantNodeInfo(
|
||||
dormant_node_info* outInfo);
|
||||
|
||||
// [e.moon 29sep99]
|
||||
// access file being played
|
||||
// - returns B_BAD_VALUE if not an add-on node created by this NodeManager,
|
||||
// or if the node has no associated file
|
||||
|
||||
status_t getFile(
|
||||
entry_ref* outFile);
|
||||
|
||||
// [e.moon 8dec99]
|
||||
// set file to play
|
||||
|
||||
status_t setFile(
|
||||
const entry_ref& file,
|
||||
bigtime_t* outDuration=0); //nyi
|
||||
|
||||
// [e.moon 23oct99]
|
||||
// returns true if the media_node has been released (call releaseNode() to
|
||||
// make this happen.)
|
||||
|
||||
bool isNodeReleased() const;
|
||||
|
||||
// now implemented by ObservableHandler [20aug99]
|
||||
// // has this reference been released?
|
||||
// bool isReleased() const;
|
||||
|
||||
public: // *** run-mode operations
|
||||
void setRunMode(
|
||||
uint32 runMode,
|
||||
bigtime_t delay=0LL);
|
||||
uint32 runMode() const;
|
||||
|
||||
bigtime_t recordingDelay() const;
|
||||
|
||||
// calculates the minimum amount of delay needed for
|
||||
// B_RECORDING mode
|
||||
// +++++ 15sep99: returns biggest_output_buffer_duration * 2
|
||||
// +++++ 28sep99: adds downstream latency
|
||||
bigtime_t calculateRecordingModeDelay(); //nyi
|
||||
|
||||
public: // *** connection access
|
||||
|
||||
// connection access: vector versions
|
||||
|
||||
status_t getInputConnections(
|
||||
vector<Connection>& ioConnections,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
status_t getOutputConnections(
|
||||
vector<Connection>& ioConnections,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
// connection access: flat array versions
|
||||
|
||||
status_t getInputConnections(
|
||||
Connection* outConnections,
|
||||
int32 maxConnections,
|
||||
int32* outNumConnections,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
status_t getOutputConnections(
|
||||
Connection* outConnections,
|
||||
int32 maxConnections,
|
||||
int32* outNumConnections,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
// +++++ connection matching by source/destination +++++
|
||||
|
||||
public: // *** position reporting/listening
|
||||
|
||||
bool positionReportsEnabled() const;
|
||||
|
||||
void enablePositionReports();
|
||||
void disablePositionReports();
|
||||
|
||||
// Fetch the approximate current position:
|
||||
// Returns the last reported position, and the
|
||||
// performance time at which that position was reached. If the
|
||||
// transport has never been started, the start position and
|
||||
// a performance time of 0 will be returned. If position updating
|
||||
// isn't currently enabled, returns B_NOT_ALLOWED.
|
||||
|
||||
status_t getLastPosition(
|
||||
bigtime_t* outPosition,
|
||||
bigtime_t* outPerfTime) const;
|
||||
|
||||
// Subscribe to regular position reports:
|
||||
// Position reporting isn't rolled into the regular observer
|
||||
// interface because a large number of messages are generated
|
||||
// (the frequency can be changed; see below.)
|
||||
|
||||
status_t addPositionObserver(
|
||||
BHandler* handler);
|
||||
|
||||
status_t removePositionObserver(
|
||||
BHandler* handler);
|
||||
|
||||
// Set how often position updates will be sent:
|
||||
// Realistically, period should be > 10000 or so.
|
||||
|
||||
status_t setPositionUpdatePeriod(
|
||||
bigtime_t period);
|
||||
|
||||
bigtime_t positionUpdatePeriod() const;
|
||||
|
||||
public: // *** BMediaRoster wrappers & convenience methods
|
||||
|
||||
// release the media node
|
||||
// (if allowed, will trigger the release/deletion of this object)
|
||||
status_t releaseNode();
|
||||
|
||||
// calculate total (internal + downstream) latency for this node
|
||||
|
||||
status_t totalLatency(
|
||||
bigtime_t* outLatency) const;
|
||||
|
||||
|
||||
// retrieve input/output matching the given destination/source.
|
||||
// returns B_MEDIA_BAD_[SOURCE | DESTINATION] if the destination
|
||||
// or source don't correspond to this node.
|
||||
|
||||
status_t findInput(
|
||||
const media_destination& forDestination,
|
||||
media_input* outInput) const;
|
||||
|
||||
status_t findOutput(
|
||||
const media_source& forSource,
|
||||
media_output* outOutput) const;
|
||||
|
||||
|
||||
// endpoint matching (given name and/or format as 'hints').
|
||||
// If no hints are given, returns the first free endpoint, if
|
||||
// any exist.
|
||||
// returns B_ERROR if no matching endpoint is found.
|
||||
|
||||
status_t findFreeInput(
|
||||
media_input* outInput,
|
||||
media_type type=B_MEDIA_UNKNOWN_TYPE,
|
||||
const char* name=0) const;
|
||||
|
||||
status_t findFreeInput(
|
||||
media_input* outInput,
|
||||
const media_format* format,
|
||||
const char* name=0) const;
|
||||
|
||||
status_t findFreeOutput(
|
||||
media_output* outOutput,
|
||||
media_type type=B_MEDIA_UNKNOWN_TYPE,
|
||||
const char* name=0) const;
|
||||
|
||||
status_t findFreeOutput(
|
||||
media_output* outOutput,
|
||||
const media_format* format,
|
||||
const char* name=0) const;
|
||||
|
||||
// node endpoint access: vector versions (wrappers for BMediaRoster
|
||||
// calls.)
|
||||
|
||||
status_t getFreeInputs(
|
||||
vector<media_input>& ioInputs,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
status_t getConnectedInputs(
|
||||
vector<media_input>& ioInputs,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
status_t getFreeOutputs(
|
||||
vector<media_output>& ioOutputs,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
status_t getConnectedOutputs(
|
||||
vector<media_output>& ioOutputs,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
|
||||
// node endpoint access: array versions (wrappers for BMediaRoster
|
||||
// calls.)
|
||||
|
||||
status_t getFreeInputs(
|
||||
media_input* outInputs,
|
||||
int32 maxInputs,
|
||||
int32* outNumInputs,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
status_t getConnectedInputs(
|
||||
media_input* outInputs,
|
||||
int32 maxInputs,
|
||||
int32* outNumInputs) const;
|
||||
|
||||
status_t getFreeOutputs(
|
||||
media_output* outOutputs,
|
||||
int32 maxOutputs,
|
||||
int32* outNumOutputs,
|
||||
media_type filterType=B_MEDIA_UNKNOWN_TYPE) const;
|
||||
|
||||
status_t getConnectedOutputs(
|
||||
media_output* outOutputs,
|
||||
int32 maxOutputs,
|
||||
int32* outNumOutputs) const;
|
||||
|
||||
public: // *** BHandler:
|
||||
virtual void MessageReceived(
|
||||
BMessage* message);
|
||||
|
||||
public: // *** IObservable: [20aug99]
|
||||
virtual void observerAdded(
|
||||
const BMessenger& observer);
|
||||
|
||||
virtual void observerRemoved(
|
||||
const BMessenger& observer);
|
||||
|
||||
virtual void notifyRelease();
|
||||
|
||||
virtual void releaseComplete();
|
||||
|
||||
protected: // *** ILockable
|
||||
// Only WRITE locking is allowed!
|
||||
|
||||
bool lock(
|
||||
lock_t type=WRITE,
|
||||
bigtime_t timeout=B_INFINITE_TIMEOUT);
|
||||
bool unlock(
|
||||
lock_t type=WRITE);
|
||||
bool isLocked(
|
||||
lock_t type=WRITE) const;
|
||||
|
||||
protected: // *** ctor (accessible to NodeManager)
|
||||
// throws runtime_error
|
||||
NodeRef(
|
||||
const media_node& node,
|
||||
NodeManager* manager,
|
||||
uint32 userFlags,
|
||||
uint32 implFlags);
|
||||
|
||||
protected: // *** endpoint-fixing operations (no lock required)
|
||||
|
||||
// 'fix' (fill in node if needed) sets of inputs/outputs
|
||||
void _fixInputs(
|
||||
media_input* inputs,
|
||||
int32 count) const;
|
||||
void _fixInputs(
|
||||
vector<media_input>& inputs) const;
|
||||
|
||||
void _fixOutputs(
|
||||
media_output* outputs,
|
||||
int32 count) const;
|
||||
void _fixOutputs(
|
||||
vector<media_output>& outputs) const;
|
||||
|
||||
protected: // *** internal/NodeManager operations (LOCK REQUIRED)
|
||||
|
||||
// call after instantiation to register info used to select and
|
||||
// create this add-on node
|
||||
|
||||
void _setAddonHint(
|
||||
const dormant_node_info* info,
|
||||
const entry_ref* file=0);
|
||||
|
||||
// call to set a new group; if 0, the node must have no
|
||||
// connections
|
||||
void _setGroup(
|
||||
NodeGroup* group);
|
||||
|
||||
// *** NodeGroup API ***
|
||||
// 9aug99: moved from NodeGroup
|
||||
// [e.moon 13oct99] removed inlines for the sake of PPC sanity
|
||||
|
||||
// initialize the given node's transport-state members
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _initTransportState();
|
||||
|
||||
status_t _setTimeSource(
|
||||
media_node_id timeSourceID);
|
||||
|
||||
status_t _setRunMode(
|
||||
const uint32 runMode,
|
||||
bigtime_t delay=0LL);
|
||||
|
||||
status_t _setRunModeAuto(
|
||||
const uint32 runMode);
|
||||
|
||||
// seek and preroll the given node.
|
||||
// *** this method should not be called from the transport thread
|
||||
// (since preroll operations can block for a relatively long time.)
|
||||
//
|
||||
// returns B_NOT_ALLOWED if the node is running, or if its NO_PREROLL
|
||||
// flag is set; otherwise, returns B_OK on success or a Media Roster
|
||||
// error.
|
||||
|
||||
status_t _preroll(
|
||||
bigtime_t position);
|
||||
|
||||
// seek the given node if possible
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _seek(
|
||||
bigtime_t position,
|
||||
bigtime_t when);
|
||||
|
||||
// seek the given (stopped) node
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _seekStopped(
|
||||
bigtime_t position);
|
||||
|
||||
// start the given node, if possible & necessary, at
|
||||
// the given time
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _start(
|
||||
bigtime_t when);
|
||||
|
||||
// stop the given node (which may or may not still be
|
||||
// a member of this group.)
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _stop();
|
||||
|
||||
// roll the given node, if possible.
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _roll(
|
||||
bigtime_t start,
|
||||
bigtime_t stop,
|
||||
bigtime_t position);
|
||||
|
||||
// refresh the node's current latency; if I reference
|
||||
// a B_RECORDING node, update its 'producer delay'.
|
||||
|
||||
status_t _updateLatency();
|
||||
|
||||
// +++++ this method may not be needed 10aug99 +++++
|
||||
// Figure the earliest time at which the given node can be started.
|
||||
// Also calculates the position at which it should start from to
|
||||
// play in sync with other nodes in the group, if the transport is
|
||||
// running; if stopped, *outPosition will be set to the current
|
||||
// start position.
|
||||
// Pass the estimated amount of time needed to prepare the
|
||||
// node for playback (ie. preroll & a little fudge factor) in
|
||||
// startDelay.
|
||||
//
|
||||
// (this may be called from the transport thread or from
|
||||
// an API-implementation method.)
|
||||
|
||||
status_t _calcStartTime(
|
||||
bigtime_t startDelay,
|
||||
bigtime_t* outTime,
|
||||
bigtime_t* outPosition); //nyi
|
||||
|
||||
// *** Position reporting ***
|
||||
|
||||
// callers: _start(), _roll(), enablePositionReports()
|
||||
status_t _startPositionThread();
|
||||
|
||||
// callers: _stop(), disablePositionReports(), dtor
|
||||
status_t _stopPositionThread();
|
||||
|
||||
// [e.moon 14oct99] handle a report
|
||||
status_t _handlePositionUpdate(
|
||||
bigtime_t perfTime,
|
||||
bigtime_t position);
|
||||
|
||||
// callers: _handlePositionUpdate
|
||||
// (schedules a position update for the given time and position;
|
||||
// if the position overlaps a cycle, adjusts time & position
|
||||
// so that the notification is sent at the cycle point.)
|
||||
status_t _schedulePositionUpdate(
|
||||
bigtime_t when,
|
||||
bigtime_t position);
|
||||
|
||||
// callers: _handlePositionUpdate, _initPositionThread()
|
||||
// Send a message to all position observers
|
||||
status_t _notifyPosition(
|
||||
bigtime_t when,
|
||||
bigtime_t position);
|
||||
|
||||
private: // *** members
|
||||
|
||||
// the node manager
|
||||
NodeManager* m_manager;
|
||||
|
||||
// owning (parent) group; may be 0 if node is not connected.
|
||||
// A connected node always has a group, since groups allow transport
|
||||
// operations. New groups are created as necessary.
|
||||
NodeGroup* m_group;
|
||||
|
||||
// the node's state
|
||||
live_node_info m_info;
|
||||
|
||||
// user-definable transport behavior
|
||||
uint32 m_flags;
|
||||
|
||||
// private/implementation flags
|
||||
|
||||
enum impl_flag_t {
|
||||
// the node was created by NodeManager
|
||||
_INTERNAL = 1<<1,
|
||||
// the node should NOT be released when this instance is destroyed
|
||||
_NO_RELEASE = 1<<2,
|
||||
// notification of the node's instantiation has been received
|
||||
// [e.moon 11oct99]
|
||||
_CREATE_NOTIFIED = 1<<3
|
||||
};
|
||||
|
||||
uint32 m_implFlags;
|
||||
|
||||
// takes BMediaNode::run_mode values or 0 (wildcard:
|
||||
// group run mode used instead)
|
||||
// May not be B_OFFLINE; that must be specified at the group level.
|
||||
|
||||
uint32 m_runMode;
|
||||
|
||||
// only valid if m_runMode is BMediaNode::B_RECORDING
|
||||
bigtime_t m_recordingDelay;
|
||||
|
||||
// Media Roster connection [e.moon 11oct99]
|
||||
bool m_watching;
|
||||
|
||||
// hint information: this info is serialized with the object
|
||||
// to provide 'hints' towards properly finding the same add-on
|
||||
// node later on. If no hint is provided, the node is assumed
|
||||
// to be external to (not created by) the NodeManager.
|
||||
|
||||
struct addon_hint;
|
||||
addon_hint* m_addonHint;
|
||||
|
||||
// * position listening:
|
||||
// - moved from NodeGroup 9aug99
|
||||
|
||||
bool m_positionReportsEnabled;
|
||||
bool m_positionReportsStarted;
|
||||
bigtime_t m_positionUpdatePeriod;
|
||||
static const bigtime_t s_defaultPositionUpdatePeriod = 50000LL;
|
||||
|
||||
MultiInvoker m_positionInvoker;
|
||||
|
||||
bigtime_t m_tpLastPositionUpdate;
|
||||
bigtime_t m_lastPosition;
|
||||
|
||||
// * synchronization threads
|
||||
|
||||
// only active if position listening has been enabled
|
||||
NodeSyncThread* m_positionThread;
|
||||
|
||||
// // only active if this node is cycling (looping)
|
||||
// CycleSyncThread* m_cycleSyncThread;
|
||||
// +++++ 10aug99: moved back to NodeGroup
|
||||
|
||||
private: // *** transport state members
|
||||
|
||||
// is this node running?
|
||||
bool m_running;
|
||||
|
||||
// has the media_node (NOT this object) been released?
|
||||
bool m_nodeReleased;
|
||||
|
||||
// is this node supposed to loop?
|
||||
bool m_cycle;
|
||||
|
||||
// has the node been prepared to start?
|
||||
bool m_prerolled;
|
||||
|
||||
// when was the node started?
|
||||
bigtime_t m_tpStart;
|
||||
|
||||
// if the node has been prerolled, m_tpLastSeek will be 0 but
|
||||
// m_lastSeekPos will reflect the node's prerolled position
|
||||
|
||||
bigtime_t m_tpLastSeek;
|
||||
bigtime_t m_lastSeekPos;
|
||||
|
||||
// has a stop event been queued? [e.moon 11oct99]
|
||||
bigtime_t m_stopQueued;
|
||||
|
||||
// last known latency for this node
|
||||
bigtime_t m_latency;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__NodeRef_H__*/
|
182
src/apps/cortex/NodeManager/NodeSyncThread.cpp
Normal file
182
src/apps/cortex/NodeManager/NodeSyncThread.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
// NodeSyncThread.cpp
|
||||
|
||||
#include "NodeSyncThread.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <Debug.h>
|
||||
#include <MediaRoster.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** dtor/ctors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
NodeSyncThread::~NodeSyncThread() {
|
||||
status_t err;
|
||||
|
||||
// clean up
|
||||
if(m_thread) {
|
||||
err = kill_thread(m_thread);
|
||||
if(err < B_OK)
|
||||
PRINT((
|
||||
"! ~NodeSyncThread(): kill_thread(%ld):\n"
|
||||
" %s\n",
|
||||
m_thread,
|
||||
strerror(err)));
|
||||
|
||||
// +++++ is a wait_for_thread() necessary?
|
||||
}
|
||||
|
||||
if(m_port) {
|
||||
err = delete_port(m_port);
|
||||
if(err < B_OK)
|
||||
PRINT((
|
||||
"! ~NodeSyncThread(): delete_port(%ld):\n"
|
||||
" %s\n",
|
||||
m_port,
|
||||
strerror(err)));
|
||||
}
|
||||
|
||||
if(m_portBuffer)
|
||||
delete [] m_portBuffer;
|
||||
if(m_messenger)
|
||||
delete m_messenger;
|
||||
}
|
||||
|
||||
NodeSyncThread::NodeSyncThread(
|
||||
const media_node& node,
|
||||
BMessenger* messenger) :
|
||||
|
||||
m_node(node),
|
||||
m_messenger(messenger),
|
||||
m_syncInProgress(false),
|
||||
m_thread(0),
|
||||
m_port(0),
|
||||
m_portBuffer(0),
|
||||
m_portBufferSize(sizeof(_sync_op)) {
|
||||
|
||||
ASSERT(m_messenger);
|
||||
|
||||
m_portBuffer = new char[m_portBufferSize];
|
||||
|
||||
m_port = create_port(
|
||||
m_portBufferSize * 16,
|
||||
"NodeSyncThread___port");
|
||||
ASSERT(m_port >= B_OK);
|
||||
|
||||
m_thread = spawn_thread(
|
||||
&_Sync,
|
||||
"NodeSyncThread",
|
||||
B_DISPLAY_PRIORITY,
|
||||
this);
|
||||
ASSERT(m_thread >= B_OK);
|
||||
resume_thread(m_thread);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// trigger a sync operation: when 'perfTime' arrives
|
||||
// for the node, a M_SYNC_COMPLETE message with the given
|
||||
// position value will be sent, unless the sync operation
|
||||
// times out, in which case M_TIMED_OUT will be sent.
|
||||
|
||||
status_t NodeSyncThread::sync(
|
||||
bigtime_t perfTime,
|
||||
bigtime_t position,
|
||||
bigtime_t timeout) {
|
||||
|
||||
status_t err;
|
||||
|
||||
if(m_syncInProgress)
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
_sync_op op = {perfTime, position, timeout};
|
||||
err = write_port(
|
||||
m_port,
|
||||
M_TRIGGER,
|
||||
&op,
|
||||
sizeof(_sync_op));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** guts
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/*static*/
|
||||
status_t NodeSyncThread::_Sync(
|
||||
void* cookie) {
|
||||
((NodeSyncThread*)cookie)->_sync();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// THREAD BODY
|
||||
//
|
||||
void NodeSyncThread::_sync() {
|
||||
ASSERT(m_port >= B_OK);
|
||||
ASSERT(m_messenger);
|
||||
|
||||
bool done = false;
|
||||
while(!done) {
|
||||
|
||||
// WAIT FOR A REQUEST
|
||||
int32 code;
|
||||
ssize_t readCount = read_port(
|
||||
m_port,
|
||||
&code,
|
||||
m_portBuffer,
|
||||
m_portBufferSize);
|
||||
|
||||
if(readCount < B_OK) {
|
||||
PRINT((
|
||||
"! NodeSyncThread::_sync(): read_port():\n"
|
||||
" %s\n",
|
||||
strerror(readCount)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(code != M_TRIGGER) {
|
||||
PRINT((
|
||||
"! NodeSyncThread::sync(): unknown message code %ld\n",
|
||||
code));
|
||||
continue;
|
||||
}
|
||||
|
||||
// SERVICE THE REQUEST
|
||||
const _sync_op& op = *(_sync_op*)m_portBuffer;
|
||||
|
||||
// pre-fill the message
|
||||
BMessage m(M_SYNC_COMPLETE);
|
||||
m.AddInt32("nodeID", m_node.node);
|
||||
m.AddInt64("perfTime", op.targetTime);
|
||||
m.AddInt64("position", op.position);
|
||||
|
||||
// sync
|
||||
status_t err = BMediaRoster::Roster()->SyncToNode(
|
||||
m_node,
|
||||
op.targetTime,
|
||||
op.timeout);
|
||||
|
||||
// deliver reply
|
||||
if(err < B_OK) {
|
||||
m.what = M_SYNC_FAILED;
|
||||
m.AddInt32("error", err);
|
||||
}
|
||||
|
||||
err = m_messenger->SendMessage(&m);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"! NodeSyncThread::_sync(): m_messenger->SendMessage():\n"
|
||||
" %s\n",
|
||||
strerror(err)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// END -- NodeSyncThread.cpp --
|
98
src/apps/cortex/NodeManager/NodeSyncThread.h
Normal file
98
src/apps/cortex/NodeManager/NodeSyncThread.h
Normal file
@ -0,0 +1,98 @@
|
||||
// NodeSyncThread.h [rewrite 14oct99]
|
||||
// * PURPOSE
|
||||
// Provide continuous synchronization notices on
|
||||
// a particular BMediaNode. Notification is sent
|
||||
// to a provided BMessenger.
|
||||
//
|
||||
// As long as a NodeSyncThread object exists its thread
|
||||
// is running. The thread blocks indefinitely, waiting
|
||||
// for a message to show up on an internal port.
|
||||
//
|
||||
// Sync-notice requests (via the +++++ sync() operation)
|
||||
// trigger a message sent to that port. The thread wakes
|
||||
// up, then calls BMediaRoster::SyncToNode() to wait
|
||||
// until a particular performace time arrives for that node.
|
||||
//
|
||||
// If SyncToNode() times out, an M_TIMED_OUT message is sent;
|
||||
// otherwise, an M_SYNC_COMPLETE message is sent.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 14oct99 Rewrite begun.
|
||||
|
||||
#ifndef __NodeSyncThread_H__
|
||||
#define __NodeSyncThread_H__
|
||||
|
||||
#include <MediaNode.h>
|
||||
#include <Messenger.h>
|
||||
#include <OS.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeSyncThread {
|
||||
public: // *** messages
|
||||
enum message_t {
|
||||
// 'nodeID' (int32) media_node_id value
|
||||
// 'perfTime' (int64) the performance time
|
||||
// 'position' (int64) corresponding (caller-provided) position
|
||||
M_SYNC_COMPLETE =NodeSyncThread_message_base,
|
||||
|
||||
// 'nodeID' (int32) media_node_id value
|
||||
// 'error' (int32) status_t value from SyncToNode()
|
||||
// 'perfTime' (int64) the performance time
|
||||
// 'position' (int64) corresponding (caller-provided) position
|
||||
M_SYNC_FAILED
|
||||
};
|
||||
|
||||
public: // *** dtor/ctors
|
||||
virtual ~NodeSyncThread();
|
||||
|
||||
NodeSyncThread(
|
||||
const media_node& node,
|
||||
BMessenger* messenger);
|
||||
|
||||
public: // *** operations
|
||||
// trigger a sync operation: when 'perfTime' arrives
|
||||
// for the node, a M_SYNC_COMPLETE message with the given
|
||||
// position value will be sent, unless the sync operation
|
||||
// times out, in which case M_TIMED_OUT will be sent.
|
||||
status_t sync(
|
||||
bigtime_t perfTime,
|
||||
bigtime_t position,
|
||||
bigtime_t timeout);
|
||||
|
||||
private:
|
||||
|
||||
// the node to watch
|
||||
media_node m_node;
|
||||
|
||||
// the messenger to inform
|
||||
BMessenger* m_messenger;
|
||||
|
||||
// if the thread is running, it has exclusive write access
|
||||
// to this flag.
|
||||
volatile bool m_syncInProgress;
|
||||
|
||||
// thread guts
|
||||
thread_id m_thread;
|
||||
port_id m_port;
|
||||
char* m_portBuffer;
|
||||
ssize_t m_portBufferSize;
|
||||
|
||||
enum _op_codes {
|
||||
M_TRIGGER
|
||||
};
|
||||
|
||||
struct _sync_op {
|
||||
bigtime_t targetTime;
|
||||
bigtime_t position;
|
||||
bigtime_t timeout;
|
||||
};
|
||||
|
||||
static status_t _Sync(
|
||||
void* cookie);
|
||||
void _sync();
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__NodeSyncThread_H__*/
|
62
src/apps/cortex/NodeManager/node_manager_impl.h
Normal file
62
src/apps/cortex/NodeManager/node_manager_impl.h
Normal file
@ -0,0 +1,62 @@
|
||||
// node_manager_impl.h
|
||||
// * PURPOSE
|
||||
// Helper classes & functions used by NodeManager,
|
||||
// NodeGroup, and NodeRef.
|
||||
// * HISTORY
|
||||
// e.moon 10jul99 Begun
|
||||
|
||||
#ifndef __node_manager_impl_H__
|
||||
#define __node_manager_impl_H__
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
#include "ILockable.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
inline void assert_locked(const ILockable* target) {
|
||||
ASSERT(target);
|
||||
ASSERT(target->isLocked());
|
||||
}
|
||||
//
|
||||
//// functor: disconnect Connection
|
||||
//
|
||||
//class disconnectFn { public:
|
||||
// NodeManager* manager;
|
||||
// disconnectFn(NodeManager* _manager) : manager(_manager) {}
|
||||
// void operator()(Connection* c) {
|
||||
// ASSERT(c);
|
||||
// ASSERT(c->isValid());
|
||||
//
|
||||
// status_t err = manager->disconnect(c);
|
||||
//
|
||||
//#if DEBUG
|
||||
// if(err < B_OK)
|
||||
// PRINT((
|
||||
// "* disconnect():\n"
|
||||
// " output %s:%s\n"
|
||||
// " input %s:%s\n"
|
||||
// " error '%s'\n\n",
|
||||
// c->sourceNode()->name(), c->outputName(),
|
||||
// c->destinationNode()->name(), c->inputName(),
|
||||
// strerror(err)));
|
||||
//#endif
|
||||
// }
|
||||
//};
|
||||
//
|
||||
//// functor: release NodeRef or NodeGroup (by pointer)
|
||||
//// +++++ currently no error checking; however, the only error
|
||||
//// release() is allowed to return is that the object has
|
||||
//// already been released.
|
||||
//
|
||||
//template <class T>
|
||||
//class releaseFn { public:
|
||||
// void operator()(T* object) {
|
||||
// ASSERT(object);
|
||||
// object->release();
|
||||
// }
|
||||
//};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__node_manager_impl_H__*/
|
157
src/apps/cortex/ParameterView/ParameterContainerView.cpp
Normal file
157
src/apps/cortex/ParameterView/ParameterContainerView.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
// ParameterContainerView.cpp
|
||||
|
||||
#include "ParameterContainerView.h"
|
||||
|
||||
// Interface Kit
|
||||
#include <ScrollBar.h>
|
||||
#include <View.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ALLOC(x) //PRINT (x)
|
||||
#define D_HOOK(x) //PRINT (x)
|
||||
#define D_INTERNAL(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
ParameterContainerView::ParameterContainerView(
|
||||
BRect dataRect,
|
||||
BView *target) :
|
||||
BView(
|
||||
target->Frame(),
|
||||
"ParameterContainerView",
|
||||
B_FOLLOW_ALL_SIDES,
|
||||
B_FRAME_EVENTS|B_WILL_DRAW),
|
||||
m_target(target),
|
||||
m_dataRect(dataRect),
|
||||
m_boundsRect(Bounds()),
|
||||
m_hScroll(0),
|
||||
m_vScroll(0) {
|
||||
D_ALLOC(("ParameterContainerView::ParameterContainerView()\n"));
|
||||
|
||||
BView* wrapper = new BView(
|
||||
m_target->Bounds(), 0, B_FOLLOW_ALL_SIDES, B_WILL_DRAW|B_FRAME_EVENTS);
|
||||
m_target->SetResizingMode(B_FOLLOW_LEFT|B_FOLLOW_TOP);
|
||||
m_target->MoveTo(B_ORIGIN);
|
||||
wrapper->AddChild(m_target);
|
||||
AddChild(wrapper);
|
||||
|
||||
BRect b = wrapper->Frame();
|
||||
|
||||
ResizeTo(
|
||||
b.Width() + B_V_SCROLL_BAR_WIDTH,
|
||||
b.Height() + B_H_SCROLL_BAR_HEIGHT);
|
||||
|
||||
BRect hsBounds = b;
|
||||
hsBounds.left--;
|
||||
hsBounds.top = hsBounds.bottom + 1;
|
||||
hsBounds.right++;
|
||||
hsBounds.bottom = hsBounds.top + B_H_SCROLL_BAR_HEIGHT + 1;
|
||||
m_hScroll = new BScrollBar(
|
||||
hsBounds,
|
||||
"hScrollBar",
|
||||
m_target,
|
||||
0, 0, B_HORIZONTAL);
|
||||
AddChild(m_hScroll);
|
||||
|
||||
BRect vsBounds = b;
|
||||
vsBounds.left = vsBounds.right + 1;
|
||||
vsBounds.top--;
|
||||
vsBounds.right = vsBounds.right + B_V_SCROLL_BAR_WIDTH + 1;
|
||||
vsBounds.bottom++;
|
||||
m_vScroll = new BScrollBar(
|
||||
vsBounds,
|
||||
"vScrollBar",
|
||||
m_target,
|
||||
0, 0, B_VERTICAL);
|
||||
AddChild(m_vScroll);
|
||||
|
||||
|
||||
SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_1_TINT));
|
||||
_updateScrollBars();
|
||||
}
|
||||
|
||||
ParameterContainerView::~ParameterContainerView() {
|
||||
D_ALLOC(("ParameterContainerView::~ParameterContainerView()\n"));
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BScrollView impl
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ParameterContainerView::FrameResized(
|
||||
float width,
|
||||
float height) {
|
||||
D_HOOK(("ParameterContainerView::FrameResized()\n"));
|
||||
|
||||
BView::FrameResized(width, height);
|
||||
// BRect b = ChildAt(0)->Frame();
|
||||
// printf("param view:\n");
|
||||
// b.PrintToStream();
|
||||
// printf("hScroll:\n");
|
||||
// m_target->ScrollBar(B_HORIZONTAL)->Frame().PrintToStream();
|
||||
// printf("vScroll:\n");
|
||||
// m_target->ScrollBar(B_VERTICAL)->Frame().PrintToStream();
|
||||
// fprintf(stderr, "m: %.1f,%.1f c: %.1f,%.1f)\n", width, height, b.Width(),b.Height());
|
||||
|
||||
if(height > m_boundsRect.Height()) {
|
||||
Invalidate(BRect(
|
||||
m_boundsRect.left, m_boundsRect.bottom, m_boundsRect.right, m_boundsRect.top+height));
|
||||
}
|
||||
|
||||
if(width > m_boundsRect.Width()) {
|
||||
Invalidate(BRect(
|
||||
m_boundsRect.right, m_boundsRect.top, m_boundsRect.left+width, m_boundsRect.bottom));
|
||||
}
|
||||
|
||||
m_boundsRect = Bounds();
|
||||
_updateScrollBars();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ParameterContainerView::_updateScrollBars() {
|
||||
D_INTERNAL(("ParameterContainerView::_updateScrollBars()\n"));
|
||||
|
||||
// fetch the vertical ScrollBar
|
||||
if (m_vScroll) {
|
||||
float height = Bounds().Height() - B_H_SCROLL_BAR_HEIGHT;
|
||||
// Disable the ScrollBar if the window is large enough to display
|
||||
// the entire parameter view
|
||||
D_INTERNAL((" -> dataRect.Height() = %f scrollView.Height() = %f\n", m_dataRect.Height(), height));
|
||||
if (height > m_dataRect.Height()) {
|
||||
D_INTERNAL((" -> disable vertical scroll bar\n"));
|
||||
m_vScroll->SetRange(0.0, 0.0);
|
||||
m_vScroll->SetProportion(1.0);
|
||||
}
|
||||
else {
|
||||
D_INTERNAL((" -> enable vertical scroll bar\n"));
|
||||
m_vScroll->SetRange(m_dataRect.top, m_dataRect.bottom - height);
|
||||
m_vScroll->SetProportion(height / m_dataRect.Height());
|
||||
}
|
||||
}
|
||||
if (m_hScroll) {
|
||||
float width = Bounds().Width() - B_V_SCROLL_BAR_WIDTH;
|
||||
// Disable the ScrollBar if the view is large enough to display
|
||||
// the entire data-rect
|
||||
D_INTERNAL((" -> dataRect.Width() = %f scrollView.Width() = %f\n", m_dataRect.Width(), width));
|
||||
if (width > m_dataRect.Width()) {
|
||||
D_INTERNAL((" -> disable horizontal scroll bar\n"));
|
||||
m_hScroll->SetRange(0.0, 0.0);
|
||||
m_hScroll->SetProportion(1.0);
|
||||
}
|
||||
else {
|
||||
D_INTERNAL((" -> enable horizontal scroll bar\n"));
|
||||
m_hScroll->SetRange(m_dataRect.left, m_dataRect.right - width);
|
||||
m_hScroll->SetProportion(width / m_dataRect.Width());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END -- ParameterContainerView.cpp --
|
56
src/apps/cortex/ParameterView/ParameterContainerView.h
Normal file
56
src/apps/cortex/ParameterView/ParameterContainerView.h
Normal file
@ -0,0 +1,56 @@
|
||||
// ParameterContainerView.h (Cortex/ParameterWindow)
|
||||
//
|
||||
// * PURPOSE
|
||||
//
|
||||
// * TODO
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 16feb2000 Begun
|
||||
//
|
||||
|
||||
#ifndef __ParameterContainerView_H__
|
||||
#define __ParameterContainerView_H__
|
||||
|
||||
// Interface Kit
|
||||
#include <View.h>
|
||||
// Support Kit
|
||||
#include <String.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
|
||||
class BScrollBar;
|
||||
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class ParameterContainerView :
|
||||
public BView {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
ParameterContainerView(
|
||||
BRect dataRect,
|
||||
BView *target);
|
||||
|
||||
virtual ~ParameterContainerView();
|
||||
|
||||
public: // *** BScrollView impl.
|
||||
|
||||
virtual void FrameResized(
|
||||
float width,
|
||||
float height);
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
void _updateScrollBars();
|
||||
|
||||
private: // *** data members
|
||||
|
||||
BView* m_target;
|
||||
BRect m_dataRect;
|
||||
BRect m_boundsRect;
|
||||
BScrollBar* m_hScroll;
|
||||
BScrollBar* m_vScroll;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __ParameterContainerView_H__ */
|
356
src/apps/cortex/ParameterView/ParameterWindow.cpp
Normal file
356
src/apps/cortex/ParameterView/ParameterWindow.cpp
Normal file
@ -0,0 +1,356 @@
|
||||
// ParameterWindow.cpp
|
||||
|
||||
#include "ParameterWindow.h"
|
||||
// ParameterWindow
|
||||
#include "ParameterContainerView.h"
|
||||
|
||||
// Application Kit
|
||||
#include <Message.h>
|
||||
#include <Messenger.h>
|
||||
// Interface Kit
|
||||
#include <Alert.h>
|
||||
#include <Menu.h>
|
||||
#include <MenuBar.h>
|
||||
#include <MenuItem.h>
|
||||
#include <Screen.h>
|
||||
#include <ScrollBar.h>
|
||||
// Media Kit
|
||||
#include <MediaRoster.h>
|
||||
#include <MediaTheme.h>
|
||||
#include <ParameterWeb.h>
|
||||
// Storage Kit
|
||||
#include <FilePanel.h>
|
||||
#include <Path.h>
|
||||
// Support Kit
|
||||
#include <String.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ALLOC(x) //PRINT (x)
|
||||
#define D_HOOK(x) //PRINT (x)
|
||||
#define D_INTERNAL(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
ParameterWindow::ParameterWindow(
|
||||
BPoint position,
|
||||
live_node_info &nodeInfo,
|
||||
BMessenger *notifyTarget)
|
||||
: BWindow(BRect(position, position + BPoint(50.0, 50.0)),
|
||||
"parameters", B_DOCUMENT_WINDOW,
|
||||
B_WILL_ACCEPT_FIRST_CLICK | B_ASYNCHRONOUS_CONTROLS),
|
||||
m_node(nodeInfo.node),
|
||||
m_parameters(0),
|
||||
m_notifyTarget(0),
|
||||
m_zoomed(false),
|
||||
m_zooming(false) {
|
||||
D_ALLOC(("ParameterWindow::ParameterWindow()\n"));
|
||||
|
||||
// add the nodes name to the title
|
||||
{
|
||||
char* title = new char[strlen(nodeInfo.name) + strlen(" parameters") + 1];
|
||||
sprintf(title, "%s parameters", nodeInfo.name);
|
||||
SetTitle(title);
|
||||
delete [] title;
|
||||
}
|
||||
// add the menu bar
|
||||
BMenuBar *menuBar = new BMenuBar(Bounds(), "ParameterWindow MenuBar");
|
||||
|
||||
BMenu *menu = new BMenu("Window");
|
||||
menu->AddItem(new BMenuItem("Start Control Panel",
|
||||
new BMessage(M_START_CONTROL_PANEL),
|
||||
'P', B_COMMAND_KEY | B_SHIFT_KEY));
|
||||
menu->AddSeparatorItem();
|
||||
menu->AddItem(new BMenuItem("Close",
|
||||
new BMessage(B_QUIT_REQUESTED),
|
||||
'W', B_COMMAND_KEY));
|
||||
menuBar->AddItem(menu);
|
||||
|
||||
// future Media Theme selection capabilities go here
|
||||
menu = new BMenu("Themes");
|
||||
BMessage *message = new BMessage(M_THEME_SELECTED);
|
||||
BMediaTheme *theme = BMediaTheme::PreferredTheme();
|
||||
message->AddInt32("themeID", theme->ID());
|
||||
BMenuItem *item = new BMenuItem(theme->Name(), message);
|
||||
item->SetMarked(true);
|
||||
menu->AddItem(item);
|
||||
menuBar->AddItem(menu);
|
||||
AddChild(menuBar);
|
||||
|
||||
_updateParameterView();
|
||||
_init();
|
||||
|
||||
// start watching for parameter web changes
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
roster->StartWatching(this, nodeInfo.node, B_MEDIA_WILDCARD);
|
||||
|
||||
if (notifyTarget) {
|
||||
m_notifyTarget = new BMessenger(*notifyTarget);
|
||||
}
|
||||
}
|
||||
|
||||
ParameterWindow::~ParameterWindow() {
|
||||
D_ALLOC(("ParameterWindow::~ParameterWindow()\n"));
|
||||
|
||||
if (m_notifyTarget) {
|
||||
delete m_notifyTarget;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// BWindow impl
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ParameterWindow::FrameResized(
|
||||
float width,
|
||||
float height) {
|
||||
D_HOOK(("ParameterWindow::FrameResized()\n"));
|
||||
|
||||
if (!m_zooming) {
|
||||
m_zoomed = false;
|
||||
}
|
||||
else {
|
||||
m_zooming = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterWindow::MessageReceived(
|
||||
BMessage *message) {
|
||||
D_MESSAGE(("ParameterWindow::MessageReceived()\n"));
|
||||
|
||||
switch (message->what) {
|
||||
case M_START_CONTROL_PANEL: {
|
||||
D_MESSAGE((" -> M_START_CONTROL_PANEL\n"));
|
||||
status_t error = _startControlPanel();
|
||||
if (error) {
|
||||
BString s = "Could not start control panel";
|
||||
s << " (" << strerror(error) << ")";
|
||||
BAlert *alert = new BAlert("", s.String(), "OK", 0, 0,
|
||||
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
||||
alert->Go(0);
|
||||
}
|
||||
bool replace = false;
|
||||
if ((message->FindBool("replace", &replace) == B_OK)
|
||||
&& (replace == true)) {
|
||||
PostMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case M_THEME_SELECTED: {
|
||||
D_MESSAGE((" -> M_THEME_SELECTED\n"));
|
||||
int32 themeID;
|
||||
if (message->FindInt32("themeID", &themeID) != B_OK) {
|
||||
return;
|
||||
}
|
||||
// not yet implemented
|
||||
break;
|
||||
}
|
||||
case B_MEDIA_WEB_CHANGED: {
|
||||
D_MESSAGE((" -> B_MEDIA_WEB_CHANGED\n"));
|
||||
_updateParameterView();
|
||||
_constrainToScreen();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BWindow::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ParameterWindow::QuitRequested() {
|
||||
D_HOOK(("ParameterWindow::QuitRequested()\n"));
|
||||
|
||||
// stop watching the MediaRoster
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (roster) {
|
||||
roster->StopWatching(this, m_node, B_MEDIA_WILDCARD);
|
||||
}
|
||||
|
||||
// tell the notification target to forget about us
|
||||
if (m_notifyTarget && m_notifyTarget->IsValid()) {
|
||||
BMessage message(M_CLOSED);
|
||||
message.AddInt32("nodeID", m_node.node);
|
||||
status_t error = m_notifyTarget->SendMessage(&message);
|
||||
if (error) {
|
||||
D_HOOK((" -> error sending message (%s) !\n", strerror(error)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ParameterWindow::Zoom(
|
||||
BPoint origin,
|
||||
float width,
|
||||
float height) {
|
||||
D_HOOK(("ParameterWindow::Zoom()\n"));
|
||||
|
||||
m_zooming = true;
|
||||
|
||||
BScreen screen(this);
|
||||
if (!screen.Frame().Contains(Frame())) {
|
||||
m_zoomed = false;
|
||||
}
|
||||
|
||||
if (!m_zoomed) {
|
||||
// resize to the ideal size
|
||||
m_manualSize = Bounds();
|
||||
ResizeTo(m_idealSize.Width(), m_idealSize.Height());
|
||||
_constrainToScreen();
|
||||
m_zoomed = true;
|
||||
}
|
||||
else {
|
||||
// resize to the most recent manual size
|
||||
ResizeTo(m_manualSize.Width(), m_manualSize.Height());
|
||||
m_zoomed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ParameterWindow::_init() {
|
||||
D_INTERNAL(("ParameterWindow::_init()\n"));
|
||||
|
||||
// offset to a new position
|
||||
_constrainToScreen();
|
||||
m_manualSize = Bounds().OffsetToCopy(0.0, 0.0);
|
||||
|
||||
// add the hidden option to close this window when the
|
||||
// control panel has been started successfully
|
||||
BMessage *message = new BMessage(M_START_CONTROL_PANEL);
|
||||
message->AddBool("replace", true);
|
||||
AddShortcut('P', B_COMMAND_KEY | B_SHIFT_KEY | B_OPTION_KEY,
|
||||
message);
|
||||
}
|
||||
|
||||
void ParameterWindow::_updateParameterView(
|
||||
BMediaTheme *theme) {
|
||||
D_INTERNAL(("ParameterWindow::_updateParameterView()\n"));
|
||||
|
||||
// clear the old version
|
||||
if (m_parameters) {
|
||||
ParameterContainerView *view = dynamic_cast<ParameterContainerView *>(FindView("ParameterContainerView"));
|
||||
RemoveChild(view);
|
||||
delete m_parameters;
|
||||
m_parameters = 0;
|
||||
delete view;
|
||||
}
|
||||
|
||||
// fetch ParameterWeb from the MediaRoster
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (roster) {
|
||||
BParameterWeb *web;
|
||||
status_t error = roster->GetParameterWebFor(m_node, &web);
|
||||
if (!error && (web->CountParameters() || web->CountGroups())) {
|
||||
// if no theme was specified, use the preferred theme
|
||||
if (!theme) {
|
||||
theme = BMediaTheme::PreferredTheme();
|
||||
}
|
||||
// acquire the view
|
||||
m_parameters = BMediaTheme::ViewFor(web, 0, theme);
|
||||
if (m_parameters) {
|
||||
BMenuBar *menuBar = KeyMenuBar();
|
||||
m_idealSize = m_parameters->Bounds();
|
||||
m_idealSize.right += B_V_SCROLL_BAR_WIDTH;
|
||||
m_idealSize.bottom += B_H_SCROLL_BAR_HEIGHT;
|
||||
if (menuBar) {
|
||||
m_parameters->MoveTo(0.0, menuBar->Bounds().bottom + 1.0);
|
||||
m_idealSize.bottom += menuBar->Bounds().bottom + 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// limit min size to avoid funny-looking scrollbars
|
||||
float hMin = B_V_SCROLL_BAR_WIDTH*6, vMin = B_H_SCROLL_BAR_HEIGHT*3;
|
||||
// limit max size to full extents of the parameter view
|
||||
float hMax = m_idealSize.Width(), vMax = m_idealSize.Height();
|
||||
SetSizeLimits(hMin, hMax, vMin, vMax);
|
||||
|
||||
// adapt the window to the new dimensions
|
||||
ResizeTo(m_idealSize.Width(), m_idealSize.Height());
|
||||
m_zoomed = true;
|
||||
|
||||
if (m_parameters) {
|
||||
BRect paramRect = m_parameters->Bounds();
|
||||
AddChild(new ParameterContainerView(paramRect, m_parameters));
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterWindow::_constrainToScreen() {
|
||||
D_INTERNAL(("ParameterWindow::_constrainToScreen()\n"));
|
||||
|
||||
BScreen screen(this);
|
||||
BRect screenRect = screen.Frame();
|
||||
BRect windowRect = Frame();
|
||||
|
||||
// if the window is outside the screen rect
|
||||
// move it to the default position
|
||||
if (!screenRect.Intersects(windowRect)) {
|
||||
windowRect.OffsetTo(screenRect.LeftTop());
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
|
||||
// if the window is larger than the screen rect
|
||||
// resize it to fit at each side
|
||||
if (!screenRect.Contains(windowRect)) {
|
||||
if (windowRect.left < screenRect.left) {
|
||||
windowRect.left = screenRect.left + 5.0;
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
if (windowRect.top < screenRect.top) {
|
||||
windowRect.top = screenRect.top + 5.0;
|
||||
MoveTo(windowRect.LeftTop());
|
||||
windowRect = Frame();
|
||||
}
|
||||
if (windowRect.right > screenRect.right) {
|
||||
windowRect.right = screenRect.right - 5.0;
|
||||
}
|
||||
if (windowRect.bottom > screenRect.bottom) {
|
||||
windowRect.bottom = screenRect.bottom - 5.0;
|
||||
}
|
||||
ResizeTo(windowRect.Width(), windowRect.Height());
|
||||
}
|
||||
}
|
||||
|
||||
status_t ParameterWindow::_startControlPanel() {
|
||||
D_INTERNAL(("ParameterWindow::_startControlPanel()\n"));
|
||||
|
||||
// get roster instance
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (!roster) {
|
||||
D_INTERNAL((" -> MediaRoster not available\n"));
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// try to StartControlPanel()
|
||||
BMessenger messenger;
|
||||
status_t error = roster->StartControlPanel(m_node, &messenger);
|
||||
if (error) {
|
||||
D_INTERNAL((" -> StartControlPanel() failed (%s)\n", strerror(error)));
|
||||
return error;
|
||||
}
|
||||
|
||||
// notify the notification target, if any
|
||||
if (m_notifyTarget) {
|
||||
BMessage message(M_CONTROL_PANEL_STARTED);
|
||||
message.AddInt32("nodeID", m_node.node);
|
||||
message.AddMessenger("messenger", messenger);
|
||||
error = m_notifyTarget->SendMessage(&message);
|
||||
if (error) {
|
||||
D_INTERNAL((" -> failed to notify target\n"));
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// END -- ParameterWindow.cpp --
|
130
src/apps/cortex/ParameterView/ParameterWindow.h
Normal file
130
src/apps/cortex/ParameterView/ParameterWindow.h
Normal file
@ -0,0 +1,130 @@
|
||||
// ParameterWindow.h (Cortex/ParameterWindow)
|
||||
//
|
||||
// * PURPOSE
|
||||
// Window subclass to contain BMediaTheme generated 'parameter
|
||||
// views' and offers elegant resizing/zooming behaviour.
|
||||
//
|
||||
// The ParameterWindow currently listens to the MediaRoster for
|
||||
// changes of the parameter web, but this should change when the
|
||||
// provided BMediaTheme becomes more 'intelligent'.
|
||||
//
|
||||
// Support for selecting alternate themes is planned, but not
|
||||
// yet finished (see the 'Themes' menu)
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 24nov99 Begun
|
||||
// c.lenz 17feb00 Added scrollbar support, migrated the
|
||||
// basic management functionality to new
|
||||
// class ParameterWindowManager
|
||||
//
|
||||
|
||||
#ifndef __ParameterWindow_H__
|
||||
#define __ParameterWindow_H__
|
||||
|
||||
// Interface Kit
|
||||
#include <Window.h>
|
||||
// Media Kit
|
||||
#include <MediaNode.h>
|
||||
|
||||
class BMessenger;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeRef;
|
||||
|
||||
class ParameterWindow :
|
||||
public BWindow {
|
||||
|
||||
public: // *** messages
|
||||
|
||||
enum message_t {
|
||||
|
||||
// OUTBOUND
|
||||
// fields:
|
||||
// B_INT32_TYPE "nodeID
|
||||
M_CLOSED = ParameterWindow_message_base,
|
||||
|
||||
// OUTBOUND
|
||||
// fields:
|
||||
// B_INT32_TYPE "nodeID"
|
||||
// B_MESSENGER_TYPE "messenger"
|
||||
M_CONTROL_PANEL_STARTED,
|
||||
|
||||
// INBOUND
|
||||
// fields:
|
||||
// B_BOOL_TYPE "replace"
|
||||
M_START_CONTROL_PANEL,
|
||||
|
||||
// INBOUND
|
||||
// fields:
|
||||
// B_INT32_TYPE "themeID"
|
||||
M_THEME_SELECTED
|
||||
};
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
ParameterWindow(
|
||||
BPoint position,
|
||||
live_node_info &nodeInfo,
|
||||
BMessenger *notifyTarget = 0);
|
||||
|
||||
virtual ~ParameterWindow();
|
||||
|
||||
public: // *** BWindow impl
|
||||
|
||||
// remember that frame was changed manually
|
||||
virtual void FrameResized(
|
||||
float width,
|
||||
float height);
|
||||
|
||||
// closes the window when the node is released
|
||||
virtual void MessageReceived(
|
||||
BMessage *message);
|
||||
|
||||
// stop watching the MediaRoster, tell the notifyTarget
|
||||
virtual bool QuitRequested();
|
||||
|
||||
// extend basic Zoom() functionality to 'minimize' the
|
||||
// window when currently at max size
|
||||
virtual void Zoom(
|
||||
BPoint origin,
|
||||
float width,
|
||||
float height);
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// is called from the ctor for window-positioning
|
||||
void _init();
|
||||
|
||||
// adds or updates the parameter view using the given
|
||||
// theme
|
||||
void _updateParameterView(
|
||||
BMediaTheme *theme = 0);
|
||||
|
||||
// resizes the window to fit in the current screen rect
|
||||
void _constrainToScreen();
|
||||
|
||||
// tries to start the nodes control panel through the
|
||||
// MediaRoster
|
||||
status_t _startControlPanel();
|
||||
|
||||
private: // *** data members
|
||||
|
||||
media_node m_node;
|
||||
|
||||
BView *m_parameters;
|
||||
|
||||
BMessenger *m_notifyTarget;
|
||||
|
||||
BRect m_manualSize;
|
||||
|
||||
BRect m_idealSize;
|
||||
|
||||
bool m_zoomed;
|
||||
|
||||
bool m_zooming;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /* __ParameterWindow_H__ */
|
397
src/apps/cortex/ParameterView/ParameterWindowManager.cpp
Normal file
397
src/apps/cortex/ParameterView/ParameterWindowManager.cpp
Normal file
@ -0,0 +1,397 @@
|
||||
// ParameterWindowManager.cpp
|
||||
|
||||
#include "ParameterWindowManager.h"
|
||||
#include "ParameterWindow.h"
|
||||
// NodeManager
|
||||
#include "NodeRef.h"
|
||||
|
||||
// Application Kit
|
||||
#include <AppDefs.h>
|
||||
// Media Kit
|
||||
#include <MediaRoster.h>
|
||||
// Support Kit
|
||||
#include <List.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
#include <Debug.h>
|
||||
#define D_ACCESS(x) //PRINT (x)
|
||||
#define D_ALLOC(x) //PRINT (x)
|
||||
#define D_INTERNAL(x) //PRINT (x)
|
||||
#define D_MESSAGE(x) //PRINT (x)
|
||||
#define D_WINDOW(x) //PRINT (x)
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal types
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// used to remember the list of ParameterWindows
|
||||
struct window_map_entry {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
window_map_entry(
|
||||
const NodeRef *ref,
|
||||
BWindow *window)
|
||||
: ref(ref),
|
||||
window(window)
|
||||
{ }
|
||||
|
||||
public: // *** data members
|
||||
|
||||
const NodeRef *ref;
|
||||
|
||||
BWindow *window;
|
||||
};
|
||||
|
||||
// used to remember the list of ControlPanels
|
||||
struct panel_map_entry {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
|
||||
panel_map_entry(
|
||||
int32 id,
|
||||
const BMessenger &messenger)
|
||||
: id(id),
|
||||
messenger(messenger)
|
||||
{ }
|
||||
|
||||
public: // *** data members
|
||||
|
||||
int32 id;
|
||||
|
||||
const BMessenger messenger;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// static member init
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const BPoint ParameterWindowManager::M_DEFAULT_OFFSET = BPoint(20.0, 20.0);
|
||||
const BPoint ParameterWindowManager::M_INIT_POSITION = BPoint(90.0, 90.0);
|
||||
|
||||
ParameterWindowManager *ParameterWindowManager::s_instance = 0;
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/* hidden */
|
||||
ParameterWindowManager::ParameterWindowManager()
|
||||
: BLooper("ParameterWindowManager",
|
||||
B_NORMAL_PRIORITY),
|
||||
m_windowList(0),
|
||||
m_panelList(0),
|
||||
m_lastWindowPosition(M_INIT_POSITION - M_DEFAULT_OFFSET) {
|
||||
D_ALLOC(("ParameterWindowManager::ParameterWindowManager()\n"));
|
||||
|
||||
m_windowList = new BList();
|
||||
m_panelList = new BList();
|
||||
Run();
|
||||
}
|
||||
|
||||
ParameterWindowManager::~ParameterWindowManager() {
|
||||
D_ALLOC(("ParameterWindowManager::~ParameterWindowManager()\n"));
|
||||
|
||||
while (m_windowList->CountItems() > 0) {
|
||||
window_map_entry *entry = static_cast<window_map_entry *>
|
||||
(m_windowList->ItemAt(0));
|
||||
if (entry && entry->window) {
|
||||
remove_observer(this, entry->ref);
|
||||
entry->window->Lock();
|
||||
entry->window->Quit();
|
||||
}
|
||||
m_windowList->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
delete m_windowList;
|
||||
|
||||
while (m_panelList->CountItems() > 0) {
|
||||
panel_map_entry *entry = static_cast<panel_map_entry *>
|
||||
(m_panelList->ItemAt(0));
|
||||
if (entry && entry->messenger.IsValid()) {
|
||||
entry->messenger.SendMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
m_panelList->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
delete m_panelList;
|
||||
|
||||
s_instance = 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** singleton access
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/*static*/
|
||||
ParameterWindowManager *ParameterWindowManager::Instance() {
|
||||
D_ACCESS(("ParameterWindowManager::Instance()\n"));
|
||||
|
||||
if (!s_instance) {
|
||||
D_ACCESS((" -> create instance\n"));
|
||||
s_instance = new ParameterWindowManager();
|
||||
}
|
||||
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ParameterWindowManager::shutDown() {
|
||||
D_WINDOW(("ParameterWindowManager::shutDown()\n"));
|
||||
|
||||
if (s_instance) {
|
||||
s_instance->Lock();
|
||||
s_instance->Quit();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
status_t ParameterWindowManager::openWindowFor(
|
||||
const NodeRef *ref) {
|
||||
D_WINDOW(("ParameterWindowManager::openWindowFor()\n"));
|
||||
|
||||
// make absolutely sure we're locked
|
||||
if (!IsLocked()) {
|
||||
debugger("The looper must be locked !");
|
||||
}
|
||||
|
||||
// make sure the ref is valid
|
||||
if (!ref) {
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
BWindow *window = 0;
|
||||
if (_findWindowFor(ref->id(), &window)) {
|
||||
// window for this node already exists, activate it
|
||||
window->SetWorkspaces(B_CURRENT_WORKSPACE);
|
||||
window->Activate();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
BMessenger messenger(0, this);
|
||||
live_node_info nodeInfo = ref->nodeInfo();
|
||||
m_lastWindowPosition += M_DEFAULT_OFFSET;
|
||||
window = new ParameterWindow(m_lastWindowPosition,
|
||||
nodeInfo, &messenger);
|
||||
if (_addWindowFor(ref, window)) {
|
||||
window->Show();
|
||||
return B_OK;
|
||||
}
|
||||
delete window;
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t ParameterWindowManager::startControlPanelFor(
|
||||
const NodeRef *ref) {
|
||||
D_WINDOW(("ParameterWindowManager::startControlPanelFor()\n"));
|
||||
|
||||
// make absolutely sure we're locked
|
||||
if (!IsLocked()) {
|
||||
debugger("The looper must be locked !");
|
||||
}
|
||||
|
||||
BMediaRoster *roster = BMediaRoster::CurrentRoster();
|
||||
if (!roster) {
|
||||
D_WINDOW((" -> MediaRoster not available\n"));
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
BMessenger messenger;
|
||||
if (_findPanelFor(ref->id(), &messenger)) {
|
||||
// find out if the messengers target still exists
|
||||
if (messenger.IsValid()) {
|
||||
return B_OK;
|
||||
}
|
||||
else {
|
||||
_removePanelFor(ref->id());
|
||||
}
|
||||
}
|
||||
|
||||
status_t error = roster->StartControlPanel(ref->node(),
|
||||
&messenger);
|
||||
if (error) {
|
||||
D_INTERNAL((" -> StartControlPanel() failed (%s)\n",
|
||||
strerror(error)));
|
||||
return error;
|
||||
}
|
||||
|
||||
_addPanelFor(ref->id(), messenger);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** BLooper impl
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ParameterWindowManager::MessageReceived(
|
||||
BMessage *message) {
|
||||
D_MESSAGE(("ParameterWindowManager::MessageReceived()\n"));
|
||||
|
||||
switch (message->what) {
|
||||
case ParameterWindow::M_CLOSED: {
|
||||
D_MESSAGE((" -> ParameterWindow::M_CLOSED\n"));
|
||||
int32 nodeID;
|
||||
if (message->FindInt32("nodeID", &nodeID) != B_OK) {
|
||||
return;
|
||||
}
|
||||
_removeWindowFor(nodeID);
|
||||
break;
|
||||
}
|
||||
case ParameterWindow::M_CONTROL_PANEL_STARTED: {
|
||||
D_MESSAGE((" -> ParameterWindow::M_CONTROL_PANEL_STARTED\n"));
|
||||
int32 nodeID;
|
||||
if (message->FindInt32("nodeID", &nodeID) != B_OK) {
|
||||
return;
|
||||
}
|
||||
BMessenger messenger;
|
||||
if (message->FindMessenger("messenger", &messenger) != B_OK) {
|
||||
return;
|
||||
}
|
||||
_addPanelFor(nodeID, messenger);
|
||||
break;
|
||||
}
|
||||
case NodeRef::M_RELEASED: {
|
||||
D_MESSAGE((" -> NodeRef::M_RELEASED\n"));
|
||||
int32 nodeID;
|
||||
if (message->FindInt32("nodeID", &nodeID) != B_OK) {
|
||||
return;
|
||||
}
|
||||
BWindow *window;
|
||||
if (_findWindowFor(nodeID, &window)) {
|
||||
window->Lock();
|
||||
window->Quit();
|
||||
_removeWindowFor(nodeID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLooper::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool ParameterWindowManager::_addWindowFor(
|
||||
const NodeRef *ref,
|
||||
BWindow *window) {
|
||||
D_INTERNAL(("ParameterWindowManager::_addWindowFor()\n"));
|
||||
|
||||
window_map_entry *entry = new window_map_entry(ref,
|
||||
window);
|
||||
if (m_windowList->AddItem(reinterpret_cast<void *>(entry))) {
|
||||
add_observer(this, entry->ref);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParameterWindowManager::_findWindowFor(
|
||||
int32 id,
|
||||
BWindow **outWindow) {
|
||||
D_INTERNAL(("ParameterWindowManager::_findWindowFor()\n"));
|
||||
|
||||
for (int32 i = 0; i < m_windowList->CountItems(); i++) {
|
||||
window_map_entry *entry = static_cast<window_map_entry *>
|
||||
(m_windowList->ItemAt(i));
|
||||
if (entry->ref->id() == id) {
|
||||
*outWindow = entry->window;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ParameterWindowManager::_removeWindowFor(
|
||||
int32 id) {
|
||||
D_INTERNAL(("ParameterWindowManager::_removeWindowFor()\n"));
|
||||
|
||||
for (int32 i = 0; i < m_windowList->CountItems(); i++) {
|
||||
window_map_entry *entry = static_cast<window_map_entry *>
|
||||
(m_windowList->ItemAt(i));
|
||||
if (entry->ref->id() == id) {
|
||||
m_windowList->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
remove_observer(this, entry->ref);
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
// try to shutdown
|
||||
if (m_windowList->CountItems() == 0) {
|
||||
int32 i = 0;
|
||||
while (true) {
|
||||
// take all invalid messengers out of the panel list
|
||||
panel_map_entry *entry = static_cast<panel_map_entry *>
|
||||
(m_panelList->ItemAt(i));
|
||||
if (!entry) {
|
||||
// end of list
|
||||
break;
|
||||
}
|
||||
if (!entry->messenger.IsValid()) {
|
||||
// this control panel doesn't exist anymore
|
||||
m_panelList->RemoveItem(entry);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (m_panelList->CountItems() == 0) {
|
||||
// neither windows nor panels to manage, go to sleep
|
||||
PostMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ParameterWindowManager::_addPanelFor(
|
||||
int32 id,
|
||||
const BMessenger &messenger) {
|
||||
D_INTERNAL(("ParameterWindowManager::_addPanelFor()\n"));
|
||||
|
||||
panel_map_entry *entry = new panel_map_entry(id,
|
||||
messenger);
|
||||
if (m_panelList->AddItem(reinterpret_cast<void *>(entry))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParameterWindowManager::_findPanelFor(
|
||||
int32 id,
|
||||
BMessenger *outMessenger) {
|
||||
D_INTERNAL(("ParameterWindowManager::_findPanelFor()\n"));
|
||||
|
||||
for (int32 i = 0; i < m_panelList->CountItems(); i++) {
|
||||
panel_map_entry *entry = static_cast<panel_map_entry *>
|
||||
(m_panelList->ItemAt(i));
|
||||
if (entry->id == id) {
|
||||
*outMessenger = entry->messenger;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ParameterWindowManager::_removePanelFor(
|
||||
int32 id) {
|
||||
D_INTERNAL(("ParameterWindowManager::_removeWindowFor()\n"));
|
||||
|
||||
for (int32 i = 0; i < m_panelList->CountItems(); i++) {
|
||||
panel_map_entry *entry = static_cast<panel_map_entry *>
|
||||
(m_panelList->ItemAt(i));
|
||||
if (entry->id == id) {
|
||||
m_panelList->RemoveItem(reinterpret_cast<void *>(entry));
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// END -- ParameterWindowManager.cpp --
|
120
src/apps/cortex/ParameterView/ParameterWindowManager.h
Normal file
120
src/apps/cortex/ParameterView/ParameterWindowManager.h
Normal file
@ -0,0 +1,120 @@
|
||||
// ParameterWindowManager.h
|
||||
//
|
||||
// * PURPOSE
|
||||
// Manages all the ParameterWindows and control panels.
|
||||
// Will not let you open multiple windows referring to
|
||||
// the same node, and takes care of quitting them all
|
||||
// when shut down.
|
||||
//
|
||||
// * HISTORY
|
||||
// c.lenz 17feb2000 Begun
|
||||
//
|
||||
|
||||
#ifndef __ParameterWindowManager_H__
|
||||
#define __ParameterWindowManager_H__
|
||||
|
||||
#include <Looper.h>
|
||||
#include <Point.h>
|
||||
|
||||
class BList;
|
||||
class BWindow;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeRef;
|
||||
|
||||
class ParameterWindowManager :
|
||||
public BLooper {
|
||||
|
||||
public: // *** constants
|
||||
|
||||
// the screen position where the first window should
|
||||
// be displayed
|
||||
static const BPoint M_INIT_POSITION;
|
||||
|
||||
// horizontal/vertical offset by which subsequent
|
||||
// parameter windows positions are shifted
|
||||
static const BPoint M_DEFAULT_OFFSET;
|
||||
|
||||
private: // *** ctor/dtor
|
||||
|
||||
// hidden ctor; is called only from inside Instance()
|
||||
ParameterWindowManager();
|
||||
|
||||
public:
|
||||
|
||||
// quits all registered parameter windows and control
|
||||
// panels
|
||||
virtual ~ParameterWindowManager();
|
||||
|
||||
public: // *** singleton access
|
||||
|
||||
// access to the one and only instance of this class
|
||||
static ParameterWindowManager *Instance();
|
||||
|
||||
// will delete the singleton instance and take down all
|
||||
// open windows
|
||||
static void shutDown();
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// request a ParameterWindow to be openened for the given
|
||||
// NodeRef; registeres the window
|
||||
status_t openWindowFor(
|
||||
const NodeRef *ref);
|
||||
|
||||
// request to call StartControlPanel() for the given
|
||||
// NodeRef; registeres the panels messenger
|
||||
status_t startControlPanelFor(
|
||||
const NodeRef *ref);
|
||||
|
||||
public: // *** BLooper impl
|
||||
|
||||
virtual void MessageReceived(
|
||||
BMessage *message);
|
||||
|
||||
private: // *** internal operations
|
||||
|
||||
// ParameterWindow management
|
||||
bool _addWindowFor(
|
||||
const NodeRef *ref,
|
||||
BWindow *window);
|
||||
bool _findWindowFor(
|
||||
int32 nodeID,
|
||||
BWindow **outWindow);
|
||||
void _removeWindowFor(
|
||||
int32 nodeID);
|
||||
|
||||
// ControlPanel management
|
||||
bool _addPanelFor(
|
||||
int32 id,
|
||||
const BMessenger &messenger);
|
||||
bool _findPanelFor(
|
||||
int32 nodeID,
|
||||
BMessenger *outMessenger);
|
||||
void _removePanelFor(
|
||||
int32 nodeID);
|
||||
|
||||
private: // *** data members
|
||||
|
||||
// a list of window_map_entry objects, ie parameter windows
|
||||
// identified by the node_id
|
||||
BList *m_windowList;
|
||||
|
||||
// a list of window_map_entry objects, this time for
|
||||
// node control panels
|
||||
BList *m_panelList;
|
||||
|
||||
// the BPoint at which the last InfoWindow was initially
|
||||
// opened
|
||||
BPoint m_lastWindowPosition;
|
||||
|
||||
private: // *** static members
|
||||
|
||||
// the magic singleton instance
|
||||
static ParameterWindowManager *s_instance;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__ParameterWindowManager_H__*/
|
343
src/apps/cortex/Persistence/ExportContext.cpp
Normal file
343
src/apps/cortex/Persistence/ExportContext.cpp
Normal file
@ -0,0 +1,343 @@
|
||||
// ExportContext.cpp
|
||||
// e.moon 30jun99
|
||||
|
||||
#include "ExportContext.h"
|
||||
#include "IPersistent.h"
|
||||
|
||||
#include <DataIO.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
ExportContext::~ExportContext() {}
|
||||
|
||||
ExportContext::ExportContext() :
|
||||
|
||||
stream(0),
|
||||
m_indentLevel(0),
|
||||
m_indentIncrement(4),
|
||||
m_attrColumn(30),
|
||||
m_state(INIT) {}
|
||||
|
||||
ExportContext::ExportContext(
|
||||
BDataIO* _stream) :
|
||||
|
||||
stream(_stream),
|
||||
m_indentLevel(0),
|
||||
m_indentIncrement(2),
|
||||
m_attrColumn(30),
|
||||
m_state(INIT) {
|
||||
|
||||
ASSERT(_stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** XML formatting helpers
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// writes a start tag. should only be called from
|
||||
// IPersistent::xmlExportBegin().
|
||||
void ExportContext::beginElement(
|
||||
const char* name) {
|
||||
|
||||
ASSERT(name);
|
||||
|
||||
if(!m_objectStack.size()) {
|
||||
reportError("beginElement(): no object being written.\n");
|
||||
return;
|
||||
}
|
||||
if(m_state != WRITE_BEGIN && m_state != WRITE_CONTENT) {
|
||||
reportError("beginElement(): not allowed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// push tag onto element stack, and link to entry for the current object
|
||||
m_elementStack.push_back(element_entry());
|
||||
m_elementStack.back().name = name;
|
||||
m_objectStack.back().element = m_elementStack.back().name.String();
|
||||
|
||||
// write tag
|
||||
BString out;
|
||||
out << "\n" << indentString() << '<' << name;
|
||||
writeString(out);
|
||||
indentMore();
|
||||
}
|
||||
|
||||
// writes an end tag corresponding to the current element.
|
||||
// should only be called from IPersistent::xmlExportEnd() or
|
||||
// xmlExportContent().
|
||||
|
||||
void ExportContext::endElement() {
|
||||
|
||||
if(!m_objectStack.size()) {
|
||||
reportError("endElement(): no object being written.\n");
|
||||
return;
|
||||
}
|
||||
ASSERT(m_elementStack.size());
|
||||
element_entry& entry = m_elementStack.back();
|
||||
|
||||
if(m_state != WRITE_END && m_state != WRITE_CONTENT) {
|
||||
reportError("endElement(): not allowed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
indentLess();
|
||||
|
||||
BString out;
|
||||
|
||||
// write closing tag
|
||||
if(!entry.hasContent)
|
||||
out << "/>";
|
||||
else
|
||||
out << "\n" << indentString() << "</" << entry.name.String() << ">";
|
||||
|
||||
writeString(out);
|
||||
|
||||
// pop element off stack
|
||||
m_elementStack.pop_back();
|
||||
}
|
||||
|
||||
// indicates that content follows (writes the end of the
|
||||
// current element's start tag.)
|
||||
void ExportContext::beginContent() {
|
||||
|
||||
if(!m_objectStack.size()) {
|
||||
reportError("beginContent(): no object being written.\n");
|
||||
return;
|
||||
}
|
||||
ASSERT(m_elementStack.size());
|
||||
element_entry& entry = m_elementStack.back();
|
||||
|
||||
if(m_state != WRITE_CONTENT) {
|
||||
reportError("beginContent(): not allowed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
BString out = ">";
|
||||
writeString(out);
|
||||
|
||||
entry.hasContent = true;
|
||||
}
|
||||
|
||||
#define _WRITE_ATTR_BODY(VAL_SPEC) \
|
||||
if(!m_objectStack.size()) {\
|
||||
reportError("writeAttr(): no object being written.\n");\
|
||||
return;\
|
||||
}\
|
||||
ASSERT(m_elementStack.size());\
|
||||
if(m_state != WRITE_ATTRIBUTES &&\
|
||||
m_state != WRITE_CONTENT) {\
|
||||
reportError("writeAttr(): not allowed (state mismatch).\n");\
|
||||
return;\
|
||||
}\
|
||||
\
|
||||
m_elementStack.back().hasAttributes = true;\
|
||||
\
|
||||
BString out;\
|
||||
out << "\n" << indentString() << key;\
|
||||
_pad_with_spaces(out, key, *this, m_attrColumn) << " = '" << VAL_SPEC << '\'';\
|
||||
\
|
||||
writeString(out);
|
||||
|
||||
#if B_BEOS_VERSION > B_BEOS_VERSION_4_5
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
int8 value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
uint8 value) {_WRITE_ATTR_BODY(uint32(value))}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
int16 value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
uint16 value) {_WRITE_ATTR_BODY(uint32(value))}
|
||||
#endif
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
int32 value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
uint32 value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
int64 value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
uint64 value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
const char* value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
const BString& value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
void ExportContext::writeAttr(
|
||||
const char* key,
|
||||
float value) {_WRITE_ATTR_BODY(value)}
|
||||
|
||||
// writes a child object.
|
||||
// should only be called from IPersistent::xmlExportContent().
|
||||
// returns B_OK on success, or B_ERROR if an error occurred.
|
||||
status_t ExportContext::writeObject(
|
||||
IPersistent* object) {
|
||||
|
||||
// * SETUP
|
||||
ASSERT(object);
|
||||
if(m_state == ABORT)
|
||||
return B_ERROR;
|
||||
state_t origState = m_state;
|
||||
|
||||
// write entry to object stack
|
||||
m_objectStack.push_back(object_entry());
|
||||
object_entry& entry = m_objectStack.back();
|
||||
entry.object = object;
|
||||
|
||||
// * START TAG
|
||||
int elements = m_elementStack.size();
|
||||
m_state = WRITE_BEGIN;
|
||||
object->xmlExportBegin(*this);
|
||||
|
||||
if(m_state == ABORT)
|
||||
return B_ERROR;
|
||||
|
||||
if(!entry.element)
|
||||
reportError("writeObject(): no start tag for object.\n");
|
||||
else if(m_elementStack.size() - elements > 1)
|
||||
reportError("writeObject(): object wrote more than one start tag.\n");
|
||||
|
||||
if(m_state == ABORT)
|
||||
return B_ERROR;
|
||||
|
||||
// * ATTRIBUTES
|
||||
m_state = WRITE_ATTRIBUTES;
|
||||
object->xmlExportAttributes(*this);
|
||||
|
||||
if(m_state == ABORT)
|
||||
return B_ERROR;
|
||||
|
||||
// * CONTENT
|
||||
m_state = WRITE_CONTENT;
|
||||
object->xmlExportContent(*this);
|
||||
|
||||
if(m_state == ABORT)
|
||||
return B_ERROR;
|
||||
|
||||
// * END
|
||||
m_state = WRITE_END;
|
||||
object->xmlExportEnd(*this);
|
||||
|
||||
m_state = origState;
|
||||
|
||||
// pop object entry
|
||||
m_objectStack.pop_back();
|
||||
|
||||
return (m_state == ABORT) ? B_ERROR : B_OK;
|
||||
}
|
||||
|
||||
// writes an arbitrary string to the stream (calls reportError()
|
||||
// on failure.)
|
||||
|
||||
status_t ExportContext::writeString(
|
||||
const BString& string) {
|
||||
|
||||
return writeString(string.String(), string.Length());
|
||||
}
|
||||
|
||||
status_t ExportContext::writeString(
|
||||
const char* data,
|
||||
ssize_t length) {
|
||||
|
||||
ssize_t written = stream->Write(data, length);
|
||||
if(written < 0) {
|
||||
BString err = "Write error: '";
|
||||
err << strerror(written) << "'.\n";
|
||||
reportError(err.String());
|
||||
return written;
|
||||
}
|
||||
else if(written < length) {
|
||||
BString err = "Write incomplete: '";
|
||||
err << written << " of " << length << " bytes written.\n";
|
||||
reportError(err.String());
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** indentation helpers
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const char* ExportContext::indentString() const {
|
||||
return m_indentString.String();
|
||||
}
|
||||
|
||||
uint16 ExportContext::indentLevel() const {
|
||||
return m_indentLevel;
|
||||
}
|
||||
|
||||
void ExportContext::indentLess() {
|
||||
m_indentLevel = (m_indentLevel > m_indentIncrement) ?
|
||||
m_indentLevel - m_indentIncrement : 0;
|
||||
m_indentString.SetTo(' ', m_indentLevel);
|
||||
}
|
||||
|
||||
void ExportContext::indentMore() {
|
||||
m_indentLevel += m_indentIncrement;
|
||||
m_indentString.SetTo(' ', m_indentLevel);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** error-reporting operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
class dump_element { public:
|
||||
BString& _s;
|
||||
|
||||
dump_element(BString& s) : _s(s) {}
|
||||
void operator()(const ExportContext::element_entry& entry) {
|
||||
_s << " " << entry.name << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
// register a fatal error; halts the write process
|
||||
// as soon as possible.
|
||||
void ExportContext::reportError(
|
||||
const char* text) {
|
||||
|
||||
m_error << "FATAL ERROR: ";
|
||||
m_error << text << "\n";
|
||||
if(m_elementStack.size()) {
|
||||
_dumpElementStack(m_error);
|
||||
}
|
||||
|
||||
m_state = ABORT;
|
||||
}
|
||||
|
||||
void ExportContext::_dumpElementStack(
|
||||
BString& out) {
|
||||
out << "Element stack:\n";
|
||||
for_each(m_elementStack.begin(), m_elementStack.end(), dump_element(out));
|
||||
}
|
||||
|
||||
// END -- ExportContext.cpp --
|
||||
|
254
src/apps/cortex/Persistence/ExportContext.h
Normal file
254
src/apps/cortex/Persistence/ExportContext.h
Normal file
@ -0,0 +1,254 @@
|
||||
// ExportContext.h
|
||||
// * PURPOSE
|
||||
// Describe the state of a serialization ('save') operation.
|
||||
// The 'load' equivalent is ImportContext.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 28jun99 Begun
|
||||
|
||||
#ifndef __ExportContext_H__
|
||||
#define __ExportContext_H__
|
||||
|
||||
#include <Debug.h>
|
||||
|
||||
#include <String.h>
|
||||
#include <ostream.h>
|
||||
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class IPersistent;
|
||||
class ExportContext;
|
||||
|
||||
// writeAttr() helper
|
||||
inline BString& _pad_with_spaces(
|
||||
BString& out,
|
||||
const char* text,
|
||||
ExportContext& context,
|
||||
uint16 column);
|
||||
|
||||
|
||||
class ExportContext {
|
||||
public: // *** ctor/dtor
|
||||
virtual ~ExportContext();
|
||||
ExportContext();
|
||||
ExportContext(
|
||||
BDataIO* _stream);
|
||||
|
||||
public: // *** public members
|
||||
|
||||
// the output stream
|
||||
BDataIO* stream;
|
||||
|
||||
// the element stack
|
||||
struct element_entry {
|
||||
element_entry() : hasAttributes(false), hasContent(false) {}
|
||||
|
||||
BString name;
|
||||
bool hasAttributes;
|
||||
bool hasContent;
|
||||
};
|
||||
|
||||
typedef list<element_entry> element_list;
|
||||
element_list m_elementStack;
|
||||
|
||||
public: // *** XML formatting helpers
|
||||
|
||||
// writes a start tag. should be called from
|
||||
// IPersistent::xmlExportBegin()
|
||||
// (or xmlExportContent(), if you're writing nested elements)
|
||||
|
||||
void beginElement(
|
||||
const char* name);
|
||||
|
||||
// writes an end tag corresponding to the current element.
|
||||
// should only be called from IPersistent::xmlExportEnd() or
|
||||
// xmlExportContent().
|
||||
void endElement();
|
||||
|
||||
// indicates that content follows (writes the end of the
|
||||
// current element's start tag.)
|
||||
void beginContent();
|
||||
|
||||
// // writes an attribute.
|
||||
// // should only be called from IPersistent::xmlExportAttributes().
|
||||
// template <class T>
|
||||
// void writeAttr(
|
||||
// const char* key,
|
||||
// T value) {
|
||||
//
|
||||
// if(!m_objectStack.size()) {
|
||||
// reportError("writeAttr(): no object being written.\n");
|
||||
// return;
|
||||
// }
|
||||
// ASSERT(m_elementStack.size());
|
||||
// if(m_state != WRITE_ATTRIBUTES &&
|
||||
// m_state != WRITE_CONTENT) {
|
||||
// reportError("writeAttr(): not allowed (state mismatch).\n");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// m_elementStack.back().hasAttributes = true;
|
||||
//
|
||||
// BString out;
|
||||
// out << "\n" << indentString() << key;
|
||||
// _pad_with_spaces(out, key, *this, m_attrColumn) << " = '" << value << '\'';
|
||||
//
|
||||
// writeString(out);
|
||||
// }
|
||||
|
||||
// [e.moon 22dec99]
|
||||
// non-template forms of writeAttr()
|
||||
|
||||
#if B_BEOS_VERSION > B_BEOS_VERSION_4_5
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
int8 value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
uint8 value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
int16 value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
uint16 value);
|
||||
#endif
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
int32 value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
uint32 value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
int64 value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
uint64 value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
const char* value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
const BString& value);
|
||||
|
||||
void writeAttr(
|
||||
const char* key,
|
||||
float value);
|
||||
|
||||
// writes a child object.
|
||||
// should only be called from IPersistent::xmlExportContent().
|
||||
// returns B_OK on success, or B_ERROR if an error occurred.
|
||||
status_t writeObject(
|
||||
IPersistent* object);
|
||||
|
||||
// writes an arbitrary string to the stream (calls reportError()
|
||||
// on failure.)
|
||||
status_t writeString(
|
||||
const BString& string);
|
||||
|
||||
status_t writeString(
|
||||
const char* data,
|
||||
ssize_t length);
|
||||
|
||||
public: // *** indentation helpers
|
||||
|
||||
// return a string padded with spaces to the current
|
||||
// indent level
|
||||
const char* indentString() const;
|
||||
|
||||
// return the current indent level
|
||||
uint16 indentLevel() const;
|
||||
|
||||
// decrease the indent level
|
||||
void indentLess();
|
||||
|
||||
// increase the indent level
|
||||
void indentMore();
|
||||
|
||||
// +++++ extra formatting controls needed [e.moon 1dec99]
|
||||
// * attrColumn access
|
||||
// * single vs. multi-line element formatting
|
||||
|
||||
|
||||
public: // *** error operations
|
||||
|
||||
// register a fatal error; halts the write process
|
||||
// as soon as possible.
|
||||
void reportError(
|
||||
const char* text);
|
||||
|
||||
// fetch error text
|
||||
const char* errorText() const { return m_error.String(); }
|
||||
|
||||
private: // members
|
||||
|
||||
// * Indentation/formatting
|
||||
|
||||
uint16 m_indentLevel;
|
||||
uint16 m_indentIncrement;
|
||||
|
||||
uint16 m_attrColumn;
|
||||
|
||||
BString m_indentString;
|
||||
|
||||
// * State
|
||||
|
||||
enum state_t {
|
||||
INIT,
|
||||
WRITE_BEGIN,
|
||||
WRITE_ATTRIBUTES,
|
||||
WRITE_CONTENT,
|
||||
WRITE_END,
|
||||
ABORT
|
||||
};
|
||||
|
||||
state_t m_state;
|
||||
BString m_error;
|
||||
|
||||
// object stack
|
||||
|
||||
struct object_entry {
|
||||
object_entry() : element(0), object(0) {}
|
||||
|
||||
const char* element;
|
||||
IPersistent* object;
|
||||
};
|
||||
|
||||
typedef list<object_entry> object_list;
|
||||
object_list m_objectStack;
|
||||
|
||||
private:
|
||||
void _dumpElementStack(
|
||||
BString& out);
|
||||
};
|
||||
|
||||
// ExportContext::writeAttr() helper
|
||||
inline BString& _pad_with_spaces(
|
||||
BString& out,
|
||||
const char* text,
|
||||
ExportContext& context,
|
||||
uint16 column) {
|
||||
|
||||
int16 spaces = column - (strlen(text) + context.indentLevel());
|
||||
if(spaces < 0) spaces = 0;
|
||||
while(spaces--) out << ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
|
||||
#endif /*__ExportContext_H__*/
|
167
src/apps/cortex/Persistence/IPersistent.h
Normal file
167
src/apps/cortex/Persistence/IPersistent.h
Normal file
@ -0,0 +1,167 @@
|
||||
// IPersistant.h
|
||||
// * PURPOSE
|
||||
// Interface to be implemented by objects that want to
|
||||
// be persistent (loadable/savable) via XML.
|
||||
//
|
||||
// * TO DO +++++ IN PROGRESS 8jul99
|
||||
// - Make the export API friendlier: classes shouldn't have to
|
||||
// know how to format XML, and it should be easy to extend
|
||||
// the serialization capabilities in a subclass. [8jul99]
|
||||
// * should this stuff be exposed via ExportContext methods?
|
||||
//
|
||||
// - FBC stuffing? [29jun99]
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 29jun99 Reworking; merging with the interface
|
||||
// formerly known as Condenser.
|
||||
// e.moon 28jun99 Begun
|
||||
|
||||
#ifndef __IPersistent_H__
|
||||
#define __IPersistent_H__
|
||||
|
||||
#include "ImportContext.h"
|
||||
#include "ExportContext.h"
|
||||
|
||||
#include <ostream.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class IPersistent {
|
||||
|
||||
public:
|
||||
// empty virtual dtor
|
||||
virtual ~IPersistent() {}
|
||||
|
||||
public: // *** REQUIRED INTERFACE
|
||||
|
||||
// 8jul99 export rework
|
||||
// // EXPORT:
|
||||
// // write an XML representation of this object (including
|
||||
// // any child objects) to the given stream. use the
|
||||
// // provided context object to format the output nicely.
|
||||
//
|
||||
// virtual void xmlExport(
|
||||
// ostream& stream,
|
||||
// ExportContext& context) const =0;
|
||||
|
||||
// EXPORT:
|
||||
// implement this method to write a start tag via
|
||||
// context.startElement(). You can also write comments or
|
||||
// processing instructions directly to the stream.
|
||||
|
||||
virtual void xmlExportBegin(
|
||||
ExportContext& context) const=0;
|
||||
|
||||
// EXPORT:
|
||||
// implement this method to write all the attributes needed
|
||||
// to represent your object, via context.writeAttribute().
|
||||
// (If subclassing an IPersistent implementation, don't forget
|
||||
// to call up to its version of this method.)
|
||||
|
||||
virtual void xmlExportAttributes(
|
||||
ExportContext& context) const=0;
|
||||
|
||||
// EXPORT: optional
|
||||
// implement this method to write any child objects, using
|
||||
// context.writeObject(). You can also write text content
|
||||
// directly to the stream.
|
||||
// (If subclassing an IPersistent implementation, don't forget
|
||||
// to call up to its version of this method.)
|
||||
|
||||
virtual void xmlExportContent(
|
||||
ExportContext& context) const { TOUCH(context); }
|
||||
|
||||
// EXPORT:
|
||||
// implement this method to write an end tag via
|
||||
// context.endElement().
|
||||
|
||||
virtual void xmlExportEnd(
|
||||
ExportContext& context) const=0;
|
||||
|
||||
// IMPORT:
|
||||
// called when the start tag of the element corresponding to
|
||||
// this object is encountered, immediately after this object
|
||||
// is constructed. no action is required.
|
||||
//
|
||||
// context.element() will return the element name that produced
|
||||
// this object.
|
||||
|
||||
virtual void xmlImportBegin(
|
||||
ImportContext& context) =0;
|
||||
|
||||
// IMPORT:
|
||||
// called for each attribute/value pair found in
|
||||
// the element corresponding to this object.
|
||||
|
||||
virtual void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) =0;
|
||||
|
||||
// IMPORT:
|
||||
// called when character data is encountered for the
|
||||
// element corresponding to this object. data is not
|
||||
// 0-terminated; this method may be called several times
|
||||
// (if, for example, the character data is broken up
|
||||
// by child elements.)
|
||||
|
||||
virtual void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) =0;
|
||||
|
||||
// IMPORT:
|
||||
// called when an object has been successfully
|
||||
// constructed 'beneath' this one.
|
||||
//
|
||||
// context.element() will return the element name corresponding
|
||||
// to the child object.
|
||||
|
||||
virtual void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context) =0;
|
||||
|
||||
// IMPORT:
|
||||
// called when close tag is encountered for the element
|
||||
// corresponding to this object. a good place to do
|
||||
// validation.
|
||||
//
|
||||
// context.element() will return the element name that produced
|
||||
// this object.
|
||||
|
||||
virtual void xmlImportComplete(
|
||||
ImportContext& context) =0;
|
||||
|
||||
public: // *** OPTIONAL CHILD-IMPORT INTERFACE
|
||||
// These methods allow an IPersistent object
|
||||
// to directly import nested elements: handy for
|
||||
// condensing more complicated document structures
|
||||
// into a single object (see MediaFormatIO for an
|
||||
// example.)
|
||||
|
||||
virtual void xmlImportChildBegin(
|
||||
const char* name,
|
||||
ImportContext& context) {
|
||||
TOUCH(name);
|
||||
context.reportWarning("Nested element not supported.");
|
||||
}
|
||||
|
||||
virtual void xmlImportChildAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) {TOUCH(key); TOUCH(value); TOUCH(context);}
|
||||
|
||||
virtual void xmlImportChildContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) {TOUCH(data); TOUCH(length); TOUCH(context);}
|
||||
|
||||
virtual void xmlImportChildComplete(
|
||||
const char* name,
|
||||
ImportContext& context) {TOUCH(name); TOUCH(context);}
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
|
||||
#endif /*__IPersistent_H__*/
|
30
src/apps/cortex/Persistence/IStateArchivable.h
Normal file
30
src/apps/cortex/Persistence/IStateArchivable.h
Normal file
@ -0,0 +1,30 @@
|
||||
// IStateArchivable.h
|
||||
// * PURPOSE
|
||||
// Similar to BArchivable, but provides for archiving of
|
||||
// a particular object's state, not the object itself.
|
||||
// This mechanism doesn't instantiate objects.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 27nov99 Begun
|
||||
|
||||
#ifndef __IStateArchivable_H__
|
||||
#define __IStateArchivable_H__
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class IStateArchivable {
|
||||
public:
|
||||
// empty virtual dtor
|
||||
virtual ~IStateArchivable() {}
|
||||
|
||||
public: // *** INTERFACE
|
||||
virtual status_t importState(
|
||||
const BMessage* archive) =0;
|
||||
|
||||
virtual status_t exportState(
|
||||
BMessage* archive) const =0;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__IStateArchivable_H__*/
|
104
src/apps/cortex/Persistence/ImportContext.cpp
Normal file
104
src/apps/cortex/Persistence/ImportContext.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// ImportContext.cpp
|
||||
// e.moon 1jul99
|
||||
|
||||
#include "ImportContext.h"
|
||||
#include "xmlparse.h"
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
ImportContext::~ImportContext() {}
|
||||
ImportContext::ImportContext(list<BString>& errors) :
|
||||
m_state(PARSING),
|
||||
m_errors(errors),
|
||||
m_pParser(0) {}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// accessors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// fetch the current element (tag)
|
||||
// (returns 0 if the stack is empty)
|
||||
const char* ImportContext::element() const {
|
||||
return (m_elementStack.size()) ?
|
||||
m_elementStack.back().String() :
|
||||
0;
|
||||
}
|
||||
|
||||
const char* ImportContext::parentElement() const {
|
||||
if(m_elementStack.size() < 2)
|
||||
return 0;
|
||||
list<BString>::const_reverse_iterator it = m_elementStack.rbegin();
|
||||
++it;
|
||||
return (*it).String();
|
||||
}
|
||||
|
||||
list<BString>& ImportContext::errors() const {
|
||||
return m_errors;
|
||||
}
|
||||
const ImportContext::state_t ImportContext::state() const {
|
||||
return m_state;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// error-reporting operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// register a warning to be returned once the deserialization
|
||||
// process is complete.
|
||||
void ImportContext::reportWarning(
|
||||
const char* pText) {
|
||||
|
||||
XML_Parser p = (XML_Parser)m_pParser;
|
||||
|
||||
BString err = "Warning: ";
|
||||
err << pText;
|
||||
if(p) {
|
||||
err << "\n (line " <<
|
||||
(uint32)XML_GetCurrentLineNumber(p) << ", column " <<
|
||||
(uint32)XML_GetCurrentColumnNumber(p) << ", element '" <<
|
||||
(element() ? element() : "(none)") << "')\n";
|
||||
} else
|
||||
err << "\n";
|
||||
m_errors.push_back(err);
|
||||
}
|
||||
|
||||
// register a fatal error; halts the deserialization process
|
||||
// as soon as possible.
|
||||
void ImportContext::reportError(
|
||||
const char* pText) {
|
||||
|
||||
XML_Parser p = (XML_Parser)m_pParser;
|
||||
|
||||
BString err = "FATAL ERROR: ";
|
||||
err << pText;
|
||||
if(p) {
|
||||
err << "\n (line " <<
|
||||
(uint32)XML_GetCurrentLineNumber(p) << ", column " <<
|
||||
(uint32)XML_GetCurrentColumnNumber(p) << ", element '" <<
|
||||
(element() ? element() : "(none)") << "')\n";
|
||||
} else
|
||||
err << "\n";
|
||||
m_errors.push_back(err);
|
||||
|
||||
m_state = ABORT;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ImportContext::reset() {
|
||||
m_state = PARSING;
|
||||
m_elementStack.clear();
|
||||
// +++++ potential for memory leaks; reset() is currently
|
||||
// only to be called after an identify cycle, during
|
||||
// which no objects are created anyway, but this still
|
||||
// gives me the shivers...
|
||||
m_objectStack.clear();
|
||||
}
|
||||
|
||||
// END -- ImportContext.cpp --
|
76
src/apps/cortex/Persistence/ImportContext.h
Normal file
76
src/apps/cortex/Persistence/ImportContext.h
Normal file
@ -0,0 +1,76 @@
|
||||
// ImportContext.h
|
||||
// * PURPOSE
|
||||
// Describe the state of a deserialization ('load') operation.
|
||||
// The 'save' equivalent is ExportContext.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 29jun99 Begun
|
||||
|
||||
#ifndef __ImportContext_H__
|
||||
#define __ImportContext_H__
|
||||
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <String.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class IPersistent;
|
||||
|
||||
class ImportContext {
|
||||
friend class Importer;
|
||||
|
||||
public: // *** types
|
||||
enum state_t {
|
||||
PARSING,
|
||||
COMPLETE,
|
||||
ABORT
|
||||
};
|
||||
|
||||
public: // *** ctor/dtor
|
||||
virtual ~ImportContext();
|
||||
ImportContext(
|
||||
list<BString>& errors);
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
// fetch the current element (tag)
|
||||
// (returns 0 if the stack is empty)
|
||||
const char* element() const;
|
||||
|
||||
// fetch the current element's parent
|
||||
// (returns 0 if the stack is empty or the current element is top-level)
|
||||
const char* parentElement() const;
|
||||
|
||||
list<BString>& errors() const;
|
||||
const state_t state() const;
|
||||
|
||||
public: // *** error-reporting operations
|
||||
|
||||
// register a warning to be returned once the deserialization
|
||||
// process is complete.
|
||||
void reportWarning(
|
||||
const char* text);
|
||||
|
||||
// register a fatal error; halts the deserialization process
|
||||
// as soon as possible.
|
||||
void reportError(
|
||||
const char* text);
|
||||
|
||||
protected: // *** internal operations
|
||||
|
||||
void reset();
|
||||
|
||||
private: // *** members
|
||||
state_t m_state;
|
||||
list<BString>& m_errors;
|
||||
|
||||
list<BString> m_elementStack;
|
||||
typedef pair<const char*, IPersistent*> object_entry;
|
||||
list<object_entry> m_objectStack;
|
||||
|
||||
void* m_pParser;
|
||||
};
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__ImportContext_H__*/
|
460
src/apps/cortex/Persistence/Importer.cpp
Normal file
460
src/apps/cortex/Persistence/Importer.cpp
Normal file
@ -0,0 +1,460 @@
|
||||
// Importer.cpp
|
||||
// e.moon 28jun99
|
||||
|
||||
#include "Importer.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#include <Autolock.h>
|
||||
#include <Debug.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// expat hooks
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void _oc_handle_start(
|
||||
void* pUser,
|
||||
const XML_Char* pName,
|
||||
const XML_Char** ppAtts) {
|
||||
((Importer*)pUser)->xmlElementStart(pName, ppAtts);
|
||||
}
|
||||
|
||||
void _oc_handle_end(
|
||||
void* pUser,
|
||||
const XML_Char* pName) {
|
||||
((Importer*)pUser)->xmlElementEnd(pName);
|
||||
}
|
||||
|
||||
void _oc_handle_pi(
|
||||
void* pUser,
|
||||
const XML_Char* pTarget,
|
||||
const XML_Char* pData) {
|
||||
((Importer*)pUser)->xmlProcessingInstruction(pTarget, pData);
|
||||
}
|
||||
|
||||
void _oc_handle_char(
|
||||
void* pUser,
|
||||
const XML_Char* pData,
|
||||
int length) {
|
||||
((Importer*)pUser)->xmlCharacterData(pData, length);
|
||||
}
|
||||
|
||||
void _oc_handle_default(
|
||||
void* pUser,
|
||||
const XML_Char* pData,
|
||||
int length) {
|
||||
((Importer*)pUser)->xmlDefaultData(pData, length);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
Importer::~Importer() {
|
||||
// clean up
|
||||
freeParser();
|
||||
|
||||
delete m_context;
|
||||
}
|
||||
|
||||
|
||||
Importer::Importer(
|
||||
list<BString>& errors) :
|
||||
|
||||
m_parser(0),
|
||||
m_docType(0),
|
||||
m_identify(false),
|
||||
m_context(new ImportContext(errors)),
|
||||
m_rootObject(0) {
|
||||
|
||||
initParser();
|
||||
}
|
||||
|
||||
Importer::Importer(
|
||||
ImportContext* context) :
|
||||
|
||||
m_parser(0),
|
||||
m_docType(0),
|
||||
m_identify(false),
|
||||
m_context(context),
|
||||
m_rootObject(0) {
|
||||
|
||||
ASSERT(m_context);
|
||||
|
||||
initParser();
|
||||
}
|
||||
|
||||
Importer::Importer(
|
||||
list<BString>& errors,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* docType) :
|
||||
|
||||
m_parser(0),
|
||||
m_docType(docType),
|
||||
m_identify(false),
|
||||
m_context(new ImportContext(errors)),
|
||||
m_rootObject(rootObject) {
|
||||
|
||||
ASSERT(rootObject);
|
||||
ASSERT(docType);
|
||||
|
||||
initParser();
|
||||
}
|
||||
|
||||
Importer::Importer(
|
||||
ImportContext* context,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* docType) :
|
||||
|
||||
m_parser(0),
|
||||
m_docType(docType),
|
||||
m_identify(false),
|
||||
m_context(context),
|
||||
m_rootObject(rootObject) {
|
||||
|
||||
ASSERT(m_context);
|
||||
ASSERT(rootObject);
|
||||
ASSERT(docType);
|
||||
|
||||
initParser();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// accessors
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// the import context
|
||||
const ImportContext& Importer::context() const {
|
||||
return *m_context;
|
||||
}
|
||||
|
||||
// matched (or provided) document type
|
||||
XML::DocumentType* Importer::docType() const {
|
||||
return m_docType;
|
||||
}
|
||||
|
||||
// completed object (available if
|
||||
// context().state() == ImportContext::COMPLETE, or
|
||||
// if a root object was provided to the ctor)
|
||||
IPersistent* Importer::target() const {
|
||||
return m_rootObject;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// put the importer into 'identify mode'
|
||||
// (disengaged once the first element is encountered)
|
||||
void Importer::setIdentifyMode() {
|
||||
reset();
|
||||
m_docType = 0;
|
||||
m_identify = true;
|
||||
}
|
||||
|
||||
void Importer::reset() {
|
||||
// doesn't forget document type from identify cycle!
|
||||
|
||||
m_identify = false;
|
||||
m_context->reset();
|
||||
m_rootObject = 0;
|
||||
}
|
||||
|
||||
// handle a buffer; return false if an error occurs
|
||||
bool Importer::parseBuffer(
|
||||
const char* pBuffer,
|
||||
uint32 length,
|
||||
bool last) {
|
||||
|
||||
ASSERT(m_parser);
|
||||
|
||||
int err = XML_Parse(m_parser, pBuffer, length, last);
|
||||
|
||||
if(!err) {
|
||||
BString str = "Parse Error: ";
|
||||
str << XML_ErrorString(XML_GetErrorCode(m_parser));
|
||||
m_context->reportError(str.String());
|
||||
return false;
|
||||
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// internal operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// create & initialize parser
|
||||
void Importer::initParser() {
|
||||
ASSERT(!m_parser);
|
||||
m_parser = XML_ParserCreate(0);
|
||||
m_context->m_pParser = m_parser;
|
||||
|
||||
XML_SetElementHandler(
|
||||
m_parser,
|
||||
&_oc_handle_start,
|
||||
&_oc_handle_end);
|
||||
|
||||
XML_SetProcessingInstructionHandler(
|
||||
m_parser,
|
||||
&_oc_handle_pi);
|
||||
|
||||
XML_SetCharacterDataHandler(
|
||||
m_parser,
|
||||
&_oc_handle_char);
|
||||
|
||||
XML_SetDefaultHandlerExpand(
|
||||
m_parser,
|
||||
&_oc_handle_default);
|
||||
|
||||
XML_SetUserData(
|
||||
m_parser,
|
||||
(void*)this);
|
||||
}
|
||||
|
||||
// clean up the parser
|
||||
void Importer::freeParser() {
|
||||
ASSERT(m_parser);
|
||||
XML_ParserFree(m_parser);
|
||||
m_parser = 0;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// XML parser event hooks
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void Importer::xmlElementStart(
|
||||
const char* pName,
|
||||
const char** ppAttributes) {
|
||||
|
||||
if(m_context->m_state != ImportContext::PARSING)
|
||||
return;
|
||||
|
||||
IPersistent* target = 0;
|
||||
|
||||
if(!m_context->m_elementStack.size()) {
|
||||
// this is the first element; identify or verify document type
|
||||
|
||||
if(m_rootObject) {
|
||||
// test against expected document type
|
||||
ASSERT(m_docType);
|
||||
if(m_docType->rootElement != pName) {
|
||||
BString err("Unexpected document element (should be <");
|
||||
err << m_docType->rootElement << "/>";
|
||||
m_context->reportError(err.String());
|
||||
return;
|
||||
}
|
||||
|
||||
// target the provided root object
|
||||
target = m_rootObject;
|
||||
}
|
||||
else {
|
||||
// look up doc type
|
||||
BAutolock _l(XML::s_docTypeLock);
|
||||
XML::doc_type_map::iterator it = XML::s_docTypeMap.find(
|
||||
BString(pName));
|
||||
|
||||
if(it != XML::s_docTypeMap.end())
|
||||
m_docType = (*it).second;
|
||||
else {
|
||||
// whoops, don't know how to handle this element:
|
||||
BString err("No document type registered for element '");
|
||||
err << pName << "'.";
|
||||
m_context->reportError(err.String());
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_identify) {
|
||||
// end of identify cycle
|
||||
m_context->m_state = ImportContext::COMPLETE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// at this point, there'd better be a valid document type
|
||||
ASSERT(m_docType);
|
||||
|
||||
// push element name onto the stack
|
||||
m_context->m_elementStack.push_back(pName);
|
||||
|
||||
// try to create an object for this element if necessary
|
||||
if(!target)
|
||||
target = m_docType->objectFor(pName);
|
||||
|
||||
if(target) {
|
||||
// call 'begin import' hook
|
||||
m_context->m_objectStack.push_back(
|
||||
make_pair(m_context->element(), target));
|
||||
target->xmlImportBegin(*m_context);
|
||||
|
||||
// error? bail
|
||||
if(m_context->state() != ImportContext::PARSING)
|
||||
return;
|
||||
|
||||
// walk attributes
|
||||
while(*ppAttributes) {
|
||||
target->xmlImportAttribute(
|
||||
ppAttributes[0],
|
||||
ppAttributes[1],
|
||||
*m_context);
|
||||
|
||||
// error? bail
|
||||
if(m_context->state() != ImportContext::PARSING)
|
||||
return;
|
||||
|
||||
ppAttributes += 2;
|
||||
}
|
||||
} else {
|
||||
// no object directly maps to this element; hand to
|
||||
// the current focus object
|
||||
|
||||
ASSERT(m_context->m_objectStack.size());
|
||||
IPersistent* curObject = m_context->m_objectStack.back().second;
|
||||
ASSERT(curObject);
|
||||
|
||||
curObject->xmlImportChildBegin(
|
||||
pName,
|
||||
*m_context);
|
||||
|
||||
// error? bail
|
||||
if(m_context->state() != ImportContext::PARSING)
|
||||
return;
|
||||
|
||||
// walk attributes
|
||||
while(*ppAttributes) {
|
||||
curObject->xmlImportChildAttribute(
|
||||
ppAttributes[0],
|
||||
ppAttributes[1],
|
||||
*m_context);
|
||||
|
||||
// error? bail
|
||||
if(m_context->state() != ImportContext::PARSING)
|
||||
return;
|
||||
|
||||
ppAttributes += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Importer::xmlElementEnd(
|
||||
const char* pName) {
|
||||
|
||||
if(m_context->m_state != ImportContext::PARSING)
|
||||
return;
|
||||
ASSERT(m_docType);
|
||||
|
||||
// PRINT(("Importer::xmlElementEnd(): %s\n", pName));
|
||||
|
||||
// compare name to element on top of stack
|
||||
if(!m_context->m_elementStack.size() ||
|
||||
m_context->m_elementStack.back() != pName) {
|
||||
m_context->reportError("Mismatched end tag.");
|
||||
return;
|
||||
}
|
||||
|
||||
// see if it matches the topmost object
|
||||
IPersistent* pObject = 0;
|
||||
if(!m_context->m_objectStack.size()) {
|
||||
m_context->reportError("No object being constructed.");
|
||||
return;
|
||||
}
|
||||
if(m_context->m_objectStack.back().first == m_context->element()) {
|
||||
// matched; pop it
|
||||
pObject = m_context->m_objectStack.back().second;
|
||||
m_context->m_objectStack.pop_back();
|
||||
}
|
||||
|
||||
if(pObject) {
|
||||
// notify object that import is complete
|
||||
pObject->xmlImportComplete(
|
||||
*m_context);
|
||||
|
||||
// error? bail
|
||||
if(m_context->state() != ImportContext::PARSING)
|
||||
return;
|
||||
|
||||
if(m_context->m_objectStack.size()) {
|
||||
// hand the newly-constructed child to its parent
|
||||
m_context->m_objectStack.back().second->xmlImportChild(
|
||||
pObject,
|
||||
*m_context);
|
||||
} else {
|
||||
// done
|
||||
ASSERT(m_context->m_elementStack.size() == 1);
|
||||
m_context->m_state = ImportContext::COMPLETE;
|
||||
if(m_rootObject) {
|
||||
ASSERT(m_rootObject == pObject);
|
||||
} else
|
||||
m_rootObject = pObject;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// notify current topmost object
|
||||
ASSERT(m_context->m_objectStack.size());
|
||||
IPersistent* curObject = m_context->m_objectStack.back().second;
|
||||
ASSERT(curObject);
|
||||
|
||||
curObject->xmlImportChildComplete(
|
||||
pName,
|
||||
*m_context);
|
||||
}
|
||||
|
||||
// remove entry from element stack
|
||||
m_context->m_elementStack.pop_back();
|
||||
ASSERT(m_context->m_objectStack.size() <= m_context->m_elementStack.size());
|
||||
}
|
||||
|
||||
void Importer::xmlProcessingInstruction(
|
||||
const char* pTarget,
|
||||
const char* pData) {
|
||||
|
||||
if(m_context->m_state != ImportContext::PARSING)
|
||||
return;
|
||||
// PRINT(("Importer::xmlProcessingInstruction(): %s, %s\n",
|
||||
// pTarget, pData));
|
||||
|
||||
}
|
||||
|
||||
// not 0-terminated
|
||||
void Importer::xmlCharacterData(
|
||||
const char* pData,
|
||||
int32 length) {
|
||||
|
||||
if(m_context->m_state != ImportContext::PARSING)
|
||||
return;
|
||||
|
||||
// see if the current element matches the topmost object
|
||||
IPersistent* pObject = 0;
|
||||
if(!m_context->m_objectStack.size()) {
|
||||
m_context->reportError("No object being constructed.");
|
||||
return;
|
||||
}
|
||||
|
||||
pObject = m_context->m_objectStack.back().second;
|
||||
if(m_context->m_objectStack.back().first == m_context->element()) {
|
||||
|
||||
pObject->xmlImportContent(
|
||||
pData,
|
||||
length,
|
||||
*m_context);
|
||||
}
|
||||
else {
|
||||
pObject->xmlImportChildContent(
|
||||
pData,
|
||||
length,
|
||||
*m_context);
|
||||
}
|
||||
}
|
||||
|
||||
// not 0-terminated
|
||||
void Importer::xmlDefaultData(
|
||||
const char* pData,
|
||||
int32 length) {
|
||||
|
||||
if(m_context->m_state != ImportContext::PARSING)
|
||||
return;
|
||||
// PRINT(("Importer::xmlDefaultData()\n"));
|
||||
}
|
||||
|
||||
// END -- Importer.cpp --
|
131
src/apps/cortex/Persistence/Importer.h
Normal file
131
src/apps/cortex/Persistence/Importer.h
Normal file
@ -0,0 +1,131 @@
|
||||
// Importer.h
|
||||
//
|
||||
// * PURPOSE
|
||||
// Given a stream of XML parser events, produce the object[s]
|
||||
// represented by the markup.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 30jun99 Moved actual object-building responsibility
|
||||
// to IPersistent; this class is now internal
|
||||
// to Cortex::XML.
|
||||
// e.moon 28jun99 Begun. [was 'Importer']
|
||||
|
||||
#ifndef __Importer_H__
|
||||
#define __Importer_H__
|
||||
|
||||
#include <list>
|
||||
#include <String.h>
|
||||
|
||||
#include "ImportContext.h"
|
||||
#include "XML.h"
|
||||
#include "xmlparse.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class Importer {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
virtual ~Importer();
|
||||
|
||||
// constructs a format-guessing Importer (uses the
|
||||
// DocumentType registry)
|
||||
Importer(
|
||||
list<BString>& errors);
|
||||
|
||||
// the Importer takes ownership of the given context!
|
||||
Importer(
|
||||
ImportContext* context);
|
||||
|
||||
// constructs a manual Importer; the given root
|
||||
// object is populated
|
||||
Importer(
|
||||
list<BString>& errors,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* docType);
|
||||
|
||||
// the Importer takes ownership of the given context!
|
||||
Importer(
|
||||
ImportContext* context,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* docType);
|
||||
|
||||
public: // *** accessors
|
||||
|
||||
// the import context
|
||||
const ImportContext& context() const;
|
||||
|
||||
// matched document type
|
||||
XML::DocumentType* docType() const;
|
||||
|
||||
// completed object (available if
|
||||
// context().state() == ImportContext::COMPLETE, or
|
||||
// if a root object was provided to the ctor)
|
||||
IPersistent* target() const;
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// put the importer into 'identify mode'
|
||||
// (disengaged once the first element is encountered)
|
||||
void setIdentifyMode();
|
||||
|
||||
// prepare to read the document after an identify cycle
|
||||
void reset();
|
||||
|
||||
// handle a buffer; return false if an error occurs
|
||||
bool parseBuffer(
|
||||
const char* buffer,
|
||||
uint32 length,
|
||||
bool last);
|
||||
|
||||
public: // *** internal operations
|
||||
|
||||
// create & initialize parser
|
||||
void initParser();
|
||||
|
||||
// clean up the parser
|
||||
void freeParser();
|
||||
|
||||
public: // *** XML parser event hooks
|
||||
|
||||
virtual void xmlElementStart(
|
||||
const char* name,
|
||||
const char** attributes);
|
||||
|
||||
virtual void xmlElementEnd(
|
||||
const char* name);
|
||||
|
||||
virtual void xmlProcessingInstruction(
|
||||
const char* target,
|
||||
const char* data);
|
||||
|
||||
// not 0-terminated
|
||||
virtual void xmlCharacterData(
|
||||
const char* data,
|
||||
int32 length);
|
||||
|
||||
// not 0-terminated
|
||||
virtual void xmlDefaultData(
|
||||
const char* data,
|
||||
int32 length);
|
||||
|
||||
private: // *** implementation
|
||||
|
||||
XML_Parser m_parser;
|
||||
XML::DocumentType* m_docType;
|
||||
|
||||
// if true, the importer is being used to identify the
|
||||
// document type -- it should halt as soon as the first
|
||||
// element is encountered.
|
||||
bool m_identify;
|
||||
|
||||
ImportContext* const m_context;
|
||||
|
||||
// the constructed object: if no rootObject was provided
|
||||
// in the ctor, this is only filled in once the document
|
||||
// end tag has been encountered.
|
||||
IPersistent* m_rootObject;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__Importer_H__*/
|
75
src/apps/cortex/Persistence/StringContent.cpp
Normal file
75
src/apps/cortex/Persistence/StringContent.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
// StringContent.cpp
|
||||
|
||||
#include "StringContent.h"
|
||||
#include "ImportContext.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// EXPORT [not implemented]
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void StringContent::xmlExportBegin(
|
||||
ExportContext& context) const {
|
||||
context.reportError("StringContent: no export");
|
||||
}
|
||||
void StringContent::xmlExportAttributes(
|
||||
ExportContext& context) const {
|
||||
context.reportError("StringContent: no export");
|
||||
}
|
||||
void StringContent::xmlExportContent(
|
||||
ExportContext& context) const {
|
||||
context.reportError("StringContent: no export");
|
||||
}
|
||||
void StringContent::xmlExportEnd(
|
||||
ExportContext& context) const {
|
||||
context.reportError("StringContent: no export");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// IMPORT
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void StringContent::xmlImportBegin(
|
||||
ImportContext& context) {}
|
||||
|
||||
void StringContent::xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) {}
|
||||
|
||||
void StringContent::xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) {
|
||||
content.Append(data, length);
|
||||
}
|
||||
|
||||
void StringContent::xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context) {
|
||||
context.reportError("StringContent: child not expected");
|
||||
}
|
||||
|
||||
void StringContent::xmlImportComplete(
|
||||
ImportContext& context) {
|
||||
|
||||
// chomp leading/trailing whitespace
|
||||
if(content.Length() == 0)
|
||||
return;
|
||||
|
||||
int32 last = 0;
|
||||
for(; last < content.Length() && isspace(content[last]); ++last) {}
|
||||
if(last > 0)
|
||||
content.Remove(0, last);
|
||||
|
||||
last = content.Length() - 1;
|
||||
int32 from = last;
|
||||
for(; from > 0 && isspace(content[from]); --from) {}
|
||||
if(from < last)
|
||||
content.Remove(from+1, last-from);
|
||||
}
|
||||
|
||||
// END -- StringContent.cpp --
|
73
src/apps/cortex/Persistence/StringContent.h
Normal file
73
src/apps/cortex/Persistence/StringContent.h
Normal file
@ -0,0 +1,73 @@
|
||||
// StringContent.h
|
||||
// * PURPOSE
|
||||
// Implements IPersistent to store element content in
|
||||
// a BString and automatically strip leading/trailing
|
||||
// whitespace. Doesn't handle child elements; export
|
||||
// is not implemented (triggers an error).
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 7dec99 Begun
|
||||
|
||||
#ifndef __StringContent_H__
|
||||
#define __StringContent_H__
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
#include "XML.h"
|
||||
#include "cortex_defs.h"
|
||||
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class StringContent :
|
||||
public IPersistent {
|
||||
|
||||
public: // content access
|
||||
BString content;
|
||||
|
||||
public: // *** dtor, default ctors
|
||||
virtual ~StringContent() {}
|
||||
StringContent() {}
|
||||
StringContent(
|
||||
const char* c) : content(c) {}
|
||||
|
||||
public: // *** IPersistent
|
||||
|
||||
// EXPORT
|
||||
|
||||
virtual void xmlExportBegin(
|
||||
ExportContext& context) const;
|
||||
|
||||
virtual void xmlExportAttributes(
|
||||
ExportContext& context) const;
|
||||
|
||||
virtual void xmlExportContent(
|
||||
ExportContext& context) const;
|
||||
|
||||
virtual void xmlExportEnd(
|
||||
ExportContext& context) const;
|
||||
|
||||
// IMPORT
|
||||
|
||||
virtual void xmlImportBegin(
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportComplete(
|
||||
ImportContext& context);
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__StringContent_H__ */
|
187
src/apps/cortex/Persistence/Wrappers/FlatMessageIO.cpp
Normal file
187
src/apps/cortex/Persistence/Wrappers/FlatMessageIO.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
// FlatMessageIO.cpp
|
||||
// e.moon 6jul99
|
||||
|
||||
#include "FlatMessageIO.h"
|
||||
//#include "xml_export_utils.h"
|
||||
|
||||
#include "ExportContext.h"
|
||||
|
||||
#include <Debug.h>
|
||||
|
||||
// base64 encoding tools
|
||||
#include <E-mail.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** constants
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const char* const FlatMessageIO::s_element = "flat-BMessage";
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor/accessor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
FlatMessageIO::~FlatMessageIO() {
|
||||
if(m_ownMessage && m_message)
|
||||
delete m_message;
|
||||
}
|
||||
FlatMessageIO::FlatMessageIO() :
|
||||
m_ownMessage(true),
|
||||
m_message(0) {}
|
||||
FlatMessageIO::FlatMessageIO(const BMessage* message) :
|
||||
m_ownMessage(false),
|
||||
m_message(const_cast<BMessage*>(message)) {}
|
||||
|
||||
void FlatMessageIO::setMessage(BMessage* message) {
|
||||
if(m_ownMessage && m_message)
|
||||
delete m_message;
|
||||
m_ownMessage = false;
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** static setup method
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// call this method to install hooks for the tags needed by
|
||||
// FlatMessageIO into the given document type
|
||||
/*static*/
|
||||
void FlatMessageIO::AddTo(XML::DocumentType* pDocType) {
|
||||
pDocType->addMapping(new Mapping<FlatMessageIO>(s_element));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** IPersistent impl.
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
|
||||
void FlatMessageIO::xmlExportBegin(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.beginElement(s_element);
|
||||
}
|
||||
|
||||
void FlatMessageIO::xmlExportAttributes(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.writeAttr("encoding", "base64");
|
||||
}
|
||||
|
||||
void FlatMessageIO::xmlExportContent(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.beginContent();
|
||||
|
||||
// convert message to base64
|
||||
ASSERT(m_message);
|
||||
ssize_t flatSize = m_message->FlattenedSize();
|
||||
ASSERT(flatSize);
|
||||
char* flatData = new char[flatSize];
|
||||
status_t err = m_message->Flatten(flatData, flatSize);
|
||||
ASSERT(err == B_OK);
|
||||
|
||||
// make plenty of room for encoded content (encode_base64 adds newlines)
|
||||
ssize_t base64Size = ((flatSize * 3) / 2);
|
||||
char* base64Data = new char[base64Size];
|
||||
ssize_t base64Used = encode_base64(base64Data, flatData, flatSize);
|
||||
base64Data[base64Used] = '\0';
|
||||
|
||||
// write the data
|
||||
|
||||
const char* pos = base64Data;
|
||||
while(*pos) {
|
||||
ssize_t chunk = 0;
|
||||
const char* nextBreak = strchr(pos, '\n');
|
||||
if(!nextBreak)
|
||||
chunk = strlen(pos);
|
||||
else
|
||||
chunk = nextBreak - pos;
|
||||
|
||||
context.writeString(context.indentString());
|
||||
context.writeString(pos, chunk);
|
||||
context.writeString("\n");
|
||||
|
||||
pos += chunk;
|
||||
if(*pos == '\n')
|
||||
++pos;
|
||||
}
|
||||
|
||||
// clean up
|
||||
delete [] flatData;
|
||||
delete [] base64Data;
|
||||
}
|
||||
|
||||
void FlatMessageIO::xmlExportEnd(
|
||||
ExportContext& context) const {
|
||||
context.endElement();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void FlatMessageIO::xmlImportBegin(
|
||||
ImportContext& context) {
|
||||
|
||||
m_base64Data = "";
|
||||
}
|
||||
|
||||
void FlatMessageIO::xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) {
|
||||
if(!strcmp(key, "encoding")) {
|
||||
if(strcmp(value, "base64") != 0)
|
||||
context.reportError("Unexpected value of 'encoding'.");
|
||||
}
|
||||
}
|
||||
|
||||
void FlatMessageIO::xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) {
|
||||
m_base64Data.Append(data, length);
|
||||
}
|
||||
|
||||
void FlatMessageIO::xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context) {
|
||||
delete child;
|
||||
context.reportError("Unexpected child object.");
|
||||
}
|
||||
|
||||
void FlatMessageIO::xmlImportComplete(
|
||||
ImportContext& context) {
|
||||
|
||||
if(m_ownMessage && m_message)
|
||||
delete m_message;
|
||||
|
||||
// decode the message
|
||||
ssize_t decodedSize = m_base64Data.Length();
|
||||
char* decodedData = new char[decodedSize];
|
||||
decodedSize = decode_base64(
|
||||
decodedData, const_cast<char*>(m_base64Data.String()),
|
||||
m_base64Data.Length(), false);
|
||||
|
||||
m_message = new BMessage();
|
||||
m_ownMessage = true;
|
||||
|
||||
status_t err = m_message->Unflatten(decodedData);
|
||||
if(err < B_OK) {
|
||||
// decode failed; report error & clean up
|
||||
BString error = "Unflatten(): ";
|
||||
error << strerror(err);
|
||||
context.reportError(error.String());
|
||||
|
||||
delete m_message;
|
||||
m_message = 0;
|
||||
}
|
||||
|
||||
m_base64Data = "";
|
||||
delete [] decodedData;
|
||||
}
|
||||
|
||||
// END -- FlatMessageIO.cpp --
|
114
src/apps/cortex/Persistence/Wrappers/FlatMessageIO.h
Normal file
114
src/apps/cortex/Persistence/Wrappers/FlatMessageIO.h
Normal file
@ -0,0 +1,114 @@
|
||||
// FlatMessageIO.h
|
||||
// * PURPOSE
|
||||
// Efficient export/import of BMessages to and from
|
||||
// XML using the Cortex persistence library.
|
||||
// Messages are stored in flattened form.
|
||||
// * HISTORY
|
||||
// e.moon 6jul99 Begun.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <flat-BMessage
|
||||
// encoding = 'base64'>
|
||||
// nKJNFBlkn3lknbxlkfnbLKN/lknlknlDSLKn3lkn3l2k35234ljk234
|
||||
// lkdsg23823nlknsdlkbNDSLKBNlkn3lk23n4kl23n423lknLKENL+==
|
||||
// </flat-BMessage>
|
||||
|
||||
#ifndef __FlatMessageIO_H__
|
||||
#define __FlatMessageIO_H__
|
||||
|
||||
#include <Message.h>
|
||||
#include <String.h>
|
||||
#include "XML.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class FlatMessageIO :
|
||||
public IPersistent {
|
||||
|
||||
public: // *** ctor/dtor/accessor
|
||||
virtual ~FlatMessageIO();
|
||||
|
||||
FlatMessageIO();
|
||||
|
||||
// When given a message to export, this object does NOT take
|
||||
// responsibility for deleting it. It will, however, handle
|
||||
// deletion of an imported BMessage.
|
||||
|
||||
FlatMessageIO(const BMessage* message);
|
||||
void setMessage(BMessage* message);
|
||||
|
||||
// Returns 0 if no message has been set, and if no message has
|
||||
// been imported.
|
||||
|
||||
const BMessage* message() const { return m_message; }
|
||||
|
||||
// Returns true if the message will be automatically deleted.
|
||||
|
||||
bool ownsMessage() const { return m_ownMessage; }
|
||||
|
||||
public: // *** static setup method
|
||||
// call this method to install hooks for the tags needed by
|
||||
// FlatMessageIO into the given document type
|
||||
static void AddTo(XML::DocumentType* pDocType);
|
||||
|
||||
public: // *** XML formatting
|
||||
static const char* const s_element;
|
||||
static const uint16 s_encodeToMax = 72;
|
||||
static const uint16 s_encodeToMin = 24;
|
||||
|
||||
public: // *** IPersistent impl.
|
||||
|
||||
// virtual void xmlExport(
|
||||
// ostream& stream,
|
||||
// ExportContext& context) const;
|
||||
|
||||
// EXPORT:
|
||||
|
||||
void xmlExportBegin(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportAttributes(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportContent(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportEnd(
|
||||
ExportContext& context) const;
|
||||
|
||||
|
||||
// IMPORT:
|
||||
|
||||
virtual void xmlImportBegin(
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportComplete(
|
||||
ImportContext& context);
|
||||
|
||||
private: // *** members
|
||||
bool m_ownMessage;
|
||||
BMessage* m_message;
|
||||
|
||||
// encoded data is cached here during the import process
|
||||
BString m_base64Data;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
|
||||
#endif /*__FlatMessageIO_H__*/
|
1373
src/apps/cortex/Persistence/Wrappers/MediaFormatIO.cpp
Normal file
1373
src/apps/cortex/Persistence/Wrappers/MediaFormatIO.cpp
Normal file
File diff suppressed because it is too large
Load Diff
132
src/apps/cortex/Persistence/Wrappers/MediaFormatIO.h
Normal file
132
src/apps/cortex/Persistence/Wrappers/MediaFormatIO.h
Normal file
@ -0,0 +1,132 @@
|
||||
// MediaFormatIO.h
|
||||
// * PURPOSE
|
||||
// Wrapper class for media_format, providing XML
|
||||
// serialization support using the Cortex::XML package.
|
||||
//
|
||||
// * TO DO +++++
|
||||
// - import & export user data?
|
||||
// - import & export metadata?
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 1jul99 Begun.
|
||||
|
||||
#ifndef __MediaFormatIO_H__
|
||||
#define __MediaFormatIO_H__
|
||||
|
||||
#include "XML.h"
|
||||
#include <MediaDefs.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class MediaFormatIO :
|
||||
public IPersistent {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
virtual ~MediaFormatIO();
|
||||
|
||||
MediaFormatIO();
|
||||
MediaFormatIO(const media_format& format);
|
||||
|
||||
public: // *** accessors
|
||||
// returns B_OK if the object contains a valid format,
|
||||
// or B_ERROR if not.
|
||||
status_t getFormat(media_format& outFormat) const;
|
||||
|
||||
public: // *** static setup method
|
||||
// call this method to install hooks for the tags needed by
|
||||
// MediaFormatIO into the given document type
|
||||
static void AddTo(XML::DocumentType* pDocType);
|
||||
|
||||
public: // *** top-level tags
|
||||
|
||||
// these tags map directly to MediaFormatIO
|
||||
static const char* const s_multi_audio_tag;
|
||||
static const char* const s_raw_audio_tag;
|
||||
static const char* const s_raw_video_tag;
|
||||
static const char* const s_multistream_tag;
|
||||
static const char* const s_encoded_audio_tag;
|
||||
static const char* const s_encoded_video_tag;
|
||||
|
||||
public: // *** nested tags
|
||||
|
||||
static const char* const s_video_display_info_tag;
|
||||
|
||||
static const char* const s_multistream_flags_tag;
|
||||
static const char* const s_multistream_vid_info_tag;
|
||||
static const char* const s_multistream_avi_info_tag;
|
||||
|
||||
static const char* const s_multi_audio_info_tag;
|
||||
|
||||
static const char* const s_media_type_tag;
|
||||
|
||||
public: // *** IPersistent
|
||||
|
||||
// void xmlExport(
|
||||
// ostream& stream,
|
||||
// ExportContext& context) const;
|
||||
|
||||
// EXPORT:
|
||||
|
||||
void xmlExportBegin(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportAttributes(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportContent(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportEnd(
|
||||
ExportContext& context) const;
|
||||
|
||||
// IMPORT
|
||||
|
||||
void xmlImportBegin(
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportComplete(
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportChildBegin(
|
||||
const char* name,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportChildAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportChildContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportChildComplete(
|
||||
const char* name,
|
||||
ImportContext& context);
|
||||
|
||||
private: // *** state
|
||||
bool m_complete;
|
||||
media_format m_format;
|
||||
|
||||
BString m_mediaType;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__MediaFormatIO_H__*/
|
549
src/apps/cortex/Persistence/Wrappers/MessageIO.cpp
Normal file
549
src/apps/cortex/Persistence/Wrappers/MessageIO.cpp
Normal file
@ -0,0 +1,549 @@
|
||||
// MessageIO.cpp
|
||||
|
||||
#include "MessageIO.h"
|
||||
|
||||
#include <Debug.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// constants
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
const char* const MessageIO::s_element = "BMessage";
|
||||
|
||||
const char* _boolEl = "bool";
|
||||
const char* _int8El = "int8";
|
||||
const char* _int16El = "int16";
|
||||
const char* _int32El = "int32";
|
||||
const char* _int64El = "int64";
|
||||
const char* _floatEl = "float";
|
||||
const char* _doubleEl = "double";
|
||||
const char* _stringEl = "string";
|
||||
const char* _pointEl = "point";
|
||||
const char* _rectEl = "rect";
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor/accessor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
MessageIO::~MessageIO() {
|
||||
if(m_ownMessage && m_message)
|
||||
delete m_message;
|
||||
}
|
||||
|
||||
MessageIO::MessageIO() :
|
||||
m_ownMessage(true),
|
||||
m_message(0) {}
|
||||
|
||||
// When given a message to export, this object does NOT take
|
||||
// responsibility for deleting it. It will, however, handle
|
||||
// deletion of an imported BMessage.
|
||||
|
||||
MessageIO::MessageIO(
|
||||
const BMessage* message) :
|
||||
m_ownMessage(false),
|
||||
m_message(const_cast<BMessage*>(message)) {
|
||||
|
||||
ASSERT(m_message);
|
||||
}
|
||||
|
||||
void MessageIO::setMessage(
|
||||
BMessage* message) {
|
||||
|
||||
if(m_ownMessage && m_message)
|
||||
delete m_message;
|
||||
m_ownMessage = false;
|
||||
m_message = message;
|
||||
|
||||
ASSERT(m_message);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** static setup method
|
||||
// -------------------------------------------------------- //
|
||||
// call this method to install hooks for the tags needed by
|
||||
// MessageIO into the given document type
|
||||
|
||||
/*static*/
|
||||
void MessageIO::AddTo(
|
||||
XML::DocumentType* docType) {
|
||||
|
||||
docType->addMapping(new Mapping<MessageIO>(s_element));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// EXPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MessageIO::xmlExportBegin(
|
||||
ExportContext& context) const {
|
||||
|
||||
if(!m_message) {
|
||||
context.reportError("No message data to export.\n");
|
||||
return;
|
||||
}
|
||||
context.beginElement(s_element);
|
||||
}
|
||||
|
||||
void MessageIO::xmlExportAttributes(
|
||||
ExportContext& context) const {
|
||||
|
||||
if(m_message->what)
|
||||
context.writeAttr("what", m_message->what);
|
||||
if(m_name.Length())
|
||||
context.writeAttr("name", m_name.String());
|
||||
}
|
||||
|
||||
void MessageIO::xmlExportContent(
|
||||
ExportContext& context) const {
|
||||
|
||||
|
||||
ASSERT(m_message);
|
||||
status_t err;
|
||||
|
||||
// +++++ the approach:
|
||||
// 1) build a list of field names
|
||||
// 2) export fields sorted first by index, then name
|
||||
|
||||
typedef vector<BString> field_set;
|
||||
field_set fields;
|
||||
|
||||
char* name;
|
||||
type_code type;
|
||||
int32 count;
|
||||
for(
|
||||
int32 n = 0;
|
||||
m_message->GetInfo(B_ANY_TYPE, n, &name, &type, &count) == B_OK;
|
||||
++n) {
|
||||
fields.push_back(name);
|
||||
}
|
||||
|
||||
if(!fields.size())
|
||||
return;
|
||||
|
||||
context.beginContent();
|
||||
|
||||
bool done = false;
|
||||
for(int32 n = 0; !done; ++n) {
|
||||
|
||||
done = true;
|
||||
|
||||
for(
|
||||
uint32 fieldIndex = 0;
|
||||
fieldIndex < fields.size();
|
||||
++fieldIndex) {
|
||||
|
||||
if(m_message->GetInfo(
|
||||
fields[fieldIndex].String(),
|
||||
&type,
|
||||
&count) < B_OK || n >= count)
|
||||
continue;
|
||||
|
||||
// found a field at the current index, so don't give up
|
||||
done = false;
|
||||
|
||||
err = _exportField(
|
||||
context,
|
||||
m_message,
|
||||
type,
|
||||
fields[fieldIndex].String(),
|
||||
n);
|
||||
|
||||
if(err < B_OK) {
|
||||
BString errText;
|
||||
errText << "Couldn't export field '" << fields[fieldIndex] <<
|
||||
"' index " << n << ": " << strerror(err) << "\n";
|
||||
context.reportError(errText.String());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageIO::xmlExportEnd(
|
||||
ExportContext& context) const {
|
||||
context.endElement();
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// IMPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void MessageIO::xmlImportBegin(
|
||||
ImportContext& context) {
|
||||
|
||||
// create the message
|
||||
if(m_message) {
|
||||
if(m_ownMessage)
|
||||
delete m_message;
|
||||
}
|
||||
m_message = new BMessage();
|
||||
m_name.SetTo("");
|
||||
}
|
||||
|
||||
void MessageIO::xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) {
|
||||
|
||||
ASSERT(m_message);
|
||||
|
||||
if(!strcmp(key, "what"))
|
||||
m_message->what = atol(value);
|
||||
else if(!strcmp(key, "name"))
|
||||
m_name.SetTo(value);
|
||||
}
|
||||
|
||||
void MessageIO::xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) {}
|
||||
|
||||
void MessageIO::xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context) {
|
||||
|
||||
ASSERT(m_message);
|
||||
|
||||
if(strcmp(context.element(), s_element) != 0) {
|
||||
context.reportError("Unexpected child element.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageIO* childMessageIO = dynamic_cast<MessageIO*>(child);
|
||||
ASSERT(childMessageIO);
|
||||
|
||||
m_message->AddMessage(
|
||||
childMessageIO->m_name.String(),
|
||||
childMessageIO->m_message);
|
||||
}
|
||||
|
||||
void MessageIO::xmlImportComplete(
|
||||
ImportContext& context) {}
|
||||
|
||||
void MessageIO::xmlImportChildBegin(
|
||||
const char* name,
|
||||
ImportContext& context) {
|
||||
|
||||
// sanity checks
|
||||
|
||||
ASSERT(m_message);
|
||||
|
||||
if(strcmp(context.parentElement(), s_element) != 0) {
|
||||
context.reportError("Unexpected parent element.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!_isValidMessageElement(context.element())) {
|
||||
context.reportError("Invalid message field element.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
m_fieldData.SetTo("");
|
||||
}
|
||||
|
||||
void MessageIO::xmlImportChildAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) {
|
||||
|
||||
if(!strcmp(key, "name"))
|
||||
m_fieldName.SetTo(value);
|
||||
if(!strcmp(key, "value"))
|
||||
m_fieldData.SetTo(value);
|
||||
}
|
||||
|
||||
void MessageIO::xmlImportChildContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) {
|
||||
|
||||
m_fieldData.Append(data, length);
|
||||
}
|
||||
|
||||
void MessageIO::xmlImportChildComplete(
|
||||
const char* name,
|
||||
ImportContext& context) {
|
||||
|
||||
ASSERT(m_message);
|
||||
|
||||
status_t err = _importField(
|
||||
m_message,
|
||||
name,
|
||||
m_fieldName.String(),
|
||||
m_fieldData.String());
|
||||
if(err < B_OK) {
|
||||
context.reportWarning("Invalid field data.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// implementation
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
bool MessageIO::_isValidMessageElement(
|
||||
const char* element) const {
|
||||
|
||||
if(!strcmp(element, _boolEl)) return true;
|
||||
if(!strcmp(element, _int8El)) return true;
|
||||
if(!strcmp(element, _int16El)) return true;
|
||||
if(!strcmp(element, _int32El)) return true;
|
||||
if(!strcmp(element, _int64El)) return true;
|
||||
if(!strcmp(element, _floatEl)) return true;
|
||||
if(!strcmp(element, _doubleEl)) return true;
|
||||
if(!strcmp(element, _stringEl)) return true;
|
||||
if(!strcmp(element, _pointEl)) return true;
|
||||
if(!strcmp(element, _rectEl)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
status_t MessageIO::_importField(
|
||||
BMessage* message,
|
||||
const char* element,
|
||||
const char* name,
|
||||
const char* data) {
|
||||
|
||||
// skip leading whitespace
|
||||
while(*data && isspace(*data)) ++data;
|
||||
|
||||
if(!strcmp(element, _boolEl)) {
|
||||
bool v;
|
||||
if(!strcmp(data, "true") || !strcmp(data, "1"))
|
||||
v = true;
|
||||
else if(!strcmp(data, "false") || !strcmp(data, "0"))
|
||||
v = false;
|
||||
else
|
||||
return B_BAD_VALUE;
|
||||
return message->AddBool(name, v);
|
||||
}
|
||||
|
||||
if(!strcmp(element, _int8El)) {
|
||||
int8 v = atoi(data);
|
||||
return message->AddInt8(name, v);
|
||||
}
|
||||
if(!strcmp(element, _int16El)) {
|
||||
int16 v = atoi(data);
|
||||
return message->AddInt16(name, v);
|
||||
}
|
||||
if(!strcmp(element, _int32El)) {
|
||||
int32 v = atol(data);
|
||||
return message->AddInt32(name, v);
|
||||
}
|
||||
if(!strcmp(element, _int64El)) {
|
||||
// int64 v = atoll(data);
|
||||
int64 v = strtoll(data, 0, 10);
|
||||
return message->AddInt64(name, v);
|
||||
}
|
||||
if(!strcmp(element, _floatEl)) {
|
||||
float v = (float)atof(data);
|
||||
return message->AddFloat(name, v);
|
||||
}
|
||||
if(!strcmp(element, _doubleEl)) {
|
||||
double v = atof(data);
|
||||
return message->AddDouble(name, v);
|
||||
}
|
||||
|
||||
if(!strcmp(element, _stringEl)) {
|
||||
// +++++ chomp leading/trailing whitespace?
|
||||
|
||||
return message->AddString(name, data);
|
||||
}
|
||||
|
||||
if(!strcmp(element, _pointEl)) {
|
||||
BPoint p;
|
||||
const char* ystart = strchr(data, ',');
|
||||
if(!ystart)
|
||||
return B_BAD_VALUE;
|
||||
++ystart;
|
||||
if(!*ystart)
|
||||
return B_BAD_VALUE;
|
||||
p.x = (float)atof(data);
|
||||
p.y = (float)atof(ystart);
|
||||
|
||||
return message->AddPoint(name, p);
|
||||
}
|
||||
|
||||
if(!strcmp(element, _rectEl)) {
|
||||
BRect r;
|
||||
const char* topstart = strchr(data, ',');
|
||||
if(!topstart)
|
||||
return B_BAD_VALUE;
|
||||
++topstart;
|
||||
if(!*topstart)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
const char* rightstart = strchr(topstart, ',');
|
||||
if(!rightstart)
|
||||
return B_BAD_VALUE;
|
||||
++rightstart;
|
||||
if(!*rightstart)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
const char* bottomstart = strchr(rightstart, ',');
|
||||
if(!bottomstart)
|
||||
return B_BAD_VALUE;
|
||||
++bottomstart;
|
||||
if(!*bottomstart)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
r.left = (float)atof(data);
|
||||
r.top = (float)atof(topstart);
|
||||
r.right = (float)atof(rightstart);
|
||||
r.bottom = (float)atof(bottomstart);
|
||||
|
||||
return message->AddRect(name, r);
|
||||
}
|
||||
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
status_t MessageIO::_exportField(
|
||||
ExportContext& context,
|
||||
BMessage* message,
|
||||
type_code type,
|
||||
const char* name,
|
||||
int32 index) const {
|
||||
|
||||
status_t err;
|
||||
BString elementName;
|
||||
BString content;
|
||||
|
||||
switch(type) {
|
||||
case B_BOOL_TYPE: {
|
||||
bool v;
|
||||
err = message->FindBool(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _boolEl;
|
||||
content = (v ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
|
||||
case B_INT8_TYPE: {
|
||||
int8 v;
|
||||
err = message->FindInt8(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _int8El;
|
||||
content << (int32)v;
|
||||
break;
|
||||
}
|
||||
|
||||
case B_INT16_TYPE: {
|
||||
int16 v;
|
||||
err = message->FindInt16(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _int16El;
|
||||
content << (int32)v;
|
||||
break;
|
||||
}
|
||||
|
||||
case B_INT32_TYPE: {
|
||||
int32 v;
|
||||
err = message->FindInt32(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _int32El;
|
||||
content << v;
|
||||
break;
|
||||
}
|
||||
|
||||
case B_INT64_TYPE: {
|
||||
int64 v;
|
||||
err = message->FindInt64(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _int64El;
|
||||
content << v;
|
||||
break;
|
||||
}
|
||||
|
||||
case B_FLOAT_TYPE: {
|
||||
float v;
|
||||
err = message->FindFloat(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _floatEl;
|
||||
content << v; // +++++ need adjustable precision!
|
||||
break;
|
||||
}
|
||||
|
||||
case B_DOUBLE_TYPE: {
|
||||
double v;
|
||||
err = message->FindDouble(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _doubleEl;
|
||||
content << (float)v; // +++++ need adjustable precision!
|
||||
break;
|
||||
}
|
||||
|
||||
case B_STRING_TYPE: {
|
||||
const char* v;
|
||||
err = message->FindString(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _stringEl;
|
||||
content = v;
|
||||
break;
|
||||
}
|
||||
|
||||
case B_POINT_TYPE: {
|
||||
BPoint v;
|
||||
err = message->FindPoint(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _pointEl;
|
||||
content << v.x << ", " << v.y;
|
||||
break;
|
||||
}
|
||||
|
||||
case B_RECT_TYPE: {
|
||||
BRect v;
|
||||
err = message->FindRect(name, index, &v);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
elementName = _rectEl;
|
||||
content << v.left << ", " << v.top << ", " <<
|
||||
v.right << ", " << v.bottom;
|
||||
break;
|
||||
}
|
||||
|
||||
case B_MESSAGE_TYPE: {
|
||||
BMessage m;
|
||||
err = message->FindMessage(name, index, &m);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
// write child message
|
||||
MessageIO io(&m);
|
||||
io.m_name = name;
|
||||
return context.writeObject(&io);
|
||||
}
|
||||
|
||||
default:
|
||||
return B_BAD_TYPE;
|
||||
}
|
||||
|
||||
// spew the element
|
||||
context.beginElement(elementName.String());
|
||||
context.writeAttr("name", name);
|
||||
context.writeAttr("value", content.String());
|
||||
// context.beginContent();
|
||||
// context.writeString(content);
|
||||
context.endElement();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
// END -- MessageIO.cpp --
|
145
src/apps/cortex/Persistence/Wrappers/MessageIO.h
Normal file
145
src/apps/cortex/Persistence/Wrappers/MessageIO.h
Normal file
@ -0,0 +1,145 @@
|
||||
// MessageIO.h
|
||||
// * PURPOSE
|
||||
// Export/import of BMessages to and from
|
||||
// XML using the Cortex persistence library.
|
||||
// Messages are stored in a user-readable form.
|
||||
//
|
||||
// TO DO +++++
|
||||
// - sanity-check string values (filter/escape single quotes)
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 1dec99 Begun.
|
||||
|
||||
#ifndef __MessageIO_H__
|
||||
#define __MessageIO_H__
|
||||
|
||||
#include <Message.h>
|
||||
#include <String.h>
|
||||
#include "XML.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class MessageIO :
|
||||
public IPersistent {
|
||||
|
||||
public: // *** ctor/dtor/accessor
|
||||
virtual ~MessageIO();
|
||||
|
||||
MessageIO(); //nyi
|
||||
|
||||
// When given a message to export, this object does NOT take
|
||||
// responsibility for deleting it. It will, however, handle
|
||||
// deletion of an imported BMessage.
|
||||
|
||||
MessageIO(
|
||||
const BMessage* message);
|
||||
void setMessage(
|
||||
BMessage* message);
|
||||
|
||||
// Returns 0 if no message has been set, and if no message has
|
||||
// been imported.
|
||||
|
||||
const BMessage* message() const { return m_message; }
|
||||
|
||||
// Returns true if the message will be automatically deleted.
|
||||
|
||||
bool ownsMessage() const { return m_ownMessage; }
|
||||
|
||||
public: // *** static setup method
|
||||
// call this method to install hooks for the tags needed by
|
||||
// MessageIO into the given document type
|
||||
static void AddTo(
|
||||
XML::DocumentType* docType);
|
||||
|
||||
public: // *** XML formatting
|
||||
static const char* const s_element;
|
||||
|
||||
public: // *** IPersistent impl.
|
||||
|
||||
// EXPORT:
|
||||
|
||||
void xmlExportBegin(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportAttributes(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportContent(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportEnd(
|
||||
ExportContext& context) const;
|
||||
|
||||
|
||||
// IMPORT:
|
||||
|
||||
virtual void xmlImportBegin(
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportComplete(
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChildBegin(
|
||||
const char* name,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChildAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChildContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChildComplete(
|
||||
const char* name,
|
||||
ImportContext& context);
|
||||
|
||||
private: // *** members
|
||||
bool m_ownMessage;
|
||||
BMessage* m_message;
|
||||
|
||||
// name of the message (if used to import a nested BMessage)
|
||||
BString m_name;
|
||||
|
||||
// current field
|
||||
BString m_fieldName;
|
||||
BString m_fieldData;
|
||||
|
||||
bool _isValidMessageElement(
|
||||
const char* element) const;
|
||||
|
||||
status_t _importField(
|
||||
BMessage* message,
|
||||
const char* element,
|
||||
const char* name,
|
||||
const char* data);
|
||||
|
||||
status_t _exportField(
|
||||
ExportContext& context,
|
||||
BMessage* message,
|
||||
type_code type,
|
||||
const char* name,
|
||||
int32 index) const;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
|
||||
#endif /*__MessageIO_H__*/
|
50
src/apps/cortex/Persistence/Wrappers/color_spaces
Normal file
50
src/apps/cortex/Persistence/Wrappers/color_spaces
Normal file
@ -0,0 +1,50 @@
|
||||
B_RGB32
|
||||
B_RGBA32
|
||||
B_RGB24
|
||||
B_RGB16
|
||||
B_RGB15
|
||||
B_RGBA15
|
||||
B_CMAP8
|
||||
B_GRAY8
|
||||
B_GRAY1
|
||||
B_RGB32_BIG
|
||||
B_RGBA32_BIG
|
||||
B_RGB24_BIG
|
||||
B_RGB16_BIG
|
||||
B_RGB15_BIG
|
||||
B_RGBA15_BIG
|
||||
B_RGB32_LITTLE
|
||||
B_RGBA32_LITTLE
|
||||
B_RGB24_LITTLE
|
||||
B_RGB16_LITTLE
|
||||
B_RGB15_LITTLE
|
||||
B_RGBA15_LITTLE
|
||||
B_YCbCr422
|
||||
B_YCbCr411
|
||||
B_YCbCr444
|
||||
B_YCbCr420
|
||||
B_YUV422
|
||||
B_YUV411
|
||||
B_YUV444
|
||||
B_YUV420
|
||||
B_YUV9
|
||||
B_YUV12
|
||||
B_UVL24
|
||||
B_UVL32
|
||||
B_UVLA32
|
||||
B_LAB24
|
||||
B_LAB32
|
||||
B_LABA32
|
||||
B_HSI24
|
||||
B_HSI32
|
||||
B_HSIA32
|
||||
B_HSV24
|
||||
B_HSV32
|
||||
B_HSVA32
|
||||
B_HLS24
|
||||
B_HLS32
|
||||
B_HLSA32
|
||||
B_CMY24
|
||||
B_CMY32
|
||||
B_CMYA32
|
||||
B_CMYK32
|
87
src/apps/cortex/Persistence/Wrappers/media_format_schema
Normal file
87
src/apps/cortex/Persistence/Wrappers/media_format_schema
Normal file
@ -0,0 +1,87 @@
|
||||
<raw_audio_format
|
||||
frame_rate = '44100.0'
|
||||
channel_count = '1'
|
||||
format = B_AUDIO_UCHAR | B_AUDIO_SHORT | B_AUDIO_FLOAT | B_AUDIO_INT
|
||||
byte_order = B_MEDIA_BIG_ENDIAN | B_MEDIA_LITTLE_ENDIAN
|
||||
buffer_size = '1024'
|
||||
/>
|
||||
|
||||
<raw_video_format
|
||||
field_rate = '30'
|
||||
interlace = '1'
|
||||
first_active = '0'
|
||||
last_active = '239'
|
||||
orientation = B_VIDEO_TOP_LEFT_RIGHT | B_VIDEO_BOTTOM_LEFT_RIGHT
|
||||
pixel_width_aspect = '3'
|
||||
pixel_height_aspect = '4'>
|
||||
|
||||
<video_display_info
|
||||
format = B_RGB32 | B_RGBA32 | B_RGB24 | B_RGB16 | B_RGB15 | B_RGBA15 | B_CMAP8 | B_GRAY8 | B_GRAY1 | <val>
|
||||
line_width = '320'
|
||||
line_count = '240'
|
||||
bytes_per_row = '1280'
|
||||
pixel_offset = '0'
|
||||
line_offset = '0'
|
||||
/>
|
||||
</raw_video_format>
|
||||
|
||||
<multistream_format
|
||||
format = B_ANY | B_VID | B_AVI | B_MPEG1 | B_MPEG2 | B_QUICKTIME | [val]
|
||||
avg_bit_rate = '0.0'
|
||||
max_bit_rate = '0.0'
|
||||
avg_chunk_size = '0'
|
||||
max_chunk_size = '0'>
|
||||
|
||||
<multistream_flags
|
||||
header_has_flags = '1' | '0' [def=0]
|
||||
clean_buffers = '1' | '0' [def=0]
|
||||
homogenous_buffers = '1' | '0' [def=0]
|
||||
/>
|
||||
|
||||
<!-- either this element -->
|
||||
<multistream_vid_info
|
||||
frame_rate = '0.0'
|
||||
width = '0'
|
||||
height = '0'
|
||||
space = [color space]
|
||||
sampling_rate = '0.0'
|
||||
sample_format = B_UNDEFINED_SAMPLES | B_LINEAR_SAMPLES | B_FLOAT_SAMPLES | B_MULAW_SAMPLES
|
||||
byte_order = B_MEDIA_BIG_ENDIAN | B_MEDIA_LITTLE_ENDIAN
|
||||
channel_count = '0'
|
||||
/>
|
||||
|
||||
<!-- or this one -->
|
||||
<multistream_avi_info
|
||||
us_per_frame = '0'
|
||||
width = '0'
|
||||
height = '0'>
|
||||
|
||||
<!-- up to 5 types may be nested -->
|
||||
<media_type>B_MEDIA_RAW_AUDIO</media_type>
|
||||
<media_type>B_MEDIA_RAW_AUDIO</media_type>
|
||||
<media_type>B_MEDIA_RAW_AUDIO</media_type>
|
||||
<media_type>B_MEDIA_RAW_AUDIO</media_type>
|
||||
<media_type>B_MEDIA_RAW_AUDIO</media_type>
|
||||
</multistream_avi_info>
|
||||
</multistream_format>
|
||||
|
||||
<encoded_audio_format
|
||||
encoding = B_ANY
|
||||
bit_rate = '0.0'
|
||||
frame_size = '0'>
|
||||
|
||||
<!-- the output format -->
|
||||
<raw_audio_format ... />
|
||||
</encoded_audio_format>
|
||||
|
||||
<encoded_video_format
|
||||
encoding = B_ANY
|
||||
avg_bit_rate = '0.0'
|
||||
max_bit_rate = '0.0'
|
||||
frame_size = '0'
|
||||
forward_history = '0'
|
||||
backward_history = '0'>
|
||||
|
||||
<!-- output format -->
|
||||
<raw_video_format ... />
|
||||
</encoded_video_format>
|
281
src/apps/cortex/Persistence/XML.cpp
Normal file
281
src/apps/cortex/Persistence/XML.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
// XML.cpp
|
||||
// e.moon 1jul99
|
||||
|
||||
#include "XML.h"
|
||||
#include "Importer.h"
|
||||
|
||||
#include <Autolock.h>
|
||||
#include <Debug.h>
|
||||
|
||||
#include "array_delete.h"
|
||||
#include "set_tools.h"
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// static members
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
XML::doc_type_map XML::s_docTypeMap;
|
||||
BLocker XML::s_docTypeLock("XML::s_docTypeLock");
|
||||
|
||||
const BMimeType XML::DocumentType::s_defaultMimeType("text/xml");
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// document type operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// takes responsibility for the given type object
|
||||
|
||||
/*static*/
|
||||
void XML::AddDocumentType(
|
||||
XML::DocumentType* type) {
|
||||
|
||||
ASSERT(type);
|
||||
BAutolock _l(s_docTypeLock);
|
||||
|
||||
// s_docTypeMap.insert(
|
||||
// make_pair(type->rootElement, type));
|
||||
s_docTypeMap.insert(
|
||||
pair<const BString, XML::DocumentType*>(type->rootElement, type));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// import/export operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// identify object in stream
|
||||
// returns:
|
||||
// - B_OK on success, or
|
||||
// - B_BAD_TYPE if no document type matches the root
|
||||
// element of the stream, or
|
||||
// - B_IO_ERROR if the document is malformed, or if a
|
||||
// read error occurs.
|
||||
|
||||
/*static*/
|
||||
status_t XML::Identify(
|
||||
BDataIO* stream,
|
||||
DocumentType** outType,
|
||||
list<BString>* outErrors) {
|
||||
|
||||
ASSERT(stream);
|
||||
|
||||
// prepare the input buffer
|
||||
const uint32 bufferSize = 4096;
|
||||
char* buffer = new char[bufferSize];
|
||||
array_delete<char> _d(buffer);
|
||||
|
||||
// prepare an Importer to figure document type (from first element)
|
||||
Importer i(*outErrors);
|
||||
i.setIdentifyMode();
|
||||
|
||||
while(
|
||||
i.context().state() == ImportContext::PARSING) {
|
||||
|
||||
// read chunk (no 0 terminator)
|
||||
ssize_t readCount = stream->Read(buffer, bufferSize);
|
||||
if(readCount == 0)
|
||||
// done
|
||||
break;
|
||||
else if(readCount < 0) {
|
||||
// error
|
||||
BString err = "Read error: '";
|
||||
err << strerror(readCount) << "'; ABORTING.";
|
||||
outErrors->push_back(err);
|
||||
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
// feed to parser
|
||||
if(!i.parseBuffer(
|
||||
buffer, readCount, !stream)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return found type
|
||||
if(i.docType()) {
|
||||
*outType = i.docType();
|
||||
return B_OK;
|
||||
}
|
||||
else return B_BAD_TYPE;
|
||||
}
|
||||
|
||||
// read the root object from the given stream
|
||||
|
||||
/*static*/
|
||||
status_t XML::Read(
|
||||
BDataIO* stream,
|
||||
IPersistent** outObject,
|
||||
list<BString>* outErrors) {
|
||||
|
||||
Importer i(*outErrors);
|
||||
status_t err = _DoRead(stream, i, outErrors);
|
||||
if(err == B_OK) {
|
||||
// return completed object
|
||||
ASSERT(i.target());
|
||||
*outObject = i.target();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
status_t XML::Read(
|
||||
BDataIO* stream,
|
||||
IPersistent** outObject,
|
||||
ImportContext* context) {
|
||||
|
||||
Importer i(context);
|
||||
status_t err = _DoRead(stream, i, &context->errors());
|
||||
if(err == B_OK) {
|
||||
// return completed object
|
||||
ASSERT(i.target());
|
||||
*outObject = i.target();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// [e.moon 26nov99]
|
||||
// populate the provided root object from the given
|
||||
// XML stream. you need to give the expected root
|
||||
// (document) element name corresponding to the
|
||||
// item you provide.
|
||||
// returns:
|
||||
// - B_OK on success, or
|
||||
// - B_IO_ERROR if the document is malformed, or if a
|
||||
// read error occurs, or
|
||||
// - B_ERROR
|
||||
|
||||
/*static*/
|
||||
status_t XML::Read(
|
||||
BDataIO* stream,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* documentType,
|
||||
list<BString>* outErrors) {
|
||||
|
||||
Importer i(*outErrors, rootObject, documentType);
|
||||
return _DoRead(stream, i, outErrors);
|
||||
}
|
||||
|
||||
/*static*/
|
||||
status_t XML::Read(
|
||||
BDataIO* stream,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* documentType,
|
||||
ImportContext* context) {
|
||||
|
||||
Importer i(context, rootObject, documentType);
|
||||
return _DoRead(stream, i, &context->errors());
|
||||
}
|
||||
|
||||
/*static*/
|
||||
status_t XML::_DoRead(
|
||||
BDataIO* stream,
|
||||
Importer& i,
|
||||
list<BString>* outErrors) {
|
||||
|
||||
// prepare the input buffer
|
||||
const uint32 bufferSize = 4096;
|
||||
char* buffer = new char[bufferSize];
|
||||
array_delete<char> _d(buffer);
|
||||
|
||||
while(
|
||||
i.context().state() == ImportContext::PARSING) {
|
||||
|
||||
// read chunk (no 0 terminator)
|
||||
ssize_t readCount = stream->Read(buffer, bufferSize);
|
||||
if(readCount == 0)
|
||||
// done
|
||||
break;
|
||||
else if(readCount < 0) {
|
||||
// error
|
||||
BString err = "Read error: '";
|
||||
err << strerror(readCount) << "'; ABORTING.";
|
||||
outErrors->push_back(err);
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
// feed to parser
|
||||
if(!i.parseBuffer(
|
||||
buffer, readCount, !stream)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status_t err = B_ERROR;
|
||||
if(i.context().state() == ImportContext::COMPLETE)
|
||||
err = B_OK;
|
||||
|
||||
// clean up
|
||||
return err;
|
||||
}
|
||||
|
||||
// write the given object to the given stream
|
||||
|
||||
/*static*/
|
||||
status_t XML::Write(
|
||||
BDataIO* stream,
|
||||
IPersistent* object,
|
||||
BString* outError) {
|
||||
|
||||
ASSERT(object);
|
||||
|
||||
ExportContext context(stream);
|
||||
status_t err = context.writeObject(object);
|
||||
if(err < B_OK)
|
||||
*outError = context.errorText();
|
||||
return err;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// XML::DocumentType
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
class _NullMapping : public XMLElementMapping {
|
||||
public:
|
||||
_NullMapping(
|
||||
const char* _element) :
|
||||
XMLElementMapping(_element) {}
|
||||
|
||||
IPersistent* create() const { return 0; }
|
||||
};
|
||||
|
||||
XML::DocumentType::~DocumentType() {
|
||||
// clean up
|
||||
ptr_set_delete(m_mappingSet.begin(), m_mappingSet.end());
|
||||
}
|
||||
|
||||
XML::DocumentType::DocumentType(
|
||||
const char* _rootElement,
|
||||
const char* _mimeType) :
|
||||
rootElement(_rootElement),
|
||||
mimeType(_mimeType ? _mimeType : s_defaultMimeType.Type()) {}
|
||||
|
||||
// *** 'factory' interface
|
||||
|
||||
// The DocumentType takes ownership of the given mapping
|
||||
// object. If a mapping for the element already exists,
|
||||
// the provided object is deleted and the method returns
|
||||
// B_NAME_IN_USE.
|
||||
status_t XML::DocumentType::addMapping(
|
||||
XMLElementMapping* mapping) {
|
||||
|
||||
pair<mapping_set::iterator, bool> ret = m_mappingSet.insert(mapping);
|
||||
if(!ret.second) {
|
||||
delete mapping;
|
||||
return B_NAME_IN_USE;
|
||||
} else
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
IPersistent* XML::DocumentType::objectFor(
|
||||
const char* element) {
|
||||
|
||||
_NullMapping m(element);
|
||||
mapping_set::iterator it = m_mappingSet.find(&m);
|
||||
|
||||
return (it != m_mappingSet.end()) ?
|
||||
(*it)->create() : 0;
|
||||
}
|
||||
|
||||
// END -- XML.cpp --
|
171
src/apps/cortex/Persistence/XML.h
Normal file
171
src/apps/cortex/Persistence/XML.h
Normal file
@ -0,0 +1,171 @@
|
||||
// XML.h
|
||||
// * PURPOSE
|
||||
// A central access point for Cortex's XML import/export
|
||||
// services. A completely static class.
|
||||
//
|
||||
// * RESPONSIBILITIES
|
||||
// - Maintain a set of XML::DocumentType objects, each
|
||||
// containing the information needed to import a particular
|
||||
// kind of XML document into native objects.
|
||||
//
|
||||
// - Provide a simple API for importing and exporting
|
||||
// IPersistent objects from/to streams.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 4oct99 API changes:
|
||||
// - BDataIO used in place of standard streams.
|
||||
// - Factory folded into XML::DocumentType
|
||||
//
|
||||
// e.moon 29jun99 Begun
|
||||
|
||||
#ifndef __XML_H__
|
||||
#define __XML_H__
|
||||
|
||||
#include "IPersistent.h"
|
||||
#include "XMLElementMapping.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <DataIO.h>
|
||||
#include <Locker.h>
|
||||
#include <Mime.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// class XML
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
class XML {
|
||||
// internal import helper
|
||||
friend class Importer;
|
||||
|
||||
public: // *** types
|
||||
class DocumentType;
|
||||
|
||||
public: // *** document type operations
|
||||
|
||||
// takes responsibility for the given type object
|
||||
static void AddDocumentType(
|
||||
XML::DocumentType* type);
|
||||
|
||||
public: // *** import/export operations
|
||||
|
||||
// identify object in stream.
|
||||
// returns:
|
||||
// - B_OK on success, or
|
||||
// - B_BAD_TYPE if no document type matches the root
|
||||
// element of the stream, or
|
||||
// - B_IO_ERROR if the document is malformed, or if a
|
||||
// read error occurs.
|
||||
|
||||
static status_t Identify(
|
||||
BDataIO* stream,
|
||||
DocumentType** outType,
|
||||
list<BString>* outErrors);
|
||||
|
||||
// create & populate the root object from the given
|
||||
// XML stream.
|
||||
// returns:
|
||||
// - B_OK on success, or
|
||||
// - B_IO_ERROR if the document is malformed, or if a
|
||||
// read error occurs, or
|
||||
// - B_ERROR
|
||||
|
||||
static status_t Read(
|
||||
BDataIO* stream,
|
||||
IPersistent** outObject,
|
||||
list<BString>* outErrors);
|
||||
|
||||
static status_t Read(
|
||||
BDataIO* stream,
|
||||
IPersistent** outObject,
|
||||
ImportContext* context); //nyi
|
||||
|
||||
// [e.moon 26nov99]
|
||||
// populate the provided root object from the given
|
||||
// XML stream. you need to provide a document type
|
||||
// that corresponds to the given object.
|
||||
// returns:
|
||||
// - B_OK on success, or
|
||||
// - B_IO_ERROR if the document is malformed, or if a
|
||||
// read error occurs, or
|
||||
// - B_ERROR
|
||||
|
||||
static status_t Read(
|
||||
BDataIO* stream,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* documentType,
|
||||
list<BString>* outErrors);
|
||||
|
||||
static status_t Read(
|
||||
BDataIO* stream,
|
||||
IPersistent* rootObject,
|
||||
XML::DocumentType* documentType,
|
||||
ImportContext* context);
|
||||
|
||||
// create an ExportContext and use it to write the given object
|
||||
// to the given stream
|
||||
|
||||
static status_t Write(
|
||||
BDataIO* stream,
|
||||
IPersistent* object,
|
||||
BString* outError);
|
||||
|
||||
private: // static members
|
||||
|
||||
typedef map<BString, DocumentType*> doc_type_map;
|
||||
|
||||
static doc_type_map s_docTypeMap;
|
||||
static BLocker s_docTypeLock;
|
||||
|
||||
private: // implementation
|
||||
static status_t _DoRead(
|
||||
BDataIO* stream,
|
||||
Importer& i,
|
||||
list<BString>* outErrors); //nyi
|
||||
};
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// class XML::DocumentType
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
class XML::DocumentType {
|
||||
public: // *** constant members
|
||||
const BString rootElement;
|
||||
const BMimeType mimeType;
|
||||
|
||||
static const BMimeType s_defaultMimeType;
|
||||
|
||||
public: // *** ctor/dtors
|
||||
virtual ~DocumentType();
|
||||
|
||||
DocumentType(
|
||||
const char* _rootElement,
|
||||
const char* _mimeType=0);
|
||||
|
||||
public: // *** 'factory' interface
|
||||
|
||||
// The DocumentType takes ownership of the given mapping
|
||||
// object. If a mapping for the element already exists,
|
||||
// the provided object is deleted and the method returns
|
||||
// B_NAME_IN_USE.
|
||||
virtual status_t addMapping(
|
||||
XMLElementMapping* mapping);
|
||||
|
||||
// returns 0 if no mapping found for the given element
|
||||
virtual IPersistent* objectFor(
|
||||
const char* element);
|
||||
|
||||
private: // implementation
|
||||
|
||||
typedef set<XMLElementMapping*, _mapping_ptr_less> mapping_set;
|
||||
mapping_set m_mappingSet;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
|
||||
#endif /*__XML_H__*/
|
62
src/apps/cortex/Persistence/XMLElementMapping.h
Normal file
62
src/apps/cortex/Persistence/XMLElementMapping.h
Normal file
@ -0,0 +1,62 @@
|
||||
// XMLElementMapping.h
|
||||
// * PURPOSE
|
||||
// A simple class (template implementing a non-template
|
||||
// base interface) to encapsulate the operation:
|
||||
// "make an object for this XML element."
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 04oct99 Begun.
|
||||
|
||||
#ifndef __XMLElementMapping_H__
|
||||
#define __XMLElementMapping_H__
|
||||
|
||||
#include <functional>
|
||||
#include <String.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class IPersistent;
|
||||
|
||||
// The base class:
|
||||
|
||||
class XMLElementMapping {
|
||||
public: // *** data
|
||||
const BString element;
|
||||
|
||||
public: // *** interface
|
||||
virtual ~XMLElementMapping() {}
|
||||
XMLElementMapping(
|
||||
const char* _element) :
|
||||
element(_element) {}
|
||||
|
||||
virtual IPersistent* create() const =0;
|
||||
};
|
||||
|
||||
// The template:
|
||||
|
||||
template <class T>
|
||||
class Mapping :
|
||||
public XMLElementMapping {
|
||||
public:
|
||||
virtual ~Mapping() {}
|
||||
Mapping(
|
||||
const char* element) :
|
||||
XMLElementMapping(element) {}
|
||||
|
||||
IPersistent* create() const {
|
||||
return new T();
|
||||
}
|
||||
};
|
||||
|
||||
// compare pointers to Mappings by element name
|
||||
struct _mapping_ptr_less : public binary_function<XMLElementMapping*,XMLElementMapping*,bool> {
|
||||
public:
|
||||
bool operator()(const XMLElementMapping* a, const XMLElementMapping* b) const {
|
||||
return a->element < b->element;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__XMLElementMapping_H__*/
|
52
src/apps/cortex/Persistence/xml_export_utils.h
Normal file
52
src/apps/cortex/Persistence/xml_export_utils.h
Normal file
@ -0,0 +1,52 @@
|
||||
// xml_export_utils.h
|
||||
// * PURPOSE
|
||||
// helper functions for writing XML representations of
|
||||
// C++ objects.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 5jul99 Begun
|
||||
|
||||
#ifndef __xml_export_utils_H__
|
||||
#define __xml_export_utils_H__
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
// Writes the correct number of spaces to the given stream,
|
||||
// so that text written after this call will start at the given
|
||||
// column.
|
||||
// Assumes that the given text string has already been written
|
||||
// (with the level of indentation currently stored in the given
|
||||
// context.)
|
||||
|
||||
inline ostream& pad_with_spaces(
|
||||
ostream& str,
|
||||
const char* text,
|
||||
ExportContext& context,
|
||||
uint16 column=30) {
|
||||
|
||||
int16 spaces = column - (strlen(text) + context.indentLevel());
|
||||
if(spaces < 0) spaces = 0;
|
||||
while(spaces--) str << ' ';
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
// Writes the given key/value as an XML attribute (a newline
|
||||
// is written first, to facilitate compact representation of
|
||||
// elements with no attributes.)
|
||||
|
||||
template <class T>
|
||||
void write_attr(
|
||||
const char* key,
|
||||
T value,
|
||||
ostream& stream,
|
||||
ExportContext& context) {
|
||||
|
||||
stream << endl << context.indentString() << key;
|
||||
pad_with_spaces(stream, key, context) << " = '" << value << '\'';
|
||||
}
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
|
||||
#endif /*__xml_export_utils_H__*/
|
BIN
src/apps/cortex/Resource.rsrc
Normal file
BIN
src/apps/cortex/Resource.rsrc
Normal file
Binary file not shown.
499
src/apps/cortex/RouteApp/ConnectionIO.cpp
Normal file
499
src/apps/cortex/RouteApp/ConnectionIO.cpp
Normal file
@ -0,0 +1,499 @@
|
||||
// ConnectionIO.cpp
|
||||
|
||||
#include "ConnectionIO.h"
|
||||
#include "LiveNodeIO.h"
|
||||
#include "NodeManager.h"
|
||||
#include "NodeSetIOContext.h"
|
||||
|
||||
#include "MediaFormatIO.h"
|
||||
#include "route_app_io.h"
|
||||
|
||||
#include <vector>
|
||||
#include <Debug.h>
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
ConnectionIO::~ConnectionIO() {
|
||||
if(m_inputNodeIO) delete m_inputNodeIO;
|
||||
if(m_outputNodeIO) delete m_outputNodeIO;
|
||||
}
|
||||
|
||||
// initialize for import
|
||||
ConnectionIO::ConnectionIO() :
|
||||
m_inputNodeIO(0),
|
||||
m_outputNodeIO(0),
|
||||
m_flags(0),
|
||||
m_exportValid(false),
|
||||
m_importState(IMPORT_NONE) {
|
||||
|
||||
m_outputFormat.type = B_MEDIA_NO_TYPE;
|
||||
m_inputFormat.type = B_MEDIA_NO_TYPE;
|
||||
m_requestedFormat.type = B_MEDIA_NO_TYPE;
|
||||
}
|
||||
|
||||
// initialize for export
|
||||
ConnectionIO::ConnectionIO(
|
||||
const Connection* con,
|
||||
const NodeManager* manager,
|
||||
const NodeSetIOContext* context) :
|
||||
|
||||
m_inputNodeIO(0),
|
||||
m_outputNodeIO(0),
|
||||
m_exportValid(false),
|
||||
m_importState(IMPORT_NONE) {
|
||||
|
||||
ASSERT(con);
|
||||
ASSERT(manager);
|
||||
ASSERT(context);
|
||||
status_t err;
|
||||
|
||||
if(!con->isValid()) {
|
||||
PRINT((
|
||||
"!!! ConnectionIO(): invalid connection\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_outputNodeIO = new LiveNodeIO(
|
||||
manager,
|
||||
context,
|
||||
con->sourceNode());
|
||||
|
||||
// fetch output (connection-point) description
|
||||
const char* name;
|
||||
if(con->getOutputHint(
|
||||
&name,
|
||||
&m_outputFormat) == B_OK)
|
||||
m_outputName = name;
|
||||
else {
|
||||
m_outputName = con->outputName();
|
||||
}
|
||||
|
||||
m_inputNodeIO = new LiveNodeIO(
|
||||
manager,
|
||||
context,
|
||||
con->destinationNode());
|
||||
|
||||
// fetch input (connection-point) description
|
||||
if(con->getInputHint(
|
||||
&name,
|
||||
&m_inputFormat) == B_OK)
|
||||
m_inputName = name;
|
||||
|
||||
else {
|
||||
m_inputName = con->inputName();
|
||||
}
|
||||
|
||||
m_requestedFormat = con->requestedFormat();
|
||||
m_flags = con->flags();
|
||||
|
||||
m_exportValid = true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// call when object imported to create the described
|
||||
// connection
|
||||
|
||||
// +++++ to do
|
||||
// smarter input/output matching -- if no name/format provided,
|
||||
// pick the first available endpoint. otherwise, make two passes:
|
||||
// 1) match all nodes w/ given name (pass wildcards through to roster)
|
||||
// 2) filter by format
|
||||
|
||||
status_t ConnectionIO::instantiate(
|
||||
NodeManager* manager,
|
||||
const NodeSetIOContext* context,
|
||||
Connection* outCon) {
|
||||
|
||||
// sanity checks
|
||||
ASSERT(manager);
|
||||
if(!m_inputNodeIO || !m_outputNodeIO)
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
status_t err;
|
||||
media_node_id node;
|
||||
|
||||
// find output node
|
||||
NodeRef* outputRef;
|
||||
err = m_outputNodeIO->getNode(manager, context, &node);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
err = manager->getNodeRef(
|
||||
node,
|
||||
&outputRef);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
// find output +++++ currently matches by name only
|
||||
const int32 outputBufferSize = 16;
|
||||
media_output outputs[outputBufferSize];
|
||||
int32 count = outputBufferSize;
|
||||
|
||||
//vector<media_output> outputs;
|
||||
// err = outputRef->getFreeOutputs(
|
||||
// outputs/*,
|
||||
// m_outputFormat.type*/);
|
||||
|
||||
err = outputRef->getFreeOutputs(
|
||||
outputs,
|
||||
outputBufferSize,
|
||||
&count);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
media_output output;
|
||||
bool found = false;
|
||||
for(int n = 0; n < count; ++n) {
|
||||
if(m_outputName == outputs[n].name) {
|
||||
output = outputs[n];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
PRINT(("!!! output '%s' of node '%s' not found\n",
|
||||
m_outputName.String(),
|
||||
outputRef->name()));
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
// find input node
|
||||
NodeRef* inputRef;
|
||||
err = m_inputNodeIO->getNode(manager, context, &node);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
err = manager->getNodeRef(
|
||||
node,
|
||||
&inputRef);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
// find input +++++ currently matches by name only
|
||||
vector<media_input> inputs;
|
||||
err = inputRef->getFreeInputs(
|
||||
inputs /*,
|
||||
m_inputFormat.type*/);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
media_input input;
|
||||
found = false;
|
||||
for(unsigned int n = 0; n < inputs.size(); ++n) {
|
||||
if(m_inputName == inputs[n].name) {
|
||||
input = inputs[n];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
PRINT(("!!! input '%s' of node '%s' not found\n",
|
||||
m_inputName.String(),
|
||||
inputRef->name()));
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
// connect
|
||||
Connection con;
|
||||
if(m_requestedFormat.type != B_MEDIA_NO_TYPE)
|
||||
err = manager->connect(
|
||||
output,
|
||||
input,
|
||||
m_requestedFormat,
|
||||
&con);
|
||||
else
|
||||
err = manager->connect(
|
||||
output,
|
||||
input,
|
||||
&con);
|
||||
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
if(outCon)
|
||||
*outCon = con;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** document-type setup
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/*static*/
|
||||
void ConnectionIO::AddTo(
|
||||
XML::DocumentType* docType) {
|
||||
|
||||
// map self
|
||||
docType->addMapping(new Mapping<ConnectionIO>(_CONNECTION_ELEMENT));
|
||||
|
||||
// map simple (content-only) elements
|
||||
// +++++ should these be added at a higher level, since they're
|
||||
// shared? no harm is done if one is added more than once,
|
||||
// since they'll always map to StringContent -- but it's way
|
||||
// messy!
|
||||
// +++++
|
||||
//docType->addMapping(new Mapping<StringContent>(_LIVE_NODE_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_NAME_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_KIND_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_FLAG_ELEMENT));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** IPersistent
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// EXPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
void ConnectionIO::xmlExportBegin(
|
||||
ExportContext& context) const {
|
||||
|
||||
if(!m_exportValid) {
|
||||
context.reportError(
|
||||
"ConnectionIO::xmlExportBegin():\n"
|
||||
"*** invalid ***\n");
|
||||
return;
|
||||
}
|
||||
|
||||
context.beginElement(_CONNECTION_ELEMENT);
|
||||
}
|
||||
|
||||
void ConnectionIO::xmlExportAttributes(
|
||||
ExportContext& context) const { TOUCH(context); }
|
||||
|
||||
void ConnectionIO::xmlExportContent(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.beginContent();
|
||||
|
||||
// write output
|
||||
{
|
||||
context.beginElement(_OUTPUT_ELEMENT);
|
||||
context.beginContent();
|
||||
|
||||
// describe the node
|
||||
// LiveNodeIO nodeIO(
|
||||
// m_manager,
|
||||
// dynamic_cast<NodeSetIOContext*>(&context),
|
||||
// m_outputNode);
|
||||
context.writeObject(m_outputNodeIO);
|
||||
|
||||
// context.beginElement(_LIVE_NODE_ELEMENT);
|
||||
// if(m_outputNodeKey.Length()) {
|
||||
// context.writeAttr("key", m_outputNodeKey);
|
||||
// }
|
||||
// else {
|
||||
// _write_simple("name", m_outputNodeName.String(), context);
|
||||
// _write_node_kinds(m_outputNodeKind, context);
|
||||
// }
|
||||
// context.endElement(); // _LIVE_NODE_ELEMENT
|
||||
|
||||
// describe the output
|
||||
|
||||
_write_simple("name", m_outputName.String(), context);
|
||||
|
||||
if(m_outputFormat.type > B_MEDIA_UNKNOWN_TYPE) {
|
||||
MediaFormatIO io(m_outputFormat);
|
||||
context.writeObject(&io);
|
||||
}
|
||||
|
||||
context.endElement(); // _OUTPUT_ELEMENT
|
||||
}
|
||||
|
||||
// write input
|
||||
{
|
||||
context.beginElement(_INPUT_ELEMENT);
|
||||
context.beginContent();
|
||||
|
||||
// describe the node
|
||||
// LiveNodeIO nodeIO(
|
||||
// m_manager,
|
||||
// dynamic_cast<NodeSetIOContext*>(&context),
|
||||
// m_inputNode);
|
||||
context.writeObject(m_inputNodeIO);
|
||||
|
||||
// context.beginElement(_LIVE_NODE_ELEMENT);
|
||||
// if(m_inputNodeKey.Length()) {
|
||||
// context.writeAttr("key", m_inputNodeKey);
|
||||
// }
|
||||
// else {
|
||||
// _write_simple("name", m_inputNodeName.String(), context);
|
||||
// _write_node_kinds(m_inputNodeKind, context);
|
||||
// }
|
||||
// context.endElement(); // _LIVE_NODE_ELEMENT
|
||||
|
||||
// describe the input
|
||||
|
||||
_write_simple("name", m_inputName.String(), context);
|
||||
|
||||
if(m_inputFormat.type > B_MEDIA_UNKNOWN_TYPE) {
|
||||
MediaFormatIO io(m_inputFormat);
|
||||
context.writeObject(&io);
|
||||
}
|
||||
|
||||
context.endElement(); // _INPUT_ELEMENT
|
||||
}
|
||||
|
||||
// write requested format
|
||||
if(m_requestedFormat.type > B_MEDIA_UNKNOWN_TYPE) {
|
||||
MediaFormatIO io(m_requestedFormat);
|
||||
BString comment = "\n";
|
||||
comment << context.indentString();
|
||||
comment << "<!-- initial requested format -->";
|
||||
context.writeString(comment);
|
||||
context.writeObject(&io);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionIO::xmlExportEnd(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.endElement(); // _CONNECTION_ELEMENT
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// IMPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void ConnectionIO::xmlImportBegin(
|
||||
ImportContext& context) { TOUCH(context); }
|
||||
|
||||
void ConnectionIO::xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) { TOUCH(key); TOUCH(value); TOUCH(context); }
|
||||
|
||||
void ConnectionIO::xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) { TOUCH(data); TOUCH(length); TOUCH(context); }
|
||||
|
||||
void ConnectionIO::xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context) {
|
||||
|
||||
status_t err;
|
||||
|
||||
if(!strcmp(context.element(), _LIVE_NODE_ELEMENT)) {
|
||||
LiveNodeIO* nodeIO = dynamic_cast<LiveNodeIO*>(child);
|
||||
ASSERT(nodeIO);
|
||||
|
||||
// store the LiveNodeIO for now; it will be used in
|
||||
// instantiate()
|
||||
|
||||
switch(m_importState) {
|
||||
case IMPORT_OUTPUT:
|
||||
m_outputNodeIO = nodeIO;
|
||||
child = 0; // don't delete child object
|
||||
break;
|
||||
|
||||
case IMPORT_INPUT:
|
||||
m_inputNodeIO = nodeIO;
|
||||
child = 0; // don't delete child object
|
||||
break;
|
||||
|
||||
case IMPORT_NONE:
|
||||
context.reportError("Unexpected node description.\n");
|
||||
delete child;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(context.element(), _NAME_ELEMENT)) {
|
||||
StringContent* c = dynamic_cast<StringContent*>(child);
|
||||
ASSERT(c);
|
||||
|
||||
switch(m_importState) {
|
||||
case IMPORT_OUTPUT:
|
||||
m_outputName = c->content;
|
||||
break;
|
||||
|
||||
case IMPORT_INPUT:
|
||||
m_inputName = c->content;
|
||||
break;
|
||||
|
||||
case IMPORT_NONE:
|
||||
context.reportError("Unexpected node name.\n");
|
||||
delete child;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
MediaFormatIO* io = dynamic_cast<MediaFormatIO*>(child);
|
||||
if(!io) {
|
||||
context.reportError("Unexpected element.\n");
|
||||
delete child;
|
||||
return;
|
||||
}
|
||||
|
||||
media_format f;
|
||||
err = io->getFormat(f);
|
||||
if(err < B_OK) {
|
||||
context.reportError("Malformed format.\n");
|
||||
delete child;
|
||||
return;
|
||||
}
|
||||
|
||||
switch(m_importState) {
|
||||
case IMPORT_OUTPUT:
|
||||
m_outputFormat = f;
|
||||
break;
|
||||
|
||||
case IMPORT_INPUT:
|
||||
m_inputFormat = f;
|
||||
break;
|
||||
|
||||
case IMPORT_NONE:
|
||||
m_requestedFormat = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(child)
|
||||
delete child;
|
||||
}
|
||||
|
||||
void ConnectionIO::xmlImportComplete(
|
||||
ImportContext& context) {
|
||||
|
||||
// +++++
|
||||
}
|
||||
|
||||
void ConnectionIO::xmlImportChildBegin(
|
||||
const char* name,
|
||||
ImportContext& context) {
|
||||
|
||||
if(!strcmp(name, "input")) {
|
||||
if(m_importState != IMPORT_NONE) {
|
||||
context.reportError("ConnectionIO: unexpected nested child element\n");
|
||||
return;
|
||||
}
|
||||
m_importState = IMPORT_INPUT;
|
||||
}
|
||||
else if(!strcmp(name, "output")) {
|
||||
if(m_importState != IMPORT_NONE) {
|
||||
context.reportError("ConnectionIO: unexpected nested child element\n");
|
||||
return;
|
||||
}
|
||||
m_importState = IMPORT_OUTPUT;
|
||||
}
|
||||
else
|
||||
context.reportError("ConnectionIO: unexpected child element\n");
|
||||
}
|
||||
|
||||
void ConnectionIO::xmlImportChildComplete(
|
||||
const char* name,
|
||||
ImportContext& context) {
|
||||
TOUCH(name); TOUCH(context);
|
||||
|
||||
m_importState = IMPORT_NONE;
|
||||
}
|
||||
|
||||
// END -- ConnectionIO.cpp --
|
133
src/apps/cortex/RouteApp/ConnectionIO.h
Normal file
133
src/apps/cortex/RouteApp/ConnectionIO.h
Normal file
@ -0,0 +1,133 @@
|
||||
// ConnectionIO.h
|
||||
// * PURPOSE
|
||||
// Manage the import and export of a user-instantiated
|
||||
// media node description.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 8dec99 Begun
|
||||
|
||||
#ifndef __ConnectionIO_H__
|
||||
#define __ConnectionIO_H__
|
||||
|
||||
#include "NodeRef.h"
|
||||
#include "Connection.h"
|
||||
#include "XML.h"
|
||||
|
||||
#include <String.h>
|
||||
#include <Entry.h>
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
class dormant_node_info;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeManager;
|
||||
class NodeSetIOContext;
|
||||
class LiveNodeIO;
|
||||
|
||||
class ConnectionIO :
|
||||
public IPersistent {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
virtual ~ConnectionIO();
|
||||
|
||||
ConnectionIO();
|
||||
|
||||
ConnectionIO(
|
||||
const Connection* con,
|
||||
const NodeManager* manager,
|
||||
const NodeSetIOContext* context);
|
||||
|
||||
bool exportValid() const { return m_exportValid; }
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// call when object imported to create the described
|
||||
// connection
|
||||
status_t instantiate(
|
||||
NodeManager* manager,
|
||||
const NodeSetIOContext* context,
|
||||
Connection* outCon);
|
||||
|
||||
public: // *** document-type setup
|
||||
static void AddTo(
|
||||
XML::DocumentType* docType);
|
||||
|
||||
public: // *** IPersistent
|
||||
|
||||
// EXPORT:
|
||||
|
||||
void xmlExportBegin(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportAttributes(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportContent(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportEnd(
|
||||
ExportContext& context) const;
|
||||
|
||||
// IMPORT:
|
||||
|
||||
virtual void xmlImportBegin(
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportComplete(
|
||||
ImportContext& context);
|
||||
|
||||
|
||||
virtual void xmlImportChildBegin(
|
||||
const char* name,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChildComplete(
|
||||
const char* name,
|
||||
ImportContext& context);
|
||||
|
||||
private: // *** implementation
|
||||
|
||||
LiveNodeIO* m_inputNodeIO;
|
||||
|
||||
BString m_outputName; // original name if available
|
||||
media_format m_outputFormat;
|
||||
|
||||
LiveNodeIO* m_outputNodeIO;
|
||||
|
||||
BString m_inputName; // original name if available
|
||||
media_format m_inputFormat;
|
||||
|
||||
media_format m_requestedFormat;
|
||||
|
||||
uint32 m_flags;
|
||||
|
||||
bool m_exportValid;
|
||||
|
||||
// import state
|
||||
enum import_state_t {
|
||||
IMPORT_NONE,
|
||||
IMPORT_OUTPUT,
|
||||
IMPORT_INPUT
|
||||
};
|
||||
import_state_t m_importState;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__ConnectionIO_H__*/
|
538
src/apps/cortex/RouteApp/DormantNodeIO.cpp
Normal file
538
src/apps/cortex/RouteApp/DormantNodeIO.cpp
Normal file
@ -0,0 +1,538 @@
|
||||
// DormantNodeIO.cpp
|
||||
|
||||
#include "DormantNodeIO.h"
|
||||
#include "ImportContext.h"
|
||||
#include "ExportContext.h"
|
||||
#include "StringContent.h"
|
||||
|
||||
#include "NodeManager.h"
|
||||
|
||||
#include <Debug.h>
|
||||
#include <MediaAddOn.h>
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <Path.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "route_app_io.h"
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
DormantNodeIO::~DormantNodeIO() {}
|
||||
|
||||
// initialize for import (to defaults)
|
||||
DormantNodeIO::DormantNodeIO() :
|
||||
m_kinds(0LL),
|
||||
m_flavorID(0),
|
||||
m_flags(0),
|
||||
m_runMode(0),
|
||||
m_recordingDelay(0),
|
||||
m_cycle(false),
|
||||
m_exportValid(false) {}
|
||||
|
||||
// initialize for export
|
||||
DormantNodeIO::DormantNodeIO(
|
||||
NodeRef* ref,
|
||||
const char* nodeKey) :
|
||||
m_exportValid(false) {
|
||||
|
||||
ASSERT(ref);
|
||||
ASSERT(nodeKey);
|
||||
status_t err;
|
||||
|
||||
m_nodeKey = nodeKey;
|
||||
|
||||
// * extract dormant-node info
|
||||
dormant_node_info info;
|
||||
err = ref->getDormantNodeInfo(&info);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! DormantNodeIO(): getDormantNodeInfo() failed:\n"
|
||||
" %s\n",
|
||||
strerror(err)));
|
||||
return;
|
||||
}
|
||||
|
||||
dormant_flavor_info flavorInfo;
|
||||
err = BMediaRoster::Roster()->GetDormantFlavorInfoFor(
|
||||
info, &flavorInfo);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! DormantNodeIO(): GetDormantFlavorInfoFor() failed:\n"
|
||||
" %s\n",
|
||||
strerror(err)));
|
||||
return;
|
||||
}
|
||||
|
||||
m_dormantName = flavorInfo.name;
|
||||
m_flavorID = info.flavor_id;
|
||||
m_kinds = flavorInfo.kinds;
|
||||
|
||||
m_flags = ref->flags();
|
||||
entry_ref file;
|
||||
if(ref->getFile(&file) == B_OK)
|
||||
m_entry.SetTo(&file);
|
||||
|
||||
m_runMode = ref->runMode();
|
||||
m_recordingDelay = ref->recordingDelay();
|
||||
m_cycle = ref->isCycling();
|
||||
|
||||
// done extracting node info; ready for export
|
||||
m_exportValid = true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** document-type setup
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/*static*/
|
||||
void DormantNodeIO::AddTo(
|
||||
XML::DocumentType* docType) {
|
||||
|
||||
// map self
|
||||
docType->addMapping(new Mapping<DormantNodeIO>(_DORMANT_NODE_ELEMENT));
|
||||
|
||||
// // map simple (content-only) elements
|
||||
// // +++++ should these be added at a higher level, since they're
|
||||
// // shared? no harm is done if one is added more than once,
|
||||
// // since they'll always map to StringContent.
|
||||
// // +++++
|
||||
// docType->addMapping(new Mapping<StringContent>(_NAME_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_KIND_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_FLAVOR_ID_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_FLAG_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_RUN_MODE_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_RECORDING_DELAY_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_CYCLE_ELEMENT));
|
||||
// docType->addMapping(new Mapping<StringContent>(_REF_ELEMENT));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// call when object imported to create the described node
|
||||
|
||||
// +++++
|
||||
|
||||
status_t DormantNodeIO::instantiate(
|
||||
NodeManager* manager,
|
||||
NodeRef** outRef) {
|
||||
|
||||
status_t err;
|
||||
BPath p;
|
||||
if(m_entry.InitCheck() == B_OK)
|
||||
m_entry.GetPath(&p);
|
||||
|
||||
// PRINT((
|
||||
// "DormantNodeIO:\n"
|
||||
// " key: %s\n"
|
||||
// " name: %s\n"
|
||||
// " flavor: %ld\n"
|
||||
// " kinds: %Lx\n"
|
||||
// " flags: %lx\n"
|
||||
// " runMode: %ld\n"
|
||||
// " recDelay: %Ld\n"
|
||||
// " cycle: %s\n"
|
||||
// " entry: %s\n\n",
|
||||
// m_nodeKey.String(),
|
||||
// m_dormantName.String(),
|
||||
// m_flavorID,
|
||||
// m_kinds,
|
||||
// m_flags,
|
||||
// m_runMode,
|
||||
// m_recordingDelay,
|
||||
// m_cycle ? "true" : "false",
|
||||
// p.Path()));
|
||||
|
||||
// find matching dormant node
|
||||
dormant_node_info info;
|
||||
err = _matchDormantNode(&info);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! _matchDormantNode() failed: %s\n", strerror(err)));
|
||||
return err;
|
||||
}
|
||||
|
||||
// instantiate node
|
||||
err = manager->instantiate(
|
||||
info,
|
||||
outRef,
|
||||
B_INFINITE_TIMEOUT,
|
||||
m_flags);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! instantiate() failed: %s\n", strerror(err)));
|
||||
return err;
|
||||
}
|
||||
|
||||
entry_ref mediaRef;
|
||||
if(m_entry.InitCheck() == B_OK && m_entry.GetRef(&mediaRef) == B_OK) {
|
||||
// set ref
|
||||
err = (*outRef)->setFile(mediaRef);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! WARNING: setFile() failed: %s\n", strerror(err)));
|
||||
}
|
||||
}
|
||||
|
||||
// set run mode
|
||||
if(m_runMode)
|
||||
(*outRef)->setRunMode(m_runMode, m_recordingDelay);
|
||||
|
||||
// set cycle state
|
||||
if(m_cycle)
|
||||
(*outRef)->setCycling(true);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t DormantNodeIO::_matchDormantNode(
|
||||
dormant_node_info* outInfo) {
|
||||
|
||||
status_t err;
|
||||
|
||||
// fetch all dormant nodes matching the signature
|
||||
const int32 bufferSize = 32;
|
||||
dormant_node_info buffer[bufferSize];
|
||||
int32 count = bufferSize;
|
||||
err = BMediaRoster::Roster()->GetDormantNodes(
|
||||
buffer,
|
||||
&count,
|
||||
0,
|
||||
0,
|
||||
m_dormantName.String(),
|
||||
m_kinds,
|
||||
0 /*~m_kinds*/);
|
||||
if(err < B_OK)
|
||||
return err;
|
||||
|
||||
if(!count)
|
||||
return B_NAME_NOT_FOUND;
|
||||
|
||||
for(int32 n = 0; n < count; ++n) {
|
||||
if(buffer[n].flavor_id == m_flavorID) {
|
||||
*outInfo = buffer[n];
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// didn't match flavor id
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** IPersistent
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// EXPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
//inline void _write_simple(
|
||||
// const char* element,
|
||||
// const char* value,
|
||||
// ExportContext& context) {
|
||||
//
|
||||
// context.beginElement(element);
|
||||
// context.beginContent();
|
||||
// context.writeString(value);
|
||||
// context.endElement();
|
||||
//}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void DormantNodeIO::xmlExportBegin(
|
||||
ExportContext& context) const {
|
||||
|
||||
if(!m_exportValid) {
|
||||
context.reportError(
|
||||
"DormantNodeIO::xmlExportBegin():\n"
|
||||
"*** invalid ***\n");
|
||||
return;
|
||||
}
|
||||
|
||||
context.beginElement(_DORMANT_NODE_ELEMENT);
|
||||
}
|
||||
|
||||
void DormantNodeIO::xmlExportAttributes(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.writeAttr("key", m_nodeKey);
|
||||
}
|
||||
|
||||
void DormantNodeIO::xmlExportContent(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.beginContent();
|
||||
BString buffer;
|
||||
|
||||
// write dormant-node description
|
||||
context.beginElement(_NAME_ELEMENT);
|
||||
context.beginContent();
|
||||
context.writeString(m_dormantName);
|
||||
context.endElement();
|
||||
|
||||
if(m_flavorID > 0) {
|
||||
buffer = "";
|
||||
buffer << m_flavorID;
|
||||
context.beginElement(_FLAVOR_ID_ELEMENT);
|
||||
context.beginContent();
|
||||
context.writeString(buffer);
|
||||
context.endElement();
|
||||
}
|
||||
|
||||
_write_node_kinds(m_kinds, context);
|
||||
// if(m_kinds & B_BUFFER_PRODUCER)
|
||||
// _write_simple(_KIND_ELEMENT, "B_BUFFER_PRODUCER", context);
|
||||
// if(m_kinds & B_BUFFER_CONSUMER)
|
||||
// _write_simple(_KIND_ELEMENT, "B_BUFFER_CONSUMER", context);
|
||||
// if(m_kinds & B_TIME_SOURCE)
|
||||
// _write_simple(_KIND_ELEMENT, "B_TIME_SOURCE", context);
|
||||
// if(m_kinds & B_CONTROLLABLE)
|
||||
// _write_simple(_KIND_ELEMENT, "B_CONTROLLABLE", context);
|
||||
// if(m_kinds & B_FILE_INTERFACE)
|
||||
// _write_simple(_KIND_ELEMENT, "B_FILE_INTERFACE", context);
|
||||
// if(m_kinds & B_ENTITY_INTERFACE)
|
||||
// _write_simple(_KIND_ELEMENT, "B_ENTITY_INTERFACE", context);
|
||||
// if(m_kinds & B_PHYSICAL_INPUT)
|
||||
// _write_simple(_KIND_ELEMENT, "B_PHYSICAL_INPUT", context);
|
||||
// if(m_kinds & B_PHYSICAL_OUTPUT)
|
||||
// _write_simple(_KIND_ELEMENT, "B_PHYSICAL_OUTPUT", context);
|
||||
// if(m_kinds & B_SYSTEM_MIXER)
|
||||
// _write_simple(_KIND_ELEMENT, "B_SYSTEM_MIXER", context);
|
||||
|
||||
// write NodeRef flags
|
||||
if(m_flags & NodeRef::NO_START_STOP)
|
||||
_write_simple(_FLAG_ELEMENT, "NO_START_STOP", context);
|
||||
if(m_flags & NodeRef::NO_SEEK)
|
||||
_write_simple(_FLAG_ELEMENT, "NO_SEEK", context);
|
||||
if(m_flags & NodeRef::NO_PREROLL)
|
||||
_write_simple(_FLAG_ELEMENT, "NO_PREROLL", context);
|
||||
if(m_flags & NodeRef::NO_STOP)
|
||||
_write_simple(_FLAG_ELEMENT, "NO_STOP", context);
|
||||
if(m_flags & NodeRef::NO_ROSTER_WATCH)
|
||||
_write_simple(_FLAG_ELEMENT, "NO_ROSTER_WATCH", context);
|
||||
if(m_flags & NodeRef::NO_POSITION_REPORTING)
|
||||
_write_simple(_FLAG_ELEMENT, "NO_POSITION_REPORTING", context);
|
||||
|
||||
// write transport settings
|
||||
if(m_runMode > 0) {
|
||||
switch(m_runMode) {
|
||||
case BMediaNode::B_OFFLINE:
|
||||
_write_simple(_RUN_MODE_ELEMENT, "B_OFFLINE", context);
|
||||
break;
|
||||
case BMediaNode::B_DECREASE_PRECISION:
|
||||
_write_simple(_RUN_MODE_ELEMENT, "B_DECREASE_PRECISION", context);
|
||||
break;
|
||||
case BMediaNode::B_INCREASE_LATENCY:
|
||||
_write_simple(_RUN_MODE_ELEMENT, "B_INCREASE_LATENCY", context);
|
||||
break;
|
||||
case BMediaNode::B_DROP_DATA:
|
||||
_write_simple(_RUN_MODE_ELEMENT, "B_DROP_DATA", context);
|
||||
break;
|
||||
case BMediaNode::B_RECORDING:
|
||||
_write_simple(_RUN_MODE_ELEMENT, "B_RECORDING", context);
|
||||
buffer = "";
|
||||
buffer << m_recordingDelay;
|
||||
_write_simple(_RECORDING_DELAY_ELEMENT, buffer.String(), context);
|
||||
break;
|
||||
default:
|
||||
buffer = "";
|
||||
buffer << m_runMode;
|
||||
_write_simple(_RUN_MODE_ELEMENT, buffer.String(), context);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_cycle) {
|
||||
context.beginElement(_CYCLE_ELEMENT);
|
||||
context.endElement();
|
||||
}
|
||||
|
||||
BPath p;
|
||||
if(
|
||||
m_entry.InitCheck() == B_OK &&
|
||||
m_entry.GetPath(&p) == B_OK)
|
||||
_write_simple(_REF_ELEMENT, p.Path(), context);
|
||||
|
||||
}
|
||||
|
||||
void DormantNodeIO::xmlExportEnd(
|
||||
ExportContext& context) const {
|
||||
|
||||
// finish
|
||||
context.endElement();
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// IMPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
//inline void _read_node_kind(
|
||||
// int64& ioKind,
|
||||
// const char* data,
|
||||
// ImportContext& context) {
|
||||
//
|
||||
// if(!strcmp(data, "B_BUFFER_PRODUCER"))
|
||||
// ioKind |= B_BUFFER_PRODUCER;
|
||||
// else if(!strcmp(data, "B_BUFFER_CONSUMER"))
|
||||
// ioKind |= B_BUFFER_CONSUMER;
|
||||
// else if(!strcmp(data, "B_TIME_SOURCE"))
|
||||
// ioKind |= B_TIME_SOURCE;
|
||||
// else if(!strcmp(data, "B_CONTROLLABLE"))
|
||||
// ioKind |= B_CONTROLLABLE;
|
||||
// else if(!strcmp(data, "B_FILE_INTERFACE"))
|
||||
// ioKind |= B_FILE_INTERFACE;
|
||||
// else if(!strcmp(data, "B_ENTITY_INTERFACE"))
|
||||
// ioKind |= B_ENTITY_INTERFACE;
|
||||
// else if(!strcmp(data, "B_PHYSICAL_INPUT"))
|
||||
// ioKind |= B_PHYSICAL_INPUT;
|
||||
// else if(!strcmp(data, "B_PHYSICAL_OUTPUT"))
|
||||
// ioKind |= B_PHYSICAL_OUTPUT;
|
||||
// else if(!strcmp(data, "B_SYSTEM_MIXER"))
|
||||
// ioKind |= B_SYSTEM_MIXER;
|
||||
// else {
|
||||
// BString err;
|
||||
// err << "_read_noderef_kind(): unknown node kind '" << data << "'\n";
|
||||
// context.reportWarning(err.String());
|
||||
// }
|
||||
//}
|
||||
|
||||
inline void _read_noderef_flag(
|
||||
int32& ioFlags,
|
||||
const char* data,
|
||||
ImportContext& context) {
|
||||
|
||||
if(!strcmp(data, "NO_START_STOP"))
|
||||
ioFlags |= NodeRef::NO_START_STOP;
|
||||
else if(!strcmp(data, "NO_SEEK"))
|
||||
ioFlags |= NodeRef::NO_SEEK;
|
||||
else if(!strcmp(data, "NO_PREROLL"))
|
||||
ioFlags |= NodeRef::NO_PREROLL;
|
||||
else if(!strcmp(data, "NO_STOP"))
|
||||
ioFlags |= NodeRef::NO_STOP;
|
||||
else if(!strcmp(data, "NO_ROSTER_WATCH"))
|
||||
ioFlags |= NodeRef::NO_ROSTER_WATCH;
|
||||
else if(!strcmp(data, "NO_POSITION_REPORTING"))
|
||||
ioFlags |= NodeRef::NO_POSITION_REPORTING;
|
||||
else {
|
||||
BString err;
|
||||
err << "_read_noderef_flag(): unknown node flag '" << data << "'\n";
|
||||
context.reportWarning(err.String());
|
||||
}
|
||||
}
|
||||
|
||||
inline void _read_run_mode(
|
||||
int32& runMode,
|
||||
const char* data,
|
||||
ImportContext& context) {
|
||||
|
||||
if(!strcmp(data, "B_OFFLINE"))
|
||||
runMode = BMediaNode::B_OFFLINE;
|
||||
else if(!strcmp(data, "B_DECREASE_PRECISION"))
|
||||
runMode = BMediaNode::B_DECREASE_PRECISION;
|
||||
else if(!strcmp(data, "B_INCREASE_LATENCY"))
|
||||
runMode = BMediaNode::B_INCREASE_LATENCY;
|
||||
else if(!strcmp(data, "B_DROP_DATA"))
|
||||
runMode = BMediaNode::B_DROP_DATA;
|
||||
else if(!strcmp(data, "B_RECORDING"))
|
||||
runMode = BMediaNode::B_RECORDING;
|
||||
else {
|
||||
BString err;
|
||||
err << "_read_run_mode(): unknown run mode '" << data << "'\n";
|
||||
context.reportWarning(err.String());
|
||||
}
|
||||
}
|
||||
|
||||
inline void _read_entry(
|
||||
BEntry& entry,
|
||||
const char* data,
|
||||
ImportContext& context) {
|
||||
|
||||
entry_ref r;
|
||||
status_t err = get_ref_for_path(data, &r);
|
||||
if(err < B_OK) {
|
||||
BString text;
|
||||
text << "_read_entry_ref(): get_ref_for_path('" << data << "') failed:\n"
|
||||
" " << strerror(err) << "\n";
|
||||
context.reportWarning(text.String());
|
||||
}
|
||||
|
||||
entry.SetTo(&r);
|
||||
}
|
||||
|
||||
|
||||
void DormantNodeIO::xmlImportBegin(
|
||||
ImportContext& context) { TOUCH(context); }
|
||||
|
||||
void DormantNodeIO::xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) {
|
||||
|
||||
if(!strcmp(key, "key")) {
|
||||
m_nodeKey = value;
|
||||
}
|
||||
else {
|
||||
BString err;
|
||||
err << "DormantNodeIO: unknown attribute '" << key << "'\n";
|
||||
context.reportError(err.String());
|
||||
}
|
||||
}
|
||||
|
||||
void DormantNodeIO::xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) { TOUCH(data); TOUCH(length); TOUCH(context); }
|
||||
|
||||
void DormantNodeIO::xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context) {
|
||||
|
||||
StringContent* obj = dynamic_cast<StringContent*>(child);
|
||||
if(!obj) {
|
||||
BString err;
|
||||
err << "DormantNodeIO: unexpected element '" <<
|
||||
context.element() << "'\n";
|
||||
context.reportError(err.String());
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcmp(context.element(), _NAME_ELEMENT))
|
||||
m_dormantName = obj->content;
|
||||
else if(!strcmp(context.element(), _KIND_ELEMENT))
|
||||
_read_node_kind(m_kinds, obj->content.String(), context);
|
||||
else if(!strcmp(context.element(), _FLAVOR_ID_ELEMENT))
|
||||
m_flavorID = atol(obj->content.String());
|
||||
else if(!strcmp(context.element(), _FLAG_ELEMENT))
|
||||
_read_noderef_flag(m_flags, obj->content.String(), context);
|
||||
else if(!strcmp(context.element(), _RUN_MODE_ELEMENT))
|
||||
_read_run_mode(m_runMode, obj->content.String(), context);
|
||||
else if(!strcmp(context.element(), _RECORDING_DELAY_ELEMENT))
|
||||
m_recordingDelay = strtoll(obj->content.String(), 0, 10);
|
||||
else if(!strcmp(context.element(), _CYCLE_ELEMENT))
|
||||
m_cycle = true;
|
||||
else if(!strcmp(context.element(), _REF_ELEMENT))
|
||||
_read_entry(m_entry, obj->content.String(), context);
|
||||
else {
|
||||
BString err;
|
||||
err << "DormantNodeIO: unexpected element '" <<
|
||||
context.element() << "'\n";
|
||||
context.reportError(err.String());
|
||||
}
|
||||
|
||||
delete child;
|
||||
}
|
||||
|
||||
void DormantNodeIO::xmlImportComplete(
|
||||
ImportContext& context) { TOUCH(context); } //nyi; +++++ final checks?
|
||||
|
||||
|
||||
// END -- DormantNodeIO.cpp --
|
111
src/apps/cortex/RouteApp/DormantNodeIO.h
Normal file
111
src/apps/cortex/RouteApp/DormantNodeIO.h
Normal file
@ -0,0 +1,111 @@
|
||||
// DormantNodeIO.h
|
||||
// * PURPOSE
|
||||
// Manage the import and export of a user-instantiated
|
||||
// media node descriptor.
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 8dec99 Begun
|
||||
|
||||
#ifndef __DormantNodeIO_H__
|
||||
#define __DormantNodeIO_H__
|
||||
|
||||
#include "NodeRef.h"
|
||||
#include "XML.h"
|
||||
|
||||
#include <String.h>
|
||||
#include <Entry.h>
|
||||
|
||||
class dormant_node_info;
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeManager;
|
||||
|
||||
class DormantNodeIO :
|
||||
public IPersistent {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
virtual ~DormantNodeIO();
|
||||
|
||||
DormantNodeIO();
|
||||
DormantNodeIO(
|
||||
NodeRef* ref,
|
||||
const char* nodeKey);
|
||||
|
||||
bool exportValid() const { return m_exportValid; }
|
||||
|
||||
const char* nodeKey() const { return m_nodeKey.String(); }
|
||||
|
||||
public: // *** operations
|
||||
|
||||
// call when object imported to create the described node
|
||||
status_t instantiate(
|
||||
NodeManager* manager,
|
||||
NodeRef** outRef);
|
||||
|
||||
public: // *** document-type setup
|
||||
static void AddTo(
|
||||
XML::DocumentType* docType);
|
||||
|
||||
public: // *** IPersistent
|
||||
|
||||
// EXPORT:
|
||||
|
||||
void xmlExportBegin(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportAttributes(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportContent(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportEnd(
|
||||
ExportContext& context) const;
|
||||
|
||||
// IMPORT:
|
||||
|
||||
virtual void xmlImportBegin(
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context);
|
||||
|
||||
virtual void xmlImportComplete(
|
||||
ImportContext& context);
|
||||
|
||||
private: // *** implementation
|
||||
// imported data
|
||||
BString m_nodeKey;
|
||||
|
||||
BString m_dormantName;
|
||||
int64 m_kinds;
|
||||
int32 m_flavorID;
|
||||
|
||||
int32 m_flags;
|
||||
int32 m_runMode;
|
||||
bigtime_t m_recordingDelay;
|
||||
bool m_cycle;
|
||||
|
||||
BEntry m_entry;
|
||||
|
||||
bool m_exportValid;
|
||||
|
||||
status_t _matchDormantNode(
|
||||
dormant_node_info* outInfo);
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__DormantNodeIO_H__*/
|
215
src/apps/cortex/RouteApp/LiveNodeIO.cpp
Normal file
215
src/apps/cortex/RouteApp/LiveNodeIO.cpp
Normal file
@ -0,0 +1,215 @@
|
||||
// LiveNodeIO.cpp
|
||||
|
||||
#include "LiveNodeIO.h"
|
||||
#include "ImportContext.h"
|
||||
#include "ExportContext.h"
|
||||
#include "NodeSetIOContext.h"
|
||||
#include "StringContent.h"
|
||||
|
||||
#include "NodeManager.h"
|
||||
|
||||
#include <Debug.h>
|
||||
#include <MediaAddOn.h>
|
||||
#include <MediaDefs.h>
|
||||
#include <MediaRoster.h>
|
||||
#include <Path.h>
|
||||
|
||||
#include "route_app_io.h"
|
||||
|
||||
__USE_CORTEX_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** ctor/dtor
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
LiveNodeIO::~LiveNodeIO() {}
|
||||
|
||||
LiveNodeIO::LiveNodeIO() :
|
||||
m_kind(0LL),
|
||||
m_exportValid(false) {}
|
||||
|
||||
LiveNodeIO::LiveNodeIO(
|
||||
const NodeManager* manager,
|
||||
const NodeSetIOContext* context,
|
||||
media_node_id node) :
|
||||
m_exportValid(false) {
|
||||
|
||||
status_t err;
|
||||
ASSERT(manager);
|
||||
ASSERT(context);
|
||||
|
||||
err = _get_node_signature(
|
||||
manager,
|
||||
context,
|
||||
node,
|
||||
m_key,
|
||||
m_name,
|
||||
m_kind);
|
||||
if(err < B_OK)
|
||||
return;
|
||||
|
||||
m_exportValid = true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** import operations
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// locate the referenced live node
|
||||
status_t LiveNodeIO::getNode(
|
||||
const NodeManager* manager,
|
||||
const NodeSetIOContext* context,
|
||||
media_node_id* outNode) const {
|
||||
|
||||
ASSERT(manager);
|
||||
ASSERT(context);
|
||||
status_t err;
|
||||
|
||||
if(hasKey()) {
|
||||
// match key against previously imported nodes
|
||||
err = context->getNodeFor(key(), outNode);
|
||||
|
||||
if(err < B_OK) {
|
||||
// match key against system nodes
|
||||
err = _match_system_node_key(key(), manager, outNode);
|
||||
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! No node found for key '%s'\n",
|
||||
key()));
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = _match_node_signature(
|
||||
name(),
|
||||
kind(),
|
||||
outNode);
|
||||
if(err < B_OK) {
|
||||
PRINT((
|
||||
"!!! No node found named '%s' with kinds %Ld\n",
|
||||
name(),
|
||||
kind()));
|
||||
return B_NAME_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** document-type setup
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
/*static*/
|
||||
void LiveNodeIO::AddTo(
|
||||
XML::DocumentType* docType) {
|
||||
|
||||
// map self
|
||||
docType->addMapping(new Mapping<LiveNodeIO>(_LIVE_NODE_ELEMENT));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// *** IPersistent
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// EXPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void LiveNodeIO::xmlExportBegin(
|
||||
ExportContext& context) const {
|
||||
|
||||
if(!m_exportValid) {
|
||||
context.reportError(
|
||||
"LiveNodeIO::xmlExportBegin():\n"
|
||||
"*** invalid ***\n");
|
||||
return;
|
||||
}
|
||||
|
||||
context.beginElement(_LIVE_NODE_ELEMENT);
|
||||
}
|
||||
|
||||
void LiveNodeIO::xmlExportAttributes(
|
||||
ExportContext& context) const {
|
||||
|
||||
if(m_key.Length() > 0)
|
||||
context.writeAttr("key", m_key.String());
|
||||
}
|
||||
|
||||
void LiveNodeIO::xmlExportContent(
|
||||
ExportContext& context) const {
|
||||
|
||||
if(m_name.Length() > 0) {
|
||||
context.beginContent();
|
||||
|
||||
_write_simple(_NAME_ELEMENT, m_name.String(), context);
|
||||
_write_node_kinds(m_kind, context);
|
||||
}
|
||||
}
|
||||
|
||||
void LiveNodeIO::xmlExportEnd(
|
||||
ExportContext& context) const {
|
||||
|
||||
context.endElement();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------- //
|
||||
// IMPORT:
|
||||
// -------------------------------------------------------- //
|
||||
|
||||
void LiveNodeIO::xmlImportBegin(
|
||||
ImportContext& context) {}
|
||||
|
||||
void LiveNodeIO::xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context) {
|
||||
|
||||
if(!strcmp(key, "key")) {
|
||||
m_key = value;
|
||||
}
|
||||
else {
|
||||
BString err;
|
||||
err << "LiveNodeIO: unknown attribute '" << key << "'\n";
|
||||
context.reportError(err.String());
|
||||
}
|
||||
}
|
||||
|
||||
void LiveNodeIO::xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context) {}
|
||||
|
||||
void LiveNodeIO::xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context) {
|
||||
|
||||
StringContent* obj = dynamic_cast<StringContent*>(child);
|
||||
if(!obj) {
|
||||
BString err;
|
||||
err << "LiveNodeIO: unexpected element '" <<
|
||||
context.element() << "'\n";
|
||||
context.reportError(err.String());
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcmp(context.element(), _NAME_ELEMENT))
|
||||
m_name = obj->content;
|
||||
else if(!strcmp(context.element(), _KIND_ELEMENT))
|
||||
_read_node_kind(m_kind, obj->content.String(), context);
|
||||
else {
|
||||
BString err;
|
||||
err << "LiveNodeIO: unexpected element '" <<
|
||||
context.element() << "'\n";
|
||||
context.reportError(err.String());
|
||||
}
|
||||
|
||||
delete child;
|
||||
}
|
||||
|
||||
void LiveNodeIO::xmlImportComplete(
|
||||
ImportContext& context) {}
|
||||
|
||||
// END -- LiveNodeIO.cpp --
|
129
src/apps/cortex/RouteApp/LiveNodeIO.h
Normal file
129
src/apps/cortex/RouteApp/LiveNodeIO.h
Normal file
@ -0,0 +1,129 @@
|
||||
// LiveNodeIO.h
|
||||
// * PURPOSE
|
||||
// Manage the import and export of an 'existing node'
|
||||
// descriptor -- which refers to a system node
|
||||
// (video input, audio mixer, etc.,) a dormant node
|
||||
// described in the same document, or some other external
|
||||
// node.
|
||||
//
|
||||
// In the first two cases, the node is described by a key
|
||||
// string; the following preset key strings correspond to
|
||||
// system nodes:
|
||||
//
|
||||
// AUDIO_INPUT
|
||||
// AUDIO_OUTPUT
|
||||
// AUDIO_MIXER
|
||||
// VIDEO_INPUT
|
||||
// VIDEO_OUTPUT
|
||||
// DAC_TIME_SOURCE
|
||||
// SYSTEM_TIME_SOURCE
|
||||
//
|
||||
// There's no way to describe a particular live node instance
|
||||
// in a persistent fashion (an instance of the same node created
|
||||
// in the future will have a different node ID.) At the moment,
|
||||
// LiveNodeIO describes such nodes via two pieces of information:
|
||||
// - the node name
|
||||
// - the node's 'kind' bitmask
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 20dec99 begun
|
||||
|
||||
#ifndef __LiveNodeIO_H__
|
||||
#define __LiveNodeIO_H__
|
||||
|
||||
#include "NodeRef.h"
|
||||
#include "XML.h"
|
||||
|
||||
#include <String.h>
|
||||
#include <Entry.h>
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeManager;
|
||||
class NodeSetIOContext;
|
||||
|
||||
class LiveNodeIO :
|
||||
public IPersistent {
|
||||
|
||||
public: // *** ctor/dtor
|
||||
virtual ~LiveNodeIO();
|
||||
|
||||
LiveNodeIO();
|
||||
LiveNodeIO(
|
||||
const NodeManager* manager,
|
||||
const NodeSetIOContext* context,
|
||||
media_node_id node);
|
||||
|
||||
bool exportValid() const { return m_exportValid; }
|
||||
|
||||
// if true, call key() to fetch the key string; otherwise,
|
||||
// call name()/kind()
|
||||
bool hasKey() const { return m_key.Length() > 0; }
|
||||
|
||||
const char* key() const { return m_key.String(); }
|
||||
|
||||
const char* name() const { return m_name.String(); }
|
||||
int64 kind() const { return m_kind; }
|
||||
|
||||
public: // *** import operations
|
||||
|
||||
// locate the referenced live node
|
||||
status_t getNode(
|
||||
const NodeManager* manager,
|
||||
const NodeSetIOContext* context,
|
||||
media_node_id* outNode) const;
|
||||
|
||||
public: // *** document-type setup
|
||||
static void AddTo(
|
||||
XML::DocumentType* docType);
|
||||
|
||||
public: // *** IPersistent
|
||||
|
||||
// EXPORT:
|
||||
|
||||
void xmlExportBegin(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportAttributes(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportContent(
|
||||
ExportContext& context) const;
|
||||
|
||||
void xmlExportEnd(
|
||||
ExportContext& context) const;
|
||||
|
||||
// IMPORT:
|
||||
|
||||
void xmlImportBegin(
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportAttribute(
|
||||
const char* key,
|
||||
const char* value,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportContent(
|
||||
const char* data,
|
||||
uint32 length,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportChild(
|
||||
IPersistent* child,
|
||||
ImportContext& context);
|
||||
|
||||
void xmlImportComplete(
|
||||
ImportContext& context);
|
||||
|
||||
private: // *** implementation
|
||||
|
||||
BString m_key;
|
||||
BString m_name;
|
||||
int64 m_kind;
|
||||
|
||||
bool m_exportValid;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__LiveNodeIO_H__*/
|
48
src/apps/cortex/RouteApp/NodeExportContext.h
Normal file
48
src/apps/cortex/RouteApp/NodeExportContext.h
Normal file
@ -0,0 +1,48 @@
|
||||
// NodeExportContext.h
|
||||
// * PURPOSE
|
||||
// Extends ExportContext to include a set of nodes
|
||||
// to be stored. This is an abstract class; implement
|
||||
// exportUIState() to store any user-interface state
|
||||
// info needed for the listed nodes.
|
||||
//
|
||||
// TO DO ++++++ GO AWAY! NodeSetIOContext provides the same
|
||||
// functionality for both import and export...
|
||||
//
|
||||
// * HISTORY
|
||||
// e.moon 7dec99 Begun
|
||||
|
||||
#ifndef __NodeExportContext_H__
|
||||
#define __NodeExportContext_H__
|
||||
|
||||
#include <vector>
|
||||
#include "ExportContext.h"
|
||||
#include "IStateArchivable.h"
|
||||
|
||||
#include "cortex_defs.h"
|
||||
__BEGIN_CORTEX_NAMESPACE
|
||||
|
||||
class NodeExportContext :
|
||||
public ExportContext {
|
||||
|
||||
public: // *** dtor/ctor
|
||||
virtual ~NodeExportContext() {}
|
||||
|
||||
NodeExportContext(
|
||||
BDataIO* _stream,
|
||||
BString& error) :
|
||||
ExportContext(_stream, error) {}
|
||||
|
||||
public: // *** members
|
||||
// set of nodes to export; if one or more nodes are
|
||||
// not fit to be exported, they may be removed or
|
||||
// set to media_node::null.node during the export process.
|
||||
typedef vector<media_node_id> media_node_set;
|
||||
media_node_set nodes;
|
||||
|
||||
public: // *** interface
|
||||
virtual void exportUIState(
|
||||
BMessage* archive) =0;
|
||||
};
|
||||
|
||||
__END_CORTEX_NAMESPACE
|
||||
#endif /*__NodeExportContext_H__*/
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user