3.20: Fix scrollbar styling

3.20: Fix scrollbar 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 {
    -GtkScrollbar-has-forward-stepper: false;
    -GtkScrollbar-has-backward-stepper: false;
}

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;
    min-width: $(subcell_size)px;
    min-height: $(3*subcell_size)px;
}

scrollbar.fine-tune slider {
    min-width: $(subcell_size / 2 + thickness)px;
    margin-left: $(subcell_size / 4)px;
    margin-right: $(subcell_size / 4)px;
}

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

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


Fixing GET spacing. Was causing invalid requests.

if (typeof(std) === 'undefined')
    std = { };

(function()
 {

     std.http = {    };

     /**
      @param {string} url If doesn't include a port, automatically
      append 80 to it.
      @param {function} cb Callback to execute on http success or
      failure.  Takes two arguments: bool indicating whether the
      request was successful or not and a data object that contains
      response headers and data.  On failure data obj is undefined.
      See system.basicHttpGet for full fields of object.
      @param {object} (optional) Each index of object corresponds to
      another field to send in http header.  The values of object
      should be strings and should correspond to the value that
      corresponds to the http request header's field.  Eg:
      additionalHeaders = {'user-agent': 'mozilla'}; Sends a header
      with user-agent: mozilla.  basicGet takes care of appending \r\n
      to headers.
      */
     std.http.basicGet = function(url, cb, additionalHeaders)
     {
         var decodedURL = decodeWebURL(url);
         if (decodedURL == null)
             throw new Error('Error in get: could not decode url passed in as first argument.');

         var headers = "GET " + decodedURL.resource + " HTTP/1.1\r\n";
         headers += "Host: " + decodedURL.host + " \r\n";
         headers += "Accept: */*\r\n";
         headers += "Connection: close\r\n\r\n";

         if (typeof(additionalHeaders) != 'undefined')
         {
             for (var s in additionalHeaders)
                 headers += s +': ' + additionalHeaders[s] + '\r\n' ;
         }

         system.basicHttpGet('get', decodedURL.protoHostPort(), headers,cb);
     };

     function URL(proto, host, resource, port)
     {
         this.proto = proto;
         this.host = host;
         this.resource = resource;
         this.port = port;

     }

     URL.prototype.protoHostPort = function()
     {
         return this.proto + '://' + this.host + ':' + this.port;
     };

     URL.prototype.printParts = function()
     {
         system.print('\n');
         system.print('Proto:' + this.proto);
         system.print('\n');
         system.print('Host:' + this.host);
         system.print('\n');
         system.print('Resource:' + this.resource);
         system.print('\n');
         system.print('Port:' + this.port);
         system.print('\n\n');
     };

     function decodeWebURL(urlIn)
     {

         //to get protocol: take everything to the left of ://.  If ://
         //does not exist, return null
         var port = '80';
         var resource = '/';

         var protSep = '://';
         var indexProtoEnd =  urlIn.search(protSep);
         if (indexProtoEnd == -1)
             return null;

         var proto = urlIn.substr(0,indexProtoEnd);
         urlIn = urlIn.substr(indexProtoEnd + protSep.length);

         var host = urlIn;
         var havePort = false;
         //if we have a port separator, everything to the left is the host;
         var portSep = ':';
         var indexPortBegin = urlIn.search(portSep);
         if (indexPortBegin != -1)
         {
             havePort = true;
             host = urlIn.substr(0,indexPortBegin);
             urlIn = urlIn.substr(indexPortBegin + portSep.length);
         }

         var resourceSep = '/';
         var resourceBeginIndex = urlIn.search(resourceSep);
         if (resourceBeginIndex != -1)
         {
             //port is everything to left of resourceSep
             if (havePort)
                 port = urlIn.substr(0,resourceBeginIndex);
             else
                 host = urlIn.substr(0,resourceBeginIndex);

             resource += urlIn.substr(resourceBeginIndex + resourceSep.length);
         }
         else
         {
             if (havePort)
                 port = urlIn;
         }
         return new URL(proto, host, resource, port);
     }

 })();

if (typeof(std) === 'undefined')
    std = { };

(function()
 {

     std.http = {    };

     /**
      @param {string} url If doesn't include a port, automatically
      append 80 to it.
      @param {function} cb Callback to execute on http success or
      failure.  Takes two arguments: bool indicating whether the
      request was successful or not and a data object that contains
      response headers and data.  On failure data obj is undefined.
      See system.basicHttpGet for full fields of object.
      @param {object} (optional) Each index of object corresponds to
      another field to send in http header.  The values of object
      should be strings and should correspond to the value that
      corresponds to the http request header's field.  Eg:
      additionalHeaders = {'user-agent': 'mozilla'}; Sends a header
      with user-agent: mozilla.  basicGet takes care of appending \r\n
      to headers.
      */
     std.http.basicGet = function(url, cb, additionalHeaders)
     {
         var decodedURL = decodeWebURL(url);
         if (decodedURL == null)
             throw new Error('Error in get: could not decode url passed in as first argument.');

         var headers ="GET  "+ decodedURL.resource + "  HTTP/1.1\r\n";
         headers += "Host: " + decodedURL.host +" \r\n";
         headers += "Accept: */*\r\n";
         headers += "Connection: close\r\n\r\n";

         if (typeof(additionalHeaders) != 'undefined')
         {
             for (var s in additionalHeaders)
                 headers += s +': ' + additionalHeaders[s] + '\r\n' ;
         }

         system.basicHttpGet('get', decodedURL.protoHostPort(), headers,cb);
     };

     function URL(proto, host, resource, port)
     {
         this.proto = proto;
         this.host = host;
         this.resource = resource;
         this.port = port;

     }

     URL.prototype.protoHostPort = function()
     {
         return this.proto + '://' + this.host + ':' + this.port;
     };

     URL.prototype.printParts = function()
     {
         system.print('\n');
         system.print('Proto:' + this.proto);
         system.print('\n');
         system.print('Host:' + this.host);
         system.print('\n');
         system.print('Resource:' + this.resource);
         system.print('\n');
         system.print('Port:' + this.port);
         system.print('\n\n');
     };

     function decodeWebURL(urlIn)
     {

         //to get protocol: take everything to the left of ://.  If ://
         //does not exist, return null
         var port = '80';
         var resource = '/';

         var protSep = '://';
         var indexProtoEnd =  urlIn.search(protSep);
         if (indexProtoEnd == -1)
             return null;

         var proto = urlIn.substr(0,indexProtoEnd);
         urlIn = urlIn.substr(indexProtoEnd + protSep.length);

         var host = urlIn;
         var havePort = false;
         //if we have a port separator, everything to the left is the host;
         var portSep = ':';
         var indexPortBegin = urlIn.search(portSep);
         if (indexPortBegin != -1)
         {
             havePort = true;
             host = urlIn.substr(0,indexPortBegin);
             urlIn = urlIn.substr(indexPortBegin + portSep.length);
         }

         var resourceSep = '/';
         var resourceBeginIndex = urlIn.search(resourceSep);
         if (resourceBeginIndex != -1)
         {
             //port is everything to left of resourceSep
             if (havePort)
                 port = urlIn.substr(0,resourceBeginIndex);
             else
                 host = urlIn.substr(0,resourceBeginIndex);

             resource += urlIn.substr(resourceBeginIndex + resourceSep.length);
         }
         else
         {
             if (havePort)
                 port = urlIn;
         }
         return new URL(proto, host, resource, port);
     }

 })();


