Migrate entry styles to "css nodes"

Migrate entry styles to "css nodes"

Upstream, gtk has changed to using "css nodes" [1].  This is a
first pass conversion that preserves most of the styles.

[1]  https://github.com/GNOME/gtk/commit/bb7d7851ac5cecf960a7ed69af06b2dd0a29484b

${
import os
import math

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

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

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

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

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
    radio_size = 26
    scale_trough_margin = 8
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 7      # 7.68
    radio_size = 18             # 18.72
    scale_trough_margin = 6     # 5.76

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

icon_small = icon_base * 3
icon_large = icon_base * 5

disabled_opacity = 0.5

}

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

    /* 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,
.spinbutton.button {
    border-radius: 0px;
    border-width: 0px;
    border-style: solid;
    background-color: @button_grey;
}

spinbutton button:last-child,
.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,
.spinbutton.button:active {
    background-color: @black;
}

spinbutton button:insensitive,
.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;
}

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

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

entry,
.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 progress,
.entry.progressbar {
    border-radius: $(2 * subcell_size)px;
    border-width: $(thickness)px;
    background-color: @selection_grey;
}

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

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

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

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

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

entry:selected,
entry:selected:focused
.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,
GtkPopover {
    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 :  -$(line_width)px;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px;

    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

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

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

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

SugarToolbarBox,
.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;
}

GtkToolButton .button:insensitive,
SugarRadioToolButton .button:insensitive {
    opacity: $(disabled_opacity);
}

.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:not(:active):not(:checked),
.toolbar GtkToolButton .button:prelight:not(:active):not(:checked) GtkBox,
SugarPaletteWindowWidget GtkToolButton .button:prelight:not(:active):not(:checked) {
    background-color: @black;
}

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

.toolbar SugarRadioToolButton *:checked:prelight,
.toolbar SugarRadioToolButton *:checked:prelight GtkBox,
SugarPaletteWindowWidget SugarRadioToolButton *:checked:prelight,
.toolbar SugarToggleToolButton *:checked:prelight,
.toolbar SugarToggleToolButton *:checked:prelight GtkBox,
SugarPaletteWindowWidget SugarToggleToolButton *:checked:prelight {
    background-color: @button_grey;
}

SugarPaletteWindowWidget SugarRadioToolButton *:checked:prelight,
SugarPaletteWindowWidget SugarToggleToolButton *:checked:prelight {
    border-color: @button_grey;
}

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,.toolbar .check,
SugarPaletteWindowWidget .check {
    color: @theme_base_color;
    border: 1px solid @theme_base_color;
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    /* Gtk 3.14+ expect these to be included in icon theme however we want to
       override that */
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/radio-selected.svg");
}

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

.radio:checked,
.radio row:selected:checked,
.radio row:selected:focused:checked {
    background-image: url("assets/radio-active.svg");
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/radio-active-selected.svg");
}

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

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/checkbox-unchecked-selected.svg");
}

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

.check:checked,
.check row:selected:checked,
.check row:selected:focused:checked {
    background-image: url("assets/checkbox-checked.svg");
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/checkbox-checked-selected.svg");
}

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

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* 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;
    background-color: transparent;
}

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 os
import math

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

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

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

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

# These sizes need to be sanity checked ...
if scaling == "100":
    xo = True
    line_width = 2.0        # 2.25px, rounded down
    thick_line_width = 3.5  # 3.5
    subcell_size = 15
    icon_base = 11
    bullet_size = 9.5
    font_height = 24
    default_padding = 6
    toolbutton_padding = 10
    radio_size = 26
    scale_trough_margin = 8
else: # About 72% of the XO size, adjusted so that eg. toolbuttons work
    xo = False
    line_width = 2.0            # 1.62 rounded up
    thick_line_width = 3.0      # 2.52
    subcell_size = 11           # 10.8
    icon_base = 8               # 7.92
    bullet_size = 6.5           # 6.84
    # This is a guess on the font size (Sans 10 at 96 DPI)
    font_height = 17
    default_padding = 4         # 4.32
    toolbutton_padding = 7      # 7.68
    radio_size = 18             # 18.72
    scale_trough_margin = 6     # 5.76

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

icon_small = icon_base * 3
icon_large = icon_base * 5

disabled_opacity = 0.5

}

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

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

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

$[if treeview_pseudo_element] GtkTreeView row:odd
$[else] GtkTreeView row:nth-child(odd)
$[end if] {
    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,
GtkPopover {
    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 :  -$(line_width)px;
    -GtkMenu-vertical-offset   : 0;

    padding: 0px;

    border-width: 2px;
    border-color: @button_grey;
    border-style: solid;
}

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

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

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

SugarToolbarBox,
.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;
}

GtkToolButton .button:insensitive,
SugarRadioToolButton .button:insensitive {
    opacity: $(disabled_opacity);
}

.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:not(:active):not(:checked),
.toolbar GtkToolButton .button:prelight:not(:active):not(:checked) GtkBox,
SugarPaletteWindowWidget GtkToolButton .button:prelight:not(:active):not(:checked) {
    background-color: @black;
}

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

.toolbar SugarRadioToolButton *:checked:prelight,
.toolbar SugarRadioToolButton *:checked:prelight GtkBox,
SugarPaletteWindowWidget SugarRadioToolButton *:checked:prelight,
.toolbar SugarToggleToolButton *:checked:prelight,
.toolbar SugarToggleToolButton *:checked:prelight GtkBox,
SugarPaletteWindowWidget SugarToggleToolButton *:checked:prelight {
    background-color: @button_grey;
}

