3.20:  Refactor spinbutton and box.linked to share styling

${
import os
import math

gtk_major, gtk_minor, gtk_patch = map(int, gtk.split('.'))
treeview_pseudo_element = gtk_major >= 3 and gtk_minor >= 16

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
    radio_size = 26
    scale_trough_margin = 8
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 = 18             # 18.72
    scale_trough_margin = 6     # 5.76

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

disabled_opacity = 0.5

}

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

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

    -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);

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

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

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

*:disabled {
    color: @panel_grey;
}

/* Backgrounds and windows */

window {
    background: @panel_grey;
    color: @black;
}

dialog {
    background: @black;
    color: @white;
}

introwindow {
    background: @white;
    color: @black;
}

label, label:disabled {
    background: transparent;
}

/* Buttons */

button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.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: @button_grey;
    color: @white;
}

button:focus {
    border-color: @white;
    color: @white;
}

button:active {
    background: @white;
    color: @black;
}

button:disabled {
    background: transparent;
}

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

/* Toggle buttons */

button:checked {
    background: @white;
    color: @black;
}

/* Entries and views (textview, treeview) */

.view,
textview text {  /* Setting a textview to background white makes it black,
                    unless you only style the text css node */
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background: @white;
    color: @black;
}

treeview header button,
treeview header button:hover:active {
    border-radius: 0px;
    background: @button_grey;
    border-width: 0px;
    color: @white;
}

/* FIXME: treeview row css nodes aren't a thing

    $[if treeview_pseudo_element] GtkTreeView row:even
    $[else] GtkTreeView row:nth-child(even)
    $[end if] {
        background: @row_even;
    }

    $[if treeview_pseudo_element] GtkTreeView row:odd
    $[else] GtkTreeView row:nth-child(odd)
    $[end if] {
        background: @row_odd;
    }
*/

entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @text_field_grey;
    border-style: solid;
    background: @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 image {
    /* Default padding is too large and causes toolbar height expansion */
    margin: $(default_padding-1)px;
}

toolitem entry {
    margin: $(subcell_size)px;
}

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

entry:focus {
    background: @white;
}

toolbar entry:focus {
    border-color: @white;
}

entry:disabled {
    background: @button_grey;
    border-color: @button_grey;
}

treeview:selected {
    background: @panel_grey;
    color: @black;
}

entry:selected,
entry:selected:focus {
    background: @selection_grey;
    color: @black;
}

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

selection {
    background: @selected_bg_color;
    color: @selected_fg_color;
}

/* Frames */

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

frame.journal-preview-box {
    border-color: @button_grey;
}

/* Combo boxes */

combobox {
    color: @white;
}

combobox button.combo arrow {
    -gtk-icon-source: -gtk-icontheme('control-popup-arrow');
    -gtk-icon-transform: scale(0.3);
    min-width: $(subcell_size)px;
    min-height: $(subcell_size)px;
}

/* What does this do?
    combobox .menu.button {
        border-color: transparent;
        border-radius: 0px;
        border-style: none;
        background: transparent;
        color: @white;
    }
*/

combobox menu {
    -GtkWidget-scroll-arrow-hlength: $(my_floor(2.5 * subcell_size));
    -GtkWidget-scroll-arrow-vlength: $(my_floor(2.5 * subcell_size));
}

/* Notebooks */

notebook {
    background: @selection_grey;
    color: @black;
    padding: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
    -GtkWidget-scroll-arrow-hlength: $(my_floor(2.5 * subcell_size));
    -GtkWidget-scroll-arrow-vlength: $(my_floor(2.5 * subcell_size));
}

notebook arrow {
    color: @white;
}

notebook tab {
    background: @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 label {
    color: @white;
    padding: $(subcell_size)px 0;
}

notebook tab button label {
    color: @black;
}

notebook tab:checked {
    background: @toolbar_grey;
}

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

/* Browse Widgets */

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

/* Control panel */

cpanelsectionview {
    background: @white;
    color: @black;
}

/* TODO:  this should only effect the control panel */
controlpanel image:disabled {
    opacity: $(disabled_opacity);
}

/* Alert */

alert {
    background: @black;
    color: @white;
}

alert *:disabled {
    background: @black;
}

button timeouticon label,
button timeouticon label:hover {
    background: @white;
    color: @button_grey;
    border-radius: $(2 * subcell_size)px;
}

button:active timeouticon label {
    background: @toolbar_grey;
    color: @white;
}

/* Tray */

htray, vtray {
    background: @toolbar_grey;
}

htray * , vtray * { background: @toolbar_grey;}

/* Menus and palettes */

palette,
popover {
    border: $(thickness)px solid @button_grey;
    background: @black;
    color: @white;
}

palette .view {
    color: @black;
}

SugarPaletteMenuWidget {
    background: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

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

SugarPaletteWindow SugarGroupBox *:disabled {
    background: @toolbar_grey;
}

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

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*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 :  -$(line_width)px;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px;

    border: $(thickness)px solid @button_grey;
}

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

menuitem:hover,
menuitem:hover {
    background: @button_grey;
}

menuitem separator {
    padding: 0;
}

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

tooltip * {
    color: @white;
}

/* Scrollbars */

scrollbar {
    -GtkRange-slider-width: $(subcell_size + 2*thickness);
    -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: @button_grey;
    border-width: 0px;
}

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

scrollbar slider:active {
    background: @text_field_grey;
}

/* Progress bars */

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

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

toolbar progressbar trough,
palette progressbar trough {
    background: @black;
}

/* Separators */

separator {
    background-color: @button_grey;
}

/* If we set the background for these, we get about 20px high
   separator.  But we only want a border through the middle */
palette widget separator {
    border-top: $(thickness)px solid @button_grey;
    margin-top: $(subcell_size)px;
    margin-bottom: -$(subcell_size)px;
    background: transparent;
}

/* Tool buttons */

toolbar,
toolbarbox {
    padding: 0;
    background: @toolbar_grey;
    color: @white;
}

/* FIXME: I needed to add the margin in the gtk3.20 port, but that wasn't
          there in the code before.  I ended up having to reduce the border
          to 2px */
toolbutton button,
filtertoolbutton button {
    border-style: none;
    background: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding - default_padding)px;
    margin: $(default_padding)px;
}

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

toolbutton button:disabled,
filtertoolbutton button:disabled {
    opacity: $(disabled_opacity);
}

toolbutton button:hover,
filtertoolbutton button:hover {
    padding: $(toolbutton_padding - 2 - default_padding)px;
    border-width: 2px;
    border-style: solid;
    background-clip: padding-box;
}

/* Edge case of the wifi toolbuttons in the frame */
framewindow toolbutton button:hover {
    padding: $(toolbutton_padding - default_padding)px;
    border-width: 0;
}

toolbutton button:hover:not(:checked),
toolbutton button:hover:not(:checked) * {
    background: @black;
}

