microRTPS: client: fix send per allocated topic

@###############################################
@#
@# EmPy template for generating microRTPS_client.cpp file
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - msgs (List) list of all msg files
@#  - multi_topics (List) list of all multi-topic names
@#  - ids (List) list of all RTPS msg ids
@###############################################
@{
import os

import genmsg.msgs

from px_generate_uorb_topic_helper import * # this is in Tools/
from px_generate_uorb_topic_files import MsgScope # this is in Tools/

topic_names = [s.short_name for s in spec]
send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
send_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
receive_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
}@
/****************************************************************************
 *
 * Copyright (c) 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
 * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "microRTPS_transport.h"
#include "microRTPS_client.h"

#include <inttypes.h>
#include <cstdio>
#include <ctime>
#include <pthread.h>

#include <ucdr/microcdr.h>
#include <px4_time.h>
#include <uORB/uORB.h>

#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
@[for topic in list(set(topic_names))]@
#include <uORB/topics/@(topic).h>
#include <uORB_microcdr/topics/@(topic).h>
@[end for]@

void *send(void *data);

uint8_t last_remote_msg_seq = 0;
uint8_t last_msg_seq = 0;

@[if recv_topics]@
// Publishers for received messages
struct RcvTopicsPubs {
@[    for idx, topic in enumerate(recv_topics)]@
	uORB::Publication <@(receive_base_types[idx])_s> @(topic)_pub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
// Subscribers for messages to send
struct SendTopicsSubs {
@[    for idx, topic in enumerate(send_topics)]@
	uORB::Subscription @(topic)_sub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
void *send(void * /*unused*/)
{
	char data_buffer[BUFFER_SIZE] = {};
	uint64_t sent = 0, total_sent = 0;
	int loop = 0, read = 0;
	uint32_t length = 0;
	size_t header_length = 0;
	SendTopicsSubs *subs = new SendTopicsSubs();

	// ucdrBuffer to serialize using the user defined buffer
	ucdrBuffer writer;
	header_length = transport_node->get_header_length();
	ucdr_init_buffer(&writer, reinterpret_cast<uint8_t *>(&data_buffer[header_length]), BUFFER_SIZE - header_length);

	struct timespec begin;
	px4_clock_gettime(CLOCK_REALTIME, &begin);

	while (!_should_exit_task) {
@[    for idx, topic in enumerate(send_topics)]@
		{
			@(send_base_types[idx])_s @(topic)_data;

			if (subs->@(topic)_sub.update(&@(topic)_data))
			{
@[        if topic == 'Timesync' or topic == 'timesync']@
				if (@(topic)_data.sys_id == 0 && @(topic)_data.seq != last_remote_msg_seq && @(topic)_data.tc1 == 0) {
					last_remote_msg_seq = @(topic)_data.seq;

					@(topic)_data.timestamp = hrt_absolute_time();
					@(topic)_data.sys_id = 1;
					@(topic)_data.seq = last_msg_seq;
					@(topic)_data.tc1 = hrt_absolute_time() * 1000ULL;
					@(topic)_data.ts1 = @(topic)_data.ts1;

					last_msg_seq++;
@[        end if]@
					// copy raw data into local buffer. Payload is shifted by header length to make room for header
					serialize_@(send_base_types[idx])(&writer, &@(topic)_data, &data_buffer[header_length], &length);

					if (0 < (read = transport_node->write(static_cast<char>(@(rtps_message_id(ids, topic))), data_buffer, length))) {
						total_sent += read;
						++sent;
					}

@[        if topic == 'Timesync' or topic == 'timesync']@
				}

@[        end if]@
			}
		}
@[    end for]@
		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

	struct timespec end;

	px4_clock_gettime(CLOCK_REALTIME, &end);

	double elapsed_secs = end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9;

	PX4_INFO("SENT: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - %.02fKB/s",
		 sent, loop, total_sent, elapsed_secs, total_sent / (1e3 * elapsed_secs));

	delete subs;

	return nullptr;
}

static int launch_send_thread(pthread_t &sender_thread)
{
	pthread_attr_t sender_thread_attr;
	pthread_attr_init(&sender_thread_attr);
	pthread_attr_setstacksize(&sender_thread_attr, PX4_STACK_ADJUSTED(2250));
	struct sched_param param;
	(void)pthread_attr_getschedparam(&sender_thread_attr, &param);
	param.sched_priority = SCHED_PRIORITY_DEFAULT;
	(void)pthread_attr_setschedparam(&sender_thread_attr, &param);
	pthread_create(&sender_thread, &sender_thread_attr, send, nullptr);

	if (pthread_setname_np(sender_thread, "micrortps_client_send")) {
		PX4_ERR("Could not set pthread name (%d)", errno);
	}

	pthread_attr_destroy(&sender_thread_attr);

	return 0;
}
@[end if]@

void micrortps_start_topics(struct timespec &begin, uint64_t &total_read, uint64_t &received, int &loop)
{
@[if recv_topics]@
	char data_buffer[BUFFER_SIZE] = {};
	int read = 0;
	uint8_t topic_ID = 255;
	RcvTopicsPubs *pubs = new RcvTopicsPubs();

	// Set the main task name to 'micrortps_client_rcv' in case there is
	// data to receive
	px4_prctl(PR_SET_NAME, "micrortps_client_rcv", px4_getpid());

	// ucdrBuffer to deserialize using the user defined buffer
	ucdrBuffer reader;
	ucdr_init_buffer(&reader, reinterpret_cast<uint8_t *>(data_buffer), BUFFER_SIZE);
@[end if]@

	px4_clock_gettime(CLOCK_REALTIME, &begin);
	_should_exit_task = false;
@[if send_topics]@

	// create a thread for sending data to the simulator
	pthread_t sender_thread;
	launch_send_thread(sender_thread);
@[end if]@

	while (!_should_exit_task) {
@[if recv_topics]@
		while (0 < (read = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE))) {
			total_read += read;
			uint64_t read_time = hrt_absolute_time();

			switch (topic_ID) {
@[    for idx, topic in enumerate(recv_topics)]@
			case @(rtps_message_id(ids, topic)): {
				@(receive_base_types[idx])_s @(topic)_data;
				deserialize_@(receive_base_types[idx])(&reader, &@(topic)_data, data_buffer);

				if (@(topic)_data.timestamp > read_time) {
					// don't allow timestamps from the future
					@(topic)_data.timestamp = read_time;
				}

				pubs->@(topic)_pub.publish(@(topic)_data);
				++received;
			}
			break;
@[    end for]@
			default:
				PX4_WARN("Unexpected topic ID '%hhu' to getMsg. Please make sure the client is capable of parsing the message associated to the topic ID '%hhu'",
					 topic_ID, topic_ID);
				break;
			}
		}
@[end if]@

		// loop forever if informed loop number is negative
		if (_options.loops >= 0 && loop >= _options.loops) { break; }

		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

@[if recv_topics]@
	delete pubs;
@[end if]@
@[if send_topics]@
	_should_exit_task = true;
	pthread_join(sender_thread, nullptr);
@[end if]@
}

@###############################################
@#
@# EmPy template for generating microRTPS_client.cpp file
@#
@###############################################
@# Start of Template
@#
@# Context:
@#  - msgs (List) list of all msg files
@#  - multi_topics (List) list of all multi-topic names
@#  - ids (List) list of all RTPS msg ids
@###############################################
@{
import os

import genmsg.msgs

from px_generate_uorb_topic_helper import * # this is in Tools/
from px_generate_uorb_topic_files import MsgScope # this is in Tools/

topic_names = [s.short_name for s in spec]
send_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
send_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.SEND]
recv_topics = [(alias[idx] if alias[idx] else s.short_name) for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
receive_base_types = [s.short_name for idx, s in enumerate(spec) if scope[idx] == MsgScope.RECEIVE]
}@
/****************************************************************************
 *
 * Copyright (c) 2017 Proyectos y Sistemas de Mantenimiento SL (eProsima).
 * Copyright (c) 2018-2021 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#include "microRTPS_transport.h"
#include "microRTPS_client.h"

#include <inttypes.h>
#include <cstdio>
#include <ctime>
#include <pthread.h>

#include <ucdr/microcdr.h>
#include <px4_time.h>
#include <uORB/uORB.h>

#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
@[for topic in list(set(topic_names))]@
#include <uORB/topics/@(topic).h>
#include <uORB_microcdr/topics/@(topic).h>
@[end for]@

void *send(void *data);

uint8_t last_remote_msg_seq = 0;
uint8_t last_msg_seq = 0;

@[if recv_topics]@
// Publishers for received messages
struct RcvTopicsPubs {
@[    for idx, topic in enumerate(recv_topics)]@
	uORB::Publication <@(receive_base_types[idx])_s> @(topic)_pub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
// Subscribers for messages to send
struct SendTopicsSubs {
@[    for idx, topic in enumerate(send_topics)]@
	uORB::Subscription @(topic)_sub{ORB_ID(@(topic))};
@[    end for]@
};
@[end if]@

@[if send_topics]@
void *send(void * /*unused*/)
{
	char data_buffer[BUFFER_SIZE] = {};
	uint64_t sent = 0, total_sent = 0;
	int loop = 0, read = 0;
	uint32_t length = 0;
	size_t header_length = 0;
	SendTopicsSubs *subs = new SendTopicsSubs();

	// ucdrBuffer to serialize using the user defined buffer
	ucdrBuffer writer;
	header_length = transport_node->get_header_length();
	ucdr_init_buffer(&writer, reinterpret_cast<uint8_t *>(&data_buffer[header_length]), BUFFER_SIZE - header_length);

	struct timespec begin;
	px4_clock_gettime(CLOCK_REALTIME, &begin);

	while (!_should_exit_task) {
@[    for idx, topic in enumerate(send_topics)]@
		@(send_base_types[idx])_s @(topic)_data;

		if (subs->@(topic)_sub.update(&@(topic)_data))
		{
@[        if topic == 'Timesync' or topic == 'timesync']@
			if (@(topic)_data.sys_id == 0 && @(topic)_data.seq != last_remote_msg_seq && @(topic)_data.tc1 == 0) {
				last_remote_msg_seq = @(topic)_data.seq;

				@(topic)_data.timestamp = hrt_absolute_time();
				@(topic)_data.sys_id = 1;
				@(topic)_data.seq = last_msg_seq;
				@(topic)_data.tc1 = hrt_absolute_time() * 1000ULL;
				@(topic)_data.ts1 = @(topic)_data.ts1;

				last_msg_seq++;
@[        end if]@
			// copy raw data into local buffer. Payload is shifted by header length to make room for header
			serialize_@(send_base_types[idx])(&writer, &@(topic)_data, &data_buffer[header_length], &length);

			if (0 < (read = transport_node->write(static_cast<char>(@(rtps_message_id(ids, topic))), data_buffer, length))) {
				total_sent += read;
				++sent;
			}

@[        if topic == 'Timesync' or topic == 'timesync']@
			}

@[        end if]@
		}
@[    end for]@
		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

	struct timespec end;

	px4_clock_gettime(CLOCK_REALTIME, &end);

	double elapsed_secs = end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1e9;

	PX4_INFO("SENT: %" PRIu64 " messages in %d LOOPS, %" PRIu64 " bytes in %.03f seconds - %.02fKB/s",
		 sent, loop, total_sent, elapsed_secs, total_sent / (1e3 * elapsed_secs));

	delete subs;

	return nullptr;
}

static int launch_send_thread(pthread_t &sender_thread)
{
	pthread_attr_t sender_thread_attr;
	pthread_attr_init(&sender_thread_attr);
	pthread_attr_setstacksize(&sender_thread_attr, PX4_STACK_ADJUSTED(2250));
	struct sched_param param;
	(void)pthread_attr_getschedparam(&sender_thread_attr, &param);
	param.sched_priority = SCHED_PRIORITY_DEFAULT;
	(void)pthread_attr_setschedparam(&sender_thread_attr, &param);
	pthread_create(&sender_thread, &sender_thread_attr, send, nullptr);

	if (pthread_setname_np(sender_thread, "micrortps_client_send")) {
		PX4_ERR("Could not set pthread name (%d)", errno);
	}

	pthread_attr_destroy(&sender_thread_attr);

	return 0;
}
@[end if]@

void micrortps_start_topics(struct timespec &begin, uint64_t &total_read, uint64_t &received, int &loop)
{
@[if recv_topics]@
	char data_buffer[BUFFER_SIZE] = {};
	int read = 0;
	uint8_t topic_ID = 255;
	RcvTopicsPubs *pubs = new RcvTopicsPubs();

	// Set the main task name to 'micrortps_client_rcv' in case there is
	// data to receive
	px4_prctl(PR_SET_NAME, "micrortps_client_rcv", px4_getpid());

	// ucdrBuffer to deserialize using the user defined buffer
	ucdrBuffer reader;
	ucdr_init_buffer(&reader, reinterpret_cast<uint8_t *>(data_buffer), BUFFER_SIZE);
@[end if]@

	px4_clock_gettime(CLOCK_REALTIME, &begin);
	_should_exit_task = false;
@[if send_topics]@

	// create a thread for sending data to the simulator
	pthread_t sender_thread;
	launch_send_thread(sender_thread);
@[end if]@

	while (!_should_exit_task) {
@[if recv_topics]@
		while (0 < (read = transport_node->read(&topic_ID, data_buffer, BUFFER_SIZE))) {
			total_read += read;
			uint64_t read_time = hrt_absolute_time();

			switch (topic_ID) {
@[    for idx, topic in enumerate(recv_topics)]@
			case @(rtps_message_id(ids, topic)): {
				@(receive_base_types[idx])_s @(topic)_data;
				deserialize_@(receive_base_types[idx])(&reader, &@(topic)_data, data_buffer);

				if (@(topic)_data.timestamp > read_time) {
					// don't allow timestamps from the future
					@(topic)_data.timestamp = read_time;
				}

				pubs->@(topic)_pub.publish(@(topic)_data);
				++received;
			}
			break;
@[    end for]@
			default:
				PX4_WARN("Unexpected topic ID '%hhu' to getMsg. Please make sure the client is capable of parsing the message associated to the topic ID '%hhu'",
					 topic_ID, topic_ID);
				break;
			}
		}
@[end if]@

		// loop forever if informed loop number is negative
		if (_options.loops >= 0 && loop >= _options.loops) { break; }

		px4_usleep(_options.sleep_ms * 1000);
		++loop;
	}

@[if recv_topics]@
	delete pubs;
@[end if]@
@[if send_topics]@
	_should_exit_task = true;
	pthread_join(sender_thread, nullptr);
@[end if]@
}


Add wrapper for addGUITextModule in DefaultGraphics.


Free-rotate controls for avatar.

Free-rotate controls for avatar.

/*  Sirikata
 *  default.em
 *
 *  Copyright (c) 2011, Ewen Cheslack-Postava
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

system.require('graphics.em');
system.require('std/movement/pursue.em');
system.require('std/script/scripter.em');
system.require('inputbinding.em');
system.require('drag/move.em');
system.require('drag/rotate.em');
system.require('drag/scale.em');
system.require('std/graphics/chat.em');
system.require('std/graphics/physics.em');
system.require('std/graphics/propertybox.em');
system.require('std/graphics/presenceList.em');
system.require('std/graphics/setMesh.em');

(
function() {

    var ns = std.graphics;

    /** @namespace
     *  The DefaultGraphics class just contains some sane defaults for
     *  interaction, allowing you to get a decent, baseline client
     *  that only requires built-in functionality. You still define
     *  the presence and which underlying graphics system to use, but
     *  this class takes care of defining all other UI and interaction.
     */
    std.graphics.DefaultGraphics = function(pres, name, cb) {
        this._pres = pres;
        this._simulator = new std.graphics.Graphics(pres, name, std.core.bind(this.finishedGraphicsInit, this, cb), true);
    };
    std.graphics.DefaultGraphics.prototype.finishedGraphicsInit = function(cb, gfx) {
        // assert(gfx == this._simulator);
        this._cameraMode = 'first';

        this._selected = null;
        this._loadingUIs = 0;

        var ui_finish_cb = std.core.bind(this.finishedUIInit, this, cb);
        this._loadingUIs++; this._scripter = new std.script.Scripter(this, ui_finish_cb);
        this._loadingUIs++; this._chat = new std.graphics.Chat(this._pres, this._simulator, ui_finish_cb);
        this._loadingUIs++; this._physics = new std.graphics.PhysicsProperties(this._simulator, ui_finish_cb);
        this._loadingUIs++; this._propertybox = new std.propertybox.PropertyBox(this, ui_finish_cb);
        this._loadingUIs++; this._presenceList = new std.graphics.PresenceList(this._pres, this._simulator, this._scripter, ui_finish_cb);
        this._loadingUIs++; this._setMesh = new std.graphics.SetMesh(this._simulator, ui_finish_cb);
    };
    std.graphics.DefaultGraphics.prototype.finishedUIInit = function(cb) {
        this._loadingUIs--;
        if (this._loadingUIs > 0) return;

        this._simulator.hideLoadScreen();

        this._moverot = new std.movement.MoveAndRotate(this._pres, std.core.bind(this.updateCameraOffset, this), 'rotation');

        this._draggers = {
            move: new std.graphics.MoveDragHandler(this._simulator),
            rotate: new std.graphics.RotateDragHandler(this._simulator),
            scale: new std.graphics.ScaleDragHandler(this._simulator)
        };

        this._binding = new std.graphics.InputBinding();
        this._simulator.inputHandler.onAnything = std.core.bind(this._binding.dispatch, this._binding);

        this._binding.addAction('quit', std.core.bind(this._simulator.quit, this._simulator));
        this._binding.addAction('screenshot', std.core.bind(this._simulator.screenshot, this._simulator));
        this._binding.addAction('toggleSuspend', std.core.bind(this._simulator.toggleSuspend, this._simulator));
        this._binding.addAction('scriptSelectedObject', std.core.bind(this.scriptSelectedObject, this));
        this._binding.addAction('scriptSelf', std.core.bind(this.scriptSelf, this));
        this._binding.addAction('togglePropertyBox', std.core.bind(this.togglePropertyBox, this));

        this._binding.addAction('toggleChat', std.core.bind(this.toggleChat, this));

        this._binding.addAction('reorientSelf', std.core.bind(this.reorientSelf, this));

        this._binding.addAction('togglePhysicsProperties', std.core.bind(this._physics.toggle, this._physics));
        this._binding.addAction('togglePresenceList', std.core.bind(this._presenceList.toggle, this._presenceList));
        this._binding.addAction('toggleSetMesh', std.core.bind(this._setMesh.toggle, this._setMesh));

        this._binding.addAction('toggleCameraMode', std.core.bind(this.toggleCameraMode, this));

        this._binding.addAction('actOnObject', std.core.bind(this.actOnObject, this));

        this._binding.addToggleAction('moveForward', std.core.bind(this.moveSelf, this, new util.Vec3(0, 0, -1)), 1, -1);
        this._binding.addToggleAction('moveBackward', std.core.bind(this.moveSelf, this, new util.Vec3(0, 0, 1)), 1, -1);
        this._binding.addToggleAction('moveLeft', std.core.bind(this.moveSelf, this, new util.Vec3(-1, 0, 0)), 1, -1);
        this._binding.addToggleAction('moveRight', std.core.bind(this.moveSelf, this, new util.Vec3(1, 0, 0)), 1, -1);
        this._binding.addToggleAction('moveUp', std.core.bind(this.moveSelf, this, new util.Vec3(0, 1, 0)), 1, -1);
        this._binding.addToggleAction('moveDown', std.core.bind(this.moveSelf, this, new util.Vec3(0, -1, 0)), 1, -1);

        this._binding.addToggleAction('rotateUp', std.core.bind(this.rotateSelf, this, new util.Vec3(1, 0, 0)), 1, -1);
        this._binding.addToggleAction('rotateDown', std.core.bind(this.rotateSelf, this, new util.Vec3(-1, 0, 0)), 1, -1);
        this._binding.addToggleAction('rotateLeft', std.core.bind(this.rotateSelf, this, new util.Vec3(0, 1, 0)), 1, -1);
        this._binding.addToggleAction('rotateRight', std.core.bind(this.rotateSelf, this, new util.Vec3(0, -1, 0)), 1, -1);

        this._binding.addFloat2Action('pickObject', std.core.bind(this.pickObject, this));

        this._binding.addAction('updatePhysicsProperties', std.core.bind(this.updatePhysicsProperties, this));

        this._binding.addAction('startMoveDrag', std.core.bind(this.startDrag, this, this._draggers.move));
        this._binding.addAction('startRotateDrag', std.core.bind(this.startDrag, this, this._draggers.rotate));
        this._binding.addAction('startScaleDrag', std.core.bind(this.startDrag, this, this._draggers.scale));
        this._binding.addAction('forwardMousePressToDragger', std.core.bind(this.forwardMousePressToDragger, this));
        this._binding.addAction('forwardMouseDragToDragger', std.core.bind(this.forwardMouseDragToDragger, this));
        this._binding.addAction('updatePropertyBox', std.core.bind(this.updatePropertyBox, this));
        this._binding.addAction('forwardMouseReleaseToDragger', std.core.bind(this.forwardMouseReleaseToDragger, this));
        this._binding.addAction('stopDrag', std.core.bind(this.stopDrag, this));

        this._binding.addAction('startFreeRotate', std.core.bind(this.startFreeRotate, this));
        this._binding.addAction('freeRotateDrag', std.core.bind(this.freeRotateDrag, this));
        this._binding.addAction('freeRotateRelease', std.core.bind(this.freeRotateRelease, this));

        /** Bindings are an *ordered* list of keys and actions. Keys
         *  are a combination of the type of event, the primary key
         *  for the event (key or mouse button), and modifiers.
         *  Modifiers are tricky to handle. You can specify a string
         *  indicating which to filter on. If you omit the string, it
         *  is equivalent to 'none'. The special values '*' and 'any'
         *  are equivalent and will match any modifier, i.e. the event
         *  will always be triggered. There isn't support
         *  for matching combinations of modifiers.
         */
        var bindings = [
            { key: ['button-pressed', 'escape'], action: 'quit' },
            { key: ['button-pressed', 'i'], action: 'screenshot' },
            { key: ['button-pressed', 'm'], action: 'toggleSuspend' },
            { key: ['button-pressed', 's', 'alt' ], action: 'scriptSelectedObject' },
            { key: ['button-pressed', 's', 'ctrl' ], action: 'scriptSelf' },

            { key: ['button-pressed', 'c', 'ctrl' ], action: 'toggleChat' },
            { key: ['button-pressed', 'p', 'ctrl' ], action: 'togglePhysicsProperties' },
            { key: ['button-pressed', 'p', 'alt' ], action: 'togglePropertyBox' },
            { key: ['button-pressed', 'l', 'ctrl' ], action: 'togglePresenceList' },
            { key: ['button-pressed', 'j', 'ctrl' ], action: 'toggleSetMesh' },

            { key: ['button-pressed', 't'], action: 'reorientSelf' },

            { key: ['mouse-click', 2], action: 'pickObject' },
            { key: ['mouse-click', 2], action: 'scriptSelectedObject' },
            { key: ['button-pressed', 'return'], action: 'actOnObject' },

            { key: ['button-pressed', 'c' ], action: 'toggleCameraMode' },

            { key: ['button', 'w' ], action: 'moveForward' },
            { key: ['button', 'up' ], action: 'moveForward' },
            { key: ['button', 's' ], action: 'moveBackward' },
            { key: ['button', 'down' ], action: 'moveBackward' },
            { key: ['button', 'a' ], action: 'moveLeft' },
            { key: ['button', 'd' ], action: 'moveRight' },
            { key: ['button', 'q' ], action: 'moveUp' },
            { key: ['button', 'z' ], action: 'moveDown' },

            { key: ['button', 'up', 'shift' ], action: 'rotateUp' },
            { key: ['button', 'down', 'shift' ], action: 'rotateDown' },
            { key: ['button', 'left' ], action: 'rotateLeft' },
            { key: ['button', 'right' ], action: 'rotateRight' },

            { key: ['mouse-press', 1 ], action: 'pickObject' },

            { key: ['mouse-press', 1 ], action: 'updatePhysicsProperties' },

            // Note that the ordering of registration here is critical.
            { key: ['mouse-press', 1, 'none' ], action: 'startMoveDrag' },
            { key: ['mouse-press', 1, 'ctrl' ], action: 'startRotateDrag' },
            { key: ['mouse-press', 1, 'alt' ], action: 'startScaleDrag' },
            { key: ['mouse-press', 1, '*'], action: 'forwardMousePressToDragger' },
            { key: ['mouse-press', 1, '*'], action: 'updatePropertyBox' },
            { key: ['mouse-drag', 1, '*'], action: 'forwardMouseDragToDragger' },
            { key: ['mouse-release', 1, '*'], action: 'forwardMouseReleaseToDragger' },
            { key: ['mouse-release', 1, '*'], action: 'stopDrag' },
            { key: ['mouse-press', 3, 'none' ], action: 'startFreeRotate' },
            { key: ['mouse-drag', 3, '*'], action: 'freeRotateDrag' },
            { key: ['mouse-release', 3, '*'], action: 'freeRotateRelease' }
        ];

        this._binding.addBindings(bindings);

        if (cb && typeof(cb) === "function")
            cb(this);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.simulator = function() {
        return this._simulator;
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.invoke = function() {
        // Just forward manual invoke commands directly
        return this._simulator.invoke.apply(this._simulator, arguments);
    };

    /** Request that the given URL be added as a module in the UI. */
    std.graphics.DefaultGraphics.prototype.addGUIModule = function(name, url, cb) {
        return this._simulator.addGUIModule(name, url, cb);
    };

    /** Request that the given script text be added as a module in the UI. */
    std.graphics.DefaultGraphics.prototype.addGUITextModule = function(name, js_text, cb) {
        return this._simulator.addGUITextModule(name, js_text, cb);
    };

    //by default how to scale translational velocity from keypresses.  (movement
    //is agonizingly slow if just set this to 1.  I really recommend 5.)
    /** @public */
    std.graphics.DefaultGraphics.prototype.defaultVelocityScaling = 5;
    //by default how to scale rotational velocity from keypresses
    /** @public */
    std.graphics.DefaultGraphics.prototype.defaultRotationalVelocityScaling = .5;

    /** @function */
    std.graphics.DefaultGraphics.prototype.toggleChat = function() {
        this._chat.toggle();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.scriptSelf = function() {
        this._scripter.script(system.self);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.togglePropertyBox = function() {
        this._propertybox.TogglePropertyBox();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.scriptSelectedObject = function() {
        this._presenceList.addObject(this._selected.toString(), 'Scripted');
        this._scripter.script(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.toggleCameraMode = function(evt) {
        this._cameraMode = this._cameraMode == 'first' ? 'third' : 'first';
        this._simulator.setCameraMode(this._cameraMode);
        this.updateCameraOffset();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.updateCameraOffset = function(evt) {
        if (this._cameraMode == 'third') {
            var orient = this._pres.getOrientation();
            this._simulator.setCameraOffset(orient.mul(<0, 1.5, 4>));
        }
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.moveSelf = function(dir, val) {
        this._moverot.move(dir, this.defaultVelocityScaling * val);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.rotateSelf = function(about, val) {
        this._moverot.rotate(about, this.defaultRotationalVelocityScaling * val);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.pickObject = function(x, y) {
        if (this._selected) {
            this._simulator.bbox(this._selected, false);
            this._selected = null;
        }

        var clicked = this._simulator.pick(x, y);
        if (clicked) {
            this._selected = clicked;
            this._simulator.bbox(this._selected, true);
        }
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.actOnObject = function(evt) {
        if (this._selected)
            { 'action' : 'touch' } >> this._selected >> [];
    }

    /** @function */
    std.graphics.DefaultGraphics.prototype.updatePhysicsProperties = function() {
        // Update even if not selected so display can be disabled
        this._physics.update(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.startDrag = function(dragger, evt) {
        if (this._selected)
            this._dragger = dragger;
        if (this._dragger)
            this._dragger.selected(this._selected, this._simulator.pickedPosition(), evt);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.forwardMousePressToDragger = function(evt) {
        if (this._dragger) this._dragger.onMousePress(evt);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.forwardMouseDragToDragger = function(evt) {
        if (this._dragger) this._dragger.onMouseDrag(evt);
        this._propertybox.HandleUpdateProperties(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.updatePropertyBox = function(evt) {
        this._propertybox.HandleUpdateProperties(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.forwardMouseReleaseToDragger = function(evt) {
        if (this._dragger) this._dragger.onMouseRelease(evt);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.stopDrag = function() {
        delete this._dragger;
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.startFreeRotate = function(evt) {
        this.startX = evt.x;
        this.startY = evt.y;
        this.startOrientation = this._pres.getOrientation();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.freeRotateDrag = function(evt) {
        var SCALE = 1;
        var dx = evt.x - this.startX;
        var dy = evt.y - this.startY;
        var dXAngle = dx * SCALE;
        var dYAngle = dy * SCALE;
        var newQuat = this.startOrientation
            .mul(new util.Quaternion(<0, 1, 0>, -dXAngle))
            .mul(new util.Quaternion(<1, 0, 0>, dYAngle));
        this._pres.setOrientation(newQuat);
        this.updateCameraOffset();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.freeRotateRelease = function(evt) {
        this.startX = null;
        this.startY = null;
        this.startOrientation = null;
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.reorientSelf = function(evt) {
        this._pres.setOrientation(util.Quaternion.fromLookAt(
            this._pres.getOrientation().zAxis().scale(-1), <0, 1, 0>));
        this.updateCameraOffset();
    }

})();

/*  Sirikata
 *  default.em
 *
 *  Copyright (c) 2011, Ewen Cheslack-Postava
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

system.require('graphics.em');
system.require('std/movement/pursue.em');
system.require('std/script/scripter.em');
system.require('inputbinding.em');
system.require('drag/move.em');
system.require('drag/rotate.em');
system.require('drag/scale.em');
system.require('std/graphics/chat.em');
system.require('std/graphics/physics.em');
system.require('std/graphics/propertybox.em');
system.require('std/graphics/presenceList.em');
system.require('std/graphics/setMesh.em');

(
function() {

    var ns = std.graphics;

    /** @namespace
     *  The DefaultGraphics class just contains some sane defaults for
     *  interaction, allowing you to get a decent, baseline client
     *  that only requires built-in functionality. You still define
     *  the presence and which underlying graphics system to use, but
     *  this class takes care of defining all other UI and interaction.
     */
    std.graphics.DefaultGraphics = function(pres, name, cb) {
        this._pres = pres;
        this._simulator = new std.graphics.Graphics(pres, name, std.core.bind(this.finishedGraphicsInit, this, cb), true);
    };
    std.graphics.DefaultGraphics.prototype.finishedGraphicsInit = function(cb, gfx) {
        // assert(gfx == this._simulator);
        this._cameraMode = 'first';

        this._selected = null;
        this._loadingUIs = 0;

        var ui_finish_cb = std.core.bind(this.finishedUIInit, this, cb);
        this._loadingUIs++; this._scripter = new std.script.Scripter(this, ui_finish_cb);
        this._loadingUIs++; this._chat = new std.graphics.Chat(this._pres, this._simulator, ui_finish_cb);
        this._loadingUIs++; this._physics = new std.graphics.PhysicsProperties(this._simulator, ui_finish_cb);
        this._loadingUIs++; this._propertybox = new std.propertybox.PropertyBox(this, ui_finish_cb);
        this._loadingUIs++; this._presenceList = new std.graphics.PresenceList(this._pres, this._simulator, this._scripter, ui_finish_cb);
        this._loadingUIs++; this._setMesh = new std.graphics.SetMesh(this._simulator, ui_finish_cb);
    };
    std.graphics.DefaultGraphics.prototype.finishedUIInit = function(cb) {
        this._loadingUIs--;
        if (this._loadingUIs > 0) return;

        this._simulator.hideLoadScreen();

        this._moverot = new std.movement.MoveAndRotate(this._pres, std.core.bind(this.updateCameraOffset, this), 'rotation');

        this._draggers = {
            move: new std.graphics.MoveDragHandler(this._simulator),
            rotate: new std.graphics.RotateDragHandler(this._simulator),
            scale: new std.graphics.ScaleDragHandler(this._simulator)
        };

        this._binding = new std.graphics.InputBinding();
        this._simulator.inputHandler.onAnything = std.core.bind(this._binding.dispatch, this._binding);

        this._binding.addAction('quit', std.core.bind(this._simulator.quit, this._simulator));
        this._binding.addAction('screenshot', std.core.bind(this._simulator.screenshot, this._simulator));
        this._binding.addAction('toggleSuspend', std.core.bind(this._simulator.toggleSuspend, this._simulator));
        this._binding.addAction('scriptSelectedObject', std.core.bind(this.scriptSelectedObject, this));
        this._binding.addAction('scriptSelf', std.core.bind(this.scriptSelf, this));
        this._binding.addAction('togglePropertyBox', std.core.bind(this.togglePropertyBox, this));

        this._binding.addAction('toggleChat', std.core.bind(this.toggleChat, this));

        this._binding.addAction('togglePhysicsProperties', std.core.bind(this._physics.toggle, this._physics));
        this._binding.addAction('togglePresenceList', std.core.bind(this._presenceList.toggle, this._presenceList));
        this._binding.addAction('toggleSetMesh', std.core.bind(this._setMesh.toggle, this._setMesh));

        this._binding.addAction('toggleCameraMode', std.core.bind(this.toggleCameraMode, this));

        this._binding.addAction('actOnObject', std.core.bind(this.actOnObject, this));

        this._binding.addToggleAction('moveForward', std.core.bind(this.moveSelf, this, new util.Vec3(0, 0, -1)), 1, -1);
        this._binding.addToggleAction('moveBackward', std.core.bind(this.moveSelf, this, new util.Vec3(0, 0, 1)), 1, -1);
        this._binding.addToggleAction('moveLeft', std.core.bind(this.moveSelf, this, new util.Vec3(-1, 0, 0)), 1, -1);
        this._binding.addToggleAction('moveRight', std.core.bind(this.moveSelf, this, new util.Vec3(1, 0, 0)), 1, -1);
        this._binding.addToggleAction('moveUp', std.core.bind(this.moveSelf, this, new util.Vec3(0, 1, 0)), 1, -1);
        this._binding.addToggleAction('moveDown', std.core.bind(this.moveSelf, this, new util.Vec3(0, -1, 0)), 1, -1);

        this._binding.addToggleAction('rotateUp', std.core.bind(this.rotateSelf, this, new util.Vec3(1, 0, 0)), 1, -1);
        this._binding.addToggleAction('rotateDown', std.core.bind(this.rotateSelf, this, new util.Vec3(-1, 0, 0)), 1, -1);
        this._binding.addToggleAction('rotateLeft', std.core.bind(this.rotateSelf, this, new util.Vec3(0, 1, 0)), 1, -1);
        this._binding.addToggleAction('rotateRight', std.core.bind(this.rotateSelf, this, new util.Vec3(0, -1, 0)), 1, -1);

        this._binding.addFloat2Action('pickObject', std.core.bind(this.pickObject, this));

        this._binding.addAction('updatePhysicsProperties', std.core.bind(this.updatePhysicsProperties, this));

        this._binding.addAction('startMoveDrag', std.core.bind(this.startDrag, this, this._draggers.move));
        this._binding.addAction('startRotateDrag', std.core.bind(this.startDrag, this, this._draggers.rotate));
        this._binding.addAction('startScaleDrag', std.core.bind(this.startDrag, this, this._draggers.scale));
        this._binding.addAction('forwardMousePressToDragger', std.core.bind(this.forwardMousePressToDragger, this));
        this._binding.addAction('forwardMouseDragToDragger', std.core.bind(this.forwardMouseDragToDragger, this));
        this._binding.addAction('updatePropertyBox', std.core.bind(this.updatePropertyBox, this));
        this._binding.addAction('forwardMouseReleaseToDragger', std.core.bind(this.forwardMouseReleaseToDragger, this));
        this._binding.addAction('stopDrag', std.core.bind(this.stopDrag, this));

        /** Bindings are an *ordered* list of keys and actions. Keys
         *  are a combination of the type of event, the primary key
         *  for the event (key or mouse button), and modifiers.
         *  Modifiers are tricky to handle. You can specify a string
         *  indicating which to filter on. If you omit the string, it
         *  is equivalent to 'none'. The special values '*' and 'any'
         *  are equivalent and will match any modifier, i.e. the event
         *  will always be triggered. There isn't support
         *  for matching combinations of modifiers.
         */
        var bindings = [
            { key: ['button-pressed', 'escape'], action: 'quit' },
            { key: ['button-pressed', 'i'], action: 'screenshot' },
            { key: ['button-pressed', 'm'], action: 'toggleSuspend' },
            { key: ['button-pressed', 's', 'alt' ], action: 'scriptSelectedObject' },
            { key: ['button-pressed', 's', 'ctrl' ], action: 'scriptSelf' },

            { key: ['button-pressed', 'c', 'ctrl' ], action: 'toggleChat' },
            { key: ['button-pressed', 'p', 'ctrl' ], action: 'togglePhysicsProperties' },
            { key: ['button-pressed', 'p', 'alt' ], action: 'togglePropertyBox' },
            { key: ['button-pressed', 'l', 'ctrl' ], action: 'togglePresenceList' },
            { key: ['button-pressed', 'j', 'ctrl' ], action: 'toggleSetMesh' },

            { key: ['mouse-click', 3], action: 'pickObject' },
            { key: ['mouse-click', 3], action: 'scriptSelectedObject' },
            { key: ['button-pressed', 'return'], action: 'actOnObject' },

            { key: ['button-pressed', 'c' ], action: 'toggleCameraMode' },

            { key: ['button', 'w' ], action: 'moveForward' },
            { key: ['button', 'up' ], action: 'moveForward' },
            { key: ['button', 's' ], action: 'moveBackward' },
            { key: ['button', 'down' ], action: 'moveBackward' },
            { key: ['button', 'a' ], action: 'moveLeft' },
            { key: ['button', 'd' ], action: 'moveRight' },
            { key: ['button', 'q' ], action: 'moveUp' },
            { key: ['button', 'z' ], action: 'moveDown' },

            { key: ['button', 'up', 'shift' ], action: 'rotateUp' },
            { key: ['button', 'down', 'shift' ], action: 'rotateDown' },
            { key: ['button', 'left' ], action: 'rotateLeft' },
            { key: ['button', 'right' ], action: 'rotateRight' },

            { key: ['mouse-press', 1 ], action: 'pickObject' },

            { key: ['mouse-press', 1 ], action: 'updatePhysicsProperties' },

            // Note that the ordering of registration here is critical.
            { key: ['mouse-press', 1, 'none' ], action: 'startMoveDrag' },
            { key: ['mouse-press', 1, 'ctrl' ], action: 'startRotateDrag' },
            { key: ['mouse-press', 1, 'alt' ], action: 'startScaleDrag' },
            { key: ['mouse-press', 1, '*'], action: 'forwardMousePressToDragger' },
            { key: ['mouse-press', 1, '*'], action: 'updatePropertyBox' },
            { key: ['mouse-drag', 1, '*'], action: 'forwardMouseDragToDragger' },
            { key: ['mouse-release', 1, '*'], action: 'forwardMouseReleaseToDragger' },
            { key: ['mouse-release', 1, '*'], action: 'stopDrag' }
        ];

        this._binding.addBindings(bindings);

        if (cb && typeof(cb) === "function")
            cb(this);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.simulator = function() {
        return this._simulator;
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.invoke = function() {
        // Just forward manual invoke commands directly
        return this._simulator.invoke.apply(this._simulator, arguments);
    };

    /** Request that the given URL be added as a module in the UI. */
    std.graphics.DefaultGraphics.prototype.addGUIModule = function(name, url, cb) {
        return this._simulator.addGUIModule(name, url, cb);
    };

    /** Request that the given script text be added as a module in the UI. */
    std.graphics.DefaultGraphics.prototype.addGUITextModule = function(name, js_text, cb) {
        return this._simulator.addGUITextModule(name, js_text, cb);
    };

    //by default how to scale translational velocity from keypresses.  (movement
    //is agonizingly slow if just set this to 1.  I really recommend 5.)
    /** @public */
    std.graphics.DefaultGraphics.prototype.defaultVelocityScaling = 5;
    //by default how to scale rotational velocity from keypresses
    /** @public */
    std.graphics.DefaultGraphics.prototype.defaultRotationalVelocityScaling = .5;

    /** @function */
    std.graphics.DefaultGraphics.prototype.toggleChat = function() {
        this._chat.toggle();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.scriptSelf = function() {
        this._scripter.script(system.self);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.togglePropertyBox = function() {
        this._propertybox.TogglePropertyBox();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.scriptSelectedObject = function() {
        this._presenceList.addObject(this._selected.toString(), 'Scripted');
        this._scripter.script(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.toggleCameraMode = function(evt) {
        this._cameraMode = this._cameraMode == 'first' ? 'third' : 'first';
        this._simulator.setCameraMode(this._cameraMode);
        this.updateCameraOffset();
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.updateCameraOffset = function(evt) {
        if (this._cameraMode == 'third') {
            var orient = this._pres.getOrientation();
            this._simulator.setCameraOffset(orient.mul(<0, 1.5, 4>));
        }
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.moveSelf = function(dir, val) {
        this._moverot.move(dir, this.defaultVelocityScaling * val);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.rotateSelf = function(about, val) {
        this._moverot.rotate(about, this.defaultRotationalVelocityScaling * val);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.pickObject = function(x, y) {
        if (this._selected) {
            this._simulator.bbox(this._selected, false);
            this._selected = null;
        }

        var clicked = this._simulator.pick(x, y);
        if (clicked) {
            this._selected = clicked;
            this._simulator.bbox(this._selected, true);
        }
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.actOnObject = function(evt) {
        if (this._selected)
            { 'action' : 'touch' } >> this._selected >> [];
    }

    /** @function */
    std.graphics.DefaultGraphics.prototype.updatePhysicsProperties = function() {
        // Update even if not selected so display can be disabled
        this._physics.update(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.startDrag = function(dragger, evt) {
        if (this._selected)
            this._dragger = dragger;
        if (this._dragger)
            this._dragger.selected(this._selected, this._simulator.pickedPosition(), evt);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.forwardMousePressToDragger = function(evt) {
        if (this._dragger) this._dragger.onMousePress(evt);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.forwardMouseDragToDragger = function(evt) {
        if (this._dragger) this._dragger.onMouseDrag(evt);
        this._propertybox.HandleUpdateProperties(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.updatePropertyBox = function(evt) {
        this._propertybox.HandleUpdateProperties(this._selected);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.forwardMouseReleaseToDragger = function(evt) {
        if (this._dragger) this._dragger.onMouseRelease(evt);
    };

    /** @function */
    std.graphics.DefaultGraphics.prototype.stopDrag = function() {
        delete this._dragger;
    };

})();


Include threshold for unstable

Include threshold for unstable

<io.jenkins.plugins.analysis.core.steps.IssuesRecorder plugin="warnings-ng@@7.3.0">
  <analysisTools>
    <io.jenkins.plugins.analysis.warnings.Cmake>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Cmake>
@[if os_name in ['linux', 'linux-armhf', 'linux-aarch64', 'linux-centos']]@
   <io.jenkins.plugins.analysis.warnings.Gcc4>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Gcc4>
@[end if]
@[if os_name == 'osx' or os_name in ['linux', 'linux-armhf', 'linux-aarch64', 'linux-centos']]@
    <io.jenkins.plugins.analysis.warnings.Clang>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Clang>
    <io.jenkins.plugins.analysis.warnings.ClangTidy>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.ClangTidy>
@[elif os_name in ['windows', 'windows-container']]@
    <io.jenkins.plugins.analysis.warnings.MsBuild>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.MsBuild>
@[else]@
@{assert False, 'Unknown os_name: ' + os_name}@
@[end if]@
  </analysisTools>
  <sourceCodeEncoding></sourceCodeEncoding>
  <sourceDirectory></sourceDirectory>
  <ignoreQualityGate>false</ignoreQualityGate>
  <ignoreFailedBuilds>true</ignoreFailedBuilds>
  <referenceJobName>-</referenceJobName>
  <failOnError>false</failOnError>
  <healthy>0</healthy>
  <unhealthy>0</unhealthy>
  <minimumSeverity plugin="analysis-model-api@@7.0.2">
    <name>LOW</name>
  </minimumSeverity>
  <filters/>
  <isEnabledForFailure>false</isEnabledForFailure>
  <isAggregatingResults>false</isAggregatingResults>
  <isBlameDisabled>false</isBlameDisabled>
  <isForensicsDisabled>false</isForensicsDisabled>
  <qualityGates>
    <io.jenkins.plugins.analysis.core.util.QualityGate>
      <threshold>1</threshold>
      <type>TOTAL</type>
      <status>WARNING</status>
    </io.jenkins.plugins.analysis.core.util.QualityGate>
  </qualityGates>
  <trendChartType>AGGREGATION_TOOLS</trendChartType>
</io.jenkins.plugins.analysis.core.steps.IssuesRecorder>

<io.jenkins.plugins.analysis.core.steps.IssuesRecorder plugin="warnings-ng@@7.2.2">
  <analysisTools>
    <io.jenkins.plugins.analysis.warnings.Cmake>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Cmake>
@[if os_name in ['linux', 'linux-armhf', 'linux-aarch64', 'linux-centos']]@
   <io.jenkins.plugins.analysis.warnings.Gcc4>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Gcc4>
@[end if]
@[if os_name == 'osx' or os_name in ['linux', 'linux-armhf', 'linux-aarch64', 'linux-centos']]@
    <io.jenkins.plugins.analysis.warnings.Clang>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Clang>
    <io.jenkins.plugins.analysis.warnings.ClangTidy>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.ClangTidy>
@[elif os_name in ['windows', 'windows-container']]@
    <io.jenkins.plugins.analysis.warnings.MsBuild>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.MsBuild>
@[else]@
@{assert False, 'Unknown os_name: ' + os_name}@
@[end if]@
  </analysisTools>
  <sourceCodeEncoding></sourceCodeEncoding>
  <sourceDirectory></sourceDirectory>
  <ignoreQualityGate>false</ignoreQualityGate>
  <ignoreFailedBuilds>true</ignoreFailedBuilds>
  <referenceJobName>-</referenceJobName>
  <failOnError>false</failOnError>
  <healthy>0</healthy>
  <unhealthy>0</unhealthy>
  <minimumSeverity plugin="analysis-model-api@@7.0.2">
    <name>LOW</name>
  </minimumSeverity>
  <filters/>
  <isEnabledForFailure>false</isEnabledForFailure>
  <isAggregatingResults>false</isAggregatingResults>
  <isBlameDisabled>false</isBlameDisabled>
  <isForensicsDisabled>false</isForensicsDisabled>
  <qualityGates/>
  <trendChartType>AGGREGATION_TOOLS</trendChartType>
</io.jenkins.plugins.analysis.core.steps.IssuesRecorder>


Style CellRendererProgress - #3812

Style CellRendererProgress - #3812

Make them similar to the progress bars inside entries, like Browse URL
entry.

Signed-off-by: Manuel Quiñones <6f5069c5b6be23302a13accec56587944be09079@laptop.org>
Acked-by: Simon Schampijer <088e16a1019277b15d58faf0541e11910eb756f6@laptop.org>
Signed-off-by: Ajay Garg <1c58a446a63e719d7b90156a8998ccc461609ed7@activitycentral.com>

${
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 scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 7      # 7.68

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26

# FIXME this should be calculated with the radio size and the bullet
# size:
scale_trough_margin = 8

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

icon_small = icon_base * 3
icon_large = icon_base * 5

}

* {
    -sugar-focus-line: @white;

    /* 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;

    /* we have to disable focus border for GtkTreeView, see #1261 */
    -GtkTreeView-interior-focus: 1;
    -GtkTreeView-focus-line-width: 0;

    -GtkTextView-interior-focus: 1;

    -GtkWidget-focus-line-width: 0;  /* Prevents some drawing glitches */
    -GtkEntry-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkWidget-focus-padding: 0;
    /* 0.05 works good for both the sugar and sugar-xo themes */
    -GtkWidget-cursor-aspect-ratio: 0.05;

    -GtkWidget-wide-separators: true;
    -GtkWidget-separator-height: $thickness;
    -GtkWidget-separator-width: $thickness;

    -GtkWidget-scroll-arrow-hlength: $subcell_size;
    -GtkWidget-scroll-arrow-vlength: $subcell_size;

    -GtkRange-activate-slider: 1;

    /* We fake the default border in the theme */
    -GtkButton-default-border: 0 0 0 0;
    -GtkButton-default-outside-border: 0 0 0 0;
    -GtkButton-image-spacing: $subcell_size;

    -GtkEntry-progress-border: $thickness $thickness $thickness $thickness;

    -GtkScrolledWindow-scrollbar-spacing: 0;

    -GtkExpander-expander-size: $font_height;
    -GtkExpander-expander-spacing: 2;

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

    -GtkToolbar-space-size: $(2*subcell_size);
    -GtkToolbar-shadow-type: none;

    -GtkProgressBar-min-horizontal-bar-height: $subcell_size;
    -GtkProgressBar-min-vertical-bar-width: $subcell_size;

    -GtkButtonBox-child-min-height: $(3*subcell_size);
    -GtkButtonBox-child-min-width: $(3*subcell_size);
    -GtkButtonBox-child-internal-pad-x: 0;
    -GtkButtonBox-child-internal-pad-y: 0;

    -GtkCheckButton-indicator-size: $radio_size;
    -GtkCheckButton-indicator-spacing: 3;

    -GtkWidget-text-handle-width: 110px;
    -GtkWidget-text-handle-height: 110px;
}

*:insensitive {
    color: @button_grey;
}

/* Backgrounds and windows */

GtkWindow {
    background-color: @panel_grey;
    color: @black;
}

GtkDialog {
    background-color: @black;
    color: @white;
}

SugarIntroWindow {
    background-color: @white;
}

SugarIntroWindow {
    color: @black;
}

GtkLabel, GtkLabel:insensitive {
    background-color: transparent;
}

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    /* It would be nicer to just set the inner-border, but that does not work
       for combo boxes ... The combobox ignores it, so set it to 0px
       See http://bugzilla.gnome.org/show_bug.cgi?id=485762 */

    -GtkButton-inner-border: 0 0 0 0;
    padding: $(border)px $(border)px $(border)px $(border)px;

    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

.button:focused {
    border-color: @white;
}

.button:active {
    background-color: @white;
}

.button:insensitive {
    background-color: transparent;
}

.button:active:focused {
    color: @black;
    border-color: @button_grey;
}

/* Spin buttons */

.spinbutton.button {
    border-radius: 0px;
    border-width: 0px;
    border-style: solid;
    background-color: @button_grey;
}

.spinbutton.button:last-child {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
    border-width: 0px 0px 0px $(thickness)px;
    border-style: solid;
    border-color: @selection_grey;
}

.spinbutton.button:active {
    background-color: @black;
}

.spinbutton.button:insensitive {
    background-color: @selection_grey;
}

/* Toggle buttons */

GtkToggleButton.button:active {
    background-color: @white;
    color: @black;
}

/* Entries and views */

.view {
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background-color: @white;
}

GtkTreeView {
    background-color: @button_grey;
}

GtkTreeView row:nth-child(even) {
    background-color: @row_even;
}
GtkTreeView row:nth-child(odd) {
    background-color: @row_odd;
}

.entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @text_field_grey;
    border-style: solid;
    background-color: @text_field_grey;
    color: @black;
    ${ entry_ythickness = my_ceil(0.2 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    ${ entry_xthickness = my_ceil(0.32 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    padding: $(2 * max(my_ceil((3*subcell_size - font_height - entry_ythickness*2)/2.0),0))px $(2 * max(subcell_size - entry_xthickness, 0))px $(2 * max(my_floor((3*subcell_size - font_height - entry_ythickness*2)/2.0), 0))px $(2 * max(subcell_size - entry_xthickness, 0))px;
}

.entry.progressbar {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    background-color: @selection_grey;
}

.entry:focused {
    background-color: @white;
}

.toolbar .entry:focused {
    border-color: @white;
}

.entry:insensitive, .view:insensitive {
    background-color: @button_grey;
}

.entry:insensitive {
    border-color: @button_grey;
}

.view:selected {
    background-color: @panel_grey;
    color: @black;
}

.view:insensitive {
    background-color: @white;
    color: @black;
}

.entry:selected,
.entry:selected:focused,
.view:selected:focused {
    background-color: @selection_grey;
    color: @black;
}

.entry:selected,
.entry:selected:focused {
    border-color: @selection_grey;
}

/* Frames */

GtkScrolledWindow.frame {
    border-style: solid;
    border-color: @selection_grey;
    border-width: $(thickness)px;
    border-radius: 0;
    padding: $(default_padding)px;
}

GtkFrame.frame.journal-preview-box {
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
}

/* Combo boxes */

GtkComboBox {
    color: @white;
}

GtkComboBox .separator {
    /* Remove the separator turning it transparent */
    color: alpha(@theme_base_color, 0.0);
}

/* Notebooks */

.notebook {
    background-color: @selection_grey;
    color: @black;
    padding: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
}

.notebook tab {
    background-color: @selection_grey;
}

/* Setting white color to the tab labels using only the previous rule */
/* doesn't work, so we have to set white color to the GtkLabel, and make */
/* sure the buttons get black color in the next two rules: */
.notebook tab GtkLabel {
    color: @white;
    padding: $(subcell_size)px 0;
}

.notebook tab .button GtkLabel {
    color: @black;
}

.notebook tab:active {
    background-color: @toolbar_grey;
}

.notebook tab .button {
    border-radius: $(toolbutton_padding)px;
}

/* Browse Widgets */

BrowseTabPage {
    background-color: @black;
}

BrowseSearchWindow .view {
    background-color: @black;
    color: @white;
    border-color: @button_grey;
    border-width: 0 $(thickness)px $(thickness)px $(thickness)px;
    border-style: solid;
}

/* Control panel */

SugarSectionView {
    background-color: @white;
    color: @black;
}

SugarSectionView *:insensitive {
    background-color: @white;
}

/* Alert */

SugarAlert {
    background-color: @black;
    color: @white;
}

SugarAlert *:insensitive {
    background-color: @black;
}

.button SugarTimeoutIcon GtkLabel,
.button SugarTimeoutIcon GtkLabel:prelight {
    background-color: @white;
    color: @button_grey;
    border-radius: $(2 * subcell_size)px;
}

.button SugarTimeoutIcon GtkLabel:active {
    background-color: @toolbar_grey;
    color: @white;
}

/* Tray */

SugarHTray, SugarVTray {
    background-color: @toolbar_grey;
}

SugarHTray * , SugarVTray * { background-color: @toolbar_grey;}

/* Menus and palettes */

SugarPaletteWindowWidget {
    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    background-color: @black;
    color: @white;
}

SugarPaletteWindowWidget .view {
    color: @black;
}

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

SugarPaletteWindow SugarGroupBox {
    background-color: @toolbar_grey;
    color: @white;
}

SugarPaletteWindow SugarGroupBox *:insensitive {
    background-color: @toolbar_grey;
}

.menu {
    background-color: @black;
    color: @white;

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*thickness));
    -GtkMenu-horizontal-padding: $thickness;
    -GtkMenu-vertical-padding  : $thickness;
    /* This means the outline of the submenu overlaps with a palette.
     * However in the case of two normal menus, they are next to each other.
     * It is not possible to be smarter about this, because the style comes from
     * the submenu. */
    -GtkMenu-horizontal-offset : 0;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px 0px $(subcell_size)px 0px;
    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

.menu :active {
    background-color: @button_grey;
}

.menuitem {
    padding: $(subcell_size)px $((subcell_size * 3 - font_height) / 2)px;
}

.menuitem:prelight {
    background-color: @button_grey;
}

.menuitem.separator {
    padding: $(subcell_size)px 0px;
}

SugarPaletteHeader.menuitem {
    padding: 0px $((subcell_size * 3 - font_height) / 2)px;
}

SugarPaletteHeader.menuitem:prelight {
    background-color: @black;
}

SugarPaletteHeaderSeparator.menuitem.separator {
    padding: 0px 0px $(subcell_size)px 0px;
}

.tooltip {
    background-color: @black;
    border-style: solid;
    border-width: $(thickness)px;
    border-color: @button_grey;
}

.tooltip * {
    color: @white;
}

/* Scrollbars */

.scrollbar {
    -GtkRange-slider-width: $subcell_size;
    -GtkRange-trough-border: 0;
    -GtkRange-stepper-size: 0;
    -GtkScrollbar-min-slider-length: $(3*subcell_size);
    -GtkScrollbar-has-forward-stepper: 0;
    -GtkScrollbar-has-backward-stepper: 0;
    -GtkScrollbar-has-secondary-forward-stepper: 0;
    -GtkScrollbar-has-secondary-backward-stepper: 0;
}

.scrollbar.trough {
    background-color: @button_grey;
    border-width: 0px;
}

.scrollbar.slider {
    background-color: @white;
    border-radius: $(2*subcell_size)px;
    border-width: 0px;
}

.scrollbar.slider:active {
    background-color: @text_field_grey;
}

/* Progress bars */

/* Scrollbar padding hack? What was that about? */

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @button_grey;
    border-radius: $(subcell_size)px;
    border-style: solid;
    border-width: $(thickness)px;
}

GtkProgressBar.trough {
    background-color: @selection_grey;
    border-style: solid;
    border-radius: $(subcell_size)px;
    border-color: @button_grey;
    border-width: $(thickness)px;
}

.toolbar GtkProgressBar.trough,
SugarPaletteWindowWidget GtkProgressBar.trough {
    background-color: @black;
}

/* Separators */

.separator {
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
}

/* Tool buttons */

.toolbar {
padding: 0px;
background-color: @toolbar_grey;
color: @white;
}

.toolbar .button,
SugarPaletteWindowWidget SugarRadioToolButton .button {
    border-color: transparent;
    border-radius: 0px;
    border-style: none;
}

.toolbar .button #gtk-toolbar-arrow {
    padding: 0 $(subcell_size + default_padding)px;
}

.toolbar GtkToolButton .button,
.toolbar SugarRadioToolButton *,
SugarPaletteWindowWidget SugarRadioToolButton *,
SugarPaletteWindowWidget GtkToolButton .button {
    background-color: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding)px;
}

.toolbar GtkToolButton .button,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    padding: $(toolbutton_padding - default_padding)px;
    border-width: $(default_padding)px;
    border-style: solid;
    background-clip: padding-box;
}

.toolbar GtkToolButton .button:prelight,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    background-color: @black;
}

.toolbar SugarRadioToolButton *:active,
SugarPaletteWindowWidget SugarRadioToolButton *:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkToolButton .button:active {
    background-color: @transparent;
}

.toolbar GtkToolButton .button:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkScrolledWindow * {
    background-color: @black;
}

.toolbar GtkComboBox .button {
    border-radius: $(2*subcell_size)px;
}

/* Scales */

.scale {
    -GtkScale-slider-length: $scale_slider_width;
    -GtkRange-slider-width: $scale_slider_width;
}

/* We have to override the color of the scale, otherwise the slider
   background image is invisible or not set for this palettes.
   Upstream bug: https://bugzilla.gnome.org/show_bug.cgi?id=686703 */
SugarPaletteWindowWidget .scale {
    color: transparent;
}

.scale.trough {
    background-color: @button_grey;
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
    margin: $(scale_trough_margin)px 0;
}

.scale.trough.vertical {
    margin: 0 $(scale_trough_margin)px;
}

.scale.trough:focused {
    border-color: @white;
}

.scale.trough.top,
.scale.trough.left {
    background-color: @white;
}

.scale.trough.top:focused,
.scale.trough.left:focused {
    border-color: @selection_grey;
}

.scale.trough {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
}

.scale.trough.vertical {
    border-radius: 0px 0px $(2*subcell_size)px $(2*subcell_size)px;
}

.scale.trough.top {
    border-radius: $(2*subcell_size)px $(2*subcell_size)px 0px 0px;
}

.scale.trough.left {
    border-radius: $(2*subcell_size)px 0px 0px $(2*subcell_size)px;
}

.scale.slider,
.scale.slider:active {
    background-color: transparent;
}

.scale.slider {
    background-image: url("assets/scale-slider.svg");
}

.scale.slider:active {
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton:prelight {
    background-color: alpha(@theme_base_color, 0.0);
}

.toolbar GtkCheckButton,
SugarPaletteWindowWidget GtkCheckButton {
    color: @theme_base_color;
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.radio:selected,
.radio:selected row:selected,
.radio:selected row:selected:focused {
    background-image: url("assets/radio-selected.svg");
}

.radio:active,
.radio row:selected:active,
.radio row:selected:focused:active {
    background-image: url("assets/radio-active.svg");
}

.radio:active:selected,
.radio:selected row:selected:active,
.radio:selected row:selected:focused:active {
    background-image: url("assets/radio-active-selected.svg");
}

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.check:selected,
.check:selected row:selected,
.check:selected row:selected:focused {
    background-image: url("assets/checkbox-unchecked-selected.svg");
}

.check:active,
.check row:selected:active,
.check row:selected:focused:active {
    background-image: url("assets/checkbox-checked.svg");
}

.check:active:selected,
.check:selected row:selected:active,
.check:selected row:selected:focused:active {
    background-image: url("assets/checkbox-checked-selected.svg");
}

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}

/* GtkImage */

GtkImage {
    background-color: transparent;
}

/* Sugar Frame Window */

SugarFrameWindow {
    background-color: @toolbar_grey;
}

/* Sugar Canvas icon */

SugarCanvasIcon,
SugarKeepIcon.button {
    border-color: transparent;
    border-radius: $(4 * thickness)px;
    border-width: 2px;
    border-style: solid;
}

SugarCanvasIcon:prelight,
SugarKeepIcon.button:prelight {
    border-color: @zoom_views_prelight;
    background-color: @zoom_views_prelight;
}

SugarCanvasIcon:active,
SugarKeepIcon.button.toggle-press {
    border-color: @zoom_views_active;
    background-color: @zoom_views_active;
}

/* Sugar CellRenderer Icons */

.cell.sugar-icon-cell {
    background-color: transparent;
    border-radius: $(4 * thickness)px;
    border-color: @white;
    border-width: $(thickness)px;
    border-style: solid;
}

.cell.sugar-icon-cell:prelight {
    background-color: @zoom_views_prelight;
}

.cell.sugar-icon-cell:active {
    background-color: @zoom_views_active;
}

/* CellRendererProgress */

.cell.progressbar, .cell.trough {
    border-style: solid;
    border-width: $(thickness)px;
    border-color: @selection_grey;
    border-radius: $(2*subcell_size)px;
}

.cell.progressbar {
    background-color: @white;
}

.cell.trough {
    background-color: @text_field_grey;
}

/* Text cursor handles */

.cursor-handle.top,
.cursor-handle.bottom {
    background-color: transparent;
    box-shadow: none;
    border-style: none;
    border-image: none;
    border-radius: 0px;
    border-width: 0px;
}

.cursor-handle.bottom {
    background-image: url("assets/cursor-handle-bottom.svg");
}

.cursor-handle.top {
    background-image: url("assets/cursor-handle-top.svg");
}

.cursor-handle.insertion-cursor {
    background-image: url("assets/cursor-handle-insertion.svg");
}

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

EvView:selected,
ApDocView:selected {
    background-color: @selection_grey;
}

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

EvView:selected,
ApDocView:selected {
    background-color: @selection_grey;
}

${
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 scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 7      # 7.68

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26

# FIXME this should be calculated with the radio size and the bullet
# size:
scale_trough_margin = 8

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

icon_small = icon_base * 3
icon_large = icon_base * 5

}

* {
    -sugar-focus-line: @white;

    /* 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;

    /* we have to disable focus border for GtkTreeView, see #1261 */
    -GtkTreeView-interior-focus: 1;
    -GtkTreeView-focus-line-width: 0;

    -GtkTextView-interior-focus: 1;

    -GtkWidget-focus-line-width: 0;  /* Prevents some drawing glitches */
    -GtkEntry-focus-line-width: 0;
    -GtkScale-focus-line-width: 0;
    -GtkWidget-focus-padding: 0;
    /* 0.05 works good for both the sugar and sugar-xo themes */
    -GtkWidget-cursor-aspect-ratio: 0.05;

    -GtkWidget-wide-separators: true;
    -GtkWidget-separator-height: $thickness;
    -GtkWidget-separator-width: $thickness;

    -GtkWidget-scroll-arrow-hlength: $subcell_size;
    -GtkWidget-scroll-arrow-vlength: $subcell_size;

    -GtkRange-activate-slider: 1;

    /* We fake the default border in the theme */
    -GtkButton-default-border: 0 0 0 0;
    -GtkButton-default-outside-border: 0 0 0 0;
    -GtkButton-image-spacing: $subcell_size;

    -GtkEntry-progress-border: $thickness $thickness $thickness $thickness;

    -GtkScrolledWindow-scrollbar-spacing: 0;

    -GtkExpander-expander-size: $font_height;
    -GtkExpander-expander-spacing: 2;

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

    -GtkToolbar-space-size: $(2*subcell_size);
    -GtkToolbar-shadow-type: none;

    -GtkProgressBar-min-horizontal-bar-height: $subcell_size;
    -GtkProgressBar-min-vertical-bar-width: $subcell_size;

    -GtkButtonBox-child-min-height: $(3*subcell_size);
    -GtkButtonBox-child-min-width: $(3*subcell_size);
    -GtkButtonBox-child-internal-pad-x: 0;
    -GtkButtonBox-child-internal-pad-y: 0;

    -GtkCheckButton-indicator-size: $radio_size;
    -GtkCheckButton-indicator-spacing: 3;

    -GtkWidget-text-handle-width: 110px;
    -GtkWidget-text-handle-height: 110px;
}

*:insensitive {
    color: @button_grey;
}

/* Backgrounds and windows */

GtkWindow {
    background-color: @panel_grey;
    color: @black;
}

GtkDialog {
    background-color: @black;
    color: @white;
}

SugarIntroWindow {
    background-color: @white;
}

SugarIntroWindow {
    color: @black;
}

GtkLabel, GtkLabel:insensitive {
    background-color: transparent;
}

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    /* It would be nicer to just set the inner-border, but that does not work
       for combo boxes ... The combobox ignores it, so set it to 0px
       See http://bugzilla.gnome.org/show_bug.cgi?id=485762 */

    -GtkButton-inner-border: 0 0 0 0;
    padding: $(border)px $(border)px $(border)px $(border)px;

    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

.button:focused {
    border-color: @white;
}

.button:active {
    background-color: @white;
}

.button:insensitive {
    background-color: transparent;
}

.button:active:focused {
    color: @black;
    border-color: @button_grey;
}

/* Spin buttons */

.spinbutton.button {
    border-radius: 0px;
    border-width: 0px;
    border-style: solid;
    background-color: @button_grey;
}

.spinbutton.button:last-child {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
    border-width: 0px 0px 0px $(thickness)px;
    border-style: solid;
    border-color: @selection_grey;
}

.spinbutton.button:active {
    background-color: @black;
}

.spinbutton.button:insensitive {
    background-color: @selection_grey;
}

/* Toggle buttons */

GtkToggleButton.button:active {
    background-color: @white;
    color: @black;
}

/* Entries and views */

.view {
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background-color: @white;
}

GtkTreeView {
    background-color: @button_grey;
}

GtkTreeView row:nth-child(even) {
    background-color: @row_even;
}
GtkTreeView row:nth-child(odd) {
    background-color: @row_odd;
}

.entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @text_field_grey;
    border-style: solid;
    background-color: @text_field_grey;
    color: @black;
    ${ entry_ythickness = my_ceil(0.2 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    ${ entry_xthickness = my_ceil(0.32 * (subcell_size*3.0/2.0 - thickness) + thickness) }
    padding: $(2 * max(my_ceil((3*subcell_size - font_height - entry_ythickness*2)/2.0),0))px $(2 * max(subcell_size - entry_xthickness, 0))px $(2 * max(my_floor((3*subcell_size - font_height - entry_ythickness*2)/2.0), 0))px $(2 * max(subcell_size - entry_xthickness, 0))px;
}

.entry.progressbar {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    background-color: @selection_grey;
}

.entry:focused {
    background-color: @white;
}

.toolbar .entry:focused {
    border-color: @white;
}

.entry:insensitive, .view:insensitive {
    background-color: @button_grey;
}

.entry:insensitive {
    border-color: @button_grey;
}

.view:selected {
    background-color: @panel_grey;
    color: @black;
}

.view:insensitive {
    background-color: @white;
    color: @black;
}

.entry:selected,
.entry:selected:focused,
.view:selected:focused {
    background-color: @selection_grey;
    color: @black;
}

.entry:selected,
.entry:selected:focused {
    border-color: @selection_grey;
}

/* Frames */

GtkScrolledWindow.frame {
    border-style: solid;
    border-color: @selection_grey;
    border-width: $(thickness)px;
    border-radius: 0;
    padding: $(default_padding)px;
}

GtkFrame.frame.journal-preview-box {
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
}

/* Combo boxes */

GtkComboBox {
    color: @white;
}

GtkComboBox .separator {
    /* Remove the separator turning it transparent */
    color: alpha(@theme_base_color, 0.0);
}

/* Notebooks */

.notebook {
    background-color: @selection_grey;
    color: @black;
    padding: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
}

.notebook tab {
    background-color: @selection_grey;
}

/* Setting white color to the tab labels using only the previous rule */
/* doesn't work, so we have to set white color to the GtkLabel, and make */
/* sure the buttons get black color in the next two rules: */
.notebook tab GtkLabel {
    color: @white;
    padding: $(subcell_size)px 0;
}

.notebook tab .button GtkLabel {
    color: @black;
}

.notebook tab:active {
    background-color: @toolbar_grey;
}

.notebook tab .button {
    border-radius: $(toolbutton_padding)px;
}

/* Browse Widgets */

BrowseTabPage {
    background-color: @black;
}

BrowseSearchWindow .view {
    background-color: @black;
    color: @white;
    border-color: @button_grey;
    border-width: 0 $(thickness)px $(thickness)px $(thickness)px;
    border-style: solid;
}

/* Control panel */

SugarSectionView {
    background-color: @white;
    color: @black;
}

SugarSectionView *:insensitive {
    background-color: @white;
}

/* Alert */

SugarAlert {
    background-color: @black;
    color: @white;
}

SugarAlert *:insensitive {
    background-color: @black;
}

.button SugarTimeoutIcon GtkLabel,
.button SugarTimeoutIcon GtkLabel:prelight {
    background-color: @white;
    color: @button_grey;
    border-radius: $(2 * subcell_size)px;
}

.button SugarTimeoutIcon GtkLabel:active {
    background-color: @toolbar_grey;
    color: @white;
}

/* Tray */

SugarHTray, SugarVTray {
    background-color: @toolbar_grey;
}

SugarHTray * , SugarVTray * { background-color: @toolbar_grey;}

/* Menus and palettes */

SugarPaletteWindowWidget {
    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    background-color: @black;
    color: @white;
}

SugarPaletteWindowWidget .view {
    color: @black;
}

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

SugarPaletteWindow SugarGroupBox {
    background-color: @toolbar_grey;
    color: @white;
}

SugarPaletteWindow SugarGroupBox *:insensitive {
    background-color: @toolbar_grey;
}

.menu {
    background-color: @black;
    color: @white;

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*thickness));
    -GtkMenu-horizontal-padding: $thickness;
    -GtkMenu-vertical-padding  : $thickness;
    /* This means the outline of the submenu overlaps with a palette.
     * However in the case of two normal menus, they are next to each other.
     * It is not possible to be smarter about this, because the style comes from
     * the submenu. */
    -GtkMenu-horizontal-offset : 0;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px 0px $(subcell_size)px 0px;
    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

.menu :active {
    background-color: @button_grey;
}

.menuitem {
    padding: $(subcell_size)px $((subcell_size * 3 - font_height) / 2)px;
}

.menuitem:prelight {
    background-color: @button_grey;
}

.menuitem.separator {
    padding: $(subcell_size)px 0px;
}

SugarPaletteHeader.menuitem {
    padding: 0px $((subcell_size * 3 - font_height) / 2)px;
}

SugarPaletteHeader.menuitem:prelight {
    background-color: @black;
}

SugarPaletteHeaderSeparator.menuitem.separator {
    padding: 0px 0px $(subcell_size)px 0px;
}

.tooltip {
    background-color: @black;
    border-style: solid;
    border-width: $(thickness)px;
    border-color: @button_grey;
}

.tooltip * {
    color: @white;
}

/* Scrollbars */

.scrollbar {
    -GtkRange-slider-width: $subcell_size;
    -GtkRange-trough-border: 0;
    -GtkRange-stepper-size: 0;
    -GtkScrollbar-min-slider-length: $(3*subcell_size);
    -GtkScrollbar-has-forward-stepper: 0;
    -GtkScrollbar-has-backward-stepper: 0;
    -GtkScrollbar-has-secondary-forward-stepper: 0;
    -GtkScrollbar-has-secondary-backward-stepper: 0;
}

.scrollbar.trough {
    background-color: @button_grey;
    border-width: 0px;
}

.scrollbar.slider {
    background-color: @white;
    border-radius: $(2*subcell_size)px;
    border-width: 0px;
}

.scrollbar.slider:active {
    background-color: @text_field_grey;
}

/* Progress bars */

/* Scrollbar padding hack? What was that about? */

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @button_grey;
    border-radius: $(subcell_size)px;
    border-style: solid;
    border-width: $(thickness)px;
}

GtkProgressBar.trough {
    background-color: @selection_grey;
    border-style: solid;
    border-radius: $(subcell_size)px;
    border-color: @button_grey;
    border-width: $(thickness)px;
}

.toolbar GtkProgressBar.trough,
SugarPaletteWindowWidget GtkProgressBar.trough {
    background-color: @black;
}

/* Separators */

.separator {
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
}

/* Tool buttons */

.toolbar {
padding: 0px;
background-color: @toolbar_grey;
color: @white;
}

.toolbar .button,
SugarPaletteWindowWidget SugarRadioToolButton .button {
    border-color: transparent;
    border-radius: 0px;
    border-style: none;
}

.toolbar .button #gtk-toolbar-arrow {
    padding: 0 $(subcell_size + default_padding)px;
}

.toolbar GtkToolButton .button,
.toolbar SugarRadioToolButton *,
SugarPaletteWindowWidget SugarRadioToolButton *,
SugarPaletteWindowWidget GtkToolButton .button {
    background-color: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding)px;
}

.toolbar GtkToolButton .button,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    padding: $(toolbutton_padding - default_padding)px;
    border-width: $(default_padding)px;
    border-style: solid;
    background-clip: padding-box;
}

.toolbar GtkToolButton .button:prelight,
SugarPaletteWindowWidget GtkToolButton .button:prelight {
    background-color: @black;
}

.toolbar SugarRadioToolButton *:active,
SugarPaletteWindowWidget SugarRadioToolButton *:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkToolButton .button:active {
    background-color: @transparent;
}

.toolbar GtkToolButton .button:active {
    background-color: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

SugarPaletteWindowWidget GtkScrolledWindow * {
    background-color: @black;
}

.toolbar GtkComboBox .button {
    border-radius: $(2*subcell_size)px;
}

/* Scales */

.scale {
    -GtkScale-slider-length: $scale_slider_width;
    -GtkRange-slider-width: $scale_slider_width;
}

/* We have to override the color of the scale, otherwise the slider
   background image is invisible or not set for this palettes.
   Upstream bug: https://bugzilla.gnome.org/show_bug.cgi?id=686703 */
SugarPaletteWindowWidget .scale {
    color: transparent;
}

.scale.trough {
    background-color: @button_grey;
    border-style: solid;
    border-color: @button_grey;
    border-width: $(thickness)px;
    margin: $(scale_trough_margin)px 0;
}

.scale.trough.vertical {
    margin: 0 $(scale_trough_margin)px;
}

.scale.trough:focused {
    border-color: @white;
}

.scale.trough.top,
.scale.trough.left {
    background-color: @white;
}

.scale.trough.top:focused,
.scale.trough.left:focused {
    border-color: @selection_grey;
}

.scale.trough {
    border-radius: 0px $(2*subcell_size)px $(2*subcell_size)px 0px;
}

.scale.trough.vertical {
    border-radius: 0px 0px $(2*subcell_size)px $(2*subcell_size)px;
}

.scale.trough.top {
    border-radius: $(2*subcell_size)px $(2*subcell_size)px 0px 0px;
}

.scale.trough.left {
    border-radius: $(2*subcell_size)px 0px 0px $(2*subcell_size)px;
}

.scale.slider,
.scale.slider:active {
    background-color: transparent;
}

.scale.slider {
    background-image: url("assets/scale-slider.svg");
}

.scale.slider:active {
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton:prelight {
    background-color: alpha(@theme_base_color, 0.0);
}

.toolbar GtkCheckButton,
SugarPaletteWindowWidget GtkCheckButton {
    color: @theme_base_color;
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.radio:selected,
.radio:selected row:selected,
.radio:selected row:selected:focused {
    background-image: url("assets/radio-selected.svg");
}

.radio:active,
.radio row:selected:active,
.radio row:selected:focused:active {
    background-image: url("assets/radio-active.svg");
}

.radio:active:selected,
.radio:selected row:selected:active,
.radio:selected row:selected:focused:active {
    background-image: url("assets/radio-active-selected.svg");
}

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    background-color: alpha(@theme_base_color, 0.0);
}

.check:selected,
.check:selected row:selected,
.check:selected row:selected:focused {
    background-image: url("assets/checkbox-unchecked-selected.svg");
}

.check:active,
.check row:selected:active,
.check row:selected:focused:active {
    background-image: url("assets/checkbox-checked.svg");
}

.check:active:selected,
.check:selected row:selected:active,
.check:selected row:selected:focused:active {
    background-image: url("assets/checkbox-checked-selected.svg");
}

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}

/* GtkImage */

GtkImage {
    background-color: transparent;
}

/* Sugar Frame Window */

SugarFrameWindow {
    background-color: @toolbar_grey;
}

/* Sugar Canvas icon */

SugarCanvasIcon,
SugarKeepIcon.button {
    border-color: transparent;
    border-radius: $(4 * thickness)px;
    border-width: 2px;
    border-style: solid;
}

SugarCanvasIcon:prelight,
SugarKeepIcon.button:prelight {
    border-color: @zoom_views_prelight;
    background-color: @zoom_views_prelight;
}

SugarCanvasIcon:active,
SugarKeepIcon.button.toggle-press {
    border-color: @zoom_views_active;
    background-color: @zoom_views_active;
}

/* Sugar CellRenderer Icons */

.cell.sugar-icon-cell {
    background-color: transparent;
    border-radius: $(4 * thickness)px;
    border-color: @white;
    border-width: $(thickness)px;
    border-style: solid;
}

.cell.sugar-icon-cell:prelight {
    background-color: @zoom_views_prelight;
}

.cell.sugar-icon-cell:active {
    background-color: @zoom_views_active;
}

/* Text cursor handles */

.cursor-handle.top,
.cursor-handle.bottom {
    background-color: transparent;
    box-shadow: none;
    border-style: none;
    border-image: none;
    border-radius: 0px;
    border-width: 0px;
}

.cursor-handle.bottom {
    background-image: url("assets/cursor-handle-bottom.svg");
}

.cursor-handle.top {
    background-image: url("assets/cursor-handle-top.svg");
}

.cursor-handle.insertion-cursor {
    background-image: url("assets/cursor-handle-insertion.svg");
}

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

EvView:selected,
ApDocView:selected {
    background-color: @selection_grey;
}

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

EvView:selected,
ApDocView:selected {
    background-color: @selection_grey;
}