Basic GtkTextView theming.

Basic GtkTextView theming.

${
import math

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

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

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

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

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

# Colors:

black = '"#000000"'
toolbar_grey = '"#404040"'
button_grey = '"#808080"'
selection_grey = '"#A6A6A6"'
panel_grey = '"#C0C0C0"'
text_field_grey = '"#E5E5E5"'
white = '"#FFFFFF"'

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

    bg[NORMAL] = $button_grey
    bg[PRELIGHT] = $button_grey
    bg[ACTIVE] = $white
    bg[SELECTED] = $text_field_grey

    fg[NORMAL]        = $black
    fg[ACTIVE]        = $black
    fg[SELECTED]      = $white
    fg[PRELIGHT]      = $white

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

    base[NORMAL] = $text_field_grey
    base[ACTIVE] = $white
    base[SELECTED] = $selection_grey
    base[PRELIGHT] = $white

    fg[INSENSITIVE] = $button_grey
    text[INSENSITIVE] = $button_grey

    xthickness = $thickness
    ythickness = $thickness

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

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

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

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

    GtkScrolledWindow::scrollbar-spacing = 0

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

    GtkTreeView::expander-size = 24

    GtkArrow::arrow-size = 1.0

    GtkToolbar::shadow-type = GTK_SHADOW_NONE

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

    engine "sugar" {
        line_width = $line_width
        thick_line_width = $thick_line_width

        # Not sure about this one, but it is not that important
        max_radius = $( 2*subcell_size )
    }
}

#############################################################
# Styles that set default background/foreground colors
#
#  We almost always need two styles because of the X windows.
#  For example bg[NORMAL] of a window (or eventbox) needs to
#  be set to be the correct window color so that X fills the
#  window correctly from the beginning, and it does not
#  flash in the wrong color.
#############################################################

style "window"
{
    bg[NORMAL] = $panel_grey
}

style "window-child"
{
    bg[INSENSITIVE] = $panel_grey
    base[INSENSITIVE] = $panel_grey

    fg[NORMAL] = $black

    engine "sugar" {
        label_fg_color = $black
    }
}

style "groupbox-panel"
{
    bg[NORMAL] = $selection_grey
}

style "groupbox-panel-child"
{
    bg[INSENSITIVE] = $selection_grey
    base[INSENSITIVE] = $selection_grey

    fg[NORMAL] = $white

    engine "sugar" {
        label_fg_color = $white
    }
}

style "groupbox-palette"
{
    bg[NORMAL] = $toolbar_grey
}

style "groupbox-palette-child"
{
    bg[INSENSITIVE] = $toolbar_grey
    base[INSENSITIVE] = $toolbar_grey

    fg[NORMAL] = $white

    engine "sugar" {
        label_fg_color = $white
    }
}

style "menu"
{
    bg[NORMAL] = $black
    bg[ACTIVE] = $button_grey

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

    xthickness = 0
    ythickness = 0
}

# Can this all be moved in the menuitem style?
style "menu-child"
{
    color["bg_color"] = $black

    fg[NORMAL]   = $white
    fg[PRELIGHT] = $white

    # Work around bug #382646
    text[NORMAL]   = $white
    text[PRELIGHT] = $white

    bg[NORMAL] = $button_grey

    bg[INSENSITIVE] = $black
    base[INSENSITIVE] = $black

    engine "sugar" {
        label_fg_color = $white
    }
}

#########################################################
#########################################################

style "textview"
{
    # We always use white here
    text[NORMAL]      = $black

    GtkTextView::interior-focus = 1
}

style "scrollbar"
{
    GtkScrollbar::slider-width = $subcell_size
    GtkScrollbar::trough-border = 0
    GtkScrollbar::stepper-spacing = 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

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

# hints for the trough padding hacks
# We cannot go trough GTK+, as we don't want a padding on all sides, but only one
# If we wanted padding on all sides, trough-border would work great.
style "hscrollbar" = "scrollbar"
{
    engine "sugar" {
        hint = "hscrollbar"
        # Rounded, not floating point value. maybe change this?
        scrollbar_border = $thickness
    }
}

style "vscrollbar" = "scrollbar"
{
    engine "sugar" {
        hint = "vscrollbar"
        scrollbar_border = $thickness
    }
}

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

    bg[NORMAL] = "#808080"

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

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

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

style "spinbutton"
{
    bg[NORMAL]  = $button_grey
    bg[ACTIVE]  = $white
    fg[NORMAL]  = $white
    fg[ACTIVE]  = $black

    engine "sugar" {
        hint = "spinbutton"
    }
}

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

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

style "frame"
{
    bg[NORMAL] = $toolbar_grey
}

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

    xthickness = 0
    ythickness = 0

    bg[ACTIVE] = $button_grey
}

style "toolbutton"
{
    color["focus_line"] = $black

    bg[PRELIGHT] = $black
    bg[ACTIVE] = $button_grey
    bg[NORMAL] = $black

    fg[NORMAL] = $white
    fg[ACTIVE] = $white
    fg[PRELIGHT] = $white

    engine "sugar" {
        max_radius = 5.0
	fake_padding = $default_padding
    }
}

style "toolbox"
{
    fg[NORMAL] = $white
    fg[ACTIVE] = $white

    bg[NORMAL] = $toolbar_grey
    bg[INSENSITIVE] = $toolbar_grey
    base[INSENSITIVE] = $toolbar_grey
}

style "panel"
{
    bg[NORMAL] = $panel_grey
    fg[NORMAL] = $black
}

style "entry"
{
    ${ entry_thickness = my_ceil(0.3 * (subcell_size*3.0/2.0 - thickness) + thickness) }

    # small inner border and a large x/ythickness for entries
    # to reduce the number of hacks needed :-)
    xthickness = $entry_thickness
    ythickness = $entry_thickness

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

    GtkWidget::focus-line-width = 0
}

style "button"
{
    fg[NORMAL]  = $white

    bg[NORMAL] = $button_grey
}

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

style "checkbutton"
{
    base[NORMAL]      = $white
    base[PRELIGHT]    = $white
    base[ACTIVE]      = $text_field_grey

    text[PRELIGHT]    = $toolbar_grey
    text[NORMAL]      = $toolbar_grey
    text[ACTIVE]      = $toolbar_grey

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

    engine "sugar" {
        # Reset the colors to the normal color again
        # as they were overriden by the button style
        fg[NORMAL]        = label_fg_color
        fg[PRELIGHT]      = label_fg_color
        fg[ACTIVE]        = label_fg_color
    }
}

style "progressbar"
{
    xthickness = 0
    ythickness = 0
}

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

    bg[PRELIGHT] = $button_grey

    text[PRELIGHT]    = $white
    text[NORMAL]      = $white
    text[ACTIVE]      = $white

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

style "checkmenuitem"
{
    # This style is only there because of bug #382646 ...
    base[NORMAL]      = $white
    base[PRELIGHT]    = $white
    base[ACTIVE]      = $text_field_grey

    text[PRELIGHT]    = $toolbar_grey
    text[NORMAL]      = $toolbar_grey
    text[ACTIVE]      = $toolbar_grey
}

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

####################################################################
# Default style, setting some generic options and style properties
####################################################################
class "GtkWidget" style "default"

####################################################################
# Styles that apply the different background (and foreground) colors
####################################################################

# This one should probably be the default (ie. no window-child style)
widget_class "<GtkWindow>"               style "window"
widget_class "<GtkWindow>*<GtkEventBox>" style "window"
widget_class "<GtkWindow>*"              style "window-child"

# SugarToolbox
#widget_class "*<SugarToolbox>" style "toolbox"
#widget_class "*<SugarToolbox>*" style "toolbox-child"
#widget_class "*<SugarToolbox>*<GtkEventBox>" style "toolbox"
widget_class "*<SugarToolbox>*"        style "toolbox"
widget_class "*<GtkToolbar>*"          style "toolbox"

