fix bug with sizeof when type of the arrays differ (#298)

Signed-off-by: William Woodall <c824fe0afe16857dd6f587aa7c4044d2642d60fb@osrfoundation.org>

// generated from ros1_bridge/resource/interface_factories.cpp.em

@###############################################
@#
@# Factory template specializations for a
@# specific message type
@#
@# EmPy template for generating
@# <pkgname>__<type>__<interfacename>__factories.cpp
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - ros2_package_name (str)
@#    The ROS 2 package name of this file
@#  - mappings (list of ros1_bridge.Mapping)
@#    Mapping between messages as well as their fields
@###############################################
@
@{
from ros1_bridge import camel_case_to_lower_case_underscore
from rosidl_parser.definition import AbstractNestedType
from rosidl_parser.definition import AbstractSequence
from rosidl_parser.definition import BoundedSequence
from rosidl_parser.definition import NamespacedType
}@
#include "@(ros2_package_name)_factories.hpp"

#include <algorithm>

#include "rclcpp/rclcpp.hpp"

// include builtin interfaces
#include <ros1_bridge/convert_builtin_interfaces.hpp>

// include ROS 1 services
@[for service in mapped_services]@
#include <@(service["ros1_package"])/@(service["ros1_name"]).h>
@[end for]@

// include ROS 2 services
@[for service in mapped_services]@
#include <@(service["ros2_package"])/srv/@(camel_case_to_lower_case_underscore(service["ros2_name"])).hpp>
@[end for]@

namespace ros1_bridge
{

std::shared_ptr<FactoryInterface>
get_factory_@(ros2_package_name)__@(interface_type)__@(interface.message_name)(const std::string & ros1_type_name, const std::string & ros2_type_name)
{
@[if not mapped_msgs]@
  (void)ros1_type_name;
  (void)ros2_type_name;
@[else]@
  // mapping from string to specialized template
@[end if]@
@[for m in mapped_msgs]@
  if (
    (ros1_type_name == "@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)" ||
     ros1_type_name == "") &&
    ros2_type_name == "@(m.ros2_msg.package_name)/msg/@(m.ros2_msg.message_name)")
  {
    return std::make_shared<
      Factory<
        @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name),
        @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name)
      >
    >("@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)", ros2_type_name);
  }
@[end for]@
  return std::shared_ptr<FactoryInterface>();
}

std::unique_ptr<ServiceFactoryInterface>
get_service_factory_@(ros2_package_name)__@(interface_type)__@(interface.message_name)(const std::string & ros_id, const std::string & package_name, const std::string & service_name)
{
@[if not mapped_services]@
  (void)ros_id;
  (void)package_name;
  (void)service_name;
@[end if]@
@[for service in mapped_services]@
  if (
    (
      ros_id == "ros1" &&
      package_name == "@(service["ros1_package"])" &&
      service_name == "@(service["ros1_name"])"
    ) || (
      ros_id == "ros2" &&
      package_name == "@(service["ros2_package"])" &&
      service_name == "srv/@(service["ros2_name"])"
    )
  ) {
    return std::unique_ptr<ServiceFactoryInterface>(new ServiceFactory<
      @(service["ros1_package"])::@(service["ros1_name"]),
      @(service["ros2_package"])::srv::@(service["ros2_name"])
    >);
  }
@[end for]@
  return nullptr;
}
@
// conversion functions for available interfaces
@[for m in mapped_msgs]@

template<>
void
Factory<
  @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name),
  @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name)
>::convert_1_to_2(
  const @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg,
  @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg)
{
@[  if not m.fields_1_to_2]@
  (void)ros1_msg;
  (void)ros2_msg;
@[  end if]@
@[  for ros1_fields, ros2_fields in m.fields_1_to_2.items()]@
@{
ros1_field_selection = '.'.join((str(field.name) for field in ros1_fields))
ros2_field_selection = '.'.join((str(field.name) for field in ros2_fields))

if isinstance(ros2_fields[-1].type, NamespacedType):
    namespaces = ros2_fields[-1].type.namespaces
    assert len(namespaces) == 2 and namespaces[1] == 'msg', \
      "messages not using the '<pkg_name>, msg, <type_name>' triplet are not supported"
}
@[    if not isinstance(ros2_fields[-1].type, AbstractNestedType)]@
  // convert non-array field
@[      if not isinstance(ros2_fields[-1].type, NamespacedType)]@
  // convert primitive field
  ros2_msg.@(ros2_field_selection) = ros1_msg.@(ros1_field_selection);
@[      elif ros2_fields[-1].type.namespaces[0] == 'builtin_interfaces']@
  // convert builtin field
  ros1_bridge::convert_1_to_2(ros1_msg.@(ros1_field_selection), ros2_msg.@(ros2_field_selection));
@[      else]@
  // convert sub message field
  Factory<
    @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
    @(ros2_fields[-1].type.namespaces[0])::msg::@(ros2_fields[-1].type.name)
  >::convert_1_to_2(
    ros1_msg.@(ros1_field_selection), ros2_msg.@(ros2_field_selection));
@[      end if]@
@[    else]@
  // convert array or sequence field
@[      if isinstance(ros2_fields[-1].type, AbstractSequence)]@
  // dynamically sized sequence, ensure destination sequence/vector size is large enough
@[        if isinstance(ros2_fields[-1].type, BoundedSequence)]@
  // bounded size sequence, check that the ros 1 vector size is not larger than the upper bound for the target
  assert(ros1_msg.@(ros1_field_selection).size() <= @(ros2_fields[-1].type.maximum_size));
@[        end if]@
  // resize ros2 field to match the ros1 field
  ros2_msg.@(ros2_field_selection).resize(ros1_msg.@(ros1_field_selection).size());
@[      else]@
  // statically sized array
  static_assert(
    (ros2_msg.@(ros2_field_selection).size()) >= (ros1_msg.@(ros1_field_selection).size()),
    "destination array not large enough for source array"
  );
@[      end if]@
@[      if not isinstance(ros2_fields[-1].type.value_type, NamespacedType)]@
  // convert primitive array elements
  std::copy(
    ros1_msg.@(ros1_field_selection).begin(),
    ros1_msg.@(ros1_field_selection).end(),
    ros2_msg.@(ros2_field_selection).begin());
@[      else]@
  // copy element wise since the type is different
  {
    auto ros1_it = ros1_msg.@(ros1_field_selection).cbegin();
    auto ros2_it = ros2_msg.@(ros2_field_selection).begin();
    for (
      ;
      ros1_it != ros1_msg.@(ros1_field_selection).cend() &&
      ros2_it != ros2_msg.@(ros2_field_selection).end();
      ++ros1_it, ++ros2_it
    )
    {
      // convert sub message element
@[        if ros2_fields[-1].type.value_type.namespaces[0] == 'builtin_interfaces']@
      ros1_bridge::convert_1_to_2(*ros1_it, *ros2_it);
@[        else]@
      Factory<
        @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
        @(ros2_fields[-1].type.value_type.namespaces[0])::msg::@(ros2_fields[-1].type.value_type.name)
      >::convert_1_to_2(
        *ros1_it, *ros2_it);
@[        end if]@
    }
  }
@[      end if]@
@[    end if]@
@[  end for]@
}

template<>
void
Factory<
  @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name),
  @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name)
>::convert_2_to_1(
  const @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg,
  @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg)
{
@[  if not m.fields_2_to_1]@
  (void)ros2_msg;
  (void)ros1_msg;
@[  end if]@
@[  for ros2_fields, ros1_fields in m.fields_2_to_1.items()]@
@{
ros2_field_selection = '.'.join((str(field.name) for field in ros2_fields))
ros1_field_selection = '.'.join((str(field.name) for field in ros1_fields))

if isinstance(ros2_fields[-1].type, NamespacedType):
    namespaces = ros2_fields[-1].type.namespaces
    assert len(namespaces) == 2 and namespaces[1] == 'msg', \
      "messages not using the '<pkg_name>, msg, <type_name>' triplet are not supported"
}
@[    if not isinstance(ros2_fields[-1].type, AbstractNestedType)]@
  // convert non-array field
@[      if not isinstance(ros2_fields[-1].type, NamespacedType)]@
  // convert primitive field
  ros1_msg.@(ros1_field_selection) = ros2_msg.@(ros2_field_selection);
@[      elif ros2_fields[-1].type.namespaces[0] == 'builtin_interfaces']@
  // convert builtin field
  ros1_bridge::convert_2_to_1(ros2_msg.@(ros2_field_selection), ros1_msg.@(ros1_field_selection));
@[      else]@
  // convert sub message field
  Factory<
    @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
    @(ros2_fields[-1].type.namespaces[0])::msg::@(ros2_fields[-1].type.name)
  >::convert_2_to_1(
    ros2_msg.@(ros2_field_selection), ros1_msg.@(ros1_field_selection));
@[      end if]@
@[    else]@
  // convert array or sequence field
@[      if isinstance(ros2_fields[-1].type, AbstractSequence)]@
  // dynamically sized sequence, ensure destination vector size is large enough
  // resize ros1 field to match the ros2 field
  ros1_msg.@(ros1_field_selection).resize(ros2_msg.@(ros2_field_selection).size());
@[      else]@
  // statically sized array
  static_assert(
    (ros1_msg.@(ros1_field_selection).size()) >= (ros2_msg.@(ros2_field_selection).size()),
    "destination array not large enough for source array"
  );
@[      end if]@
@[      if not isinstance(ros2_fields[-1].type.value_type, NamespacedType)]@
  // convert primitive array elements
  std::copy(
    ros2_msg.@(ros2_field_selection).begin(),
    ros2_msg.@(ros2_field_selection).end(),
    ros1_msg.@(ros1_field_selection).begin());
@[      else]@
  // copy element wise since the type is different
  {
    auto ros2_it = ros2_msg.@(ros2_field_selection).cbegin();
    auto ros1_it = ros1_msg.@(ros1_field_selection).begin();
    for (
      ;
      ros2_it != ros2_msg.@(ros2_field_selection).cend() &&
      ros1_it != ros1_msg.@(ros1_field_selection).end();
      ++ros2_it, ++ros1_it
    )
    {
      // convert sub message element
@[        if ros2_fields[-1].type.value_type.namespaces[0] == 'builtin_interfaces']@
      ros1_bridge::convert_2_to_1(*ros2_it, *ros1_it);
@[        else]@
      Factory<
        @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
        @(ros2_fields[-1].type.value_type.namespaces[0])::msg::@(ros2_fields[-1].type.value_type.name)
      >::convert_2_to_1(
        *ros2_it, *ros1_it);
@[        end if]@
    }
  }
@[      end if]@
@[    end if]@
@[  end for]@
}
@[end for]@
@
@[for service in mapped_services]@

@[  for frm, to in [("1", "2"), ("2", "1")]]@
@[    for type in ["Request", "Response"]]@
template <>
void ServiceFactory<
  @(service["ros1_package"])::@(service["ros1_name"]),
  @(service["ros2_package"])::srv::@(service["ros2_name"])
