Add a border for checkboxes

Add a border for checkboxes

${
import math

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

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

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

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
    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

}

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

    /* A lot of these will probably need to be changed, but this has to
       be done when the exact sizes are known */
    -GtkWidget-interior-focus: 0;

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

    -GtkTextView-interior-focus: 1;

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

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

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

    -GtkRange-activate-slider: 1;

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

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

    -GtkScrolledWindow-scrollbar-spacing: 0;

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

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

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

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

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

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

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

*:insensitive {
    color: @button_grey;
}

/* Backgrounds and windows */

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

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

SugarIntroWindow {
    background-color: @white;
}

SugarIntroWindow {
    color: @black;
}

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

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    padding: $(border)px $(border)px $(border)px $(border)px;
    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

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

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

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

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

/* Spin buttons */

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

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

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

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

/* Toggle buttons */

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

/* Entries and views */

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

GtkTreeView {
    background-color: @button_grey;
}

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

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

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

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

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

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

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

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

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

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

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

/* Frames */

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

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

/* Combo boxes */

GtkComboBox {
    color: @white;
}

GtkComboBox .menu.button {
    border-color: transparent;
    border-radius: 0px;
    border-style: none;
    background-color: transparent;
    color: @white;
}

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

/* Notebooks */

.notebook {
    background-color: @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-color: @selection_grey;
}

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

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

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

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

/* Browse Widgets */

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

/* Control panel */

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

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

/* Alert */

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

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

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

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

/* Tray */

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

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

/* Menus and palettes */

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

SugarPaletteWindowWidget .view {
    color: @black;
}

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

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

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

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

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

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

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

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

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

.menuitem.separator {
    padding: 0;
}

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

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

SugarPaletteHeaderSeparator.menuitem.separator {
    padding: 0;
}

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

.tooltip * {
    color: @white;
}

/* Scrollbars */

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

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

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

/* Progress bars */

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

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

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

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

/* Separators */

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

/* Tool buttons */

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

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

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

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

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

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

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

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

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

SugarPaletteWindowWidget GtkScrolledWindow {
    background-color: @black;
}

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

/* Scales */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/* Radio and check buttons */

GtkCheckButton {
    border: 1px solid @black;
}

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

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

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

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

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

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

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

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

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

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

/* Tool items */

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

/* GtkImage */

GtkImage {
    background-color: transparent;
}

/* Sugar Frame Window */

SugarFrameWindow {
    background-color: @toolbar_grey;
}

/* Sugar Canvas icon */

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

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

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

/* Sugar CellRenderer Icons */

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

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

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

/* CellRendererProgress */

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

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

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

/* Text cursor handles */

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

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

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

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

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

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

${
import math

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

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

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

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
    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

}

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

    /* A lot of these will probably need to be changed, but this has to
       be done when the exact sizes are known */
    -GtkWidget-interior-focus: 0;

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

    -GtkTextView-interior-focus: 1;

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

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

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

    -GtkRange-activate-slider: 1;

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

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

    -GtkScrolledWindow-scrollbar-spacing: 0;

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

    -GtkTreeView-expander-size: $font_height;

    -GtkArrow-arrow-size: 1.0;

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

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

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

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

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

*:insensitive {
    color: @button_grey;
}

/* Backgrounds and windows */

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

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

SugarIntroWindow {
    background-color: @white;
}

SugarIntroWindow {
    color: @black;
}

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

/* Buttons */

.button {
    ${ border = max(0, my_ceil((3*subcell_size/2.0 - icon_small / 2.0))) }
    padding: $(border)px $(border)px $(border)px $(border)px;
    border-width: $(thickness)px;
    border-color: @button_grey;
    border-style: solid;
    border-radius: $(2*subcell_size)px;
    background-color: @button_grey;
    color: @white;
}

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

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

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

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

/* Spin buttons */

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

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

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

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

/* Toggle buttons */

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

/* Entries and views */

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

GtkTreeView {
    background-color: @button_grey;
}

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

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

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

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

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

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

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

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

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

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

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

/* Frames */

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

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

/* Combo boxes */

GtkComboBox {
    color: @white;
}

GtkComboBox .menu.button {
    border-color: transparent;
    border-radius: 0px;
    border-style: none;
    background-color: transparent;
    color: @white;
}

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

/* Notebooks */

.notebook {
    background-color: @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-color: @selection_grey;
}

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

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

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

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

/* Browse Widgets */

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

/* Control panel */

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

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

/* Alert */

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

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

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

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

/* Tray */

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

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

/* Menus and palettes */

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

SugarPaletteWindowWidget .view {
    color: @black;
}

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

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

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

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

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

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

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

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

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

.menuitem.separator {
    padding: 0;
}

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

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

SugarPaletteHeaderSeparator.menuitem.separator {
    padding: 0;
}

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

.tooltip * {
    color: @white;
}

/* Scrollbars */

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

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

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

/* Progress bars */

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

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

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

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

/* Separators */

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

/* Tool buttons */

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

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

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

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

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

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

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

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

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

SugarPaletteWindowWidget GtkScrolledWindow {
    background-color: @black;
}

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

/* Scales */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/* Radio and check buttons */

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

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

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

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

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

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

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

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

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

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

/* Tool items */

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

/* GtkImage */

GtkImage {
    background-color: transparent;
}

/* Sugar Frame Window */

SugarFrameWindow {
    background-color: @toolbar_grey;
}

/* Sugar Canvas icon */

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

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

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

/* Sugar CellRenderer Icons */

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

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

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

/* CellRendererProgress */

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

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

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

/* Text cursor handles */

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

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

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

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

/* Application specific properties */

EvView {
    background-color: @panel_grey;
}

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


Fix buttons colors in the toolbar

Fix buttons colors in the toolbar