# SugarPalette
widget_class "<SugarPalette>"          style "menu"
widget_class "<SugarPalette>.*"        style "menu-child"

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

# SugarPanel
widget_class "*<SugarPanel>*"          style "panel"

# GroupBoxes (don't set bg[NORMAL] on the widget itself)
widget_class "<GtkWindow>*<SugarGroupBox>*<GtkEventBox>" style "groupbox-panel"
widget_class "<GtkWindow>*<SugarGroupBox>*"   style "groupbox-panel-child"

widget_class "<SugarPalette>*<SugarGroupBox>*<GtkEventBox>" style "groupbox-palette"
widget_class "<SugarPalette>*<SugarGroupBox>*"   style "groupbox-palette-child"

####################################################################
# Normal widget styles, using the above things
####################################################################
# The following only uses widget_class matches to get the priority
# right. An alternative would be to lower the priority of the
# background matches (which need widget_class) to eg. "application"

# Menu
widget_class "*<GtkMenuShell>"            style "menu"               # Why is this menu shell?
widget_class "*<GtkMenuShell>.*"          style "menu-child"         # Why is this menu shell?
widget_class "*<GtkMenuItem>*"            style "menuitem"
widget_class "*<GtkCheckMenuItem>"        style "checkmenuitem"
widget_class "*<GtkSeparatorMenuItem>*"   style "separatormenuitem"

# Buttons and Combos
widget_class "*<GtkComboBox>*"         style "combobox"
widget_class "*<GtkComboBoxEntry>*"    style "comboboxentry"
widget_class "*<GtkCombo>*"            style "comboboxentry"
widget_class "*<GtkButton>*"           style "button"
widget_class "*<GtkCheckButton>*"      style "checkbutton"

# Entries
widget_class "*<GtkEntry>"       style "entry"
widget_class "*<GtkSpinButton>"  style "spinbutton"
widget_class "*<GtkTextView>"    style "textview"

# Misc widgets
widget_class "*<GtkNotebook>"    style "notebook"
widget_class "*<GtkHScrollbar>"  style "hscrollbar"
widget_class "*<GtkVScrollbar>"  style "vscrollbar"
widget_class "*<GtkHScale>"      style "hscale"
widget_class "*<GtkVScale>"      style "vscale"
widget_class "*<GtkProgressBar>" style "progressbar"

# Toolbar
widget_class "*<GtkToolButton>*"          style "toolbutton"

${
import math

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

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

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

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

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

# Colors:

black = '"#000000"'
toolbar_grey = '"#404040"'
button_grey = '"#808080"'
selection_grey = '"#A6A6A6"'
panel_grey = '"#C0C0C0"'
text_field_grey = '"#E5E5E5"'
white = '"#FFFFFF"'

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

    bg[NORMAL] = $button_grey
    bg[PRELIGHT] = $button_grey
    bg[ACTIVE] = $white
    bg[SELECTED] = $text_field_grey

    fg[NORMAL]        = $black
    fg[ACTIVE]        = $black
    fg[SELECTED]      = $white
    fg[PRELIGHT]      = $white

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

    base[NORMAL] = $text_field_grey
    base[ACTIVE] = $white
    base[SELECTED] = $selection_grey
    base[PRELIGHT] = $white

    fg[INSENSITIVE] = $button_grey
    text[INSENSITIVE] = $button_grey

    xthickness = $thickness
    ythickness = $thickness

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

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

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

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

    GtkScrolledWindow::scrollbar-spacing = 0

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

    GtkTreeView::expander-size = 24

    GtkArrow::arrow-size = 1.0

    GtkToolbar::shadow-type = GTK_SHADOW_NONE

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

    engine "sugar" {
        line_width = $line_width
        thick_line_width = $thick_line_width

        # Not sure about this one, but it is not that important
        max_radius = $( 2*subcell_size )
    }
}

#############################################################
# Styles that set default background/foreground colors
#
#  We almost always need two styles because of the X windows.
#  For example bg[NORMAL] of a window (or eventbox) needs to
#  be set to be the correct window color so that X fills the
#  window correctly from the beginning, and it does not
#  flash in the wrong color.
#############################################################

style "window"
{
    bg[NORMAL] = $panel_grey
}

style "window-child"
{
    bg[INSENSITIVE] = $panel_grey
    base[INSENSITIVE] = $panel_grey

    fg[NORMAL] = $black

    engine "sugar" {
        label_fg_color = $black
    }
}

style "groupbox-panel"
{
    bg[NORMAL] = $selection_grey
}

style "groupbox-panel-child"
{
    bg[INSENSITIVE] = $selection_grey
    base[INSENSITIVE] = $selection_grey

    fg[NORMAL] = $white

    engine "sugar" {
        label_fg_color = $white
    }
}

style "groupbox-palette"
{
    bg[NORMAL] = $toolbar_grey
}

style "groupbox-palette-child"
{
    bg[INSENSITIVE] = $toolbar_grey
    base[INSENSITIVE] = $toolbar_grey

    fg[NORMAL] = $white

    engine "sugar" {
        label_fg_color = $white
    }
}

style "menu"
{
    bg[NORMAL] = $black
    bg[ACTIVE] = $button_grey

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

    xthickness = 0
    ythickness = 0
}

# Can this all be moved in the menuitem style?
style "menu-child"
{
    color["bg_color"] = $black

    fg[NORMAL]   = $white
    fg[PRELIGHT] = $white

    # Work around bug #382646
    text[NORMAL]   = $white
    text[PRELIGHT] = $white

    bg[NORMAL] = $button_grey

    bg[INSENSITIVE] = $black
    base[INSENSITIVE] = $black

    engine "sugar" {
        label_fg_color = $white
    }
}

#########################################################
#########################################################

style "scrollbar"
{
    GtkScrollbar::slider-width = $subcell_size
    GtkScrollbar::trough-border = 0
    GtkScrollbar::stepper-spacing = 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

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

# hints for the trough padding hacks
# We cannot go trough GTK+, as we don't want a padding on all sides, but only one
# If we wanted padding on all sides, trough-border would work great.
style "hscrollbar" = "scrollbar"
{
    engine "sugar" {
        hint = "hscrollbar"
        # Rounded, not floating point value. maybe change this?
        scrollbar_border = $thickness
    }
}

style "vscrollbar" = "scrollbar"
{
    engine "sugar" {
        hint = "vscrollbar"
        scrollbar_border = $thickness
    }
}

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

    bg[NORMAL] = "#808080"

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

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

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

style "spinbutton"
{
    bg[NORMAL]  = $button_grey
    bg[ACTIVE]  = $white
    fg[NORMAL]  = $white
    fg[ACTIVE]  = $black

    engine "sugar" {
        hint = "spinbutton"
    }
}

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

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

style "frame"
{
    bg[NORMAL] = $toolbar_grey
}

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

    xthickness = 0
    ythickness = 0

    bg[ACTIVE] = $button_grey
}

style "toolbutton"
{
    color["focus_line"] = $black

    bg[PRELIGHT] = $black
    bg[ACTIVE] = $button_grey
    bg[NORMAL] = $black

    fg[NORMAL] = $white
    fg[ACTIVE] = $white
    fg[PRELIGHT] = $white

    engine "sugar" {
        max_radius = 5.0
	fake_padding = $default_padding
    }
}

style "toolbox"
{
    fg[NORMAL] = $white
    fg[ACTIVE] = $white

    bg[NORMAL] = $toolbar_grey
    bg[INSENSITIVE] = $toolbar_grey
    base[INSENSITIVE] = $toolbar_grey
}