>::translate_@(frm)_to_@(to)(
@[      if frm == "1"]@
  const @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1,
  @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2
@[      else]@
  const @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2,
  @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1
@[      end if]@
) {
@[      for field in service["fields"][type.lower()]]@
@[        if field["array"]]@
  req@(to).@(field["ros" + to]["name"]).resize(req@(frm).@(field["ros" + frm]["name"]).size());
  auto @(field["ros1"]["name"])1_it = req1.@(field["ros1"]["name"]).begin();
  auto @(field["ros2"]["name"])2_it = req2.@(field["ros2"]["name"]).begin();
  while (
    @(field["ros1"]["name"])1_it != req1.@(field["ros1"]["name"]).end() &&
    @(field["ros2"]["name"])2_it != req2.@(field["ros2"]["name"]).end()
  ) {
    auto & @(field["ros1"]["name"])1 = *(@(field["ros1"]["name"])1_it++);
    auto & @(field["ros2"]["name"])2 = *(@(field["ros2"]["name"])2_it++);
@[      else]@
  auto & @(field["ros1"]["name"])1 = req1.@(field["ros1"]["name"]);
  auto & @(field["ros2"]["name"])2 = req2.@(field["ros2"]["name"]);
@[        end if]@
@[        if field["basic"]]@
  @(field["ros2"]["name"])@(to) = @(field["ros1"]["name"])@(frm);
@[        else]@
  Factory<@(field["ros1"]["cpptype"]),@(field["ros2"]["cpptype"])>::convert_@(frm)_to_@(to)(@
@(field["ros2"]["name"])@(frm), @(field["ros1"]["name"])@(to));
@[        end if]@
@[        if field["array"]]@
  }
@[        end if]@
@[      end for]@
}

@[    end for]@
@[  end for]@
@[end for]@
}  // namespace ros1_bridge

// generated from ros1_bridge/resource/interface_factories.cpp.em

@###############################################
@#
@# Factory template specializations for a
@# specific message type
@#
@# EmPy template for generating
@# <pkgname>__<type>__<interfacename>__factories.cpp
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - ros2_package_name (str)
@#    The ROS 2 package name of this file
@#  - mappings (list of ros1_bridge.Mapping)
@#    Mapping between messages as well as their fields
@###############################################
@
@{
from ros1_bridge import camel_case_to_lower_case_underscore
from rosidl_parser.definition import AbstractNestedType
from rosidl_parser.definition import AbstractSequence
from rosidl_parser.definition import BoundedSequence
from rosidl_parser.definition import NamespacedType
}@
#include "@(ros2_package_name)_factories.hpp"

#include <algorithm>

#include "rclcpp/rclcpp.hpp"

// include builtin interfaces
#include <ros1_bridge/convert_builtin_interfaces.hpp>

// include ROS 1 services
@[for service in mapped_services]@
#include <@(service["ros1_package"])/@(service["ros1_name"]).h>
@[end for]@

// include ROS 2 services
@[for service in mapped_services]@
#include <@(service["ros2_package"])/srv/@(camel_case_to_lower_case_underscore(service["ros2_name"])).hpp>
@[end for]@

namespace ros1_bridge
{

std::shared_ptr<FactoryInterface>
get_factory_@(ros2_package_name)__@(interface_type)__@(interface.message_name)(const std::string & ros1_type_name, const std::string & ros2_type_name)
{
@[if not mapped_msgs]@
  (void)ros1_type_name;
  (void)ros2_type_name;
@[else]@
  // mapping from string to specialized template
@[end if]@
@[for m in mapped_msgs]@
  if (
    (ros1_type_name == "@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)" ||
     ros1_type_name == "") &&
    ros2_type_name == "@(m.ros2_msg.package_name)/msg/@(m.ros2_msg.message_name)")
  {
    return std::make_shared<
      Factory<
        @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name),
        @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name)
      >
    >("@(m.ros1_msg.package_name)/@(m.ros1_msg.message_name)", ros2_type_name);
  }
@[end for]@
  return std::shared_ptr<FactoryInterface>();
}

std::unique_ptr<ServiceFactoryInterface>
get_service_factory_@(ros2_package_name)__@(interface_type)__@(interface.message_name)(const std::string & ros_id, const std::string & package_name, const std::string & service_name)
{
@[if not mapped_services]@
  (void)ros_id;
  (void)package_name;
  (void)service_name;
@[end if]@
@[for service in mapped_services]@
  if (
    (
      ros_id == "ros1" &&
      package_name == "@(service["ros1_package"])" &&
      service_name == "@(service["ros1_name"])"
    ) || (
      ros_id == "ros2" &&
      package_name == "@(service["ros2_package"])" &&
      service_name == "srv/@(service["ros2_name"])"
    )
  ) {
    return std::unique_ptr<ServiceFactoryInterface>(new ServiceFactory<
      @(service["ros1_package"])::@(service["ros1_name"]),
      @(service["ros2_package"])::srv::@(service["ros2_name"])
    >);
  }
@[end for]@
  return nullptr;
}
@
// conversion functions for available interfaces
@[for m in mapped_msgs]@

template<>
void
Factory<
  @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name),
  @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name)
>::convert_1_to_2(
  const @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg,
  @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg)
{
@[  if not m.fields_1_to_2]@
  (void)ros1_msg;
  (void)ros2_msg;
@[  end if]@
@[  for ros1_fields, ros2_fields in m.fields_1_to_2.items()]@
@{
ros1_field_selection = '.'.join((str(field.name) for field in ros1_fields))
ros2_field_selection = '.'.join((str(field.name) for field in ros2_fields))

if isinstance(ros2_fields[-1].type, NamespacedType):
    namespaces = ros2_fields[-1].type.namespaces
    assert len(namespaces) == 2 and namespaces[1] == 'msg', \
      "messages not using the '<pkg_name>, msg, <type_name>' triplet are not supported"
}
@[    if not isinstance(ros2_fields[-1].type, AbstractNestedType)]@
  // convert non-array field
@[      if not isinstance(ros2_fields[-1].type, NamespacedType)]@
  // convert primitive field
  ros2_msg.@(ros2_field_selection) = ros1_msg.@(ros1_field_selection);
@[      elif ros2_fields[-1].type.namespaces[0] == 'builtin_interfaces']@
  // convert builtin field
  ros1_bridge::convert_1_to_2(ros1_msg.@(ros1_field_selection), ros2_msg.@(ros2_field_selection));
@[      else]@
  // convert sub message field
  Factory<
    @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
    @(ros2_fields[-1].type.namespaces[0])::msg::@(ros2_fields[-1].type.name)
  >::convert_1_to_2(
    ros1_msg.@(ros1_field_selection), ros2_msg.@(ros2_field_selection));
@[      end if]@
@[    else]@
  // convert array or sequence field
@[      if isinstance(ros2_fields[-1].type, AbstractSequence)]@
  // dynamically sized sequence, ensure destination sequence/vector size is large enough
@[        if isinstance(ros2_fields[-1].type, BoundedSequence)]@
  // bounded size sequence, check that the ros 1 vector size is not larger than the upper bound for the target
  assert(ros1_msg.@(ros1_field_selection).size() <= @(ros2_fields[-1].type.maximum_size));
@[        end if]@
  // resize ros2 field to match the ros1 field
  ros2_msg.@(ros2_field_selection).resize(ros1_msg.@(ros1_field_selection).size());
@[      else]@
  // statically sized array
  static_assert(
    sizeof(ros2_msg.@(ros2_field_selection)) >= sizeof(ros1_msg.@(ros1_field_selection)),
    "destination array not large enough for source array"
  );
@[      end if]@
@[      if not isinstance(ros2_fields[-1].type.value_type, NamespacedType)]@
  // convert primitive array elements
  std::copy(
    ros1_msg.@(ros1_field_selection).begin(),
    ros1_msg.@(ros1_field_selection).end(),
    ros2_msg.@(ros2_field_selection).begin());
@[      else]@
  // copy element wise since the type is different
  {
    auto ros1_it = ros1_msg.@(ros1_field_selection).cbegin();
    auto ros2_it = ros2_msg.@(ros2_field_selection).begin();
    for (
      ;
      ros1_it != ros1_msg.@(ros1_field_selection).cend() &&
      ros2_it != ros2_msg.@(ros2_field_selection).end();
      ++ros1_it, ++ros2_it
    )
    {
      // convert sub message element
@[        if ros2_fields[-1].type.value_type.namespaces[0] == 'builtin_interfaces']@
      ros1_bridge::convert_1_to_2(*ros1_it, *ros2_it);
@[        else]@
      Factory<
        @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
        @(ros2_fields[-1].type.value_type.namespaces[0])::msg::@(ros2_fields[-1].type.value_type.name)
      >::convert_1_to_2(
        *ros1_it, *ros2_it);
@[        end if]@
    }
  }
@[      end if]@
@[    end if]@
@[  end for]@
}

template<>
void
Factory<
  @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name),
  @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name)
>::convert_2_to_1(
  const @(m.ros2_msg.package_name)::msg::@(m.ros2_msg.message_name) & ros2_msg,
  @(m.ros1_msg.package_name)::@(m.ros1_msg.message_name) & ros1_msg)
{
@[  if not m.fields_2_to_1]@
  (void)ros2_msg;
  (void)ros1_msg;
@[  end if]@
@[  for ros2_fields, ros1_fields in m.fields_2_to_1.items()]@
@{
ros2_field_selection = '.'.join((str(field.name) for field in ros2_fields))
ros1_field_selection = '.'.join((str(field.name) for field in ros1_fields))

if isinstance(ros2_fields[-1].type, NamespacedType):
    namespaces = ros2_fields[-1].type.namespaces
    assert len(namespaces) == 2 and namespaces[1] == 'msg', \
      "messages not using the '<pkg_name>, msg, <type_name>' triplet are not supported"
}
@[    if not isinstance(ros2_fields[-1].type, AbstractNestedType)]@
  // convert non-array field
@[      if not isinstance(ros2_fields[-1].type, NamespacedType)]@
  // convert primitive field
  ros1_msg.@(ros1_field_selection) = ros2_msg.@(ros2_field_selection);
@[      elif ros2_fields[-1].type.namespaces[0] == 'builtin_interfaces']@
  // convert builtin field
  ros1_bridge::convert_2_to_1(ros2_msg.@(ros2_field_selection), ros1_msg.@(ros1_field_selection));
@[      else]@
  // convert sub message field
  Factory<
    @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
    @(ros2_fields[-1].type.namespaces[0])::msg::@(ros2_fields[-1].type.name)
  >::convert_2_to_1(
    ros2_msg.@(ros2_field_selection), ros1_msg.@(ros1_field_selection));
@[      end if]@
@[    else]@
  // convert array or sequence field
@[      if isinstance(ros2_fields[-1].type, AbstractSequence)]@
  // dynamically sized sequence, ensure destination vector size is large enough
  // resize ros1 field to match the ros2 field
  ros1_msg.@(ros1_field_selection).resize(ros2_msg.@(ros2_field_selection).size());
@[      else]@
  // statically sized array
  static_assert(
    sizeof(ros1_msg.@(ros1_field_selection)) >= sizeof(ros2_msg.@(ros2_field_selection)),
    "destination array not large enough for source array"
  );
@[      end if]@
@[      if not isinstance(ros2_fields[-1].type.value_type, NamespacedType)]@
  // convert primitive array elements
  std::copy(
    ros2_msg.@(ros2_field_selection).begin(),
    ros2_msg.@(ros2_field_selection).end(),
    ros1_msg.@(ros1_field_selection).begin());
@[      else]@
  // copy element wise since the type is different
  {
    auto ros2_it = ros2_msg.@(ros2_field_selection).cbegin();
    auto ros1_it = ros1_msg.@(ros1_field_selection).begin();
    for (
      ;
      ros2_it != ros2_msg.@(ros2_field_selection).cend() &&
      ros1_it != ros1_msg.@(ros1_field_selection).end();
      ++ros2_it, ++ros1_it
    )
    {
      // convert sub message element
@[        if ros2_fields[-1].type.value_type.namespaces[0] == 'builtin_interfaces']@
      ros1_bridge::convert_2_to_1(*ros2_it, *ros1_it);
@[        else]@
      Factory<
        @(ros1_fields[-1].pkg_name)::@(ros1_fields[-1].msg_name),
        @(ros2_fields[-1].type.value_type.namespaces[0])::msg::@(ros2_fields[-1].type.value_type.name)
      >::convert_2_to_1(
        *ros2_it, *ros1_it);
@[        end if]@
    }
  }
@[      end if]@
@[    end if]@
@[  end for]@
}
@[end for]@
@
@[for service in mapped_services]@