toolbutton button:checked,
toolbutton button:checked * {
    background: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

toolbutton button:checked:hover,
toolbutton button:checked:hover * {
    background: @button_grey;
    border-color: @button_grey;
}

toolbutton button:active,
filtertoolbutton button:active,
toolbutton button:active * {
    background: @button_grey;
    color: white;
    border-radius: $(toolbutton_padding)px;
}

palette scrolledwindow {
    background: @black;
}

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

/* Scales */

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

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

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

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

scale trough:focus {
    border-color: @white;
}

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

scale slider,
scale slider:active {
    background: transparent;
}

scale slider {
    -gtk-icon-source: url("assets/scale-slider.svg");
    margin: -$(my_floor(subcell_size/2))px;
    min-width: $(2*subcell_size)px;
    min-height: $(2*subcell_size)px;
}

scale slider:active {
    -gtk-icon-source: url("assets/scale-slider-active.svg");
    margin: -$(my_floor(subcell_size/2))px;
    min-width: $(2*subcell_size)px;
    min-height: $(2*subcell_size)px;
}

/* Radio and check buttons */

check:hover {
    background: alpha(@theme_base_color, 0.0);
}

toolbar check,
palette check {
    color: @theme_base_color;
    border: 1px solid @theme_base_color;
}

radio,
radio row:selected,
radio row:selected:focus {
    -gtk-icon-source: url("assets/radio.svg");
    background: alpha(@theme_base_color, 0.0);
}

radio:selected,
radio:selected row:selected,
radio:selected row:selected:focus {
    -gtk-icon-source: url("assets/radio-selected.svg");
}

radio:active,
radio row:selected:active,
radio row:selected:focus:active {
    -gtk-icon-source: url("assets/radio-active.svg");
}

radio:checked,
radio row:selected:checked,
radio row:selected:focus:checked {
   -gtk-icon-source: url("assets/radio-active.svg");
}

radio:active:selected,
radio:selected row:selected:active,
radio:selected row:selected:focus:active {
    -gtk-icon-source: url("assets/radio-active-selected.svg");
}

radio:checked:selected,
radio:selected row:selected:checked,
radio:selected row:selected:focus:checked {
    -gtk-icon-source: url("assets/radio-active-selected.svg");
}

check,
check row:selected,
check row:selected:focus {
    -gtk-icon-source: url("assets/checkbox-unchecked.svg");
    background: alpha(@theme_base_color, 0.0);
}

check:selected,
check:selected row:selected,
check:selected row:selected:focus {
    -gtk-icon-source: url("assets/checkbox-unchecked-selected.svg");
}

check:active,
check row:selected:active,
check row:selected:focus:active {
    -gtk-icon-source: url("assets/checkbox-checked.svg");
}

check:checked,
check row:selected:checked,
check row:selected:focus:checked {
    -gtk-icon-source: url("assets/checkbox-checked.svg");
}

check:active:selected,
check:selected row:selected:active,
check:selected row:selected:focus:active {
    -gtk-icon-source: url("assets/checkbox-checked-selected.svg");
}

check:checked:selected,
check:selected row:selected:checked,
check:selected row:selected:focus:checked {
    background-image: url("assets/checkbox-checked-selected.svg");
    -gtk-icon-source: url("assets/checkbox-checked-selected.svg");
}

/* Linked boxes (eg. combobox entries, spinbuttons) */

box.linked > *:not(:first-child):not(:last-child),
spinbutton :not(entry):not(:first-child):not(:last-child),
popover.touch-selection box.horizontal > *:not(:first-child):not(:last-child) {
    border-radius: 0;
}

box.horizontal.linked > *:first-child:not(:last-child),
spinbutton.horizontal entry,
popover.touch-selection box.horizontal > *:first-child:not(:last-child) {
    border-radius: $(2 * subcell_size)px 0 0 $(2 * subcell_size)px;
}

box.horizontal.linked > *:not(:first-child):last-child,
spinbutton.horizontal  button:last-child,
popover.touch-selection box.horizontal > *:not(:first-child):last-child {
    border-radius: 0 $(2 * subcell_size)px $(2 * subcell_size)px 0;
}

box.vertical.linked > *:first-child:not(:last-child),
spinbutton.vertical button.up {
    border-radius: $(2 * subcell_size)px $(2 * subcell_size)px 0 0;
}

box.vertical.linked > *:not(:first-child):last-child,
spinbutton.vertical button.down {
    border-radius: 0 0 $(2 * subcell_size)px $(2 * subcell_size)px;
}

/* Tool items */

toolitem {
    /* arrow in the toolbarbox */
    color: @white;
}

.palette-down,
.toolbar-down {
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}

/* GtkImage */

image {
    background: transparent;
}

/* Sugar Frame Window */

framewindow {
    background: @toolbar_grey;
}

/* Sugar Canvas icon */

canvasicon {
    border-color: transparent;
    border-radius: $(4 * thickness)px;
    border-width: 2px;
    border-style: solid;
    background: transparent;
}

canvasicon:hover {
    border-color: @zoom_views_prelight;
    background: @zoom_views_prelight;
}

canvasicon.toggle-press {
    border-color: @zoom_views_active;
    background: @zoom_views_active;
}

/* Sugar CellRenderer Icons */

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

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

.cell.sugar-icon-cell:active {
    background: @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: @white;
}

.cell.trough {
    background: @text_field_grey;
}

/* Text cursor handles */

cursor-handle.top,
cursor-handle.bottom {
    background: transparent;
    border-radius: 10px;
    border-width: $(thickness)px;
    border-color: @panel_grey;
    border-style: solid;
}

cursor-handle.bottom {
    -gtk-icon-source: url("assets/cursor-handle-bottom.svg");
}

cursor-handle.top {
    -gtk-icon-source: url("assets/cursor-handle-top.svg");
}

cursor-handle.insertion-cursor {
    -gtk-icon-source: url("assets/cursor-handle-insertion.svg");
}

/* Application specific properties */

EvView {
    background: @panel_grey;
}

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

${
import os
import math

gtk_major, gtk_minor, gtk_patch = map(int, gtk.split('.'))
treeview_pseudo_element = gtk_major >= 3 and gtk_minor >= 16

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
    radio_size = 26
    scale_trough_margin = 8
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 = 18             # 18.72
    scale_trough_margin = 6     # 5.76

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

disabled_opacity = 0.5

}

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

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

    -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);

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

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

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

*:disabled {
    color: @panel_grey;
}

/* Backgrounds and windows */

window {
    background: @panel_grey;
    color: @black;
}

dialog {
    background: @black;
    color: @white;
}

introwindow {
    background: @white;
    color: @black;
}

label, label:disabled {
    background: transparent;
}

/* Buttons */

button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.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: @button_grey;
    color: @white;
}

button:focus {
    border-color: @white;
    color: @white;
}

button:active {
    background: @white;
    color: @black;
}

button:disabled {
    background: transparent;
}

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

/* Linked boxes (eg. combobox entries) */

box.linked *:not(:first-child):not(:last-child) {
    border-radius: 0;
}