Fix the indentation for the warnings job snippet (#838)

    <io.jenkins.plugins.analysis.core.steps.IssuesRecorder plugin="warnings-ng@@8.1.0">
      <analysisTools>
        <io.jenkins.plugins.analysis.warnings.Cmake>
          <id></id>
          <name></name>
          <pattern>@[if build_tool == 'colcon']ws/log/build_*/*/stdout_stderr.log@[end if]</pattern>
          <reportEncoding></reportEncoding>
          <skipSymbolicLinks>false</skipSymbolicLinks>
        </io.jenkins.plugins.analysis.warnings.Cmake>
        <io.jenkins.plugins.analysis.warnings.Gcc4>
          <id></id>
          <name></name>
          <pattern>@[if build_tool == 'colcon']ws/log/build_*/*/stdout_stderr.log@[end if]</pattern>
          <reportEncoding></reportEncoding>
          <skipSymbolicLinks>false</skipSymbolicLinks>
        </io.jenkins.plugins.analysis.warnings.Gcc4>
      </analysisTools>
      <sourceCodeEncoding></sourceCodeEncoding>
      <sourceDirectory></sourceDirectory>
      <ignoreQualityGate>false</ignoreQualityGate>
      <ignoreFailedBuilds>true</ignoreFailedBuilds>
      <referenceJobName>-</referenceJobName>
      <failOnError>false</failOnError>
      <healthy>0</healthy>
      <unhealthy>0</unhealthy>
      <minimumSeverity plugin="analysis-model-api@@8.0.1">
        <name>LOW</name>
      </minimumSeverity>
      <filters>
        <io.jenkins.plugins.analysis.core.filter.ExcludeMessage>
          <pattern>^Manually-specified variables were not used by the project:$</pattern>
        </io.jenkins.plugins.analysis.core.filter.ExcludeMessage>
      </filters>
      <isEnabledForFailure>false</isEnabledForFailure>
      <isAggregatingResults>false</isAggregatingResults>
      <isBlameDisabled>false</isBlameDisabled>
      <isForensicsDisabled>false</isForensicsDisabled>
@[if unstable_threshold != '']@
      <qualityGates>
        <io.jenkins.plugins.analysis.core.util.QualityGate>
          <threshold>@unstable_threshold</threshold>
          <type>TOTAL</type>
          <status>WARNING</status>
        </io.jenkins.plugins.analysis.core.util.QualityGate>
      </qualityGates>
@[else]@
      <qualityGates/>
@[end if]@
      <trendChartType>AGGREGATION_TOOLS</trendChartType>
    </io.jenkins.plugins.analysis.core.steps.IssuesRecorder>


fix reconfigure groovy for non-release jobs

import difflib.DiffUtils
import groovy.io.FileType
import hudson.AbortException
import java.io.StringBufferInputStream
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.stream.StreamSource
import jenkins.model.Jenkins
import org.apache.xml.serialize.OutputFormat
import org.apache.xml.serialize.XMLSerializer
import org.xml.sax.InputSource

// job types with their prefix and a list of job names
// used to remove obsolete jobs with the same prefix which are not part of the list
job_prefixes_and_names = [:]
@[for job_type in sorted(job_prefixes_and_names.keys())]@
job_prefixes_and_names['@job_type'] = [
    'job_prefix': '@(job_prefixes_and_names[job_type][0])',
    'job_names': [],
]
@{
job_names = sorted(job_prefixes_and_names[job_type][1])
group_size = 1000
}@
// group job names in chunks of @group_size to not exceed groovy limits
@[for i in range(0, len(job_names), group_size)]@
def add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_names) {
@[for j in range(i, min(i + group_size, len(job_names)))]@
job_names << '@(job_names[j])'
@[end for]@
}
add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_prefixes_and_names['@job_type'].job_names)
@[end for]@
@[end for]@

println '# BEGIN SECTION: Groovy script - reconfigure'
dry_run = @('true' if dry_run else 'false')
dry_run_suffix = dry_run ? ' (dry run)' : ''

@[if vars().get('expected_num_views')]@
// reconfigure views
println '# BEGIN SUBSECTION: reconfigure @(expected_num_views) views'
created_views = 0
updated_views = 0
skipped_views = 0

view_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/view_configs'

def view_dir = new File(view_config_dir)
def views = view_dir.listFiles()
views.sort()

if (views.size() != @(expected_num_views)) {
    println "ERROR: Found different number of view configs than expected!! " + views.size() + " is not @(expected_num_views) as expected."
    // fail this build
    throw new AbortException("Wrong number of view configs")
}

views.each{
    found_view = false
    view_name = it.getName()
    view_config = new File(it.path).getText('UTF-8')
    for (v in Jenkins.instance.views) {
        if (v.name != view_name) continue
        found_view = true
        output_stream= new ByteArrayOutputStream()
        v.writeXml(output_stream)
        reader = new StringReader(output_stream.toString())
        source = new InputSource(reader)

        diff = diff_configs(source, view_config)
        if (!diff) {
            println "Skipped view '" + view_name + "' because the config is the same"
            skipped_views += 1
        } else {
            println "Updating view '" + view_name + "'" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    <<<'

            if (!dry_run) {
                reader = new StringReader(view_config)
                source = new StreamSource(reader)
                v.updateByXml(source)
            }
            updated_views += 1
        }
        break
    }
    if (!found_view) {
        println "Creating view '" + view_name + "'" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(view_config)
            View.createViewFromXML(view_name, stream)
        }
        created_views += 1
    }
}

println 'Created ' + created_views + ' views, updated ' + updated_views + ' views, skipped ' + skipped_views + ' views' + dry_run_suffix + '.'
println '# END SUBSECTION'