@[  for frm, to in [("1", "2"), ("2", "1")]]@
@[    for type in ["Request", "Response"]]@
template <>
void ServiceFactory<
  @(service["ros1_package"])::@(service["ros1_name"]),
  @(service["ros2_package"])::srv::@(service["ros2_name"])
>::translate_@(frm)_to_@(to)(
@[      if frm == "1"]@
  const @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1,
  @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2
@[      else]@
  const @(service["ros2_package"])::srv::@(service["ros2_name"])::@(type)& req2,
  @(service["ros1_package"])::@(service["ros1_name"])::@(type)& req1
@[      end if]@
) {
@[      for field in service["fields"][type.lower()]]@
@[        if field["array"]]@
  req@(to).@(field["ros" + to]["name"]).resize(req@(frm).@(field["ros" + frm]["name"]).size());
  auto @(field["ros1"]["name"])1_it = req1.@(field["ros1"]["name"]).begin();
  auto @(field["ros2"]["name"])2_it = req2.@(field["ros2"]["name"]).begin();
  while (
    @(field["ros1"]["name"])1_it != req1.@(field["ros1"]["name"]).end() &&
    @(field["ros2"]["name"])2_it != req2.@(field["ros2"]["name"]).end()
  ) {
    auto & @(field["ros1"]["name"])1 = *(@(field["ros1"]["name"])1_it++);
    auto & @(field["ros2"]["name"])2 = *(@(field["ros2"]["name"])2_it++);
@[      else]@
  auto & @(field["ros1"]["name"])1 = req1.@(field["ros1"]["name"]);
  auto & @(field["ros2"]["name"])2 = req2.@(field["ros2"]["name"]);
@[        end if]@
@[        if field["basic"]]@
  @(field["ros2"]["name"])@(to) = @(field["ros1"]["name"])@(frm);
@[        else]@
  Factory<@(field["ros1"]["cpptype"]),@(field["ros2"]["cpptype"])>::convert_@(frm)_to_@(to)(@
@(field["ros2"]["name"])@(frm), @(field["ros1"]["name"])@(to));
@[        end if]@
@[        if field["array"]]@
  }
@[        end if]@
@[      end for]@
}

@[    end for]@
@[  end for]@
@[end for]@
}  // namespace ros1_bridge


fix the case where catkin is used from installed

@{
import yaml, os, sys, glob, em

class Pkg:
    def __init__(self, **kwds):
        self.__dict__.update(path=None, genlang=None, depends=None)
        self.__dict__.update(kwds)
    def __repr__(self):
        return "path=%s depends=%s genlang=%s\n" % (self.path, self.depends, self.genlang)

pkgs = {}

PKG_NAME_FIELD = "Catkin-ProjectName"
PKG_DEP_FIELD = "Depends"
GENERATOR_FIELD = "Catkin-ROS-Message-Generator"

def read_control_file(source_dir, control_file):
    stackyaml = yaml.load(open(control_file))

    pkg_name = stackyaml[PKG_NAME_FIELD]
    # print >>sys.stderr, "pkg_name=<%s>" % pkg_name
    if 'ALL' not in enabled_projects and pkg_name not in enabled_projects:
        return
    pkgs[pkg_name] = p = Pkg(path=source_dir)
    p.depends = stackyaml.get('Depends', '')
    if type(p.depends) == str:
        p.depends = set([x.strip() for x in p.depends.split(',') if len(x.strip()) > 0])
    else:
        p.depends = set([])
    p.genlang = stackyaml.get(GENERATOR_FIELD, None)
    # print >>sys.stderr, "p=", p

#def find_external_pkgs(pkgs):
#    return reduce( set.union, pkgs.values()) - set(dep_map.keys())

def remove_deps(pkgs, name):
    for dep_list in [p.depends for p in pkgs.values()]:
        dep_list.difference_update([name])

def topo_packages_generators_first(pkgs):
    """
    First return packages which have generators. Then return the rest.
    """

    def next_pkg():
        for name, p in pkgs.items():
            if p.genlang and not p.depends:
                return (name,p)
        for name, p in pkgs.items():
            if not p.depends:
                return (name,p)
        return None

    while len(pkgs) > 0:
        (name,p) = next_pkg()
        del pkgs[name]
        remove_deps(pkgs, name)
        yield (name,p)

stackyamls = glob.glob(os.path.join(source_root_dir, '*', 'stack.yaml'))
# print >>sys.stderr, "stackyamls=", stackyamls
for stackyamlfilename in stackyamls:
    if os.path.basename(os.path.dirname(stackyamlfilename)).startswith('.'):
        continue
    read_control_file(os.path.dirname(stackyamlfilename), stackyamlfilename)
if pkgs:
    all_deps = reduce(set.union, [p.depends for p in pkgs.values()])
else:
    all_deps = set()

unknown_deps = all_deps - set(pkgs)

#remove unknown dependencies from the list
for p in pkgs.values():
    p.depends = set(p.depends) - set(unknown_deps)

if 'catkin' in pkgs:
    del pkgs['catkin']
    remove_deps(pkgs, 'catkin')

langs = [name for name,p in pkgs.items() if p.genlang]

topo_pkgs = topo_packages_generators_first(pkgs)
}

