*** empty log message ***
This commit is contained in:
parent
632c90daac
commit
979b4096e6
|
|
@ -13,6 +13,7 @@
|
|||
#include <buttonHandle.h>
|
||||
#include <buttonEvent.h>
|
||||
#include <buttonEventDataAttribute.h>
|
||||
#include <pointerTo.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ClientButtonDevice
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
TypeHandle MouseAndKeyboard::_type_handle;
|
||||
|
||||
TypeHandle MouseAndKeyboard::_mods_type;
|
||||
TypeHandle MouseAndKeyboard::_pixel_xyz_type;
|
||||
TypeHandle MouseAndKeyboard::_xyz_type;
|
||||
TypeHandle MouseAndKeyboard::_button_events_type;
|
||||
|
|
@ -35,17 +34,14 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const string& name) :
|
|||
_window(window),
|
||||
_device(device)
|
||||
{
|
||||
_mods = new ModifierButtonDataAttribute();
|
||||
_pixel_xyz = new Vec3DataAttribute(LPoint3f(0, 0, 0));
|
||||
_xyz = new Vec3DataAttribute(LPoint3f(0, 0, 0));
|
||||
_button_events = new ButtonEventDataAttribute();
|
||||
|
||||
_got_mouse_attrib.set_attribute(_mods_type, _mods);
|
||||
_got_mouse_attrib.set_attribute(_pixel_xyz_type, _pixel_xyz);
|
||||
_got_mouse_attrib.set_attribute(_xyz_type, _xyz);
|
||||
_got_mouse_attrib.set_attribute(_button_events_type, _button_events);
|
||||
|
||||
_no_mouse_attrib.set_attribute(_mods_type, _mods);
|
||||
_no_mouse_attrib.set_attribute(_button_events_type, _button_events);
|
||||
}
|
||||
|
||||
|
|
@ -63,9 +59,6 @@ transmit_data(NodeAttributes &data) {
|
|||
_button_events->push_back(be);
|
||||
}
|
||||
|
||||
// Get modifier buttons
|
||||
_mods->set_mods(_window->get_modifier_buttons(_device));
|
||||
|
||||
if (_window->has_pointer(_device)) {
|
||||
const MouseData &mdata = _window->get_mouse_data(_device);
|
||||
|
||||
|
|
@ -105,9 +98,6 @@ init_type() {
|
|||
register_type(_type_handle, "MouseAndKeyboard",
|
||||
DataNode::get_class_type());
|
||||
|
||||
ModifierButtonDataTransition::init_type();
|
||||
register_data_transition(_mods_type, "ModifierButtons",
|
||||
ModifierButtonDataTransition::get_class_type());
|
||||
Vec3DataTransition::init_type();
|
||||
register_data_transition(_pixel_xyz_type, "PixelXYZ",
|
||||
Vec3DataTransition::get_class_type());
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@
|
|||
#include <pandabase.h>
|
||||
|
||||
#include <dataNode.h>
|
||||
#include <modifierButtonDataTransition.h>
|
||||
#include <modifierButtonDataAttribute.h>
|
||||
#include <vec3DataTransition.h>
|
||||
#include <vec3DataAttribute.h>
|
||||
#include <buttonEventDataTransition.h>
|
||||
|
|
@ -57,7 +55,6 @@ public:
|
|||
public:
|
||||
NodeAttributes _got_mouse_attrib;
|
||||
NodeAttributes _no_mouse_attrib;
|
||||
PT(ModifierButtonDataAttribute) _mods;
|
||||
PT(Vec3DataAttribute) _pixel_xyz;
|
||||
PT(Vec3DataAttribute) _xyz;
|
||||
PT(ButtonEventDataAttribute) _button_events;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
buttonEventDataAttribute.h buttonEventDataTransition.I \
|
||||
buttonEventDataTransition.cxx buttonEventDataTransition.h \
|
||||
config_dgraph.cxx config_dgraph.h dataGraphTraversal.cxx \
|
||||
dataGraphTraversal.h dataNode.cxx dataNode.h dataRelation.I \
|
||||
dataGraphTraversal.h \
|
||||
dataGraphTraverser.I dataGraphTraverser.cxx dataGraphTraverser.h \
|
||||
dataNode.cxx dataNode.h dataRelation.I \
|
||||
dataRelation.N dataRelation.cxx dataRelation.h \
|
||||
describe_data_verbose.cxx describe_data_verbose.h \
|
||||
doubleDataAttribute.I doubleDataAttribute.cxx doubleDataAttribute.h \
|
||||
|
|
@ -24,9 +26,6 @@
|
|||
intDataTransition.h matrixDataAttribute.I matrixDataAttribute.cxx \
|
||||
matrixDataAttribute.h matrixDataTransition.I \
|
||||
matrixDataTransition.cxx matrixDataTransition.h \
|
||||
modifierButtonDataAttribute.I modifierButtonDataAttribute.cxx \
|
||||
modifierButtonDataAttribute.h modifierButtonDataTransition.I \
|
||||
modifierButtonDataTransition.cxx modifierButtonDataTransition.h \
|
||||
vec3DataAttribute.I vec3DataAttribute.cxx vec3DataAttribute.h \
|
||||
vec3DataTransition.I vec3DataTransition.cxx vec3DataTransition.h \
|
||||
vec4DataAttribute.I vec4DataAttribute.cxx vec4DataAttribute.h \
|
||||
|
|
@ -35,7 +34,8 @@
|
|||
#define INSTALL_HEADERS \
|
||||
buttonEventDataAttribute.I buttonEventDataAttribute.h \
|
||||
buttonEventDataTransition.I buttonEventDataTransition.h \
|
||||
dataGraphTraversal.h dataNode.h dataRelation.I dataRelation.h \
|
||||
dataGraphTraversal.h \
|
||||
dataNode.h dataRelation.I dataRelation.h \
|
||||
describe_data_verbose.h doubleDataAttribute.I doubleDataAttribute.h \
|
||||
doubleDataTransition.I doubleDataTransition.h \
|
||||
doublePtrDataAttribute.I doublePtrDataAttribute.h \
|
||||
|
|
@ -43,8 +43,6 @@
|
|||
intDataAttribute.I intDataAttribute.h intDataTransition.I \
|
||||
intDataTransition.h matrixDataAttribute.I matrixDataAttribute.h \
|
||||
matrixDataTransition.I matrixDataTransition.h \
|
||||
modifierButtonDataAttribute.I modifierButtonDataAttribute.h \
|
||||
modifierButtonDataTransition.I modifierButtonDataTransition.h \
|
||||
numericDataAttribute.I numericDataAttribute.h \
|
||||
numericDataTransition.I numericDataTransition.h \
|
||||
pointerDataAttribute.I pointerDataAttribute.h \
|
||||
|
|
|
|||
|
|
@ -7,12 +7,27 @@
|
|||
#include "buttonEventDataTransition.h"
|
||||
|
||||
#include <indent.h>
|
||||
|
||||
#include <modifierButtons.h>
|
||||
#include <algorithm>
|
||||
|
||||
TypeHandle ButtonEventDataAttribute::_type_handle;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ButtonEventDataAttribute::update_mods
|
||||
// Access: Public
|
||||
// Description: Updates the indicated ModifierButtons object with all
|
||||
// of the button up/down transitions indicated in the
|
||||
// queue.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void ButtonEventDataAttribute::
|
||||
update_mods(ModifierButtons &mods) const {
|
||||
Buttons::const_iterator bi;
|
||||
for (bi = _buttons.begin(); bi != _buttons.end(); ++bi) {
|
||||
mods.add_event(*bi);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ButtonEventDataAttribute::make_copy
|
||||
// Access: Public, Virtual
|
||||
|
|
@ -43,6 +58,38 @@ get_handle() const {
|
|||
return ButtonEventDataTransition::get_class_type();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ButtonEventDataAttribute::merge
|
||||
// Access: Public, Virtual
|
||||
// Description: Attempts to merge this attribute with the other one,
|
||||
// if that makes sense to do. Returns a new
|
||||
// NodeAttribute pointer that represents the merge, or
|
||||
// if the merge is not possible, returns the "other"
|
||||
// pointer unchanged (which is the result of the merge).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeAttribute *ButtonEventDataAttribute::
|
||||
merge(const NodeAttribute *other) const {
|
||||
const ButtonEventDataAttribute *oa;
|
||||
DCAST_INTO_R(oa, other, NULL);
|
||||
|
||||
if (_buttons.empty()) {
|
||||
return (ButtonEventDataAttribute *)oa;
|
||||
|
||||
} else if (oa->_buttons.empty()) {
|
||||
return (ButtonEventDataAttribute *)this;
|
||||
|
||||
} else {
|
||||
// We have to create a new data attribute that includes both sets
|
||||
// of buttons.
|
||||
ButtonEventDataAttribute *new_attrib =
|
||||
new ButtonEventDataAttribute(*this);
|
||||
|
||||
new_attrib->_buttons.insert(new_attrib->_buttons.end(),
|
||||
oa->_buttons.begin(), oa->_buttons.end());
|
||||
return new_attrib;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ButtonEventDataAttribute::output
|
||||
// Access: Public, Virtual
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <vector>
|
||||
|
||||
class ButtonEventDataTransition;
|
||||
class ModifierButtons;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ButtonEventDataAttribute
|
||||
|
|
@ -42,12 +43,16 @@ public:
|
|||
INLINE void clear();
|
||||
INLINE void push_back(const ButtonEvent &event);
|
||||
|
||||
void update_mods(ModifierButtons &mods) const;
|
||||
|
||||
public:
|
||||
virtual NodeAttribute *make_copy() const;
|
||||
virtual NodeAttribute *make_initial() const;
|
||||
|
||||
virtual TypeHandle get_handle() const;
|
||||
|
||||
virtual NodeAttribute *merge(const NodeAttribute *other) const;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@
|
|||
#include "matrixDataAttribute.h"
|
||||
#include "buttonEventDataTransition.h"
|
||||
#include "buttonEventDataAttribute.h"
|
||||
#include "modifierButtonDataTransition.h"
|
||||
#include "modifierButtonDataAttribute.h"
|
||||
#include "dataGraphTraversal.h"
|
||||
|
||||
#include <dconfig.h>
|
||||
|
||||
|
|
@ -38,9 +35,6 @@ ConfigureFn(config_dgraph) {
|
|||
MatrixDataAttribute::init_type();
|
||||
ButtonEventDataTransition::init_type();
|
||||
ButtonEventDataAttribute::init_type();
|
||||
ModifierButtonDataTransition::init_type();
|
||||
ModifierButtonDataAttribute::init_type();
|
||||
DataGraphTraversal::init_spam_flag();
|
||||
|
||||
DataRelation::register_with_factory();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,82 +4,7 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "dataGraphTraversal.h"
|
||||
#include "dataNode.h"
|
||||
#include "dataRelation.h"
|
||||
#include "intDataTransition.h"
|
||||
#include "intDataAttribute.h"
|
||||
#include "config_dgraph.h"
|
||||
#include "describe_data_verbose.h"
|
||||
|
||||
#include <renderRelation.h>
|
||||
#include <dftraverser.h>
|
||||
#include <node.h>
|
||||
#include <typeHandle.h>
|
||||
|
||||
|
||||
// Here's the static member of DataGraphTraversal.
|
||||
TypeHandle DataGraphTraversal::_spam_flag_type;
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraversal::init_spam_flag
|
||||
// Access: Public, Static
|
||||
// Description: This function is called during static
|
||||
// initialization and registers a convenient type handle
|
||||
// for the spam flag.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataGraphTraversal::
|
||||
init_spam_flag() {
|
||||
// We shouldn't try to call this function more than once. This
|
||||
// function is only to be called during static initialization.
|
||||
nassertv(_spam_flag_type == TypeHandle::none());
|
||||
IntDataTransition::init_type();
|
||||
register_type(_spam_flag_type, "SpamFlagTransition",
|
||||
IntDataTransition::get_class_type());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphVisitor::reached_node
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool DataGraphVisitor::
|
||||
reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &) {
|
||||
// Is the spam flag set?
|
||||
bool has_spam_flag =
|
||||
state.has_attribute(DataGraphTraversal::_spam_flag_type);
|
||||
|
||||
if (has_spam_flag && dgraph_cat.is_info()) {
|
||||
dgraph_cat.info() << "Sending into " << *node << " {\n";
|
||||
describe_data_verbose(dgraph_cat.info(false), state.get_attributes(), 2);
|
||||
dgraph_cat.info(false) << "}\n";
|
||||
}
|
||||
|
||||
if (node->is_of_type(DataNode::get_class_type())) {
|
||||
DataNode *dnode = DCAST(DataNode, node);
|
||||
|
||||
// Now let the data node process the data itself.
|
||||
dnode->transmit_data(state.get_attributes());
|
||||
|
||||
if (dnode->get_spam_mode() && !has_spam_flag) {
|
||||
// If the node we pass through has the spam flag set, then we'll
|
||||
// set it on our current state, and insist that it gets through
|
||||
// to all our children.
|
||||
state.set_attribute(DataGraphTraversal::_spam_flag_type,
|
||||
new IntDataAttribute);
|
||||
has_spam_flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_spam_flag && !node->_children.empty() &&
|
||||
dgraph_cat.is_info()) {
|
||||
dgraph_cat.info() << "Receiving from " << *node << " {\n";
|
||||
describe_data_verbose(dgraph_cat.info(false), state.get_attributes(), 2);
|
||||
dgraph_cat.info(false) << "}\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#include "dataGraphTraverser.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -90,7 +15,6 @@ reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &) {
|
|||
// downwards.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void traverse_data_graph(Node *root) {
|
||||
DataGraphVisitor dgv;
|
||||
df_traverse(root, dgv, AllAttributesWrapper(),
|
||||
NullLevelState(), DataRelation::get_class_type());
|
||||
DataGraphTraverser dgt;
|
||||
dgt.traverse(root);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,46 +17,12 @@
|
|||
// This is the main interface to the data graph. Traversing the data
|
||||
// graph transmits data down the graph--reading data from devices and
|
||||
// sending it to the DataNodes that want it.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <pandabase.h>
|
||||
|
||||
#include <traverserVisitor.h>
|
||||
#include <allTransitionsWrapper.h>
|
||||
#include <allAttributesWrapper.h>
|
||||
#include <nullLevelState.h>
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : DataGraphTraversal
|
||||
// Description : This is mainly a holder for the static
|
||||
// _spam_flag_type TypeHandle, which is a registered
|
||||
// NumericDataAttribute that keeps track of the state of
|
||||
// the spam flag during data graph traversal. The class
|
||||
// otherwise doesn't do anything useful, but see the
|
||||
// traverse_data_graph() function, below.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA DataGraphTraversal {
|
||||
public:
|
||||
static void init_spam_flag();
|
||||
|
||||
static TypeHandle _spam_flag_type;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : DataGraphVisitor
|
||||
// Description : The TraverserVisitor to handle data graph traversals.
|
||||
// This manages the spam flag, and may eventually handle
|
||||
// merging of data from different parents into a single
|
||||
// node.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA DataGraphVisitor :
|
||||
public TraverserVisitor<AllTransitionsWrapper, NullLevelState> {
|
||||
public:
|
||||
bool reached_node(Node *node, AllAttributesWrapper &state, NullLevelState &);
|
||||
};
|
||||
|
||||
class Node;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: traverse_data_graph
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// Filename: dataGraphTraverser.I
|
||||
// Created by: drose (05Feb01)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE DataGraphTraverser::
|
||||
DataGraphTraverser() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::traverse
|
||||
// Access: Public
|
||||
// Description: Fires off the traversal, beginning at the indicated
|
||||
// node.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DataGraphTraverser::
|
||||
traverse(Node *node) {
|
||||
NodeAttributes empty;
|
||||
r_traverse(node, empty, false);
|
||||
resume_all();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::traverse_below
|
||||
// Access: Public
|
||||
// Description: Fires off the traversal, beginning *below* the
|
||||
// indicated node. The transmit_data() function is not
|
||||
// called on the starting node; instead, the indicated
|
||||
// data attributes are taken as the output of that node.
|
||||
//
|
||||
// If the node defines transmit_data_per_child(), that
|
||||
// function is still called, once for each child as
|
||||
// usual.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DataGraphTraverser::
|
||||
traverse_below(Node *node, const NodeAttributes &data) {
|
||||
NodeAttributes copy = data;
|
||||
r_traverse_below(node, copy, false);
|
||||
resume_all();
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
// Filename: dataGraphTraverser.cxx
|
||||
// Created by: drose (05Feb01)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "dataGraphTraverser.h"
|
||||
#include "dataRelation.h"
|
||||
#include "dataNode.h"
|
||||
#include "config_dgraph.h"
|
||||
#include "describe_data_verbose.h"
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::r_traverse
|
||||
// Access: Private
|
||||
// Description: The recursive implementation of traverse().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataGraphTraverser::
|
||||
r_traverse(Node *node, NodeAttributes &data, bool has_spam_mode) {
|
||||
DataNode *data_node = (DataNode *)NULL;
|
||||
if (node->is_of_type(DataNode::get_class_type())) {
|
||||
DCAST_INTO_V(data_node, node);
|
||||
|
||||
#ifndef NDEBUG
|
||||
has_spam_mode = has_spam_mode || data_node->get_spam_mode();
|
||||
#endif
|
||||
}
|
||||
|
||||
int num_parents = node->get_num_parents(DataRelation::get_class_type());
|
||||
|
||||
if (num_parents > 1) {
|
||||
// This node is multiply parented, so we must save it for later.
|
||||
save(node, data, has_spam_mode, num_parents);
|
||||
|
||||
} else {
|
||||
|
||||
// Now ask the node to transmit its overall data.
|
||||
if (data_node != (DataNode *)NULL) {
|
||||
#ifndef NDEBUG
|
||||
if (has_spam_mode && dgraph_cat.is_info()) {
|
||||
dgraph_cat.info() << "Sending into " << *node << " {\n";
|
||||
describe_data_verbose(dgraph_cat.info(false), data, 2);
|
||||
dgraph_cat.info(false) << "}\n";
|
||||
|
||||
} else if (dgraph_cat.is_spam()) {
|
||||
dgraph_cat.spam() << "Sending into " << *node << " {\n";
|
||||
describe_data_verbose(dgraph_cat.spam(false), data, 2);
|
||||
dgraph_cat.spam(false) << "}\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
data_node->transmit_data(data);
|
||||
}
|
||||
|
||||
r_traverse_below(node, data, has_spam_mode);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::r_traverse_below
|
||||
// Access: Private
|
||||
// Description: Resumes the traversal below the indicated node, given
|
||||
// the output from a previous call to
|
||||
// Node::transmit_data().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataGraphTraverser::
|
||||
r_traverse_below(Node *node, NodeAttributes &data, bool has_spam_mode) {
|
||||
DataNode *data_node = (DataNode *)NULL;
|
||||
if (node->is_of_type(DataNode::get_class_type())) {
|
||||
DCAST_INTO_V(data_node, node);
|
||||
|
||||
#ifndef NDEBUG
|
||||
has_spam_mode = has_spam_mode || data_node->get_spam_mode();
|
||||
#endif
|
||||
}
|
||||
|
||||
int num_children = node->get_num_children(DataRelation::get_class_type());
|
||||
|
||||
if (num_children > 0) {
|
||||
// For the first n - 1 children we need to make a copy of our
|
||||
// NodeAttributes for each one--this allows our children to modify
|
||||
// the set freely without affecting its siblings.
|
||||
|
||||
for (int i = 0; i < num_children - 1; i++) {
|
||||
NodeAttributes copy = data;
|
||||
if (data_node != (DataNode *)NULL) {
|
||||
data_node->transmit_data_per_child(copy, i);
|
||||
}
|
||||
|
||||
NodeRelation *arc =
|
||||
node->get_child(DataRelation::get_class_type(), i);
|
||||
r_traverse(arc->get_child(), copy, has_spam_mode);
|
||||
}
|
||||
|
||||
// For the last child, we don't have to bother making a copy since
|
||||
// no one cares any more. This is a slight optimization.
|
||||
if (data_node != (DataNode *)NULL) {
|
||||
data_node->transmit_data_per_child(data, num_children - 1);
|
||||
}
|
||||
|
||||
NodeRelation *arc =
|
||||
node->get_child(DataRelation::get_class_type(), num_children - 1);
|
||||
|
||||
if (dgraph_cat.is_spam()) {
|
||||
dgraph_cat.spam() << "Traversing " << *arc << "\n";
|
||||
}
|
||||
|
||||
r_traverse(arc->get_child(), data, has_spam_mode);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::save
|
||||
// Access: Private
|
||||
// Description: Saves the current state for later, until we have
|
||||
// extracted the data from all the parents of this node.
|
||||
// When the last parent is accounted for, resumes the
|
||||
// traversal for this node and removes it from the saved
|
||||
// queue.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataGraphTraverser::
|
||||
save(Node *node, const NodeAttributes &data, bool has_spam_mode,
|
||||
int num_parents) {
|
||||
|
||||
States::iterator si;
|
||||
si = _saved_states.find(node);
|
||||
|
||||
if (si != _saved_states.end()) {
|
||||
SavedState &state = (*si).second;
|
||||
state._data.merge_from(state._data, data);
|
||||
state._has_spam_mode = state._has_spam_mode || has_spam_mode;
|
||||
state._num_parents_so_far++;
|
||||
|
||||
if (dgraph_cat.is_spam()) {
|
||||
dgraph_cat.spam()
|
||||
<< "Encountered " << *node << " with " << num_parents
|
||||
<< " parents; " << state._num_parents_so_far
|
||||
<< " processed so far.\n";
|
||||
}
|
||||
|
||||
nassertv(state._num_parents_so_far <= num_parents);
|
||||
if (state._num_parents_so_far == num_parents) {
|
||||
// All parents are accounted for, so resume traversal!
|
||||
resume(node, state);
|
||||
_saved_states.erase(si);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// This node has not been encountered before, so save an entry for
|
||||
// it.
|
||||
if (dgraph_cat.is_spam()) {
|
||||
dgraph_cat.spam()
|
||||
<< "Encountered " << *node << " with " << num_parents
|
||||
<< " parents.\n";
|
||||
}
|
||||
|
||||
SavedState &state = _saved_states[node];
|
||||
state._data = data;
|
||||
state._has_spam_mode = has_spam_mode;
|
||||
state._num_parents_so_far = 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::resume
|
||||
// Access: Private
|
||||
// Description: Resumes traversal from a previously-saved state.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataGraphTraverser::
|
||||
resume(Node *node, DataGraphTraverser::SavedState &state) {
|
||||
DataNode *data_node = (DataNode *)NULL;
|
||||
if (node->is_of_type(DataNode::get_class_type())) {
|
||||
DCAST_INTO_V(data_node, node);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (state._has_spam_mode && dgraph_cat.is_info()) {
|
||||
dgraph_cat.info() << "Sending into " << *node << " {\n";
|
||||
describe_data_verbose(dgraph_cat.info(false), state._data, 2);
|
||||
dgraph_cat.info(false) << "}\n";
|
||||
|
||||
} else if (dgraph_cat.is_spam()) {
|
||||
dgraph_cat.spam() << "Sending into " << *node << " {\n";
|
||||
describe_data_verbose(dgraph_cat.spam(false), state._data, 2);
|
||||
dgraph_cat.spam(false) << "}\n";
|
||||
}
|
||||
#endif
|
||||
data_node->transmit_data(state._data);
|
||||
}
|
||||
|
||||
r_traverse_below(node, state._data, state._has_spam_mode);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataGraphTraverser::resume_all
|
||||
// Access: Private
|
||||
// Description: Called after the traversal has completed, this
|
||||
// resumes all of the unresumed saved states. There
|
||||
// shouldn't actually be any such things, unless a node
|
||||
// is multiply parented to a node outside of the data
|
||||
// graph.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataGraphTraverser::
|
||||
resume_all() {
|
||||
while (!_saved_states.empty()) {
|
||||
States::iterator si = _saved_states.begin();
|
||||
Node *node = (*si).first;
|
||||
SavedState &state = (*si).second;
|
||||
|
||||
dgraph_cat.warning()
|
||||
<< *node << " is parented both inside and outside of the data graph:\n";
|
||||
int num_parents = node->get_num_parents(DataRelation::get_class_type());
|
||||
for (int i = 0; i < num_parents; i++) {
|
||||
NodeRelation *arc = node->get_parent(DataRelation::get_class_type(), i);
|
||||
dgraph_cat.warning(false)
|
||||
<< " " << *arc << "\n";
|
||||
}
|
||||
|
||||
resume(node, state);
|
||||
_saved_states.erase(si);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// Filename: dataGraphTraverser.h
|
||||
// Created by: drose (05Feb01)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef DATAGRAPHTRAVERSER_H
|
||||
#define DATAGRAPHTRAVERSER_H
|
||||
|
||||
#include <pandabase.h>
|
||||
|
||||
#include <nodeAttributes.h>
|
||||
|
||||
class Node;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : DataGraphTraverser
|
||||
// Description : This class manages the traversal of the data graph,
|
||||
// so that all data attributes generated by nodes in the
|
||||
// graph are transmitted to nodes below them.
|
||||
//
|
||||
// A bit of logic is employed to ensure that if a node
|
||||
// is assigned to two parents, the data from both
|
||||
// parents is merged before passing it to the child.
|
||||
//
|
||||
// This class need not be exported from PANDA.DLL.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class DataGraphTraverser {
|
||||
public:
|
||||
INLINE DataGraphTraverser();
|
||||
|
||||
INLINE void traverse(Node *node);
|
||||
INLINE void traverse_below(Node *node, const NodeAttributes &data);
|
||||
|
||||
private:
|
||||
class SavedState {
|
||||
public:
|
||||
NodeAttributes _data;
|
||||
bool _has_spam_mode;
|
||||
int _num_parents_so_far;
|
||||
};
|
||||
|
||||
void r_traverse(Node *node, NodeAttributes &data, bool has_spam_mode);
|
||||
void r_traverse_below(Node *node, NodeAttributes &data, bool has_spam_mode);
|
||||
void save(Node *node, const NodeAttributes &data, bool has_spam_mode,
|
||||
int num_parents);
|
||||
void resume(Node *node, SavedState &state);
|
||||
void resume_all();
|
||||
|
||||
typedef map<Node *, SavedState> States;
|
||||
States _saved_states;
|
||||
};
|
||||
|
||||
#include "dataGraphTraverser.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -16,6 +16,23 @@ DataNode::
|
|||
DataNode(const string &name) : NamedNode(name) {
|
||||
_spam_mode = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataNode::transmit_data_per_child
|
||||
// Access: Public, Virtual
|
||||
// Description: Should be overridden in a derived class that wants to
|
||||
// send a different data stream to each child.
|
||||
// Normally, a node only overrides transmit_data(),
|
||||
// which takes a set of input data attributes and
|
||||
// generates a set of output data attributes. A node
|
||||
// may also override transmit_data_per_child(), which is
|
||||
// called after transmit_data(), once per child; this
|
||||
// function may be used to send individual data
|
||||
// attributes to each child.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataNode::
|
||||
transmit_data_per_child(NodeAttributes &, int) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataNode::set_spam_mode
|
||||
|
|
|
|||
|
|
@ -70,6 +70,9 @@ public:
|
|||
virtual void
|
||||
transmit_data(NodeAttributes &data)=0;
|
||||
|
||||
virtual void
|
||||
transmit_data_per_child(NodeAttributes &data, int child_index);
|
||||
|
||||
PUBLISHED:
|
||||
void set_spam_mode(bool flag);
|
||||
bool get_spam_mode() const;
|
||||
|
|
|
|||
|
|
@ -26,35 +26,32 @@ describe_data_verbose(ostream &out, const NodeAttributes &state,
|
|||
NodeAttributes::const_iterator nai;
|
||||
|
||||
for (nai = state.begin(); nai != state.end(); ++nai) {
|
||||
// We'll exclude the spam flag. No one wants to see that.
|
||||
TypeHandle type = (*nai).first;
|
||||
if (type != DataGraphTraversal::_spam_flag_type) {
|
||||
const PT(NodeAttribute) &attrib = (*nai).second;
|
||||
if (attrib != (NodeAttribute *)NULL) {
|
||||
// Now extract the type name out of the type flag.
|
||||
string actual_name = type.get_name();
|
||||
string::size_type underscore = actual_name.rfind('_');
|
||||
string name;
|
||||
if (underscore == string::npos) {
|
||||
// There was no underscore, so this name wasn't created via
|
||||
// register_data_transition(). Huh. Well, that's legitimate
|
||||
// (if unexpected), so just print the whole name.
|
||||
name = actual_name;
|
||||
} else {
|
||||
// Assume that, if the name was created via
|
||||
// register_data_transition(), the original name was the part
|
||||
// before the last underscore.
|
||||
name = actual_name.substr(0, underscore);
|
||||
}
|
||||
|
||||
indent(out, indent_level) << name;
|
||||
if ((int)name.length() < data_indent_level) {
|
||||
indent(out, data_indent_level - name.length());
|
||||
} else {
|
||||
out << " ";
|
||||
}
|
||||
out << *attrib << "\n";
|
||||
const PT(NodeAttribute) &attrib = (*nai).second;
|
||||
if (attrib != (NodeAttribute *)NULL) {
|
||||
// Now extract the type name out of the type flag.
|
||||
TypeHandle type = (*nai).first;
|
||||
string actual_name = type.get_name();
|
||||
string::size_type underscore = actual_name.rfind('_');
|
||||
string name;
|
||||
if (underscore == string::npos) {
|
||||
// There was no underscore, so this name wasn't created via
|
||||
// register_data_transition(). Huh. Well, that's legitimate
|
||||
// (if unexpected), so just print the whole name.
|
||||
name = actual_name;
|
||||
} else {
|
||||
// Assume that, if the name was created via
|
||||
// register_data_transition(), the original name was the part
|
||||
// before the last underscore.
|
||||
name = actual_name.substr(0, underscore);
|
||||
}
|
||||
|
||||
indent(out, indent_level) << name;
|
||||
if ((int)name.length() < data_indent_level) {
|
||||
indent(out, data_indent_level - name.length());
|
||||
} else {
|
||||
out << " ";
|
||||
}
|
||||
out << *attrib << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
// Filename: modifierButtonDataAttribute.I
|
||||
// Created by: drose (27Mar00)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE ModifierButtonDataAttribute::
|
||||
ModifierButtonDataAttribute() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::Copy Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE ModifierButtonDataAttribute::
|
||||
ModifierButtonDataAttribute(const ModifierButtonDataAttribute ©) :
|
||||
NodeAttribute(copy),
|
||||
_mods(copy._mods)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::Copy Assignment Operator
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void ModifierButtonDataAttribute::
|
||||
operator = (const ModifierButtonDataAttribute ©) {
|
||||
NodeAttribute::operator = (copy);
|
||||
_mods = copy._mods;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::set_mods
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void ModifierButtonDataAttribute::
|
||||
set_mods(const ModifierButtons &mods) {
|
||||
_mods = mods;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::set_mods
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const ModifierButtons &ModifierButtonDataAttribute::
|
||||
get_mods() const {
|
||||
return _mods;
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
// Filename: modifierButtonDataAttribute.cxx
|
||||
// Created by: drose (27Mar00)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "modifierButtonDataAttribute.h"
|
||||
#include "modifierButtonDataTransition.h"
|
||||
|
||||
#include <indent.h>
|
||||
|
||||
TypeHandle ModifierButtonDataAttribute::_type_handle;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::make_copy
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeAttribute *ModifierButtonDataAttribute::
|
||||
make_copy() const {
|
||||
return new ModifierButtonDataAttribute(*this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::make_initial
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeAttribute *ModifierButtonDataAttribute::
|
||||
make_initial() const {
|
||||
return new ModifierButtonDataAttribute;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::get_handle
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
TypeHandle ModifierButtonDataAttribute::
|
||||
get_handle() const {
|
||||
return ModifierButtonDataTransition::get_class_type();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::output
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void ModifierButtonDataAttribute::
|
||||
output(ostream &out) const {
|
||||
out << _mods;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::write
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void ModifierButtonDataAttribute::
|
||||
write(ostream &out, int indent_level) const {
|
||||
indent(out, indent_level) << _mods << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataAttribute::internal_compare_to
|
||||
// Access: Protected, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int ModifierButtonDataAttribute::
|
||||
internal_compare_to(const NodeAttribute *other) const {
|
||||
const ModifierButtonDataAttribute *ot;
|
||||
DCAST_INTO_R(ot, other, false);
|
||||
|
||||
if (_mods != ot->_mods) {
|
||||
return (_mods < ot->_mods) ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
// Filename: modifierButtonDataAttribute.h
|
||||
// Created by: drose (27Mar00)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef MODIFIERBUTTONDATAATTRIBUTE_H
|
||||
#define MODIFIERBUTTONDATAATTRIBUTE_H
|
||||
|
||||
#include <pandabase.h>
|
||||
|
||||
#include <nodeAttribute.h>
|
||||
#include <modifierButtons.h>
|
||||
|
||||
class ModifierButtonDataTransition;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ModifierButtonDataAttribute
|
||||
// Description : This data graph attribute stores the current state of
|
||||
// some various modifier buttons (e.g. shift, control,
|
||||
// alt, and the mouse buttons), as generated for
|
||||
// instance by a GraphicsWindow.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA ModifierButtonDataAttribute : public NodeAttribute {
|
||||
public:
|
||||
INLINE ModifierButtonDataAttribute();
|
||||
INLINE ModifierButtonDataAttribute(const ModifierButtonDataAttribute ©);
|
||||
INLINE void operator = (const ModifierButtonDataAttribute ©);
|
||||
|
||||
public:
|
||||
INLINE void set_mods(const ModifierButtons &mods);
|
||||
INLINE const ModifierButtons &get_mods() const;
|
||||
|
||||
virtual NodeAttribute *make_copy() const;
|
||||
virtual NodeAttribute *make_initial() const;
|
||||
|
||||
virtual TypeHandle get_handle() const;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
protected:
|
||||
virtual int internal_compare_to(const NodeAttribute *other) const;
|
||||
|
||||
private:
|
||||
ModifierButtons _mods;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
NodeAttribute::init_type();
|
||||
register_type(_type_handle, "ModifierButtonDataAttribute",
|
||||
NodeAttribute::get_class_type());
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
friend class ModifierButtonDataTransition;
|
||||
};
|
||||
|
||||
#include "modifierButtonDataAttribute.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
// Filename: modifierButtonDataTransition.I
|
||||
// Created by: drose (27Mar00)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE ModifierButtonDataTransition::
|
||||
ModifierButtonDataTransition() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::Copy Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE ModifierButtonDataTransition::
|
||||
ModifierButtonDataTransition(const ModifierButtonDataTransition ©) :
|
||||
NodeTransition(copy)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::Copy Assignment Operator
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void ModifierButtonDataTransition::
|
||||
operator = (const ModifierButtonDataTransition ©) {
|
||||
NodeTransition::operator = (copy);
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
// Filename: modifierButtonDataTransition.cxx
|
||||
// Created by: drose (01Mar00)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "modifierButtonDataTransition.h"
|
||||
#include "modifierButtonDataAttribute.h"
|
||||
|
||||
TypeHandle ModifierButtonDataTransition::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::make_copy
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeTransition *ModifierButtonDataTransition::
|
||||
make_copy() const {
|
||||
return new ModifierButtonDataTransition(*this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::make_attrib
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeAttribute *ModifierButtonDataTransition::
|
||||
make_attrib() const {
|
||||
return new ModifierButtonDataAttribute;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::compose
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns a new transition that corresponds to the
|
||||
// composition of this transition with the second
|
||||
// transition (which must be of an equivalent type).
|
||||
// This may return the same pointer as either source
|
||||
// transition. Applying the transition returned from
|
||||
// this function to an attribute attribute will produce
|
||||
// the same effect as applying each transition
|
||||
// separately.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeTransition *ModifierButtonDataTransition::
|
||||
compose(const NodeTransition *) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::invert
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeTransition *ModifierButtonDataTransition::
|
||||
invert() const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::apply
|
||||
// Access: Public, Virtual
|
||||
// Description: Returns a new attribute (or possibly the same
|
||||
// attribute) that represents the effect of applying this
|
||||
// indicated transition to the indicated attribute. The
|
||||
// source attribute may be NULL, indicating the initial
|
||||
// attribute.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeAttribute *ModifierButtonDataTransition::
|
||||
apply(const NodeAttribute *attrib) const {
|
||||
return (NodeAttribute *)attrib;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtonDataTransition::internal_compare_to
|
||||
// Access: Protected, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int ModifierButtonDataTransition::
|
||||
internal_compare_to(const NodeTransition *) const {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
// Filename: modifierButtonDataTransition.h
|
||||
// Created by: drose (01Mar00)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef MODIFIERBUTTONDATATRANSITION_H
|
||||
#define MODIFIERBUTTONDATATRANSITION_H
|
||||
|
||||
#include <pandabase.h>
|
||||
|
||||
#include <nodeTransition.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ModifierButtonDataTransition
|
||||
// Description :
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA ModifierButtonDataTransition : public NodeTransition {
|
||||
public:
|
||||
INLINE ModifierButtonDataTransition();
|
||||
INLINE ModifierButtonDataTransition(const ModifierButtonDataTransition ©);
|
||||
INLINE void operator = (const ModifierButtonDataTransition ©);
|
||||
|
||||
public:
|
||||
virtual NodeTransition *make_copy() const;
|
||||
virtual NodeAttribute *make_attrib() const;
|
||||
|
||||
virtual NodeTransition *compose(const NodeTransition *other) const;
|
||||
virtual NodeTransition *invert() const;
|
||||
virtual NodeAttribute *apply(const NodeAttribute *attrib) const;
|
||||
|
||||
protected:
|
||||
virtual int internal_compare_to(const NodeTransition *other) const;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
NodeTransition::init_type();
|
||||
register_type(_type_handle, "ModifierButtonDataTransition",
|
||||
NodeTransition::get_class_type());
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#include "modifierButtonDataTransition.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -134,34 +134,6 @@ has_keyboard(int device) const {
|
|||
return _input_devices[device].has_keyboard();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsWindow::get_modifier_buttons
|
||||
// Access: Public
|
||||
// Description: Returns the set of ModifierButtons currently being
|
||||
// monitored for the nth input device.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const ModifierButtons &GraphicsWindow::
|
||||
get_modifier_buttons(int device) const {
|
||||
nassertr(device >= 0 && device < (int)_input_devices.size(),
|
||||
*(new ModifierButtons));
|
||||
return _input_devices[device].get_modifier_buttons();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsWindow::set_modifier_buttons
|
||||
// Access: Public
|
||||
// Description: Changes the set of ModifierButtons that will be
|
||||
// monitored for the nth input device. It is
|
||||
// recommended that you first retrieve the existing set
|
||||
// via get_modifier_buttons(), so that any current
|
||||
// button state will not be lost.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void GraphicsWindow::
|
||||
set_modifier_buttons(int device, const ModifierButtons &mods) {
|
||||
nassertv(device >= 0 && device < (int)_input_devices.size());
|
||||
_input_devices[device].set_modifier_buttons(mods);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsWindow::get_mouse_data
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -133,9 +133,6 @@ PUBLISHED:
|
|||
INLINE bool has_pointer(int device) const;
|
||||
INLINE bool has_keyboard(int device) const;
|
||||
|
||||
INLINE const ModifierButtons &get_modifier_buttons(int device) const;
|
||||
INLINE void set_modifier_buttons(int device, const ModifierButtons &mods);
|
||||
|
||||
public:
|
||||
INLINE const MouseData &get_mouse_data(int device) const;
|
||||
INLINE bool has_button_event(int device) const;
|
||||
|
|
|
|||
|
|
@ -44,31 +44,6 @@ get_mouse_data() const {
|
|||
return _mouse_data;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsWindowInputDevice::get_modifier_buttons
|
||||
// Access: Public
|
||||
// Description: Returns the set of ModifierButtons currently being
|
||||
// monitored for the nth input device.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const ModifierButtons &GraphicsWindowInputDevice::
|
||||
get_modifier_buttons() const {
|
||||
return _mods;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsWindowInputDevice::set_modifier_buttons
|
||||
// Access: Public
|
||||
// Description: Changes the set of ModifierButtons that will be
|
||||
// monitored for the nth input device. It is
|
||||
// recommended that you first retrieve the existing set
|
||||
// via get_modifier_buttons(), so that any current
|
||||
// button state will not be lost.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void GraphicsWindowInputDevice::
|
||||
set_modifier_buttons(const ModifierButtons &mods) {
|
||||
_mods = mods;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GraphicsWindowInputDevice::set_pointer_in_window
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -29,16 +29,6 @@ GraphicsWindowInputDevice(const string &name, int flags) :
|
|||
_name(name),
|
||||
_flags(flags)
|
||||
{
|
||||
// We'll define the mouse buttons and the traditional modifier keys
|
||||
// as the default modifiers. Individual GraphicsWindows can change
|
||||
// this if they want.
|
||||
|
||||
_mods.add_button(MouseButton::one());
|
||||
_mods.add_button(MouseButton::two());
|
||||
_mods.add_button(MouseButton::three());
|
||||
_mods.add_button(KeyboardButton::shift());
|
||||
_mods.add_button(KeyboardButton::control());
|
||||
_mods.add_button(KeyboardButton::alt());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -85,7 +75,6 @@ GraphicsWindowInputDevice(const GraphicsWindowInputDevice ©) :
|
|||
_name(copy._name),
|
||||
_flags(copy._flags),
|
||||
_mouse_data(copy._mouse_data),
|
||||
_mods(copy._mods),
|
||||
_button_events(copy._button_events)
|
||||
{
|
||||
}
|
||||
|
|
@ -100,7 +89,6 @@ operator = (const GraphicsWindowInputDevice ©) {
|
|||
_name = copy._name;
|
||||
_flags = copy._flags;
|
||||
_mouse_data = copy._mouse_data;
|
||||
_mods = copy._mods;
|
||||
_button_events = copy._button_events;
|
||||
}
|
||||
|
||||
|
|
@ -147,8 +135,7 @@ get_button_event() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsWindowInputDevice::
|
||||
button_down(ButtonHandle button) {
|
||||
_button_events.push_back(ButtonEvent(button, true, _mods));
|
||||
_mods.button_down(button);
|
||||
_button_events.push_back(ButtonEvent(button, true));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -158,6 +145,5 @@ button_down(ButtonHandle button) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GraphicsWindowInputDevice::
|
||||
button_up(ButtonHandle button) {
|
||||
_mods.button_up(button);
|
||||
_button_events.push_back(ButtonEvent(button, false, _mods));
|
||||
_button_events.push_back(ButtonEvent(button, false));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <buttonEvent.h>
|
||||
#include <mouseData.h>
|
||||
#include <modifierButtons.h>
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
|
@ -41,8 +40,6 @@ public:
|
|||
INLINE bool has_keyboard() const;
|
||||
|
||||
INLINE const MouseData &get_mouse_data() const;
|
||||
INLINE const ModifierButtons &get_modifier_buttons() const;
|
||||
INLINE void set_modifier_buttons(const ModifierButtons &mods);
|
||||
|
||||
bool has_button_event() const;
|
||||
ButtonEvent get_button_event();
|
||||
|
|
@ -73,7 +70,6 @@ private:
|
|||
string _name;
|
||||
int _flags;
|
||||
MouseData _mouse_data;
|
||||
ModifierButtons _mods;
|
||||
ButtonEvents _button_events;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1048,7 +1048,10 @@ int framework_main(int argc, char *argv[]) {
|
|||
PT(Transform2SG) tball2cam = new Transform2SG("tball2cam");
|
||||
tball2cam->set_arc(arc1);
|
||||
new DataRelation(trackball, tball2cam);
|
||||
new DataRelation(drive_interface, tball2cam);
|
||||
|
||||
PT(Transform2SG) drive2cam = new Transform2SG("drive2cam");
|
||||
drive2cam->set_arc(arc1);
|
||||
new DataRelation(drive_interface, drive2cam);
|
||||
|
||||
// Create a ButtonThrower to throw events from the keyboard.
|
||||
PT(ButtonThrower) et = new ButtonThrower("kb-events");
|
||||
|
|
|
|||
|
|
@ -9,6 +9,20 @@
|
|||
|
||||
TypeHandle NodeAttribute::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: NodeAttribute::merge
|
||||
// Access: Public, Virtual
|
||||
// Description: Attempts to merge this attribute with the other one,
|
||||
// if that makes sense to do. Returns a new
|
||||
// NodeAttribute pointer that represents the merge, or
|
||||
// if the merge is not possible, returns the "other"
|
||||
// pointer unchanged (which is the result of the merge).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
NodeAttribute *NodeAttribute::
|
||||
merge(const NodeAttribute *other) const {
|
||||
return (NodeAttribute *)other;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: NodeAttribute::output
|
||||
// Access: Public, Virtual
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ public:
|
|||
|
||||
virtual TypeHandle get_handle() const=0;
|
||||
|
||||
virtual NodeAttribute *merge(const NodeAttribute *other) const;
|
||||
|
||||
PUBLISHED:
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
|
|
|||
|
|
@ -221,6 +221,35 @@ apply_from(const NodeAttributes &other, const NodeTransitionCache &trans) {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: NodeAttributes::merge_from
|
||||
// Access: Public
|
||||
// Description: Computes the union of the two NodeAttributes sets and
|
||||
// stores the result in this object (which may be the
|
||||
// same object as either of the two sources). If a
|
||||
// given attribute is set in both sources (and cannot be
|
||||
// union sensibly), the one from the second source
|
||||
// overrides.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void NodeAttributes::
|
||||
merge_from(const NodeAttributes &a, const NodeAttributes &b) {
|
||||
if (&a == this && b.is_empty()) {
|
||||
// Trivial do-nothing case.
|
||||
|
||||
} else if (&b == this && a.is_empty()) {
|
||||
// Trivial do-nothing case.
|
||||
|
||||
} else {
|
||||
Attributes temp;
|
||||
|
||||
tmap_merge_union(a._attributes.begin(), a._attributes.end(),
|
||||
b._attributes.begin(), b._attributes.end(),
|
||||
inserter(temp, temp.begin()));
|
||||
|
||||
_attributes.swap(temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: NodeAttributes::output
|
||||
|
|
|
|||
|
|
@ -68,7 +68,9 @@ public:
|
|||
void apply_from(const NodeAttributes &other,
|
||||
const NodeTransitionCache &trans);
|
||||
|
||||
public:
|
||||
void merge_from(const NodeAttributes &a, const NodeAttributes &b);
|
||||
|
||||
PUBLISHED:
|
||||
void output(ostream &out) const;
|
||||
void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -252,9 +252,9 @@ store_to(const NodeTransitionCache *a, NodeRelation *arc,
|
|||
|
||||
// And now actually add them.
|
||||
NodeTransitions temp;
|
||||
tmap_union(nt._transitions.begin(), nt._transitions.end(),
|
||||
a->_cache.begin(), a->_cache.end(),
|
||||
inserter(temp._transitions, temp._transitions.begin()));
|
||||
tmap_override_union(nt._transitions.begin(), nt._transitions.end(),
|
||||
a->_cache.begin(), a->_cache.end(),
|
||||
inserter(temp._transitions, temp._transitions.begin()));
|
||||
nt._transitions.swap(temp._transitions);
|
||||
}
|
||||
|
||||
|
|
@ -281,9 +281,9 @@ c_union(const NodeTransitionCache *a, const NodeTransitionCache *b) {
|
|||
// Neither is empty. Build and return a new list.
|
||||
NodeTransitionCache *result = new NodeTransitionCache;
|
||||
|
||||
tmap_union(a->_cache.begin(), a->_cache.end(),
|
||||
b->_cache.begin(), b->_cache.end(),
|
||||
inserter(result->_cache, result->_cache.begin()));
|
||||
tmap_override_union(a->_cache.begin(), a->_cache.end(),
|
||||
b->_cache.begin(), b->_cache.end(),
|
||||
inserter(result->_cache, result->_cache.begin()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,9 +170,9 @@ clear() {
|
|||
void NodeTransitions::
|
||||
copy_transitions_from(const NodeTransitions &other) {
|
||||
Transitions temp;
|
||||
tmap_union(_transitions.begin(), _transitions.end(),
|
||||
other._transitions.begin(), other._transitions.end(),
|
||||
inserter(temp, temp.begin()));
|
||||
tmap_override_union(_transitions.begin(), _transitions.end(),
|
||||
other._transitions.begin(), other._transitions.end(),
|
||||
inserter(temp, temp.begin()));
|
||||
_transitions.swap(temp);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ tmap_get_interest(InputIterator1 first1, InputIterator1 last1,
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: tmap_union
|
||||
// Function: tmap_override_union
|
||||
// Description: Accepts two NodeTransition or NodeAttribute maps, and
|
||||
// builds a new map which is the union of the first two.
|
||||
// If an element appears in both maps, the entry from
|
||||
|
|
@ -45,9 +45,9 @@ tmap_get_interest(InputIterator1 first1, InputIterator1 last1,
|
|||
////////////////////////////////////////////////////////////////////
|
||||
template<class InputIterator1, class InputIterator2, class OutputIterator>
|
||||
OutputIterator
|
||||
tmap_union(InputIterator1 first1, InputIterator1 last1,
|
||||
InputIterator2 first2, InputIterator2 last2,
|
||||
OutputIterator result) {
|
||||
tmap_override_union(InputIterator1 first1, InputIterator1 last1,
|
||||
InputIterator2 first2, InputIterator2 last2,
|
||||
OutputIterator result) {
|
||||
while (first1 != last1 && first2 != last2) {
|
||||
if ((*first1).first < (*first2).first) {
|
||||
*result = *first1;
|
||||
|
|
@ -79,6 +79,50 @@ tmap_union(InputIterator1 first1, InputIterator1 last1,
|
|||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: tmap_merge_union
|
||||
// Description: Accepts two NodeAttribute maps, and builds a new map
|
||||
// which is the union of the first two. If an element
|
||||
// appears in both maps, the two are merged, preferring
|
||||
// the second.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class InputIterator1, class InputIterator2, class OutputIterator>
|
||||
OutputIterator
|
||||
tmap_merge_union(InputIterator1 first1, InputIterator1 last1,
|
||||
InputIterator2 first2, InputIterator2 last2,
|
||||
OutputIterator result) {
|
||||
while (first1 != last1 && first2 != last2) {
|
||||
if ((*first1).first < (*first2).first) {
|
||||
*result = *first1;
|
||||
++first1;
|
||||
++result;
|
||||
} else if ((*first2).first < (*first1).first) {
|
||||
*result = *first2;
|
||||
++first2;
|
||||
++result;
|
||||
} else {
|
||||
PT(NodeAttribute) c = (*first1).second->merge((*first2).second);
|
||||
*result = pair<TypeHandle, PT(NodeAttribute)>((*first1).first, c);
|
||||
++first1;
|
||||
++first2;
|
||||
++result;
|
||||
}
|
||||
}
|
||||
|
||||
while (first1 != last1) {
|
||||
*result = *first1;
|
||||
++first1;
|
||||
++result;
|
||||
}
|
||||
|
||||
while (first2 != last2) {
|
||||
*result = *first2;
|
||||
++first2;
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: tmap_arc_union
|
||||
// Description: As above, but updates the transitions on the way to
|
||||
|
|
|
|||
|
|
@ -28,19 +28,6 @@ ButtonEvent(ButtonHandle button, bool down) :
|
|||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ButtonEvent::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE ButtonEvent::
|
||||
ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods) :
|
||||
_button(button),
|
||||
_down(down),
|
||||
_mods(mods)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ButtonEvent::Copy Constructor
|
||||
// Access: Public
|
||||
|
|
@ -49,8 +36,7 @@ ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods) :
|
|||
INLINE ButtonEvent::
|
||||
ButtonEvent(const ButtonEvent ©) :
|
||||
_button(copy._button),
|
||||
_down(copy._down),
|
||||
_mods(copy._mods)
|
||||
_down(copy._down)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +49,6 @@ INLINE void ButtonEvent::
|
|||
operator = (const ButtonEvent ©) {
|
||||
_button = copy._button;
|
||||
_down = copy._down;
|
||||
((ModifierButtons &)_mods) = copy._mods;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -74,8 +59,7 @@ operator = (const ButtonEvent ©) {
|
|||
INLINE bool ButtonEvent::
|
||||
operator == (const ButtonEvent &other) const {
|
||||
return (_button == other._button &&
|
||||
_down == other._down &&
|
||||
_mods == other._mods);
|
||||
_down == other._down);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -99,9 +83,5 @@ operator < (const ButtonEvent &other) const {
|
|||
return _button < other._button;
|
||||
}
|
||||
|
||||
if (_down != other._down) {
|
||||
return _down < other._down;
|
||||
}
|
||||
|
||||
return _mods < other._mods;
|
||||
return _down < other._down;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,5 +18,4 @@ output(ostream &out) const {
|
|||
} else {
|
||||
out << " up";
|
||||
}
|
||||
out << " with " << _mods;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,19 +9,16 @@
|
|||
#include <pandabase.h>
|
||||
|
||||
#include "buttonHandle.h"
|
||||
#include "modifierButtons.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ButtonEvent
|
||||
// Description : Records a transition of one button from up to down or
|
||||
// vice-versa, as well as an optional recording of some
|
||||
// ModifierButtons that were being tracked at the time.
|
||||
// vice-versa.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA ButtonEvent {
|
||||
public:
|
||||
INLINE ButtonEvent();
|
||||
INLINE ButtonEvent(ButtonHandle button, bool down);
|
||||
INLINE ButtonEvent(ButtonHandle button, bool down, ModifierButtons mods);
|
||||
INLINE ButtonEvent(const ButtonEvent ©);
|
||||
INLINE void operator = (const ButtonEvent ©);
|
||||
|
||||
|
|
@ -33,7 +30,6 @@ public:
|
|||
|
||||
ButtonHandle _button;
|
||||
bool _down;
|
||||
const ModifierButtons _mods;
|
||||
};
|
||||
|
||||
INLINE ostream &operator << (ostream &out, const ButtonEvent &be) {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,21 @@ get_button(int index) const {
|
|||
return _button_list[index];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtons::add_event
|
||||
// Access: Public
|
||||
// Description: Calls button_down() or button_up(), as appropriate,
|
||||
// according to the indicated ButtonEvent.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool ModifierButtons::
|
||||
add_event(const ButtonEvent &event) {
|
||||
if (event._down) {
|
||||
return button_down(event._button);
|
||||
} else {
|
||||
return button_up(event._button);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtons::is_down
|
||||
// Access: Public
|
||||
|
|
@ -86,3 +101,14 @@ is_down(int index) const {
|
|||
nassertr(index >= 0 && index < (int)_button_list.size(), false);
|
||||
return ((_state & ((BitmaskType)1 << index)) != 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: ModifierButtons::is_any_down
|
||||
// Access: Public
|
||||
// Description: Returns true if any of the tracked button are known
|
||||
// to be down, or false if all of them are up.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool ModifierButtons::
|
||||
is_any_down() const {
|
||||
return _state != 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,16 +129,19 @@ remove_button(ButtonHandle button) {
|
|||
// If the given button is one of the buttons that is
|
||||
// currently being monitored, this will update the
|
||||
// internal state appropriately; otherwise, it will do
|
||||
// nothing.
|
||||
// nothing. Returns true if the button is one that was
|
||||
// monitored, or false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void ModifierButtons::
|
||||
bool ModifierButtons::
|
||||
button_down(ButtonHandle button) {
|
||||
for (int i = 0; i < (int)_button_list.size(); i++) {
|
||||
if (button == _button_list[i]) {
|
||||
_state |= ((BitmaskType)1 << i);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -148,16 +151,19 @@ button_down(ButtonHandle button) {
|
|||
// If the given button is one of the buttons that is
|
||||
// currently being monitored, this will update the
|
||||
// internal state appropriately; otherwise, it will do
|
||||
// nothing.
|
||||
// nothing. Returns true if the button is one that was
|
||||
// monitored, or false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void ModifierButtons::
|
||||
bool ModifierButtons::
|
||||
button_up(ButtonHandle button) {
|
||||
for (int i = 0; i < (int)_button_list.size(); i++) {
|
||||
if (button == _button_list[i]) {
|
||||
_state &= ~((BitmaskType)1 << i);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "buttonHandle.h"
|
||||
#include "pointerToArray.h"
|
||||
#include "buttonEvent.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : ModifierButtons
|
||||
|
|
@ -18,7 +19,7 @@
|
|||
// known to be down or up.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA ModifierButtons {
|
||||
public:
|
||||
PUBLISHED:
|
||||
ModifierButtons();
|
||||
ModifierButtons(const ModifierButtons ©);
|
||||
~ModifierButtons();
|
||||
|
|
@ -35,11 +36,13 @@ public:
|
|||
INLINE int get_num_buttons() const;
|
||||
INLINE ButtonHandle get_button(int index) const;
|
||||
|
||||
void button_down(ButtonHandle button);
|
||||
void button_up(ButtonHandle button);
|
||||
bool button_down(ButtonHandle button);
|
||||
bool button_up(ButtonHandle button);
|
||||
INLINE bool add_event(const ButtonEvent &event);
|
||||
|
||||
bool is_down(ButtonHandle button) const;
|
||||
INLINE bool is_down(int index) const;
|
||||
INLINE bool is_any_down() const;
|
||||
|
||||
void output(ostream &out) const;
|
||||
void write(ostream &out) const;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#define SOURCES \
|
||||
buttonThrower.cxx buttonThrower.h config_tform.cxx config_tform.h \
|
||||
dataValve.I dataValve.cxx dataValve.h \
|
||||
driveInterface.cxx driveInterface.h mouseWatcher.I mouseWatcher.cxx \
|
||||
mouseWatcher.h mouseWatcherRegion.I mouseWatcherRegion.cxx \
|
||||
mouseWatcherRegion.h planarSlider.cxx planarSlider.h trackball.cxx \
|
||||
|
|
@ -16,7 +17,8 @@
|
|||
transform2sg.cxx transform2sg.h
|
||||
|
||||
#define INSTALL_HEADERS \
|
||||
buttonThrower.h driveInterface.h mouseWatcher.I mouseWatcher.h \
|
||||
buttonThrower.h dataValve.I dataValve.h \
|
||||
driveInterface.h mouseWatcher.I mouseWatcher.h \
|
||||
mouseWatcherRegion.I mouseWatcherRegion.h planarSlider.h \
|
||||
trackball.h transform2sg.h
|
||||
|
||||
|
|
|
|||
|
|
@ -86,9 +86,14 @@ get_modifier_buttons() const {
|
|||
// be empty, and the ButtonThrower will therefore ignore
|
||||
// all ModifierButtons attached to the key events, but
|
||||
// if one or more buttons have been added to this set,
|
||||
// and those modifier buttons are set on the button
|
||||
// event, then the event name will be prepended with the
|
||||
// names of the modifier buttons.
|
||||
// then the event name will be prepended with the names
|
||||
// of the modifier buttons.
|
||||
//
|
||||
// It is recommended that you change this setting by
|
||||
// first calling get_modifier_buttons(), making
|
||||
// adjustments, and passing the new value to
|
||||
// set_modifier_buttons(). This way the current state
|
||||
// of the modifier buttons will not be lost.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void ButtonThrower::
|
||||
set_modifier_buttons(const ModifierButtons &mods) {
|
||||
|
|
@ -110,20 +115,24 @@ transmit_data(NodeAttributes &data) {
|
|||
for (bi = b->begin(); bi != b->end(); ++bi) {
|
||||
const ButtonEvent &be = (*bi);
|
||||
string event_name = _prefix + be._button.get_name();
|
||||
if (!be._down) {
|
||||
event_name += "-up";
|
||||
|
||||
} else {
|
||||
// We only prepend modifier names on the button-down events.
|
||||
string prepend;
|
||||
|
||||
for (int i = 0; i < _mods.get_num_buttons(); i++) {
|
||||
ButtonHandle modifier = _mods.get_button(i);
|
||||
if (be._mods.is_down(modifier)) {
|
||||
prepend += modifier.get_name() + "-";
|
||||
if (be._down) {
|
||||
if (!_mods.button_down(be._button)) {
|
||||
// We only prepend modifier names on the button-down events,
|
||||
// and only for buttons which are not themselves modifiers.
|
||||
string prepend;
|
||||
|
||||
for (int i = 0; i < _mods.get_num_buttons(); i++) {
|
||||
ButtonHandle modifier = _mods.get_button(i);
|
||||
if (_mods.is_down(modifier)) {
|
||||
prepend += modifier.get_name() + "-";
|
||||
}
|
||||
}
|
||||
event_name = prepend + event_name;
|
||||
}
|
||||
event_name = prepend + event_name;
|
||||
|
||||
} else {
|
||||
_mods.button_up(be._button);
|
||||
event_name += "-up";
|
||||
}
|
||||
|
||||
throw_event(event_name);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "config_tform.h"
|
||||
|
||||
#include "dataValve.h"
|
||||
#include "driveInterface.h"
|
||||
#include "buttonThrower.h"
|
||||
#include "mouseWatcher.h"
|
||||
|
|
@ -30,6 +32,7 @@ const double drive_horizontal_ramp_up_time = config_tform.GetDouble("drive-horiz
|
|||
const double drive_horizontal_ramp_down_time = config_tform.GetDouble("drive-horizontal-ramp-down-time", 0.0);
|
||||
|
||||
ConfigureFn(config_tform) {
|
||||
DataValve::init_type();
|
||||
DriveInterface::init_type();
|
||||
ButtonThrower::init_type();
|
||||
MouseWatcher::init_type();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
// Filename: dataValve.I
|
||||
// Created by: drose (05Feb01)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::Control::Constructor
|
||||
// Access: Published
|
||||
// Description: A unique Control can be assigned to each child of the
|
||||
// DataValve, or the same Control may be shared between
|
||||
// different children as desired. Each Control may be
|
||||
// explicitly set on or off to enable or disable all
|
||||
// data flow to that child, respectively; it may also be
|
||||
// set to a "buttons" state, in which it enables or
|
||||
// disables data flow to the child based on the current
|
||||
// state of the modifier buttons as held by the user.
|
||||
//
|
||||
// In addition to being assigned to control all data to
|
||||
// a particular child, a particular Control may be set
|
||||
// to control only data of a particular type to a
|
||||
// particular child. See set_control() and
|
||||
// set_fine_control() on DataValve.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE DataValve::Control::
|
||||
Control() {
|
||||
_state = S_on;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::Control::set_on
|
||||
// Access: Published
|
||||
// Description: Sets the state on this particular Control to be on
|
||||
// until further notice. Child nodes guarded by this
|
||||
// Control will receive data.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DataValve::Control::
|
||||
set_on() {
|
||||
_state = S_on;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::Control::set_off
|
||||
// Access: Published
|
||||
// Description: Sets the state on this particular Control to be off
|
||||
// until further notice. Child nodes guarded by this
|
||||
// Control will not receive data.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DataValve::Control::
|
||||
set_off() {
|
||||
_state = S_off;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::Control::set_buttons
|
||||
// Access: Published
|
||||
// Description: Sets the state on this particular Control to be
|
||||
// dependent on the current set of buttons being held
|
||||
// down by the user. The ModifierButtons object here
|
||||
// must exactly match the ModifierButtons object set on
|
||||
// the DataValve in order and number of buttons.
|
||||
//
|
||||
// The best way to generate the ModifierButtons list is
|
||||
// to create a new ModifierButtons based on the value
|
||||
// returned by DataValve::get_modifier_buttons(), then
|
||||
// explicitly call button_up() and button_down() on each
|
||||
// button that you want to be up or down.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DataValve::Control::
|
||||
set_buttons(const ModifierButtons &mods) {
|
||||
_state = S_buttons;
|
||||
_mods = mods;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::set_default_control
|
||||
// Access: Public
|
||||
// Description: Sets the Control that will apply to each child that
|
||||
// does not have a particular control set via
|
||||
// set_control().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DataValve::
|
||||
set_default_control(DataValve::Control *control) {
|
||||
_default_control = control;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::get_default_control
|
||||
// Access: Public
|
||||
// Description: Returns the Control that will apply to each child
|
||||
// that does not have a particular control set via
|
||||
// set_control().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE DataValve::Control *DataValve::
|
||||
get_default_control() const {
|
||||
return _default_control;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::set_modifier_buttons
|
||||
// Access: Public
|
||||
// Description: Specifies the set of buttons that the DataValve will
|
||||
// monitor as modifier buttons. The exact same set of
|
||||
// ModifierButtons must be specified on the DataValve as
|
||||
// well as on all of its Controls in order to
|
||||
// successfully use modifier buttons to switch the
|
||||
// valves automatically.
|
||||
//
|
||||
// This need not match the set of ModifierButtons set on
|
||||
// the window, but it may be less confusing if it does.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void DataValve::
|
||||
set_modifier_buttons(const ModifierButtons &mods) {
|
||||
_mods = mods;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::get_modifier_buttons
|
||||
// Access: Public
|
||||
// Description: Returns the set of ModifierButtons currently being
|
||||
// monitored. See set_modifier_buttons().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const ModifierButtons &DataValve::
|
||||
get_modifier_buttons() const {
|
||||
return _mods;
|
||||
}
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
// Filename: dataValve.cxx
|
||||
// Created by: drose (05Feb01)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "dataValve.h"
|
||||
|
||||
#include <dataRelation.h>
|
||||
#include <buttonEventDataAttribute.h>
|
||||
#include <buttonEventDataTransition.h>
|
||||
#include <nodeAttributes.h>
|
||||
|
||||
TypeHandle DataValve::_type_handle;
|
||||
|
||||
TypeHandle DataValve::_button_events_type;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::Control::is_on
|
||||
// Access: Published
|
||||
// Description: Returns true if the indicated Control is currently
|
||||
// on, given the particular DataNode it applies to.
|
||||
// (The DataNode is necessary to determine the current
|
||||
// state of the ModiferButtons.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool DataValve::Control::
|
||||
is_on(const DataValve &valve) const {
|
||||
switch (_state) {
|
||||
case S_on:
|
||||
return true;
|
||||
|
||||
case S_off:
|
||||
return false;
|
||||
|
||||
case S_buttons:
|
||||
return _mods == valve.get_modifier_buttons();
|
||||
}
|
||||
|
||||
// Invalid state.
|
||||
nassertr(false, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::Constructor
|
||||
// Access: Published
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
DataValve::
|
||||
DataValve(const string &name) : DataNode(name) {
|
||||
_default_control = new Control;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::Destructor
|
||||
// Access: Published, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
DataValve::
|
||||
~DataValve() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::set_control
|
||||
// Access: Published
|
||||
// Description: Sets an overall Control on the indicated child node.
|
||||
// The nth child of this node, zero-based, will receive
|
||||
// data only while the indicated Control is on. This
|
||||
// affects all types of data that is not specifically
|
||||
// mentioned by set_fine_control().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
set_control(int child_index, Control *control) {
|
||||
ensure_child_index(child_index);
|
||||
nassertv(child_index >= 0 && child_index < (int)_controls.size());
|
||||
_controls[child_index]._control = control;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::clear_control
|
||||
// Access: Published
|
||||
// Description: Removes the overall Control, as well as all fine
|
||||
// controls, from the indicated child node. The
|
||||
// indicated child will now be affected only by the
|
||||
// default control. See set_control() and
|
||||
// set_fine_control().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
clear_control(int child_index) {
|
||||
nassertv(child_index >= 0);
|
||||
if (child_index < (int)_controls.size()) {
|
||||
_controls[child_index]._control.clear();
|
||||
_controls[child_index]._fine_controls.clear();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::has_control
|
||||
// Access: Published
|
||||
// Description: Returns true if the indicated child has a particular
|
||||
// Control set, or if it has *any* fine controls set.
|
||||
// If this returns false, the child is controlled only
|
||||
// by the default control. See set_control() and
|
||||
// set_fine_control().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool DataValve::
|
||||
has_control(int child_index) const {
|
||||
nassertr(child_index >= 0, false);
|
||||
if (child_index < (int)_controls.size()) {
|
||||
return
|
||||
!_controls[child_index]._control.is_null() ||
|
||||
!_controls[child_index]._fine_controls.empty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::get_control
|
||||
// Access: Published
|
||||
// Description: Returns the control that has been set for the
|
||||
// indicated child, or NULL if no control has been set
|
||||
// (which implies the default control is in affect for
|
||||
// the indicated child). Note that it is possible for
|
||||
// this function to return NULL even if has_control(),
|
||||
// above, returned true, since a fine control may have
|
||||
// been set without setting a particular general
|
||||
// control.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
DataValve::Control *DataValve::
|
||||
get_control(int child_index) const {
|
||||
nassertr(child_index >= 0, NULL);
|
||||
if (child_index < (int)_controls.size()) {
|
||||
return _controls[child_index]._control;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::set_fine_control
|
||||
// Access: Published
|
||||
// Description: Sets the "fine" control for the indicated child index
|
||||
// and the indicated data type. This overrides the
|
||||
// control set by set_control() for this child, but only
|
||||
// for data of this type. This can be used to send
|
||||
// data of different types to different children.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
set_fine_control(int child_index, TypeHandle data_type, Control *control) {
|
||||
ensure_child_index(child_index);
|
||||
nassertv(child_index >= 0 && child_index < (int)_controls.size());
|
||||
_controls[child_index]._fine_controls[data_type] = control;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::clear_fine_control
|
||||
// Access: Published
|
||||
// Description: Removes the particular fine control set by
|
||||
// set_fine_control() for the indicated child index and
|
||||
// data type. Data of this type will now be controlled
|
||||
// by the Control indicated by set_control() for this
|
||||
// child, or by set_default_control() if nothing is
|
||||
// indicated by set_control().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
clear_fine_control(int child_index, TypeHandle data_type) {
|
||||
nassertv(child_index >= 0);
|
||||
if (child_index < (int)_controls.size()) {
|
||||
_controls[child_index]._fine_controls.erase(data_type);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::has_fine_control
|
||||
// Access: Published
|
||||
// Description: Returns true if the indicated child has a particular
|
||||
// fine control set for the indicated data type, or
|
||||
// false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool DataValve::
|
||||
has_fine_control(int child_index, TypeHandle data_type) const {
|
||||
nassertr(child_index >= 0, false);
|
||||
if (child_index < (int)_controls.size()) {
|
||||
return _controls[child_index]._fine_controls.count(data_type) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::get_fine_control
|
||||
// Access: Published
|
||||
// Description: Returns the fine control that has been set for the
|
||||
// indicated child and data type, or NULL if no such
|
||||
// fine control has been set.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
DataValve::Control *DataValve::
|
||||
get_fine_control(int child_index, TypeHandle data_type) const {
|
||||
nassertr(child_index >= 0, NULL);
|
||||
if (child_index < (int)_controls.size()) {
|
||||
FineControls::const_iterator fci =
|
||||
_controls[child_index]._fine_controls.find(data_type);
|
||||
if (fci != _controls[child_index]._fine_controls.end()) {
|
||||
return (*fci).second;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::write
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
write(ostream &out, int indent_level) const {
|
||||
indent(out, indent_level)
|
||||
<< "DataValve " << get_name() << ": (default control is "
|
||||
<< (_default_control->is_on(*this) ? "on" : "off") << ")\n";
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < _controls.size(); i++) {
|
||||
if (has_control(i)) {
|
||||
const Child &child = _controls[i];
|
||||
indent(out, indent_level + 2)
|
||||
<< "Child " << i << ": ";
|
||||
|
||||
if (child._control.is_null()) {
|
||||
out << "(default)";
|
||||
} else {
|
||||
out << (child._control->is_on(*this) ? "on" : "off");
|
||||
}
|
||||
out << "\n";
|
||||
|
||||
FineControls::const_iterator fci;
|
||||
for (fci = child._fine_controls.begin();
|
||||
fci != child._fine_controls.end();
|
||||
++fci) {
|
||||
indent(out, indent_level + 4)
|
||||
<< (*fci).first << " "
|
||||
<< ((*fci).second->is_on(*this) ? "on" : "off")
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::ensure_child_index
|
||||
// Access: Private
|
||||
// Description: Guarantees that we have at least enough children in
|
||||
// the _control vector to describe the indicated
|
||||
// child_index.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
ensure_child_index(int child_index) {
|
||||
_controls.reserve(child_index + 1);
|
||||
while ((int)_controls.size() <= child_index) {
|
||||
_controls.push_back(Child());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::transmit_data
|
||||
// Access: Public, Virtual
|
||||
// Description: Called once before transmit_data_per_child() is
|
||||
// called for each child, this gives the node a chance
|
||||
// to process its inputs.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
transmit_data(NodeAttributes &data) {
|
||||
// Update our modifier buttons during the overall pass.
|
||||
const ButtonEventDataAttribute *b;
|
||||
if (get_attribute_into(b, data, _button_events_type)) {
|
||||
b->update_mods(_mods);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::transmit_data_per_child
|
||||
// Access: Public, Virtual
|
||||
// Description: Should be overridden in a derived class that wants to
|
||||
// send a different data stream to each child.
|
||||
// Normally, a node only overrides transmit_data(),
|
||||
// which takes a set of input data attributes and
|
||||
// generates a set of output data attributes. A node
|
||||
// may also override transmit_data_per_child(), which is
|
||||
// called after transmit_data(), once per child; this
|
||||
// function may be used to send individual data
|
||||
// attributes to each child.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
transmit_data_per_child(NodeAttributes &data, int child_index) {
|
||||
// Consider which data this child wants to see.
|
||||
static const FineControls empty_controls;
|
||||
|
||||
Control *control = _default_control;
|
||||
const FineControls *fine_controls = &empty_controls;
|
||||
|
||||
if (child_index < (int)_controls.size()) {
|
||||
if (!_controls[child_index]._control.is_null()) {
|
||||
control = _controls[child_index]._control;
|
||||
}
|
||||
fine_controls = &_controls[child_index]._fine_controls;
|
||||
}
|
||||
|
||||
if (control->is_on(*this)) {
|
||||
if (fine_controls->empty()) {
|
||||
// The control is on, and we have no further fine-tuning. All
|
||||
// data goes to the indicated node.
|
||||
return;
|
||||
|
||||
} else {
|
||||
// The control is on, and we have some fine-tuning. Any data
|
||||
// type explicitly "off" should be yanked.
|
||||
FineControls::const_iterator fci;
|
||||
for (fci = fine_controls->begin(); fci != fine_controls->end(); ++fci) {
|
||||
TypeHandle data_type = (*fci).first;
|
||||
Control *fcontrol = (*fci).second;
|
||||
|
||||
if (!fcontrol->is_on(*this)) {
|
||||
data.clear_attribute(data_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (fine_controls->empty()) {
|
||||
// The control is off, and we have no further fine-tuning. All
|
||||
// data gets eliminated.
|
||||
data.clear();
|
||||
|
||||
} else {
|
||||
// The control is off, and we have some fine-tuning. Any data
|
||||
// type explicitly "on" should be preserved.
|
||||
NodeAttributes temp;
|
||||
|
||||
FineControls::const_iterator fci;
|
||||
for (fci = fine_controls->begin(); fci != fine_controls->end(); ++fci) {
|
||||
TypeHandle data_type = (*fci).first;
|
||||
Control *fcontrol = (*fci).second;
|
||||
|
||||
if (fcontrol->is_on(*this)) {
|
||||
NodeAttribute *attrib = data.get_attribute(data_type);
|
||||
if (attrib != (NodeAttribute *)NULL) {
|
||||
temp.set_attribute(data_type, attrib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DataValve::init_type
|
||||
// Access: Public, Static
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DataValve::
|
||||
init_type() {
|
||||
DataNode::init_type();
|
||||
register_type(_type_handle, "DataValve",
|
||||
DataNode::get_class_type());
|
||||
|
||||
ButtonEventDataTransition::init_type();
|
||||
register_data_transition(_button_events_type, "ButtonEvents",
|
||||
ButtonEventDataTransition::get_class_type());
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// Filename: dataValve.h
|
||||
// Created by: drose (05Feb01)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef DATAVALVE_H
|
||||
#define DATAVALVE_H
|
||||
|
||||
#include <pandabase.h>
|
||||
|
||||
#include <dataNode.h>
|
||||
#include <modifierButtons.h>
|
||||
#include <referenceCount.h>
|
||||
#include <pointerTo.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : DataValve
|
||||
// Description : This DataNode can be used to selectively control
|
||||
// which child node or nodes receive certain data graph
|
||||
// events, based on the type of data and possibly on the
|
||||
// combination of modifier buttons being held down by
|
||||
// the user.
|
||||
//
|
||||
// It's particularly useful when used in conjunction
|
||||
// with one or more Trackball interfaces, for instance,
|
||||
// to move around various different objects according to
|
||||
// the key combination being held down.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA DataValve : public DataNode {
|
||||
PUBLISHED:
|
||||
DataValve(const string &name = "");
|
||||
virtual ~DataValve();
|
||||
|
||||
// The Control is used extensively within the DataValve class to
|
||||
// manage the data flow; see the comments on the Control
|
||||
// constructor.
|
||||
class EXPCL_PANDA Control : public ReferenceCount {
|
||||
PUBLISHED:
|
||||
INLINE Control();
|
||||
|
||||
INLINE void set_on();
|
||||
INLINE void set_off();
|
||||
INLINE void set_buttons(const ModifierButtons &mods);
|
||||
|
||||
bool is_on(const DataValve &valve) const;
|
||||
|
||||
private:
|
||||
enum State {
|
||||
S_on,
|
||||
S_off,
|
||||
S_buttons
|
||||
};
|
||||
State _state;
|
||||
ModifierButtons _mods;
|
||||
};
|
||||
|
||||
INLINE void set_default_control(Control *control);
|
||||
INLINE Control *get_default_control() const;
|
||||
|
||||
void set_control(int child_index, Control *control);
|
||||
void clear_control(int child_index);
|
||||
bool has_control(int child_index) const;
|
||||
Control *get_control(int child_index) const;
|
||||
|
||||
void set_fine_control(int child_index, TypeHandle data_type, Control *control);
|
||||
void clear_fine_control(int child_index, TypeHandle data_type);
|
||||
bool has_fine_control(int child_index, TypeHandle data_type) const;
|
||||
Control *get_fine_control(int child_index, TypeHandle data_type) const;
|
||||
|
||||
INLINE void set_modifier_buttons(const ModifierButtons &mods);
|
||||
INLINE const ModifierButtons &get_modifier_buttons() const;
|
||||
|
||||
public:
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
private:
|
||||
void ensure_child_index(int child_index);
|
||||
|
||||
ModifierButtons _mods;
|
||||
PT(Control) _default_control;
|
||||
|
||||
typedef map<TypeHandle, PT(Control)> FineControls;
|
||||
|
||||
class Child {
|
||||
public:
|
||||
PT(Control) _control;
|
||||
FineControls _fine_controls;
|
||||
};
|
||||
|
||||
typedef vector<Child> Controls;
|
||||
Controls _controls;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// From parent class DataNode
|
||||
////////////////////////////////////////////////////////////////////
|
||||
public:
|
||||
virtual void
|
||||
transmit_data(NodeAttributes &data);
|
||||
|
||||
virtual void
|
||||
transmit_data_per_child(NodeAttributes &data, int child_index);
|
||||
|
||||
// inputs
|
||||
static TypeHandle _button_events_type;
|
||||
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type();
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#include "dataValve.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -13,18 +13,14 @@
|
|||
#include <modifierButtons.h>
|
||||
#include <buttonEventDataTransition.h>
|
||||
#include <buttonEventDataAttribute.h>
|
||||
#include <modifierButtonDataTransition.h>
|
||||
#include <modifierButtonDataAttribute.h>
|
||||
#include <keyboardButton.h>
|
||||
#include <mouseButton.h>
|
||||
#include <dataGraphTraversal.h>
|
||||
#include <dataGraphTraverser.h>
|
||||
#include <allAttributesWrapper.h>
|
||||
#include <dftraverser.h>
|
||||
#include <dataRelation.h>
|
||||
|
||||
TypeHandle DriveInterface::_type_handle;
|
||||
|
||||
TypeHandle DriveInterface::_mods_type;
|
||||
TypeHandle DriveInterface::_xyz_type;
|
||||
TypeHandle DriveInterface::_button_events_type;
|
||||
TypeHandle DriveInterface::_transform_type;
|
||||
|
|
@ -126,6 +122,10 @@ DriveInterface(const string &name) : DataNode(name) {
|
|||
|
||||
_cs = default_coordinate_system;
|
||||
|
||||
_mods.add_button(MouseButton::one());
|
||||
_mods.add_button(MouseButton::two());
|
||||
_mods.add_button(MouseButton::three());
|
||||
|
||||
_transform = new MatrixDataAttribute;
|
||||
_transform->set_value(_mat);
|
||||
_attrib.set_attribute(_transform_type, _transform);
|
||||
|
|
@ -652,13 +652,9 @@ get_mat() const {
|
|||
void DriveInterface::
|
||||
force_dgraph() {
|
||||
_transform->set_value(_mat);
|
||||
int num_children = get_num_children(DataRelation::get_class_type());
|
||||
for (int i = 0; i < num_children; i++) {
|
||||
DataGraphVisitor dgv;
|
||||
df_traverse(get_child(DataRelation::get_class_type(), i),
|
||||
dgv, AllAttributesWrapper(_attrib),
|
||||
NullLevelState(), DataRelation::get_class_type());
|
||||
}
|
||||
|
||||
DataGraphTraverser dgt;
|
||||
dgt.traverse_below(this, _attrib);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -858,6 +854,7 @@ transmit_data(NodeAttributes &data) {
|
|||
ButtonEventDataAttribute::const_iterator bi;
|
||||
for (bi = b->begin(); bi != b->end(); ++bi) {
|
||||
const ButtonEvent &be = (*bi);
|
||||
_mods.add_event(be);
|
||||
|
||||
if (be._button == KeyboardButton::up()) {
|
||||
_up_arrow.set_key(be._down);
|
||||
|
|
@ -872,7 +869,6 @@ transmit_data(NodeAttributes &data) {
|
|||
}
|
||||
|
||||
// Now look for mouse activity.
|
||||
bool any_button = false;
|
||||
double x = 0.0;
|
||||
double y = 0.0;
|
||||
|
||||
|
|
@ -881,18 +877,9 @@ transmit_data(NodeAttributes &data) {
|
|||
LVecBase3f p = xyz->get_value();
|
||||
x = p[0];
|
||||
y = p[1];
|
||||
|
||||
const ModifierButtonDataAttribute *button;
|
||||
if (get_attribute_into(button, data, _mods_type)) {
|
||||
ModifierButtons mods = button->get_mods();
|
||||
any_button =
|
||||
mods.is_down(MouseButton::one()) ||
|
||||
mods.is_down(MouseButton::two()) ||
|
||||
mods.is_down(MouseButton::three());
|
||||
}
|
||||
}
|
||||
|
||||
apply(x, y, any_button);
|
||||
apply(x, y, _mods.is_any_down());
|
||||
_transform->set_value(_mat);
|
||||
|
||||
// Now send our matrix down the pipe.
|
||||
|
|
@ -911,9 +898,6 @@ init_type() {
|
|||
register_type(_type_handle, "DriveInterface",
|
||||
DataNode::get_class_type());
|
||||
|
||||
ModifierButtonDataTransition::init_type();
|
||||
register_data_transition(_mods_type, "ModifierButtons",
|
||||
ModifierButtonDataTransition::get_class_type());
|
||||
Vec3DataTransition::init_type();
|
||||
register_data_transition(_xyz_type, "XYZ",
|
||||
Vec3DataTransition::get_class_type());
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <matrixDataTransition.h>
|
||||
#include <matrixDataAttribute.h>
|
||||
#include <nodeAttributes.h>
|
||||
#include <modifierButtons.h>
|
||||
#include <luse.h>
|
||||
#include <lmatrix.h>
|
||||
|
||||
|
|
@ -122,6 +123,9 @@ private:
|
|||
bool _is_force_roll;
|
||||
CoordinateSystem _cs;
|
||||
|
||||
// Remember which mouse buttons are being held down.
|
||||
ModifierButtons _mods;
|
||||
|
||||
// Remember which arrow keys are being held down and which aren't,
|
||||
// and at what point they last changed state.
|
||||
class KeyHeld {
|
||||
|
|
@ -152,7 +156,6 @@ public:
|
|||
PT(MatrixDataAttribute) _transform;
|
||||
|
||||
// inputs
|
||||
static TypeHandle _mods_type;
|
||||
static TypeHandle _xyz_type;
|
||||
static TypeHandle _button_events_type;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,8 @@
|
|||
|
||||
#include <mouse.h>
|
||||
#include <mouseData.h>
|
||||
#include <modifierButtons.h>
|
||||
#include <buttonEventDataTransition.h>
|
||||
#include <buttonEventDataAttribute.h>
|
||||
#include <modifierButtonDataTransition.h>
|
||||
#include <modifierButtonDataAttribute.h>
|
||||
#include <keyboardButton.h>
|
||||
#include <mouseButton.h>
|
||||
#include <throw_event.h>
|
||||
|
|
@ -21,7 +18,6 @@
|
|||
|
||||
TypeHandle MouseWatcher::_type_handle;
|
||||
|
||||
TypeHandle MouseWatcher::_mods_type;
|
||||
TypeHandle MouseWatcher::_xyz_type;
|
||||
TypeHandle MouseWatcher::_button_events_type;
|
||||
|
||||
|
|
@ -313,9 +309,6 @@ init_type() {
|
|||
register_type(_type_handle, "MouseWatcher",
|
||||
DataNode::get_class_type());
|
||||
|
||||
ModifierButtonDataTransition::init_type();
|
||||
register_data_transition(_mods_type, "ModifierButtons",
|
||||
ModifierButtonDataTransition::get_class_type());
|
||||
Vec3DataTransition::init_type();
|
||||
register_data_transition(_xyz_type, "XYZ",
|
||||
Vec3DataTransition::get_class_type());
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ public:
|
|||
transmit_data(NodeAttributes &data);
|
||||
|
||||
// inputs & outputs
|
||||
static TypeHandle _mods_type;
|
||||
static TypeHandle _xyz_type;
|
||||
static TypeHandle _button_events_type;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,15 @@
|
|||
#include <mouse.h>
|
||||
#include <mouseData.h>
|
||||
#include <modifierButtons.h>
|
||||
#include <modifierButtonDataTransition.h>
|
||||
#include <buttonEventDataTransition.h>
|
||||
#include <buttonEventDataAttribute.h>
|
||||
#include <mouseButton.h>
|
||||
#include <get_rel_pos.h>
|
||||
|
||||
TypeHandle PlanarSlider::_type_handle;
|
||||
|
||||
TypeHandle PlanarSlider::_mods_type;
|
||||
TypeHandle PlanarSlider::_xyz_type;
|
||||
TypeHandle PlanarSlider::_button_events_type;
|
||||
TypeHandle PlanarSlider::_transform_type;
|
||||
|
||||
|
||||
|
|
@ -34,6 +35,8 @@ PlanarSlider(const string &name) : DataNode(name) {
|
|||
|
||||
_transform = new MatrixDataAttribute;
|
||||
_attrib.set_attribute(_transform_type, _transform);
|
||||
|
||||
_mods.add_button(MouseButton::one());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -146,19 +149,17 @@ get_mouse_pos() const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void PlanarSlider::
|
||||
transmit_data(NodeAttributes &data) {
|
||||
const NodeAttribute *button = data.get_attribute(_mods_type);
|
||||
// First, update our modifier buttons.
|
||||
const ButtonEventDataAttribute *b;
|
||||
if (get_attribute_into(b, data, _button_events_type)) {
|
||||
b->update_mods(_mods);
|
||||
}
|
||||
|
||||
// Now look for a mouse position.
|
||||
const NodeAttribute *xyz = data.get_attribute(_xyz_type);
|
||||
|
||||
if (xyz != (NodeAttribute *)NULL) {
|
||||
bool is_down = false;
|
||||
|
||||
if (button != (NodeAttribute *)NULL) {
|
||||
ModifierButtons mods =
|
||||
DCAST(ModifierButtonDataAttribute, button)->get_mods();
|
||||
is_down = mods.is_down(MouseButton::one());
|
||||
}
|
||||
|
||||
if (is_down) {
|
||||
if (_mods.is_any_down()) {
|
||||
LVecBase3f p = DCAST(Vec3DataAttribute, xyz)->get_value();
|
||||
set_mouse_pos(LPoint2f(p[0], p[1]));
|
||||
}
|
||||
|
|
@ -180,13 +181,13 @@ init_type() {
|
|||
register_type(_type_handle, "PlanarSlider",
|
||||
DataNode::get_class_type());
|
||||
|
||||
ModifierButtonDataTransition::init_type();
|
||||
register_data_transition(_mods_type, "ModifierButtons",
|
||||
ModifierButtonDataTransition::get_class_type());
|
||||
Vec3DataTransition::init_type();
|
||||
register_data_transition(_xyz_type, "XYZ",
|
||||
Vec3DataTransition::get_class_type());
|
||||
MatrixDataTransition::init_type();
|
||||
register_data_transition(_transform_type, "Transform",
|
||||
MatrixDataTransition::get_class_type());
|
||||
ButtonEventDataTransition::init_type();
|
||||
register_data_transition(_button_events_type, "ButtonEvents",
|
||||
ButtonEventDataTransition::get_class_type());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <nodeAttributes.h>
|
||||
#include <luse.h>
|
||||
#include <lmatrix.h>
|
||||
#include <modifierButtons.h>
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -46,6 +47,7 @@ private:
|
|||
CoordinateSystem _cs;
|
||||
LMatrix4f _transform_mat, _inverse_mat;
|
||||
LPoint2f _mouse_pos;
|
||||
ModifierButtons _mods;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// From parent class DataNode
|
||||
|
|
@ -59,8 +61,8 @@ public:
|
|||
PT(MatrixDataAttribute) _transform;
|
||||
|
||||
// inputs
|
||||
static TypeHandle _mods_type;
|
||||
static TypeHandle _xyz_type;
|
||||
static TypeHandle _button_events_type;
|
||||
|
||||
// outputs
|
||||
static TypeHandle _transform_type;
|
||||
|
|
|
|||
|
|
@ -10,15 +10,14 @@
|
|||
#include <mouseData.h>
|
||||
#include <modifierButtons.h>
|
||||
#include <buttonEventDataTransition.h>
|
||||
#include <modifierButtonDataTransition.h>
|
||||
#include <modifierButtonDataAttribute.h>
|
||||
#include <buttonEventDataAttribute.h>
|
||||
#include <mouseButton.h>
|
||||
#include <get_rel_pos.h>
|
||||
|
||||
TypeHandle Trackball::_type_handle;
|
||||
|
||||
TypeHandle Trackball::_mods_type;
|
||||
TypeHandle Trackball::_pixel_xyz_type;
|
||||
TypeHandle Trackball::_button_events_type;
|
||||
TypeHandle Trackball::_transform_type;
|
||||
|
||||
#define B1_MASK 1
|
||||
|
|
@ -46,6 +45,10 @@ Trackball(const string &name) : DataNode(name) {
|
|||
_rel_to = NULL;
|
||||
_cs = default_coordinate_system;
|
||||
|
||||
_mods.add_button(MouseButton::one());
|
||||
_mods.add_button(MouseButton::two());
|
||||
_mods.add_button(MouseButton::three());
|
||||
|
||||
_transform = new MatrixDataAttribute;
|
||||
_transform->set_value(LMatrix4f::ident_mat());
|
||||
_attrib.set_attribute(_transform_type, _transform);
|
||||
|
|
@ -471,7 +474,13 @@ recompute() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void Trackball::
|
||||
transmit_data(NodeAttributes &data) {
|
||||
const NodeAttribute *button = data.get_attribute(_mods_type);
|
||||
// First, update our modifier buttons.
|
||||
const ButtonEventDataAttribute *b;
|
||||
if (get_attribute_into(b, data, _button_events_type)) {
|
||||
b->update_mods(_mods);
|
||||
}
|
||||
|
||||
// Now, check for mouse motion.
|
||||
const NodeAttribute *pixel_xyz = data.get_attribute(_pixel_xyz_type);
|
||||
|
||||
if (pixel_xyz != (NodeAttribute *)NULL) {
|
||||
|
|
@ -480,19 +489,14 @@ transmit_data(NodeAttributes &data) {
|
|||
float this_y = p[1];
|
||||
int this_button = 0;
|
||||
|
||||
if (button != (NodeAttribute *)NULL) {
|
||||
ModifierButtons mods =
|
||||
DCAST(ModifierButtonDataAttribute, button)->get_mods();
|
||||
|
||||
if (mods.is_down(MouseButton::one())) {
|
||||
this_button |= B1_MASK;
|
||||
}
|
||||
if (mods.is_down(MouseButton::two())) {
|
||||
this_button |= B2_MASK;
|
||||
}
|
||||
if (mods.is_down(MouseButton::three())) {
|
||||
this_button |= B3_MASK;
|
||||
}
|
||||
if (_mods.is_down(MouseButton::one())) {
|
||||
this_button |= B1_MASK;
|
||||
}
|
||||
if (_mods.is_down(MouseButton::two())) {
|
||||
this_button |= B2_MASK;
|
||||
}
|
||||
if (_mods.is_down(MouseButton::three())) {
|
||||
this_button |= B3_MASK;
|
||||
}
|
||||
|
||||
float x = this_x - _lastx;
|
||||
|
|
@ -521,12 +525,12 @@ init_type() {
|
|||
register_type(_type_handle, "Trackball",
|
||||
DataNode::get_class_type());
|
||||
|
||||
ModifierButtonDataTransition::init_type();
|
||||
register_data_transition(_mods_type, "ModifierButtons",
|
||||
ModifierButtonDataTransition::get_class_type());
|
||||
Vec3DataTransition::init_type();
|
||||
register_data_transition(_pixel_xyz_type, "PixelXYZ",
|
||||
Vec3DataTransition::get_class_type());
|
||||
ButtonEventDataTransition::init_type();
|
||||
register_data_transition(_button_events_type, "ButtonEvents",
|
||||
ButtonEventDataTransition::get_class_type());
|
||||
MatrixDataTransition::init_type();
|
||||
register_data_transition(_transform_type, "Transform",
|
||||
MatrixDataTransition::get_class_type());
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <matrixDataTransition.h>
|
||||
#include <matrixDataAttribute.h>
|
||||
#include <nodeAttributes.h>
|
||||
#include <modifierButtons.h>
|
||||
#include <luse.h>
|
||||
#include <lmatrix.h>
|
||||
|
||||
|
|
@ -103,6 +104,9 @@ private:
|
|||
const Node *_rel_to;
|
||||
CoordinateSystem _cs;
|
||||
|
||||
// Remember which mouse buttons are being held down.
|
||||
ModifierButtons _mods;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// From parent class DataNode
|
||||
|
|
@ -116,8 +120,8 @@ public:
|
|||
PT(MatrixDataAttribute) _transform;
|
||||
|
||||
// inputs
|
||||
static TypeHandle _mods_type;
|
||||
static TypeHandle _pixel_xyz_type;
|
||||
static TypeHandle _button_events_type;
|
||||
|
||||
// outputs
|
||||
static TypeHandle _transform_type;
|
||||
|
|
|
|||
Loading…
Reference in New Issue