@[end if]@
// reconfigure jobs
println '# BEGIN SUBSECTION: reconfigure @(expected_num_jobs) jobs'
created_jobs = 0
updated_jobs = 0
skipped_jobs = 0

job_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/job_configs'

def job_dir = new File(job_config_dir)
def jobs = job_dir.listFiles()
jobs.sort()

if (jobs.size() != @(expected_num_jobs)) {
    println "ERROR: Found different number of job configs than expected!! " + jobs.size() + " is not @(expected_num_jobs) as expected."
    // fail this build
    throw new AbortException("Wrong number of job configs")
}

jobs.each{
    found_project = false
    job_name = it.getName()
    job_config = new File(it.path).getText('UTF-8')
    for (p in Jenkins.instance.allItems) {
        if (p.name != job_name) continue
        found_project = true
        job_config_file = p.getConfigFile()

        diff = diff_configs(job_config_file.getFile(), job_config)
        if (!diff) {
            println "Skipped job '" + job_name + "' because the config is the same"
            skipped_jobs += 1
        } else {
            println "Updating job '" + job_name + "'" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    <<<'

            if (!dry_run) {
                reader = new StringReader(job_config)
                source = new StreamSource(reader)
                p.updateByXml(source)
            }
            updated_jobs += 1
        }
        break
    }
    if (!found_project) {
        println "Creating job '" + job_name + "'" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(job_config)
            Jenkins.instance.createProjectFromXML(job_name, stream)
        }
        created_jobs += 1
    }
}

println 'Created ' + created_jobs + ' jobs, updated ' + updated_jobs + ' jobs, skipped ' + skipped_jobs + ' jobs' + dry_run_suffix + '.'
println '# END SUBSECTION'

// delete obsolete jobs
for (item in job_prefixes_and_names) {
    job_type = item.key
    job_prefix = item.value.job_prefix
    job_names = item.value.job_names
    deleted = 0
    println "# BEGIN SUBSECTION: Groovy script - delete obsolete '" + job_type + "' jobs"
    println "Searching for obsolete jobs starting with '" + job_prefix + "'"
    for (p in Jenkins.instance.allItems) {
        if (!p.name.startsWith(job_prefix)) continue
        if (p.name in job_names) continue
        println "Deleting job '" + p.name + "'" + dry_run_suffix
        if (!dry_run) {
            p.delete()
        }
        deleted += 1
    }
    println 'Deleted ' + deleted + ' jobs' + dry_run_suffix + '.'
    println '# END SUBSECTION'
}

println '# END SECTION'

def diff_configs(current_config, new_config) {

    factory = DocumentBuilderFactory.newInstance()
    builder = factory.newDocumentBuilder()

    current_doc = builder.parse(current_config)

    new_config_stream = new StringBufferInputStream(new_config)
    new_doc = builder.parse(new_config_stream)

    // ignore description which contains a timestamp
    if (current_doc.getElementsByTagName('description')) {
        current_doc.getElementsByTagName('description').item(0).setTextContent('')
    }
    if (new_doc.getElementsByTagName('description')) {
        new_doc.getElementsByTagName('description').item(0).setTextContent('')
    }

    current_string = format_xml(current_doc)
    new_string = format_xml(new_doc)

    if (current_string == new_string) {
        return []
    }

    current_lines = []
    current_string.eachLine { line, count ->
        current_lines << line
    }
    new_lines = []
    new_string.eachLine { line, count ->
        new_lines << line
    }

    patch = DiffUtils.diff(current_lines, new_lines)
    return DiffUtils.generateUnifiedDiff('current config', 'new config', current_lines, patch, 0)
}

def format_xml(doc) {
    format = new OutputFormat(doc)
    format.setIndenting(true)
    format.setIndent(2)
    writer = new StringWriter()
    serializer = new XMLSerializer(writer, format)
    serializer.serialize(doc)
    return writer.toString()
}

import difflib.DiffUtils
import groovy.io.FileType
import hudson.AbortException
import java.io.StringBufferInputStream
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.stream.StreamSource
import jenkins.model.Jenkins
import org.apache.xml.serialize.OutputFormat
import org.apache.xml.serialize.XMLSerializer
import org.xml.sax.InputSource

// job types with their prefix and a list of job names
// used to remove obsolete jobs with the same prefix which are not part of the list
job_prefixes_and_names = [:]
@[for job_type in sorted(job_prefixes_and_names.keys())]@
job_prefixes_and_names['@job_type'] = [
    'job_prefix': '@(job_prefixes_and_names[job_type][0])',
    'job_names': [],
]
@{
job_names = sorted(job_prefixes_and_names[job_type][1])
group_size = 1000
}@
// group job names in chunks of @group_size to not exceed groovy limits
@[for i in range(0, len(job_names), group_size)]@
def add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_names) {
@[for j in range(i, min(i + group_size, len(job_names)))]@
job_names << '@(job_names[j])'
@[end for]@
}
add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_prefixes_and_names['@job_type'].job_names)
@[end for]@
@[end for]@

println '# BEGIN SECTION: Groovy script - reconfigure'
dry_run = @('true' if dry_run else 'false')
dry_run_suffix = dry_run ? ' (dry run)' : ''

// reconfigure views
println '# BEGIN SUBSECTION: reconfigure @(expected_num_views) views'
created_views = 0
updated_views = 0
skipped_views = 0

view_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/view_configs'

def view_dir = new File(view_config_dir)
def views = view_dir.listFiles()
views.sort()

if (views.size() != @(expected_num_views)) {
    println "ERROR: Found different number of view configs than expected!! " + views.size() + " is not @(expected_num_views) as expected."
    // fail this build
    throw new AbortException("Wrong number of view configs")
}

views.each{
    found_view = false
    view_name = it.getName()
    view_config = new File(it.path).getText('UTF-8')
    for (v in Jenkins.instance.views) {
        if (v.name != view_name) continue
        found_view = true
        output_stream= new ByteArrayOutputStream()
        v.writeXml(output_stream)
        reader = new StringReader(output_stream.toString())
        source = new InputSource(reader)

        diff = diff_configs(source, view_config)
        if (!diff) {
            println "Skipped view '" + view_name + "' because the config is the same"
            skipped_views += 1
        } else {
            println "Updating view '" + view_name + "'" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    <<<'

            if (!dry_run) {
                reader = new StringReader(view_config)
                source = new StreamSource(reader)
                v.updateByXml(source)
            }
            updated_views += 1
        }
        break
    }
    if (!found_view) {
        println "Creating view '" + view_name + "'" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(view_config)
            View.createViewFromXML(view_name, stream)
        }
        created_views += 1
    }
}