message(STATUS "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
message(STATUS "~~         traversing stacks/projects in dependency order         ~~")
message(STATUS "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
set(CATKIN_GENLANGS @(' '.join(langs)))

@[for name,pkg in topo_pkgs]
message(STATUS "+++ @name")
stamp(@(pkg.path)/stack.yaml)
add_subdirectory(@(pkg.path))
@[end for]

@{
import yaml, os, sys, glob, em

class Pkg:
    def __init__(self, **kwds):
        self.__dict__.update(path=None, genlang=None, depends=None)
        self.__dict__.update(kwds)
    def __repr__(self):
        return "path=%s depends=%s genlang=%s\n" % (self.path, self.depends, self.genlang)

pkgs = {}

PKG_NAME_FIELD = "Catkin-ProjectName"
PKG_DEP_FIELD = "Depends"
GENERATOR_FIELD = "Catkin-ROS-Message-Generator"

def read_control_file(source_dir, control_file):
    stackyaml = yaml.load(open(control_file))

    pkg_name = stackyaml[PKG_NAME_FIELD]
    # print >>sys.stderr, "pkg_name=<%s>" % pkg_name
    if 'ALL' not in enabled_projects and pkg_name not in enabled_projects:
        return
    pkgs[pkg_name] = p = Pkg(path=source_dir)
    p.depends = stackyaml.get('Depends', '')
    if type(p.depends) == str:
        p.depends = set([x.strip() for x in p.depends.split(',') if len(x.strip()) > 0])
    else:
        p.depends = set([])
    p.genlang = stackyaml.get(GENERATOR_FIELD, None)
    # print >>sys.stderr, "p=", p

#def find_external_pkgs(pkgs):
#    return reduce( set.union, pkgs.values()) - set(dep_map.keys())

def remove_deps(pkgs, name):
    for dep_list in [p.depends for p in pkgs.values()]:
        dep_list.difference_update([name])

def topo_packages_generators_first(pkgs):
    """
    First return packages which have generators. Then return the rest.
    """

    def next_pkg():
        for name, p in pkgs.items():
            if p.genlang and not p.depends:
                return (name,p)
        for name, p in pkgs.items():
            if not p.depends:
                return (name,p)
        return None

    while len(pkgs) > 0:
        (name,p) = next_pkg()
        del pkgs[name]
        remove_deps(pkgs, name)
        yield (name,p)

stackyamls = glob.glob(os.path.join(source_root_dir, '*', 'stack.yaml'))
# print >>sys.stderr, "stackyamls=", stackyamls
for stackyamlfilename in stackyamls:
    if os.path.basename(os.path.dirname(stackyamlfilename)).startswith('.'):
        continue
    read_control_file(os.path.dirname(stackyamlfilename), stackyamlfilename)
all_deps = reduce(set.union, [p.depends for p in pkgs.values()])

unknown_deps = all_deps - set(pkgs)

#remove unknown dependencies from the list
for p in pkgs.values():
  p.depends = set(p.depends) - set(unknown_deps)

del pkgs['catkin']
remove_deps(pkgs, 'catkin')

langs = [name for name,p in pkgs.items() if p.genlang]

topo_pkgs = topo_packages_generators_first(pkgs)
}

message(STATUS "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
message(STATUS "~~         traversing stacks/projects in dependency order         ~~")
message(STATUS "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
set(CATKIN_GENLANGS @(' '.join(langs)))

@[for name,pkg in topo_pkgs]
message(STATUS "+++ @name")
stamp(@(pkg.path)/stack.yaml)
add_subdirectory(@(pkg.path))
@[end for]


Fixes undefined view in transition bug

Fixes undefined view in transition bug

#= require jquery
#= require jquery_ujs
#
#= require handlebars
#= require ember
#= require ember-data
#= require ember-animate
#= require ember-animated-outlet
#= require_self
#= require quiz_buzz

# for more details see: http://emberjs.com/guides/application/
emberPaths = ['/']
validEmberPath = emberPaths.any (path)->
  window.location.pathname == path || window.location.pathname[0..path.length+1] == path + '#'

window.QuizBuzz = {}
if validEmberPath
  window.QuizBuzz = Ember.Application.create(
    rootElement: '#ember-quiz-buzz'

    # Basic logging, e.g. "Transitioned into 'post'"
    # LOG_TRANSITIONS: true

    # Extremely detailed logging, highlighting every internal
    # step made while transitioning into a route, including
    # `beforeModel`, `model`, and `afterModel` hooks, and
    # information about redirects and aborted transitions
    # LOG_TRANSITIONS_INTERNAL: true
    )

onDocHeightChanged = ()->
  docHeight = $(document).height()
  bodyHeight = $('body').height()
  if docHeight > bodyHeight
    footerHeight = 50
    # if the doc's height is greater than the body's height,
    # then a new element has been rendered or a height increased,
    # increase the body to account for that,
    # adding-in the height of the absolute positioned footer
    $('body').height(docHeight + footerHeight)

$(document).on 'ready.height-fix', ()->
  $(document).off('ready.height-fix')
  onDocHeightChanged()
  $(window).on 'load.height-fix', ()->
    $(window).off('load.height-fix')
    onDocHeightChanged()
  $(window).resize ()->
    onDocHeightChanged()
  $(window).scroll ()->
    onDocHeightChanged()

Ember.View.reopen
  # Basic animation for all of the views
  willAnimateIn: ()->
    @$()?.css("opacity", 0)
  animateIn: ()->
    @$()?.fadeTo(500, 1)
  # Overriding the animateOut without doing view cleanup will result in old views sticking around
  # animateOut: ()->
  #   @$()?.fadeTo(500, 0, done)

  # An element was inserted, so update the body height because the doc height may have changed
  didInsertElement: ()->
    @_super()
    Ember.run.scheduleOnce('afterRender', @, onDocHeightChanged)

#= require jquery
#= require jquery_ujs
#
#= require handlebars
#= require ember
#= require ember-data
#= require ember-animate
#= require ember-animated-outlet
#= require_self
#= require quiz_buzz

# for more details see: http://emberjs.com/guides/application/
emberPaths = ['/']
validEmberPath = emberPaths.any (path)->
  window.location.pathname == path || window.location.pathname[0..path.length+1] == path + '#'

window.QuizBuzz = {}
if validEmberPath
  window.QuizBuzz = Ember.Application.create(
    rootElement: '#ember-quiz-buzz'

    # Basic logging, e.g. "Transitioned into 'post'"
    # LOG_TRANSITIONS: true

    # Extremely detailed logging, highlighting every internal
    # step made while transitioning into a route, including
    # `beforeModel`, `model`, and `afterModel` hooks, and
    # information about redirects and aborted transitions
    # LOG_TRANSITIONS_INTERNAL: true
    )

onDocHeightChanged = ()->
  docHeight = $(document).height()
  bodyHeight = $('body').height()
  if docHeight > bodyHeight
    footerHeight = 50
    # if the doc's height is greater than the body's height,
    # then a new element has been rendered or a height increased,
    # increase the body to account for that,
    # adding-in the height of the absolute positioned footer
    $('body').height(docHeight + footerHeight)

$(document).on 'ready.height-fix', ()->
  $(document).off('ready.height-fix')
  onDocHeightChanged()
  $(window).on 'load.height-fix', ()->
    $(window).off('load.height-fix')
    onDocHeightChanged()
  $(window).resize ()->
    onDocHeightChanged()
  $(window).scroll ()->
    onDocHeightChanged()

Ember.View.reopen
  # Basic animation for all of the views
  willAnimateIn: ()->
    this.$().css("opacity", 0)
  animateIn: ()->
    this.$().fadeTo(500, 1)
  # Overriding the animateOut without doing view cleanup will result in old views sticking around
  # animateOut: ()->
  #   this.$().fadeTo(500, 0, done)

  # An element was inserted, so update the body height because the doc height may have changed
  didInsertElement: ()->
    @_super()
    Ember.run.scheduleOnce('afterRender', @, onDocHeightChanged)


refactor command generation

refactor command generation

@[if os_name == 'ubuntu']@
@{
from itertools import product
commands = []
}@
@[  if arch in ['amd64', 'i386']]@
@{
for distribution, archive_type in product((os_code_name, os_code_name + '-updates', os_code_name + '-security'), ('deb', 'deb-src')):
    commands.append('echo "%s http://archive.ubuntu.com/ubuntu/ %s multiverse" >> /etc/apt/sources.list' % (archive_type, distribution))
}@
@[  elif arch in ['armhf', 'armv8']]@
@{
for distribution, archive_type in product((os_code_name, os_code_name + '-updates', os_code_name + '-security'), ('deb', 'deb-src')):
    commands.append('echo "%s http://ports.ubuntu.com// %s multiverse" >> /etc/apt/sources.list' % (archive_type, distribution))
}@
@[  end if]@
@[  if commands]@
RUN @(' && '.join(commands))
@[  end if]@
@[else if os_name == 'debian']@
# Add contrib and non-free to debian images
RUN echo deb http://http.debian.net/debian @os_code_name contrib non-free | tee -a /etc/apt/sources.list
@[end if]@

@[if os_name == 'ubuntu']@
@[  if arch in ['amd64', 'i386']]@
# Add multiverse
RUN echo "deb http://archive.ubuntu.com/ubuntu/ @(os_code_name) multiverse" >> /etc/apt/sources.list && echo "deb-src http://archive.ubuntu.com/ubuntu/ @(os_code_name) multiverse" >> /etc/apt/sources.list && echo "deb-src http://archive.ubuntu.com/ubuntu/ @(os_code_name)-updates multiverse" >> /etc/apt/sources.list && echo "deb-src http://archive.ubuntu.com/ubuntu/ @(os_code_name)-updates multiverse" >> /etc/apt/sources.list && echo "deb-src http://archive.ubuntu.com/ubuntu/ @(os_code_name)-security multiverse" >> /etc/apt/sources.list && echo "deb-src http://archive.ubuntu.com/ubuntu/ @(os_code_name)-security multiverse" >> /etc/apt/sources.list
@[  elif arch in ['armhf', 'armv8']]@
# Add multiverse
RUN echo "deb http://ports.ubuntu.com/ @(os_code_name) multiverse" >> /etc/apt/sources.list && echo "deb-src http://ports.ubuntu.com/ @(os_code_name) multiverse" >> /etc/apt/sources.list && echo "deb-src http://ports.ubuntu.com/ @(os_code_name)-updates multiverse" >> /etc/apt/sources.list && echo "deb-src http://ports.ubuntu.com/ @(os_code_name)-updates multiverse" >> /etc/apt/sources.list && echo "deb-src http://ports.ubuntu.com/ @(os_code_name)-security multiverse" >> /etc/apt/sources.list && echo "deb-src http://ports.ubuntu.com/ @(os_code_name)-security multiverse" >> /etc/apt/sources.list
@[  end if]@
@[else if os_name == 'debian']@
# Add contrib and non-free to debian images
RUN echo deb http://http.debian.net/debian @os_code_name contrib non-free | tee -a /etc/apt/sources.list
@[end if]@


Avoiding the need for lsb-release

Avoiding the need for lsb-release

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
    now_str=now_str,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@[if 'packages' in locals()]@
@[if packages]@

# install packages
RUN apt-get update && apt-get install -q -y \
    @(' \\\n    '.join(packages))@ \
    && rm -rf /var/lib/apt/lists/*

@[end if]@
@[end if]@

# setup keys
RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys D2486D2DD83DB69272AFE98867170598AF249743

# setup sources.list
RUN . /etc/os-release \
    && echo "deb http://packages.osrfoundation.org/gazebo/$ID-stable $VERSION_CODENAME main" > /etc/apt/sources.list.d/gazebo-latest.list

# install gazebo packages
RUN apt-get update && apt-get install -q -y \
    @(' \\\n    '.join(gazebo_packages))@  \
    && rm -rf /var/lib/apt/lists/*

# setup environment
EXPOSE 11345

@[if 'entrypoint_name' in locals()]@
@[if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[end if]@
@[end if]@
@{
cmds = [
'gzserver',
]
}@
CMD ["@(' && '.join(cmds))"]

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
    now_str=now_str,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@[if 'packages' in locals()]@
@[if packages]@

# install packages
RUN apt-get update && apt-get install -q -y \
    @(' \\\n    '.join(packages))@ \
    && rm -rf /var/lib/apt/lists/*

@[end if]@
@[end if]@

# setup keys
RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys D2486D2DD83DB69272AFE98867170598AF249743

# setup sources.list
RUN echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-latest.list

# install gazebo packages
RUN apt-get update && apt-get install -q -y \
    @(' \\\n    '.join(gazebo_packages))@  \
    && rm -rf /var/lib/apt/lists/*

# setup environment
EXPOSE 11345

@[if 'entrypoint_name' in locals()]@
@[if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[end if]@
@[end if]@
@{
cmds = [
'gzserver',
]
}@
CMD ["@(' && '.join(cmds))"]


Fix documentation of `RCLCPP_[INFO,WARN,...]` (#1943)

In the pull request https://github.com/ros2/rclcpp/pull/1442 the ability
to use `std::string` as the first argument to the logging functions was
(rightfully) removed.

This commit just corrects the documentation of the macro, since it
confused me a bit ;)

Signed-off-by: Daniel Reuter <9126ce7210890a9d575946ba5fa55c83c76d4730@googlemail.com>

// generated from rclcpp/resource/logging.hpp.em

// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__LOGGING_HPP_
#define RCLCPP__LOGGING_HPP_

#include <sstream>
#include <type_traits>

#include "rclcpp/logger.hpp"
#include "rcutils/logging_macros.h"
#include "rclcpp/utilities.hpp"

// These are used for compiling out logging macros lower than a minimum severity.
#define RCLCPP_LOG_MIN_SEVERITY_DEBUG 0
#define RCLCPP_LOG_MIN_SEVERITY_INFO 1
#define RCLCPP_LOG_MIN_SEVERITY_WARN 2
#define RCLCPP_LOG_MIN_SEVERITY_ERROR 3
#define RCLCPP_LOG_MIN_SEVERITY_FATAL 4
#define RCLCPP_LOG_MIN_SEVERITY_NONE 5

#define RCLCPP_FIRST_ARG(N, ...) N
#define RCLCPP_ALL_BUT_FIRST_ARGS(N, ...) __VA_ARGS__

/**
 * \def RCLCPP_LOG_MIN_SEVERITY
 * Define RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
 * in your build options to compile out anything below that severity.
 * Use RCLCPP_LOG_MIN_SEVERITY_NONE to compile out all macros.
 */
#ifndef RCLCPP_LOG_MIN_SEVERITY
#define RCLCPP_LOG_MIN_SEVERITY RCLCPP_LOG_MIN_SEVERITY_DEBUG
#endif

@{
from collections import OrderedDict
from copy import deepcopy
from rcutils.logging import feature_combinations
from rcutils.logging import get_suffix_from_features
from rcutils.logging import severities
from rcutils.logging import throttle_args
from rcutils.logging import throttle_params

throttle_args['condition_before'] = 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(clock, duration)'
del throttle_params['get_time_point_value']
throttle_params['clock'] = 'rclcpp::Clock that will be used to get the time point.'
throttle_params.move_to_end('clock', last=False)

rclcpp_feature_combinations = OrderedDict()
for combinations, feature in feature_combinations.items():
    # skip feature combinations using 'named'
    if 'named' in combinations:
        continue
    rclcpp_feature_combinations[combinations] = feature
# add a stream variant for each available feature combination
stream_arg = 'stream_arg'
for combinations, feature in list(rclcpp_feature_combinations.items()):
    combinations = ('stream', ) + combinations
    feature = deepcopy(feature)
    feature.params[stream_arg] = 'The argument << into a stringstream'
    rclcpp_feature_combinations[combinations] = feature

def get_rclcpp_suffix_from_features(features):
    suffix = get_suffix_from_features(features)
    if 'stream' in features:
        suffix = '_STREAM' + suffix
    return suffix
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
 */
///@@{
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_@(severity)@(suffix)(...)
@[ end for]@

#else
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
// The RCLCPP_@(severity)@(suffix) macro is surrounded by do { .. } while (0)
// to implement the standard C macro idiom to make the macro safe in all
// contexts; see http://c-faq.com/cpp/multistmt.html for more information.
/**
 * \def RCLCPP_@(severity)@(suffix)
 * Log a message with severity @(severity)@
@[ if rclcpp_feature_combinations[feature_combination].doc_lines]@
 with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in rclcpp_feature_combinations[feature_combination].doc_lines]@
 * @(doc_line)
@[ end for]@
 * \param logger The `rclcpp::Logger` to use
@[ for param_name, doc_line in rclcpp_feature_combinations[feature_combination].params.items()]@
 * \param @(param_name) @(doc_line)
@[ end for]@
@[ if 'stream' not in feature_combination]@
 * \param ... The format string, followed by the variable arguments for the format string.
@[ end if]@
 */
@{params = rclcpp_feature_combinations[feature_combination].params.keys()}@
#define RCLCPP_@(severity)@(suffix)(logger@(''.join([', ' + p for p in params]))@
@[ if 'stream' not in feature_combination]@
, ...@
@[ end if]@
) \
  do { \
    static_assert( \
      ::std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(logger)>::type>::type, \
      typename ::rclcpp::Logger>::value, \
      "First argument to logging macros must be an rclcpp::Logger"); \
@[ if 'throttle' in feature_combination]@ \
    auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
      try { \
        *time_point = c.now().nanoseconds(); \
      } catch (...) { \
        RCUTILS_SAFE_FWRITE_TO_STDERR( \
        "[rclcpp|logging.hpp] RCLCPP_@(severity)@(suffix) could not get current time stamp\n"); \
        return RCUTILS_RET_ERROR; \
      } \
        return RCUTILS_RET_OK; \
    }; \
@[ end if] \
@[ if 'stream' in feature_combination]@
    std::stringstream rclcpp_stream_ss_; \
    rclcpp_stream_ss_ << @(stream_arg); \
@[ end if]@
    RCUTILS_LOG_@(severity)@(get_suffix_from_features(feature_combination))_NAMED( \
@{params = ['get_time_point' if p == 'clock' and 'throttle' in feature_combination else p for p in params]}@
@[ if params]@
@(''.join(['      ' + p + ', \\\n' for p in params if p != stream_arg]))@
@[ end if]@
      (logger).get_name(), \
@[ if 'stream' not in feature_combination]@
      __VA_ARGS__); \
@[ else]@
      "%s", rclcpp_stream_ss_.str().c_str()); \
@[ end if]@
  } while (0)

@[ end for]@
#endif
///@@}

@[end for]@

#endif  // RCLCPP__LOGGING_HPP_

// generated from rclcpp/resource/logging.hpp.em

// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__LOGGING_HPP_
#define RCLCPP__LOGGING_HPP_

#include <sstream>
#include <type_traits>

#include "rclcpp/logger.hpp"
#include "rcutils/logging_macros.h"
#include "rclcpp/utilities.hpp"

// These are used for compiling out logging macros lower than a minimum severity.
#define RCLCPP_LOG_MIN_SEVERITY_DEBUG 0
#define RCLCPP_LOG_MIN_SEVERITY_INFO 1
#define RCLCPP_LOG_MIN_SEVERITY_WARN 2
#define RCLCPP_LOG_MIN_SEVERITY_ERROR 3
#define RCLCPP_LOG_MIN_SEVERITY_FATAL 4
#define RCLCPP_LOG_MIN_SEVERITY_NONE 5

#define RCLCPP_FIRST_ARG(N, ...) N
#define RCLCPP_ALL_BUT_FIRST_ARGS(N, ...) __VA_ARGS__

/**
 * \def RCLCPP_LOG_MIN_SEVERITY
 * Define RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
 * in your build options to compile out anything below that severity.
 * Use RCLCPP_LOG_MIN_SEVERITY_NONE to compile out all macros.
 */
#ifndef RCLCPP_LOG_MIN_SEVERITY
#define RCLCPP_LOG_MIN_SEVERITY RCLCPP_LOG_MIN_SEVERITY_DEBUG
#endif

@{
from collections import OrderedDict
from copy import deepcopy
from rcutils.logging import feature_combinations
from rcutils.logging import get_suffix_from_features
from rcutils.logging import severities
from rcutils.logging import throttle_args
from rcutils.logging import throttle_params

throttle_args['condition_before'] = 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(clock, duration)'
del throttle_params['get_time_point_value']
throttle_params['clock'] = 'rclcpp::Clock that will be used to get the time point.'
throttle_params.move_to_end('clock', last=False)

rclcpp_feature_combinations = OrderedDict()
for combinations, feature in feature_combinations.items():
    # skip feature combinations using 'named'
    if 'named' in combinations:
        continue
    rclcpp_feature_combinations[combinations] = feature
# add a stream variant for each available feature combination
stream_arg = 'stream_arg'
for combinations, feature in list(rclcpp_feature_combinations.items()):
    combinations = ('stream', ) + combinations
    feature = deepcopy(feature)
    feature.params[stream_arg] = 'The argument << into a stringstream'
    rclcpp_feature_combinations[combinations] = feature

def get_rclcpp_suffix_from_features(features):
    suffix = get_suffix_from_features(features)
    if 'stream' in features:
        suffix = '_STREAM' + suffix
    return suffix
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
 */
///@@{
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_@(severity)@(suffix)(...)
@[ end for]@

#else
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
// The RCLCPP_@(severity)@(suffix) macro is surrounded by do { .. } while (0)
// to implement the standard C macro idiom to make the macro safe in all
// contexts; see http://c-faq.com/cpp/multistmt.html for more information.
/**
 * \def RCLCPP_@(severity)@(suffix)
 * Log a message with severity @(severity)@
@[ if rclcpp_feature_combinations[feature_combination].doc_lines]@
 with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in rclcpp_feature_combinations[feature_combination].doc_lines]@
 * @(doc_line)
@[ end for]@
 * \param logger The `rclcpp::Logger` to use
@[ for param_name, doc_line in rclcpp_feature_combinations[feature_combination].params.items()]@
 * \param @(param_name) @(doc_line)
@[ end for]@
@[ if 'stream' not in feature_combination]@
 * \param ... The format string, followed by the variable arguments for the format string.
 * It also accepts a single argument of type std::string.
@[ end if]@
 */
@{params = rclcpp_feature_combinations[feature_combination].params.keys()}@
#define RCLCPP_@(severity)@(suffix)(logger@(''.join([', ' + p for p in params]))@
@[ if 'stream' not in feature_combination]@
, ...@
@[ end if]@
) \
  do { \
    static_assert( \
      ::std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(logger)>::type>::type, \
      typename ::rclcpp::Logger>::value, \
      "First argument to logging macros must be an rclcpp::Logger"); \
@[ if 'throttle' in feature_combination]@ \
    auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
      try { \
        *time_point = c.now().nanoseconds(); \
      } catch (...) { \
        RCUTILS_SAFE_FWRITE_TO_STDERR( \
        "[rclcpp|logging.hpp] RCLCPP_@(severity)@(suffix) could not get current time stamp\n"); \
        return RCUTILS_RET_ERROR; \
      } \
        return RCUTILS_RET_OK; \
    }; \
@[ end if] \
@[ if 'stream' in feature_combination]@
    std::stringstream rclcpp_stream_ss_; \
    rclcpp_stream_ss_ << @(stream_arg); \
@[ end if]@
    RCUTILS_LOG_@(severity)@(get_suffix_from_features(feature_combination))_NAMED( \
@{params = ['get_time_point' if p == 'clock' and 'throttle' in feature_combination else p for p in params]}@
@[ if params]@
@(''.join(['      ' + p + ', \\\n' for p in params if p != stream_arg]))@
@[ end if]@
      (logger).get_name(), \
@[ if 'stream' not in feature_combination]@
      __VA_ARGS__); \
@[ else]@
      "%s", rclcpp_stream_ss_.str().c_str()); \
@[ end if]@
  } while (0)

