GtkDialog: theme dialog with a black bg and white text

This is coherent with the themeing for Palettes and modal
alerts. Follow up of SL #3971

Signed-off-by: Simon Schampijer <simon@laptop.org
Acked-by: Manuel Quiñones <6f5069c5b6be23302a13accec56587944be09079@laptop.org>

${
import math

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

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

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

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

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26
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;
    -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: false;
    -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: 84px;
    -GtkWidget-text-handle-height: 84px;
}

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

/* Handle this differently? */
.window *:insensitive {
    background-color: @panel_grey;
}

/* Buttons */

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

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

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

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

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

.button:active:focused {
    color: @black;
}

/* 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;
    color: @black;
}

GtkTreeView {
    background-color: @button_grey;
}

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

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

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

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

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

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

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

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

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

/* Combo boxes */

GtkComboBox {
    color: @white;
}

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

/* Notebooks */

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

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

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

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

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

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

/* Browse Widgets */

BrowseTabPage {
    background-color: @black;
}

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

/* Menus and palettes */

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

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

.tooltip * {
    color: @white;
}

/* Scrollbars */

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

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

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

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

/* Progress bars */

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

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @white;
    border-radius: 10px;
    border-style: solid;
    border-width: 0px;
}

GtkProgressBar.trough {
    background-color: alpha (@black, 0.0);
    border-style: solid;
    border-radius: 10px;
    border-color: @button_grey;
    border-width: 2px;
}

/* Separators */

GtkVSeparator, GtkHSeparator,
.toolbar GtkSeparatorToolItem {
    color: @button_grey;
}

/* 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 GtkToolButton .button,
.toolbar SugarRadioToolButton *,
SugarPaletteWindowWidget SugarRadioToolButton *,
SugarPaletteWindowWidget GtkToolButton .button {
    background-color: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding)px;
}

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

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

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

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

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

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

/* Scales */

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

GtkScale.trough {
    background-color: @button_grey;
    border-style: solid;
    border-radius: 30px;
    border-color: @button_grey;
    border-width: 2px;
}

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

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

GtkScale.slider {
    color: alpha(@theme_base_color, 0.0);
    background-color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider.svg");
}