println 'Created ' + created_views + ' views, updated ' + updated_views + ' views, skipped ' + skipped_views + ' views' + dry_run_suffix + '.'
println '# END SUBSECTION'

// reconfigure jobs
println '# BEGIN SUBSECTION: reconfigure @(expected_num_jobs) jobs'
created_jobs = 0
updated_jobs = 0
skipped_jobs = 0

job_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/job_configs'

def job_dir = new File(job_config_dir)
def jobs = job_dir.listFiles()
jobs.sort()

if (jobs.size() != @(expected_num_jobs)) {
    println "ERROR: Found different number of job configs than expected!! " + jobs.size() + " is not @(expected_num_jobs) as expected."
    // fail this build
    throw new AbortException("Wrong number of job configs")
}

jobs.each{
    found_project = false
    job_name = it.getName()
    job_config = new File(it.path).getText('UTF-8')
    for (p in Jenkins.instance.allItems) {
        if (p.name != job_name) continue
        found_project = true
        job_config_file = p.getConfigFile()

        diff = diff_configs(job_config_file.getFile(), job_config)
        if (!diff) {
            println "Skipped job '" + job_name + "' because the config is the same"
            skipped_jobs += 1
        } else {
            println "Updating job '" + job_name + "'" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    <<<'

            if (!dry_run) {
                reader = new StringReader(job_config)
                source = new StreamSource(reader)
                p.updateByXml(source)
            }
            updated_jobs += 1
        }
        break
    }
    if (!found_project) {
        println "Creating job '" + job_name + "'" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(job_config)
            Jenkins.instance.createProjectFromXML(job_name, stream)
        }
        created_jobs += 1
    }
}

println 'Created ' + created_jobs + ' jobs, updated ' + updated_jobs + ' jobs, skipped ' + skipped_jobs + ' jobs' + dry_run_suffix + '.'
println '# END SUBSECTION'

// delete obsolete jobs
for (item in job_prefixes_and_names) {
    job_type = item.key
    job_prefix = item.value.job_prefix
    job_names = item.value.job_names
    deleted = 0
    println "# BEGIN SUBSECTION: Groovy script - delete obsolete '" + job_type + "' jobs"
    println "Searching for obsolete jobs starting with '" + job_prefix + "'"
    for (p in Jenkins.instance.allItems) {
        if (!p.name.startsWith(job_prefix)) continue
        if (p.name in job_names) continue
        println "Deleting job '" + p.name + "'" + dry_run_suffix
        if (!dry_run) {
            p.delete()
        }
        deleted += 1
    }
    println 'Deleted ' + deleted + ' jobs' + dry_run_suffix + '.'
    println '# END SUBSECTION'
}

println '# END SECTION'

def diff_configs(current_config, new_config) {

    factory = DocumentBuilderFactory.newInstance()
    builder = factory.newDocumentBuilder()

    current_doc = builder.parse(current_config)

    new_config_stream = new StringBufferInputStream(new_config)
    new_doc = builder.parse(new_config_stream)

    // ignore description which contains a timestamp
    if (current_doc.getElementsByTagName('description')) {
        current_doc.getElementsByTagName('description').item(0).setTextContent('')
    }
    if (new_doc.getElementsByTagName('description')) {
        new_doc.getElementsByTagName('description').item(0).setTextContent('')
    }

    current_string = format_xml(current_doc)
    new_string = format_xml(new_doc)

    if (current_string == new_string) {
        return []
    }

    current_lines = []
    current_string.eachLine { line, count ->
        current_lines << line
    }
    new_lines = []
    new_string.eachLine { line, count ->
        new_lines << line
    }

    patch = DiffUtils.diff(current_lines, new_lines)
    return DiffUtils.generateUnifiedDiff('current config', 'new config', current_lines, patch, 0)
}

def format_xml(doc) {
    format = new OutputFormat(doc)
    format.setIndenting(true)
    format.setIndent(2)
    writer = new StringWriter()
    serializer = new XMLSerializer(writer, format)
    serializer.serialize(doc)
    return writer.toString()
}


fixed hands export

fixed hands export

#!/bin/sh

@[if DEVELSPACE]@
export THOR_SCRIPTS=@(PROJECT_SOURCE_DIR)/scripts
@[else]@
export THOR_SCRIPTS=@(CMAKE_INSTALL_PREFIX)/@(CATKIN_PACKAGE_SHARE_DESTINATION)/scripts
@[end if]@
export THOR_ROOT=$(cd "@(CMAKE_SOURCE_DIR)/.."; pwd)

# set THOR_* environment variables
alias l_vt_hand='. l_vt_hand'
alias r_vt_hand='. r_vt_hand'
alias l_thor_mang_hand='. l_thor_mang_hand'
alias r_thor_mang_hand='. r_thor_mang_hand'

# include THOR_scripts hooks
#if [ -d $THOR_SCRIPTS ]; then
#  . $THOR_SCRIPTS/functions.sh
#  . $THOR_SCRIPTS/robot.sh ""

#  _THOR_include "$THOR_SCRIPTS/setup.d/*.sh"
#  _THOR_include "$THOR_SCRIPTS/$HOSTNAME/setup.d/*.sh"

#  if [ -r "$THOR_SCRIPTS/$HOSTNAME/setup.sh" ]; then
#      echo "Including $THOR_SCRIPTS/$HOSTNAME/setup.sh..." >&2
#      . "$THOR_SCRIPTS/$HOSTNAME/setup.sh"
#  fi
#fi

# export additional ROS_PACKAGE_PATH for indigo
if [ "$ROS_DISTRO" = "indigo" ]; then
    export ROS_BOOST_LIB_DIR_NAME=/usr/lib/x86_64-linux-gnu
    export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:$ROS_WORKSPACE/../external
fi

# export some variables
export PATH=$THOR_SCRIPTS/helper:$PATH
export ROS_WORKSPACE=$THOR_ROOT/src
export THOR_MOTION_HOSTNAME="thor-motion"
export THOR_PERCEPTION_HOSTNAME="thor-perception"
export ROBOT_HOSTNAMES="thor-motion thor-perception"
export ROBOT_USER="thor"
export THOR_ROBOT_TYPE="thor_mang_hands"
export LEFT_HAND_TYPE="l_vt_hand"
export RIGHT_HAND_TYPE="r_vt_hand"