@[ end for]@
#endif
///@@}

@[end for]@

#endif  // RCLCPP__LOGGING_HPP_


Fix Emscripten compilation

Fix Emscripten compilation

CXXFLAGS = -O3 -Wall -g -c -std=c++14 -ferror-limit=1  -s USE_ZLIB=1 -s USE_LIBPNG=1 -I../OpenALSoundSystem -I../noudar-core/noudar-core/include -Iincludes -I../noudar-rendering -I../gles2-renderer/ -IX11-version -I../glm -s ALLOW_MEMORY_GROWTH=1 -DGLM_FORCE_RADIANS -I../stb -DUSE_ITEMS_INSTANTLY

LDFLAGS =  -O3 -s USE_ZLIB=1 -s USE_LIBPNG=1 -s --preload-file res --use-preload-plugins  -s ALLOW_MEMORY_GROWTH=1

CXX = em++
CC = emcc

OBJS = X11-version/main.o \
    ../noudar-core/noudar-core/src/Vec2i.o \
    ../noudar-core/noudar-core/src/CActor.o \
    ../noudar-core/noudar-core/src/CMonster.o \
    ../noudar-core/noudar-core/src/CMonsterGenerator.o \
    ../noudar-core/noudar-core/src/CTeam.o \
    ../noudar-core/noudar-core/src/CMap.o \
    ../noudar-core/noudar-core/src/CCharacter.o \
    ../noudar-core/noudar-core/src/CCharacterArchetype.o \
    ../noudar-core/noudar-core/src/Common.o \
    ../noudar-core/noudar-core/src/CDoorway.o \
    ../noudar-core/noudar-core/src/IFileLoaderDelegate.o \
    ../noudar-core/noudar-core/src/IMapElement.o \
    ../noudar-core/noudar-core/src/CGame.o \
    ../noudar-core/noudar-core/src/CItem.o \
    ../noudar-core/noudar-core/src/CStorageItem.o \
    ../noudar-core/noudar-core/src/CPlainFileLoader.o \
    ../noudar-core/noudar-core/src/CGameDelegate.o \
    ../noudar-core/noudar-core/src/commands/IGameCommand.o \
    ../noudar-core/noudar-core/src/commands/CMoveActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CLoadNewLevelCommand.o \
    ../noudar-core/noudar-core/src/commands/CTurnActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CQuitCommand.o \
    ../noudar-core/noudar-core/src/commands/CNullCommand.o \
    ../noudar-core/noudar-core/src/commands/CEndTurnCommand.o \
        ../noudar-core/noudar-core/src/commands/CPickItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CDropItemCommand.o \
    ../noudar-core/noudar-core/src/commands/CSuggestItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CUseCurrentItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCycleNextItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCyclePreviousItemCommand.o \
    	       ../noudar-rendering/RenderingJobSnapshotAdapter.o \
               ../noudar-rendering/GameNativeAPI.o \
               ../noudar-rendering/Camera.o \
               ../noudar-rendering/DungeonGLES2Renderer.o \
               ../noudar-rendering/LightningStrategy.o \
               ../noudar-rendering/VisibilityStrategy.o \
               ../noudar-rendering/NoudarDungeonSnapshot.o \
               ../noudar-rendering/SplatAnimation.o \
                ../noudar-rendering/CTile3DProperties.o \
               ../noudar-rendering/NoudarGLES2Bridge.o \
               X11-version/WindowOperationsX11.o \
               X11-version/x11_emscripten.o \
               ../noudar-rendering/Logger.o \
               ../noudar-rendering/CLerp.o \
               ../noudar-rendering/NativeBitmap.o \
               ../noudar-rendering/VBORenderingJob.o \
               ../noudar-rendering/LoadPNG.o \
                ../OpenALSoundSystem/SoundClip.o \
                ../OpenALSoundSystem/SoundEmitter.o \
                ../OpenALSoundSystem/SoundListener.o \
                ../OpenALSoundSystem/SoundUtils.o

TARGET = noudar.html

$(TARGET):	$(OBJS)
	$(CXX) -o $(TARGET) $(OBJS) $(LDFLAGS)

all:	$(TARGET)

clean:
	rm -f $(OBJS) $(TARGET)
	rm *~
	rm *.js
	rm *.mem
	rm *.html
	rm *.data

CXXFLAGS = -O3 -Wall -g -c -std=c++14 -ferror-limit=1  -s USE_ZLIB=1 -s USE_LIBPNG=1 -I../OpenALSoundSystem -I../noudar-core/noudar-core/include -Iincludes -I../noudar-rendering -I../gles2-renderer/ -IX11-version -I../glm -s ALLOW_MEMORY_GROWTH=1 -DGLM_FORCE_RADIANS -I../stb -DUSE_ITEMS_INSTANTLY