GtkScale.slider:active {
    color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton {
    color: @black;
}

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

.toolbar GtkCheckButton *:active,
SugarPaletteWindowWidget GtkCheckButton *:active {
    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 Favorite Activity icon */

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

SugarFavoriteActivityIcon:prelight {
    border-color: @zoom_views_prelight;
    background-color: @zoom_views_prelight;
}

SugarFavoriteActivityIcon:active {
    border-color: @zoom_views_active;
    background-color: @zoom_views_active;
}

/* Text cursor handles */

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

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

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

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

${
import math

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

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

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

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

# Radio size used to be:
#
# radio_size = my_floor(subcell_size + bullet_size + line_width)
#
# But a screenshot shows that the graphic was actually rendered at 26px
# so the SVG displays at the correct size.
# FIXME this only works for 100, has not been tested in 75 resolution
radio_size = 26
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;
    -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: false;
    -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: 84px;
    -GtkWidget-text-handle-height: 84px;
}

/* Backgrounds and windows */

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

SugarIntroWindow {
    background-color: @white;
}

SugarIntroWindow {
    color: @black;
}

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

/* Handle this differently? */
.window *:insensitive {
    background-color: @panel_grey;
}

/* Buttons */

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

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

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

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

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

.button:active:focused {
    color: @black;
}

/* 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;
    color: @black;
}

GtkTreeView {
    background-color: @button_grey;
}

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

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

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

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

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

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

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

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

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

/* Combo boxes */

GtkComboBox {
    color: @white;
}

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

/* Notebooks */

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

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

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

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

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

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

/* Browse Widgets */

BrowseTabPage {
    background-color: @black;
}

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

/* Menus and palettes */

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

SugarPaletteMenuWidget {
    background-color: @black;
}

SugarPaletteMenuWidget * {
    color: @white;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

.tooltip * {
    color: @white;
}

/* Scrollbars */

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

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

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

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

/* Progress bars */

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

GtkProgressBar.progressbar {
    background-color: @white;
    border-color: @white;
    border-radius: 10px;
    border-style: solid;
    border-width: 0px;
}

GtkProgressBar.trough {
    background-color: alpha (@black, 0.0);
    border-style: solid;
    border-radius: 10px;
    border-color: @button_grey;
    border-width: 2px;
}

/* Separators */

GtkVSeparator, GtkHSeparator,
.toolbar GtkSeparatorToolItem {
    color: @button_grey;
}

/* 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 GtkToolButton .button,
.toolbar SugarRadioToolButton *,
SugarPaletteWindowWidget SugarRadioToolButton *,
SugarPaletteWindowWidget GtkToolButton .button {
    background-color: transparent;
    border-radius: $(toolbutton_padding)px;
    padding: $(toolbutton_padding)px;
}

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

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

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

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

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

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

/* Scales */

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

GtkScale.trough {
    background-color: @button_grey;
    border-style: solid;
    border-radius: 30px;
    border-color: @button_grey;
    border-width: 2px;
}

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

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

GtkScale.slider {
    color: alpha(@theme_base_color, 0.0);
    background-color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider.svg");
}

GtkScale.slider:active {
    color: alpha(@theme_base_color, 0.0);
    background-image: url("assets/scale-slider-active.svg");
}

/* Radio and check buttons */

GtkCheckButton {
    color: @black;
}

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

.toolbar GtkCheckButton *:active,
SugarPaletteWindowWidget GtkCheckButton *:active {
    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 Favorite Activity icon */

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

SugarFavoriteActivityIcon:prelight {
    border-color: @zoom_views_prelight;
    background-color: @zoom_views_prelight;
}

SugarFavoriteActivityIcon:active {
    border-color: @zoom_views_active;
    background-color: @zoom_views_active;
}

/* Text cursor handles */

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

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

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

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


Install abi-compliance-checker from source to avoid Focal package bug (#817)

* Install abi-complinace-checker from source to avoid Focal package bug

* Only install abi-compliance-checker from source on Focal

* Use the same RUN directive for download + installing. Add cleanup

* Apply patch for ubuntu versions that include new ones after Focal

* Simplify distro checking for abi patching following ahcorde suggestion

* Update ros_buildfarm/templates/devel/devel_task.Dockerfile.em

Co-authored-by: Alejandro Hernández Cordero <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* Update ros_buildfarm/templates/devel/devel_task.Dockerfile.em

Co-authored-by: Alejandro Hernández Cordero <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* Update ros_buildfarm/templates/devel/devel_task.Dockerfile.em

Co-authored-by: Alejandro Hernández Cordero <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* Update ros_buildfarm/templates/devel/devel_task.Dockerfile.em

Co-authored-by: Alejandro Hernández Cordero <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* Update ros_buildfarm/templates/devel/devel_task.Dockerfile.em

Co-authored-by: Alejandro Hernández Cordero <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* removed repeated line

Signed-off-by: ahcorde <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* Moved abichecker block above dependencies

Signed-off-by: ahcorde <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* Removed already installed dependencies

Signed-off-by: ahcorde <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* installed python3-pip

Signed-off-by: ahcorde <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* Installed abichecker dependencies conditionally

Signed-off-by: ahcorde <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>

* remove single use variable

Co-authored-by: Alejandro Hernández Cordero <fe24216aaec92db5406308aba9df1265f69c8694@gmail.com>
Co-authored-by: Dirk Thomas <19795c8185177e940eedd30ceb2b716a54a3d5dc@users.noreply.github.com>

# generated from @template_name

@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@

VOLUME ["/var/cache/apt/archives"]

ENV DEBIAN_FRONTEND noninteractive

@(TEMPLATE(
    'snippet/old_release_set.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@

@(TEMPLATE(
    'snippet/setup_locale.Dockerfile.em',
    timezone=timezone,
))@

RUN useradd -u @uid -l -m buildfarm

@(TEMPLATE(
    'snippet/setup_nvidia_docker2.Dockerfile.em'
))@

@(TEMPLATE(
    'snippet/add_distribution_repositories.Dockerfile.em',
    distribution_repository_keys=distribution_repository_keys,
    distribution_repository_urls=distribution_repository_urls,
    os_name=os_name,
    os_code_name=os_code_name,
    add_source=False,
))@

@(TEMPLATE(
    'snippet/add_additional_repositories.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@

@(TEMPLATE(
    'snippet/add_wrapper_scripts.Dockerfile.em',
    wrapper_scripts=wrapper_scripts,
))@

# automatic invalidation once every day
RUN echo "@today_str"

@(TEMPLATE(
    'snippet/install_python3.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@

RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y git python3-yaml

@[if build_tool == 'colcon']@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y python3-pip
@# colcon-core.package_identification.python needs at least setuptools 30.3.0
@# pytest-rerunfailures enables usage of --retest-until-pass
RUN pip3 install -U setuptools pytest-rerunfailures
@[end if]@
@[if ros_version == 2]@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y ros-@(rosdistro_name)-ros-workspace
@[end if]@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y ccache

@[if run_abichecker]@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y python3-catkin-pkg-modules python3-pip
@[if os_name == 'ubuntu' and os_code_name not in ('xenial', 'bionic')]@
# Focal/Groovy abi-compliance-checker package has a bug that breaks python invocation
# See: https://github.com/lvc/abi-compliance-checker/pull/80#issuecomment-652521014
# Install 2.3 version from source, needs perl
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y curl make perl
RUN curl -sL https://github.com/lvc/abi-compliance-checker/archive/2.3.tar.gz | tar xvz -C /tmp && \
    make install prefix=/usr -C /tmp/abi-compliance-checker-2.3 && \
    rm -fr /tmp/abi-compliance
@[else]@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y abi-compliance-checker
@[end if]@
RUN pip3 install -U auto_abi_checker
@[end if]@

@(TEMPLATE(
    'snippet/set_environment_variables.Dockerfile.em',
    environment_variables=build_environment_variables,
))@

@(TEMPLATE(
    'snippet/install_dependencies.Dockerfile.em',
    dependencies=dependencies,
    dependency_versions=dependency_versions,
))@

@(TEMPLATE(
    'snippet/install_dependencies_from_file.Dockerfile.em',
    install_lists=install_lists,
))@

# After all dependencies are installed, update ccache symlinks.
# This command is supposed to be invoked whenever a new compiler is installed
# but that isn't happening. So we invoke it here to make sure all compilers are
# picked up.
# TODO(nuclearsandwich) add link to Debian bug report when one is opened.
RUN which update-ccache-symlinks >/dev/null 2>&1 && update-ccache-symlinks

USER buildfarm
ENTRYPOINT ["sh", "-c"]
@{
cmd = \
    'PATH=/usr/lib/ccache:$PATH' + \
    ' PYTHONPATH=/tmp/ros_buildfarm:$PYTHONPATH python3 -u'
if not testing:
    cmd += \
        ' /tmp/ros_buildfarm/scripts/devel/build_and_install.py' + \
        ' --rosdistro-name ' + rosdistro_name + \
        ' --ros-version ' + str(ros_version) + \
        ' --clean-before'
    if run_abichecker:
        cmd += ' --run-abichecker'
else:
    cmd += \
        ' /tmp/ros_buildfarm/scripts/devel/build_and_test.py' + \
        ' --rosdistro-name %s' % rosdistro_name + \
        ' --ros-version ' + str(ros_version)
    if require_gpu_support:
        cmd += ' --require-gpu-support'
cmd += \
    ' --build-tool ' + build_tool + \
    ' --workspace-root ' + workspace_root + \
    ' --parent-result-space' + ''.join([' %s/install_isolated' % (space) for space in parent_result_space])
if vars().get('build_tool_args'):
    cmd += ' --build-tool-args ' + ' '.join(build_tool_args)
if vars().get('build_tool_test_args'):
    cmd += ' --build-tool-test-args ' + ' '.join(
        a if ' ' not in a else '"%s"' % a for a in build_tool_test_args)
}@
CMD ["@(cmd.replace('"', '\\"'))"]

# generated from @template_name

@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@

VOLUME ["/var/cache/apt/archives"]

ENV DEBIAN_FRONTEND noninteractive

@(TEMPLATE(
    'snippet/old_release_set.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@

@(TEMPLATE(
    'snippet/setup_locale.Dockerfile.em',
    timezone=timezone,
))@

RUN useradd -u @uid -l -m buildfarm

@(TEMPLATE(
    'snippet/setup_nvidia_docker2.Dockerfile.em'
))@

@(TEMPLATE(
    'snippet/add_distribution_repositories.Dockerfile.em',
    distribution_repository_keys=distribution_repository_keys,
    distribution_repository_urls=distribution_repository_urls,
    os_name=os_name,
    os_code_name=os_code_name,
    add_source=False,
))@

@(TEMPLATE(
    'snippet/add_additional_repositories.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
))@

@(TEMPLATE(
    'snippet/add_wrapper_scripts.Dockerfile.em',
    wrapper_scripts=wrapper_scripts,
))@

# automatic invalidation once every day
RUN echo "@today_str"

@(TEMPLATE(
    'snippet/install_python3.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@

RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y git python3-yaml

@[if build_tool == 'colcon']@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y python3-pip
@# colcon-core.package_identification.python needs at least setuptools 30.3.0
@# pytest-rerunfailures enables usage of --retest-until-pass
RUN pip3 install -U setuptools pytest-rerunfailures
@[end if]@
@[if ros_version == 2]@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y ros-@(rosdistro_name)-ros-workspace
@[end if]@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y ccache

@(TEMPLATE(
    'snippet/set_environment_variables.Dockerfile.em',
    environment_variables=build_environment_variables,
))@

@(TEMPLATE(
    'snippet/install_dependencies.Dockerfile.em',
    dependencies=dependencies,
    dependency_versions=dependency_versions,
))@

@(TEMPLATE(
    'snippet/install_dependencies_from_file.Dockerfile.em',
    install_lists=install_lists,
))@

@[if run_abichecker]@
RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y abi-compliance-checker python3 python3-catkin-pkg-modules python3-pip
RUN pip3 install -U auto_abi_checker
@[end if]@

# After all dependencies are installed, update ccache symlinks.
# This command is supposed to be invoked whenever a new compiler is installed
# but that isn't happening. So we invoke it here to make sure all compilers are
# picked up.
# TODO(nuclearsandwich) add link to Debian bug report when one is opened.
RUN which update-ccache-symlinks >/dev/null 2>&1 && update-ccache-symlinks

USER buildfarm
ENTRYPOINT ["sh", "-c"]
@{
cmd = \
    'PATH=/usr/lib/ccache:$PATH' + \
    ' PYTHONPATH=/tmp/ros_buildfarm:$PYTHONPATH python3 -u'
if not testing:
    cmd += \
        ' /tmp/ros_buildfarm/scripts/devel/build_and_install.py' + \
        ' --rosdistro-name ' + rosdistro_name + \
        ' --ros-version ' + str(ros_version) + \
        ' --clean-before'
    if run_abichecker:
        cmd += ' --run-abichecker'
else:
    cmd += \
        ' /tmp/ros_buildfarm/scripts/devel/build_and_test.py' + \
        ' --rosdistro-name %s' % rosdistro_name + \
        ' --ros-version ' + str(ros_version)
    if require_gpu_support:
        cmd += ' --require-gpu-support'
cmd += \
    ' --build-tool ' + build_tool + \
    ' --workspace-root ' + workspace_root + \
    ' --parent-result-space' + ''.join([' %s/install_isolated' % (space) for space in parent_result_space])
if vars().get('build_tool_args'):
    cmd += ' --build-tool-args ' + ' '.join(build_tool_args)
if vars().get('build_tool_test_args'):
    cmd += ' --build-tool-test-args ' + ' '.join(
        a if ' ' not in a else '"%s"' % a for a in build_tool_test_args)
}@
CMD ["@(cmd.replace('"', '\\"'))"]


microRTPS: micrortps_timesync: reduce max RTTI to 50ms

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

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

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

/*!
 * @@file microRTPS_timesync.cpp
 * @@brief source code for time sync implementation
 */

#include <time.h>
#include <cmath>
#include <iostream>

#include "microRTPS_timesync.h"

TimeSync::TimeSync()
    : _offset_ns(-1),
      _skew_ns_per_sync(0.0),
      _num_samples(0),
      _request_reset_counter(0),
      _last_msg_seq(0),
      _last_remote_msg_seq(0)
{ }

TimeSync::~TimeSync() { stop(); }

void TimeSync::start(const TimesyncPublisher* pub) {
	stop();

	_timesync_pub = (*pub);

	auto run = [this]() {
		while (!_request_stop) {
			timesync_msg_t msg = newTimesyncMsg();

			_timesync_pub.publish(&msg);

			std::this_thread::sleep_for(std::chrono::milliseconds(100));
		}
	};
	_request_stop = false;
	_send_timesync_thread.reset(new std::thread(run));
}

void TimeSync::stop() {
	_request_stop = true;
	if (_send_timesync_thread && _send_timesync_thread->joinable()) _send_timesync_thread->join();
	_send_timesync_thread.reset();
}

void TimeSync::reset() {
	_num_samples = 0;
	_request_reset_counter = 0;
}

bool TimeSync::addMeasurement(int64_t local_t1_ns, int64_t remote_t2_ns, int64_t local_t3_ns) {
	int64_t rtti = local_t3_ns - local_t1_ns;

	// assume rtti is evenly split both directions
	int64_t remote_t3_ns = remote_t2_ns + rtti / 2ll;

	int64_t measurement_offset = remote_t3_ns - local_t3_ns;

	if (_request_reset_counter > REQUEST_RESET_COUNTER_THRESHOLD) {
		reset();
		std::cout << "\033[1;33m[ micrortps__timesync ]\tTimesync clock changed, resetting\033[0m" << std::endl;
	}

        if (_num_samples == 0) {
                updateOffset(measurement_offset);
                _skew_ns_per_sync = 0;
        }

	if (_num_samples >= WINDOW_SIZE) {
		if (std::abs(measurement_offset - _offset_ns.load()) > TRIGGER_RESET_THRESHOLD_NS) {
			_request_reset_counter++;
			std::cout << "\033[1;33m[ micrortps__timesync ]\tTimesync offset outlier, discarding\033[0m" << std::endl;
			return false;
		} else {
			_request_reset_counter = 0;
		}
	}

	// ignore if rtti > 50ms
	if (rtti > 50ll * 1000ll * 1000ll) {
		std::cout << "\033[1;33m[ micrortps__timesync ]\tRTTI too high for timesync: " << rtti / (1000ll * 1000ll) << "ms\033[0m" << std::endl;
		return false;
	}

	double alpha = ALPHA_FINAL;
	double beta = BETA_FINAL;

	if (_num_samples < WINDOW_SIZE) {
		double schedule = (double)_num_samples / WINDOW_SIZE;
		double s = 1. - exp(.5 * (1. - 1. / (1. - schedule)));
		alpha = (1. - s) * ALPHA_INITIAL + s * ALPHA_FINAL;
		beta = (1. - s) * BETA_INITIAL + s * BETA_FINAL;
	}

	int64_t offset_prev = _offset_ns.load();
	updateOffset(static_cast<int64_t>((_skew_ns_per_sync + _offset_ns.load()) * (1. - alpha) +
					  measurement_offset * alpha));
	_skew_ns_per_sync =
	    static_cast<int64_t>(beta * (_offset_ns.load() - offset_prev) + (1. - beta) * _skew_ns_per_sync);

	_num_samples++;

	return true;
}

void TimeSync::processTimesyncMsg(timesync_msg_t * msg) {
	if (getMsgSysID(msg) == 1 && getMsgSeq(msg) != _last_remote_msg_seq) {
                _last_remote_msg_seq = getMsgSeq(msg);

		if (getMsgTC1(msg) > 0) {
			if (!addMeasurement(getMsgTS1(msg), getMsgTC1(msg), getMonoRawTimeNSec())) {
				std::cerr << "\033[1;33m[ micrortps__timesync ]\tOffset not updated\033[0m" << std::endl;
			}

		} else if (getMsgTC1(msg) == 0) {
			setMsgTimestamp(msg, getMonoTimeUSec());
			setMsgSysID(msg, 0);
			setMsgSeq(msg, getMsgSeq(msg) + 1);
			setMsgTC1(msg, getMonoRawTimeNSec());

			_timesync_pub.publish(msg);
		}
	}
}

timesync_msg_t TimeSync::newTimesyncMsg() {
	timesync_msg_t msg{};

	setMsgTimestamp(&msg, getMonoTimeUSec());
	setMsgSysID(&msg, 0);
	setMsgSeq(&msg, _last_msg_seq);
	setMsgTC1(&msg, 0);
	setMsgTS1(&msg, getMonoRawTimeNSec());

	_last_msg_seq++;

	return msg;
}

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

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

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

/*!
 * @@file microRTPS_timesync.cpp
 * @@brief source code for time sync implementation
 */

#include <time.h>
#include <cmath>
#include <iostream>

#include "microRTPS_timesync.h"

TimeSync::TimeSync()
    : _offset_ns(-1),
      _skew_ns_per_sync(0.0),
      _num_samples(0),
      _request_reset_counter(0),
      _last_msg_seq(0),
      _last_remote_msg_seq(0)
{ }

TimeSync::~TimeSync() { stop(); }

void TimeSync::start(const TimesyncPublisher* pub) {
	stop();

	_timesync_pub = (*pub);

	auto run = [this]() {
		while (!_request_stop) {
			timesync_msg_t msg = newTimesyncMsg();

			_timesync_pub.publish(&msg);

			std::this_thread::sleep_for(std::chrono::milliseconds(100));
		}
	};
	_request_stop = false;
	_send_timesync_thread.reset(new std::thread(run));
}

void TimeSync::stop() {
	_request_stop = true;
	if (_send_timesync_thread && _send_timesync_thread->joinable()) _send_timesync_thread->join();
	_send_timesync_thread.reset();
}

void TimeSync::reset() {
	_num_samples = 0;
	_request_reset_counter = 0;
}

bool TimeSync::addMeasurement(int64_t local_t1_ns, int64_t remote_t2_ns, int64_t local_t3_ns) {
	int64_t rtti = local_t3_ns - local_t1_ns;

	// assume rtti is evenly split both directions
	int64_t remote_t3_ns = remote_t2_ns + rtti / 2ll;

	int64_t measurement_offset = remote_t3_ns - local_t3_ns;

	if (_request_reset_counter > REQUEST_RESET_COUNTER_THRESHOLD) {
		reset();
		std::cout << "\033[1;33m[ micrortps__timesync ]\tTimesync clock changed, resetting\033[0m" << std::endl;
	}

        if (_num_samples == 0) {
                updateOffset(measurement_offset);
                _skew_ns_per_sync = 0;
        }

	if (_num_samples >= WINDOW_SIZE) {
		if (std::abs(measurement_offset - _offset_ns.load()) > TRIGGER_RESET_THRESHOLD_NS) {
			_request_reset_counter++;
			std::cout << "\033[1;33m[ micrortps__timesync ]\tTimesync offset outlier, discarding\033[0m" << std::endl;
			return false;
		} else {
			_request_reset_counter = 0;
		}
	}

	// ignore if rtti > 100ms
	if (rtti > 100ll * 1000ll * 1000ll) {
		std::cout << "\033[1;33m[ micrortps__timesync ]\tRTTI too high for timesync: " << rtti / (1000ll * 1000ll) << "ms\033[0m" << std::endl;
		return false;
	}

	double alpha = ALPHA_FINAL;
	double beta = BETA_FINAL;

	if (_num_samples < WINDOW_SIZE) {
		double schedule = (double)_num_samples / WINDOW_SIZE;
		double s = 1. - exp(.5 * (1. - 1. / (1. - schedule)));
		alpha = (1. - s) * ALPHA_INITIAL + s * ALPHA_FINAL;
		beta = (1. - s) * BETA_INITIAL + s * BETA_FINAL;
	}

	int64_t offset_prev = _offset_ns.load();
	updateOffset(static_cast<int64_t>((_skew_ns_per_sync + _offset_ns.load()) * (1. - alpha) +
					  measurement_offset * alpha));
	_skew_ns_per_sync =
	    static_cast<int64_t>(beta * (_offset_ns.load() - offset_prev) + (1. - beta) * _skew_ns_per_sync);

	_num_samples++;

	return true;
}

void TimeSync::processTimesyncMsg(timesync_msg_t * msg) {
	if (getMsgSysID(msg) == 1 && getMsgSeq(msg) != _last_remote_msg_seq) {
                _last_remote_msg_seq = getMsgSeq(msg);

		if (getMsgTC1(msg) > 0) {
			if (!addMeasurement(getMsgTS1(msg), getMsgTC1(msg), getMonoRawTimeNSec())) {
				std::cerr << "\033[1;33m[ micrortps__timesync ]\tOffset not updated\033[0m" << std::endl;
			}

		} else if (getMsgTC1(msg) == 0) {
			setMsgTimestamp(msg, getMonoTimeUSec());
			setMsgSysID(msg, 0);
			setMsgSeq(msg, getMsgSeq(msg) + 1);
			setMsgTC1(msg, getMonoRawTimeNSec());

			_timesync_pub.publish(msg);
		}
	}
}

timesync_msg_t TimeSync::newTimesyncMsg() {
	timesync_msg_t msg{};

	setMsgTimestamp(&msg, getMonoTimeUSec());
	setMsgSysID(&msg, 0);
	setMsgSeq(&msg, _last_msg_seq);
	setMsgTC1(&msg, 0);
	setMsgTS1(&msg, getMonoRawTimeNSec());

	_last_msg_seq++;

	return msg;
}


Made changes to prox manager so that when get a proximity event we do not directly fire callback, instead, we call beginFeatureSubscribe, which begins a process that will eventually trigger callbacks.  This way, we can collect feature data before things get too far along.

////////////Now prox manager///////////

(function()
 {
     //map
     //keys: sporef of presence
     //values: map of actual visible objects
     var pResultSet = {};

     /**
      These get placed into the awaitingFeatureData and
      haveFeatureDataFor map
      @param {visible} vis - The visible that we request features for.
      @param {presence} featGrabber - The presence that we're using to
      request features and listen for feature updates.
      @param {presence} subscriber (optional) - A presence
      that wants to receive updates on vis.
      */
     function SubscriptionElement (vis,featGrabber,subscriber)
     {
         this.vis = vis;
         this.featGrabber =featGrabber;
         this.subscribers = {};
         if (typeof(subscriber) != 'undefined')
             this.subscribers[subscriber.toString()] = subscriber;
     }

     SubscriptionElement.prototype.addSubscriber =
         function(presToAdd)
     {
         this.subscribers[presToAdd.toString()] = presToAdd;
     };

     //returns true if there are no subscribers left.
     SubscriptionElement.prototype.noSubscribers =
         function()
     {
         for (var s in this.subscribers)
             return false;

         return true;
     };

     SubscriptionElement.prototype.removeSubscriber =
         function(presToRemove)
     {
         if (!(presToRemove.toString() in this.subscribers))
             delete this.subscribers[presToRemove.toString()];
     };

     //map
     //keys: sporef of visible awaiting data on
     //value: SubscriptionElement
     var awaitingFeatureData = {};

     //map
     //keys: sporef of visibles
     //keys: SubscriptionElement
     var haveFeatureDataFor  = {};

     //map
     //keys: sporef of presence
     //values: array of proximity added functions.  note, some
     //elements may be null if the proximity added function was
     //deregistered.
     var pAddCB = {};

     //map
     //keys: sporef of presence
     //values: array of proximity removed functions.  note, some
     //elements may be null if the proximity removed function was
     //deregistered.
     var pRemCB = {};

     //map
     //keys: sporef of presence
     //values: presences themselves.
     var sporefToPresences = {};

     system.onPresenceConnected(
         function(newPres)
         {
             sporefToPresences[newPres.toString()] = newPres;
             pResultSet[newPres.toString()]        = {};
             pAddCB[newPres.toString()]            = {};
             pRemCB[newPres.toString()]            = {};
         });

     //Does not hold internal data.  Instead, has private-like
     //variables defined above.
     function ProxManager()
     {
     }

     ProxManager.prototype.getProxResultSet = function(pres)
     {
         if (!(pres.toString() in pResultSet))
             throw new Error('Error getting prox result set.  ' +
                             'No record of presence.');

         return pResultSet[pres.toString()];
     };

     ProxManager.prototype.setProxAddCB = function(pres,proxAddCB)
     {
         if (!(pres.toString() in pAddCB))
         {
             throw new Error('Cannot add prox callback because do ' +
                             'Not have associated presence in ProxManager.');
         }

         var pArray = pAddCB[pres.toString()];

	 for (var i = 0; i <= pArray.length; i++)
	 {
	     if ((typeof(pArray[i]) == 'undefined') || (pArray[i] == null))
	     {
		 pArray[i] = proxAddCB;
		 return i;
	     }
	 }
         //shouldn't ever get here (notice the <= on exit condition of
         //for loop).  Just added so that syntax
         //highlighting of emacs doesn't keep bothering me.
         return null;
     };

     ProxManager.prototype.setProxRemCB = function (pres,proxRemCB)
     {
         if (!(pres.toString() in pRemCB))
         {
             throw new Error('Cannot add prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pres.toString()];

	 for (var i = 0; i <= pArray.length; i++)
	 {
	     if ((typeof(pArray[i]) == 'undefined') || (pArray[i] == null))
	     {
		 pArray[i] = proxRemCB;
		 return i;
	     }
	 }
         //shouldn't ever get here (notice the <= on exit condition of
         //for loop).  Just added so that syntax
         //highlighting of emacs doesn't keep bothering me.
         return null;
     };

     ProxManager.prototype.delProxAddCB = function(pres,addID)
     {
         if (!(pres.toString() in pAddCB))
         {
             throw new Error('Cannot del prox add callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pAddCB[pres.toString()];

         if (!(typeof(pArray[addID]) == 'undefined'))
             pArray[addID] = null;
     };

     ProxManager.prototype.delProxRemCB = function(pres,remID)
     {
         if (!(pres.toString() in pRemCB))
         {
             throw new Error('Cannot del prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pres.toString()];

         if (!(typeof(pArray[addID]) == 'undefined'))
             pArray[addID] = null;
     };

     /**
      Gets called by system whenever visibleObj is within proximity
      range of pres.  Will trigger proxadded callbacks to be called
      if we already have the featureData for visibleObj.  Otherwise,
      queue visibleObj onto awaitingFeatureData.
      */
     ProxManager.prototype.proxAddedEvent = function (pres,visibleObj)
     {
         if (!(pres.toString() in pAddCB))
         {
             throw new Error('Cannot call add event callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         if (visibleObj.toString() in haveFeatureDataFor)
         {
             //we already have feature data for this visible.  add pres
             //as subscriber to haveFeatureDataFor map and trigger callback.
             var subElement = haveFeatureDataFor[visibleObj.toString()];
             subElement.addSubscriber(pres);
             this.triggerAddCallback(pres,subElement.vis);
         }
         else
         {
             //we do not already have feature data for this visible.
             //Check if we're waiting on feature data for this object.
             //If we are, then, just add pres as a subscriber.  If we
             //are not, initiate feature object subscription using
             //pres as the requester.  If we are, waiting, then add
             //pres as a subscriber for this feature data.
             if (visibleObj.toString() in awaitingFeatureData)
             {
                 var subElement = awaitingFeatureData[visibleObj.toString()];
                 subElement.addSubscriber(pres);
             }
             else
             {
                 var subElement = new SubscriptionElement(visibleObj,pres,pres);
                 this.beginFeatureSubscribe(pres, visibleObj);
                 awaitingFeatureData[visibleObj.toString()] = subElement;
             }
         }
     };

     //tells pres to listen for feature vector from visibleObj
     ProxManager.prototype.beginFeatureSubscribe = function(pres,visibleObj)
     {
         //for now, simple sanity check.  After 1 second, release,
         //saying that featureData is 1.
         system.timeout(1,std.core.bind(this.subscribeComplete,this,pres,visibleObj,1));
     };

     //should get called when a presence has received feature data
     //from visibleObj.
     ProxManager.prototype.subscribeComplete =
         function(pres,visibleObj,featureData)
     {
         visibleObj.featureData = featureData;
         if (!(visibleObj.toString() in awaitingFeatureData))
         {
             throw new Error('Error in subscribeComplete.  No longer '+
                             'have visibleObj in awaitingFeatureData.');
         }

         var subElement = awaitingFeatureData[visibleObj.toString()];
         delete awaitingFeatureData[visibleObj.toString()];

         if (subElement.noSubscribers())
         {
             //no one is waiting for/actually cares about the new
             //data.
             this.killSubscription(pres,visibleObj);
             return;
         }

         //know that we've received the data and that others want to
         //hear about it.  add subElement to haveFeatureDataFor map
         //and notify all subscribers that visibleObj (with
         //featureData) is in its prox result set and add visibleObj
         //to result set.
         haveFeatureDataFor[visibleObj.toString()] = subElement;
         for (var s in subElement.subscribers)
         {
             this.triggerAddCallback(subElement.subscribers[s],
                                     subElement.vis);
         }
     };

     //actually fire callback for pres's having seen visibleObj in-world
     //and add visibleObj to result set.
     ProxManager.prototype.triggerAddCallback = function(pres,visibleObj)
     {
         pResultSet[pres.toString()][visibleObj.toString()] = visibleObj;
         var pArray = pAddCB[pres.toString()];
         //trigger all non-null non-undefined callbacks
         system.changeSelf(pres);
	 for (var i in pArray)
         {
	     if ((typeof(pArray[i]) != 'undefined') && (pArray[i] != null))
		 pArray[i](visibleObj);
         }
     };

     ProxManager.prototype.proxRemovedEvent = function (pres,visibleObj)
     {
         if (!(pres.toString() in pRemCB))
         {
             throw new Error('Cannot call rem event callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pres.toString()];

         //remove from to proxResultSet
        delete pArray[visibleObj.toString()];
        system.changeSelf(pres);
        //trigger all non-null non-undefined callbacks
	for (var i in pArray)
	    if ((typeof(pArray[i]) != 'undefined') && (pArray[i] != null))
		pArray[i](visibleObj);
     };

     //actually register an instance of proxmanager with system.
     system.__registerProxManager(new ProxManager());
 }
)();

////////////Now prox manager///////////

(function()
 {
     //map
     //keys: sporef of presence
     //values: array of actual visible objects
     var pResultSet = { };

     // //map
     // //keys: sporef of visible awaiting data on
     // //value: Tuparray of visible objects awaiting data,
     // var awaitingFeatureData ={ };

     //map
     //keys: sporef of presence
     //values: array of proximity added functions
     var pAddCB = { };

     //map
     //keys: sporef of presence
     //values: array of proximity removed functions
     var pRemCB = {};

     //map
     //keys: sporef of presence
     //values: presences themselves.
     var sporefToPresences = {};

     system.onPresenceConnected(
         function(newPres)
         {
             sporefToPresences[newPres.toString()] = newPres;
             pResultSet[newPres.toString()]        = {};
             pAddCB[newPres.toString()]            = {};
             pRemCB[newPres.toString()]            = {};
         });

     function ProxManager()
     {
     }

     ProxManager.prototype.getProxResultSet = function(pres)
     {
         if (!(pres.toString() in pResultSet))
             throw new Error('Error getting prox result set.  ' +
                             'No record of presence.');

         return pResultSet[pres.toString()];
     };

     ProxManager.prototype.setProxAddCB = function(pres,proxAddCB)
     {
         if (!(pres.toString() in pAddCB))
         {
             throw new Error('Cannot add prox callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pAddCB[pres.toString()];

	 for (var i = 0; i <= pArray.length; i++)
	 {
	     if ((typeof(pArray[i]) == 'undefined') || (pArray[i] == null))
	     {
		 pArray[i] = proxAddCB;
		 return i;
	     }
	 }
         //shouldn't ever get here (notice the <= on exit condition of
         //for loop).  Just added so that syntax
         //highlighting of emacs doesn't keep bothering me.
         return null;
     };

     ProxManager.prototype.setProxRemCB = function (pres,proxRemCB)
     {
         if (!(pres.toString() in pRemCB))
         {
             throw new Error('Cannot add prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pres.toString()];

	 for (var i = 0; i <= pArray.length; i++)
	 {
	     if ((typeof(pArray[i]) == 'undefined') || (pArray[i] == null))
	     {
		 pArray[i] = proxRemCB;
		 return i;
	     }
	 }
         //shouldn't ever get here (notice the <= on exit condition of
         //for loop).  Just added so that syntax
         //highlighting of emacs doesn't keep bothering me.
         return null;
     };

     ProxManager.prototype.delProxAddCB = function(pres,addID)
     {
         if (!(pres.toString() in pAddCB))
         {
             throw new Error('Cannot del prox add callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pAddCB[pres.toString()];

         if (!(typeof(pArray[addID]) == 'undefined'))
             pArray[addID] = null;
     };

     ProxManager.prototype.delProxRemCB = function(pres,remID)
     {
         if (!(pres.toString() in pRemCB))
         {
             throw new Error('Cannot del prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pres.toString()];

         if (!(typeof(pArray[addID]) == 'undefined'))
             pArray[addID] = null;
     };

     ProxManager.prototype.proxAddedEvent = function (pres,visibleObj)
     {
         if (!(pres.toString() in pAddCB))
         {
             throw new Error('Cannot call add event callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         pResultSet[pres.toString()][visibleObj.toString()] = visibleObj;
         var pArray = pAddCB[pres.toString()];
         //trigger all non-null non-undefined callbacks
         system.changeSelf(pres);
	 for (var i in pArray)
         {
	     if ((typeof(pArray[i]) != 'undefined') && (pArray[i] != null))
		 pArray[i](visibleObj);
         }
     };

     ProxManager.prototype.proxRemovedEvent = function (pres,visibleObj)
     {
         if (!(pres.toString() in pRemCB))
         {
             throw new Error('Cannot call rem event callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pres.toString()];

         //remove from to proxResultSet
        delete pArray[visibleObj.toString()];
        system.changeSelf(pres);
        //trigger all non-null non-undefined callbacks
	for (var i in pArray)
	    if ((typeof(pArray[i]) != 'undefined') && (pArray[i] != null))
		pArray[i](visibleObj);
     };

     system.__registerProxManager(new ProxManager());

 }
)();