# adding ssh keys
if [ -d "$THOR_ROOT/.ssh/" ] && [ "$(ls -A $THOR_ROOT/.ssh/)" ]; then
    #echo "Adding ssh keys from '$THOR_ROOT/.ssh/':"
    for f in $THOR_ROOT/.ssh/*; do
        ssh-add $f
    done
fi

#!/bin/sh

@[if DEVELSPACE]@
export THOR_SCRIPTS=@(PROJECT_SOURCE_DIR)/scripts
@[else]@
export THOR_SCRIPTS=@(CMAKE_INSTALL_PREFIX)/@(CATKIN_PACKAGE_SHARE_DESTINATION)/scripts
@[end if]@
export THOR_ROOT=$(cd "@(CMAKE_SOURCE_DIR)/.."; pwd)

# set THOR_* environment variables
alias l_vt_hand='. l_vt_hand'
alias r_vt_hand='. r_vt_hand'
alias l_thor_mang_hand='. l_thor_mang_hand'
alias r_thor_mang_hand='. r_thor_mang_hand'

# include THOR_scripts hooks
#if [ -d $THOR_SCRIPTS ]; then
#  . $THOR_SCRIPTS/functions.sh
#  . $THOR_SCRIPTS/robot.sh ""

#  _THOR_include "$THOR_SCRIPTS/setup.d/*.sh"
#  _THOR_include "$THOR_SCRIPTS/$HOSTNAME/setup.d/*.sh"

#  if [ -r "$THOR_SCRIPTS/$HOSTNAME/setup.sh" ]; then
#      echo "Including $THOR_SCRIPTS/$HOSTNAME/setup.sh..." >&2
#      . "$THOR_SCRIPTS/$HOSTNAME/setup.sh"
#  fi
#fi

# export additional ROS_PACKAGE_PATH for indigo
if [ "$ROS_DISTRO" = "indigo" ]; then
    export ROS_BOOST_LIB_DIR_NAME=/usr/lib/x86_64-linux-gnu
    export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:$ROS_WORKSPACE/../external
fi

# export some variables
export PATH=$THOR_SCRIPTS/helper:$PATH
export ROS_WORKSPACE=$THOR_ROOT/src
export THOR_MOTION_HOSTNAME="thor-motion"
export THOR_PERCEPTION_HOSTNAME="thor-perception"
export ROBOT_HOSTNAMES="thor-motion thor-perception"
export ROBOT_USER="thor"
export THOR_ROBOT_TYPE="thor_mang_hands"
export THOR_LEFT_HAND_TYPE="l_vt_hand"
export THOR_RIGHT_HAND_TYPE="r_vt_hand"

# adding ssh keys
if [ -d "$THOR_ROOT/.ssh/" ] && [ "$(ls -A $THOR_ROOT/.ssh/)" ]; then
    #echo "Adding ssh keys from '$THOR_ROOT/.ssh/':"
    for f in $THOR_ROOT/.ssh/*; do
        ssh-add $f
    done
fi


fixing syntax error

fixing syntax error

class Herd.ApplicationSerializer extends DS.ActiveModelSerializer
  normalizePayload: (payload) ->
    if payload.child_assets
      if !payload.assets
        payload.assets = []
      payload.assets = payload.assets.concat(payload.child_assets)
      delete payload.child_assets

    payload

class Herd.ApplicationSerializer extends DS.ActiveModelSerializer
  normalizePayload: (payload) ->
    if payload.child_assets

      payload.assets = payload.assets ? payload.assets.concat(payload.child_assets) : payload.child_assets;
      delete payload.child_assets

    payload


Update function prefix (#596)

Update function prefix (#596)

Signed-off-by: Pablo Garrido <1f109d638b993afb237f09eb50ebc5be9e6bfcb3@gmail.com>

@# Included from rosidl_typesupport_introspection_c/resource/idl__type_support.c.em
@{
from rosidl_cmake import convert_camel_case_to_lower_case_underscore
from rosidl_generator_c import idl_structure_type_to_c_include_prefix
from rosidl_parser.definition import AbstractGenericString
from rosidl_parser.definition import AbstractNestedType
from rosidl_parser.definition import AbstractSequence
from rosidl_parser.definition import AbstractString
from rosidl_parser.definition import AbstractWString
from rosidl_parser.definition import Array
from rosidl_parser.definition import BasicType
from rosidl_parser.definition import BoundedSequence
from rosidl_parser.definition import NamespacedType

include_parts = [package_name] + list(interface_path.parents[0].parts) + [
    'detail', convert_camel_case_to_lower_case_underscore(interface_path.stem)]
include_base = '/'.join(include_parts)

header_files = [
    'stddef.h',  # providing offsetof()
    include_base + '__rosidl_typesupport_introspection_c.h',
    package_name + '/msg/rosidl_typesupport_introspection_c__visibility_control.h',
    'rosidl_typesupport_introspection_c/field_types.h',
    'rosidl_typesupport_introspection_c/identifier.h',
    'rosidl_typesupport_introspection_c/message_introspection.h',
    include_base + '__functions.h',
    include_base + '__struct.h',
]

function_prefix = '__'.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name]) + '__rosidl_typesupport_introspection_c'
}@
@[for header_file in header_files]@
@[    if header_file in include_directives]@
// already included above
// @
@[    else]@
@{include_directives.add(header_file)}@
@[    end if]@
@[    if '/' not in header_file]@
#include <@(header_file)>
@[    else]@
#include "@(header_file)"
@[    end if]@
@[end for]@

@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@# Collect necessary include directives for all members
@{
from collections import OrderedDict
includes = OrderedDict()
for member in message.structure.members:
    if isinstance(member.type, AbstractSequence) and isinstance(member.type.value_type, BasicType):
        member_names = includes.setdefault(
            'rosidl_runtime_c/primitives_sequence_functions.h', [])
        member_names.append(member.name)
        continue
    type_ = member.type
    if isinstance(type_, AbstractNestedType):
        type_ = type_.value_type
    if isinstance(type_, AbstractString):
        member_names = includes.setdefault('rosidl_runtime_c/string_functions.h', [])
        member_names.append(member.name)
    elif isinstance(type_, AbstractWString):
        member_names = includes.setdefault(
            'rosidl_runtime_c/u16string_functions.h', [])
        member_names.append(member.name)
    elif isinstance(type_, NamespacedType):
        include_prefix = idl_structure_type_to_c_include_prefix(type_)
        member_names = includes.setdefault(
            include_prefix + '.h', [])
        member_names.append(member.name)
        include_prefix_detail = idl_structure_type_to_c_include_prefix(type_, 'detail')
        member_names = includes.setdefault(
            include_prefix_detail + '__rosidl_typesupport_introspection_c.h', [])
        member_names.append(member.name)
}@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@[if includes]@

// Include directives for member types
@[    for header_file, member_names in includes.items()]@
@[        for member_name in member_names]@
// Member `@(member_name)`
@[        end for]@
@[        if header_file in include_directives]@
// already included above
// @
@[        else]@
@{include_directives.add(header_file)}@
@[        end if]@
#include "@(header_file)"
@[    end for]@
@[end if]@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#ifdef __cplusplus
extern "C"
{
#endif

@
@#######################################################################
@# define callback functions
@#######################################################################
void @(function_prefix)__@(message.structure.namespaced_type.name)_init_function(
  void * message_memory, enum rosidl_runtime_c__message_initialization _init)
{
  // TODO(karsten1987): initializers are not yet implemented for typesupport c
  // see https://github.com/ros2/ros2/issues/397
  (void) _init;
  @('__'.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name]))__init(message_memory);
}

void @(function_prefix)__@(message.structure.namespaced_type.name)_fini_function(void * message_memory)
{
  @('__'.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name]))__fini(message_memory);
}

@[for member in message.structure.members]@
@[  if isinstance(member.type, AbstractNestedType) and isinstance(member.type.value_type, NamespacedType)]@
size_t @(function_prefix)__size_function__@(member.type.value_type.name)__@(member.name)(
  const void * untyped_member)
{
@[    if isinstance(member.type, Array)]@
  (void)untyped_member;
  return @(member.type.size);
@[    else]@
  const @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (const @('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  return member->size;
@[    end if]@
}

const void * @(function_prefix)__get_const_function__@(member.type.value_type.name)__@(member.name)(
  const void * untyped_member, size_t index)
{
@[    if isinstance(member.type, Array)]@
  const @('__'.join(member.type.value_type.namespaced_name())) * member =
    (const @('__'.join(member.type.value_type.namespaced_name())) *)(untyped_member);
  return &member[index];
@[    else]@
  const @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (const @('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  return &member->data[index];
@[    end if]@
}

void * @(function_prefix)__get_function__@(member.type.value_type.name)__@(member.name)(
  void * untyped_member, size_t index)
{
@[    if isinstance(member.type, Array)]@
  @('__'.join(member.type.value_type.namespaced_name())) * member =
    (@('__'.join(member.type.value_type.namespaced_name())) *)(untyped_member);
  return &member[index];
@[    else]@
  @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (@('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  return &member->data[index];
@[    end if]@
}

@[    if isinstance(member.type, AbstractSequence)]@
bool @(function_prefix)__resize_function__@(member.type.value_type.name)__@(member.name)(
  void * untyped_member, size_t size)
{
  @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (@('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  @('__'.join(member.type.value_type.namespaced_name()))__Sequence__fini(member);
  return @('__'.join(member.type.value_type.namespaced_name()))__Sequence__init(member, size);
}

@[    end if]@
@[  end if]@
@[end for]@
static rosidl_typesupport_introspection_c__MessageMember @(function_prefix)__@(message.structure.namespaced_type.name)_message_member_array[@(len(message.structure.members))] = {
@{
for index, member in enumerate(message.structure.members):
    type_ = member.type
    if isinstance(type_, AbstractNestedType):
        type_ = type_.value_type

    print('  {')

    # const char * name_
    print('    "%s",  // name' % member.name)
    if isinstance(type_, BasicType):
        # uint8_t type_id_
        print('    rosidl_typesupport_introspection_c__ROS_TYPE_%s,  // type' % type_.typename.replace(' ', '_').upper())
        # size_t string_upper_bound
        print('    0,  // upper bound of string')
        # const rosidl_generator_c::MessageTypeSupportHandle * members_
        print('    NULL,  // members of sub message')
    elif isinstance(type_, AbstractGenericString):
        # uint8_t type_id_
        if isinstance(type_, AbstractString):
            print('    rosidl_typesupport_introspection_c__ROS_TYPE_STRING,  // type')
        elif isinstance(type_, AbstractWString):
            print('    rosidl_typesupport_introspection_c__ROS_TYPE_WSTRING,  // type')
        else:
            assert False, 'Unknown type: ' + str(type_)
        # size_t string_upper_bound
        print('    %u,  // upper bound of string' % (type_.maximum_size if type_.has_maximum_size() else 0))
        # const rosidl_generator_c::MessageTypeSupportHandle * members_
        print('    NULL,  // members of sub message')
    else:
        # uint8_t type_id_
        print('    rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE,  // type')
        # size_t string_upper_bound
        print('    0,  // upper bound of string')
        # const rosidl_message_type_support_t * members_
        print('    NULL,  // members of sub message (initialized later)')
    # bool is_array_
    print('    %s,  // is array' % ('true' if isinstance(member.type, AbstractNestedType) else 'false'))
    # size_t array_size_
    print('    %u,  // array size' % (member.type.size if isinstance(member.type, Array) else (member.type.maximum_size if isinstance(member.type, BoundedSequence) else 0)))
    # bool is_upper_bound_
    print('    %s,  // is upper bound' % ('true' if isinstance(member.type, BoundedSequence) else 'false'))
    # unsigned long offset_
    print('    offsetof(%s__%s, %s),  // bytes offset in struct' % ('__'.join([package_name] + list(interface_path.parents[0].parts)), message.structure.namespaced_type.name, member.name))
    # void * default_value_
    print('    NULL,  // default value')  # TODO default value to be set

    function_suffix = ('%s__%s' % (member.type.value_type.name, member.name)) if isinstance(member.type, AbstractNestedType) and isinstance(member.type.value_type, NamespacedType) else None

    # size_t(const void *) size_function
    print('    %s,  // size() function pointer' % ('%s__size_function__%s' % (function_prefix, function_suffix) if function_suffix else 'NULL'))
    # const void *(const void *, size_t) get_const_function
    print('    %s,  // get_const(index) function pointer' % ('%s__get_const_function__%s' % (function_prefix, function_suffix) if function_suffix else 'NULL'))
    # void *(void *, size_t) get_function
    print('    %s,  // get(index) function pointer' % ('%s__get_function__%s' % (function_prefix, function_suffix) if function_suffix else 'NULL'))
    # void(void *, size_t) resize_function
    print('    %s  // resize(index) function pointer' % ('%s__resize_function__%s' % (function_prefix, function_suffix) if function_suffix and isinstance(member.type, AbstractSequence) else 'NULL'))

    if index < len(message.structure.members) - 1:
        print('  },')
    else:
        print('  }')
}@
};

static const rosidl_typesupport_introspection_c__MessageMembers @(function_prefix)__@(message.structure.namespaced_type.name)_message_members = {
  "@('__'.join([package_name] + list(interface_path.parents[0].parts)))",  // message namespace
  "@(message.structure.namespaced_type.name)",  // message name
  @(len(message.structure.members)),  // number of fields
  sizeof(@('__'.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name]))),
  @(function_prefix)__@(message.structure.namespaced_type.name)_message_member_array,  // message members
  @(function_prefix)__@(message.structure.namespaced_type.name)_init_function,  // function to initialize message memory (memory has to be allocated)
  @(function_prefix)__@(message.structure.namespaced_type.name)_fini_function  // function to terminate message instance (will not free memory)
};

// this is not const since it must be initialized on first access
// since C does not allow non-integral compile-time constants
static rosidl_message_type_support_t @(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle = {
  0,
  &@(function_prefix)__@(message.structure.namespaced_type.name)_message_members,
  get_message_typesupport_handle_function,
};

ROSIDL_TYPESUPPORT_INTROSPECTION_C_EXPORT_@(package_name)
const rosidl_message_type_support_t *
ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(rosidl_typesupport_introspection_c, @(', '.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name])))() {
@[for i, member in enumerate(message.structure.members)]@
@{
type_ = member.type
if isinstance(type_, AbstractNestedType):
    type_ = type_.value_type
}@
@[    if isinstance(type_, NamespacedType)]@
  @(function_prefix)__@(message.structure.namespaced_type.name)_message_member_array[@(i)].members_ =
    ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(rosidl_typesupport_introspection_c, @(', '.join(type_.namespaced_name())))();
@[    end if]@
@[end for]@
  if (!@(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle.typesupport_identifier) {
    @(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle.typesupport_identifier =
      rosidl_typesupport_introspection_c__identifier;
  }
  return &@(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle;
}
#ifdef __cplusplus
}
#endif

@# Included from rosidl_typesupport_introspection_c/resource/idl__type_support.c.em
@{
from rosidl_cmake import convert_camel_case_to_lower_case_underscore
from rosidl_generator_c import idl_structure_type_to_c_include_prefix
from rosidl_parser.definition import AbstractGenericString
from rosidl_parser.definition import AbstractNestedType
from rosidl_parser.definition import AbstractSequence
from rosidl_parser.definition import AbstractString
from rosidl_parser.definition import AbstractWString
from rosidl_parser.definition import Array
from rosidl_parser.definition import BasicType
from rosidl_parser.definition import BoundedSequence
from rosidl_parser.definition import NamespacedType

include_parts = [package_name] + list(interface_path.parents[0].parts) + [
    'detail', convert_camel_case_to_lower_case_underscore(interface_path.stem)]
include_base = '/'.join(include_parts)

header_files = [
    'stddef.h',  # providing offsetof()
    include_base + '__rosidl_typesupport_introspection_c.h',
    package_name + '/msg/rosidl_typesupport_introspection_c__visibility_control.h',
    'rosidl_typesupport_introspection_c/field_types.h',
    'rosidl_typesupport_introspection_c/identifier.h',
    'rosidl_typesupport_introspection_c/message_introspection.h',
    include_base + '__functions.h',
    include_base + '__struct.h',
]

function_prefix = message.structure.namespaced_type.name + '__rosidl_typesupport_introspection_c'
}@
@[for header_file in header_files]@
@[    if header_file in include_directives]@
// already included above
// @
@[    else]@
@{include_directives.add(header_file)}@
@[    end if]@
@[    if '/' not in header_file]@
#include <@(header_file)>
@[    else]@
#include "@(header_file)"
@[    end if]@
@[end for]@

@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@# Collect necessary include directives for all members
@{
from collections import OrderedDict
includes = OrderedDict()
for member in message.structure.members:
    if isinstance(member.type, AbstractSequence) and isinstance(member.type.value_type, BasicType):
        member_names = includes.setdefault(
            'rosidl_runtime_c/primitives_sequence_functions.h', [])
        member_names.append(member.name)
        continue
    type_ = member.type
    if isinstance(type_, AbstractNestedType):
        type_ = type_.value_type
    if isinstance(type_, AbstractString):
        member_names = includes.setdefault('rosidl_runtime_c/string_functions.h', [])
        member_names.append(member.name)
    elif isinstance(type_, AbstractWString):
        member_names = includes.setdefault(
            'rosidl_runtime_c/u16string_functions.h', [])
        member_names.append(member.name)
    elif isinstance(type_, NamespacedType):
        include_prefix = idl_structure_type_to_c_include_prefix(type_)
        member_names = includes.setdefault(
            include_prefix + '.h', [])
        member_names.append(member.name)
        include_prefix_detail = idl_structure_type_to_c_include_prefix(type_, 'detail')
        member_names = includes.setdefault(
            include_prefix_detail + '__rosidl_typesupport_introspection_c.h', [])
        member_names.append(member.name)
}@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@[if includes]@

// Include directives for member types
@[    for header_file, member_names in includes.items()]@
@[        for member_name in member_names]@
// Member `@(member_name)`
@[        end for]@
@[        if header_file in include_directives]@
// already included above
// @
@[        else]@
@{include_directives.add(header_file)}@
@[        end if]@
#include "@(header_file)"
@[    end for]@
@[end if]@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#ifdef __cplusplus
extern "C"
{
#endif

@
@#######################################################################
@# define callback functions
@#######################################################################
void @(function_prefix)__@(message.structure.namespaced_type.name)_init_function(
  void * message_memory, enum rosidl_runtime_c__message_initialization _init)
{
  // TODO(karsten1987): initializers are not yet implemented for typesupport c
  // see https://github.com/ros2/ros2/issues/397
  (void) _init;
  @('__'.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name]))__init(message_memory);
}

void @(function_prefix)__@(message.structure.namespaced_type.name)_fini_function(void * message_memory)
{
  @('__'.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name]))__fini(message_memory);
}

@[for member in message.structure.members]@
@[  if isinstance(member.type, AbstractNestedType) and isinstance(member.type.value_type, NamespacedType)]@
size_t @(function_prefix)__size_function__@(member.type.value_type.name)__@(member.name)(
  const void * untyped_member)
{
@[    if isinstance(member.type, Array)]@
  (void)untyped_member;
  return @(member.type.size);
@[    else]@
  const @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (const @('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  return member->size;
@[    end if]@
}

const void * @(function_prefix)__get_const_function__@(member.type.value_type.name)__@(member.name)(
  const void * untyped_member, size_t index)
{
@[    if isinstance(member.type, Array)]@
  const @('__'.join(member.type.value_type.namespaced_name())) * member =
    (const @('__'.join(member.type.value_type.namespaced_name())) *)(untyped_member);
  return &member[index];
@[    else]@
  const @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (const @('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  return &member->data[index];
@[    end if]@
}

void * @(function_prefix)__get_function__@(member.type.value_type.name)__@(member.name)(
  void * untyped_member, size_t index)
{
@[    if isinstance(member.type, Array)]@
  @('__'.join(member.type.value_type.namespaced_name())) * member =
    (@('__'.join(member.type.value_type.namespaced_name())) *)(untyped_member);
  return &member[index];
@[    else]@
  @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (@('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  return &member->data[index];
@[    end if]@
}

@[    if isinstance(member.type, AbstractSequence)]@
bool @(function_prefix)__resize_function__@(member.type.value_type.name)__@(member.name)(
  void * untyped_member, size_t size)
{
  @('__'.join(member.type.value_type.namespaced_name()))__Sequence * member =
    (@('__'.join(member.type.value_type.namespaced_name()))__Sequence *)(untyped_member);
  @('__'.join(member.type.value_type.namespaced_name()))__Sequence__fini(member);
  return @('__'.join(member.type.value_type.namespaced_name()))__Sequence__init(member, size);
}

@[    end if]@
@[  end if]@
@[end for]@
static rosidl_typesupport_introspection_c__MessageMember @(function_prefix)__@(message.structure.namespaced_type.name)_message_member_array[@(len(message.structure.members))] = {
@{
for index, member in enumerate(message.structure.members):
    type_ = member.type
    if isinstance(type_, AbstractNestedType):
        type_ = type_.value_type

    print('  {')

    # const char * name_
    print('    "%s",  // name' % member.name)
    if isinstance(type_, BasicType):
        # uint8_t type_id_
        print('    rosidl_typesupport_introspection_c__ROS_TYPE_%s,  // type' % type_.typename.replace(' ', '_').upper())
        # size_t string_upper_bound
        print('    0,  // upper bound of string')
        # const rosidl_generator_c::MessageTypeSupportHandle * members_
        print('    NULL,  // members of sub message')
    elif isinstance(type_, AbstractGenericString):
        # uint8_t type_id_
        if isinstance(type_, AbstractString):
            print('    rosidl_typesupport_introspection_c__ROS_TYPE_STRING,  // type')
        elif isinstance(type_, AbstractWString):
            print('    rosidl_typesupport_introspection_c__ROS_TYPE_WSTRING,  // type')
        else:
            assert False, 'Unknown type: ' + str(type_)
        # size_t string_upper_bound
        print('    %u,  // upper bound of string' % (type_.maximum_size if type_.has_maximum_size() else 0))
        # const rosidl_generator_c::MessageTypeSupportHandle * members_
        print('    NULL,  // members of sub message')
    else:
        # uint8_t type_id_
        print('    rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE,  // type')
        # size_t string_upper_bound
        print('    0,  // upper bound of string')
        # const rosidl_message_type_support_t * members_
        print('    NULL,  // members of sub message (initialized later)')
    # bool is_array_
    print('    %s,  // is array' % ('true' if isinstance(member.type, AbstractNestedType) else 'false'))
    # size_t array_size_
    print('    %u,  // array size' % (member.type.size if isinstance(member.type, Array) else (member.type.maximum_size if isinstance(member.type, BoundedSequence) else 0)))
    # bool is_upper_bound_
    print('    %s,  // is upper bound' % ('true' if isinstance(member.type, BoundedSequence) else 'false'))
    # unsigned long offset_
    print('    offsetof(%s__%s, %s),  // bytes offset in struct' % ('__'.join([package_name] + list(interface_path.parents[0].parts)), message.structure.namespaced_type.name, member.name))
    # void * default_value_
    print('    NULL,  // default value')  # TODO default value to be set

    function_suffix = ('%s__%s' % (member.type.value_type.name, member.name)) if isinstance(member.type, AbstractNestedType) and isinstance(member.type.value_type, NamespacedType) else None

    # size_t(const void *) size_function
    print('    %s,  // size() function pointer' % ('%s__size_function__%s' % (function_prefix, function_suffix) if function_suffix else 'NULL'))
    # const void *(const void *, size_t) get_const_function
    print('    %s,  // get_const(index) function pointer' % ('%s__get_const_function__%s' % (function_prefix, function_suffix) if function_suffix else 'NULL'))
    # void *(void *, size_t) get_function
    print('    %s,  // get(index) function pointer' % ('%s__get_function__%s' % (function_prefix, function_suffix) if function_suffix else 'NULL'))
    # void(void *, size_t) resize_function
    print('    %s  // resize(index) function pointer' % ('%s__resize_function__%s' % (function_prefix, function_suffix) if function_suffix and isinstance(member.type, AbstractSequence) else 'NULL'))

    if index < len(message.structure.members) - 1:
        print('  },')
    else:
        print('  }')
}@
};

static const rosidl_typesupport_introspection_c__MessageMembers @(function_prefix)__@(message.structure.namespaced_type.name)_message_members = {
  "@('__'.join([package_name] + list(interface_path.parents[0].parts)))",  // message namespace
  "@(message.structure.namespaced_type.name)",  // message name
  @(len(message.structure.members)),  // number of fields
  sizeof(@('__'.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name]))),
  @(function_prefix)__@(message.structure.namespaced_type.name)_message_member_array,  // message members
  @(function_prefix)__@(message.structure.namespaced_type.name)_init_function,  // function to initialize message memory (memory has to be allocated)
  @(function_prefix)__@(message.structure.namespaced_type.name)_fini_function  // function to terminate message instance (will not free memory)
};

// this is not const since it must be initialized on first access
// since C does not allow non-integral compile-time constants
static rosidl_message_type_support_t @(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle = {
  0,
  &@(function_prefix)__@(message.structure.namespaced_type.name)_message_members,
  get_message_typesupport_handle_function,
};

ROSIDL_TYPESUPPORT_INTROSPECTION_C_EXPORT_@(package_name)
const rosidl_message_type_support_t *
ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(rosidl_typesupport_introspection_c, @(', '.join([package_name] + list(interface_path.parents[0].parts) + [message.structure.namespaced_type.name])))() {
@[for i, member in enumerate(message.structure.members)]@
@{
type_ = member.type
if isinstance(type_, AbstractNestedType):
    type_ = type_.value_type
}@
@[    if isinstance(type_, NamespacedType)]@
  @(function_prefix)__@(message.structure.namespaced_type.name)_message_member_array[@(i)].members_ =
    ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(rosidl_typesupport_introspection_c, @(', '.join(type_.namespaced_name())))();
@[    end if]@
@[end for]@
  if (!@(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle.typesupport_identifier) {
    @(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle.typesupport_identifier =
      rosidl_typesupport_introspection_c__identifier;
  }
  return &@(function_prefix)__@(message.structure.namespaced_type.name)_message_type_support_handle;
}
#ifdef __cplusplus
}
#endif