${
import math

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

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

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

# These sizes need to be sanity checked ...
if theme == "sugar-xo":
    xo = True
    line_width = 2.25       # 2.25
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    bullet_size = 9.5
    font_height = 24
else: # About 50% smaller
    xo = False
    line_width = 1.125
    thick_line_width = 1.75
    subcell_size = 7
    bullet_size = 5
    font_height = 12

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

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

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

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

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

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

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

    xthickness = $thickness
    ythickness = $thickness

    # A lot of these will probably need to be changed, but this has to
    # be done when the exact sizes are known
    GtkWidget::interior-focus = 0
    GtkWidget::focus-line-width = 1   # This does not do anything really
    GtkWidget::focus-padding = 0
    # 0.05 works good for both the sugar and sugar-xo themes
    GtkWidget::cursor-aspect-ratio = 0.05

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

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

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

    GtkScrolledWindow::scrollbar-spacing = 0

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

    GtkTreeView::expander-size = 24

    GtkArrow::arrow-size = 1.0

    GtkToolbar::shadow-type = GTK_SHADOW_NONE

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

        max_radius = $( xo ? 25 : 15)
    }
}

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

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

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

    bg[NORMAL] = @bg_color
}

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

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

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

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

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

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

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

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

style "comboboxentry"
{
    # Copied from the "default" style. Part of the workaround for bug #382646.
    text[NORMAL] = "#000000"
    text[ACTIVE] = "#000000"
    text[SELECTED] = "#000000"
    text[PRELIGHT] = "#000000"

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

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

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

    xthickness = 0
    ythickness = 0

    bg[ACTIVE] = "#808080"
}

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

    #bg[NORMAL] = "#404040"
    bg[PRELIGHT] = "#000000"
    bg[ACTIVE] = "#808080"

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

    engine "sugar" {
        max_radius = 5.0
    }
}

style "toolbox"
{
    color["bg_color"] = "#404040"

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

    bg[NORMAL] = @bg_color
}

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

style "entry"
{
    # small inner border and a large x/ythickness for entries
    # to reduce the number of hacks needed :-)
    xthickness = $(thickness*3)
    ythickness = $(thickness*3)

    # This tries to get a height of exactly 45 pixel for the entry.
    GtkEntry::inner-border = { $(subcell_size - thickness*3), $(subcell_size - thickness*3),
                               $(my_floor((3*subcell_size - font_height - thickness*3*2)/2)), $(my_ceil((3*subcell_size - font_height - thickness*3*2)/2)) }

    GtkWidget::focus-line-width = 0
}

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

    bg[NORMAL] = "#808080"
}

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

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

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

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

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

style "progressbar"
{
    xthickness = 0
    ythickness = 0

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

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

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

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

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

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

    xthickness = 0
    ythickness = 0
}

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

    bg[PRELIGHT] = "#808080"

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

    GtkMenuItem::horizontal-padding = $line_width
    xthickness = $(my_ceil(line_width * 2))
    ythickness = $thickness
}

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

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

widget_class "<GtkWindow>.*"                style "window-content"

widget_class "*<SugarToolbox>*"             style "toolbox"
widget_class "*<GtkToolButton>*"            style "toolbutton"

widget_class "*<GtkMenu>*"                style "menu"
widget_class "*<GtkMenuItem>*"            style "menuitem"
widget_class "*<GtkSeparatorMenuItem>*"   style "menuitem"

widget_class "*<GtkComboBox>*"         style "combobox"
widget_class "*<GtkComboBoxEntry>*"    style "comboboxentry"
widget_class "*<GtkCombo>*"            style "comboboxentry"
widget_class "*<GtkButton>*"           style "button"
widget_class "*<GtkCheckButton>*"      style "checkbutton"

widget_class "*<SugarPanel>"           style "panel"

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

${
import math

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

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

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

# These sizes need to be sanity checked ...
if theme == "sugar-xo":
    xo = True
    line_width = 2.25       # 2.25
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    bullet_size = 9.5
    font_height = 24
else: # About 50% smaller
    xo = False
    line_width = 1.125
    thick_line_width = 1.75
    subcell_size = 7
    bullet_size = 5
    font_height = 12

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

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

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

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

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

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

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

    xthickness = $thickness
    ythickness = $thickness

    # A lot of these will probably need to be changed, but this has to
    # be done when the exact sizes are known
    GtkWidget::interior-focus = 0
    GtkWidget::focus-line-width = 1   # This does not do anything really
    GtkWidget::focus-padding = 0
    # 0.05 works good for both the sugar and sugar-xo themes
    GtkWidget::cursor-aspect-ratio = 0.05

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

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

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

    GtkScrolledWindow::scrollbar-spacing = 0

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

    GtkTreeView::expander-size = 24

    GtkArrow::arrow-size = 1.0

    GtkToolbar::shadow-type = GTK_SHADOW_NONE

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

        max_radius = $( xo ? 25 : 15)
    }
}

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

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

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

    bg[NORMAL] = @bg_color
}

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

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

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

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

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

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

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

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

style "comboboxentry"
{
    # Copied from the "default" style. Part of the workaround for bug #382646.
    text[NORMAL] = "#000000"
    text[ACTIVE] = "#000000"
    text[SELECTED] = "#000000"
    text[PRELIGHT] = "#000000"

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

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

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

    xthickness = 0
    ythickness = 0

    bg[ACTIVE] = "#808080"
}

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

    #bg[NORMAL] = "#404040"
    bg[PRELIGHT] = "#000000"
    bg[ACTIVE] = "#808080"

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

    engine "sugar" {
        max_radius = 5.0
    }
}

style "toolbox"
{
    color["bg_color"] = "#404040"

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

    bg[NORMAL] = @bg_color
}

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

style "entry"
{
    # small inner border and a large x/ythickness for entries
    # to reduce the number of hacks needed :-)
    xthickness = $(thickness*3)
    ythickness = $(thickness*3)

    # This tries to get a height of exactly 45 pixel for the entry.
    GtkEntry::inner-border = { $(subcell_size - thickness*3), $(subcell_size - thickness*3),
                               $(my_floor((3*subcell_size - font_height - thickness*3*2)/2)), $(my_ceil((3*subcell_size - font_height - thickness*3*2)/2)) }

    GtkWidget::focus-line-width = 0
}

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

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

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

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

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

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