LDFLAGS =  -O3 -s USE_ZLIB=1 -s USE_LIBPNG=1 -s --preload-file res --use-preload-plugins  -s ALLOW_MEMORY_GROWTH=1

CXX = em++
CC = emcc

OBJS = X11-version/main.o \
    ../noudar-core/noudar-core/src/Vec2i.o \
    ../noudar-core/noudar-core/src/CActor.o \
    ../noudar-core/noudar-core/src/CMonster.o \
    ../noudar-core/noudar-core/src/CMonsterGenerator.o \
    ../noudar-core/noudar-core/src/CTeam.o \
    ../noudar-core/noudar-core/src/CMap.o \
    ../noudar-core/noudar-core/src/CCharacter.o \
    ../noudar-core/noudar-core/src/CCharacterArchetype.o \
    ../noudar-core/noudar-core/src/Common.o \
    ../noudar-core/noudar-core/src/CDoorway.o \
    ../noudar-core/noudar-core/src/IFileLoaderDelegate.o \
    ../noudar-core/noudar-core/src/IMapElement.o \
    ../noudar-core/noudar-core/src/CGame.o \
    ../noudar-core/noudar-core/src/CItem.o \
    ../noudar-core/noudar-core/src/CStorageItem.o \
    ../noudar-core/noudar-core/src/CPlainFileLoader.o \
    ../noudar-core/noudar-core/src/CGameDelegate.o \
    ../noudar-core/noudar-core/src/commands/IGameCommand.o \
    ../noudar-core/noudar-core/src/commands/CMoveActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CLoadNewLevelCommand.o \
    ../noudar-core/noudar-core/src/commands/CTurnActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CQuitCommand.o \
    ../noudar-core/noudar-core/src/commands/CNullCommand.o \
    ../noudar-core/noudar-core/src/commands/CEndTurnCommand.o \
        ../noudar-core/noudar-core/src/commands/CPickItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CDropItemCommand.o \
    ../noudar-core/noudar-core/src/commands/CSuggestItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CUseCurrentItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCycleNextItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCyclePreviousItemCommand.o \
    	       ../noudar-rendering/RenderingJobSnapshotAdapter.o \
               ../noudar-rendering/GameNativeAPI.o \
               ../noudar-rendering/Camera.o \
               ../noudar-rendering/DungeonGLES2Renderer.o \
               ../noudar-rendering/LightningStrategy.o \
               ../noudar-rendering/VisibilityStrategy.o \
               ../noudar-rendering/NoudarDungeonSnapshot.o \
               ../noudar-rendering/SplatAnimation.o \
               ../noudar-rendering/GraphicNode.o \
                ../noudar-rendering/CTile3DProperties.o \
               ../noudar-rendering/NoudarGLES2Bridge.o \
               X11-version/WindowOperationsX11.o \
               X11-version/x11_emscripten.o \
               ../noudar-rendering/Logger.o \
               ../noudar-rendering/CLerp.o \
               ../noudar-rendering/NativeBitmap.o \
               ../noudar-rendering/VBORenderingJob.o \
               ../noudar-rendering/LoadPNG.o \
                ../OpenALSoundSystem/SoundClip.o \
                ../OpenALSoundSystem/SoundEmitter.o \
                ../OpenALSoundSystem/SoundListener.o \
                ../OpenALSoundSystem/SoundUtils.o

TARGET = noudar.html

$(TARGET):	$(OBJS)
	$(CXX) -o $(TARGET) $(OBJS) $(LDFLAGS)

all:	$(TARGET)

clean:
	rm -f $(OBJS) $(TARGET)
	rm *~
	rm *.js
	rm *.mem
	rm *.html
	rm *.data


trigger doc independent job on timer

trigger doc independent job on timer

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
@(SNIPPET(
    'log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
  <keepDependencies>false</keepDependencies>
  <properties>
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_requeue-job',
))@
  </properties>
@(SNIPPET(
    'scm_null',
))@
  <assignedNode>@(node_label if node_label else 'buildslave')</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@(SNIPPET(
    'trigger_timer',
    spec='H 3 * * *',
))@
  </triggers>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: Clone ros_buildfarm"',
        'rm -fr ros_buildfarm',
        'git clone --depth 1 %s%s ros_buildfarm' % ('-b %s ' % ros_buildfarm_repository.version if ros_buildfarm_repository.version else '', ros_buildfarm_repository.url),
        'git -C ros_buildfarm log -n 1',
        'rm -fr ros_buildfarm/.git',
        'rm -fr ros_buildfarm/doc',
        'echo "# END SECTION"',
        'rm -fr repositories',
    ]),
))@
@[for repo_url in doc_repositories]@
@{
import os
repo_name = os.path.splitext(os.path.basename(repo_url))[0]
}@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: Clone %s"' % repo_name,
        'git clone --depth 1 %s $WORKSPACE/repositories/%s' % (repo_url, repo_name),
        'git -C $WORKSPACE/repositories/%s log -n 1' % repo_name,
        'rm -fr $WORKSPACE/repositories/%s/.git' % repo_name,
        'echo "# END SECTION"',
    ]),
))@
@[end for]@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_doc_independent',
        'rm -fr $WORKSPACE/generated_documentation',
        'mkdir -p $WORKSPACE/docker_doc_independent',
        'mkdir -p $WORKSPACE/generated_documentation',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_doc_independent/docker.cid > $WORKSPACE/docker_generating_docker/docker_doc_independent.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating documentation for independent packages',
        'echo "# BEGIN SECTION: Generate Dockerfile - doc independent task"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/doc/run_doc_independent_job.py' +
        ' ' + config_url +
        ' ' + doc_build_name +
        ' ' + ' '.join(repository_args) +
        ' --dockerfile-dir $WORKSPACE/docker_doc_independent',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - doc independent"',
        'cd $WORKSPACE/docker_doc_independent',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build -t doc_independent .',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - doc independent"',
        'docker run' +
        ' --cidfile=$WORKSPACE/docker_doc_independent/docker.cid' +
        ' -e=HOME=/home/buildfarm' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/repositories:/tmp/repositories' +
        ' -v $WORKSPACE/generated_documentation:/tmp/generated_documentation' +
        ' doc_independent',
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ -d "$WORKSPACE/generated_documentation/independent/api" ]; then',
        '  echo "# BEGIN SECTION: rsync API documentation to server"',
        '  ssh jenkins-slave@repo "mkdir -p /var/repos/docs/independent/api"',
        '  cd $WORKSPACE/generated_documentation/independent/api',
        '  for pkg_name in $(find . -maxdepth 1 -mindepth 1 -type d); do',
        '    rsync -e "ssh -o StrictHostKeyChecking=no" -r --delete $pkg_name/html jenkins-slave@repo:/var/repos/docs/independent/api/$pkg_name',
        '  done',
        '  echo "# END SECTION"',
        'fi',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_id=credential_id,
))@
  </buildWrappers>
</project>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'</description>
@(SNIPPET(
    'log-rotator',
    days_to_keep=100,
    num_to_keep=100,
))@
  <keepDependencies>false</keepDependencies>
  <properties>
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_requeue-job',
))@
  </properties>
@(SNIPPET(
    'scm_null',
))@
  <assignedNode>@(node_label if node_label else 'buildslave')</assignedNode>
  <canRoam>false</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers>
@(SNIPPET(
    'trigger_poll',
    spec='H 3 * * *',
))@
  </triggers>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: Clone ros_buildfarm"',
        'rm -fr ros_buildfarm',
        'git clone --depth 1 %s%s ros_buildfarm' % ('-b %s ' % ros_buildfarm_repository.version if ros_buildfarm_repository.version else '', ros_buildfarm_repository.url),
        'git -C ros_buildfarm log -n 1',
        'rm -fr ros_buildfarm/.git',
        'rm -fr ros_buildfarm/doc',
        'echo "# END SECTION"',
        'rm -fr repositories',
    ]),
))@
@[for repo_url in doc_repositories]@
@{
import os
repo_name = os.path.splitext(os.path.basename(repo_url))[0]
}@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: Clone %s"' % repo_name,
        'git clone --depth 1 %s $WORKSPACE/repositories/%s' % (repo_url, repo_name),
        'git -C $WORKSPACE/repositories/%s log -n 1' % repo_name,
        'rm -fr $WORKSPACE/repositories/%s/.git' % repo_name,
        'echo "# END SECTION"',
    ]),
))@
@[end for]@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_doc_independent',
        'rm -fr $WORKSPACE/generated_documentation',
        'mkdir -p $WORKSPACE/docker_doc_independent',
        'mkdir -p $WORKSPACE/generated_documentation',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_doc_independent/docker.cid > $WORKSPACE/docker_generating_docker/docker_doc_independent.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating documentation for independent packages',
        'echo "# BEGIN SECTION: Generate Dockerfile - doc independent task"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/doc/run_doc_independent_job.py' +
        ' ' + config_url +
        ' ' + doc_build_name +
        ' ' + ' '.join(repository_args) +
        ' --dockerfile-dir $WORKSPACE/docker_doc_independent',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - doc independent"',
        'cd $WORKSPACE/docker_doc_independent',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build -t doc_independent .',
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - doc independent"',
        'docker run' +
        ' --cidfile=$WORKSPACE/docker_doc_independent/docker.cid' +
        ' -e=HOME=/home/buildfarm' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/repositories:/tmp/repositories' +
        ' -v $WORKSPACE/generated_documentation:/tmp/generated_documentation' +
        ' doc_independent',
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ -d "$WORKSPACE/generated_documentation/independent/api" ]; then',
        '  echo "# BEGIN SECTION: rsync API documentation to server"',
        '  ssh jenkins-slave@repo "mkdir -p /var/repos/docs/independent/api"',
        '  cd $WORKSPACE/generated_documentation/independent/api',
        '  for pkg_name in $(find . -maxdepth 1 -mindepth 1 -type d); do',
        '    rsync -e "ssh -o StrictHostKeyChecking=no" -r --delete $pkg_name/html jenkins-slave@repo:/var/repos/docs/independent/api/$pkg_name',
        '  done',
        '  echo "# END SECTION"',
        'fi',
    ]),
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=[],
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_id=credential_id,
))@
  </buildWrappers>
</project>


only add the entry when its not already there