style "panel"
{
    bg[NORMAL] = $panel_grey
    fg[NORMAL] = $black
}

style "entry"
{
    ${ entry_thickness = my_ceil(0.3 * (subcell_size*3.0/2.0 - thickness) + thickness) }

    # small inner border and a large x/ythickness for entries
    # to reduce the number of hacks needed :-)
    xthickness = $entry_thickness
    ythickness = $entry_thickness

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

    GtkWidget::focus-line-width = 0
}

style "button"
{
    fg[NORMAL]  = $white

    bg[NORMAL] = $button_grey
}

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

style "checkbutton"
{
    base[NORMAL]      = $white
    base[PRELIGHT]    = $white
    base[ACTIVE]      = $text_field_grey

    text[PRELIGHT]    = $toolbar_grey
    text[NORMAL]      = $toolbar_grey
    text[ACTIVE]      = $toolbar_grey

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

    engine "sugar" {
        # Reset the colors to the normal color again
        # as they were overriden by the button style
        fg[NORMAL]        = label_fg_color
        fg[PRELIGHT]      = label_fg_color
        fg[ACTIVE]        = label_fg_color
    }
}

style "progressbar"
{
    xthickness = 0
    ythickness = 0
}

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

    bg[PRELIGHT] = $button_grey

    text[PRELIGHT]    = $white
    text[NORMAL]      = $white
    text[ACTIVE]      = $white

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

style "checkmenuitem"
{
    # This style is only there because of bug #382646 ...
    base[NORMAL]      = $white
    base[PRELIGHT]    = $white
    base[ACTIVE]      = $text_field_grey

    text[PRELIGHT]    = $toolbar_grey
    text[NORMAL]      = $toolbar_grey
    text[ACTIVE]      = $toolbar_grey
}

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

####################################################################
# Default style, setting some generic options and style properties
####################################################################
class "GtkWidget" style "default"

####################################################################
# Styles that apply the different background (and foreground) colors
####################################################################

# This one should probably be the default (ie. no window-child style)
widget_class "<GtkWindow>"               style "window"
widget_class "<GtkWindow>*<GtkEventBox>" style "window"
widget_class "<GtkWindow>*"              style "window-child"

# SugarToolbox
#widget_class "*<SugarToolbox>" style "toolbox"
#widget_class "*<SugarToolbox>*" style "toolbox-child"
#widget_class "*<SugarToolbox>*<GtkEventBox>" style "toolbox"
widget_class "*<SugarToolbox>*"        style "toolbox"
widget_class "*<GtkToolbar>*"          style "toolbox"

# SugarPalette
widget_class "<SugarPalette>"          style "menu"
widget_class "<SugarPalette>.*"        style "menu-child"

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

# SugarPanel
widget_class "*<SugarPanel>*"          style "panel"

# GroupBoxes (don't set bg[NORMAL] on the widget itself)
widget_class "<GtkWindow>*<SugarGroupBox>*<GtkEventBox>" style "groupbox-panel"
widget_class "<GtkWindow>*<SugarGroupBox>*"   style "groupbox-panel-child"

widget_class "<SugarPalette>*<SugarGroupBox>*<GtkEventBox>" style "groupbox-palette"
widget_class "<SugarPalette>*<SugarGroupBox>*"   style "groupbox-palette-child"

####################################################################
# Normal widget styles, using the above things
####################################################################
# The following only uses widget_class matches to get the priority
# right. An alternative would be to lower the priority of the
# background matches (which need widget_class) to eg. "application"

# Menu
widget_class "*<GtkMenuShell>"            style "menu"               # Why is this menu shell?
widget_class "*<GtkMenuShell>.*"          style "menu-child"         # Why is this menu shell?
widget_class "*<GtkMenuItem>*"            style "menuitem"
widget_class "*<GtkCheckMenuItem>"        style "checkmenuitem"
widget_class "*<GtkSeparatorMenuItem>*"   style "separatormenuitem"

# Buttons and Combos
widget_class "*<GtkComboBox>*"         style "combobox"
widget_class "*<GtkComboBoxEntry>*"    style "comboboxentry"
widget_class "*<GtkCombo>*"            style "comboboxentry"
widget_class "*<GtkButton>*"           style "button"
widget_class "*<GtkCheckButton>*"      style "checkbutton"

# Entries
widget_class "*<GtkEntry>"       style "entry"
widget_class "*<GtkSpinButton>"  style "spinbutton"

# Misc widgets
widget_class "*<GtkNotebook>"    style "notebook"
widget_class "*<GtkHScrollbar>"  style "hscrollbar"
widget_class "*<GtkVScrollbar>"  style "vscrollbar"
widget_class "*<GtkHScale>"      style "hscale"
widget_class "*<GtkVScale>"      style "vscale"
widget_class "*<GtkProgressBar>" style "progressbar"

# Toolbar
widget_class "*<GtkToolButton>*"          style "toolbutton"


Fix typo in featureObject/proxManager.

Fix typo in featureObject/proxManager.

system.require('featureObject.em');