style "progressbar"
{
    xthickness = 0
    ythickness = 0

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

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

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

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

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

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

    xthickness = 0
    ythickness = 0
}

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

    bg[PRELIGHT] = "#808080"

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

    GtkMenuItem::horizontal-padding = $line_width
    xthickness = $(my_ceil(line_width * 2))
    ythickness = $thickness
}

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

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

widget_class "<GtkWindow>.*"                style "window-content"

widget_class "*<SugarToolbox>*"             style "toolbox"
widget_class "*<GtkToolButton>*"            style "toolbutton"

widget_class "*<GtkMenu>*"                style "menu"
widget_class "*<GtkMenuItem>*"            style "menuitem"
widget_class "*<GtkSeparatorMenuItem>*"   style "menuitem"

widget_class "*<GtkComboBox>*"         style "combobox"
widget_class "*<GtkComboBoxEntry>*"    style "comboboxentry"
widget_class "*<GtkCombo>*"            style "comboboxentry"
widget_class "*<GtkButton>*"           style "button"
widget_class "*<GtkCheckButton>*"      style "checkbutton"

widget_class "*<SugarPanel>"           style "panel"

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


use different subdomain for multiverse for old releases

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

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


replace httpredir.debian.org with cloudfront.debian.net

add comment about mirror selection

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

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


use parameterized aditional rosdep rule files

update rosdep in separate statement to allow none or multiple rule files
use rosdep install like in other templates
install ros-workspace to get ROS 2 setup files

Signed-off-by: Mikael Arguedas <a16f295d1139a1392bb19262902c32cf3e999ce4@gmail.com>

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
    'lsb-release',
    'wget',
]
# add 'python3-pip' to 'template_dependencies' if pip dependencies are declared
if 'pip3_install' in locals():
    if isinstance(pip3_install, list) and pip3_install != []:
        template_dependencies.append('python3-pip')
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
# setup ros2 keys
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116

# setup sources.list
RUN echo "deb http://packages.ros.org/ros2/ubuntu `lsb_release -sc` main" > /etc/apt/sources.list.d/ros2-latest.list

@(TEMPLATE(
    'snippet/install_ros_bootstrap_tools.Dockerfile.em',
    ros_version=version,
))@

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

@[if 'pip3_install' in locals()]@
@[  if pip3_install]@
# install python packages
RUN pip3 install -U \
    @(' \\\n    '.join(pip3_install))@

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

# install ros2 packages
ENV ROS_DISTRO @ros2distro_name
RUN mkdir -p /opt/ros/$ROS_DISTRO
ARG ROS2_BINARY_URL=@ros2_binary_url
RUN wget -q $ROS2_BINARY_URL -O - | \
    tar -xj --strip-components=1 -C /opt/ros/$ROS_DISTRO

@[if 'rosdep_override' in locals()]@
# add custom rosdep rule files
@[  for rule_file in rosdep_override]@
COPY @rule_file /etc/ros/rosdep/
RUN echo "yaml file:///etc/ros/rosdep/@rule_file" | \
    cat - /etc/ros/rosdep/sources.list.d/20-default.list > temp && \
    mv temp /etc/ros/rosdep/sources.list.d/20-default.list
@[  end for]@
RUN rosdep update

@[end if]@
@
@[if 'rosdep' in locals()]@
@[  if 'install' in rosdep]@
@(TEMPLATE(
    'snippet/install_rosdep_dependencies.Dockerfile.em',
    install_args=rosdep['install'],
))@

@[  end if]@
@[end if]@
@
# install setup files
RUN apt-get update && apt install -q -y \
    ros-$ROS_DISTRO-ros-workspace \
    && rm -rf /var/lib/apt/lists/*

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

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

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
    'lsb-release',
    'wget',
]
# add 'python3-pip' to 'template_dependencies' if pip dependencies are declared
if 'pip3_install' in locals():
    if isinstance(pip3_install, list) and pip3_install != []:
        template_dependencies.append('python3-pip')
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
# setup ros2 keys
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116

# setup sources.list
RUN echo "deb http://packages.ros.org/ros2/ubuntu `lsb_release -sc` main" > /etc/apt/sources.list.d/ros2-latest.list

@(TEMPLATE(
    'snippet/install_ros_bootstrap_tools.Dockerfile.em',
    ros_version=version,
))@

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

@[if 'pip3_install' in locals()]@
@[  if pip3_install]@
# install python packages
RUN pip3 install -U \
    @(' \\\n    '.join(pip3_install))@

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

# install ros2 packages
ENV ROS_DISTRO @ros2distro_name
RUN mkdir -p /opt/ros/$ROS_DISTRO
ARG ROS2_BINARY_URL=@ros2_binary_url
RUN wget -q $ROS2_BINARY_URL -O - | \
    tar -xj --strip-components=1 -C /opt/ros/$ROS_DISTRO

# install dependencies
RUN apt-get update && rosdep install -y \
    --from-paths /opt/ros/$ROS_DISTRO/share \
    --ignore-src \
    --skip-keys " \
      @(' \\\n      '.join(skip_keys))@ " \
    && rm -rf /var/lib/apt/lists/*

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

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


implicitly inherit from object (#50)

implicitly inherit from object (#50)

// generated from rcutils/resource/logging_macros.h.em

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

/*! \file */

#ifndef RCUTILS__LOGGING_MACROS_H_
#define RCUTILS__LOGGING_MACROS_H_

#include "rcutils/logging.h"

#include <stdio.h>
#include <stdlib.h>