@[if os_name == 'ubuntu']@
@{
from itertools import product
}@
@[  if arch in ['amd64', 'i386']]@
@{
archive_commands = []
old_releases_commands = []
for distribution, archive_type in product((os_code_name, os_code_name + '-updates', os_code_name + '-security'), ('deb', 'deb-src')):
    archive_entry = '%s http://archive.ubuntu.com/ubuntu/ %s multiverse' % (archive_type, distribution)
    old_releases_entry = '%s http://old-releases.ubuntu.com/ubuntu/ %s multiverse' % (archive_type, distribution)
    archive_commands.append('(grep -q -F -x -e "%s" /etc/apt/sources.list || echo "%s" >> /etc/apt/sources.list)' % (archive_entry, archive_entry))
    old_releases_commands.append('(grep -q -F -x -e "%s" /etc/apt/sources.list || echo "%s" >> /etc/apt/sources.list)' % (old_releases_entry, old_releases_entry))
}@
RUN grep -q -F -e "deb http://old-releases.ubuntu.com" /etc/apt/sources.list && (@(' && '.join(old_releases_commands))) || (@(' && '.join(archive_commands)))
@[  elif arch in ['armhf', 'armv8']]@
@{
commands = []
for distribution, archive_type in product((os_code_name, os_code_name + '-updates', os_code_name + '-security'), ('deb', 'deb-src')):
    entry = '%s http://ports.ubuntu.com// %s multiverse' % (archive_type, distribution)
    commands.append('(grep -q -F -x -e "%s" /etc/apt/sources.list || echo "%s" >> /etc/apt/sources.list)' % (entry, entry))
}@
RUN @(' && '.join(commands))
@[  end if]@
@[else if os_name == 'debian']@
# Add contrib and non-free to debian images
# Using httpredir here to match mirror used in osrf image
# (https://github.com/osrf/multiarch-docker-image-generation/blob/d251b9a/build-image.sh#L46)
@{
entry = 'deb http://httpredir.debian.org/debian %s contrib non-free' % os_code_name
}@
RUN grep -q -F -x -e "@(entry)" /etc/apt/sources.list || echo "@(entry)" >> /etc/apt/sources.list
# Hit cloudfront mirror because of corrupted packages on fastly mirrors (https://github.com/ros-infrastructure/ros_buildfarm/issues/455)
# You can remove this line to target the default mirror or replace this to use the mirror of your preference
RUN sed -i 's/httpredir.debian.org/cloudfront.debian.net/' /etc/apt/sources.list
@[end if]@

@[if os_name == 'ubuntu']@
@{
from itertools import product
}@
@[  if arch in ['amd64', 'i386']]@
@{
archive_commands = []
old_releases_commands = []
for distribution, archive_type in product((os_code_name, os_code_name + '-updates', os_code_name + '-security'), ('deb', 'deb-src')):
    archive_entry = '%s http://archive.ubuntu.com/ubuntu/ %s multiverse' % (archive_type, distribution)
    old_releases_entry = '%s http://old-releases.ubuntu.com/ubuntu/ %s multiverse' % (archive_type, distribution)
    archive_commands.append('echo "%s" >> /etc/apt/sources.list' % archive_entry)
    old_releases_commands.append('echo "%s" >> /etc/apt/sources.list' % old_releases_entry)
}@
RUN grep -q -F -e "deb http://old-releases.ubuntu.com" /etc/apt/sources.list && (@(' && '.join(old_releases_commands))) || (@(' && '.join(archive_commands)))
@[  elif arch in ['armhf', 'armv8']]@
@{
commands = []
for distribution, archive_type in product((os_code_name, os_code_name + '-updates', os_code_name + '-security'), ('deb', 'deb-src')):
    entry = '%s http://ports.ubuntu.com// %s multiverse' % (archive_type, distribution)
    commands.append('echo "%s" >> /etc/apt/sources.list' % entry)
}@
RUN @(' && '.join(commands))
@[  end if]@
@[else if os_name == 'debian']@
# Add contrib and non-free to debian images
# Using httpredir here to match mirror used in osrf image
# (https://github.com/osrf/multiarch-docker-image-generation/blob/d251b9a/build-image.sh#L46)
@{
entry = 'deb http://httpredir.debian.org/debian %s contrib non-free' % os_code_name
}@
RUN echo "@(entry)" | tee -a /etc/apt/sources.list
# Hit cloudfront mirror because of corrupted packages on fastly mirrors (https://github.com/ros-infrastructure/ros_buildfarm/issues/455)
# You can remove this line to target the default mirror or replace this to use the mirror of your preference
RUN sed -i 's/httpredir.debian.org/cloudfront.debian.net/' /etc/apt/sources.list
@[end if]@


Some sizing rework.

Some sizing rework.

${
import math

def my_floor(num):
    return int(math.floor(num))

def my_ceil(num):
    return int(math.ceil(num))

# Should we set the line width in the engine to 2.25, and draw non pixel aligned lines?
# Are these already the correct sizes for the XO?

# These sizes need to be sanity checked ...
if theme == "sugar-xo":
    line_width = 2          # 2.25
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    bullet_size = 9
else:
    line_width = 1
    thick_line_width = 2
    subcell_size = 7
    bullet_size = 4

radio_size = my_floor(subcell_size + bullet_size + line_width)
scale_slider_width = my_floor(2 * subcell_size + line_width)
thickness = my_ceil(line_width)

}
style "default"
{
    # Will be overriden in some widgets
    color["focus_line"] = "#FFFFFF"

    bg[NORMAL] = "#808080"
    bg[PRELIGHT] = "#808080"
    bg[ACTIVE] = "#ffffff"
    bg[SELECTED] = "#e7e7e7"

    fg[NORMAL]        = "#000000"
    fg[ACTIVE]        = "#000000"
    fg[SELECTED]      = "#ffffff"
    fg[PRELIGHT]      = "#ffffff"

    text[NORMAL] = "#000000" # "#7f7f7f" 1. need to patch GTK+ to use ACTIVE when focused, 2. what color should this really be?
    text[ACTIVE] = "#000000"
    text[SELECTED] = "#000000"
    text[PRELIGHT] = "#000000"

    base[NORMAL] = "#E6E6E6"
    base[ACTIVE] = "#ffffff"
    base[SELECTED] = "#a6a6a6"
    base[PRELIGHT] = "#ffffff"

    fg[INSENSITIVE] = "#808080"
    text[INSENSITIVE] = "#808080"

    xthickness = $thickness
    ythickness = $thickness

    # A lot of these will probably need to be changed, but this has to
    # be done when the exact sizes are known
    GtkWidget::interior-focus = 0
    GtkWidget::focus-line-width = 1   # This does not do anything really
    GtkWidget::focus-padding = 0

    GtkWidget::wide-separators = 1
    GtkWidget::separator-height = $thickness
    GtkWidget::separator-width = $thickness

    GtkRange::activate-slider = 1
    GtkButton::inner-border = { 6, 6, 6, 6 }           # ??

    GtkButton::default-border = { 1, 1, 1, 1 }         # line_width - focus-line-width
    GtkButton::default-outside-border = { 2, 2, 2, 2 } # focus-line-width

    GtkScrolledWindow::scrollbar-spacing = 0

    GtkExpander::expander-size = 24
    GtkExpander::expander-spacing = 2     # XXX

    GtkTreeView::expander-size = 24

    GtkArrow::arrow-size = 1.0

    engine "sugar" {
        # Is this correct? Should we make sure it is pixel aligned?
        # 6 pixel for the XO version, and 3 for the "normal" one
        line_width = $line_width
        # 9-10 pixel for the XO version, and 4 for the "normal" one
        thick_line_width = $thick_line_width

        max_radius = 20.0
    }
}

style "window-content"
{
    color["bg_color"] = "#C0C0C0"

    bg[INSENSITIVE] = @bg_color
    base[INSENSITIVE] = @bg_color
}

style "window"
{
    color["bg_color"] = "#C0C0C0"

    bg[NORMAL] = @bg_color
}

style "scrollbar"
{
    GtkRange::slider-width = 10    # ?
    GtkRange::trough-border = 3    # line_width
    GtkRange::stepper-spacing = 0
    GtkScrollbar::min-slider-length = 30    # ?
    GtkScrollbar::has-forward-stepper = 0
    GtkScrollbar::has-backward-stepper = 0
    GtkScrollbar::has-secondary-forward-stepper = 0
    GtkScrollbar::has-secondary-backward-stepper = 0

    # Hack for mozilla as it does not obey the above style properties ...
    # Really, mozilla should be fixed ...
    GtkRange::stepper-size = 1

    engine "sugar" {
        hint = "scrollbar"
    }
}

style "scale"
{
    GtkWidget::focus-line-width = 0

    # "square" slider (really round of course)
    # Same as indicator-size?
    GtkRange::slider-width = $scale_slider_width
    GtkScale::slider-length = $scale_slider_width
    GtkRange::trough-border = 0
    GtkRange::stepper-spacing = 0
    GtkRange::trough-side-details = 1
}

# hints for the trough sizing hacks
style "hscale" = "scale"
{
    engine "sugar" {
        hint = "hscale"
    }
}

style "vscale" = "scale"
{
    engine "sugar" {
        hint = "vscale"
    }
}

style "spinbutton"
{
    fg[NORMAL]        = "#ffffff"
    engine "sugar" {
        hint = "spinbutton"
    }
}

style "comboboxentry"
{
    engine "sugar" {
        hint = "comboboxentry"
    }
}

style "frame"
{
    bg[NORMAL] = "#404040"
}

style "notebook"
{
    GtkNotebook::tab-overlap = -2

    xthickness = 0
    ythickness = 0
}

style "toolbutton"
{
    color["focus_line"] = "#000000"

    # This makes the normal background of comboboxes invisible
    bg[NORMAL] = "#404040"
    bg[PRELIGHT] = "#000000"

    fg[NORMAL] = "#FFFFFF"
    fg[ACTIVE] = "#FFFFFF"
    fg[PRELIGHT] = "#ffffff"

    # Work around GTK+ bug #382646
    text[NORMAL] = "#FFFFFF"
    text[ACTIVE] = "#FFFFFF"
    text[PRELIGHT] = "#ffffff"

    engine "sugar" {
        max_radius = 5.0
    }
}

style "toolbox"
{
    GtkToolbar::shadow-type = GTK_SHADOW_NONE

    color["bg_color"] = "#404040"

    bg[ACTIVE] = "#808080"
    bg[NORMAL] = @bg_color
    bg[INSENSITIVE] = @bg_color

    fg[NORMAL] = "#FFFFFF"
    fg[ACTIVE] = "#FFFFFF"
}

style "panel"
{
    bg[NORMAL] = "#C0C0C0"
    fg[NORMAL] = "#000000"
}

style "entry"
{
    # small inner border and a large x/ythickness for entries
    # to reduce the number of hacks needed :-)
    # This size needs to be choosen so the round corner work with the height
    # Something like (height / 2.0 - line_width) / sqrt(2), where height
    # is font height + x/ythickness
    xthickness = 7
    ythickness = 7

    GtkEntry::inner-border = { 0, 0, 0, 0 }

    GtkWidget::focus-line-width = 0
}

style "button"
{
    fg[NORMAL]        = "#ffffff"
}

style "checkbutton"
{
    fg[NORMAL]        = "#000000"
    fg[PRELIGHT]      = "#000000"
    fg[ACTIVE]        = "#000000"

    base[NORMAL]      = "#ffffff"
    base[PRELIGHT]    = "#ffffff"
    base[ACTIVE]      = "#E6E6E6"

    text[PRELIGHT]    = "#5d5d5d"
    text[NORMAL]      = "#5d5d5d"
    text[ACTIVE]      = "#5d5d5d"

    # I won't get the exact sizing, but this should be pretty close
    GtkCheckButton::indicator-size = $radio_size
    GtkCheckButton::indicator-spacing = 3
    GtkWidget::focus-padding = 3
}

style "progressbar"
{
    xthickness = 0
    ythickness = 0

    # Make the bar visible (only until the engine implements it!)
    bg[PRELIGHT] = "#ffffff"
}

style "menu"
{
    color["bg_color"] = "#000000"

    fg[NORMAL]   = "#ffffff"
    fg[PRELIGHT] = "#ffffff"
    bg[NORMAL] = "#808080"

    # The following colors are for the check and radio menu items
    base[NORMAL]      = "#ffffff"
    base[PRELIGHT]    = "#ffffff"
    base[ACTIVE]      = "#E6E6E6"

    text[PRELIGHT]    = "#5d5d5d"
    text[NORMAL]      = "#5d5d5d"
    text[ACTIVE]      = "#5d5d5d"

    #GtkMenu::horizontal-padding = 0
    #GtkMenu::vertical-padding   = 0

    xthickness = 0
    #ythickness = 0
}

