Logo Search packages:      
Sourcecode: k3d version File versions

object_model.cpp

// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "object_model.h"

#include <k3dsdk/algebra.h>
#include <k3dsdk/application.h>
#include <k3dsdk/command_node.h>
#include <k3dsdk/file_filter.h>
#include <k3dsdk/ianimation_render_engine.h>
#include <k3dsdk/iapplication_plugin_factory.h>
#include <k3dsdk/ibezier_channel.h>
#include <k3dsdk/idag.h>
#include <k3dsdk/idocument.h>
#include <k3dsdk/idocument_plugin_factory.h>
#include <k3dsdk/igeometry_read_format.h>
#include <k3dsdk/igeometry_write_format.h>
#include <k3dsdk/iobject.h>
#include <k3dsdk/iplugin_factory.h>
#include <k3dsdk/iplugin_factory_collection.h>
#include <k3dsdk/iproperty.h>
#include <k3dsdk/iproperty_collection.h>
#include <k3dsdk/iselectable.h>
#include <k3dsdk/istill_render_engine.h>
#include <k3dsdk/iuser_interface.h>
#include <k3dsdk/iwritable_property.h>
#include <k3dsdk/iviewport.h>
#include <k3dsdk/iviewport_host.h>
#include <k3dsdk/objects.h>
#include <k3dsdk/plugins.h>
#include <k3dsdk/scripting.h>
#include <k3dsdk/selection.h>
#include <k3dsdk/serialization.h>
#include <k3dsdk/state_change_set.h>
#include <k3dsdk/viewport.h>

#include <boost/filesystem/path.hpp>

#include <cmath>
#include <fstream>
#include <iostream>
#include <stdexcept>

#include <jsapi.h>

namespace libk3djavascript
{

namespace javascript
{

/// JavaScript class for a "generic" (wraps any interface) JavaScript object
JSClass generic_object_class =
{
      "k3d_generic_object", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};

/// Casts a "generic" (wraps any interface) JavaScript object to a specific interface type (could return NULL)
template<class InterfaceType>
InterfaceType cast(JSContext* Context, JSObject* Object)
{
      return (JS_GetClass(Object) == &generic_object_class) ? dynamic_cast<InterfaceType>(reinterpret_cast<k3d::iunknown*>(JS_GetPrivate(Context, Object))) : 0;
}

const jsval convert(JSContext* Context, const bool From)
{
      return BOOLEAN_TO_JSVAL(From);
}

const jsval convert(JSContext* Context, const unsigned long From)
{
      return DOUBLE_TO_JSVAL(JS_NewDouble(Context, From));
}

const jsval convert(JSContext* Context, const double From)
{
      return DOUBLE_TO_JSVAL(JS_NewDouble(Context, From));
}

const jsval convert(JSContext* Context, const std::string& From)
{
      return STRING_TO_JSVAL(JS_NewStringCopyZ(Context, From.c_str()));
}

const jsval convert(JSContext* Context, const k3d::vector2& From)
{
      jsval values[2];
      values[0] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[0]));
      values[1] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[1]));

      return OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 2, values));
}

const jsval convert(JSContext* Context, const k3d::vector3& From)
{
      jsval values[3];
      values[0] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[0]));
      values[1] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[1]));
      values[2] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[2]));

      return OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 3, values));
}

const jsval convert(JSContext* Context, const k3d::vector4& From)
{
      jsval values[4];
      values[0] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[0]));
      values[1] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[1]));
      values[2] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[2]));
      values[3] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From[3]));

      return OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 4, values));
}

const jsval convert(JSContext* Context, const k3d::angle_axis& From)
{
      jsval values[4];
      values[0] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From.angle));
      values[1] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From.axis[0]));
      values[2] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From.axis[1]));
      values[3] = DOUBLE_TO_JSVAL(JS_NewDouble(Context, From.axis[2]));

      return OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 4, values));
}

template<typename data_t>
const jsval convert(JSContext* Context, const std::vector<data_t>& From)
{
      std::vector<jsval> values;
      for(typename std::vector<data_t>::const_iterator value = From.begin(); value != From.end(); ++value)
            values.push_back(convert(Context, *value));

      return OBJECT_TO_JSVAL(JS_NewArrayObject(Context, values.size(), &values.front()));
}

const jsval convert(JSContext* Context, const boost::any Value)
{
      const std::type_info& value_type = Value.type();

      // We put the likely types up front for efficiency ...
      if(value_type == typeid(bool))
            return convert(Context, boost::any_cast<bool>(Value));

      if(value_type == typeid(double))
            return convert(Context, boost::any_cast<double>(Value));

      if(value_type == typeid(std::string))
            return convert(Context, boost::any_cast<std::string>(Value));

      if(value_type == typeid(k3d::vector2))
            return convert(Context, boost::any_cast<k3d::vector2>(Value));

      if(value_type == typeid(k3d::vector3))
            return convert(Context, boost::any_cast<k3d::vector3>(Value));

      if(value_type == typeid(k3d::vector4))
            return convert(Context, boost::any_cast<k3d::vector4>(Value));

      if(value_type == typeid(k3d::angle_axis))
            return convert(Context, boost::any_cast<k3d::angle_axis>(Value));

      if(value_type == typeid(unsigned long))
            return convert(Context, boost::any_cast<unsigned long>(Value));

      if(value_type == typeid(k3d::iobject*))
            return OBJECT_TO_JSVAL(create_generic_object(*boost::any_cast<k3d::iobject*>(Value), Context));

      if(value_type == typeid(boost::filesystem::path))
            return convert(Context, boost::any_cast<boost::filesystem::path>(Value).native_file_string());

      std::cerr << error << __PRETTY_FUNCTION__ << " : unrecognized type" << std::endl;
      return JSVAL_NULL;
}

bool convert(JSContext* Context, const jsval From, bool& To)
{
      JSBool to;
      if(JS_TRUE == JS_ValueToBoolean(Context, From, &to))
            {
                  To = JS_TRUE == to;
                  return true;
            }

      return false;
}

bool convert(JSContext* Context, const jsval From, double& To)
{
      return JS_TRUE == JS_ValueToNumber(Context, From, &To);
}

bool convert(JSContext* Context, const jsval From, k3d::vector2& To)
{
      if(JSVAL_IS_OBJECT(From))
            {
                  jsval element;

                  JS_GetElement(Context, JSVAL_TO_OBJECT(From), 0, &element); JS_ValueToNumber(Context, element, &To[0]);
                  JS_GetElement(Context, JSVAL_TO_OBJECT(From), 1, &element); JS_ValueToNumber(Context, element, &To[1]);

                  return true;
            }

      return false;
}