(function()
 {
     //if we request the feature object this many times from another
     //visible, and do not receive it, then just fire the prox event
     //for the visible setting featureObject to be undefined.
     var NUM_TRIES_TO_GET_FEATURE_OBJECT = 3;
     //if we haven't heard back from visible in this amount of time,
     //then either re-issue the feature object request (up to
     //NUM_TRIES_TO_GET_FATURE_OBJECT times), or just pass through
     //undefined for the visible's featureObject (if tried that many
     //times, and didn't get a response).
     var TIME_TO_WAIT_FOR_FEATURE_OBJECT_RESP = 10;

     //Want to avoid firing prox callbacks in cases where we get a
     //removal quickly followed by an addition.  (can happen if a
     //dynamic object switches to static tree, even if object does not
     //move at all).  To mask this, we wait this number of seconds before
     //actually firing removal notifications.
     var TIME_TO_WAIT_BETWEEN_REMOVAL_AND_NOTIFICATION = 1;

     //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,proxMan,subscriber)
     {
         this.vis = vis;
         this.featGrabber =featGrabber;
         this.proxMan = proxMan;
         this.subscribers = {};
         if (typeof(subscriber) != 'undefined')
             this.subscribers[subscriber.toString()] = subscriber;

         this.awaitingTimeout  = {};
     }

     /**
      @return {bool} -- True if should fire a prox callback after this
      event, false otherwise.

      Note: the reason for the return value here is that we try to
      mask removals followed by quick additions (reason: the interface
      that pinto provides us doesn't actually tell us when something
      has moved out of our proximity query, it tells us when something
      has moved out of a tree that we were watching).  For instance,
      if a visible moves from the dynamic tree to the static tree, we
      get a removal and addition, even if the visible itself has not
      moved at all.
      */
     SubscriptionElement.prototype.addSubscriber =
         function(presToAdd)
     {
         var pString = presToAdd.toString();
         this.subscribers[pString] = presToAdd;

         //may have gotten a quick removal followed by addition.  if
         //did, then clear waiting timeout.
         if (pString in this.awaitingTimeout)
         {
             this.awaitingTimeout[pString].clear();
             delete this.awaitingTimeout[pString];
             return false;
         }

         return true;
     };

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

         return true;
     };

     SubscriptionElement.prototype.startRemoveSubscriber =
         function(presToRemove)
     {
         var timer = system.timeout(
             TIME_TO_WAIT_BETWEEN_REMOVAL_AND_NOTIFICATION,
             std.core.bind(elemFinalizeRemove,undefined,this,presToRemove));

         //if already had a timer waiting on removing the subscriber,
         //cancel it first.
         var pString = presToRemove.toString();
         if (pString in this.awaitingTimeout)
             this.awaitingTimeout[pString].clear();

         this.awaitingTimeout[pString] = timer;
     };

     function elemFinalizeRemove(subElement,presToRemove)
     {
         if (presToRemove.toString() in subElement.subscribers)
             delete subElement.subscribers[presToRemove.toString()];

         finalizeRemove(
             presToRemove,subElement.vis,subElement.proxMan,subElement);
     }

     //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)
         {
             var nString = newPres.toString();
             sporefToPresences[nString] = newPres;
             pResultSet[nString]        = {};
             pAddCB[nString]            = [];
             pRemCB[nString]            = [];
         });

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

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

         return pResultSet[pString];
     };

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

         var pArray = pAddCB[pString];

	 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)
     {
         var pString = pres.toString();
         if (!(pString in pRemCB))
         {
             throw new Error('Cannot add prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pString];

	 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)
     {
         var pString = pres.toString();
         if (!(pString in pAddCB))
         {
             throw new Error('Cannot del prox add callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pAddCB[pString];

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

     ProxManager.prototype.delProxRemCB = function(pres,remID)
     {
         var pString = pres.toString();

         if (!(pString in pRemCB))
         {
             throw new Error('Cannot del prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pString];

         if (!(typeof(pArray[remID]) == 'undefined'))
             pArray[remID] = 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)
     {
         var pString = pres.toString();
         var vString = visibleObj.toString();
         //ignore events where you have entered your own prox set.
         if (pString == vString)
             return;

         if (!(pString in pAddCB))
         {
             throw new Error('Cannot call add event callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         if (vString in haveFeatureDataFor)
         {
             //we already have feature data for this visible.  add pres
             //as subscriber to haveFeatureDataFor map and trigger callback.
             var subElement = haveFeatureDataFor[vString];
             var issueCallback = subElement.addSubscriber(pres);
             if (issueCallback)
                 this.triggerAddCallback(pres,subElement.vis);
         }
         else
         {
             //short-circuits: does not send message to get feature data.
             var subElement =
                 new SubscriptionElement(visibleObj,pres,this,pres);

             haveFeatureDataFor[vString] = subElement;
             this.triggerAddCallback(pres,subElement.vis);
         }
     };

     /**
      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)
     // {
     //     //ignore events where you have entered your own prox set.
     //     if (pres.toString() == visibleObj.toString())
     //         return;

     //     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.
     //numTriesLeft should be left blank except when this function
     //calls itself.  (the idea is that it tries to get feature object
     //data from a presence multiple times and numTriesLeft is a
     //simple way for the function to keep track of how many times
     //we've attempted to get feature object data from other side.
     ProxManager.prototype.beginFeatureSubscribe =
         function(pres,visibleObj,numTriesLeft)
     {
         if (typeof(numTriesLeft) == 'undefined')
             numTriesLeft = NUM_TRIES_TO_GET_FEATURE_OBJECT;

         //If we try multiple times, and still don't get a response, the visible
         //probably is not running the feature object protocol.  We
         //can't do anything about that, and instead fire callback
         //with undefined passed through for featureObject.
         if (numTriesLeft <=0)
         {
             this.subscribeComplete(pres,visibleObj,undefined);
             return;
         }
         --numTriesLeft;

         //see handleFeautreObjectSubscribe (featureObject.em) to see
         //what format the feature object subscription request message
         //should take.
         pres # {'presFeatureSubRequest':true} >> visibleObj >>
             [std.core.bind(handleFeatureData,undefined,this,pres,visibleObj),
              TIME_TO_WAIT_FOR_FEATURE_OBJECT_RESP,
              std.core.bind(
                  this.beginFeatureSubscribe,
                  this,pres,visibleObj,numTriesLeft)];

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

     function handleFeatureData(proxMan,mPres,vis,featureMsg,visSender)
     {
         //see handleFeatureObjectSubscribe for structure of reply
         //mesages.
         proxMan.subscribeComplete(mPres,vis,featureMsg.data);
     }

     //Call this function when you want pres to unsubscribe for
     //feature data from visibleObj.
     ProxManager.prototype.killSubscription = function(pres,visibleObj)
     {
         //short-circuits.
         return;

         //see handleFeautreObjectRequestUnsubscribe message handler
         //in featureObject.em to see what the format of a
         //killSubscription message should look like.
         pres # {'presFeatureReqUnsub': true} >> visibleObj >> [];
     };

     //should get called when a presence has received feature data
     //from visibleObj.
     ProxManager.prototype.subscribeComplete =
         function(pres,visibleObj,featureData)
     {
         var vString = visibleObj.toString();

         visibleObj.featureObject= featureData;
         if (!(vString in awaitingFeatureData))
         {
             throw new Error('Error in subscribeComplete.  No longer '+
                             'have visibleObj in awaitingFeatureData.');
         }

         var subElement = awaitingFeatureData[vString];
         delete awaitingFeatureData[vString];

         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[vString] = 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)
     {
         var pString = pres.toString();
         pResultSet[pString][visibleObj.toString()] = visibleObj;
         var pArray = pAddCB[pString];
         //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);
         }
     };

     //called by system when pinto actually states that visibleObj is
     //distant from pres.  If the visible exists in awaitingFeatureData
     //then we just remove a subscriber from it.  If the visible exists in
     //haveFeatureDataFor, then fire removed callback, decrement
     //subscribers.  If no subscribers left in haveFeatureDataFor, then
     //unsubscribe from feature data, and remove element from
     //haveFeatureDataFor.
     ProxManager.prototype.proxRemovedEvent = function (pres,visibleObj)
     {
         var pString = pres.toString();
         var vString = visibleObj.toString();
         if (pString == vString)
             return;

         if (vString in awaitingFeatureData)
             awaitingFeatureData[vString].startRemoveSubscriber(pres);
         else
         {
             //this line ensures that visibleObj will maintain
             //featureData associated with stored vis.
             visibleObj = haveFeatureDataFor[vString].vis;
             var subElem = haveFeatureDataFor[vString];
             subElem.startRemoveSubscriber(pres);
         }
     };

     //actually call remove callback
     function finalizeRemove (pres,visibleObj,proxMan,subElem)
     {
         if (subElem.noSubscribers())
         {
             proxMan.killSubscription(pres,visibleObj);
             delete haveFeatureDataFor[visibleObj.toString()];
         }

         //actually fire removed callback.
         proxMan.triggerRemoveCallback(pres,visibleObj);
     };

     ProxManager.prototype.triggerRemoveCallback = function(pres,visibleObj)
     {
         var pString = pres.toString();

         if (!(pString in pRemCB))
         {
             throw new Error('Cannot call rem event callback because do ' +
                             'not have associated presence in ProxManager.');
         }
         if (!(pString in pResultSet))
         {
             throw new Error('Cannot call rem event callback for presence ' +
                             'because no presence in pResultSet matches.');
         }

         //remove from to proxResultSet
         delete pResultSet[pString][visibleObj.toString()];

         //actually issue callbacks
         var pArray = pRemCB[pString];
         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);
         }

     };

     //This function handles all the feature object updates.  For now,
     //we have a very dumb protocol.  We transmit the full feature
     //object instead of the parts that have changed/been updated.
     function handleFeatureObjUpdates(updateMsg,sender)
     {
         var update = updateMsg.featureObjUpdate;
         var sString = sender.toString();

         if (!(sString in haveFeatureDataFor))
         {
             throw new Error('Error upon reception of feature object ' +
                             'update.  Had no subscription to sender ' +
                             'of message.');
         }

         var localVis = haveFeatureDataFor[sString].vis;
         localVis.featureObject = update;

         //sends ack to other side so update sender knows we're still
         //alive.
         updateMsg.makeReply({}) >> [];
     }
     //see sendFeaturesToSubscribers function in featureObject.em for
     //format of featureObject update messages.
     handleFeatureObjUpdates << {'featureObjUpdate'::};

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

system.require('featureObject.em');

(function()
 {
     //if we request the feature object this many times from another
     //visible, and do not receive it, then just fire the prox event
     //for the visible setting featureObject to be undefined.
     var NUM_TRIES_TO_GET_FEATURE_OBJECT = 3;
     //if we haven't heard back from visible in this amount of time,
     //then either re-issue the feature object request (up to
     //NUM_TRIES_TO_GET_FATURE_OBJECT times), or just pass through
     //undefined for the visible's featureObject (if tried that many
     //times, and didn't get a response).
     var TIME_TO_WAIT_FOR_FEATURE_OBJECT_RESP = 10;

     //Want to avoid firing prox callbacks in cases where we get a
     //removal quickly followed by an addition.  (can happen if a
     //dynamic object switches to static tree, even if object does not
     //move at all).  To mask this, we wait this number of seconds before
     //actually firing removal notifications.
     var TIME_TO_WAIT_BETWEEN_REMOVAL_AND_NOTIFICATION = 1;

     //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,proxMan,subscriber)
     {
         this.vis = vis;
         this.featGrabber =featGrabber;
         this.proxMan = proxMan;
         this.subscribers = {};
         if (typeof(subscriber) != 'undefined')
             this.subscribers[subscriber.toString()] = subscriber;

         this.awaitingTimeout  = {};
     }

     /**
      @return {bool} -- True if should fire a prox callback after this
      event, false otherwise.

      Note: the reason for the return value here is that we try to
      mask removals followed by quick additions (reason: the interface
      that pinto provides us doesn't actually tell us when something
      has moved out of our proximity query, it tells us when something
      has moved out of a tree that we were watching).  For instance,
      if a visible moves from the dynamic tree to the static tree, we
      get a removal and addition, even if the visible itself has not
      moved at all.
      */
     SubscriptionElement.prototype.addSubscriber =
         function(presToAdd)
     {
         var pString = presToAdd.toString();
         this.subscribers[pString] = presToAdd;

         //may have gotten a quick removal followed by addition.  if
         //did, then clear waiting timeout.
         if (pString in this.awaitingTimeout)
         {
             this.awaitingTimeout[pString].clear();
             delete this.awaitingTimeout[pString];
             return false;
         }

         return true;
     };

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

         return true;
     };

     SubscriptionElement.prototype.startRemoveSubscriber =
         function(presToRemove)
     {
         var timer = system.timeout(
             TIME_TO_WAIT_BETWEEN_REMOVAL_AND_NOTIFICATION,
             std.core.bind(elemFinalizeRemove,undefined,this,presToRemove));

         //if already had a timer waiting on removing the subscriber,
         //cancel it first.
         var pString = presToRemove.toString();
         if (pString in this.awaitingTimeout)
             this.awaitingTimeout[pString].clear();

         this.awaitingTimeout[pString] = timer;
     };

     function elemFinalizeRemove(subElement,presToRemove)
     {
         if (presToRemove.toString() in subElement.subscribers)
             delete subElement.subscribers[presToRemove.toString()];

         finalizeRemove(
             presToRemove,subElement.vis,subElement.proxMan,subElement);
     }

     //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)
         {
             var nString = newPres.toString();
             sporefToPresences[nString] = newPres;
             pResultSet[nString]        = {};
             pAddCB[nString]            = [];
             pRemCB[nString]            = [];
         });

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

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

         return pResultSet[pString];
     };

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

         var pArray = pAddCB[pString];

	 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)
     {
         var pString = pres.toString();
         if (!(pString in pRemCB))
         {
             throw new Error('Cannot add prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pString];

	 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)
     {
         var pString = pres.toString();
         if (!(pString in pAddCB))
         {
             throw new Error('Cannot del prox add callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pAddCB[pString];

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

     ProxManager.prototype.delProxRemCB = function(pres,remID)
     {
         var pString = pres.toString();

         if (!(pString in pRemCB))
         {
             throw new Error('Cannot del prox rem callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         var pArray = pRemCB[pString];

         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)
     {
         var pString = pres.toString();
         var vString = visibleObj.toString();
         //ignore events where you have entered your own prox set.
         if (pString == vString)
             return;

         if (!(pString in pAddCB))
         {
             throw new Error('Cannot call add event callback because do ' +
                             'not have associated presence in ProxManager.');
         }

         if (vString in haveFeatureDataFor)
         {
             //we already have feature data for this visible.  add pres
             //as subscriber to haveFeatureDataFor map and trigger callback.
             var subElement = haveFeatureDataFor[vString];
             var issueCallback = subElement.addSubscriber(pres);
             if (issueCallback)
                 this.triggerAddCallback(pres,subElement.vis);
         }
         else
         {
             //short-circuits: does not send message to get feature data.
             var subElement =
                 new SubscriptionElement(visibleObj,pres,this,pres);

             haveFeatureDataFor[vString] = subElement;
             this.triggerAddCallback(pres,subElement.vis);
         }
     };

     /**
      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)
     // {
     //     //ignore events where you have entered your own prox set.
     //     if (pres.toString() == visibleObj.toString())
     //         return;

     //     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.
     //numTriesLeft should be left blank except when this function
     //calls itself.  (the idea is that it tries to get feature object
     //data from a presence multiple times and numTriesLeft is a
     //simple way for the function to keep track of how many times
     //we've attempted to get feature object data from other side.
     ProxManager.prototype.beginFeatureSubscribe =
         function(pres,visibleObj,numTriesLeft)
     {
         if (typeof(numTriesLeft) == 'undefined')
             numTriesLeft = NUM_TRIES_TO_GET_FEATURE_OBJECT;

         //If we try multiple times, and still don't get a response, the visible
         //probably is not running the feature object protocol.  We
         //can't do anything about that, and instead fire callback
         //with undefined passed through for featureObject.
         if (numTriesLeft <=0)
         {
             this.subscribeComplete(pres,visibleObj,undefined);
             return;
         }
         --numTriesLeft;

         //see handleFeautreObjectSubscribe (featureObject.em) to see
         //what format the feature object subscription request message
         //should take.
         pres # {'presFeatureSubRequest':true} >> visibleObj >>
             [std.core.bind(handleFeatureData,undefined,this,pres,visibleObj),
              TIME_TO_WAIT_FOR_FEATURE_OBJECT_RESP,
              std.core.bind(
                  this.beginFeatureSubscribe,
                  this,pres,visibleObj,numTriesLeft)];

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

     function handleFeatureData(proxMan,mPres,vis,featureMsg,visSender)
     {
         //see handleFeatureObjectSubscribe for structure of reply
         //mesages.
         proxMan.subscribeComplete(mPres,vis,featureMsg.data);
     }

     //Call this function when you want pres to unsubscribe for
     //feature data from visibleObj.
     ProxManager.prototype.killSubscription = function(pres,visibleObj)
     {
         //short-circuits.
         return;

         //see handleFeautreObjectRequestUnsubscribe message handler
         //in featureObject.em to see what the format of a
         //killSubscription message should look like.
         pres # {'presFeatureReqUnsub': true} >> visibleObj >> [];
     };

     //should get called when a presence has received feature data
     //from visibleObj.
     ProxManager.prototype.subscribeComplete =
         function(pres,visibleObj,featureData)
     {
         var vString = visibleObj.toString();

         visibleObj.featureObject= featureData;
         if (!(vString in awaitingFeatureData))
         {
             throw new Error('Error in subscribeComplete.  No longer '+
                             'have visibleObj in awaitingFeatureData.');
         }

         var subElement = awaitingFeatureData[vString];
         delete awaitingFeatureData[vString];

         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[vString] = 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)
     {
         var pString = pres.toString();
         pResultSet[pString][visibleObj.toString()] = visibleObj;
         var pArray = pAddCB[pString];
         //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);
         }
     };

     //called by system when pinto actually states that visibleObj is
     //distant from pres.  If the visible exists in awaitingFeatureData
     //then we just remove a subscriber from it.  If the visible exists in
     //haveFeatureDataFor, then fire removed callback, decrement
     //subscribers.  If no subscribers left in haveFeatureDataFor, then
     //unsubscribe from feature data, and remove element from
     //haveFeatureDataFor.
     ProxManager.prototype.proxRemovedEvent = function (pres,visibleObj)
     {
         var pString = pres.toString();
         var vString = visibleObj.toString();
         if (pString == vString)
             return;

         if (vString in awaitingFeatureData)
             awaitingFeatureData[vString].startRemoveSubscriber(pres);
         else
         {
             //this line ensures that visibleObj will maintain
             //featureData associated with stored vis.
             visibleObj = haveFeatureDataFor[vString].vis;
             var subElem = haveFeatureDataFor[vString];
             subElem.startRemoveSubscriber(pres);
         }
     };

     //actually call remove callback
     function finalizeRemove (pres,visibleObj,proxMan,subElem)
     {
         if (subElem.noSubscribers())
         {
             proxMan.killSubscription(pres,visibleObj);
             delete haveFeatureDataFor[visibleObj.toString()];
         }

         //actually fire removed callback.
         proxMan.triggerRemoveCallback(pres,visibleObj);
     };

     ProxManager.prototype.triggerRemoveCallback = function(pres,visibleObj)
     {
         var pString = pres.toString();

         if (!(pString in pRemCB))
         {
             throw new Error('Cannot call rem event callback because do ' +
                             'not have associated presence in ProxManager.');
         }
         if (!(pString in pResultSet))
         {
             throw new Error('Cannot call rem event callback for presence ' +
                             'because no presence in pResultSet matches.');
         }

         //remove from to proxResultSet
         delete pResultSet[pString][visibleObj.toString()];

         //actually issue callbacks
         var pArray = pRemCB[pString];
         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);
         }

     };

     //This function handles all the feature object updates.  For now,
     //we have a very dumb protocol.  We transmit the full feature
     //object instead of the parts that have changed/been updated.
     function handleFeatureObjUpdates(updateMsg,sender)
     {
         var update = updateMsg.featureObjUpdate;
         var sString = sender.toString();

         if (!(sString in haveFeatureDataFor))
         {
             throw new Error('Error upon reception of feature object ' +
                             'update.  Had no subscription to sender ' +
                             'of message.');
         }

         var localVis = haveFeatureDataFor[sString].vis;
         localVis.featureObject = update;

         //sends ack to other side so update sender knows we're still
         //alive.
         updateMsg.makeReply({}) >> [];
     }
     //see sendFeaturesToSubscribers function in featureObject.em for
     //format of featureObject update messages.
     handleFeatureObjUpdates << {'featureObjUpdate'::};

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


Soccer game now works.

Soccer game now works.

//have a ball and soccer players that are playing in a circular area.
//If the ball ever leaves the circular area, the captain runs to get it.
//None of the players ever leave the circular area.  Instead, they run around it.
//If any of the players are near a ball, they reserve the right to kick it.  Then, they run towards it, and the ball zooms off in  the direction that the player kicked it.

function presAdded()
{
    system.print("\n\npres added\n\n");
}

function degreesToRadians(degree)
{
    return 3.14159*degree/180.0;
}

function getMaxAngle(playerPos,centerPos,dist)
{
    return getAngle(playerPos,centerPos,dist) + degreesToRadians(15.0);
}

function getBaseAngle(playerPos,centerPos,dist)
{
    return getAngle(playerPos,centerPos,dist) + degreesToRadians(-15.0);
}

function getAngle(from, to,dist)
{
    return util.asin( (to.y - from.y) /dist);
}

//takes in a presence, angle that you want the player to be traveling to relative to the
function setVelocity(player,angle,speed)
{
    var xSpeed = util.cos(angle)*speed;
    var ySpeed = util.sin(angle)*speed;
    var zSpeed = 0;
    var newVeloc = new util.Vec3(xSpeed,ySpeed,zSpeed);
    player.setVelocity(newVeloc);
}

function setVelocityToCenter(ball,center,speed)
{
    var ballPos = ball.getPosition();
    var unNormalizedVeloc = new util.Vec3(center.x - ballPos.x, center.y - ballPos.y, center.z - ballPos.z);
    var normalizedVeloc = unNormalizedVeloc.normal();
    system.print("\n\nunnormal: " + unNormalizedVeloc + "\n\n");
    system.print("\nnormal: " + normalizedVeloc + "\n\n");

    normalizedVeloc.x = normalizedVeloc.x*speed;
    normalizedVeloc.y = normalizedVeloc.y*speed;
    normalizedVeloc.z = normalizedVeloc.z*speed;
    ball.setVelocity(normalizedVeloc);
}

function createTeamObject(ball)
{
    var team = util.create_watched();
    team.count = 0;
    team.maxPlayers = 11;
    team.kickLock = false;
    team.playerRadius = 3;

    team.playerMesh = "meerkat:///danielrh/Poisonbird.dae";
    team.ball = ball;
    team.center = ball.getPosition();
    team.radius = 15;
    team.playerSpeed = .5;
    team.ballSpeed = 1;
    team.allPlayers = new Array();
    team.captain = null;
    team.addPlayer = function(newPlayer)
    {
        team.allPlayers.push(newPlayer);
        newPlayer.whichPlayer = team.count;
        if (team.count == 0)
        {
            team.captain = newPlayer;
        }
        team.count = team.count + 1;
    };

    team.playerWanderPeriod = 4;
    team.playerWander = function (playerToWander)
    {
        //if moving out of center of circle
        var angle = 0;
        var baseAngle = 0;
        var maxAngle = degreesToRadians(360);
        var dist = playerToWander.distance(team.center);
        if (dist > team.radius)
        {
            setVelocityToCenter(playerToWander,team.center,team.playerSpeed);
        }
        else
        {
            angle = baseAngle + (maxAngle - baseAngle)*util.rand();
            setVelocity(playerToWander,angle,team.playerSpeed);
        }

        system.timeout(team.playerWanderPeriod,null,
                       function ()
                       {
                         team.playerWander(playerToWander);
                       });
    };

    team.kickBall = function(playerToKick)
    {
        team.kickLock = true;
        playerToKick.setPosition(team.ball.getPosition());
        var angle = util.rand()*degreesToRadians(360);
        setVelocity(team.ball,angle,team.ballSpeed);
        team.kickLock = false;
    };

    team.fetchBall = function(playerToFetch)
    {
        team.kickLock = true;
        playerToFetch.setPosition(team.ball.getPosition());
        setVelocityToCenter(team.ball,team.center,team.ballSpeed);
        team.kickLock = false;
    };

    return team;
}

mTeam = createTeamObject(system.presences[0]);
function waitForJoin(presJoined)
{
    var predicate = function()
    {
        return presJoined.isConnected;
    };

    var callbacker = function()
    {
        mTeam.addPlayer(presJoined);
    };

    util.create_when(
        predicate,
        callbacker,
        5,
        presJoined);
}

teamArray = new Array();
for (var s=0; s < mTeam.maxPlayers; ++s)
{
    teamArray.push(system.create_presence(mTeam.playerMesh,presAdded));
    waitForJoin(teamArray[s]);
}

var predTeamBegin = function()
{
    return (mTeam.count == mTeam.maxPlayers);
};
var cbTeamBegin = function()
{
    system.print("\n\ngot into cbTeamBegin\n\n");
    playSoccer(mTeam);
};

util.create_when(
    predTeamBegin,
    cbTeamBegin,
    5,
    mTeam);

function ballNearKick(player,team)
{
    //when ball is near a player, player locks it and tries to kick it.
    var ballNearPred = function()
    {
        if ((team.ball.distance(player.getPosition())  < team.playerRadius) &&
            (! team.kickLock) &&
            (team.ball.distance(team.center) < team.radius))
        {

            return true;
        }

        return false;
    };

    var ballNearCB = function()
    {
        team.kickBall(player);
    };

    util.create_when(
        ballNearPred,
        ballNearCB,
        .5,
        player);
}

function playSoccer(team)
{
    //get players to wander around
    for (var s=0; s < team.allPlayers.length; ++s)
    {
        var whichPlayer = team.allPlayers[s].whichPlayer;
        system.print("\n\nBeginning wander for player" + whichPlayer+ "\n\n");
        team.playerWander(team.allPlayers[s]);
    }

    //when ball is near player, player kicks it.
    for (var t=0; t < team.allPlayers.length; ++t)
    {
        ballNearKick(team.allPlayers[t],team);
    }

    //when ball goes out of play, captain kicks it back
    var ballGonePred = function()
    {
        if (team.ball.distance(team.center) > team.radius)
        {
            return true;
        }

        return false;
    };

    var ballGoneCB = function()
    {
        team.fetchBall(team.captain);
    };

    util.create_when(
        ballGonePred,
        ballGoneCB,
        5,
        team.ball);

}

function playSoccer2(team)
{
    //when ball goes out of play, captain kicks it back
    // var ballGonePred = function()
    // {
    //     if (team.ball.distance(team.center) > team.radius)
    //     {
    //         return true;
    //     }

    //     return false;
    // };

    // var ballGoneCB = function()
    // {
    //     team.fetchBall(team.captain);
    // };

    // util.create_when(
    //     ballGonePred,
    //     ballGoneCB,
    //     5,
    //     team.ball);

    for (var s=0; s < team.allPlayers.length; ++s)
    {
//         //when ball is near a player, player locks it and tries to kick it.
//         var ballNearPred = function()
//         {
//             if ((team.ball.distance(team.allPlayers[s].getPosition())  < team.playerRadius) &&
//                 (! team.kickLock) &&
//                 (team.ball.distance(team.center) < team.radius))
//             {
//                 return true;
//             }

//             return false;
//         };

//         var ballNearCB = function()
//         {
//             team.kickBall(team.allPlayers[s]);
//         };

//         util.create_when(
//             ballNearPred,
//             ballNearCB,
//             .5,
//             team.allPlayers[s]
//         );

        //player wander
        team.playerWander(team.allPlayers[s]);
    }
}

//have a ball and soccer players that are playing in a circular area.
//If the ball ever leaves the circular area, the captain runs to get it.
//None of the players ever leave the circular area.  Instead, they run around it.
//If any of the players are near a ball, they reserve the right to kick it.  Then, they run towards it, and the ball zooms off in  the direction that the player kicked it.

function presAdded()
{
    system.print("\n\npres added\n\n");
}

function degreesToRadians(degree)
{
    return 3.14159*degree/180.0;
}

function getMaxAngle(playerPos,centerPos,dist)
{
    return getAngle(playerPos,centerPos,dist) + degreesToRadians(15.0);
}

function getBaseAngle(playerPos,centerPos,dist)
{
    return getAngle(playerPos,centerPos,dist) + degreesToRadians(-15.0);
}

function getAngle(from, to,dist)
{
    return util.asin( (to.y - from.y) /dist);
}

//takes in a presence, angle that you want the player to be traveling to relative to the
function setVelocity(player,angle,speed)
{
    var xSpeed = util.cos(angle)*speed;
    var ySpeed = util.sin(angle)*speed;
    var zSpeed = 0;
    var newVeloc = new util.Vec3(xSpeed,ySpeed,zSpeed);
    player.setVelocity(newVeloc);
}

function createTeamObject(ball)
{
    var team = util.create_watched();
    team.count = 0;
    team.maxPlayers = 11;
    team.kickLock = false;

    team.playerMesh = "meerkat:///danielrh/Poisonbird.dae";
    team.ball = ball;
    team.center = ball.getPosition();
    team.radius = 30;
    team.playerSpeed = .5;
    team.ballSpeed = 1;
    team.allPlayers = new Array();
    team.captain = null;
    team.addPlayer = function(newPlayer)
    {
        team.allPlayers.push(newPlayer);
        if (team.count == 0)
        {
            team.captain = newPlayer;
        }
        team.count = team.count + 1;
    };

    team.playerWanderPeriod = 4;
    team.playerWander = function (playerToWander)
    {
        //if moving out of center of circle
        var angle = 0;
        var baseAngle = 0;
        var maxAngle = degreesToRadians(360);
        var dist = playerToWander.distance(team.center);
        if (dist > team.radius)
        {
            baseAngle = getBaseAngle(playerToWander.getPosition(),team.center,dist);
            maxAngle  = getMaxAngle(playerToWander.getPosition(),team.center,dist);
        }

        angle = baseAngle + (maxAngle - baseAngle)*util.rand();
        setVelocity(playerToWander,angle,team.playerSpeed);
        system.timeout(team.playerWanderPeriod,null,team.playerWander);
    };

    team.kickBall = function(playerToKick)
    {
        team.kickLock = true;
        playerToKick.setPosition(team.ball.getPosition());
        var angle = util.rand()*degreesToRadians(360);
        setVelocity(team.ball,angle,team.ballSpeed);
        team.kickLock = false;
    };

    team.fetchBall = function(playerToFetch)
    {
        team.kickLock = true;
        playerToFetch.setPosition(team.ball.getPosition());
        var dist = team.ball.distance(team.center);
        var angle = getAngle(team.ball.getPosition(),team.center,dist);
        setVelocity(team.ball,angle,team.ballSpeed);
    };

    return team;
}

mTeam = createTeamObject(system.presences[0]);

for (var s=0; s < mTeam.maxPlayers; ++s)
{
    var newPres = system.create_presence(mTeam.playerMesh,presAdded);

    var predicate = function()
    {
        return newPres.isConnected;
    };

    var callbacker = function()
    {
        mTeam.addPlayer(newPres);
    };

    var whener = util.create_when(
        predicate,
        callbacker,
        5,
        newPres);
}

var predTeamBegin = function()
{
    return (mTeam.count == mTeam.maxPlayers);
};
var cbTeamBegin = function()
{
    system.print("\n\ngot into cbTeamBegin\n\n");
    playSoccer(mTeam);
};

util.create_when(
    predTeamBegin,
    cbTeamBegin,
    5,
    mTeam);

function playSoccer(team)
{
    system.print("\n\ngot into the playsoccer function\n\n");

    for (var s=0; s < team.allPlayers.length; ++s)
    {
        //system.print("\n\nprinting\n\n");
        team.playerWander(team.allPlayers[s]);
    }
}

function playSoccer2(team)
{
    //when ball goes out of play, captain kicks it back
    // var ballGonePred = function()
    // {
    //     if (team.ball.distance(team.center) > team.radius)
    //     {
    //         return true;
    //     }

    //     return false;
    // };

    // var ballGoneCB = function()
    // {
    //     team.fetchBall(team.captain);
    // };

    // util.create_when(
    //     ballGonePred,
    //     ballGoneCB,
    //     5,
    //     team.ball);

    for (var s=0; s < team.allPlayers.length; ++s)
    {
//         //when ball is near a player, player locks it and tries to kick it.
//         var ballNearPred = function()
//         {
//             if ((team.ball.distance(team.allPlayers[s].getPosition())  < team.playerRadius) &&
//                 (! team.kickLock) &&
//                 (team.ball.distance(team.center) < team.radius))
//             {
//                 return true;
//             }

//             return false;
//         };

//         var ballNearCB = function()
//         {
//             team.kickBall(team.allPlayers[s]);
//         };

//         util.create_when(
//             ballNearPred,
//             ballNearCB,
//             .5,
//             team.allPlayers[s]
//         );

        //player wander
        team.playerWander(team.allPlayers[s]);
    }
}


If no unstable_threshold is defined, avoid quality gates

<io.jenkins.plugins.analysis.core.steps.IssuesRecorder plugin="warnings-ng@@7.3.0">
  <analysisTools>
    <io.jenkins.plugins.analysis.warnings.Cmake>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Cmake>
    <io.jenkins.plugins.analysis.warnings.Gcc4>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Gcc4>
  </analysisTools>
  <sourceCodeEncoding></sourceCodeEncoding>
  <sourceDirectory></sourceDirectory>
  <ignoreQualityGate>false</ignoreQualityGate>
  <ignoreFailedBuilds>true</ignoreFailedBuilds>
  <referenceJobName>-</referenceJobName>
  <failOnError>false</failOnError>
  <healthy>0</healthy>
  <unhealthy>0</unhealthy>
  <minimumSeverity plugin="analysis-model-api@@7.0.2">
    <name>LOW</name>
  </minimumSeverity>
  <filters/>
  <isEnabledForFailure>false</isEnabledForFailure>
  <isAggregatingResults>false</isAggregatingResults>
  <isBlameDisabled>false</isBlameDisabled>
  <isForensicsDisabled>false</isForensicsDisabled>
@[if unstable_threshold != '']@
  <qualityGates>
    <io.jenkins.plugins.analysis.core.util.QualityGate>
      <threshold>@unstable_threshold</threshold>
      <type>TOTAL</type>
      <status>WARNING</status>
    </io.jenkins.plugins.analysis.core.util.QualityGate>
  </qualityGates>
@[else]@
  <qualityGates/>
@[end if]@
  <trendChartType>AGGREGATION_TOOLS</trendChartType>
</io.jenkins.plugins.analysis.core.steps.IssuesRecorder>

<io.jenkins.plugins.analysis.core.steps.IssuesRecorder plugin="warnings-ng@@7.3.0">
  <analysisTools>
    <io.jenkins.plugins.analysis.warnings.Cmake>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Cmake>
    <io.jenkins.plugins.analysis.warnings.Gcc4>
      <id></id>
      <name></name>
      <pattern></pattern>
      <reportEncoding></reportEncoding>
      <skipSymbolicLinks>false</skipSymbolicLinks>
    </io.jenkins.plugins.analysis.warnings.Gcc4>
  </analysisTools>
  <sourceCodeEncoding></sourceCodeEncoding>
  <sourceDirectory></sourceDirectory>
  <ignoreQualityGate>false</ignoreQualityGate>
  <ignoreFailedBuilds>true</ignoreFailedBuilds>
  <referenceJobName>-</referenceJobName>
  <failOnError>false</failOnError>
  <healthy>0</healthy>
  <unhealthy>0</unhealthy>
  <minimumSeverity plugin="analysis-model-api@@7.0.2">
    <name>LOW</name>
  </minimumSeverity>
  <filters/>
  <isEnabledForFailure>false</isEnabledForFailure>
  <isAggregatingResults>false</isAggregatingResults>
  <isBlameDisabled>false</isBlameDisabled>
  <isForensicsDisabled>false</isForensicsDisabled>
  <qualityGates>
    <io.jenkins.plugins.analysis.core.util.QualityGate>
@[if unstable_threshold != '']@
      <threshold>@unstable_threshold</threshold>
@[else]@
      <threshold/>
@[end if]@
      <type>TOTAL</type>
      <status>WARNING</status>
    </io.jenkins.plugins.analysis.core.util.QualityGate>
  </qualityGates>
  <trendChartType>AGGREGATION_TOOLS</trendChartType>
</io.jenkins.plugins.analysis.core.steps.IssuesRecorder>


Add a bunch of built-in properties on presence which just call the corresponding getter/setter methods. Also return many of these as fields to pretty print so that we get useful information when printing a presence.

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

/* Add functions to the prototype for presence */

system.__presence_constructor__.prototype.runSimulation =
function(name)
{
  system.print("\n\nIn side the runSimulation\n\n");
  var pres = this;
  if(!system.shim.graphics)
  {
	  system.print("\n\n Got the new graphics object\n\n");
    system.shim.graphics = new Object();
  }
  if(!system.shim.graphics[pres])
  {
    system.shim.graphics[pres] = new Object();
    system.print("\n\nBefore _runSimulation\n\n");
    system.shim.graphics[pres][name] = pres._runSimulation(name);
  }

  var return_val = system.shim.graphics[pres][name];

	return return_val;
};

system.__presence_constructor__.prototype.getSimulation =
function(name)
{

  if(!system.shim.graphics || !system.shim.graphics[this]) return undefined;
  return system.shim.graphics[this][name];
};

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

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

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

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

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

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

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

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

/* Add functions to the prototype for presence */

system.__presence_constructor__.prototype.runSimulation =
function(name)
{
  system.print("\n\nIn side the runSimulation\n\n");
  var pres = this;
  if(!system.shim.graphics)
  {
	  system.print("\n\n Got the new graphics object\n\n");
    system.shim.graphics = new Object();
  }
  if(!system.shim.graphics[pres])
  {
    system.shim.graphics[pres] = new Object();
    system.print("\n\nBefore _runSimulation\n\n");
    system.shim.graphics[pres][name] = pres._runSimulation(name);
  }

  var return_val = system.shim.graphics[pres][name];

	return return_val;
}

system.__presence_constructor__.prototype.getSimulation =
function(name)
{

  if(!system.shim.graphics || !system.shim.graphics[this]) return undefined;
  return system.shim.graphics[this][name];
}


pin sphinx version due to issue with latest release 2.0.0

# generated from @template_name

FROM ubuntu:xenial

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

ENV DEBIAN_FRONTEND noninteractive

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

RUN useradd -u @uid -m buildfarm

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

@(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='ubuntu',
    os_code_name='xenial',
))@

RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y make python-catkin-pkg-modules python-dateutil python-pip python-wstool python-yaml
# Pin sphinx at 1.8.5 re: https://github.com/ros-infrastructure/ros_buildfarm/issues/614
RUN pip install -U catkin-sphinx sphinx==1.8.5

USER buildfarm

ENTRYPOINT ["sh", "-c"]
@{
cmd = '/tmp/ros_buildfarm/scripts/doc/invoke_doc_targets.sh' + \
    ' /tmp/repositories' + \
    ' /tmp/generated_documentation/independent/api'
}@
CMD ["@cmd"]

# generated from @template_name

FROM ubuntu:xenial

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

ENV DEBIAN_FRONTEND noninteractive

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

RUN useradd -u @uid -m buildfarm

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

@(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='ubuntu',
    os_code_name='xenial',
))@

RUN python3 -u /tmp/wrapper_scripts/apt.py update-install-clean -q -y make python-catkin-pkg-modules python-dateutil python-pip python-wstool python-yaml
RUN pip install -U catkin-sphinx sphinx

USER buildfarm

ENTRYPOINT ["sh", "-c"]
@{
cmd = '/tmp/ros_buildfarm/scripts/doc/invoke_doc_targets.sh' + \
    ' /tmp/repositories' + \
    ' /tmp/generated_documentation/independent/api'
}@
CMD ["@cmd"]