SugarPaletteWindowWidget SugarRadioToolButton *:checked:prelight,
SugarPaletteWindowWidget SugarToggleToolButton *:checked:prelight {
    border-color: @button_grey;
}

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,.toolbar .check,
SugarPaletteWindowWidget .check {
    color: @theme_base_color;
    border: 1px solid @theme_base_color;
}

.radio,
.radio row:selected,
.radio row:selected:focused {
    background-image: url("assets/radio.svg");
    /* Gtk 3.14+ expect these to be included in icon theme however we want to
       override that */
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/radio-selected.svg");
}

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

.radio:checked,
.radio row:selected:checked,
.radio row:selected:focused:checked {
    background-image: url("assets/radio-active.svg");
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/radio-active-selected.svg");
}

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

.check,
.check row:selected,
.check row:selected:focused {
    background-image: url("assets/checkbox-unchecked.svg");
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/checkbox-unchecked-selected.svg");
}

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

.check:checked,
.check row:selected:checked,
.check row:selected:focused:checked {
    background-image: url("assets/checkbox-checked.svg");
    -gtk-icon-source: 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");
    -gtk-icon-source: url("assets/checkbox-checked-selected.svg");
}

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

/* Tool items */

.toolitem {
    /* arrow in the toolbarbox */
    color: @white;
    /* 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;
    background-color: transparent;
}

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


use non-bool type for structure_needs_at_least_one_member field (#376)

Signed-off-by: Dirk Thomas <19795c8185177e940eedd30ceb2b716a54a3d5dc@users.noreply.github.com>

@#typedefs for arrays need to be defined outside of the struct
@{
from collections import OrderedDict

from rosidl_adapter.msg import get_idl_type
from rosidl_adapter.msg import to_idl_literal
from rosidl_adapter.msg import string_to_idl_string_literal

typedefs = OrderedDict()
def get_idl_type_identifier(idl_type):
    return idl_type.replace('::', '__') \
        .replace('<', '__').replace('>', '') \
        .replace('[', '__').replace(']', '')
}@
@[for field in msg.fields]@
@{
idl_type = get_idl_type(field.type)
}@
@[  if field.type.is_fixed_size_array()]@
@{
idl_base_type = idl_type.split('[', 1)[0]
idl_base_type_identifier = idl_base_type.replace('::', '__')
# only necessary for complex types
if idl_base_type_identifier != idl_base_type:
    if idl_base_type_identifier not in typedefs:
        typedefs[idl_base_type_identifier] = idl_base_type
    else:
        assert typedefs[idl_base_type_identifier] == idl_base_type
idl_type_identifier = get_idl_type_identifier(idl_type) + '[' + str(field.type.array_size) + ']'
if idl_type_identifier not in typedefs:
    typedefs[idl_type_identifier] = idl_base_type_identifier
else:
    assert typedefs[idl_type_identifier] == idl_base_type_identifier
}@
@[  end if]@
@[end for]@
@[for k, v in typedefs.items()]@
    typedef @(v) @(k);
@[end for]@
@[if msg.constants]@
    module @(msg.msg_name)_Constants {
@[  for constant in msg.constants]@
      const @(get_idl_type(constant.type)) @(constant.name) = @(to_idl_literal(get_idl_type(constant.type), constant.value));
@[  end for]@
    };
@[end if]@
@#
@[if msg.annotations.get('comment', [])]@
    @@verbatim (language="comment", text=@(string_to_idl_string_literal('\n'.join(msg.annotations['comment']))))
@[end if]@
    struct @(msg.msg_name) {
@[if msg.fields]@
@[  for i, field in enumerate(msg.fields)]@
@[if i > 0]@

@[end if]@
@[    if field.annotations.get('comment', [])]@
      @@verbatim (language="comment", text=@(string_to_idl_string_literal('\n'.join(field.annotations['comment']))))
@[    end if]@
@[    if field.default_value is not None]@
      @@default (value=@(to_idl_literal(get_idl_type(field.type), field.default_value)))
@[    end if]@
@[    if 'unit' in field.annotations]@
      @@unit (value=@(string_to_idl_string_literal(field.annotations['unit'])))
@[    end if]@
@{
idl_type = get_idl_type(field.type)
}@
@[    if field.type.is_fixed_size_array()]@
@{
idl_type = get_idl_type_identifier(idl_type)
}@
@[    end if]@
      @(idl_type) @(field.name);
@[  end for]@
@[else]@
      uint8 structure_needs_at_least_one_member;
@[end if]@
    };

@#typedefs for arrays need to be defined outside of the struct
@{
from collections import OrderedDict

from rosidl_adapter.msg import get_idl_type
from rosidl_adapter.msg import to_idl_literal
from rosidl_adapter.msg import string_to_idl_string_literal

typedefs = OrderedDict()
def get_idl_type_identifier(idl_type):
    return idl_type.replace('::', '__') \
        .replace('<', '__').replace('>', '') \
        .replace('[', '__').replace(']', '')
}@
@[for field in msg.fields]@
@{
idl_type = get_idl_type(field.type)
}@
@[  if field.type.is_fixed_size_array()]@
@{
idl_base_type = idl_type.split('[', 1)[0]
idl_base_type_identifier = idl_base_type.replace('::', '__')
# only necessary for complex types
if idl_base_type_identifier != idl_base_type:
    if idl_base_type_identifier not in typedefs:
        typedefs[idl_base_type_identifier] = idl_base_type
    else:
        assert typedefs[idl_base_type_identifier] == idl_base_type
idl_type_identifier = get_idl_type_identifier(idl_type) + '[' + str(field.type.array_size) + ']'
if idl_type_identifier not in typedefs:
    typedefs[idl_type_identifier] = idl_base_type_identifier
else:
    assert typedefs[idl_type_identifier] == idl_base_type_identifier
}@
@[  end if]@
@[end for]@
@[for k, v in typedefs.items()]@
    typedef @(v) @(k);
@[end for]@
@[if msg.constants]@
    module @(msg.msg_name)_Constants {
@[  for constant in msg.constants]@
      const @(get_idl_type(constant.type)) @(constant.name) = @(to_idl_literal(get_idl_type(constant.type), constant.value));
@[  end for]@
    };
@[end if]@
@#
@[if msg.annotations.get('comment', [])]@
    @@verbatim (language="comment", text=@(string_to_idl_string_literal('\n'.join(msg.annotations['comment']))))
@[end if]@
    struct @(msg.msg_name) {
@[if msg.fields]@
@[  for i, field in enumerate(msg.fields)]@
@[if i > 0]@

@[end if]@
@[    if field.annotations.get('comment', [])]@
      @@verbatim (language="comment", text=@(string_to_idl_string_literal('\n'.join(field.annotations['comment']))))
@[    end if]@
@[    if field.default_value is not None]@
      @@default (value=@(to_idl_literal(get_idl_type(field.type), field.default_value)))
@[    end if]@
@[    if 'unit' in field.annotations]@
      @@unit (value=@(string_to_idl_string_literal(field.annotations['unit'])))
@[    end if]@
@{
idl_type = get_idl_type(field.type)
}@
@[    if field.type.is_fixed_size_array()]@
@{
idl_type = get_idl_type_identifier(idl_type)
}@
@[    end if]@
      @(idl_type) @(field.name);
@[  end for]@
@[else]@
      boolean structure_needs_at_least_one_member;
@[end if]@
    };


fix string join in sample.em

fix string join in sample.em

#! $Id: //projects/empy/sample.em#50 $ $Date: 2003/10/27 $
@# Bangpaths are handled properly (the above line will not appear).
#! This line however will appear (not the first line in the script).
@# This is a comment.  This should not appear in the processed output.
This is text.  It should appear in the processed output.
This is a literal at sign: @@.
This is a line continuation; @
this will appear on the same line.
Note that it will actually eat any white@ space (one word).
@{
# The em.py script has to be somewhere in sys.path!
import em
}@

@# Escape codes.
This will appear on one line.@\nThis will appear on a separate line.
This is separated by a tab:@\tSee?
These are uppercase As (presuming ASCII): A, @\q1001, @\o101, @\d065, @\x41.
@{
import sys
# This is just a normal Python comment.
print("This is more text.")
}@
@# Note the @{ ... }@ convention to suppress the newline following the }.
@# Also note that comments are completely tossed: This is not expanded: @(x).

@# The basics.
@{
import sys, math, string
x = 4
s = 'alpha'
word = "book"
l = [3, 2, 1]
def square(n):
    return n**2
friends = ['Albert', 'Betty', 'Charles', 'Donald']
}@
The basics: The square of @(x) is @(x**2), or @(square(x)).
Internal whitespace is fine: @( x ) squared is @( square(x) ).
Statements: @{sys.stdout.write("%d**2 = %d" % (x, square(x)))}.
Whitespace too: @{ sys.stdout.write("%d**2 = %d (still)" % (x, square(x))) }.
@{
print("But only on single-line statement expansions.")
if 1:
    print("Internal whitespace on multi-line statements is significant.")
for i in range(2):
    print("Normal Python indentation rules must be followed here.")
}@
Simple expressions: x is @x, l is @l, s is "@s," and @x squared is @square(x).
Literals too: x is @x, but would be written @@x.
Trailing dots are ignored: The value of x is @x.
Quotes outside of expansions are also ignored: This is quoted: "x is @x."
@# Whitespace is important in simple expressions.
Array subscription: The first element of l is @l[0].
But this is not: The first element of l is not @l [0].
That was equivalent to: @(l) and then [0], not @l[0].
But whitespace can go inside the brackets: @l[0] is @l[ 0 ].
Same with functions: @square(x) is @square( x ).
The same applies to the other forms.
Involved: The legal digits are @string.digits.
More involved: The names of my friends are @(', '.join(friends)).
Following expressions: Pluralize "@word" as "@(word)s," or maybe "@word@ s."
By default str is used (@s), but you can use repr if you want (@`s`).
Conditional expressions: @(x ? "x is true" ! "x is false").
Pluralization: How many words? @x word@(x != 1 ? 's').
Protected expressions: @(foo $ "foo is not defined").
Also here, whitespace isn't important: @(bar$"bar isn't defined either").
The math module has @(math ? "been imported" $ "not been imported").
The re module has @(re ? "been imported" $ "not been imported").
Division by zero is @(x/0 $ "illegal").
To swallow errors, use None: @(buh $ None) [two spaces].
This is self-expanding: @:2 + 2:(this will get replaced with 4):
You can expand multiple times: @
@empy.expand("@empy.expand('@:2 + 2:hugalugahglughalug:')")

@# More complex examples, including classes.
@{
class C:
    def __init__(self, name):
        self.name = name

    def greetString(self):
        return "Hello, %s" % self.name

    def printGreeting(self):
        sys.stdout.write("Hello, %s" % self.name) # implicit None return

c = C("empy")
}@
c's class is @c.__class__.__name__.
c's name is @c.name.
Method call: @c.greetString().
Note that None is not expanded: @(None) [two spaces].
But the string 'None' is, of course, printed fine: @('None').
So a function can return None for side effects only: @c.printGreeting().

@# Control.
@{
a = 5 # something positive
b = -3 # something negative
z = 0 # zero
}@
If: a is @[if a > 0]positive@[end if].
If/else: b is @[if b > 0]positive@[else]negative@[end if].
If/elif/else: cmp(a, b) is @
@[if a < b]negative@[elif a > b]positive@[else]zero@[end if].
Numbers:@
@[for i in range(10)] @i@[end for].
Evens:@
@[for i in range(10)]@[if i % 2 == 1]@[continue]@[end if] @i@[end for].
Integers less than 5:@
@[for i in range(10)]@[if i >= 5]@[break]@[end if] @i@[end for].
Countdown:@
@{j = 10}@[while j >= 0] @j@{j = j - 1}@[end while].
While/else: @[while z]shouldn't get here@[else]works@[end while].
For/else:@
@[for i in range(3)] @i@[else] also works@[end for].
Tuple unpacking:@[for x in [1, 2, 3, 4]] <@x>@[end for].
Tuple unpacking:@[for x, in [[1], [2], [3], [4]]] <@x>@[end for].
Tuple unpacking:@[for (x,) in [[1], [2], [3], [4]]] <@x>@[end for].
Tuple unpacking:@[for x, y in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x), (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x, y) in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for x, (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for x, (y,) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x), y in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x,), y in [[[1], 2], [[3], 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x,), (y,) in [[[1], [2]], [[3], [4]]]] <@x, @y>@[end for].
More tuple unpacking:@[for (((x))), ((y),) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for].
Garbage is @[try]@hglhagulahguha@[except NameError]not defined@[end try].
Division by zero is @[try]@(a/z)@[except ZeroDivisionError]illegal@[end try].
Catch all: @[try]@ghlaghlhagl@[except]something happened@[end try].
Finally works: @
@[try]@[try]@(1/0)@[finally]finally, @[end try]@[except]and caught@[end try].
@[def sign(x)]@x is @[if x > 0]positive@[elif x < 0]negative@[else]zero@[end if]@[end def]@
Define: @sign(a), @sign(b), @sign(z).

@# Diversions.  Again, a trailing @ is used to suppress the following newline.
A. This text is undiverted.
@empy.startDiversion(1)@
C. This text is diverted.
@empy.stopDiverting()@
B. This text is also undiverted.
@empy.playDiversion(1)@
D. Again, this text is undiverted.
@empy.startDiversion('a')@
E. This text is diverted and then undiverted@
@empy.stopDiverting()@
@empy.replayDiversion('a').
@empy.playDiversion('a') (this should appear twice).
@empy.startDiversion('q')@
F. This text is diverted and then cancelled.
@empy.playDiversion('q')@
G. This text is again undiverted.
@empy.startDiversion('x')@
X. This text will be purged and should not appear!
@empy.stopDiverting()@
H. There should be one remaining diversion: @empy.getAllDiversions().
@empy.purgeDiversion('x')@
I. But not after purging it: @empy.getAllDiversions().
@{
# Finally, make a manual diversion and manipulate it.
empy.createDiversion('z')
zDiversion = empy.retrieveDiversion('z')
zDiversion.write("J. This should be the final diversion, created manually.\n")
empy.playDiversion('z')
}@

@# Parsing checks.
Blanks: @(''), @(""), @(''''''), @("""""").
Single quotes: @('\''), @("'"), @("""'""").
Double quotes: @("\""), @('"'), @('''"''').
Triple quotes: @("\"\"\""), @('"""'), @('\'\'\''), @("'''").
Quotes surrounded by spaces: @(""" " """), @(''' ' ''').
At signs: @('@'), @("@"), @('''@'''), @("""@""").
Close parentheses: @(')'), @(")"), @((")")), @((')')).
Close parentheses in quotes: @("')'"), @('\')\'').
Close braces with an intervening space: @
@{sys.stdout.write("}")} @{sys.stdout.write('}')}.
Repr of a backquote: @`'`'`.
Exes: @("?"?'x'), @(0?"!"!'x'), @(0?":":'x'), @("]"?'x'), @(1?"x"!"]").
Dollar signs: @("$"$None), @(asdf?"$"$"$"), @(1?asdf$"$").
These are strings:
@'single quoted string'
@"double quoted string"
@'single quoted string with escaped \'single quotes\''
@"double quoted string with escaped \"double quotes\""
@'''triple single quoted string'''
@"""triple double quoted string"""
@'single quoted string with "double quotes"'
@"double quoted string with 'single quotes'"
@'''triple single quoted continued \
string'''
@"""triple double quoted continued \
string"""
@'''triple single quoted
...multi-line string'''
@"""triple double quoted
... multi-line string"""

@# Significators.
@%a
@%b
@%c "x"
@%d "x"
@%e "x y"
@%f "x y"
Encountered significators:
a and b should be None: @`__a__`, @`__b__`
c and d should be 'x': @`__c__`, @`__d__`
e and f should be 'x y': @`__e__`, @`__f__`

@# Filters.
@{import string}@
This line should be in mixed case.
@empy.setFilter(string.lower)@
This line should be all lowercase.
@empy.setFilter(string.upper)@
This line should be all uppercase (how gauche).
@empy.setFilter([em.LineBufferedFilter(), lambda x: '[%s]\n' % x[:-1]])@
This line should be bracketed.
So should this line.
@empy.setFilter([em.SizeBufferedFilter(5), lambda x: '*' + x])@
There should be stars every five characters on this line.
@empy.nullFilter()@
This line should not appear at all!
@empy.resetFilter()@
This line should be back to mixed case.
@empy.attachFilter(string.upper)@
@empy.attachFilter(em.LineBufferedFilter())@
@empy.attachFilter(lambda x: '[%s]\n' % x[:-1])@
This line should be all uppercase with brackets.
@empy.resetFilter()@
This line should be back to mixed case (again).

@# Contexts, metaoperations.
@{
import StringIO
def context():
    return "%s:%d" % empy.identify()
stringFile = StringIO.StringIO("2 + 2 = @(2 + 2) [@context()].\n")
}@
The new context is @context().
File inclusion [@context()]: @empy.include(stringFile)@
Expansion [@context()]: @
@empy.expand("This should be appear [@context()]") @
on the same line as this [@context()].
More expansion [@context()]: @
@{sys.stdout.write(empy.expand("Another expansion [@context()]"))}.
This is the next line [@context()].
Quoting: @empy.quote("x when quoted would be '@x' or @x").
More quoting: @empy.quote("This will be @doubled but '''@this is not'''").
Here's the last view of the old context: @context().
Creating a new context ...
@empy.pushContext()@
The current context is: @context().
@?NewName
The context name should now be 'NewName': @context().
@!1000
The line number should now be 1000: @context().
@empy.popContext()@
Back to the old context: @context().

@# Embedded interpreters and standalone expansion.
@{
q = 1
}@
Interpreter's q is @q.
@{
try:
    i = em.Interpreter()
    i.string("@{q = 10}")
    i.string("Embedded interpreter's q is @q.\n")
finally:
    i.shutdown()
}@
Interpreter's q is still @q; the embedded interpreter had no effect.
Standalone expansion: @em.expand("1 + 1 is @(1 + 1).")
With locals: @em.expand("@x + @y is @(x + y).", x=2, y=3)
@{
g = {}
}@
With globals: @em.expand("@{x = 10}g's x is @x.", g)
Still with globals: @em.expand("g's x + 1 is @(x + 1).", g)
g's x is still @g['x'].

@# Hooks.
@{
class SampleHook(em.Hook):
    def null(self):
        self.interpreter.write('[SampleHook.null invoked]')

sampleHook = SampleHook()
empy.addHook(sampleHook)
}@
Invoking the sample hook: @empy.invokeHook('null').
@{
empy.removeHook(sampleHook)
}@

@# Custom.
@{
def customCallback(contents, empy=empy):
    empy.write('[%s]' % contents)
empy.registerCallback(customCallback)
}@
Using a custom markup: @<This appears in brackets>.
Again: @<<There are angle brackets in this one>>.
Once more: @<This is a right angle bracket in quotes: ">">.
@{
empy.deregisterCallback()
}@

@# Finals; note these are evaluated in reverse order.
@empy.atExit(lambda: empy.string("Version: @empy.VERSION.\n"))@
@empy.atExit(lambda: empy.write("This is the penultimate line.\n"))@

#! $Id: //projects/empy/sample.em#50 $ $Date: 2003/10/27 $
@# Bangpaths are handled properly (the above line will not appear).
#! This line however will appear (not the first line in the script).
@# This is a comment.  This should not appear in the processed output.
This is text.  It should appear in the processed output.
This is a literal at sign: @@.
This is a line continuation; @
this will appear on the same line.
Note that it will actually eat any white@ space (one word).
@{
# The em.py script has to be somewhere in sys.path!
import em
}@

@# Escape codes.
This will appear on one line.@\nThis will appear on a separate line.
This is separated by a tab:@\tSee?
These are uppercase As (presuming ASCII): A, @\q1001, @\o101, @\d065, @\x41.
@{
import sys
# This is just a normal Python comment.
print("This is more text.")
}@
@# Note the @{ ... }@ convention to suppress the newline following the }.
@# Also note that comments are completely tossed: This is not expanded: @(x).

@# The basics.
@{
import sys, math, string
x = 4
s = 'alpha'
word = "book"
l = [3, 2, 1]
def square(n):
    return n**2
friends = ['Albert', 'Betty', 'Charles', 'Donald']
}@
The basics: The square of @(x) is @(x**2), or @(square(x)).
Internal whitespace is fine: @( x ) squared is @( square(x) ).
Statements: @{sys.stdout.write("%d**2 = %d" % (x, square(x)))}.
Whitespace too: @{ sys.stdout.write("%d**2 = %d (still)" % (x, square(x))) }.
@{
print("But only on single-line statement expansions.")
if 1:
    print("Internal whitespace on multi-line statements is significant.")
for i in range(2):
    print("Normal Python indentation rules must be followed here.")
}@
Simple expressions: x is @x, l is @l, s is "@s," and @x squared is @square(x).
Literals too: x is @x, but would be written @@x.
Trailing dots are ignored: The value of x is @x.
Quotes outside of expansions are also ignored: This is quoted: "x is @x."
@# Whitespace is important in simple expressions.
Array subscription: The first element of l is @l[0].
But this is not: The first element of l is not @l [0].
That was equivalent to: @(l) and then [0], not @l[0].
But whitespace can go inside the brackets: @l[0] is @l[ 0 ].
Same with functions: @square(x) is @square( x ).
The same applies to the other forms.
Involved: The legal digits are @string.digits.
More involved: The names of my friends are @string.join(friends, ', ').
Following expressions: Pluralize "@word" as "@(word)s," or maybe "@word@ s."
By default str is used (@s), but you can use repr if you want (@`s`).
Conditional expressions: @(x ? "x is true" ! "x is false").
Pluralization: How many words? @x word@(x != 1 ? 's').
Protected expressions: @(foo $ "foo is not defined").
Also here, whitespace isn't important: @(bar$"bar isn't defined either").
The math module has @(math ? "been imported" $ "not been imported").
The re module has @(re ? "been imported" $ "not been imported").
Division by zero is @(x/0 $ "illegal").
To swallow errors, use None: @(buh $ None) [two spaces].
This is self-expanding: @:2 + 2:(this will get replaced with 4):
You can expand multiple times: @
@empy.expand("@empy.expand('@:2 + 2:hugalugahglughalug:')")

@# More complex examples, including classes.
@{
class C:
    def __init__(self, name):
        self.name = name

    def greetString(self):
        return "Hello, %s" % self.name

    def printGreeting(self):
        sys.stdout.write("Hello, %s" % self.name) # implicit None return

c = C("empy")
}@
c's class is @c.__class__.__name__.
c's name is @c.name.
Method call: @c.greetString().
Note that None is not expanded: @(None) [two spaces].
But the string 'None' is, of course, printed fine: @('None').
So a function can return None for side effects only: @c.printGreeting().

@# Control.
@{
a = 5 # something positive
b = -3 # something negative
z = 0 # zero
}@
If: a is @[if a > 0]positive@[end if].
If/else: b is @[if b > 0]positive@[else]negative@[end if].
If/elif/else: cmp(a, b) is @
@[if a < b]negative@[elif a > b]positive@[else]zero@[end if].
Numbers:@
@[for i in range(10)] @i@[end for].
Evens:@
@[for i in range(10)]@[if i % 2 == 1]@[continue]@[end if] @i@[end for].
Integers less than 5:@
@[for i in range(10)]@[if i >= 5]@[break]@[end if] @i@[end for].
Countdown:@
@{j = 10}@[while j >= 0] @j@{j = j - 1}@[end while].
While/else: @[while z]shouldn't get here@[else]works@[end while].
For/else:@
@[for i in range(3)] @i@[else] also works@[end for].
Tuple unpacking:@[for x in [1, 2, 3, 4]] <@x>@[end for].
Tuple unpacking:@[for x, in [[1], [2], [3], [4]]] <@x>@[end for].
Tuple unpacking:@[for (x,) in [[1], [2], [3], [4]]] <@x>@[end for].
Tuple unpacking:@[for x, y in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x), (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x, y) in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for x, (y) in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for x, (y,) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x), y in [[1, 2], [3, 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x,), y in [[[1], 2], [[3], 4]]] <@x, @y>@[end for].
Tuple unpacking:@[for (x,), (y,) in [[[1], [2]], [[3], [4]]]] <@x, @y>@[end for].
More tuple unpacking:@[for (((x))), ((y),) in [[1, [2]], [3, [4]]]] <@x, @y>@[end for].
Garbage is @[try]@hglhagulahguha@[except NameError]not defined@[end try].
Division by zero is @[try]@(a/z)@[except ZeroDivisionError]illegal@[end try].
Catch all: @[try]@ghlaghlhagl@[except]something happened@[end try].
Finally works: @
@[try]@[try]@(1/0)@[finally]finally, @[end try]@[except]and caught@[end try].
@[def sign(x)]@x is @[if x > 0]positive@[elif x < 0]negative@[else]zero@[end if]@[end def]@
Define: @sign(a), @sign(b), @sign(z).

@# Diversions.  Again, a trailing @ is used to suppress the following newline.
A. This text is undiverted.
@empy.startDiversion(1)@
C. This text is diverted.
@empy.stopDiverting()@
B. This text is also undiverted.
@empy.playDiversion(1)@
D. Again, this text is undiverted.
@empy.startDiversion('a')@
E. This text is diverted and then undiverted@
@empy.stopDiverting()@
@empy.replayDiversion('a').
@empy.playDiversion('a') (this should appear twice).
@empy.startDiversion('q')@
F. This text is diverted and then cancelled.
@empy.playDiversion('q')@
G. This text is again undiverted.
@empy.startDiversion('x')@
X. This text will be purged and should not appear!
@empy.stopDiverting()@
H. There should be one remaining diversion: @empy.getAllDiversions().
@empy.purgeDiversion('x')@
I. But not after purging it: @empy.getAllDiversions().
@{
# Finally, make a manual diversion and manipulate it.
empy.createDiversion('z')
zDiversion = empy.retrieveDiversion('z')
zDiversion.write("J. This should be the final diversion, created manually.\n")
empy.playDiversion('z')
}@

@# Parsing checks.
Blanks: @(''), @(""), @(''''''), @("""""").
Single quotes: @('\''), @("'"), @("""'""").
Double quotes: @("\""), @('"'), @('''"''').
Triple quotes: @("\"\"\""), @('"""'), @('\'\'\''), @("'''").
Quotes surrounded by spaces: @(""" " """), @(''' ' ''').
At signs: @('@'), @("@"), @('''@'''), @("""@""").
Close parentheses: @(')'), @(")"), @((")")), @((')')).
Close parentheses in quotes: @("')'"), @('\')\'').
Close braces with an intervening space: @
@{sys.stdout.write("}")} @{sys.stdout.write('}')}.
Repr of a backquote: @`'`'`.
Exes: @("?"?'x'), @(0?"!"!'x'), @(0?":":'x'), @("]"?'x'), @(1?"x"!"]").
Dollar signs: @("$"$None), @(asdf?"$"$"$"), @(1?asdf$"$").
These are strings:
@'single quoted string'
@"double quoted string"
@'single quoted string with escaped \'single quotes\''
@"double quoted string with escaped \"double quotes\""
@'''triple single quoted string'''
@"""triple double quoted string"""
@'single quoted string with "double quotes"'
@"double quoted string with 'single quotes'"
@'''triple single quoted continued \
string'''
@"""triple double quoted continued \
string"""
@'''triple single quoted
...multi-line string'''
@"""triple double quoted
... multi-line string"""

@# Significators.
@%a
@%b
@%c "x"
@%d "x"
@%e "x y"
@%f "x y"
Encountered significators:
a and b should be None: @`__a__`, @`__b__`
c and d should be 'x': @`__c__`, @`__d__`
e and f should be 'x y': @`__e__`, @`__f__`

@# Filters.
@{import string}@
This line should be in mixed case.
@empy.setFilter(string.lower)@
This line should be all lowercase.
@empy.setFilter(string.upper)@
This line should be all uppercase (how gauche).
@empy.setFilter([em.LineBufferedFilter(), lambda x: '[%s]\n' % x[:-1]])@
This line should be bracketed.
So should this line.
@empy.setFilter([em.SizeBufferedFilter(5), lambda x: '*' + x])@
There should be stars every five characters on this line.
@empy.nullFilter()@
This line should not appear at all!
@empy.resetFilter()@
This line should be back to mixed case.
@empy.attachFilter(string.upper)@
@empy.attachFilter(em.LineBufferedFilter())@
@empy.attachFilter(lambda x: '[%s]\n' % x[:-1])@
This line should be all uppercase with brackets.
@empy.resetFilter()@
This line should be back to mixed case (again).

@# Contexts, metaoperations.
@{
import StringIO
def context():
    return "%s:%d" % empy.identify()
stringFile = StringIO.StringIO("2 + 2 = @(2 + 2) [@context()].\n")
}@
The new context is @context().
File inclusion [@context()]: @empy.include(stringFile)@
Expansion [@context()]: @
@empy.expand("This should be appear [@context()]") @
on the same line as this [@context()].
More expansion [@context()]: @
@{sys.stdout.write(empy.expand("Another expansion [@context()]"))}.
This is the next line [@context()].
Quoting: @empy.quote("x when quoted would be '@x' or @x").
More quoting: @empy.quote("This will be @doubled but '''@this is not'''").
Here's the last view of the old context: @context().
Creating a new context ...
@empy.pushContext()@
The current context is: @context().
@?NewName
The context name should now be 'NewName': @context().
@!1000
The line number should now be 1000: @context().
@empy.popContext()@
Back to the old context: @context().

@# Embedded interpreters and standalone expansion.
@{
q = 1
}@
Interpreter's q is @q.
@{
try:
    i = em.Interpreter()
    i.string("@{q = 10}")
    i.string("Embedded interpreter's q is @q.\n")
finally:
    i.shutdown()
}@
Interpreter's q is still @q; the embedded interpreter had no effect.
Standalone expansion: @em.expand("1 + 1 is @(1 + 1).")
With locals: @em.expand("@x + @y is @(x + y).", x=2, y=3)
@{
g = {}
}@
With globals: @em.expand("@{x = 10}g's x is @x.", g)
Still with globals: @em.expand("g's x + 1 is @(x + 1).", g)
g's x is still @g['x'].

@# Hooks.
@{
class SampleHook(em.Hook):
    def null(self):
        self.interpreter.write('[SampleHook.null invoked]')

sampleHook = SampleHook()
empy.addHook(sampleHook)
}@
Invoking the sample hook: @empy.invokeHook('null').
@{
empy.removeHook(sampleHook)
}@

@# Custom.
@{
def customCallback(contents, empy=empy):
    empy.write('[%s]' % contents)
empy.registerCallback(customCallback)
}@
Using a custom markup: @<This appears in brackets>.
Again: @<<There are angle brackets in this one>>.
Once more: @<This is a right angle bracket in quotes: ">">.
@{
empy.deregisterCallback()
}@

@# Finals; note these are evaluated in reverse order.
@empy.atExit(lambda: empy.string("Version: @empy.VERSION.\n"))@
@empy.atExit(lambda: empy.write("This is the penultimate line.\n"))@


python version

python version

#!/usr/bin/make -f
# -*- makefile -*-

# Uncomment this to turn on verbose mode.
@{
from sys import version_info as v
pyversion="%u.%u" % (v.major, v.minor)
}
export DH_VERBOSE=1
export DH_OPTIONS=-v
# this is the --install-layout=deb variety
export PYTHONPATH=@(CMAKE_INSTALL_PREFIX)/lib/python@(pyversion)/dist-packages

%:
	dh  $@@

override_dh_auto_configure:
	dh_auto_configure -Scmake -- \
		-DCMAKE_INSTALL_PREFIX="@(CMAKE_INSTALL_PREFIX)" \
		-DCMAKE_PREFIX_PATH="@(CMAKE_INSTALL_PREFIX)" \
		-DCATKIN_PACKAGE_PREFIX="@(CATKIN_PACKAGE_PREFIX)" \
		-DCATKIN=YES
	dh_auto_configure -Spython_distutils

override_dh_auto_install:
	dh_auto_install -Scmake
	dh_auto_install -Spython_distutils -- \
		--prefix="@(CMAKE_INSTALL_PREFIX)" --install-layout=deb

override_dh_auto_build:
	dh_auto_build -Scmake
	dh_auto_build -Spython_distutils

#!/usr/bin/make -f
# -*- makefile -*-

# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
export DH_OPTIONS=-v

%:
	dh  $@@

override_dh_auto_configure:
	dh_auto_configure -Scmake -- \
		-DCMAKE_INSTALL_PREFIX="@(CMAKE_INSTALL_PREFIX)" \
		-DCMAKE_PREFIX_PATH="@(CMAKE_INSTALL_PREFIX)" \
		-DCATKIN_PACKAGE_PREFIX="@(CATKIN_PACKAGE_PREFIX)" \
		-DCATKIN=YES
	dh_auto_configure -Spython_distutils

override_dh_auto_install:
	dh_auto_install -Scmake
	dh_auto_install -Spython_distutils -- \
		--prefix="@(CMAKE_INSTALL_PREFIX)" --install-layout=deb

override_dh_auto_build:
	dh_auto_build -Scmake
	dh_auto_build -Spython_distutils


Add python executable to ROSUNIT_EXE so it runs correctly on Windows (#200)

include(CMakeParseArguments)

macro(rosunit_initialize_tests)
@[if DEVELSPACE]@
  # binary and script in develspace
  set(ROSUNIT_SCRIPTS_DIR "@(CMAKE_CURRENT_SOURCE_DIR)/scripts")
  set(ROSUNIT_EXE "${PYTHON_EXECUTABLE} ${ROSUNIT_SCRIPTS_DIR}/rosunit")
@[else]@
  # binary and script in installspace
  set(ROSUNIT_SCRIPTS_DIR "${rosunit_DIR}/../scripts")
  set(ROSUNIT_EXE "${PYTHON_EXECUTABLE} ${rosunit_DIR}/../../../@(CATKIN_GLOBAL_BIN_DESTINATION)/rosunit")
@[end if]@
endmacro()

rosunit_initialize_tests()

function(add_pyunit file)

  message(WARNING "add_pyunit() is deprecated.  For Python tests, use catkin_add_nosetests() instead.")

  # Look for optional TIMEOUT argument, #2645
  cmake_parse_arguments(_pyunit "" "TIMEOUT;WORKING_DIRECTORY" "" ${ARGN})
  if(NOT _pyunit_TIMEOUT)
    set(_pyunit_TIMEOUT 60.0)
  endif()

  # Check that the file exists, #1621
  set(_file_name _file_name-NOTFOUND)
  if(IS_ABSOLUTE ${file})
    set(_file_name ${file})
  else()
    find_file(_file_name ${file}
              PATHS ${CMAKE_CURRENT_SOURCE_DIR}
              NO_DEFAULT_PATH
              NO_CMAKE_FIND_ROOT_PATH)  # for cross-compilation.  thanks jeremy.
    if(NOT _file_name)
      message(FATAL_ERROR "Can't find pyunit file \"${file}\"")
    endif()
  endif()

  # We look for ROS_TEST_COVERAGE=1
  # to indicate that coverage reports are being requested.
  if("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
    set(_covarg "--cov")
  else()
    set(_covarg)
  endif()

  # Create a legal test name, in case the target name has slashes in it
  string(REPLACE "/" "_" _testname ${file})
  # We use rostest to call the executable to get process control, #1629
  set(cmd "${ROSUNIT_EXE} --name=${_testname} --time-limit=${_pyunit_TIMEOUT} --package=${PROJECT_NAME} -- ${_file_name} ${_covarg}")
  catkin_run_tests_target("rosunit" ${_testname} "rosunit-${_testname}.xml" COMMAND ${cmd} WORKING_DIRECTORY ${_pyunit_WORKING_DIRECTORY})
endfunction()

include(CMakeParseArguments)

macro(rosunit_initialize_tests)
@[if DEVELSPACE]@
  # binary and script in develspace
  set(ROSUNIT_SCRIPTS_DIR "@(CMAKE_CURRENT_SOURCE_DIR)/scripts")
  set(ROSUNIT_EXE "${ROSUNIT_SCRIPTS_DIR}/rosunit")
@[else]@
  # binary and script in installspace
  set(ROSUNIT_SCRIPTS_DIR "${rosunit_DIR}/../scripts")
  set(ROSUNIT_EXE "${rosunit_DIR}/../../../@(CATKIN_GLOBAL_BIN_DESTINATION)/rosunit")
@[end if]@
endmacro()

rosunit_initialize_tests()

function(add_pyunit file)

  message(WARNING "add_pyunit() is deprecated.  For Python tests, use catkin_add_nosetests() instead.")

  # Look for optional TIMEOUT argument, #2645
  cmake_parse_arguments(_pyunit "" "TIMEOUT;WORKING_DIRECTORY" "" ${ARGN})
  if(NOT _pyunit_TIMEOUT)
    set(_pyunit_TIMEOUT 60.0)
  endif()

  # Check that the file exists, #1621
  set(_file_name _file_name-NOTFOUND)
  if(IS_ABSOLUTE ${file})
    set(_file_name ${file})
  else()
    find_file(_file_name ${file}
              PATHS ${CMAKE_CURRENT_SOURCE_DIR}
              NO_DEFAULT_PATH
              NO_CMAKE_FIND_ROOT_PATH)  # for cross-compilation.  thanks jeremy.
    if(NOT _file_name)
      message(FATAL_ERROR "Can't find pyunit file \"${file}\"")
    endif()
  endif()

  # We look for ROS_TEST_COVERAGE=1
  # to indicate that coverage reports are being requested.
  if("$ENV{ROS_TEST_COVERAGE}" STREQUAL "1")
    set(_covarg "--cov")
  else()
    set(_covarg)
  endif()

  # Create a legal test name, in case the target name has slashes in it
  string(REPLACE "/" "_" _testname ${file})
  # We use rostest to call the executable to get process control, #1629
  set(cmd "${ROSUNIT_EXE} --name=${_testname} --time-limit=${_pyunit_TIMEOUT} --package=${PROJECT_NAME} -- ${_file_name} ${_covarg}")
  catkin_run_tests_target("rosunit" ${_testname} "rosunit-${_testname}.xml" COMMAND ${cmd} WORKING_DIRECTORY ${_pyunit_WORKING_DIRECTORY})
endfunction()