#if __cplusplus
extern "C"
{
#endif

/**
 * \def RCUTILS_LOG_MIN_SEVERITY
 * Define RCUTILS_LOG_MIN_SEVERITY=RCUTILS_LOG_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
 * in your build options to compile out anything below that severity.
 */
#ifndef RCUTILS_LOG_MIN_SEVERITY
#define RCUTILS_LOG_MIN_SEVERITY RCUTILS_LOG_SEVERITY_DEBUG
#endif

// Provide the compiler with branch prediction information
#ifndef _WIN32
/**
 * \def RCUTILS_LIKELY
 * Instruct the compiler to optimize for the case where the argument equals 1.
 */
# define RCUTILS_LIKELY(x) __builtin_expect((x), 1)
/**
 * \def RCUTILS_UNLIKELY
 * Instruct the compiler to optimize for the case where the argument equals 0.
 */
# define RCUTILS_UNLIKELY(x) __builtin_expect((x), 0)
#else
/**
 * \def RCUTILS_LIKELY
 * No op since Windows doesn't support providing branch prediction information.
 */
# define RCUTILS_LIKELY(x) (x)
/**
 * \def RCUTILS_UNLIKELY
 * No op since Windows doesn't support providing branch prediction information.
 */
# define RCUTILS_UNLIKELY(x) (x)
#endif  // _WIN32

/**
 * \def RCUTILS_LOGGING_AUTOINIT
 * \brief Initialize the rcl logging library.
 * Usually it is unnecessary to call the macro directly.
 * All logging macros ensure that this has been called once.
 */
#define RCUTILS_LOGGING_AUTOINIT \
  if (RCUTILS_UNLIKELY(!g_rcutils_logging_initialized)) { \
    rcutils_logging_initialize(); \
  }

/**
 * \def RCUTILS_LOG_COND_NAMED
 * The logging macro all other logging macros call directly or indirectly.
 * \param severity The severity level
 * \param condition_before The condition macro(s) inserted before the log call
 * \param condition_after The condition macro(s) inserted after the log call
 * \param name The name of the logger
 * \param ... The format string, followed by the variable arguments for the format string
 */
#define RCUTILS_LOG_COND_NAMED(severity, condition_before, condition_after, name, ...) \
  { \
    RCUTILS_LOGGING_AUTOINIT \
    static rcutils_log_location_t __rcutils_logging_location = {__func__, __FILE__, __LINE__}; \
    condition_before \
    if (severity >= g_rcutils_logging_severity_threshold) { \
      rcutils_log(&__rcutils_logging_location, severity, name, __VA_ARGS__); \
    } \
    condition_after \
  }

///@@{
/**
 * \def RCUTILS_LOG_CONDITION_EMPTY
 * An empty macro which can be used as a placeholder for `condition_before`
 * and `condition_after` which doesn't affect the logging call.
 */
#define RCUTILS_LOG_CONDITION_EMPTY
///@@}

/** @@name Macros for the `once` condition which ignores all subsequent log
 * calls except the first one.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_ONCE_BEFORE
 * A macro initializing and checking the `once` condition.
 */
#define RCUTILS_LOG_CONDITION_ONCE_BEFORE \
  { \
    static int __rcutils_logging_once = 0; \
    if (RCUTILS_UNLIKELY(0 == __rcutils_logging_once)) { \
      __rcutils_logging_once = 1;
/**
 * \def RCUTILS_LOG_CONDITION_ONCE_AFTER
 * A macro finalizing the `once` condition.
 */
#define RCUTILS_LOG_CONDITION_ONCE_AFTER } \
  }
///@@}

/** @@name Macros for the `expression` condition which ignores the log calls
 * when the expression evaluates to false.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_EXPRESSION_BEFORE
 * A macro checking the `expression` condition.
 */
#define RCUTILS_LOG_CONDITION_EXPRESSION_BEFORE(expression) \
  if (expression) {
/**
 * \def RCUTILS_LOG_CONDITION_EXPRESSION_AFTER
 * A macro finalizing the `expression` condition.
 */
#define RCUTILS_LOG_CONDITION_EXPRESSION_AFTER }
///@@}

/** @@name Macros for the `function` condition which ignores the log calls
 * when the function returns false.
 */
///@@{
/// The filter function signature.
/**
 * \return true to log the message, false to ignore the message
 */
typedef bool (* RclLogFilter)();
/**
 * \def RCUTILS_LOG_CONDITION_FUNCTION_BEFORE
 * A macro checking the `function` condition.
 */
#define RCUTILS_LOG_CONDITION_FUNCTION_BEFORE(function) \
  if ((*function)()) {
/**
 * \def RCUTILS_LOG_CONDITION_FUNCTION_AFTER
 * A macro finalizing the `function` condition.
 */
#define RCUTILS_LOG_CONDITION_FUNCTION_AFTER }
///@@}

/** @@name Macros for the `skipfirst` condition which ignores the first log
 * call but processes all subsequent calls.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_SKIPFIRST_BEFORE
 * A macro initializing and checking the `skipfirst` condition.
 */
#define RCUTILS_LOG_CONDITION_SKIPFIRST_BEFORE \
  { \
    static bool __rcutils_logging_first = true; \
    if (RCUTILS_UNLIKELY(true == __rcutils_logging_first)) { \
      __rcutils_logging_first = false; \
    } else {
/**
 * \def RCUTILS_LOG_CONDITION_SKIPFIRST_AFTER
 * A macro finalizing the `skipfirst` condition.
 */
#define RCUTILS_LOG_CONDITION_SKIPFIRST_AFTER } \
  }
///@@}

/** @@name Macros for the `throttle` condition which ignores log calls if the
 * last logged message is not longer ago than the specified duration.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_THROTTLE_BEFORE
 * A macro initializing and checking the `throttle` condition.
 */
#define RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(time_source_type, duration) { \
    static rcutils_duration_value_t __rcutils_logging_duration = RCUTILS_MS_TO_NS((rcutils_duration_value_t)duration); \
    static rcutils_time_point_value_t __rcutils_logging_last_logged = 0; \
    rcutils_time_point_value_t __rcutils_logging_now = 0; \
    bool __rcutils_logging_condition = true; \
    if (rcutils_steady_time_now(&__rcutils_logging_now) != RCUTILS_RET_OK) { \
      rcutils_log( \
        &__rcutils_logging_location, RCUTILS_LOG_SEVERITY_ERROR, "", \
        "%s() at %s:%d getting current steady time failed\n", \
        __func__, __FILE__, __LINE__); \
    } else { \
      __rcutils_logging_condition = __rcutils_logging_now >= __rcutils_logging_last_logged + __rcutils_logging_duration; \
    } \
 \
    if (RCUTILS_LIKELY(__rcutils_logging_condition)) { \
      __rcutils_logging_last_logged = __rcutils_logging_now;

/**
 * \def RCUTILS_LOG_CONDITION_THROTTLE_AFTER
 * A macro finalizing the `throttle` condition.
 */
#define RCUTILS_LOG_CONDITION_THROTTLE_AFTER } \
  }
///@@}

@{
severities = ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL')

from collections import OrderedDict
default_args = OrderedDict((
  ('condition_before', 'RCUTILS_LOG_CONDITION_EMPTY'),
  ('condition_after', 'RCUTILS_LOG_CONDITION_EMPTY'),
  ('name', '""'),
))

name_params = OrderedDict((
  ('name', 'The name of the logger'),
))
name_args = {'name': 'name'}

once_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_ONCE_BEFORE',
  'condition_after': 'RCUTILS_LOG_CONDITION_ONCE_AFTER'}
name_doc_lines = [
  'All subsequent log calls except the first one are being ignored.']

expression_params = OrderedDict((
  ('expression', 'The expression determining if the message should be logged'),
))
expression_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_EXPRESSION_BEFORE(expression)',
  'condition_after': 'RCUTILS_LOG_CONDITION_EXPRESSION_AFTER'}
expression_doc_lines = [
  'Log calls are being ignored when the expression evaluates to false.']

function_params = OrderedDict((
  ('function', 'The functions return value determines if the message should be logged'),
))
function_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_FUNCTION_BEFORE(function)',
  'condition_after': 'RCUTILS_LOG_CONDITION_FUNCTION_AFTER'}
function_doc_lines = [
  'Log calls are being ignored when the function returns false.']

skipfirst_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_SKIPFIRST_BEFORE',
  'condition_after': 'RCUTILS_LOG_CONDITION_SKIPFIRST_AFTER'}
skipfirst_doc_lines = [
  'The first log call is being ignored but all subsequent calls are being processed.']

throttle_params = OrderedDict((
  ('time_source_type', 'The time source type of the time to be used'),
  ('duration', 'The duration of the throttle interval'),
))
throttle_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(time_source_type, duration)',
  'condition_after': 'RCUTILS_LOG_CONDITION_THROTTLE_AFTER'}
throttle_doc_lines = [
  'Log calls are being ignored if the last logged message is not longer ago than the specified duration.']

class Feature:
  __slots__ = ('params', 'args', 'doc_lines')
  def __init__(self, *, params=None, args=None, doc_lines=None):
    if params is None:
      params = {}
    self.params = params
    if args is None:
      args = {}
    self.args = args
    if doc_lines is None:
      doc_lines = []
    self.doc_lines = doc_lines

feature_combinations = OrderedDict((
  ('', Feature()),
  ('_NAMED', Feature(
    params=name_params,
    args=name_args)),
  ('_ONCE', Feature(
    params=None,
    args=once_args,
    doc_lines=name_doc_lines)),
  ('_ONCE_NAMED', Feature(
    params=name_params,
    args={**once_args, **name_args},
    doc_lines=name_doc_lines)),
  ('_EXPRESSION', Feature(
    params=expression_params,
    args=expression_args,
    doc_lines=expression_doc_lines)),
  ('_EXPRESSION_NAMED', Feature(
    params=OrderedDict((*expression_params.items(), *name_params.items())),
    args={**expression_args, **name_args},
    doc_lines=expression_doc_lines + name_doc_lines)),
  ('_FUNCTION', Feature(
    params=function_params,
    args=function_args,
    doc_lines=function_doc_lines)),
  ('_FUNCTION_NAMED', Feature(
    params=OrderedDict((*function_params.items(), *name_params.items())),
    args={**function_args, **name_args},
    doc_lines=function_doc_lines + name_doc_lines)),
  ('_SKIPFIRST', Feature(
    params=None,
    args=skipfirst_args,
    doc_lines=skipfirst_doc_lines)),
  ('_SKIPFIRST_NAMED', Feature(
    params=name_params,
    args={**skipfirst_args, **name_args},
    doc_lines=skipfirst_doc_lines)),
  ('_THROTTLE', Feature(
    params=throttle_params,
    args=throttle_args,
    doc_lines=throttle_doc_lines)),
  ('_SKIPFIRST_THROTTLE', Feature(
    params=throttle_params,
    args={
      'condition_before': ' '.join([
        throttle_args['condition_before'],
        skipfirst_args['condition_before']]),
      'condition_after': ' '.join([
        throttle_args['condition_after'], skipfirst_args['condition_after']]),
    },
    doc_lines=skipfirst_doc_lines + throttle_doc_lines)),
  ('_THROTTLE_NAMED', Feature(
    params=OrderedDict((*throttle_params.items(), *name_params.items())),
    args={**throttle_args, **name_args},
    doc_lines=throttle_doc_lines)),
  ('_SKIPFIRST_THROTTLE_NAMED', Feature(
    params=OrderedDict((*throttle_params.items(), *name_params.items())),
    args={
      **{
        'condition_before': ' '.join([
          throttle_args['condition_before'],
          skipfirst_args['condition_before']]),
        'condition_after': ' '.join([
          throttle_args['condition_after'],
          skipfirst_args['condition_after']]),
      }, **name_args
    },
    doc_lines=skipfirst_doc_lines + throttle_doc_lines)),
))

def get_macro_parameters(suffix):
  return ''.join([p + ', ' for p in feature_combinations[suffix].params.keys()])

def get_macro_arguments(suffix):
  args = []
  for k, default_value in default_args.items():
    value = feature_combinations[suffix].args.get(k, default_value)
    args.append(value)
  return ''.join([a + ', ' for a in args])
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
 */
///@@{
#if (RCUTILS_LOG_MIN_SEVERITY > RCUTILS_LOG_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for suffix in feature_combinations]@
/// Empty logging macro due to the preprocessor definition of RCUTILS_LOG_MIN_SEVERITY.
# define RCUTILS_LOG_@(severity)@(suffix)(@(get_macro_parameters(suffix))format, ...)
@[ end for]@

#else
@[ for suffix in feature_combinations]@
/**
 * \def RCUTILS_LOG_@(severity)@(suffix)
 * Log a message with severity @(severity)@
@[ if feature_combinations[suffix].doc_lines]@
 with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in feature_combinations[suffix].doc_lines]@
 * @(doc_line)
@[ end for]@
@[ for param_name, doc_line in feature_combinations[suffix].params.items()]@
 * \param @(param_name) @(doc_line)
@[ end for]@
 * \param ... The format string, followed by the variable arguments for the format string
 */
# define RCUTILS_LOG_@(severity)@(suffix)(@(get_macro_parameters(suffix))...) \
  RCUTILS_LOG_COND_NAMED( \
    RCUTILS_LOG_SEVERITY_@(severity), \
    @(get_macro_arguments(suffix))\
    __VA_ARGS__)
@[ end for]@
#endif
///@@}

@[end for]@
#if __cplusplus
}
#endif