box.linked *:first-child:not(:last-child) {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

box.linked *:not(:first-child):last-child {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

/* Spin buttons */

spinbutton button {
    background: @button_grey;
    border-style: solid;
    border-radius: 0;
}

spinbutton.horizontal button:last-child {
    border-radius: 0 $(2*subcell_size)px $(2*subcell_size)px 0;
}

spinbutton.vertical button:first-child {
    border-radius: $(2*subcell_size)px $(2*subcell_size)px 0 0;
    border-width: $(thickness)px $(thickness)px 0 $(thickness)px;
    border-style: solid;
}

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

spinbutton button:active {
    background: @black;
}

spinbutton button:disabled {
    background: @selection_grey;
}

/* Toggle buttons */

button:checked {
    background: @white;
    color: @black;
}

/* Entries and views (textview, treeview) */

.view,
textview text {  /* Setting a textview to background white makes it black,
                    unless you only style the text css node */
    border-width: 0px;
    border-style: none;
    border-radius: 0px;
    padding: 0px;
    background: @white;
    color: @black;
}

treeview header button,
treeview header button:hover:active {
    border-radius: 0px;
    background: @button_grey;
    border-width: 0px;
    color: @white;
}

/* FIXME: treeview row css nodes aren't a thing

    $[if treeview_pseudo_element] GtkTreeView row:even
    $[else] GtkTreeView row:nth-child(even)
    $[end if] {
        background: @row_even;
    }

    $[if treeview_pseudo_element] GtkTreeView row:odd
    $[else] GtkTreeView row:nth-child(odd)
    $[end if] {
        background: @row_odd;
    }
*/

entry {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    border-color: @text_field_grey;
    border-style: solid;
    background: @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 image {
    /* Default padding is too large and causes toolbar height expansion */
    margin: $(default_padding-1)px;
}

toolitem entry {
    margin: $(subcell_size)px;
}

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

entry:focus {
    background: @white;
}

toolbar entry:focus {
    border-color: @white;
}

entry:disabled {
    background: @button_grey;
    border-color: @button_grey;
}

treeview:selected {
    background: @panel_grey;
    color: @black;
}

entry:selected,
entry:selected:focus {
    background: @selection_grey;
    color: @black;
}

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

selection {
    background: @selected_bg_color;
    color: @selected_fg_color;
}

/* Frames */

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

frame.journal-preview-box {
    border-color: @button_grey;
}

/* Combo boxes */

combobox {
    color: @white;
}

combobox button.combo arrow {
    -gtk-icon-source: -gtk-icontheme('control-popup-arrow');
    -gtk-icon-transform: scale(0.3);
    min-width: $(subcell_size)px;
    min-height: $(subcell_size)px;
}

/* What does this do?
    combobox .menu.button {
        border-color: transparent;
        border-radius: 0px;
        border-style: none;
        background: transparent;
        color: @white;
    }
*/

combobox menu {
    -GtkWidget-scroll-arrow-hlength: $(my_floor(2.5 * subcell_size));
    -GtkWidget-scroll-arrow-vlength: $(my_floor(2.5 * subcell_size));
}

/* Notebooks */

notebook {
    background: @selection_grey;
    color: @black;
    padding: 0px;
    -GtkNotebook-tab-overlap: -2;
    -GtkNotebook-tab-curvature: $default_padding;
    -GtkWidget-scroll-arrow-hlength: $(my_floor(2.5 * subcell_size));
    -GtkWidget-scroll-arrow-vlength: $(my_floor(2.5 * subcell_size));
}

notebook arrow {
    color: @white;
}

notebook tab {
    background: @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 label {
    color: @white;
    padding: $(subcell_size)px 0;
}

notebook tab button label {
    color: @black;
}

notebook tab:checked {
    background: @toolbar_grey;
}

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

/* Browse Widgets */

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

/* Control panel */

cpanelsectionview {
    background: @white;
    color: @black;
}

/* TODO:  this should only effect the control panel */
controlpanel image:disabled {
    opacity: $(disabled_opacity);
}

/* Alert */

alert {
    background: @black;
    color: @white;
}

alert *:disabled {
    background: @black;
}

button timeouticon label,
button timeouticon label:hover {
    background: @white;
    color: @button_grey;
    border-radius: $(2 * subcell_size)px;
}

button:active timeouticon label {
    background: @toolbar_grey;
    color: @white;
}

/* Tray */

htray, vtray {
    background: @toolbar_grey;
}

htray * , vtray * { background: @toolbar_grey;}

/* Menus and palettes */

palette,
popover {
    border: $(thickness)px solid @button_grey;
    background: @black;
    color: @white;
}

palette .view {
    color: @black;
}

SugarPaletteMenuWidget {
    background: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

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

SugarPaletteWindow SugarGroupBox *:disabled {
    background: @toolbar_grey;
}

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

    -GtkMenu-scroll-arrow-vlength: $(my_floor(subcell_size/0.7 + 2*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 :  -$(line_width)px;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px;

    border: $(thickness)px solid @button_grey;
}

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

menuitem:hover,
menuitem:hover {
    background: @button_grey;
}

menuitem separator {
    padding: 0;
}

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

tooltip * {
    color: @white;
}

/* Scrollbars */

scrollbar {
    -GtkRange-slider-width: $(subcell_size + 2*thickness);
    -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: @button_grey;
    border-width: 0px;
}

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

scrollbar slider:active {
    background: @text_field_grey;
}

/* Progress bars */

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

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

toolbar progressbar trough,
palette progressbar trough {
    background: @black;
}

/* Separators */

separator {
    background-color: @button_grey;
}

/* If we set the background for these, we get about 20px high
   separator.  But we only want a border through the middle */
palette widget separator {
    border-top: $(thickness)px solid @button_grey;
    margin-top: $(subcell_size)px;
    margin-bottom: -$(subcell_size)px;
    background: transparent;
}

/* Tool buttons */

toolbar,
toolbarbox {
    padding: 0;
    background: @toolbar_grey;
    color: @white;
}

/* FIXME: I needed to add the margin in the gtk3.20 port, but that wasn't
          there in the code before.  I ended up having to reduce the border
          to 2px */
toolbutton button,
filtertoolbutton button {
    border-style: none;
    background: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding - default_padding)px;
    margin: $(default_padding)px;
}

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

toolbutton button:disabled,
filtertoolbutton button:disabled {
    opacity: $(disabled_opacity);
}

toolbutton button:hover,
filtertoolbutton button:hover {
    padding: $(toolbutton_padding - 2 - default_padding)px;
    border-width: 2px;
    border-style: solid;
    background-clip: padding-box;
}

/* Edge case of the wifi toolbuttons in the frame */
framewindow toolbutton button:hover {
    padding: $(toolbutton_padding - default_padding)px;
    border-width: 0;
}

toolbutton button:hover:not(:checked),
toolbutton button:hover:not(:checked) * {
    background: @black;
}

toolbutton button:checked,
toolbutton button:checked * {
    background: @button_grey;
    border-radius: $(toolbutton_padding)px;
}

toolbutton button:checked:hover,
toolbutton button:checked:hover * {
    background: @button_grey;
    border-color: @button_grey;
}

toolbutton button:active,
filtertoolbutton button:active,
toolbutton button:active * {
    background: @button_grey;
    color: white;
    border-radius: $(toolbutton_padding)px;
}

palette scrolledwindow {
    background: @black;
}

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

/* Scales */

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

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

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

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

scale trough:focus {
    border-color: @white;
}

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

scale slider,
scale slider:active {
    background: transparent;
}

scale slider {
    -gtk-icon-source: url("assets/scale-slider.svg");
    margin: -$(my_floor(subcell_size/2))px;
    min-width: $(2*subcell_size)px;
    min-height: $(2*subcell_size)px;
}

scale slider:active {
    -gtk-icon-source: url("assets/scale-slider-active.svg");
    margin: -$(my_floor(subcell_size/2))px;
    min-width: $(2*subcell_size)px;
    min-height: $(2*subcell_size)px;
}

/* Radio and check buttons */

check:hover {
    background: alpha(@theme_base_color, 0.0);
}

toolbar check,
palette check {
    color: @theme_base_color;
    border: 1px solid @theme_base_color;
}

radio,
radio row:selected,
radio row:selected:focus {
    -gtk-icon-source: url("assets/radio.svg");
    background: alpha(@theme_base_color, 0.0);
}

radio:selected,
radio:selected row:selected,
radio:selected row:selected:focus {
    -gtk-icon-source: url("assets/radio-selected.svg");
}

radio:active,
radio row:selected:active,
radio row:selected:focus:active {
    -gtk-icon-source: url("assets/radio-active.svg");
}

radio:checked,
radio row:selected:checked,
radio row:selected:focus:checked {
   -gtk-icon-source: url("assets/radio-active.svg");
}

radio:active:selected,
radio:selected row:selected:active,
radio:selected row:selected:focus:active {
    -gtk-icon-source: url("assets/radio-active-selected.svg");
}

radio:checked:selected,
radio:selected row:selected:checked,
radio:selected row:selected:focus:checked {
    -gtk-icon-source: url("assets/radio-active-selected.svg");
}

check,
check row:selected,
check row:selected:focus {
    -gtk-icon-source: url("assets/checkbox-unchecked.svg");
    background: alpha(@theme_base_color, 0.0);
}

check:selected,
check:selected row:selected,
check:selected row:selected:focus {
    -gtk-icon-source: url("assets/checkbox-unchecked-selected.svg");
}

check:active,
check row:selected:active,
check row:selected:focus:active {
    -gtk-icon-source: url("assets/checkbox-checked.svg");
}

check:checked,
check row:selected:checked,
check row:selected:focus:checked {
    -gtk-icon-source: url("assets/checkbox-checked.svg");
}

check:active:selected,
check:selected row:selected:active,
check:selected row:selected:focus:active {
    -gtk-icon-source: url("assets/checkbox-checked-selected.svg");
}

check:checked:selected,
check:selected row:selected:checked,
check:selected row:selected:focus:checked {
    background-image: url("assets/checkbox-checked-selected.svg");
    -gtk-icon-source: url("assets/checkbox-checked-selected.svg");
}

/* Tool items */

toolitem {
    /* arrow in the toolbarbox */
    color: @white;
}

.palette-down,
.toolbar-down {
    /* outline of the toolbarbutton when palette is expanded */
    border-style: solid;
    border-width: 2px;
    border-color: @button_grey;
}

/* GtkImage */

image {
    background: transparent;
}

/* Sugar Frame Window */

framewindow {
    background: @toolbar_grey;
}

/* Sugar Canvas icon */

canvasicon {
    border-color: transparent;
    border-radius: $(4 * thickness)px;
    border-width: 2px;
    border-style: solid;
    background: transparent;
}

canvasicon:hover {
    border-color: @zoom_views_prelight;
    background: @zoom_views_prelight;
}

canvasicon.toggle-press {
    border-color: @zoom_views_active;
    background: @zoom_views_active;
}

/* Sugar CellRenderer Icons */

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

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

.cell.sugar-icon-cell:active {
    background: @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: @white;
}

.cell.trough {
    background: @text_field_grey;
}

/* Text cursor handles */

cursor-handle.top,
cursor-handle.bottom {
    background: transparent;
    border-radius: 10px;
    border-width: $(thickness)px;
    border-color: @panel_grey;
    border-style: solid;
}

cursor-handle.bottom {
    -gtk-icon-source: url("assets/cursor-handle-bottom.svg");
}

cursor-handle.top {
    -gtk-icon-source: url("assets/cursor-handle-top.svg");
}

cursor-handle.insertion-cursor {
    -gtk-icon-source: url("assets/cursor-handle-insertion.svg");
}

/* Application specific properties */

EvView {
    background: @panel_grey;
}

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


Wrap physics functions and property in Presence to translate between raw string form and regular scripting objects.

/*  Sirikata
 *  presence.em
 *
 *  Copyright (c) 2011, Bhupesh Chandra
 *  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.
 */

/* Add functions to the prototype for presence */

(function() {
     // Just a cache, but the underlying system will also store copies
     // and give us the existing one. This is necessary to make reset
     // transparent.
     var sims = {};

     system.__presence_constructor__.prototype.runSimulation = function(name) {
         if (!sims[this]) sims[this] = {};
         if (!sims[this][name]) sims[this][name] = this._runSimulation(name);
         return sims[this][name];
     };
 })();

system.__presence_constructor__.prototype.getSimulation =
    system.__presence_constructor__.prototype.runSimulation;

system.__presence_constructor__.prototype.onProxAdded =
function (funcToCallback)
{
    system.__sys_onProxAdded(this,funcToCallback);
};

system.__presence_constructor__.prototype.onProxRemoved =
function (funcToCallback)
{
    system.__sys_onProxRemoved(this,funcToCallback);
};

Object.defineProperty(system.__presence_constructor__.prototype, "position",
                      {
                          get: function() { return this.getPosition(); },
                          set: function() { return this.setPosition.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "velocity",
                      {
                          get: function() { return this.getVelocity(); },
                          set: function() { return this.setVelocity.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "orientation",
                      {
                          get: function() { return this.getOrientation(); },
                          set: function() { return this.setOrientation.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "orientationVel",
                      {
                          get: function() { return this.getOrientationVel(); },
                          set: function() { return this.setOrientationVel.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "scale",
                      {
                          get: function() { return this.getScale(); },
                          set: function() { return this.setScale.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "mesh",
                      {
                          get: function() { return this.getMesh(); },
                          set: function() { return this.setMesh.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "physics",
                      {
                          get: function() { return this.getPhysics(); },
                          set: function() { return this.updatePhysics.apply(this, arguments); },
                          enumerable: true
                      }
);

system.__presence_constructor__.prototype.__prettyPrintFieldsData__ = [
    "position", "velocity",
    "orientation", "orientationVel",
    "scale", "mesh", "physics"
];
system.__presence_constructor__.prototype.__prettyPrintFields__ = function() {
    return this.__prettyPrintFieldsData__;
};

{
  /** @namespace presence */
  var presence = function()
  {
    /**@function
       @description Returns the position of the presence
       @return the vector corresponding to the position of the presence
       @type util.Vec3
    */
    presence.prototype.getPosition = function(){}

    /**@function
       @description sets the position of the presence to a new value
       @param newpos  The new position of the presence to set to

    */

    presence.prototype.setPosition = function(/** util.Vec3 */ newpos){}

    /**@function
       @description sets the velocity of the presence to a new value
       @param newvel  The new velocity of the presence to set to

    */

    presence.prototype.setVelocity = function(/** util.Vec3 */ newvel){}

      /**
       @function
       Disconnects presence from space.  Emerson presence object still exists, but its isConnected field
       evaluates to false.
       */
      presence.prototype.disconnect = function()
      {
      };

      /**
       @return Object containing all data associated with this presence.  Fields of returned object:
            @param {string} sporef,
            @param {vec3} pos,
       @param {vec3} vel,
       @param {string} posTime,
       @param {quaternion} orient,
       @param {quaternion} orientVel,
       @param {string} orientTime,
       @param {string} mesh,
       @param {number} scale,
       @param {boolean} isCleared ,
       @param {uint32} contextId,
       @param {boolean} isConnected,
       @param {function, null} connectedCallback,
       @param {boolean} isSuspended,
       @param {vec3} suspendedVelocity,
       @param {quaternion} suspendedOrientationVelocity,
       @param {function, null} proxAddedCallback,
       @param {function, null} proxRemovedCallback,
       */
      presence.prototype.getAllData = function()
      {
      };

    /**@function
       @description Returns the velocity of the presence
       @return the vector corresponding to the velocity of the presence
       @type util.Vec3
    */

    presence.prototype.getVelocity = function(){}

    /**@function
       @description sets the orientation of the presence to a new value
       @param newvel  The new orientation of the presence to set to
    */

    presence.prototype.setOrientation = function(/** util.Quaternion */ newpos){}

    /**@function
       @description Returns the orientation of the presence
       @return the quaternion corresponding to the  of the presence
       @type util.Quaternion
    */

    presence.prototype.getOrientation = function(){}

    /**@function
       @description sets the orientation velocity (both angular velocity and the axis of rotation)
          of the presence to a new value
       @param newvel  The new orientation velocity of the presence to set to
    */

    presence.prototype.setOrientationVel = function(/** util.Quaternion */ newvel){}

    /**@function
       @description Returns the orientation velocity of the presence
       @return the quaternion corresponding to the  orientation velocity of the presence
       @type util.Quaternion
    */

    presence.prototype.getOrientationVel = function(){}

    /**@function
       @description scales of the mesh of the presence to a new value
       @param scale the factor by which to scale the mesh
    */

    presence.prototype.setScale = function(/**Number */ scale){}

    /**@function
       @description Returns the factor by which the mesh has been scaled from the original size
       @return  the factor by which the mesh has been scaled from the original size
       @type Number
    */

    presence.prototype.getScale = function(){}

    /**@function
       @description sets the mesh of the presence
       @param newmesh The url for the mesh to set to
    */

    presence.prototype.setMesh = function(/**String */ newmesh){}

     /**@function
       @description Returns the mesh of the presence
       @return  the url for the mesh of the presence
       @type String
    */

    presence.prototype.getMesh = function(){}

      /** @function
       *  @description Returns the (decoded) physics settings of the presence.
       *  @type Object
       */
      presence.prototype.getPhysics = function() {};

      /** @function
       *  @description Sets the physics parameters for the
       *               presence. Unspecified values will use the
       *               defaults even if they were previously
       *               specified.
       *  @param newphys The new physics settings for the object
       */
      presence.prototype.setPhysics = function(/**Object */ newphys) {};

      /** @function
       *  @description Updates the physics parameters for the presence. Unspecified values use their previous setting.
       *  @param newphys The new physics settings for the object.
       */
      presence.prototype.updatePhysics = function(/**Object */ newphys) {};

      /** @function
       @return Returns the identifier for the space that the presence is in.
       @type String
       */
      presence.prototype.getSpaceID = function(){}

      /** @function
       @return Returns the identifier for the presence in the space that it's in.
       @type String
       */
      presence.prototype.getPresenceID = function(){}

  };

}

(function() {

      // Physics is stored as an opaque string. This takes care of
      // encoding and decoding that string. Currently the only
      // supported type is JSON encoding.

      var decodePhysics = function(phy) {
          if (phy.length == 0) return {};
          return JSON.parse(phy);
      };

      var encodePhysics = function(phy) {
          return JSON.stringify(phy);
      };

      var valid_physics_fields = ['treatment', 'bounds', 'mass'];
      var orig_get_physics = system.__presence_constructor__.prototype.getPhysics;
      var orig_set_physics = system.__presence_constructor__.prototype.setPhysics;

      system.__presence_constructor__.prototype.getPhysics = function() {
          return decodePhysics( orig_get_physics.apply(this) );
      };

      system.__presence_constructor__.prototype.setPhysics = function(update) {
          var raw = {};
          for(var i in valid_physics_fields) {
              if (valid_physics_fields[i] in update)
                  raw[valid_physics_fields[i]] = update[valid_physics_fields[i]];
          }
          return orig_set_physics.apply(this, [encodePhysics(raw)]);
      };

      system.__presence_constructor__.prototype.updatePhysics = function(update) {
          var raw = this.getPhysics();
          for(var i in valid_physics_fields) {
              if (valid_physics_fields[i] in update)
                  raw[valid_physics_fields[i]] = update[valid_physics_fields[i]];
          }
          return orig_set_physics.apply(this, [encodePhysics(raw)]);
      };

})();

/*  Sirikata
 *  presence.em
 *
 *  Copyright (c) 2011, Bhupesh Chandra
 *  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.
 */

/* Add functions to the prototype for presence */

(function() {
     // Just a cache, but the underlying system will also store copies
     // and give us the existing one. This is necessary to make reset
     // transparent.
     var sims = {};

     system.__presence_constructor__.prototype.runSimulation = function(name) {
         if (!sims[this]) sims[this] = {};
         if (!sims[this][name]) sims[this][name] = this._runSimulation(name);
         return sims[this][name];
     };
 })();

system.__presence_constructor__.prototype.getSimulation =
    system.__presence_constructor__.prototype.runSimulation;

system.__presence_constructor__.prototype.onProxAdded =
function (funcToCallback)
{
    system.__sys_onProxAdded(this,funcToCallback);
};

system.__presence_constructor__.prototype.onProxRemoved =
function (funcToCallback)
{
    system.__sys_onProxRemoved(this,funcToCallback);
};

Object.defineProperty(system.__presence_constructor__.prototype, "position",
                      {
                          get: function() { return this.getPosition(); },
                          set: function() { return this.setPosition.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "velocity",
                      {
                          get: function() { return this.getVelocity(); },
                          set: function() { return this.setVelocity.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "orientation",
                      {
                          get: function() { return this.getOrientation(); },
                          set: function() { return this.setOrientation.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "orientationVel",
                      {
                          get: function() { return this.getOrientationVel(); },
                          set: function() { return this.setOrientationVel.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "scale",
                      {
                          get: function() { return this.getScale(); },
                          set: function() { return this.setScale.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "mesh",
                      {
                          get: function() { return this.getMesh(); },
                          set: function() { return this.setMesh.apply(this, arguments); },
                          enumerable: true
                      }
);

Object.defineProperty(system.__presence_constructor__.prototype, "physics",
                      {
                          get: function() { return this.getPhysics(); },
                          set: function() { return this.setPhysics.apply(this, arguments); },
                          enumerable: true
                      }
);

system.__presence_constructor__.prototype.__prettyPrintFieldsData__ = [
    "position", "velocity",
    "orientation", "orientationVel",
    "scale", "mesh", "physics"
];
system.__presence_constructor__.prototype.__prettyPrintFields__ = function() {
    return this.__prettyPrintFieldsData__;
};

{
  /** @namespace presence */
  var presence = function()
  {
    /**@function
       @description Returns the position of the presence
       @return the vector corresponding to the position of the presence
       @type util.Vec3
    */
    presence.prototype.getPosition = function(){}

    /**@function
       @description sets the position of the presence to a new value
       @param newpos  The new position of the presence to set to

    */

    presence.prototype.setPosition = function(/** util.Vec3 */ newpos){}

    /**@function
       @description sets the velocity of the presence to a new value
       @param newvel  The new velocity of the presence to set to

    */

    presence.prototype.setVelocity = function(/** util.Vec3 */ newvel){}

      /**
       @function
       Disconnects presence from space.  Emerson presence object still exists, but its isConnected field
       evaluates to false.
       */
      presence.prototype.disconnect = function()
      {
      };

      /**
       @return Object containing all data associated with this presence.  Fields of returned object:
            @param {string} sporef,
            @param {vec3} pos,
       @param {vec3} vel,
       @param {string} posTime,
       @param {quaternion} orient,
       @param {quaternion} orientVel,
       @param {string} orientTime,
       @param {string} mesh,
       @param {number} scale,
       @param {boolean} isCleared ,
       @param {uint32} contextId,
       @param {boolean} isConnected,
       @param {function, null} connectedCallback,
       @param {boolean} isSuspended,
       @param {vec3} suspendedVelocity,
       @param {quaternion} suspendedOrientationVelocity,
       @param {function, null} proxAddedCallback,
       @param {function, null} proxRemovedCallback,
       */
      presence.prototype.getAllData = function()
      {
      };

    /**@function
       @description Returns the velocity of the presence
       @return the vector corresponding to the velocity of the presence
       @type util.Vec3
    */

    presence.prototype.getVelocity = function(){}

    /**@function
       @description sets the orientation of the presence to a new value
       @param newvel  The new orientation of the presence to set to
    */

    presence.prototype.setOrientation = function(/** util.Quaternion */ newpos){}

    /**@function
       @description Returns the orientation of the presence
       @return the quaternion corresponding to the  of the presence
       @type util.Quaternion
    */

    presence.prototype.getOrientation = function(){}

    /**@function
       @description sets the orientation velocity (both angular velocity and the axis of rotation)
          of the presence to a new value
       @param newvel  The new orientation velocity of the presence to set to
    */

    presence.prototype.setOrientationVel = function(/** util.Quaternion */ newvel){}

    /**@function
       @description Returns the orientation velocity of the presence
       @return the quaternion corresponding to the  orientation velocity of the presence
       @type util.Quaternion
    */

    presence.prototype.getOrientationVel = function(){}

    /**@function
       @description scales of the mesh of the presence to a new value
       @param scale the factor by which to scale the mesh
    */

    presence.prototype.setScale = function(/**Number */ scale){}

    /**@function
       @description Returns the factor by which the mesh has been scaled from the original size
       @return  the factor by which the mesh has been scaled from the original size
       @type Number
    */

    presence.prototype.getScale = function(){}

    /**@function
       @description sets the mesh of the presence
       @param newmesh The url for the mesh to set to
    */

    presence.prototype.setMesh = function(/**String */ newmesh){}

     /**@function
       @description Returns the mesh of the presence
       @return  the url for the mesh of the presence
       @type String
    */

    presence.prototype.getMesh = function(){}

      /** @function
       @return Returns the identifier for the space that the presence is in.
       @type String
       */
      presence.prototype.getSpaceID = function(){}

      /** @function
       @return Returns the identifier for the presence in the space that it's in.
       @type String
       */
      presence.prototype.getPresenceID = function(){}

  };

}


fix action arguments for rescheduling aborted builds

@(SNIPPET(
    'builder_system-groovy',
    command=
"""// VERFIY THAT FREE DISK SPACE THRESHOLD IS NOT VIOLATED
import hudson.AbortException
import hudson.model.Cause.UserIdCause
import hudson.model.ParametersAction
import hudson.node_monitors.AbstractDiskSpaceMonitor
import hudson.node_monitors.DiskSpaceMonitor
import hudson.node_monitors.DiskSpaceMonitorDescriptor.DiskSpace
import hudson.node_monitors.Messages
import hudson.node_monitors.NodeMonitor
import java.util.logging.Logger
import org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildAction

println "# BEGIN SECTION: Check free disk space"

def node = build.getBuiltOn()
def root_path = node.getRootPath()
def usable_disk_space = root_path.getUsableDiskSpace()
println "Usable disk space = " + usable_disk_space + " bytes"

try {
  for (node_monitor in NodeMonitor.getAll()) {
    if (!(node_monitor instanceof DiskSpaceMonitor)) continue

    free_space_threshold = node_monitor.getThresholdBytes()
    println "Free space threshold = " + free_space_threshold + " bytes"

    if (usable_disk_space < free_space_threshold) {
      println "Free disk space is lower than the threshold, aborting this build, disabling the slave, rescheduling the job"

      // mark slave as offline and log event
      def computer = node.toComputer()
      def disk_space = new DiskSpace(root_path.getRemote(), usable_disk_space)
      disk_space.setTriggered(node_monitor.getClass(), true);
      if (node_monitor.getDescriptor().markOffline(computer, disk_space)) {
        def logger = Logger.getLogger(AbstractDiskSpaceMonitor.class.getName())
        logger.warning(Messages.DiskSpaceMonitor_MarkedOffline(computer.getName()))
      }

      // schedule rebuild
      def project = build.getProject()
      if (!project.isInQueue()) {
        project.scheduleBuild(1, new UserIdCause(), *build.getActions(ParametersAction))
      }

      // add badge to build
      build.getActions().add(GroovyPostbuildAction.createInfoBadge("Free disk space is too low, disabled slave '" + computer.name + "', rescheduled job"))

      // abort this build
      throw new AbortException("Aborting build due to free disk space being lower than the threshold")
    }
    break
  }
} finally {
  println "# END SECTION"
}
""",
    script_file=None,
    classpath='',
))@

@(SNIPPET(
    'builder_system-groovy',
    command=
"""// VERFIY THAT FREE DISK SPACE THRESHOLD IS NOT VIOLATED
import hudson.AbortException
import hudson.model.Cause.UserIdCause
import hudson.model.ParametersAction
import hudson.node_monitors.AbstractDiskSpaceMonitor
import hudson.node_monitors.DiskSpaceMonitor
import hudson.node_monitors.DiskSpaceMonitorDescriptor.DiskSpace
import hudson.node_monitors.Messages
import hudson.node_monitors.NodeMonitor
import java.util.logging.Logger
import org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildAction

println "# BEGIN SECTION: Check free disk space"

def node = build.getBuiltOn()
def root_path = node.getRootPath()
def usable_disk_space = root_path.getUsableDiskSpace()
println "Usable disk space = " + usable_disk_space + " bytes"

try {
  for (node_monitor in NodeMonitor.getAll()) {
    if (!(node_monitor instanceof DiskSpaceMonitor)) continue

    free_space_threshold = node_monitor.getThresholdBytes()
    println "Free space threshold = " + free_space_threshold + " bytes"

    if (usable_disk_space < free_space_threshold) {
      println "Free disk space is lower than the threshold, aborting this build, disabling the slave, rescheduling the job"

      // mark slave as offline and log event
      def computer = node.toComputer()
      def disk_space = new DiskSpace(root_path.getRemote(), usable_disk_space)
      disk_space.setTriggered(node_monitor.getClass(), true);
      if (node_monitor.getDescriptor().markOffline(computer, disk_space)) {
        def logger = Logger.getLogger(AbstractDiskSpaceMonitor.class.getName())
        logger.warning(Messages.DiskSpaceMonitor_MarkedOffline(computer.getName()))
      }

      // schedule rebuild
      def project = build.getProject()
      if (!project.isInQueue()) {
        project.scheduleBuild(1, new UserIdCause(), build.getActions(ParametersAction))
      }

      // add badge to build
      build.getActions().add(GroovyPostbuildAction.createInfoBadge("Free disk space is too low, disabled slave '" + computer.name + "', rescheduled job"))

      // abort this build
      throw new AbortException("Aborting build due to free disk space being lower than the threshold")
    }
    break
  }
} finally {
  println "# END SECTION"
}
""",
    script_file=None,
    classpath='',
))@


Fix Emscripten compilation

Fix Emscripten compilation

Some changes in the compiler seem to have broken the compilation

CXXFLAGS = -O2 -Werror -Wall -g -c -std=c++14 -ferror-limit=1  -s USE_ZLIB=1 -s USE_LIBPNG=1 -I../OpenALSoundSystem -I../noudar-core/noudar-core/include -Iincludes -I../noudar-rendering -I../gles2-renderer/ -IX11-version -I../glm -I../nanovg/src -s ALLOW_MEMORY_GROWTH=1 -DGLM_FORCE_RADIANS
LDFLAGS =  -O2 -s USE_ZLIB=1 -s USE_LIBPNG=1 -s --preload-file res --use-preload-plugins  -s ALLOW_MEMORY_GROWTH=1
CXX = em++
CC = emcc
OBJS = X11-version/main.o \
    ../noudar-core/noudar-core/src/Vec2i.o \
    ../noudar-core/noudar-core/src/CActor.o \
    ../noudar-core/noudar-core/src/CMonster.o \
    ../noudar-core/noudar-core/src/CMonsterGenerator.o \
    ../noudar-core/noudar-core/src/CTeam.o \
    ../noudar-core/noudar-core/src/CMap.o \
    ../noudar-core/noudar-core/src/CHuntApproach.o \
    ../noudar-core/noudar-core/src/CCharacter.o \
    ../noudar-core/noudar-core/src/CCharacterArchetype.o \
    ../noudar-core/noudar-core/src/Common.o \
    ../noudar-core/noudar-core/src/CDoorway.o \
    ../noudar-core/noudar-core/src/IFileLoaderDelegate.o \
    ../noudar-core/noudar-core/src/IMapElement.o \
    ../noudar-core/noudar-core/src/CGame.o \
    ../noudar-core/noudar-core/src/CItem.o \
    ../noudar-core/noudar-core/src/CStorageItem.o \
    ../noudar-core/noudar-core/src/CPlainFileLoader.o \
    ../noudar-core/noudar-core/src/CGameDelegate.o \
    ../noudar-core/noudar-core/src/commands/IGameCommand.o \
    ../noudar-core/noudar-core/src/commands/CMoveActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CLoadNewLevelCommand.o \
    ../noudar-core/noudar-core/src/commands/CTurnActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CQuitCommand.o \
    ../noudar-core/noudar-core/src/commands/CNullCommand.o \
    ../noudar-core/noudar-core/src/commands/CEndTurnCommand.o \
        ../noudar-core/noudar-core/src/commands/CPickItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CDropItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CUseCurrentItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCycleNextItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCyclePreviousItemCommand.o \
    	       ../noudar-rendering/RenderingJobSnapshotAdapter.o \
               ../noudar-rendering/GameNativeAPI.o \
               ../noudar-rendering/OverlayNanoVGRenderer.o \
               ../noudar-rendering/DungeonGLES2Renderer.o \
               ../noudar-rendering/LightningStrategy.o \
               ../noudar-rendering/VisibilityStrategy.o \
               ../noudar-rendering/NoudarDungeonSnapshot.o \
               ../noudar-rendering/SplatAnimation.o \
               ../noudar-rendering/Animation.o \
               ../noudar-rendering/AnimationStep.o \
               ../noudar-rendering/GraphicNode.o \
                ../noudar-rendering/CTile3DProperties.o \
               ../noudar-rendering/NoudarGLES2Bridge.o \
               X11-version/WindowOperationsX11.o \
               X11-version/x11_emscripten.o \
               ../gles2-renderer/Logger.o \
               ../gles2-renderer/CLerp.o \
               ../gles2-renderer/Camera.o \
               ../gles2-renderer/Trig.o \
               ../gles2-renderer/TrigBatch.o \
               ../gles2-renderer/NativeBitmap.o \
               ../gles2-renderer/WavefrontOBJReader.o \
               ../gles2-renderer/VBORenderingJob.o \
               ../gles2-renderer/WavefrontMaterialReader.o \
               ../gles2-renderer/Texture.o \
               ../gles2-renderer/LoadPNG.o \
                ../nanovg/src/nanovg.o \
                ../OpenALSoundSystem/SoundClip.o \
                ../OpenALSoundSystem/SoundEmitter.o \
                ../OpenALSoundSystem/SoundListener.o \
                ../OpenALSoundSystem/SoundUtils.o

TARGET = noudar.html

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

all:	$(TARGET)

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

CXXFLAGS = -O2 -Werror -Wall -g -c -std=c++0x -ferror-limit=1  -s USE_ZLIB=1 -s USE_LIBPNG=1 -I../OpenALSoundSystem -I../noudar-core/noudar-core/include -Iincludes -I../noudar-rendering -I../gles2-renderer/ -IX11-version -I../glm -I../nanovg/src -s ALLOW_MEMORY_GROWTH=1 -DGLM_FORCE_RADIANS
LDFLAGS =  -O2 -s USE_ZLIB=1 -s USE_LIBPNG=1 -s --preload-file res --use-preload-plugins  -s ALLOW_MEMORY_GROWTH=1
CXX = em++
CC = emcc
OBJS = X11-version/main.o \
    ../noudar-core/noudar-core/src/Vec2i.o \
    ../noudar-core/noudar-core/src/CActor.o \
    ../noudar-core/noudar-core/src/CMonster.o \
    ../noudar-core/noudar-core/src/CMonsterGenerator.o \
    ../noudar-core/noudar-core/src/CTeam.o \
    ../noudar-core/noudar-core/src/CMap.o \
    ../noudar-core/noudar-core/src/CHuntApproach.o \
    ../noudar-core/noudar-core/src/CCharacter.o \
    ../noudar-core/noudar-core/src/CCharacterArchetype.o \
    ../noudar-core/noudar-core/src/Common.o \
    ../noudar-core/noudar-core/src/CDoorway.o \
    ../noudar-core/noudar-core/src/IFileLoaderDelegate.o \
    ../noudar-core/noudar-core/src/IMapElement.o \
    ../noudar-core/noudar-core/src/CGame.o \
    ../noudar-core/noudar-core/src/CItem.o \
    ../noudar-core/noudar-core/src/CStorageItem.o \
    ../noudar-core/noudar-core/src/CPlainFileLoader.o \
    ../noudar-core/noudar-core/src/CGameDelegate.o \
    ../noudar-core/noudar-core/src/commands/IGameCommand.o \
    ../noudar-core/noudar-core/src/commands/CMoveActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CLoadNewLevelCommand.o \
    ../noudar-core/noudar-core/src/commands/CTurnActorCommand.o \
    ../noudar-core/noudar-core/src/commands/CQuitCommand.o \
    ../noudar-core/noudar-core/src/commands/CNullCommand.o \
    ../noudar-core/noudar-core/src/commands/CEndTurnCommand.o \
        ../noudar-core/noudar-core/src/commands/CPickItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CDropItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CUseCurrentItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCycleNextItemCommand.o \
        ../noudar-core/noudar-core/src/commands/CCyclePreviousItemCommand.o \
    	       ../noudar-rendering/RenderingJobSnapshotAdapter.o \
               ../noudar-rendering/GameNativeAPI.o \
               ../noudar-rendering/OverlayNanoVGRenderer.o \
               ../noudar-rendering/DungeonGLES2Renderer.o \
               ../noudar-rendering/LightningStrategy.o \
               ../noudar-rendering/VisibilityStrategy.o \
               ../noudar-rendering/NoudarDungeonSnapshot.o \
               ../noudar-rendering/SplatAnimation.o \
               ../noudar-rendering/Animation.o \
               ../noudar-rendering/AnimationStep.o \
               ../noudar-rendering/GraphicNode.o \
                ../noudar-rendering/CTile3DProperties.o \
               ../noudar-rendering/NoudarGLES2Bridge.o \
               X11-version/WindowOperationsX11.o \
               X11-version/x11_emscripten.o \
               ../gles2-renderer/Logger.o \
               ../gles2-renderer/CLerp.o \
               ../gles2-renderer/Camera.o \
               ../gles2-renderer/Trig.o \
               ../gles2-renderer/TrigBatch.o \
               ../gles2-renderer/NativeBitmap.o \
               ../gles2-renderer/WavefrontOBJReader.o \
               ../gles2-renderer/VBORenderingJob.o \
               ../gles2-renderer/WavefrontMaterialReader.o \
               ../gles2-renderer/Texture.o \
               ../gles2-renderer/LoadPNG.o \
                ../nanovg/src/nanovg.o \
                ../OpenALSoundSystem/SoundClip.o \
                ../OpenALSoundSystem/SoundEmitter.o \
                ../OpenALSoundSystem/SoundListener.o \
                ../OpenALSoundSystem/SoundUtils.o

TARGET = noudar.html

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

all:	$(TARGET)

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


fix linter regression in generated code

fix linter regression in generated code

// generated from rosidl_generator_c/resource/msg__functions.c.em
// generated code does not contain a copyright notice

@#######################################################################
@# EmPy template for generating <msg>__functions.c files
@#
@# Context:
@#  - spec (rosidl_parser.MessageSpecification)
@#    Parsed specification of the .msg file
@#  - subfolder (string)
@#    The subfolder / subnamespace of the message
@#    Either 'msg' or 'srv'
@#  - get_header_filename_from_msg_name (function)
@#######################################################################
@
@{
from rosidl_generator_c import get_typename_of_base_type
from rosidl_generator_c import primitive_value_to_c
from rosidl_generator_c import value_to_c

msg_typename = '%s__%s__%s' % (spec.base_type.pkg_name, subfolder, spec.base_type.type)
array_typename = '%s__Array' % msg_typename
}@
#include "@(spec.base_type.pkg_name)/@(subfolder)/@(get_header_filename_from_msg_name(spec.base_type.type))__functions.h"

#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

@#######################################################################
@# include message dependencies
@#######################################################################
@{
from collections import OrderedDict
includes = OrderedDict()
for field in spec.fields:
    if field.type.is_primitive_type():
        if field.type.type == 'string':
            field_names = includes.setdefault('rosidl_generator_c/string_functions.h', [])
            field_names.append(field.name)
        else:
            if field.type.is_dynamic_array():
                field_names = includes.setdefault('rosidl_generator_c/primitives_array_functions.h', [])
                field_names.append(field.name)
    else:
        field_names = includes.setdefault(
            '%s/msg/%s__functions.h' %
            (field.type.pkg_name, get_header_filename_from_msg_name(field.type.type)),
            [])
        field_names.append(field.name)
}@
@[if includes]@
// include message dependencies
@[  for header_file, field_names in includes.items()]@
@[    for field_name in field_names]@
// @(field_name)
@[    end for]@
#include "@(header_file)"
@[  end for]@

@[end if]@
@
@#######################################################################
@# message functions
@#######################################################################
bool
@(msg_typename)__init(@(msg_typename) * msg)
{
  if (!msg) {
    return false;
  }
@{
label_prefix = 'abort_init_'
last_label_index = 0
lines = []
abort_lines = []
for field in spec.fields:
    lines.append('// ' + field.name)
    if not field.type.is_array:
        # non-array field
        if field.type.is_primitive_type():
            if field.type.type == 'string':
                lines.append('if (!rosidl_generator_c__String__init(&msg->%s)) {' % field.name)
                lines.append('  %s__destroy(msg);' % msg_typename)
                lines.append('  return false;')
                lines.append('}')
                if field.default_value is not None:
                    lines.append('{')
                    value = value_to_c(field.type, field.default_value)
                    lines.append('  bool success = rosidl_generator_c__String__assign(&msg->%s, %s);' % (field.name, value))
                    lines.append('  if (!success) {')
                    lines.append('    goto %s%s;' % (label_prefix, last_label_index))
                    abort_lines[0:0] = [
                      '  rosidl_generator_c__String__fini(&msg->%s);' % field.name,
                      '%s%d:' % (label_prefix, last_label_index),
                    ]
                    last_label_index += 1
                    lines.append('  }')
                    lines.append('}')
            elif field.default_value is not None:
                # set default value of primitive type
                lines.append('msg->%s = %s;' % (field.name, value_to_c(field.type, field.default_value)))

        else:
            # initialize the sub message
            lines.append('if (!%s__%s__%s__init(&msg->%s)) {' % (field.type.pkg_name, 'msg', field.type.type, field.name))
            lines.append('  %s__destroy(msg);' % msg_typename)
            lines.append('  return false;')
            lines.append('}')
    elif field.type.is_fixed_size_array():
        if field.type.is_primitive_type() and field.type.type != 'string':
            if field.default_value is not None:
                # set default value for each array element
                for i, default_value in enumerate(field.default_value):
                    lines.append('msg->%s[%d] = %s;' % (field.name, i, primitive_value_to_c(field.type.type, field.default_value[i])))
        if not field.type.is_primitive_type() or field.type.type == 'string':
            # initialize each array element
            lines.append('for (size_t i = 0; i < %d; ++i) {' % field.type.array_size)
            lines.append('  if (!%s__init(&msg->%s[i])) {' % (get_typename_of_base_type(field.type), field.name))
            lines.append('    %s__destroy(msg);' % msg_typename)
            lines.append('    return false;')
            lines.append('  }')
            lines.append('}')

    else:  # dynamic array
        if field.default_value is None:
            # initialize the dynamic array with a capacity of zero
            lines.append('if (!%s__Array__init(&msg->%s, 0)) {' % (get_typename_of_base_type(field.type), field.name))
            lines.append('  %s__destroy(msg);' % msg_typename)
            lines.append('  return false;')
            lines.append('}')
        else:
            # initialize the dynamic array with the number of default values
            lines.append('{')
            lines.append('  bool success = %s__Array__init(&msg->%s, %d);' % (get_typename_of_base_type(field.type), field.name, len(field.default_value)))
            lines.append('  if (!success) {')
            lines.append('    goto %s%d;' % (label_prefix, last_label_index))
            abort_lines[0:0] = [
              '  %s__Array__fini(&msg->%s);' % (get_typename_of_base_type(field.type), field.name),
              '%s%d:' % (label_prefix, last_label_index),
            ]
            last_label_index += 1
            lines.append('  }')
            lines.append('}')
            # set default value for each array element
            for i, default_value in enumerate(field.default_value):
                lines.append('msg->%s.data[%d] = %s;' % (field.name, i, primitive_value_to_c(field.type.type, field.default_value[i])))

for line in lines:
    print('  ' + line)
}@
  return true;
@{
if abort_lines:
    # remove lines before the first label since they are unreachable
    while not abort_lines[0].startswith(label_prefix):
        abort_lines.pop(0)
    for line in abort_lines:
        print(line)
    print('  return false;')
}@
}

void
@(msg_typename)__fini(@(msg_typename) * msg)
{
  if (!msg) {
    return;
  }
@{
lines = []
for field in spec.fields:
    lines.append('// ' + field.name)
    if not field.type.is_array:
        # non-array field
        if not field.type.is_primitive_type() or field.type.type == 'string':
            # finalize sub messages and strings
            lines.append('%s__fini(&msg->%s);' % (get_typename_of_base_type(field.type), field.name))

    elif field.type.is_fixed_size_array():
        if not field.type.is_primitive_type() or field.type.type == 'string':
            lines.append('for (size_t i = 0; i < %d; ++i) {' % field.type.array_size)
            # initialize each array element
            lines.append('  %s__fini(&msg->%s[i]);' % (get_typename_of_base_type(field.type), field.name))
            lines.append('}')

    else:
        # finalize the dynamic array
        lines.append('%s__Array__fini(&msg->%s);' % (get_typename_of_base_type(field.type), field.name))
for line in lines:
    print('  ' + line)
}@
}

@(msg_typename) *
@(msg_typename)__create()
{
  @(msg_typename) * msg = (@(msg_typename) *)malloc(sizeof(@(msg_typename)));
  if (!msg) {
    return NULL;
  }
  memset(msg, 0, sizeof(@(msg_typename)));
  bool success = @(msg_typename)__init(msg);
  if (!success) {
    free(msg);
    return NULL;
  }
  return msg;
}

void
@(msg_typename)__destroy(@(msg_typename) * msg)
{
  if (msg) {
    @(msg_typename)__fini(msg);
  }
  free(msg);
}

@#######################################################################
@# array functions
@#######################################################################
bool
@(array_typename)__init(@(array_typename) * array, size_t size)
{
  if (!array) {
    return false;
  }
  @(msg_typename) * data = NULL;
  if (size) {
    data = (@(msg_typename) *)calloc(size, sizeof(@(msg_typename)));
    if (!data) {
      return false;
    }
    // initialize all array elements
    size_t i;
    for (i = 0; i < size; ++i) {
      bool success = @(msg_typename)__init(&data[i]);
      if (!success) {
        break;
      }
    }
    if (i < size) {
      // if initialization failed finalize the already initialized array elements
      for (; i > 0; --i) {
        @(msg_typename)__fini(&data[i - 1]);
      }
      free(data);
      return false;
    }
  }
  array->data = data;
  array->size = size;
  array->capacity = size;
  return true;
}

void
@(array_typename)__fini(@(array_typename) * array)
{
  if (!array) {
    return;
  }
  if (array->data) {
    // ensure that data and capacity values are consistent
    assert(array->capacity > 0);
    // finalize all array elements
    for (size_t i = 0; i < array->capacity; ++i) {
      @(msg_typename)__fini(&array->data[i]);
    }
    free(array->data);
    array->data = NULL;
    array->size = 0;
    array->capacity = 0;
  } else {
    // ensure that data, size, and capacity values are consistent
    assert(0 == array->size);
    assert(0 == array->capacity);
  }
}

@(array_typename) *
@(array_typename)__create(size_t size)
{
  @(array_typename) * array = (@(array_typename) *)malloc(sizeof(@(array_typename)));
  if (!array) {
    return NULL;
  }
  bool success = @(array_typename)__init(array, size);
  if (!success) {
    free(array);
    return NULL;
  }
  return array;
}

void
@(array_typename)__destroy(@(array_typename) * array)
{
  if (array) {
    @(array_typename)__fini(array);
  }
  free(array);
}

// generated from rosidl_generator_c/resource/msg__functions.c.em
// generated code does not contain a copyright notice

@#######################################################################
@# EmPy template for generating <msg>__functions.c files
@#
@# Context:
@#  - spec (rosidl_parser.MessageSpecification)
@#    Parsed specification of the .msg file
@#  - subfolder (string)
@#    The subfolder / subnamespace of the message
@#    Either 'msg' or 'srv'
@#  - get_header_filename_from_msg_name (function)
@#######################################################################
@
@{
from rosidl_generator_c import get_typename_of_base_type
from rosidl_generator_c import primitive_value_to_c
from rosidl_generator_c import value_to_c

msg_typename = '%s__%s__%s' % (spec.base_type.pkg_name, subfolder, spec.base_type.type)
array_typename = '%s__Array' % msg_typename
}@
#include "@(spec.base_type.pkg_name)/@(subfolder)/@(get_header_filename_from_msg_name(spec.base_type.type))__functions.h"

#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

@#######################################################################
@# include message dependencies
@#######################################################################
@{
from collections import OrderedDict
includes = OrderedDict()
for field in spec.fields:
    if field.type.is_primitive_type():
        if field.type.type == 'string':
            field_names = includes.setdefault('rosidl_generator_c/string_functions.h', [])
            field_names.append(field.name)
        else:
            if field.type.is_dynamic_array():
                field_names = includes.setdefault('rosidl_generator_c/primitives_array_functions.h', [])
                field_names.append(field.name)
    else:
        field_names = includes.setdefault(
            '%s/msg/%s__functions.h' %
            (field.type.pkg_name, get_header_filename_from_msg_name(field.type.type)),
            [])
        field_names.append(field.name)
}@
@[if includes]@
// include message dependencies
@[  for header_file, field_names in includes.items()]@
@[    for field_name in field_names]@
// @(field_name)
@[    end for]@
#include "@(header_file)"
@[  end for]@

@[end if]@
@
@#######################################################################
@# message functions
@#######################################################################
bool
@(msg_typename)__init(@(msg_typename) * msg)
{
  if (!msg) {
    return false;
  }
@{
label_prefix = 'abort_init_'
last_label_index = 0
lines = []
abort_lines = []
for field in spec.fields:
    lines.append('// ' + field.name)
    if not field.type.is_array:
        # non-array field
        if field.type.is_primitive_type():
            if field.type.type == 'string':
                lines.append('if (!rosidl_generator_c__String__init(&msg->%s)) {' % field.name)
                lines.append('  %s__destroy(msg);' % msg_typename)
                lines.append('  return false;')
                lines.append('}')
                if field.default_value is not None:
                    lines.append('{')
                    value = value_to_c(field.type, field.default_value)
                    lines.append('  bool success = rosidl_generator_c__String__assign(&msg->%s, %s);' % (field.name, value))
                    lines.append('  if (!success) {')
                    lines.append('    goto %s%s;' % (label_prefix, last_label_index))
                    abort_lines[0:0] = [
                      '  rosidl_generator_c__String__fini(&msg->%s);' % field.name,
                      '%s%d:' % (label_prefix, last_label_index),
                    ]
                    last_label_index += 1
                    lines.append('  }')
                    lines.append('}')
            elif field.default_value is not None:
                # set default value of primitive type
                lines.append('msg->%s = %s;' % (field.name, value_to_c(field.type, field.default_value)))

        else:
            # initialize the sub message
            lines.append('if (!%s__%s__%s__init(&msg->%s)) {' % (field.type.pkg_name, 'msg', field.type.type, field.name))
            lines.append('  %s__destroy(msg);' % msg_typename)
            lines.append('  return false;')
            lines.append('}')
    elif field.type.is_fixed_size_array():
        if field.type.is_primitive_type() and field.type.type != 'string':
            if field.default_value is not None:
                # set default value for each array element
                for i, default_value in enumerate(field.default_value):
                    lines.append('msg->%s[%d] = %s;' % (field.name, i, primitive_value_to_c(field.type.type, field.default_value[i])))
        if not field.type.is_primitive_type() or field.type.type == 'string':
            # initialize each array element
            lines.append('for (size_t i = 0; i < %d; ++i) {' % field.type.array_size)
            lines.append('  if(!%s__init(&msg->%s[i])) {' % (get_typename_of_base_type(field.type), field.name))
            lines.append('    %s__destroy(msg);' % msg_typename)
            lines.append('    return false;')
            lines.append('  }')
            lines.append('}')

    else:  # dynamic array
        if field.default_value is None:
            # initialize the dynamic array with a capacity of zero
            lines.append('if (!%s__Array__init(&msg->%s, 0)) {' % (get_typename_of_base_type(field.type), field.name))
            lines.append('  %s__destroy(msg);' % msg_typename)
            lines.append('  return false;')
            lines.append('}')
        else:
            # initialize the dynamic array with the number of default values
            lines.append('{')
            lines.append('  bool success = %s__Array__init(&msg->%s, %d);' % (get_typename_of_base_type(field.type), field.name, len(field.default_value)))
            lines.append('  if (!success) {')
            lines.append('    goto %s%d;' % (label_prefix, last_label_index))
            abort_lines[0:0] = [
              '  %s__Array__fini(&msg->%s);' % (get_typename_of_base_type(field.type), field.name),
              '%s%d:' % (label_prefix, last_label_index),
            ]
            last_label_index += 1
            lines.append('  }')
            lines.append('}')
            # set default value for each array element
            for i, default_value in enumerate(field.default_value):
                lines.append('msg->%s.data[%d] = %s;' % (field.name, i, primitive_value_to_c(field.type.type, field.default_value[i])))

for line in lines:
    print('  ' + line)
}@
  return true;
@{
if abort_lines:
    # remove lines before the first label since they are unreachable
    while not abort_lines[0].startswith(label_prefix):
        abort_lines.pop(0)
    for line in abort_lines:
        print(line)
    print('  return false;')
}@
}

void
@(msg_typename)__fini(@(msg_typename) * msg)
{
  if (!msg) {
    return;
  }
@{
lines = []
for field in spec.fields:
    lines.append('// ' + field.name)
    if not field.type.is_array:
        # non-array field
        if not field.type.is_primitive_type() or field.type.type == 'string':
            # finalize sub messages and strings
            lines.append('%s__fini(&msg->%s);' % (get_typename_of_base_type(field.type), field.name))

    elif field.type.is_fixed_size_array():
        if not field.type.is_primitive_type() or field.type.type == 'string':
            lines.append('for (size_t i = 0; i < %d; ++i) {' % field.type.array_size)
            # initialize each array element
            lines.append('  %s__fini(&msg->%s[i]);' % (get_typename_of_base_type(field.type), field.name))
            lines.append('}')

    else:
        # finalize the dynamic array
        lines.append('%s__Array__fini(&msg->%s);' % (get_typename_of_base_type(field.type), field.name))
for line in lines:
    print('  ' + line)
}@
}

@(msg_typename) *
@(msg_typename)__create()
{
  @(msg_typename) * msg = (@(msg_typename) *)malloc(sizeof(@(msg_typename)));
  if (!msg) {
    return NULL;
  }
  memset(msg, 0, sizeof(@(msg_typename)));
  bool success = @(msg_typename)__init(msg);
  if (!success) {
    free(msg);
    return NULL;
  }
  return msg;
}

void
@(msg_typename)__destroy(@(msg_typename) * msg)
{
  if (msg) {
    @(msg_typename)__fini(msg);
  }
  free(msg);
}

@#######################################################################
@# array functions
@#######################################################################
bool
@(array_typename)__init(@(array_typename) * array, size_t size)
{
  if (!array) {
    return false;
  }
  @(msg_typename) * data = NULL;
  if (size) {
    data = (@(msg_typename) *)calloc(size, sizeof(@(msg_typename)));
    if (!data) {
      return false;
    }
    // initialize all array elements
    size_t i;
    for (i = 0; i < size; ++i) {
      bool success = @(msg_typename)__init(&data[i]);
      if (!success) {
        break;
      }
    }
    if (i < size) {
      // if initialization failed finalize the already initialized array elements
      for (; i > 0; --i) {
        @(msg_typename)__fini(&data[i - 1]);
      }
      free(data);
      return false;
    }
  }
  array->data = data;
  array->size = size;
  array->capacity = size;
  return true;
}

void
@(array_typename)__fini(@(array_typename) * array)
{
  if (!array) {
    return;
  }
  if (array->data) {
    // ensure that data and capacity values are consistent
    assert(array->capacity > 0);
    // finalize all array elements
    for (size_t i = 0; i < array->capacity; ++i) {
      @(msg_typename)__fini(&array->data[i]);
    }
    free(array->data);
    array->data = NULL;
    array->size = 0;
    array->capacity = 0;
  } else {
    // ensure that data, size, and capacity values are consistent
    assert(0 == array->size);
    assert(0 == array->capacity);
  }
}

@(array_typename) *
@(array_typename)__create(size_t size)
{
  @(array_typename) * array = (@(array_typename) *)malloc(sizeof(@(array_typename)));
  if (!array) {
    return NULL;
  }
  bool success = @(array_typename)__init(array, size);
  if (!success) {
    free(array);
    return NULL;
  }
  return array;
}

void
@(array_typename)__destroy(@(array_typename) * array)
{
  if (array) {
    @(array_typename)__fini(array);
  }
  free(array);
}