bool convert(JSContext* Context, const jsval From, k3d::vector3& To)
{
      if(JSVAL_IS_OBJECT(From))
            {
                  jsval element;

                  JS_GetElement(Context, JSVAL_TO_OBJECT(From), 0, &element); JS_ValueToNumber(Context, element, &To[0]);
                  JS_GetElement(Context, JSVAL_TO_OBJECT(From), 1, &element); JS_ValueToNumber(Context, element, &To[1]);
                  JS_GetElement(Context, JSVAL_TO_OBJECT(From), 2, &element); JS_ValueToNumber(Context, element, &To[2]);

                  return true;
            }

      return false;
}

bool convert(JSContext* Context, const jsval From, unsigned long& To)
{
      double to;
      if(JS_TRUE == JS_ValueToNumber(Context, From, &to))
            {
                  To = static_cast<unsigned long>(std::max(0.0, to));
                  return true;
            }

      return false;
}

const std::string string_cast(JSContext* Context, const jsval From)
{
      if(JSVAL_IS_NULL(From) || JSVAL_IS_VOID(From))
            return std::string();

      return std::string(JS_GetStringBytes(JS_ValueToString(Context, From)));
}

const boost::any convert(JSContext* Context, const jsval From, const std::type_info& TargetType)
{
      if(TargetType == typeid(bool))
            {
                  bool result;
                  if(convert(Context, From, result))
                        return boost::any(result);

                  return boost::any();
            }

      if(TargetType == typeid(double))
            {
                  double result;
                  if(convert(Context, From, result))
                        return boost::any(result);

                  return boost::any();
            }

      if(TargetType == typeid(std::string))
            {
                  return boost::any(string_cast(Context, From));
            }

      if(TargetType == typeid(k3d::vector3))
            {
                  k3d::vector3 result;
                  if(convert(Context, From, result))
                        return boost::any(result);

                  return boost::any();
            }

      if(TargetType == typeid(k3d::angle_axis))
            {
                  if(!JSVAL_IS_OBJECT(From))
                        return boost::any();

                  jsuint length = 0;
                  if(JS_TRUE != JS_GetArrayLength(Context, JSVAL_TO_OBJECT(From), &length))
                        return boost::any();

                  if(3 == length)
                        {
                              k3d::euler_angles euler(0, 0, 0, k3d::euler_angles::ZXYstatic);

                              jsval element;
                              JS_GetElement(Context, JSVAL_TO_OBJECT(From), 0, &element); JS_ValueToNumber(Context, element, &euler.n[1]);
                              JS_GetElement(Context, JSVAL_TO_OBJECT(From), 1, &element); JS_ValueToNumber(Context, element, &euler.n[2]);
                              JS_GetElement(Context, JSVAL_TO_OBJECT(From), 2, &element); JS_ValueToNumber(Context, element, &euler.n[0]);

                              euler.n[0] = k3d::radians(euler.n[0]);
                              euler.n[1] = k3d::radians(euler.n[1]);
                              euler.n[2] = k3d::radians(euler.n[2]);

                              return boost::any(k3d::angle_axis(euler));
                        }
                  else if(4 == length)
                        {
                              k3d::angle_axis result;

                              jsval element;
                              JS_GetElement(Context, JSVAL_TO_OBJECT(From), 0, &element); JS_ValueToNumber(Context, element, &result.angle);
                              JS_GetElement(Context, JSVAL_TO_OBJECT(From), 1, &element); JS_ValueToNumber(Context, element, &result.axis[0]);
                              JS_GetElement(Context, JSVAL_TO_OBJECT(From), 2, &element); JS_ValueToNumber(Context, element, &result.axis[1]);
                              JS_GetElement(Context, JSVAL_TO_OBJECT(From), 3, &element); JS_ValueToNumber(Context, element, &result.axis[2]);

                              result.angle = k3d::radians(result.angle);

                              return boost::any(result);
                        }
            }

      if(TargetType == typeid(unsigned long))
            {
                  unsigned long result;
                  if(convert(Context, From, result))
                        return boost::any(result);

                  return boost::any();
            }

      if(TargetType == typeid(k3d::iobject*))
            {
                  if(JSVAL_IS_OBJECT(From))
                        return boost::any(javascript::cast<k3d::iobject*>(Context, JSVAL_TO_OBJECT(From)));

                  return boost::any();
            }

      std::cerr << error << __PRETTY_FUNCTION__ << " : unrecognized type" << std::endl;
      return boost::any();
}

} // namespace javascript

JSBool get_viewport_host(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iviewport_host* const viewport_host = javascript::cast<k3d::iviewport*>(Context, Object)->host();
      if(viewport_host)
            *Value = OBJECT_TO_JSVAL(create_generic_object(*viewport_host, Context));
      else
            *Value = JSVAL_NULL;

      return JS_TRUE;
}

JSBool set_viewport_host(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      return_val_if_fail(JSVAL_IS_OBJECT(*Value), JS_FALSE);

      k3d::iviewport_host* const viewport_host = javascript::cast<k3d::iviewport_host*>(Context, JSVAL_TO_OBJECT(*Value));
      javascript::cast<k3d::iviewport*>(Context, Object)->set_host(viewport_host);

      return JS_TRUE;
}

/// Adds k3d::iviewport-specific behavior to a "generic" (wraps any interface) JavaScript object
00378 void add_viewport_behavior(JSContext* Context, JSObject* Object)
{
      k3d::iviewport* const viewport = javascript::cast<k3d::iviewport*>(Context, Object);
      if(!viewport)
            return;

      JS_DefineProperty(Context, Object, "viewport_host", 0, &get_viewport_host, &set_viewport_host, JSPROP_ENUMERATE | JSPROP_PERMANENT);
};

JSBool get_scalar_curve(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::ibezier_channel<void>* const bezier_channel = javascript::cast<k3d::ibezier_channel<void>*>(Context, Object);
      return_val_if_fail(bezier_channel, JS_FALSE);

      k3d::ibezier_channel<void>::control_points_t control_points;
      bezier_channel->get_curve(control_points);

      // Create the generic object instance ...
      JSObject* const object = JS_NewObject(Context, &javascript::generic_object_class, 0, 0);
      return_val_if_fail(object, JS_FALSE);

      // Add control point and value properties ...
      jsval js_control_points = javascript::convert(Context, control_points);
      JS_SetProperty(Context, object, "control_points", &js_control_points);

      *Value = OBJECT_TO_JSVAL(object);

      return JS_TRUE;
}