#endif  // RCUTILS__LOGGING_MACROS_H_

// generated from rcutils/resource/logging_macros.h.em

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

/*! \file */

#ifndef RCUTILS__LOGGING_MACROS_H_
#define RCUTILS__LOGGING_MACROS_H_

#include "rcutils/logging.h"

#include <stdio.h>
#include <stdlib.h>

#if __cplusplus
extern "C"
{
#endif

/**
 * \def RCUTILS_LOG_MIN_SEVERITY
 * Define RCUTILS_LOG_MIN_SEVERITY=RCUTILS_LOG_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
 * in your build options to compile out anything below that severity.
 */
#ifndef RCUTILS_LOG_MIN_SEVERITY
#define RCUTILS_LOG_MIN_SEVERITY RCUTILS_LOG_SEVERITY_DEBUG
#endif

// Provide the compiler with branch prediction information
#ifndef _WIN32
/**
 * \def RCUTILS_LIKELY
 * Instruct the compiler to optimize for the case where the argument equals 1.
 */
# define RCUTILS_LIKELY(x) __builtin_expect((x), 1)
/**
 * \def RCUTILS_UNLIKELY
 * Instruct the compiler to optimize for the case where the argument equals 0.
 */
# define RCUTILS_UNLIKELY(x) __builtin_expect((x), 0)
#else
/**
 * \def RCUTILS_LIKELY
 * No op since Windows doesn't support providing branch prediction information.
 */
# define RCUTILS_LIKELY(x) (x)
/**
 * \def RCUTILS_UNLIKELY
 * No op since Windows doesn't support providing branch prediction information.
 */
# define RCUTILS_UNLIKELY(x) (x)
#endif  // _WIN32

/**
 * \def RCUTILS_LOGGING_AUTOINIT
 * \brief Initialize the rcl logging library.
 * Usually it is unnecessary to call the macro directly.
 * All logging macros ensure that this has been called once.
 */
#define RCUTILS_LOGGING_AUTOINIT \
  if (RCUTILS_UNLIKELY(!g_rcutils_logging_initialized)) { \
    rcutils_logging_initialize(); \
  }

/**
 * \def RCUTILS_LOG_COND_NAMED
 * The logging macro all other logging macros call directly or indirectly.
 * \param severity The severity level
 * \param condition_before The condition macro(s) inserted before the log call
 * \param condition_after The condition macro(s) inserted after the log call
 * \param name The name of the logger
 * \param ... The format string, followed by the variable arguments for the format string
 */
#define RCUTILS_LOG_COND_NAMED(severity, condition_before, condition_after, name, ...) \
  { \
    RCUTILS_LOGGING_AUTOINIT \
    static rcutils_log_location_t __rcutils_logging_location = {__func__, __FILE__, __LINE__}; \
    condition_before \
    if (severity >= g_rcutils_logging_severity_threshold) { \
      rcutils_log(&__rcutils_logging_location, severity, name, __VA_ARGS__); \
    } \
    condition_after \
  }

///@@{
/**
 * \def RCUTILS_LOG_CONDITION_EMPTY
 * An empty macro which can be used as a placeholder for `condition_before`
 * and `condition_after` which doesn't affect the logging call.
 */
#define RCUTILS_LOG_CONDITION_EMPTY
///@@}

/** @@name Macros for the `once` condition which ignores all subsequent log
 * calls except the first one.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_ONCE_BEFORE
 * A macro initializing and checking the `once` condition.
 */
#define RCUTILS_LOG_CONDITION_ONCE_BEFORE \
  { \
    static int __rcutils_logging_once = 0; \
    if (RCUTILS_UNLIKELY(0 == __rcutils_logging_once)) { \
      __rcutils_logging_once = 1;
/**
 * \def RCUTILS_LOG_CONDITION_ONCE_AFTER
 * A macro finalizing the `once` condition.
 */
#define RCUTILS_LOG_CONDITION_ONCE_AFTER } \
  }
///@@}

/** @@name Macros for the `expression` condition which ignores the log calls
 * when the expression evaluates to false.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_EXPRESSION_BEFORE
 * A macro checking the `expression` condition.
 */
#define RCUTILS_LOG_CONDITION_EXPRESSION_BEFORE(expression) \
  if (expression) {
/**
 * \def RCUTILS_LOG_CONDITION_EXPRESSION_AFTER
 * A macro finalizing the `expression` condition.
 */
#define RCUTILS_LOG_CONDITION_EXPRESSION_AFTER }
///@@}

/** @@name Macros for the `function` condition which ignores the log calls
 * when the function returns false.
 */
///@@{
/// The filter function signature.
/**
 * \return true to log the message, false to ignore the message
 */
typedef bool (* RclLogFilter)();
/**
 * \def RCUTILS_LOG_CONDITION_FUNCTION_BEFORE
 * A macro checking the `function` condition.
 */
#define RCUTILS_LOG_CONDITION_FUNCTION_BEFORE(function) \
  if ((*function)()) {
/**
 * \def RCUTILS_LOG_CONDITION_FUNCTION_AFTER
 * A macro finalizing the `function` condition.
 */
#define RCUTILS_LOG_CONDITION_FUNCTION_AFTER }
///@@}

/** @@name Macros for the `skipfirst` condition which ignores the first log
 * call but processes all subsequent calls.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_SKIPFIRST_BEFORE
 * A macro initializing and checking the `skipfirst` condition.
 */
#define RCUTILS_LOG_CONDITION_SKIPFIRST_BEFORE \
  { \
    static bool __rcutils_logging_first = true; \
    if (RCUTILS_UNLIKELY(true == __rcutils_logging_first)) { \
      __rcutils_logging_first = false; \
    } else {
/**
 * \def RCUTILS_LOG_CONDITION_SKIPFIRST_AFTER
 * A macro finalizing the `skipfirst` condition.
 */
#define RCUTILS_LOG_CONDITION_SKIPFIRST_AFTER } \
  }
///@@}

/** @@name Macros for the `throttle` condition which ignores log calls if the
 * last logged message is not longer ago than the specified duration.
 */
///@@{
/**
 * \def RCUTILS_LOG_CONDITION_THROTTLE_BEFORE
 * A macro initializing and checking the `throttle` condition.
 */
#define RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(time_source_type, duration) { \
    static rcutils_duration_value_t __rcutils_logging_duration = RCUTILS_MS_TO_NS((rcutils_duration_value_t)duration); \
    static rcutils_time_point_value_t __rcutils_logging_last_logged = 0; \
    rcutils_time_point_value_t __rcutils_logging_now = 0; \
    bool __rcutils_logging_condition = true; \
    if (rcutils_steady_time_now(&__rcutils_logging_now) != RCUTILS_RET_OK) { \
      rcutils_log( \
        &__rcutils_logging_location, RCUTILS_LOG_SEVERITY_ERROR, "", \
        "%s() at %s:%d getting current steady time failed\n", \
        __func__, __FILE__, __LINE__); \
    } else { \
      __rcutils_logging_condition = __rcutils_logging_now >= __rcutils_logging_last_logged + __rcutils_logging_duration; \
    } \
 \
    if (RCUTILS_LIKELY(__rcutils_logging_condition)) { \
      __rcutils_logging_last_logged = __rcutils_logging_now;

/**
 * \def RCUTILS_LOG_CONDITION_THROTTLE_AFTER
 * A macro finalizing the `throttle` condition.
 */
#define RCUTILS_LOG_CONDITION_THROTTLE_AFTER } \
  }
///@@}

@{
severities = ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL')

from collections import OrderedDict
default_args = OrderedDict((
  ('condition_before', 'RCUTILS_LOG_CONDITION_EMPTY'),
  ('condition_after', 'RCUTILS_LOG_CONDITION_EMPTY'),
  ('name', '""'),
))

name_params = OrderedDict((
  ('name', 'The name of the logger'),
))
name_args = {'name': 'name'}

once_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_ONCE_BEFORE',
  'condition_after': 'RCUTILS_LOG_CONDITION_ONCE_AFTER'}
name_doc_lines = [
  'All subsequent log calls except the first one are being ignored.']

expression_params = OrderedDict((
  ('expression', 'The expression determining if the message should be logged'),
))
expression_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_EXPRESSION_BEFORE(expression)',
  'condition_after': 'RCUTILS_LOG_CONDITION_EXPRESSION_AFTER'}
expression_doc_lines = [
  'Log calls are being ignored when the expression evaluates to false.']

function_params = OrderedDict((
  ('function', 'The functions return value determines if the message should be logged'),
))
function_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_FUNCTION_BEFORE(function)',
  'condition_after': 'RCUTILS_LOG_CONDITION_FUNCTION_AFTER'}
function_doc_lines = [
  'Log calls are being ignored when the function returns false.']

skipfirst_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_SKIPFIRST_BEFORE',
  'condition_after': 'RCUTILS_LOG_CONDITION_SKIPFIRST_AFTER'}
skipfirst_doc_lines = [
  'The first log call is being ignored but all subsequent calls are being processed.']

throttle_params = OrderedDict((
  ('time_source_type', 'The time source type of the time to be used'),
  ('duration', 'The duration of the throttle interval'),
))
throttle_args = {
  'condition_before': 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(time_source_type, duration)',
  'condition_after': 'RCUTILS_LOG_CONDITION_THROTTLE_AFTER'}
throttle_doc_lines = [
  'Log calls are being ignored if the last logged message is not longer ago than the specified duration.']

class Feature(object):
  __slots__ = ('params', 'args', 'doc_lines')
  def __init__(self, *, params=None, args=None, doc_lines=None):
    if params is None:
      params = {}
    self.params = params
    if args is None:
      args = {}
    self.args = args
    if doc_lines is None:
      doc_lines = []
    self.doc_lines = doc_lines

feature_combinations = OrderedDict((
  ('', Feature()),
  ('_NAMED', Feature(
    params=name_params,
    args=name_args)),
  ('_ONCE', Feature(
    params=None,
    args=once_args,
    doc_lines=name_doc_lines)),
  ('_ONCE_NAMED', Feature(
    params=name_params,
    args={**once_args, **name_args},
    doc_lines=name_doc_lines)),
  ('_EXPRESSION', Feature(
    params=expression_params,
    args=expression_args,
    doc_lines=expression_doc_lines)),
  ('_EXPRESSION_NAMED', Feature(
    params=OrderedDict((*expression_params.items(), *name_params.items())),
    args={**expression_args, **name_args},
    doc_lines=expression_doc_lines + name_doc_lines)),
  ('_FUNCTION', Feature(
    params=function_params,
    args=function_args,
    doc_lines=function_doc_lines)),
  ('_FUNCTION_NAMED', Feature(
    params=OrderedDict((*function_params.items(), *name_params.items())),
    args={**function_args, **name_args},
    doc_lines=function_doc_lines + name_doc_lines)),
  ('_SKIPFIRST', Feature(
    params=None,
    args=skipfirst_args,
    doc_lines=skipfirst_doc_lines)),
  ('_SKIPFIRST_NAMED', Feature(
    params=name_params,
    args={**skipfirst_args, **name_args},
    doc_lines=skipfirst_doc_lines)),
  ('_THROTTLE', Feature(
    params=throttle_params,
    args=throttle_args,
    doc_lines=throttle_doc_lines)),
  ('_SKIPFIRST_THROTTLE', Feature(
    params=throttle_params,
    args={
      'condition_before': ' '.join([
        throttle_args['condition_before'],
        skipfirst_args['condition_before']]),
      'condition_after': ' '.join([
        throttle_args['condition_after'], skipfirst_args['condition_after']]),
    },
    doc_lines=skipfirst_doc_lines + throttle_doc_lines)),
  ('_THROTTLE_NAMED', Feature(
    params=OrderedDict((*throttle_params.items(), *name_params.items())),
    args={**throttle_args, **name_args},
    doc_lines=throttle_doc_lines)),
  ('_SKIPFIRST_THROTTLE_NAMED', Feature(
    params=OrderedDict((*throttle_params.items(), *name_params.items())),
    args={
      **{
        'condition_before': ' '.join([
          throttle_args['condition_before'],
          skipfirst_args['condition_before']]),
        'condition_after': ' '.join([
          throttle_args['condition_after'],
          skipfirst_args['condition_after']]),
      }, **name_args
    },
    doc_lines=skipfirst_doc_lines + throttle_doc_lines)),
))

def get_macro_parameters(suffix):
  return ''.join([p + ', ' for p in feature_combinations[suffix].params.keys()])

def get_macro_arguments(suffix):
  args = []
  for k, default_value in default_args.items():
    value = feature_combinations[suffix].args.get(k, default_value)
    args.append(value)
  return ''.join([a + ', ' for a in args])
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
 */
///@@{
#if (RCUTILS_LOG_MIN_SEVERITY > RCUTILS_LOG_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for suffix in feature_combinations]@
/// Empty logging macro due to the preprocessor definition of RCUTILS_LOG_MIN_SEVERITY.
# define RCUTILS_LOG_@(severity)@(suffix)(@(get_macro_parameters(suffix))format, ...)
@[ end for]@

#else
@[ for suffix in feature_combinations]@
/**
 * \def RCUTILS_LOG_@(severity)@(suffix)
 * Log a message with severity @(severity)@
@[ if feature_combinations[suffix].doc_lines]@
 with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in feature_combinations[suffix].doc_lines]@
 * @(doc_line)
@[ end for]@
@[ for param_name, doc_line in feature_combinations[suffix].params.items()]@
 * \param @(param_name) @(doc_line)
@[ end for]@
 * \param ... The format string, followed by the variable arguments for the format string
 */
# define RCUTILS_LOG_@(severity)@(suffix)(@(get_macro_parameters(suffix))...) \
  RCUTILS_LOG_COND_NAMED( \
    RCUTILS_LOG_SEVERITY_@(severity), \
    @(get_macro_arguments(suffix))\
    __VA_ARGS__)
@[ end for]@
#endif
///@@}

@[end for]@
#if __cplusplus
}
#endif

#endif  // RCUTILS__LOGGING_MACROS_H_


Cleanup template formatting

Cleanup template formatting

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@{
template_dependencies = []
# add 'python3-pip' to 'template_dependencies' if pip dependencies are declared
if 'pip3_install' in locals():
    if isinstance(pip3_install, list) and pip3_install != []:
        template_dependencies.append('python3-pip')
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
@[if 'pip3_install' in locals()]@
@[  if pip3_install]@
# install python packages
RUN pip3 install -U \
    @(' \\\n    '.join(pip3_install))@

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

@(TEMPLATE(
    'snippet/setup_ros_sources.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
    rosdistro_name=rosdistro_name,
    ros_version='1',
))@

ENV ROS1_DISTRO @rosdistro_name
ENV ROS2_DISTRO @ros2distro_name

@[if 'ros_packages' in locals()]@
@(TEMPLATE(
    'snippet/label_and_install_package_list.Dockerfile.em',
    group='ros',
    packages=ros_packages,
))@
@[end if]@
@[if 'ros2_packages' in locals()]@
@(TEMPLATE(
    'snippet/label_and_install_package_list.Dockerfile.em',
    group='ros2',
    packages=ros2_packages,
))@
@[end if]@
@[if 'downstream_packages' in locals()]@
@[  if downstream_packages]@
# install downstream packages
RUN apt-get update && apt-get install -y --no-install-recommends \
    @(' \\\n    '.join(downstream_packages))@  \
    && rm -rf /var/lib/apt/lists/*

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


Small Format Cleenup

Small Format Cleenup

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
    now_str=now_str,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
))@
MAINTAINER Dirk Thomas dthomas+buildfarm@@osrfoundation.org

# setup environment
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
@[if 'packages' in locals()]@
@[if packages]@

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

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

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

# setup sources.list
RUN echo "deb http://packages.ros.org/ros/@os_name @os_code_name main" > /etc/apt/sources.list.d/ros-latest.list

# install bootstrap tools
ENV ROS_DISTRO @rosdistro_name
RUN apt-get update && apt-get install --no-install-recommends -y \
    python-rosdep \
    python-rosinstall \
    python-vcstools \
    && rm -rf /var/lib/apt/lists/*

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

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

# setup entrypoint
COPY ./ros_entrypoint.sh /
RUN chmod +x ./ros_entrypoint.sh

@[if 'entrypoint_name' in locals()]@
@[if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
ENTRYPOINT ["/@entrypoint_file"]
@[end if]@
@[end if]@
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
    now_str=now_str,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
))@
MAINTAINER Dirk Thomas dthomas+buildfarm@@osrfoundation.org

# setup environment
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
@[if 'packages' in locals()]@
@[if packages]@

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

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

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

# setup sources.list
RUN echo "deb http://packages.ros.org/ros/@os_name @os_code_name main" > /etc/apt/sources.list.d/ros-latest.list

# install bootstrap tools
ENV ROS_DISTRO @rosdistro_name
RUN apt-get update && apt-get install --no-install-recommends -y \
    python-rosdep \
    python-rosinstall \
    python-vcstools \
    && rm -rf /var/lib/apt/lists/*

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

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

# setup entrypoint
COPY ./ros_entrypoint.sh /
RUN chmod +x ./ros_entrypoint.sh

@[if 'entrypoint_name' in locals()]@
@[if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# install packages
ENTRYPOINT ["/@entrypoint_file"]
@[end if]@
@[end if]@

@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]