File: [Development] / inventor / doc / man / ivman / ClassDef.c++ (download)
Revision 1.1.1.1 (vendor branch), Tue Aug 15 12:56:12 2000 UTC (17 years, 2 months ago) by naaman
Branch: sgi, MAIN
CVS Tags: start, release-2_1_5-9, release-2_1_5-8, release-2_1_5-10, HEAD Changes since 1.1: +0 -0
lines
Initial check-in based on 2.1.5 (SGI IRIX) source tree.
|
/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
#include <Inventor/SoLists.h>
#include <Inventor/SoPath.h>
#include <Inventor/engines/SoConcatenate.h>
#include <Inventor/engines/SoEngine.h>
#include <Inventor/engines/SoGate.h>
#include <Inventor/engines/SoSelectOne.h>
#include <Inventor/fields/SoField.h>
#include <Inventor/fields/SoMFTime.h>
#include <Inventor/fields/SoMFInt32.h>
#include <Inventor/fields/SoMFUInt32.h>
#include <Inventor/fields/SoSFTime.h>
#include <Inventor/fields/SoSFInt32.h>
#include <Inventor/fields/SoSFUInt32.h>
#include <Inventor/nodekits/SoBaseKit.h>
#include <Inventor/nodekits/SoNodekitCatalog.h>
#include "Man.h"
/////////////////////////////////////////////////////////////////////////////
//
// Class: ClassDef
//
/////////////////////////////////////////////////////////////////////////////
const ClassDef * ClassDef::curClass = NULL;
ClassDef::ClassDef()
{
parentClass = NULL;
}
ClassDef::~ClassDef()
{
if (type.isDerivedFrom(SoBase::getClassTypeId()) && instance != NULL)
((SoBase *) instance)->unref();
}
void
ClassDef::addInherit(ClassDef *def)
{
// FALSE = add to start of list
inherits.add(new Inherit(def->name), FALSE);
}
SbBool
ClassDef::read()
{
SbName nm;
SbBool ok = TRUE;
curClass = this;
// Read statements while they exist
while (ok && Reader::readName(nm)) {
// See what kind of statement we have
switch (getStatementType(nm)) {
case NAME:
ok = (Reader::readName(name) &&
Reader::readName(cPrefix) &&
Reader::readName(cEnumPrefix) &&
Reader::readBracketed(shortDescription));
if (! ok)
Reader::error("NAME spec");
break;
case DESCRIPTION:
ok = Reader::readBracketed(longDescription);
if (! ok)
Reader::error("DESC");
break;
case INCLUDE:
ok = readItem(new Include, &includes);
break;
case TYPEDEF:
ok = readItem(new Typedef, &typedefs);
break;
case DEFINE:
ok = readItem(new Define, &defines);
break;
case ENUM:
ok = readItem(new Enum, &enums);
break;
case FIELD:
ok = readItem(new Field, &fields);
break;
case INPUT:
ok = readItem(new Input, &inputs);
break;
case OUTPUT:
ok = readItem(new Output, &outputs);
break;
case PART:
ok = readItem(new Part, &parts);
break;
case METHOD:
ok = readItem(new Method, &methods);
break;
case DRAGGER_RESOURCE:
ok = readItem(new DraggerResource, &draggerResources);
break;
case MACRO:
ok = readItem(new Macro, ¯os);
break;
case ACTION:
ok = readItem(new Action, &actions);
break;
case MISC:
ok = readItem(new Misc, &miscs);
break;
case ALSO:
ok = readItem(new Also, &alsos);
break;
case BEGIN:
{
SbName langName;
ok = Reader::readName(langName);
if (ok) {
if (langName == "C")
Item::setCurLanguage(C);
else if (langName == "C++")
Item::setCurLanguage(C_PLUS_PLUS);
else
ok = FALSE;
}
if (! ok)
Reader::error("BEGIN language name");
}
break;
case END:
Item::setCurLanguage(ALL);
break;
default:
Reader::error("statement keyword");
ok = FALSE;
}
}
// If there's stuff left in the file, error
if (Reader::get() != EOF)
ok = FALSE;
return ok;
}
SbBool
ClassDef::readItem(Item *item, ItemList *list)
{
if (! item->read())
return FALSE;
list->add(item);
return TRUE;
}
ClassDef::StatementType
ClassDef::getStatementType(const SbName &keyword)
{
static SbName *names = NULL;
static const char *keywords[] = {
"NAME",
"DESC",
"INCLUDE",
"TYPEDEF",
"DEFINE",
"ENUM",
"FIELD",
"INPUT",
"OUTPUT",
"PART",
"METHOD",
"MACRO",
"ACTION",
"MISC",
"ALSO",
"BEGIN",
"END",
"DRAGGER_RESOURCE",
};
#define NUM_KEY_WORDS (sizeof(keywords) / sizeof(keywords[0]))
int i;
if (names == NULL) {
names = new SbName[NUM_KEY_WORDS];
for (i = 0; i < NUM_KEY_WORDS; i++)
names[i] = keywords[i];
}
for (i = 0; i < NUM_KEY_WORDS; i++)
if (keyword == keywords[i])
return (StatementType) i;
return UNKNOWN;
}
SbBool
ClassDef::setUp(const ClassDef *mainDef)
{
curClass = this;
// Find out the type of the class, if any. Remove the "So" from
// the name, since the names don't contain the prefix.
// But first check to see if the prefix is Sb. If so, don't setup
// up the file format section.
SbBool isSb = strncmp(name.getString(), "Sb", 2) ? FALSE : TRUE;
type = SoType::fromName(name.getString() + 2);
// Get an instance of the class, if possible. If this is not the
// main ClassDef (mainDef is NULL), we will use an instance of the
// main ClassDef for this class as well, rather than choosing one
// randomly in getInstance().
if (mainDef != NULL)
instance = mainDef->instance;
else if (type.isBad() || isSb)
instance = NULL;
else {
instance = getInstance();
// Ref instance if necessary to keep it around
if (type.isDerivedFrom(SoBase::getClassTypeId()) && instance != NULL)
((SoBase *) instance)->ref();
}
// Set the C prefix to the class name, unless it was specified
if (Writer::getLanguage() == C && ! cPrefix)
cPrefix = name;
if (Writer::getLanguage() == C)
setUpTypedefs();
if (! fields.isEmpty() && ! setUpFields())
return FALSE;
if (! inputs.isEmpty() && ! setUpInputs())
return FALSE;
if (! outputs.isEmpty() && ! setUpOutputs())
return FALSE;
// Look up part types (if any)
if (! parts.isEmpty() && ! setUpParts())
return FALSE;
// Produce nodekit catalog and file format if necessary
if (! isSb) {
setUpCatalog();
setUpFileFormat();
}
return TRUE;
}
SbBool
ClassDef::setUpTypedefs()
{
// The C language version has a typedef for the class if the
// prefix is not the same as the class name
if (cPrefix != name) {
Typedef *def = new Typedef;
def->language = C;
def->type = name.getString();
def->name = cPrefix;
typedefs.add(def);
}
return TRUE;
}
SbBool
ClassDef::setUpFields()
{
if (instance == NULL ||
! type.isDerivedFrom(SoFieldContainer::getClassTypeId())) {
fprintf(stderr, "Can't create a (non-abstract) field container from "
"class \"%s\"\n", name.getString());
return FALSE;
}
SoFieldContainer *fc = (SoFieldContainer *) instance;
for (Item *item = fields.first; item != NULL; item = item->next) {
Field *f = (Field *) item;
SoField *field = fc->getField(f->name);
if (field == NULL) {
fprintf(stderr, "No field \"%s\" in class \"%s\"\n",
f->name.getString(), name.getString());
return FALSE;
}
SbString typeName = "So";
SoType fieldType = field->getTypeId();
if (fieldType == SoSFInt32::getClassTypeId()) {
typeName += "SFInt32";
} else if (fieldType == SoMFInt32::getClassTypeId()) {
typeName += "MFInt32";
} else if (fieldType == SoSFUInt32::getClassTypeId()) {
typeName += "SFUInt32";
} else if (fieldType == SoMFUInt32::getClassTypeId()) {
typeName += "MFUInt32";
} else
typeName += fieldType.getName().getString();
f->type = typeName;
}
return TRUE;
}
SbBool
ClassDef::setUpInputs()
{
if (instance == NULL ||
! type.isDerivedFrom(SoEngine::getClassTypeId())) {
fprintf(stderr, "Can't create a (non-abstract) engine from "
"class \"%s\"\n", name.getString());
return FALSE;
}
SoEngine *eng = (SoEngine *) instance;
for (Item *item = inputs.first; item != NULL; item = item->next) {
Input *in = (Input *) item;
SoField *input = eng->getField(in->name);
if (input == NULL) {
fprintf(stderr,
"No input \"%s\" in class \"%s\"\n"
"Assuming <inputType>\n",
in->name.getString(), name.getString());
SbString typeName = "<inputType>";
in->type = typeName;
}
else {
SbString typeName = "So";
SoType inputType = input->getTypeId();
if (inputType == SoSFInt32::getClassTypeId()) {
typeName += "SFInt32";
} else if (inputType == SoMFInt32::getClassTypeId()) {
typeName += "MFInt32";
} else if (inputType == SoSFUInt32::getClassTypeId()) {
typeName += "SFUInt32";
} else if (inputType == SoMFUInt32::getClassTypeId()) {
typeName += "MFUInt32";
} else
typeName += inputType.getName().getString();
in->type = typeName;
}
}
return TRUE;
}
SbBool
ClassDef::setUpOutputs()
{
if (instance == NULL ||
! type.isDerivedFrom(SoEngine::getClassTypeId())) {
fprintf(stderr, "Can't create a (non-abstract) engine from "
"class \"%s\"\n", name.getString());
return FALSE;
}
SoEngine *eng = (SoEngine *) instance;
for (Item *item = outputs.first; item != NULL; item = item->next) {
Output *out = (Output *) item;
SoEngineOutput *output = eng->getOutput(out->name);
if (output == NULL) {
fprintf(stderr, "No output \"%s\" in class \"%s\"\n"
"Assuming <outputType>\n",
out->name.getString(), name.getString());
SbString typeName = "<outputType>";
out->type = typeName;
}
else {
SbString typeName = "So";
SoType outputType = output->getConnectionType();
if (outputType == SoSFInt32::getClassTypeId()) {
typeName += "SFInt32";
} else if (outputType == SoMFInt32::getClassTypeId()) {
typeName += "MFInt32";
} else if (outputType == SoSFUInt32::getClassTypeId()) {
typeName += "SFUInt32";
} else if (outputType == SoMFUInt32::getClassTypeId()) {
typeName += "MFUInt32";
} else
typeName += outputType.getName().getString();
out->type = typeName;
}
}
return TRUE;
}
SbBool
ClassDef::setUpParts()
{
if (instance == NULL ||
! type.isDerivedFrom(SoBaseKit::getClassTypeId())) {
fprintf(stderr, "Can't create a (non-abstract) node kit from "
"class \"%s\"\n", name.getString());
return FALSE;
}
SoBaseKit *kit = (SoBaseKit *) instance;
for (Item *item = parts.first; item != NULL; item = item->next) {
Part *part = (Part *) item;
const SoNodekitCatalog *cat = kit->getNodekitCatalog();
SoType type = cat->getType(part->name);
if (type.isBad()) {
fprintf(stderr,"No part \"%s\" in class \"%s\"\n",
part->name.getString(), name.getString());
return FALSE;
}
SbString typeName = "So";
typeName += type.getName().getString();
part->type = typeName;
}
return TRUE;
}
SbBool
ClassDef::setUpCatalog()
{
int i;
if (instance == NULL ||
! type.isDerivedFrom(SoBaseKit::getClassTypeId())) {
return TRUE;
}
SoBaseKit *kit = (SoBaseKit *) instance;
const SoNodekitCatalog *cat = kit->getNodekitCatalog();
//
// Main catalog part table
//
// The .ne (need) request makes sure the smallest table fits on
// the page. For some reason, tbl gets the wrong value for its own
// .ne, so this hack fixes up some simple cases.
// First, figure out how many entries are gonna be in the table:
int numInPublicTable = 0;
for (i = 0; i < cat->getNumEntries(); i++ )
if (cat->isPublic(i))
numInPublicTable++;
// Next, print a .ne command to a string, adding our fudge number:
#define PUBLIC_TBL_FUDGE_NUM 9
char nePublicString[20];
sprintf(nePublicString, ".ne %d\n", PUBLIC_TBL_FUDGE_NUM+numInPublicTable);
catalog = nePublicString;
catalog += ".TS\n"
"box, tab(!);\n"
"cb s s s\n"
"lb lb lb lb\n"
"lb lb lb lb\n"
"l l l c.\n"
"All parts\n"
"!!!NULL by\n"
"Part Name!Part Type!Default Type!Default\n"
"\n";
for (i = 0; i < cat->getNumEntries(); i++ ) {
if (cat->isPublic(i)) {
// Part name
catalog += cat->getName(i).getString();
// Part type
catalog += "!";
catalog += cat->getType(i).getName().getString();
// Default type
catalog += "!";
if (cat->getDefaultType(i) != cat->getType(i))
catalog += cat->getDefaultType(i).getName().getString();
else
catalog += "--";
// NULL by default
catalog += "!";
catalog += (cat->isNullByDefault(i) ? "yes" : "no");
catalog += "\n";
}
}
catalog += "\n.TE\n";
//
// Catalog list part table
//
// Make sure there is at least one list first
SbBool anyLists = FALSE;
for (i = 0; i < cat->getNumEntries(); i++ ) {
if (cat->isList(i) && cat->isPublic(i)) {
anyLists = TRUE;
break;
}
}
if (anyLists) {
// The .ne (need) request makes sure the smallest table fits on
// the page. For some reason, tbl gets the wrong value for its own
// .ne, so this hack fixes up some simple cases.
// First, figure out how many entries are gonna be in the table:
int numInListTable = 0;
for (i = 0; i < cat->getNumEntries(); i++ )
if (cat->isList(i) && cat->isPublic(i))
numInListTable++;
// Next, print a .ne command to a string, adding our fudge number:
#define LIST_TBL_FUDGE_NUM 9
char neListSTring[20];
sprintf(neListSTring, ".ne %d\n", LIST_TBL_FUDGE_NUM+numInListTable);
catalog += neListSTring;
catalog += ".TS\n"
"box, tab(!);\n"
"cb s s\n"
"lb lb lbw(30n)\n"
"l l l.\n"
"Extra information for list parts from above table\n"
".sp\n"
"Part Name!Container Type!Permissible Types\n"
"\n";
for (i = 0; i < cat->getNumEntries(); i++ ) {
if (cat->isList(i) && cat->isPublic(i)) {
// Part name
catalog += cat->getName(i).getString();
// Container type
catalog += "!";
catalog += cat->getListContainerType(i).getName().getString();
// Permissible types
// Note: type names are entered within T{ and T}
// blocks so that they can be formatted when necessary.
catalog += "!T{\n";
const SoTypeList &permTypes = cat->getListItemTypes(i);
for (int j = 0; j < permTypes.getLength(); j++) {
catalog += permTypes[j].getName().getString();
if (j < permTypes.getLength() - 1)
catalog += ", ";
}
catalog += "\nT}";
catalog += "\n";
}
}
catalog += "\n.TE\n";
}
return TRUE;
}
SbBool
ClassDef::setUpFileFormat()
{
hasFileFormat = FALSE;
fileFormat.makeEmpty();
fileFormatTabLength = -1;
// We need file format if the class is derived from SoNode or SoEngine
if (type.isDerivedFrom(SoNode::getClassTypeId()) ||
type.isDerivedFrom(SoEngine::getClassTypeId())) {
// Abstract class?
if (isAbstract) {
fileFormat = "This is an abstract class. See the reference page "
"of a derived class for the format and default values.\n";
hasFileFormat = TRUE;
}
else if (type.isDerivedFrom(SoBaseKit::getClassTypeId())) {
////////////////////////////////////////////////////////
//
// Special case for nodekits. They print their fields in
// a different order than they are defined.
//
if (instance == NULL)
return TRUE;
SoBaseKit *kit = (SoBaseKit *) instance;
const SoNodekitCatalog *cat = kit->getNodekitCatalog();
SoFieldList list;
kit->getFields(list);
SoField *field;
int partNum, i;
SbString fieldVal;
SbName fieldName;
int len;
// First, write out fields that are NOT parts...
// Whatever they are, just write out the field value.
for (i = 0; i < list.getLength(); i++) {
field = list[i];
kit->getFieldName(field, fieldName);
partNum = cat->getPartNumber(fieldName);
if (partNum == SO_CATALOG_NAME_NOT_FOUND) {
field->get(fieldVal);
fileFormat += fieldName.getString();
fileFormat += "\t";
fileFormat += fieldVal;
fileFormat += "\n";
len = fieldName.getLength();
if (len > fileFormatTabLength)
fileFormatTabLength = len;
}
}
// Note: we skip all of this for the BOOK version...
if (! Writer::isForBook()) {
// Second, write out fields that are PUBLIC PARTS.
// Private parts do not get written in the man page.
// When writing parts, we write <"resourcename"
// resource> if the node is named in the global
// database. Otherwise, we write out the scene graph.
SoNode *partNode;
for (i = 0; i < list.getLength(); i++) {
field = list[i];
kit->getFieldName(field, fieldName);
partNum = cat->getPartNumber(fieldName);
if (partNum != SO_CATALOG_NAME_NOT_FOUND &&
cat->isPublic(partNum)) {
partNode = ((SoSFNode *)field)->getValue();
if (partNode == NULL || !partNode->getName()) {
// If the node has no name in the global dictionary
// or is NULL, print out the value of the node.
// This might turn out to be an entire scene graph!
field->get(fieldVal);
}
else {
// Just print out the name of the node.
fieldVal = "<";
fieldVal += partNode->getName().getString();
fieldVal += " resource>";
}
fileFormat += fieldName.getString();
fileFormat += "\t";
fileFormat += fieldVal;
fileFormat += "\n";
len = fieldName.getLength();
if (len > fileFormatTabLength)
fileFormatTabLength = len;
}
}
// Finally, dragger resources may exist for parts that are in
// nested draggers. Write out these nested partNames
// and <"resourcename" resource>
if (! draggerResources.isEmpty()) {
Item *item;
for (item = draggerResources.first;
item != NULL; item = item->next ) {
DraggerResource *res = (DraggerResource *) item;
// Check if any of the parts that use this resource
// do not simply match the partNames in the catalog.
// If not, we write out the partName and resource.
for (int j = 0; j < res->getNumPartDefs(); j++ ) {
const SbName pDefName = res->getPartDefName(j);
if ( cat->getPartNumber(pDefName)
== SO_CATALOG_NAME_NOT_FOUND ) {
// Write it all out...
fieldVal = "<";
fieldVal += res->getResourceName().getString();
fieldVal += " resource>";
fileFormat += pDefName.getString();
fileFormat += "\t";
fileFormat += fieldVal;
fileFormat += "\n";
len = pDefName.getLength();
if (len > fileFormatTabLength)
fileFormatTabLength = len;
}
}
}
}
}
hasFileFormat = TRUE;
//
////////////////////////////////////////////////////////
}
else {
SoFieldContainer *fc = (SoFieldContainer *) instance;
////////////////////////////////////////////////////////
//
// Special case for engines whose constructor takes the
// type of field to operate on
//
if (fc->isOfType(SoConcatenate::getClassTypeId()))
fileFormat += "type\t<inputType>\n"
"input0\t[]\n"
"input1\t[]\n"
"input2\t[]\n"
"input3\t[]\n"
"input4\t[]\n"
"input5\t[]\n"
"input6\t[]\n"
"input7\t[]\n"
"input8\t[]\n"
"input9\t[]\n";
else if (fc->isOfType(SoGate::getClassTypeId()) ||
fc->isOfType(SoSelectOne::getClassTypeId()))
fileFormat += "type\t<inputType>\ninput\t[]\n";
//
////////////////////////////////////////////////////////
// Make sure each field does not have a default value
SoFieldList list;
fc->getFields(list);
for (int i = 0; i < list.getLength(); i++) {
SoField *field = list[i];
SbString fieldVal;
SbName fieldName;
int len;
fc->getFieldName(field, fieldName);
// Special case for time fields connected to the clock
if (field->isConnected() &&
(field->isOfType(SoSFTime::getClassTypeId()) ||
field->isOfType(SoMFTime::getClassTypeId())))
fieldVal = "<current time>";
else
field->get(fieldVal);
fileFormat += fieldName.getString();
fileFormat += "\t";
fileFormat += fieldVal;
fileFormat += "\n";
len = fieldName.getLength();
if (len > fileFormatTabLength)
fileFormatTabLength = len;
}
hasFileFormat = TRUE;
}
}
return TRUE;
}
void *
ClassDef::getInstance()
{
void *inst;
isAbstract = ! type.canCreateInstance();
// If the given class is not abstract, return an instance of it
if (! isAbstract)
inst = type.createInstance();
else {
// Otherwise, find all classes derived from the class and
// return the first one that is not abstract
SoTypeList list;
SoType::getAllDerivedFrom(type, list);
inst = NULL;
for (int i = 0; i < list.getLength(); i++) {
if (list[i].canCreateInstance()) {
inst = list[i].createInstance();
break;
}
}
}
// No non-abstract classes
return inst;
}
void
ClassDef::cullMethods()
{
////////////////////////////////////////////
//
// This method is called on the main ClassDef - the one the man
// page is for. It removes any method from an inherited class that:
// (1) is a constructor,
// or (2) has the same name as a method on a more derived class,
// or (3) is a destructor and the language is C++.
//
// It also removes hidden methods from the main ClassDef and all
// inherited ClassDefs.
//
////////////////////////////////////////////
ClassDef *cd, *inheritedCd;
Method *meth, *inhMeth, *prevMeth;
//
// Pass 1: Mark all constructors and destructors
//
for (cd = parentClass; cd != NULL; cd = cd->parentClass) {
for (meth = (Method *) cd->methods.first;
meth != NULL;
meth = (Method *) meth->next) {
// Remove constructors
if (meth->type == Method::CONSTRUCTOR)
meth->skipThis = TRUE;
// Remove destructors from C++
else if (meth->type == Method::DESTRUCTOR &&
Writer::getLanguage() == C_PLUS_PLUS)
meth->skipThis = TRUE;
}
}
//
// Pass 2: Mark any method with the same name as in a more
// derived class. This is a slow way to do this, but it shouldn't
// be too bad in most cases.
//
for (cd = this; cd != NULL; cd = cd->parentClass) {
for (meth = (Method *) cd->methods.first;
meth != NULL;
meth = (Method *) meth->next) {
if (! meth->skipThis) {
// Look through inherited classes for a method with
// the same name
for (inheritedCd = cd->parentClass; inheritedCd != NULL;
inheritedCd = inheritedCd->parentClass) {
if (Writer::getLanguage() == C) {
for (inhMeth = (Method *) inheritedCd->methods.first;
inhMeth != NULL;
inhMeth = (Method *) inhMeth->next) {
if (inhMeth->cName == meth->cName)
inhMeth->skipThis = TRUE;
}
}
else {
for (inhMeth = (Method *) inheritedCd->methods.first;
inhMeth != NULL;
inhMeth = (Method *) inhMeth->next) {
if (inhMeth->name == meth->name)
inhMeth->skipThis = TRUE;
}
}
}
}
}
}
//
// Pass 3: Mark hidden methods
//
for (cd = this; cd != NULL; cd = cd->parentClass) {
for (meth = (Method *) cd->methods.first;
meth != NULL;
meth = (Method *) meth->next) {
if (meth->isHidden)
meth->skipThis = TRUE;
}
}
//
// Pass 4: Actually remove marked methods from lists
//
for (cd = this; cd != NULL; cd = cd->parentClass) {
prevMeth = NULL;
for (meth = (Method *) cd->methods.first;
meth != NULL;
meth = (Method *) meth->next) {
if (meth->skipThis) {
if (prevMeth == NULL)
cd->methods.first = meth->next;
else
prevMeth->next = meth->next;
}
else
prevMeth = meth;
}
}
}
void
ClassDef::write(SbBool)
{
curClass = this;
Writer::writePreface(name);
if (Writer::isForBook())
writeForBook();
else
writeForNonBook();
}
void
ClassDef::writeForBook()
{
ClassDef *cd;
SbBool anyItem;
////////////////////////////////////////////////////////
//
// NAME section
//
writeName();
////////////////////////////////////////////////////////
//
// INHERITS FROM section
//
Writer::startSection("INHERITS FROM");
inherits.write();
////////////////////////////////////////////////////////
//
// DESCRIPTION section
//
Writer::startSection("DESCRIPTION");
Writer::writeBracketed(longDescription);
////////////////////////////////////////////////////////
//
// FIELDS section
//
anyItem = FALSE;
for (cd = this; cd != NULL; cd = cd->parentClass) {
if (! cd->fields.isEmpty()) {
anyItem = TRUE;
break;
}
}
if (anyItem) {
Writer::startSection("FIELDS");
if (! fields.isEmpty())
fields.write();
for (cd = parentClass; cd != NULL; cd = cd->parentClass)
if (! cd->fields.isEmpty())
cd->writeFromClass(&cd->fields, "Fields", TRUE);
}
////////////////////////////////////////////////////////
//
// INPUTS section
//
anyItem = FALSE;
for (cd = this; cd != NULL; cd = cd->parentClass) {
if (! cd->inputs.isEmpty()) {
anyItem = TRUE;
break;
}
}
if (anyItem) {
Writer::startSection("INPUTS");
if (! inputs.isEmpty())
inputs.write();
for (cd = parentClass; cd != NULL; cd = cd->parentClass)
if (! cd->inputs.isEmpty())
cd->writeFromClass(&cd->inputs, "Inputs", TRUE);
}
////////////////////////////////////////////////////////
//
// OUTPUTS section
//
anyItem = FALSE;
for (cd = this; cd != NULL; cd = cd->parentClass) {
if (! cd->outputs.isEmpty()) {
anyItem = TRUE;
break;
}
}
if (anyItem) {
Writer::startSection("OUTPUTS");
if (! outputs.isEmpty())
outputs.write();
for (cd = parentClass; cd != NULL; cd = cd->parentClass)
if (! cd->outputs.isEmpty())
cd->writeFromClass(&cd->outputs, "Outputs", TRUE);
}
////////////////////////////////////////////////////////
//
// PARTS section
//
anyItem = FALSE;
for (cd = this; cd != NULL; cd = cd->parentClass) {
if (! cd->parts.isEmpty()) {
anyItem = TRUE;
break;
}
}
if (anyItem) {
Writer::startSection("PARTS");
if (! parts.isEmpty())
parts.write();
for (cd = parentClass; cd != NULL; cd = cd->parentClass)
if (! cd->parts.isEmpty())
cd->writeFromClass(&cd->parts, "Parts", TRUE);
}
////////////////////////////////////////////////////////
//
// METHODS section
//
anyItem = FALSE;
for (cd = this; cd != NULL; cd = cd->parentClass) {
if (! cd->methods.isEmpty()) {
anyItem = TRUE;
break;
}
}
if (anyItem) {
Writer::startSection(Writer::getLanguage() == C ?
"FUNCTIONS" : "METHODS");
if (! methods.isEmpty())
methods.write();
for (cd = parentClass; cd != NULL; cd = cd->parentClass)
if (! cd->methods.isEmpty())
cd->writeFromClass(&cd->methods,
(Writer::getLanguage() == C ?
"Functions" : "Methods"),
TRUE);
}
////////////////////////////////////////////////////////
//
// MACROS section
//
anyItem = FALSE;
for (cd = this; cd != NULL; cd = cd->parentClass) {
if (! cd->macros.isEmpty()) {
anyItem = TRUE;
break;
}
}
if (anyItem) {
Writer::startSection("MACROS");
if (! macros.isEmpty())
macros.write();
for (cd = parentClass; cd != NULL; cd = cd->parentClass)
if (! cd->macros.isEmpty())
cd->writeFromClass(&cd->macros, "Macros", TRUE);
}
////////////////////////////////////////////////////////
//
// ACTION BEHAVIOR section
//
if (! actions.isEmpty()) {
Writer::startSection("ACTION BEHAVIOR");
actions.write();
}
////////////////////////////////////////////////////////
//
// CATALOG PARTS section
//
if (! ! catalog) {
Writer::startSection("CATALOG PARTS");
Writer::write(catalog);
}
////////////////////////////////////////////////////////
//
// FILE FORMAT section
//
writeFileFormat();
////////////////////////////////////////////////////////
//
// Include files
//
if (! includes.isEmpty()) {
Writer::startSection("INCLUDE FILE");
Writer::changeSize(-1);
// Include files for this class
includes.write();
// XXX we needed to list these things somewhere,
// so here they are --AE
// Typedefs for this class
typedefs.write();
// Definitions for this class
defines.write();
// Enums for this class
enums.write();
}
////////////////////////////////////////////////////////
//
// Miscellaneous sections
//
if (! miscs.isEmpty())
miscs.write();
////////////////////////////////////////////////////////
//
// SEE ALSO section
//
if (! alsos.isEmpty()) {
Writer::startSection("SEE ALSO");
alsos.write();
}
////////////////////////////////////////////////////////
Writer::finish();
}
void
ClassDef::writeForNonBook()
{
////////////////////////////////////////////////////////
//
// NAME section
//
writeName();
////////////////////////////////////////////////////////
//
// INHERITS FROM section
//
Writer::startSection("INHERITS FROM");
inherits.write();
////////////////////////////////////////////////////////
//
// SYNOPSIS section
//
writeSynopsis();
////////////////////////////////////////////////////////
//
// DESCRIPTION section
//
Writer::startSection("DESCRIPTION");
Writer::writeBracketed(longDescription);
////////////////////////////////////////////////////////
//
// FIELDS section
//
if (! fields.isEmpty()) {
Writer::startSection("FIELDS");
fields.write();
}
////////////////////////////////////////////////////////
//
// INPUTS section
//
if (! inputs.isEmpty()) {
Writer::startSection("INPUTS");
inputs.write();
}
////////////////////////////////////////////////////////
//
// OUTPUTS section
//
if (! outputs.isEmpty()) {
Writer::startSection("OUTPUTS");
outputs.write();
}
////////////////////////////////////////////////////////
//
// PARTS section
//
if (! parts.isEmpty()) {
Writer::startSection("PARTS");
parts.write();
}
////////////////////////////////////////////////////////
//
// METHODS section
//
if (! methods.isEmpty()) {
Writer::startSection(Writer::getLanguage() == C ?
"FUNCTIONS" : "METHODS");
methods.write();
}
////////////////////////////////////////////////////////
//
// MACROS section
//
if (! macros.isEmpty()) {
Writer::startSection("MACROS");
macros.write();
}
////////////////////////////////////////////////////////
//
// ACTION BEHAVIOR section
//
if (! actions.isEmpty()) {
Writer::startSection("ACTION BEHAVIOR");
actions.write();
}
////////////////////////////////////////////////////////
//
// CATALOG PARTS section
//
if (! ! catalog) {
Writer::startSection("CATALOG PARTS");
Writer::write(catalog);
}
////////////////////////////////////////////////////////
//
// DRAGGER RESOURCES section (for draggers)
//
if (! draggerResources.isEmpty()) {
Writer::startSection("DRAGGER RESOURCES");
draggerResources.write();
}
////////////////////////////////////////////////////////
//
// FILE FORMAT section
//
writeFileFormat();
////////////////////////////////////////////////////////
//
// Miscellaneous sections
//
if (! miscs.isEmpty())
miscs.write();
////////////////////////////////////////////////////////
//
// SEE ALSO section
//
if (! alsos.isEmpty()) {
Writer::startSection("SEE ALSO");
alsos.write();
}
////////////////////////////////////////////////////////
Writer::finish();
}
void
ClassDef::writeName()
{
Writer::startSection("NAME");
Writer::write(name);
if (Writer::getLanguage() == C) {
Writer::write(" (");
Writer::write(cPrefix);
Writer::write(") \\(em ");
}
else
Writer::write(" \\(em ");
Writer::writeBracketed(shortDescription);
}
void
ClassDef::writeSynopsis()
{
ClassDef *cd;
Writer::setInSynopsis(TRUE);
Writer::startSection("SYNOPSIS");
Writer::changeSize(-1);
// Include files for this class
includes.write();
// Typedefs for this class
typedefs.write();
// Definitions for this class
defines.write();
// Enums for this class
enums.write();
// Fields for this class, then all parent classes
for (cd = this; cd != NULL; cd = cd->parentClass)
if (! cd->fields.isEmpty())
cd->writeFromClass(&cd->fields, "Fields");
// Inputs for this class, then all parent classes
for (cd = this; cd != NULL; cd = cd->parentClass)
if (! cd->inputs.isEmpty())
cd->writeFromClass(&cd->inputs, "Inputs");
// Outputs for this class, then all parent classes
for (cd = this; cd != NULL; cd = cd->parentClass)
if (! cd->outputs.isEmpty())
cd->writeFromClass(&cd->outputs, "Outputs");
// Parts for this class, then all parent classes
for (cd = this; cd != NULL; cd = cd->parentClass)
if (! cd->parts.isEmpty())
cd->writeFromClass(&cd->parts, "Parts");
// Methods for this class, then all parent classes
for (cd = this; cd != NULL; cd = cd->parentClass)
if (! cd->methods.isEmpty())
cd->writeFromClass(&cd->methods,
(Writer::getLanguage() == C ?
"Functions" : "Methods"));
// Macros for this class, then all parent classes
for (cd = this; cd != NULL; cd = cd->parentClass)
if (! cd->macros.isEmpty())
cd->writeFromClass(&cd->macros, "Macros");
Writer::setInSynopsis(FALSE);
}
void
ClassDef::writeFileFormat()
{
// Paths have a special file format:
if (type == SoPath::getClassTypeId()) {
Writer::startSection("FILE FORMAT/DEFAULTS");
Writer::setFill(FALSE);
Writer::setFont(Writer::FIXED_ROMAN);
Writer::write("SoPath {\n");
Writer::setBaseIndent(0.5);
Writer::write("[head node]\n"
"[number of remaining indices]\n"
"[index]\n"
"\\&...\n"
"[index]\n");
Writer::setBaseIndent(0.0);
Writer::write("}\n\n");
Writer::setFill(TRUE);
Writer::setFont(Writer::TEXT_ROMAN);
Writer::write("Note that the indices in a written path are "
"adjusted based on the nodes that are actually "
"written to a file. Since nodes in the graph "
"that have no effect on the path (such as some "
"separator nodes) are not written, the siblings "
"of such nodes must undergo index adjustment "
"when written. The actual nodes in the graph "
"remain unchanged.\n");
}
else if (hasFileFormat) {
Writer::startSection("FILE FORMAT/DEFAULTS");
if (isAbstract)
Writer::write(fileFormat);
else {
Writer::setFill(FALSE);
Writer::setFont(Writer::FIXED_ROMAN);
// Remove "So" prefix from name
Writer::write(name.getString() + 2);
Writer::write(" {\n");
// Classes with no fields have an empty fileFormat string
if (! ! fileFormat) {
Writer::setBaseIndent(0.5);
Writer::setTab(fileFormatTabLength +
(fileFormatTabLength < 0 ? 0 : 2));
Writer::write(fileFormat);
Writer::setBaseIndent(0.0);
}
Writer::write("}\n");
}
}
}
void
ClassDef::writeFromClass(ItemList *list, const char *whats, SbBool runIn)
{
if (Writer::isForBook()) {
Writer::setBaseIndent(0.0);
Writer::setFont(Writer::TEXT_BOLD);
}
else {
Writer::setBaseIndent(0.5);
Writer::setFont(Writer::TEXT_ROMAN);
}
Writer::write(whats);
Writer::write(" from class ");
Writer::write(name);
Writer::write(":\n");
if (Writer::isForBook()) {
Writer::setBaseIndent(0.5);
Writer::space(.25);
}
else {
Writer::setBaseIndent(0.0);
Writer::space();
}
list->write(runIn);
}