JSBool set_scalar_curve(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      // Sanity checks ...
      k3d::ibezier_channel<void>* const bezier_channel = javascript::cast<k3d::ibezier_channel<void>*>(Context, Object);
      return_val_if_fail(bezier_channel, JS_FALSE);

      // Get control point and value properties ...
      return_val_if_fail(JSVAL_IS_OBJECT(*Value), JS_FALSE);
      JSObject* const object = JSVAL_TO_OBJECT(*Value);
      return_val_if_fail(object, JS_FALSE);

      jsval js_control_points = JSVAL_VOID;
      return_val_if_fail(JS_TRUE == JS_GetProperty(Context, object, "control_points", &js_control_points), JS_FALSE);
      jsuint js_control_points_length = 0;
      return_val_if_fail(JS_TRUE == JS_GetArrayLength(Context, JSVAL_TO_OBJECT(js_control_points), &js_control_points_length), JS_FALSE);

      // Convert control points ...
      k3d::ibezier_channel<void>::control_points_t control_points;
      for(jsuint i = 0; i < js_control_points_length; ++i)
            {
                  jsval element; JS_GetElement(Context, JSVAL_TO_OBJECT(js_control_points), i, &element);
                  k3d::vector2 control_point; javascript::convert(Context, element, control_point);
                  control_points.push_back(control_point);
            }

      // Make it happen ...
      bezier_channel->set_curve(control_points);

      return JS_TRUE;
}

JSBool get_color_curve(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::ibezier_channel<k3d::vector3>* const bezier_channel = javascript::cast<k3d::ibezier_channel<k3d::vector3>*>(Context, Object);
      return_val_if_fail(bezier_channel, JS_FALSE);

      k3d::ibezier_channel<k3d::vector3>::control_points_t control_points;
      k3d::ibezier_channel<k3d::vector3>::values_t values;
      bezier_channel->get_curve(control_points, values);

      // Create the generic object instance ...
      JSObject* const object = JS_NewObject(Context, &javascript::generic_object_class, 0, 0);
      return_val_if_fail(object, JS_FALSE);

      // Add control point and value properties ...
      jsval js_control_points = javascript::convert(Context, control_points);
      JS_SetProperty(Context, object, "control_points", &js_control_points);

      jsval js_values = javascript::convert(Context, values);
      JS_SetProperty(Context, object, "values", &js_values);

      *Value = OBJECT_TO_JSVAL(object);

      return JS_TRUE;
}

JSBool set_color_curve(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      // Sanity checks ...
      k3d::ibezier_channel<k3d::vector3>* const bezier_channel = javascript::cast<k3d::ibezier_channel<k3d::vector3>*>(Context, Object);
      return_val_if_fail(bezier_channel, JS_FALSE);

      // Get control point and value properties ...
      return_val_if_fail(JSVAL_IS_OBJECT(*Value), JS_FALSE);
      JSObject* const object = JSVAL_TO_OBJECT(*Value);
      return_val_if_fail(object, JS_FALSE);

      jsval js_control_points = JSVAL_VOID;
      return_val_if_fail(JS_TRUE == JS_GetProperty(Context, object, "control_points", &js_control_points), JS_FALSE);
      jsuint js_control_points_length = 0;
      return_val_if_fail(JS_TRUE == JS_GetArrayLength(Context, JSVAL_TO_OBJECT(js_control_points), &js_control_points_length), JS_FALSE);

      jsval js_values = JSVAL_VOID;
      return_val_if_fail(JS_TRUE == JS_GetProperty(Context, object, "values", &js_values), JS_FALSE);
      jsuint js_values_length = 0;
      return_val_if_fail(JS_TRUE == JS_GetArrayLength(Context, JSVAL_TO_OBJECT(js_values), &js_values_length), JS_FALSE);

      // Convert control points ...
      k3d::ibezier_channel<k3d::vector3>::control_points_t control_points;
      for(jsuint i = 0; i < js_control_points_length; ++i)
            {
                  jsval element; JS_GetElement(Context, JSVAL_TO_OBJECT(js_control_points), i, &element);
                  k3d::vector2 control_point; javascript::convert(Context, element, control_point);
                  control_points.push_back(control_point);
            }

      // Convert values ...
      k3d::ibezier_channel<k3d::vector3>::values_t values;
      for(jsuint i = 0; i < js_values_length; ++i)
            {
                  jsval element; JS_GetElement(Context, JSVAL_TO_OBJECT(js_values), i, &element);
                  k3d::vector3 value; javascript::convert(Context, element, value);
                  values.push_back(value);
            }

      // Make it happen ...
      bezier_channel->set_curve(control_points, values);

      return JS_TRUE;
}

/// Adds k3d::ibezier_channel-specific behavior to a "generic" (wraps any interface) JavaScript object
00510 void add_bezier_channel_behavior(JSContext* Context, JSObject* Object)
{
      // See if this object supports a scalar channel (k3d::ibezier_channel<void>)
      k3d::ibezier_channel<void>* const scalar_bezier_channel = javascript::cast<k3d::ibezier_channel<void>*>(Context, Object);
      if(scalar_bezier_channel)
            {
                  // Add properties ...
                  JS_DefineProperty(Context, Object, "curve", 0, &get_scalar_curve, &set_scalar_curve, JSPROP_ENUMERATE | JSPROP_PERMANENT);
                  return;
            }

      // See if this object supports a color channel (k3d::ibezier_channel<k3d::vector3>)
      k3d::ibezier_channel<k3d::vector3>* const color_bezier_channel = javascript::cast<k3d::ibezier_channel<k3d::vector3>*>(Context, Object);
      if(color_bezier_channel)
            {
                  // Add properties ...
                  JS_DefineProperty(Context, Object, "curve", 0, &get_color_curve, &set_color_curve, JSPROP_ENUMERATE | JSPROP_PERMANENT);
                  return;
            }
}

/// k3d::istill_render_engine::render_preview() wrapper
00532 JSBool still_render_engine_render_preview(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      javascript::cast<k3d::istill_render_engine*>(Context, Object)->render_preview();
      return JS_TRUE;
}

/// k3d::istill_render_engine::render_frame() wrapper
00539 JSBool still_render_engine_render_frame(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      if(JSVAL_IS_STRING(argv[0]))
            return javascript::cast<k3d::istill_render_engine*>(Context, Object)->render_frame(
                  boost::filesystem::path(javascript::string_cast(Context, argv[0]), boost::filesystem::native), JSVAL_TO_BOOLEAN(argv[1])) ? JS_TRUE : JS_FALSE;

      return JS_FALSE;
}

/// k3d::ianimation_render_engine::render_animation() wrapper
00549 JSBool animation_render_engine_render_animation(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      if(JSVAL_IS_STRING(argv[0]))
            return javascript::cast<k3d::ianimation_render_engine*>(Context, Object)->render_animation(
                  boost::filesystem::path(javascript::string_cast(Context, argv[0]), boost::filesystem::native), JSVAL_TO_BOOLEAN(argv[1])) ? JS_TRUE : JS_FALSE;

      return JS_FALSE;
}