style "menuitem"
{
    GtkCheckMenuItem::indicator-size = $radio_size

    bg[PRELIGHT] = "#808080"

    # For check/radio menu items
    text[NORMAL] = "#FFFFFF"
    text[PRELIGHT] = "#FFFFFF"

    #GtkMenuItem::horizontal-padding = 0
    #xthickness = 0
}

style "separatormenuitem"
{
    GtkMenuItem::horizontal-padding = 0
    xthickness = 0
}

class "GtkWidget"      style "default"
class "GtkWindow"      style "window"
class "GtkEventBox"    style "window"
class "GtkNotebook"    style "notebook"
class "GtkEntry"       style "entry"
class "GtkSpinButton"  style "spinbutton"
class "GtkScrollbar"   style "scrollbar"
class "GtkHScale"      style "hscale"
class "GtkVScale"      style "vscale"
class "GtkProgressBar" style "progressbar"

widget_class "<GtkWindow>.*"      style "window-content"
widget_class "*<GtkComboBoxEntry>*" style "comboboxentry"
widget_class "*<GtkCombo>*" style "comboboxentry"
widget_class "*<SugarToolbox>*" style "toolbox"
widget_class "*<GtkToolButton>*" style "toolbutton"
widget_class "*<GtkToolItem>*<GtkComboBox>*" style "toolbutton"
widget_class "*<GtkToolItem>*<GtkButton>*" style "toolbutton"
widget_class "*<GtkMenu>*" style "menu"
widget_class "*<GtkMenuItem>*" style "menuitem"
widget_class "*<GtkSeparatorMenuItem>*" style "menuitem"

widget_class "*<GtkButton>*" style "button"
widget_class "*<GtkCheckButton>*" style "checkbutton"

widget_class "*<SugarPanel>" style "panel"

widget_class "*<SugarFrameWindow>*" style "frame"

${
import math

# Should we set the line width in the engine to 2.25, and draw non pixel aligned lines?
# Are these already the correct sizes for the XO?

# These sizes need to be sanity checked ...

line_width = 2        # 2.25
thick_line_width = 4  # 3.5
subcell_size = 15
bullet_size = 9
}
${
radio_size = math.floor(subcell_size + bullet_size + line_width)
scale_slider_width = math.floor(2 * subcell_size + line_width)

}
style "default"
{
    # Will be overriden in some widgets
    color["focus_line"] = "#FFFFFF"

    bg[NORMAL] = "#808080"
    bg[PRELIGHT] = "#808080"
    bg[ACTIVE] = "#ffffff"
    bg[SELECTED] = "#e7e7e7"

    fg[NORMAL]        = "#000000"
    fg[ACTIVE]        = "#000000"
    fg[SELECTED]      = "#ffffff"
    fg[PRELIGHT]      = "#ffffff"

    text[NORMAL] = "#000000" # "#7f7f7f" 1. need to patch GTK+ to use ACTIVE when focused, 2. what color should this really be?
    text[ACTIVE] = "#000000"
    text[SELECTED] = "#000000"
    text[PRELIGHT] = "#000000"

    base[NORMAL] = "#E6E6E6"
    base[ACTIVE] = "#ffffff"
    base[SELECTED] = "#a6a6a6"
    base[PRELIGHT] = "#ffffff"

    fg[INSENSITIVE] = "#808080"
    text[INSENSITIVE] = "#808080"

    xthickness = $line_width
    ythickness = $line_width

    # A lot of these will probably need to be changed, but this has to
    # be done when the exact sizes are known
    GtkWidget::interior-focus = 0
    GtkWidget::focus-line-width = 1   # This does not do anything really
    GtkWidget::focus-padding = 0

    GtkWidget::wide-separators = 1
    GtkWidget::separator-height = $line_width
    GtkWidget::separator-width = $line_width

    GtkRange::activate-slider = 1
    GtkButton::inner-border = { 6, 6, 6, 6 }           # ??

    GtkButton::default-border = { 1, 1, 1, 1 }         # line_width - focus-line-width
    GtkButton::default-outside-border = { 2, 2, 2, 2 } # focus-line-width

    GtkScrolledWindow::scrollbar-spacing = 0

    GtkCheckMenuItem::indicator-size = $radio_size
    GtkExpander::expander-size = 24
    GtkExpander::expander-spacing = 2     # XXX

    GtkTreeView::expander-size = 24

    engine "sugar" {
        # Is this correct? Should we make sure it is pixel aligned?
        # 6 pixel for the XO version, and 3 for the "normal" one
        line_width = $line_width
        # 9-10 pixel for the XO version, and 4 for the "normal" one
        thick_line_width = $thick_line_width

        max_radius = 20.0
    }
}

style "window-content"
{
    color["bg_color"] = "#C0C0C0"

    bg[INSENSITIVE] = @bg_color
    base[INSENSITIVE] = @bg_color
}

style "window"
{
    color["bg_color"] = "#C0C0C0"

    bg[NORMAL] = @bg_color
}

style "scrollbar"
{
    GtkRange::slider-width = 10    # ?
    GtkRange::trough-border = 3    # line_width
    GtkRange::stepper-spacing = 0
    GtkScrollbar::min-slider-length = 30    # ?
    GtkScrollbar::has-forward-stepper = 0
    GtkScrollbar::has-backward-stepper = 0
    GtkScrollbar::has-secondary-forward-stepper = 0
    GtkScrollbar::has-secondary-backward-stepper = 0

    # Hack for mozilla as it does not obey the above style properties ...
    # Really, mozilla should be fixed ...
    GtkRange::stepper-size = 1

    engine "sugar" {
        hint = "scrollbar"
    }
}

style "scale"
{
    GtkWidget::focus-line-width = 0

    # "square" slider (really round of course)
    # Same as indicator-size?
    GtkRange::slider-width = $scale_slider_width
    GtkScale::slider-length = $scale_slider_width
    GtkRange::trough-border = 0
    GtkRange::stepper-spacing = 0
    GtkRange::trough-side-details = 1
}

# hints for the trough sizing hacks
style "hscale" = "scale"
{
    engine "sugar" {
        hint = "hscale"
    }
}

style "vscale" = "scale"
{
    engine "sugar" {
        hint = "vscale"
    }
}

style "spinbutton"
{
    fg[NORMAL]        = "#ffffff"
    engine "sugar" {
        hint = "spinbutton"
    }
}

style "comboboxentry"
{
    engine "sugar" {
        hint = "comboboxentry"
    }
}

style "frame"
{
    bg[NORMAL] = "#404040"
}

style "notebook"
{
    GtkNotebook::tab-overlap = -2

    xthickness = 0
    ythickness = 0
}

style "toolbutton"
{
    color["focus_line"] = "#000000"

    # This makes the normal background of comboboxes invisible
    bg[NORMAL] = "#404040"
    bg[PRELIGHT] = "#000000"

    fg[NORMAL] = "#FFFFFF"
    fg[ACTIVE] = "#FFFFFF"
    fg[PRELIGHT] = "#ffffff"

    # Work around GTK+ bug #382646
    text[NORMAL] = "#FFFFFF"
    text[ACTIVE] = "#FFFFFF"
    text[PRELIGHT] = "#ffffff"

    engine "sugar" {
        max_radius = 5.0
    }
}

style "toolbox"
{
    GtkToolbar::shadow-type = GTK_SHADOW_NONE

    color["bg_color"] = "#404040"

    bg[ACTIVE] = "#808080"
    bg[NORMAL] = @bg_color
    bg[INSENSITIVE] = @bg_color

    fg[NORMAL] = "#FFFFFF"
    fg[ACTIVE] = "#FFFFFF"
}

style "panel"
{
    bg[NORMAL] = "#C0C0C0"
    fg[NORMAL] = "#000000"
}

style "entry"
{
    # small inner border and a large x/ythickness for entries
    # to reduce the number of hacks needed :-)
    # This size needs to be choosen so the round corner work with the height
    # Something like (height / 2.0 - line_width) / sqrt(2), where height
    # is font height + x/ythickness
    xthickness = 7
    ythickness = 7

    GtkEntry::inner-border = { 0, 0, 0, 0 }

    GtkWidget::focus-line-width = 0
}

style "button"
{
    fg[NORMAL]        = "#ffffff"
}

style "checkbutton"
{
    fg[NORMAL]        = "#000000"
    fg[PRELIGHT]      = "#000000"
    fg[ACTIVE]        = "#000000"

    base[NORMAL]      = "#ffffff"
    base[PRELIGHT]    = "#ffffff"
    base[ACTIVE]      = "#E6E6E6"

    text[PRELIGHT]    = "#5d5d5d"
    text[NORMAL]      = "#5d5d5d"
    text[ACTIVE]      = "#5d5d5d"

    # I won't get the exact sizing, but this should be pretty close
    GtkCheckButton::indicator-size = $radio_size
    GtkCheckButton::indicator-spacing = 3
    GtkWidget::focus-padding = 3
}

style "progressbar"
{
    xthickness = 0
    ythickness = 0

    # Make the bar visible (only until the engine implements it!)
    bg[PRELIGHT] = "#ffffff"
}

style "menu"
{
    color["bg_color"] = "#000000"

    fg[NORMAL]   = "#ffffff"
    fg[PRELIGHT] = "#ffffff"
    bg[NORMAL] = "#808080"

    # The following colors are for the check and radio menu items
    base[NORMAL]      = "#ffffff"
    base[PRELIGHT]    = "#ffffff"
    base[ACTIVE]      = "#E6E6E6"

    text[PRELIGHT]    = "#5d5d5d"
    text[NORMAL]      = "#5d5d5d"
    text[ACTIVE]      = "#5d5d5d"

    #GtkMenu::horizontal-padding = 0
    #GtkMenu::vertical-padding   = 0

    xthickness = 0
    #ythickness = 0
}

style "menuitem"
{
    bg[PRELIGHT] = "#808080"

    text[NORMAL] = "#FFFFFF"
    text[PRELIGHT] = "#FFFFFF"

    #GtkMenuItem::horizontal-padding = 0
    xthickness = 0
}

class "GtkWidget"      style "default"
class "GtkWindow"      style "window"
class "GtkEventBox"    style "window"
class "GtkNotebook"    style "notebook"
class "GtkEntry"       style "entry"
class "GtkSpinButton"  style "spinbutton"
class "GtkScrollbar"   style "scrollbar"
class "GtkHScale"      style "hscale"
class "GtkVScale"      style "vscale"
class "GtkProgressBar" style "progressbar"

widget_class "<GtkWindow>.*"      style "window-content"
widget_class "*<GtkComboBoxEntry>*" style "comboboxentry"
widget_class "*<GtkCombo>*" style "comboboxentry"
widget_class "*<SugarToolbox>*" style "toolbox"
widget_class "*<GtkToolButton>*" style "toolbutton"
widget_class "*<GtkToolItem>*<GtkComboBox>*" style "toolbutton"
widget_class "*<GtkToolItem>*<GtkButton>*" style "toolbutton"
widget_class "*<GtkMenu>*" style "menu"
widget_class "*<GtkMenuItem>*" style "menuitem"

widget_class "*<GtkButton>*" style "button"
widget_class "*<GtkCheckButton>*" style "checkbutton"

widget_class "*<SugarPanel>" style "panel"

widget_class "*<SugarFrameWindow>*" style "frame"