/// Adds k3d::istill_render_engine-specific behavior to a "generic" (wraps any interface) JavaScript object
00559 void add_still_render_engine_behavior(JSContext* Context, JSObject* Object)
{
      k3d::istill_render_engine* const render_engine = javascript::cast<k3d::istill_render_engine*>(Context, Object);
      if(!render_engine)
            return;

      JS_DefineFunction(Context, Object, "RenderPreview", &still_render_engine_render_preview, 0, 0);
      JS_DefineFunction(Context, Object, "RenderFrame", &still_render_engine_render_frame, 2, 0);
}

/// Adds k3d::ianimation_render_engine-specific behavior to a "generic" (wraps any interface) JavaScript object
00570 void add_animation_render_engine_behavior(JSContext* Context, JSObject* Object)
{
      k3d::ianimation_render_engine* const render_engine = javascript::cast<k3d::ianimation_render_engine*>(Context, Object);
      if(!render_engine)
            return;

      JS_DefineFunction(Context, Object, "RenderAnimation", &animation_render_engine_render_animation, 2, 0);
}

/// k3d::icommand_node::execute_command wrapper
00580 JSBool execute_command(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      const std::string command(javascript::string_cast(Context, argv[0]));
      return_val_if_fail(command.size(), JS_FALSE);

      const std::string arguments(javascript::string_cast(Context, argv[1]));

      return javascript::cast<k3d::icommand_node*>(Context, Object)->execute_command(command, arguments) ? JS_TRUE : JS_FALSE;
}

/// Adds command-node-specific behavior to a "generic" (wraps any interface) JavaScript object
00591 void add_command_node_behavior(JSContext* Context, JSObject* Object)
{
      // Test to see if it's a command-node ...
      k3d::icommand_node* const command_node = javascript::cast<k3d::icommand_node*>(Context, Object);
      if(!command_node)
            return;

      // Define functions ...
      JS_DefineFunction(Context, Object, "Command", &execute_command, 2, 0);

}

/// k3d::iproperty::value wrapper
00604 JSBool get_property(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iproperty_collection* const property_collection = javascript::cast<k3d::iproperty_collection*>(Context, Object);
      k3d::iproperty* const property = property_collection->properties()[JSVAL_TO_INT(ID)];

      *Value = javascript::convert(Context, property->value());

      return JS_TRUE;
}

/// k3d::iproperty::set_value wrapper
00615 JSBool set_property(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iproperty_collection* const property_collection = javascript::cast<k3d::iproperty_collection*>(Context, Object);
      k3d::iproperty* const property = property_collection->properties()[JSVAL_TO_INT(ID)];
      k3d::iwritable_property* const writable_property = dynamic_cast<k3d::iwritable_property*>(property);
      return_val_if_fail(writable_property, JS_FALSE);

      writable_property->set_value(javascript::convert(Context, *Value, property->type()));

      return JS_TRUE;
}

JSBool get_property_collection_properties(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      // Get the set of all properties ...
      const k3d::iproperty_collection::properties_t properties(javascript::cast<k3d::iproperty_collection*>(Context, Object)->properties());

      // convert the set to a Javascript array ...
      std::vector<jsval> values;
      for(k3d::iproperty_collection::properties_t::const_iterator property = properties.begin(); property != properties.end(); ++property)
            values.push_back(OBJECT_TO_JSVAL(create_property(**property, Context)));

      // Return that baby ...
      if(values.size())
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, values.size(), &values.front()));
      else
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 0, 0));

      return JS_TRUE;
}

/// Returns an object property by name
00647 JSBool get_property_by_name(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      // Gotta have a name to work with ...
      const std::string name(JS_GetStringBytes(JS_ValueToString(Context, argv[0])));
      return_val_if_fail(name.size(), JS_FALSE);

      // Get the set of all properties ...
      const k3d::iproperty_collection::properties_t properties(javascript::cast<k3d::iproperty_collection*>(Context, Object)->properties());
      for(k3d::iproperty_collection::properties_t::const_iterator property = properties.begin(); property != properties.end(); ++property)
            {
                  if(name == (*property)->name())
                        {
                              *Result = OBJECT_TO_JSVAL(create_property(**property, Context));
                              return JS_TRUE;
                        }
            }

      *Result = JSVAL_NULL;
      return JS_TRUE;
}

/// Adds property-collection-specific behavior to a "generic" (wraps any interface) JavaScript object
00669 void add_property_collection_behavior(JSContext* Context, JSObject* Object)
{
      // Test to see if it's a property collection ...
      k3d::iproperty_collection* const property_collection = javascript::cast<k3d::iproperty_collection*>(Context, Object);
      if(!property_collection)
            return;

      // Define functions ...
      JS_DefineFunction(Context, Object, "Property", &get_property_by_name, 1, 0);

      // Define properties ...
      const k3d::iproperty_collection::properties_t properties(property_collection->properties());

      unsigned char index = 0;
      for(k3d::iproperty_collection::properties_t::const_iterator property = properties.begin(); property != properties.end(); ++property)
            {
                  if(dynamic_cast<k3d::iwritable_property*>(*property))
                        JS_DefinePropertyWithTinyId(Context, Object, (*property)->name().c_str(), index++, 0, &get_property, &set_property, JSPROP_ENUMERATE | JSPROP_PERMANENT);
                  else
                        JS_DefinePropertyWithTinyId(Context, Object, (*property)->name().c_str(), index++, 0, &get_property, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
            }

      JS_DefineProperty(Context, Object, "properties", 0, &get_property_collection_properties, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

JSBool get_property_name(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iproperty* const property = javascript::cast<k3d::iproperty*>(Context, Object);
      *Value = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, property->name().c_str()));
      return JS_TRUE;
}

JSBool get_property_description(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iproperty* const property = javascript::cast<k3d::iproperty*>(Context, Object);
      *Value = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, property->description().c_str()));
      return JS_TRUE;
}

/// Adds k3d::iproperty specific behavior to a "generic" (wraps any interface) JavaScript object
00709 void add_property_behavior(JSContext* Context, JSObject* Object)
{
      // Test to see that it's a property ...
      k3d::iproperty* const property = javascript::cast<k3d::iproperty*>(Context, Object);
      if(!property)
            return;

      // Define properties (!)
      JS_DefineProperty(Context, Object, "name", 0, &get_property_name, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
      JS_DefineProperty(Context, Object, "description", 0, &get_property_description, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

/// Wraps iobject::document()
00722 JSBool get_document(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iobject* const object = javascript::cast<k3d::iobject*>(Context, Object);
      *Value = OBJECT_TO_JSVAL(create_document(object->document(), Context));
      return JS_TRUE;
}

/// Wraps iobject::factory()
00730 JSBool get_factory(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iobject* const object = javascript::cast<k3d::iobject*>(Context, Object);
      *Value = OBJECT_TO_JSVAL(create_plugin_factory(object->factory(), Context));
      return JS_TRUE;
}

/// Adds iobject-specific behavior to a "generic" (wraps any interface) JavaScript object
00738 void add_document_object_behavior(JSContext* Context, JSObject* Object)
{
      // Test to see if it's a document object ...
      k3d::iobject* const object = javascript::cast<k3d::iobject*>(Context, Object);
      if(!object)
            return;

      // Define functions ...

      // Define properties ...
      JS_DefineProperty(Context, Object, "document", 0, &get_document, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
      JS_DefineProperty(Context, Object, "factory", 0, &get_factory, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

/// Adds k3d::iapplication_plugin_factory specific behavior to a "generic" (wraps any interface) JavaScript object
00753 void add_application_plugin_factory_behavior(JSContext* Context, JSObject* Object)
{
      k3d::iapplication_plugin_factory* const application_plugin_factory = javascript::cast<k3d::iapplication_plugin_factory*>(Context, Object);
      if(!application_plugin_factory)
            return;

      // Define functions ...
//    JS_DefineFunction(Context, Object, "CreatePlugin", &create_application_plugin, 0, 0);

      // Define properties ...
      JS_DefineProperty(Context, Object, "application_plugin", BOOLEAN_TO_JSVAL(true), 0, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

/// Adds k3d::idocument_plugin_factory specific behavior to a "generic" (wraps any interfa) JavaScript object
00767 void add_document_plugin_factory_behavior(JSContext* Context, JSObject* Object)
{
      k3d::idocument_plugin_factory* const document_plugin_factory = javascript::cast<k3d::idocument_plugin_factory*>(Context, Object);
      if(!document_plugin_factory)
            return;

      // Define functions ...
//    JS_DefineFunction(Context, Object, "CreatePlugin", &create_document_plugin, 1, 0);

      // Define properties ...
      JS_DefineProperty(Context, Object, "document_plugin", BOOLEAN_TO_JSVAL(true), 0, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

JSBool get_plugin_factory_name(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iplugin_factory* const plugin_factory = javascript::cast<k3d::iplugin_factory*>(Context, Object);
      *Value = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, plugin_factory->name().c_str()));
      return JS_TRUE;
}

JSBool get_plugin_factory_short_description(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iplugin_factory* const plugin_factory = javascript::cast<k3d::iplugin_factory*>(Context, Object);
      *Value = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, plugin_factory->short_description().c_str()));
      return JS_TRUE;
}

JSBool get_plugin_factory_default_category(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      k3d::iplugin_factory* const plugin_factory = javascript::cast<k3d::iplugin_factory*>(Context, Object);
      *Value = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, plugin_factory->default_category().c_str()));
      return JS_TRUE;
}

/// Adds k3d::iplugin_factory-specific behavior to a "generic" (wraps any interface) JavaScript object
00802 void add_plugin_factory_behavior(JSContext* Context, JSObject* Object)
{
      // Test to see if it's a plugin factory ...
      k3d::iplugin_factory* const plugin_factory = javascript::cast<k3d::iplugin_factory*>(Context, Object);
      if(!plugin_factory)
            return;

      // Define functions ...

      // Define properties ...
      JS_DefineProperty(Context, Object, "name", 0, &get_plugin_factory_name, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
      JS_DefineProperty(Context, Object, "short_description", 0, &get_plugin_factory_short_description, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
      JS_DefineProperty(Context, Object, "default_category", 0, &get_plugin_factory_default_category, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

/// Wraps k3d::iuser_interface::browser_navigate()
00818 JSBool browser_navigate(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval *Result)
{
      javascript::cast<k3d::iuser_interface*>(Context, Object)->browser_navigate(javascript::string_cast(Context, argv[0]));
      return JS_TRUE;
}

/// Wraps k3d::iuser_interface::message()
00825 JSBool message(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval *Result)
{
      javascript::cast<k3d::iuser_interface*>(Context, Object)->message(javascript::string_cast(Context, argv[0]), javascript::string_cast(Context, argv[1]));
      return JS_TRUE;
}

/// Wraps k3d::iuser_interface::error_message()
00832 JSBool error_message(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval *Result)
{
      javascript::cast<k3d::iuser_interface*>(Context, Object)->error_message(javascript::string_cast(Context, argv[0]), javascript::string_cast(Context, argv[1]));
      return JS_TRUE;
}

/// Wraps k3d::iuser_interface::query_message()
00839 JSBool query_message(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval *Result)
{
      std::vector<std::string> buttons;
      for(uintN i = 2; i < argc; i++)
            buttons.push_back(javascript::string_cast(Context, argv[i]));

      *Result = INT_TO_JSVAL(javascript::cast<k3d::iuser_interface*>(Context, Object)->query_message(javascript::string_cast(Context, argv[0]), javascript::string_cast(Context, argv[1]), 0, buttons));
      return JS_TRUE;
}

/// Wraps k3d::iuser_interface::get_file_path
00850 JSBool get_file_path(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval *Result)
{
      boost::filesystem::path file;
      javascript::cast<k3d::iuser_interface*>(Context, Object)->get_file_path(javascript::string_cast(Context, argv[0]), javascript::string_cast(Context, argv[1]), JSVAL_TO_BOOLEAN(argv[2]), boost::filesystem::path(javascript::string_cast(Context, argv[3]), boost::filesystem::native), file);
      *Result = STRING_TO_JSVAL(JS_NewStringCopyZ(Context, file.native_file_string().c_str()));
      return JS_TRUE;
}

/// Wraps k3d::iuser_interface::show
00859 JSBool show(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval *Result)
{
      if(!JSVAL_IS_OBJECT(argv[0]))
            return JS_FALSE;

      k3d::iunknown* const object = javascript::cast<k3d::iunknown*>(Context, JSVAL_TO_OBJECT(argv[0]));
      if(!object)
            return JS_FALSE;

      *Result = BOOLEAN_TO_JSVAL(javascript::cast<k3d::iuser_interface*>(Context, Object)->show(*object));
      return JS_TRUE;
}

/// Wraps k3d::iuser_interface::show_viewport
00873 JSBool show_viewport(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval *Result)
{
      if(!JSVAL_IS_OBJECT(argv[0]))
            return JS_FALSE;

      k3d::iviewport* const viewport = javascript::cast<k3d::iviewport*>(Context, JSVAL_TO_OBJECT(argv[0]));
      return_val_if_fail(viewport, JS_FALSE);

      *Result = BOOLEAN_TO_JSVAL(javascript::cast<k3d::iuser_interface*>(Context, Object)->show_viewport(*viewport));
      return JS_TRUE;
}

/// Adds k3d::iuser_interface-specific behavior to a "generic" (wraps any interface) JavaScript object
00886 void add_user_interface_behavior(JSContext* Context, JSObject* Object)
{
      // See if this object supports k3d::iuser_interface behavior
      k3d::iuser_interface* const user_interface = javascript::cast<k3d::iuser_interface*>(Context, Object);
      if(!user_interface)
            return;

      // Add methods
      JS_DefineFunction(Context, Object, "BrowserNavigate", &browser_navigate, 1, 0);
      JS_DefineFunction(Context, Object, "Message", &message, 2, 0);
      JS_DefineFunction(Context, Object, "ErrorMessage", &error_message, 2, 0);
      JS_DefineFunction(Context, Object, "QueryMessage", &query_message, 5, 0);
      JS_DefineFunction(Context, Object, "GetFilePath", &get_file_path, 4, 0);
      JS_DefineFunction(Context, Object, "Show", &show, 1, 0);
      JS_DefineFunction(Context, Object, "ShowViewport", &show_viewport, 1, 0);
}

/// Wraps k3d::iobject_collection::CreateObject()
00904 JSBool create_object(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      // Look for object types by name ...
      const std::string name(javascript::string_cast(Context, argv[0]));
      const k3d::iplugin_factory_collection::factories_t factories(k3d::plugins(name));

      // If we got exactly one object type, create an instance of it ...
      if(1 != factories.size())
            {
                  std::cerr << error << __PRETTY_FUNCTION__ << ": couldn't find plugin to match [" << name << "]" << std::endl;
                  return JS_FALSE;
            }

      // Create an object ...
      k3d::iobject* const object = k3d::create_document_plugin(**factories.begin(), *javascript::cast<k3d::idocument*>(Context, Object), "");
      if(!object)
            return JS_TRUE;

      // Return that baby...
      *Result = OBJECT_TO_JSVAL(create_document_object(*object, Context));

      return JS_TRUE;
}

/// Returns an array containing every object in the collection
00929 JSBool get_all_objects(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      // Get the set of all objects in the document ...
      const k3d::iobject_collection::objects_t& objects(javascript::cast<k3d::idocument*>(Context, Object)->objects().collection());

      // convert the set to a Javascript array ...
      std::vector<jsval> values;
      for(k3d::iobject_collection::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
            values.push_back(OBJECT_TO_JSVAL(create_document_object(**object, Context)));

      // Return that baby ...
      if(values.size())
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, values.size(), &values.front()));
      else
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 0, 0));

      return JS_TRUE;
}

/// Returns an object by name
00949 JSBool get_object_by_name(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      // Look for objects by name ...
      const k3d::objects_t objects(k3d::find_objects(javascript::cast<k3d::idocument*>(Context, Object)->objects(), javascript::string_cast(Context, argv[0])));

      // If we found something return it ...
      if(1 != objects.size())
            return JS_TRUE;

      *Result = OBJECT_TO_JSVAL(create_document_object(**objects.begin(), Context));

      return JS_TRUE;
}

/// Wraps k3d::iobject_collection::delete_objects
00964 JSBool delete_object(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      k3d::objects_t objects;

      if(JSVAL_IS_STRING(argv[0]))
            {
                  // Look for our victim by name ...
                  objects = k3d::find_objects(javascript::cast<k3d::idocument*>(Context, Object)->objects(), javascript::string_cast(Context, argv[0]));
            }
      else if(JSVAL_IS_OBJECT(argv[0]))
            {
                  // We got a direct reference to the object ...
                  k3d::iobject* const object = javascript::cast<k3d::iobject*>(Context, JSVAL_TO_OBJECT(argv[0]));
                  if(object)
                        objects.insert(object);
            }

      k3d::delete_objects(*javascript::cast<k3d::idocument*>(Context, Object), objects);
      return JS_TRUE;
}

/// Adds k3d::iobject_collection-specific behavior to a "generic" (wraps any interface) JavaScript object
00986 void add_object_collection_behavior(JSContext* Context, JSObject* Object)
{
      // See if this object supports k3d::iobject_collection
      k3d::idocument* const document = javascript::cast<k3d::idocument*>(Context, Object);
      if(!document)
            return;

      // Add methods
      JS_DefineFunction(Context, Object, "CreateObject", &create_object, 1, 0);
      JS_DefineFunction(Context, Object, "Object", &get_object_by_name, 1, 0);
      JS_DefineFunction(Context, Object, "DeleteObject", &delete_object, 1, 0);

      // Add properties ...
      JS_DefineProperty(Context, Object, "objects", 0, &get_all_objects, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

JSBool set_dependency(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      // Lookup the two properties ...
      return_val_if_fail(JSVAL_IS_OBJECT(argv[0]), JS_FALSE);
      k3d::iproperty* const from = javascript::cast<k3d::iproperty*>(Context, JSVAL_TO_OBJECT(argv[0]));
      return_val_if_fail(from, JS_FALSE);

      return_val_if_fail(JSVAL_IS_OBJECT(argv[1]), JS_FALSE);
      k3d::iproperty* const to = JSVAL_IS_NULL(argv[1]) ? 0 : javascript::cast<k3d::iproperty*>(Context, JSVAL_TO_OBJECT(argv[1]));

      // Ensure that their types match ...
      if(from && to)
            return_val_if_fail(from->type() == to->type(), JS_FALSE);

      // No problemo, so setup the dependency ...
      k3d::idag::dependencies_t dependencies;
      dependencies[from] = to;
      javascript::cast<k3d::idocument*>(Context, Object)->dag().set_dependencies(dependencies);

      return JS_TRUE;
}

/// Adds k3d::idag-specific behavior to a "generic" (wraps any interface) JavaScript object
01025 void add_dag_behavior(JSContext* Context, JSObject* Object)
{
      k3d::idocument* const document = javascript::cast<k3d::idocument*>(Context, Object);
      if(!document)
            return;

      // Add methods ...
      JS_DefineFunction(Context, Object, "SetDependency", &set_dependency, 2, 0);
}

JSBool import_file(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      const boost::filesystem::path filepath(javascript::string_cast(Context, argv[0]), boost::filesystem::native);
      return_val_if_fail(!filepath.empty(), JS_FALSE);

      const std::string formatname(javascript::string_cast(Context, argv[1]));

      k3d::auto_ptr<k3d::igeometry_read_format> filter(
            formatname.size() ?
            k3d::file_filter<k3d::igeometry_read_format>(formatname) :
            k3d::auto_file_filter<k3d::igeometry_read_format>(filepath));
      if(!filter.get())
            {
                  std::cerr << error << "Could not find geometry import plugin [" << formatname << "] for [" << filepath.native_file_string() << "]" << std::endl;
                  return JS_FALSE;
            }

      return k3d::import_file(*javascript::cast<k3d::idocument*>(Context, Object), *filter, filepath) ? JS_TRUE : JS_FALSE;
}

JSBool export_file(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      const boost::filesystem::path filepath(javascript::string_cast(Context, argv[0]), boost::filesystem::native);
      return_val_if_fail(!filepath.empty(), JS_FALSE);

      const std::string formatname(javascript::string_cast(Context, argv[1]));

      k3d::auto_ptr<k3d::igeometry_write_format> filter(
            formatname.size() ?
            k3d::file_filter<k3d::igeometry_write_format>(formatname) :
            k3d::auto_file_filter<k3d::igeometry_write_format>(filepath));
      if(!filter.get())
            {
                  std::cerr << error << "Could not find geometry export plugin [" << formatname << "] for [" << filepath.native_file_string() << "]" << std::endl;
                  return JS_FALSE;
            }

      return k3d::export_file(*javascript::cast<k3d::idocument*>(Context, Object), *filter, filepath) ? JS_TRUE : JS_FALSE;
}

JSBool save(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      const boost::filesystem::path filepath(javascript::string_cast(Context, argv[0]), boost::filesystem::native);
      javascript::cast<k3d::idocument*>(Context, Object)->save(filepath);
      return JS_TRUE;
}

JSBool start_state_change_set(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      k3d::start_state_change_set(*javascript::cast<k3d::idocument*>(Context, Object));
      return JS_TRUE;
}

JSBool finish_state_change_set(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      k3d::finish_state_change_set(*javascript::cast<k3d::idocument*>(Context, Object), javascript::string_cast(Context, argv[0]));
      return JS_TRUE;
}

JSBool redraw_all(JSContext* Context, JSObject* Object, uintN argc, jsval* argv, jsval* Result)
{
      k3d::viewport::redraw_all(*javascript::cast<k3d::idocument*>(Context, Object), JSVAL_TO_BOOLEAN(argv[0]) ? k3d::iviewport::SYNCHRONOUS : k3d::iviewport::ASYNCHRONOUS);
      return JS_TRUE;
}

/// Adds k3d::idocument-specific behavior to a "generic" (wraps any interface) JavaScript object
01101 void add_document_behavior(JSContext* Context, JSObject* Object)
{
      // See if this object supports k3d::idocument
      k3d::idocument* const document = javascript::cast<k3d::idocument*>(Context, Object);
      if(!document)
            return;

      // Add methods
      JS_DefineFunction(Context, Object, "Import", &import_file, 2, 0);
      JS_DefineFunction(Context, Object, "Export", &export_file, 2, 0);
      JS_DefineFunction(Context, Object, "Save", &save, 1, 0);
      JS_DefineFunction(Context, Object, "StartChangeSet", &start_state_change_set, 0, 0);
      JS_DefineFunction(Context, Object, "FinishChangeSet", &finish_state_change_set, 1, 0);
      JS_DefineFunction(Context, Object, "RedrawAll", &redraw_all, 1, 0);
}

JSBool get_user_interface(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      if(k3d::application().user_interface())
            *Value = OBJECT_TO_JSVAL(create_user_interface(*k3d::application().user_interface(), Context));

      return JS_TRUE;
}

JSBool get_documents(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      // convert the set of open documents to a Javascript array ...
      std::vector<jsval> values;
      const k3d::iapplication::document_collection_t documents(k3d::application().documents());
      for(k3d::iapplication::document_collection_t::const_iterator document = documents.begin(); document != documents.end(); ++document)
            values.push_back(OBJECT_TO_JSVAL(create_document(**document, Context)));

      // Return that baby ...
      if(values.size())
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, values.size(), &values.front()));
      else
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 0, 0));

      return JS_TRUE;
}

JSBool new_document(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      k3d::idocument* const document = k3d::application().create_document();
      if(document)
            *Result = OBJECT_TO_JSVAL(create_document(*document, Context));

      return JS_TRUE;
}

JSBool open_document(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      k3d::idocument* const document = k3d::application().open_document(boost::filesystem::path(javascript::string_cast(Context, argv[0]), boost::filesystem::native));
      if(document)
            *Result = OBJECT_TO_JSVAL(create_document(*document, Context));

      return JS_TRUE;
}

01160 JSBool close_document(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
/** \todo Implement this! */
/*
      if(!JSVAL_IS_OBJECT(argv[0]))
            return JS_FALSE;

      k3d::idocument* const document = CJavaScriptDocument::Interface(Context, JSVAL_TO_OBJECT(argv[0]));
      if(!document)
            return JS_FALSE;

      k3d::application().CloseDocument(*document);
*/
      return JS_TRUE;
}

JSBool command_node(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      const std::string nodepath = javascript::string_cast(Context, argv[0]);
      if(0 == nodepath.size())
            {
                  JS_ReportError(Context, "Empty command node path");
                  return JS_FALSE;
            }

      k3d::icommand_node* const node = k3d::get_command_node(nodepath);
      if(0 == node)
            {
                  // Instead of returning a failure, we return success with a NULL object - this allows scripts
                  // to test for nodes at runtime, e.g. testing to see if a particular window is open.
                  return JS_TRUE;
            }

      *Result = OBJECT_TO_JSVAL(create_generic_object(*node, Context));
      return JS_TRUE;
}

/// Throws a C++ exception, for use in troubleshooting the application exception-handling - developers only!
01198 JSBool throw_exception(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      // You asked for it!
      throw std::runtime_error(javascript::string_cast(Context, argv[0]));

      return JS_TRUE;
}

/// Returns true iff it's safe to exit the application
01207 JSBool safe_to_close_application(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      *Result = BOOLEAN_TO_JSVAL(k3d::application().safe_to_close_signal().emit());
      return JS_TRUE;
}

/// Requests application exit
01214 JSBool exit_application(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      *Result = BOOLEAN_TO_JSVAL(k3d::application().exit());
      return JS_TRUE;
}

/// Returns an array containing every plugin factory in the collection
01221 JSBool get_plugin_factories(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      // Get the set of all factories in the application ...
      const k3d::iplugin_factory_collection::factories_t& factories(javascript::cast<k3d::iapplication*>(Context, Object)->plugins());

      // convert the set to a Javascript array ...
      std::vector<jsval> values;
      for(k3d::iplugin_factory_collection::factories_t::const_iterator factory = factories.begin(); factory != factories.end(); ++factory)
            values.push_back(OBJECT_TO_JSVAL(create_plugin_factory(**factory, Context)));

      // Return that baby ...
      if(values.size())
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, values.size(), &values.front()));
      else
            *Value = OBJECT_TO_JSVAL(JS_NewArrayObject(Context, 0, 0));

      return JS_TRUE;
}

/// Adds k3d::iapplication-specific behavior to a "generic" (wraps any interface) JavaScript object
01241 void add_application_behavior(JSContext* Context, JSObject* Object)
{
      // See if this object supports k3d::iapplication
      k3d::iapplication* const application = javascript::cast<k3d::iapplication*>(Context, Object);
      if(!application)
            return;

      // Add methods
      JS_DefineFunction(Context, Object, "NewDocument", &new_document, 0, 0);
      JS_DefineFunction(Context, Object, "OpenDocument", &open_document, 1, 0);
      JS_DefineFunction(Context, Object, "CloseDocument", &close_document, 1, 0);
      JS_DefineFunction(Context, Object, "CommandNode", &command_node, 1, 0);
      JS_DefineFunction(Context, Object, "ThrowException", &throw_exception, 1, 0);
      JS_DefineFunction(Context, Object, "SafeToClose", &safe_to_close_application, 0, 0);
      JS_DefineFunction(Context, Object, "Exit", &exit_application, 0, 0);

      // Add properties
      JS_DefineProperty(Context, Object, "ui", 0, &get_user_interface, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
      JS_DefineProperty(Context, Object, "plugin_factories", 0, &get_plugin_factories, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
      JS_DefineProperty(Context, Object, "documents", 0, &get_documents, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
}

JSBool get_selected(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      *Value = BOOLEAN_TO_JSVAL(javascript::cast<k3d::iselectable*>(Context, Object)->is_selected());
      return JS_TRUE;
}

JSBool set_selected(JSContext* Context, JSObject* Object, jsval ID, jsval* Value)
{
      if(JSVAL_TO_BOOLEAN(*Value))
            {
                  javascript::cast<k3d::iselectable*>(Context, Object)->select();
            }
      else
            {
                  javascript::cast<k3d::iselectable*>(Context, Object)->deselect();
            }

      return JS_TRUE;
}

/// Adds k3d::iselectable behavior to a "generic" (wraps any interface) JavaScript object
01284 void add_selectable_behavior(JSContext* Context, JSObject* Object)
{
      // See if this object supports k3d::iselectable
      k3d::iselectable* const selectable = javascript::cast<k3d::iselectable*>(Context, Object);
      if(!selectable)
            return;

      JS_DefineProperty(Context, Object, "selected", 0, &get_selected, &set_selected, JSPROP_ENUMERATE | JSPROP_PERMANENT);
}

JSBool play_script_file(JSContext* Context, JSObject* Object, uintN argc, jsval *argv, jsval* Result)
{
      // Get the file path ...
      const std::string filepath(javascript::string_cast(Context, argv[0]));
      return_val_if_fail(filepath.size(), JS_FALSE);

      // Setup our stream ...
      std::ifstream file(filepath.c_str());

      bool recognized = false;
      bool executed = false;

      // Setup (optional) document and object contexts ...
      k3d::iobject* const document_object = javascript::cast<k3d::iobject*>(Context, Object);
      k3d::idocument* const document = document_object ? &document_object->document() : javascript::cast<k3d::idocument*>(Context, Object);

      k3d::iscript_engine::context_t context;
      if(document)
            context.push_back(document);
      if(document_object)
            context.push_back(document_object);

      k3d::execute_script(file, filepath, context, recognized, executed);
      *Result = BOOLEAN_TO_JSVAL(recognized && executed);
      return JS_TRUE;
}

/// Adds scripted behavior to a "generic" (wraps any interface) JavaScript object
01322 void add_scripted_behavior(JSContext* Context, JSObject* Object)
{
      // Add methods
      JS_DefineFunction(Context, Object, "PlayScriptFile", &play_script_file, 1, 0);
}

/// Creates a generic (wraps any interface) JavaScript object
01329 JSObject* raw_create_generic_object(k3d::iunknown& Object, JSContext* Context)
{
      // Create the generic object instance ...
      JSObject* const object = JS_NewObject(Context, &javascript::generic_object_class, 0, 0);

      // Set the wrapped-object's interface ...
      JS_SetPrivate(Context, object, &Object);

      return object;
}

JSObject* create_application(k3d::iunknown& Object, JSContext* Context)
{
      JSObject* const object = raw_create_generic_object(Object, Context);

      // Add behavior to the object
      add_application_behavior(Context, object);
      add_command_node_behavior(Context, object);
      add_property_collection_behavior(Context, object);
      add_scripted_behavior(Context, object);

      return object;
}

JSObject* create_document(k3d::iunknown& Object, JSContext* Context)
{
      JSObject* const object = raw_create_generic_object(Object, Context);

      // Add behavior to the object
      add_command_node_behavior(Context, object);
      add_dag_behavior(Context, object);
      add_document_behavior(Context, object);
      add_object_collection_behavior(Context, object);
      add_property_collection_behavior(Context, object);
      add_scripted_behavior(Context, object);

      return object;
}

JSObject* create_plugin_factory(k3d::iunknown& Object, JSContext* Context)
{
      JSObject* const object = raw_create_generic_object(Object, Context);

      // Add behavior to the object
      add_application_plugin_factory_behavior(Context, object);
      add_document_plugin_factory_behavior(Context, object);
      add_plugin_factory_behavior(Context, object);

      return object;
}

JSObject* create_property(k3d::iunknown& Object, JSContext* Context)
{
      JSObject* const object = raw_create_generic_object(Object, Context);

      // Add behavior to the object
      add_property_behavior(Context, object);

      return object;
}

JSObject* create_document_object(k3d::iunknown& Object, JSContext* Context)
{
      JSObject* const object = raw_create_generic_object(Object, Context);

      // Add behavior to the object
      add_bezier_channel_behavior(Context, object);
      add_command_node_behavior(Context, object);
      add_document_object_behavior(Context, object);
      add_property_collection_behavior(Context, object);
      add_still_render_engine_behavior(Context, object);
      add_animation_render_engine_behavior(Context, object);
      add_scripted_behavior(Context, object);
      add_selectable_behavior(Context, object);
      add_viewport_behavior(Context, object);

      return object;
}

JSObject* create_generic_object(k3d::iunknown& Object, JSContext* Context)
{
      JSObject* const object = raw_create_generic_object(Object, Context);

      // Add behavior to the object
      add_application_behavior(Context, object);
      add_application_plugin_factory_behavior(Context, object);
      add_bezier_channel_behavior(Context, object);
      add_command_node_behavior(Context, object);
      add_dag_behavior(Context, object);
      add_document_behavior(Context, object);
      add_document_object_behavior(Context, object);
      add_document_plugin_factory_behavior(Context, object);
      add_object_collection_behavior(Context, object);
      add_plugin_factory_behavior(Context, object);
      add_property_behavior(Context, object);
      add_property_collection_behavior(Context, object);
      add_still_render_engine_behavior(Context, object);
      add_animation_render_engine_behavior(Context, object);
      add_scripted_behavior(Context, object);
      add_selectable_behavior(Context, object);
      add_user_interface_behavior(Context, object);
      add_viewport_behavior(Context, object);

      return object;
}

JSObject* create_user_interface(k3d::iunknown& Object, JSContext* Context)
{
      JSObject* const object = raw_create_generic_object(Object, Context);

      // Add behavior to the object
      add_command_node_behavior(Context, object);
      add_property_collection_behavior(Context, object);
      add_user_interface_behavior(Context, object);

      return object;
}

} // namespace libk3djavascript



Generated by  Doxygen 1.6.0   Back to index