text:
import os.path

from ginga.gtk3w import GtkHelp
import ginga.icons

from ginga.misc import Callback, Bunch, Settings, LineHistory
from functools import reduce

from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from gi.repository import GdkPixbuf

import gi
has_webkit = False

try:
    # this is necessary to prevent a warning message on import
    gi.require_version('WebKit2', '4.0')

    from gi.repository import WebKit2 as WebKit  # noqa
    has_webkit = True
except Exception:
    try:
        gi.require_version('WebKit', '3.0')
        from gi.repository import WebKit  # noqa
    except Exception:
        pass

__all__ = ['WidgetError', 'WidgetBase', 'TextEntry', 'TextEntrySet',
           'TextArea', 'Label', 'Button', 'ComboBox',
           'SpinBox', 'Slider', 'Dial', 'ScrollBar', 'CheckBox', 'ToggleButton',
           'RadioButton', 'Image', 'ProgressBar', 'StatusBar', 'TreeView',
           'WebView', 'ContainerBase', 'Box', 'HBox', 'VBox', 'Frame',
           'Expander', 'TabWidget', 'StackWidget', 'MDIWidget', 'ScrollArea',
           'Splitter', 'GridBox', 'Toolbar', 'MenuAction',
           'Menu', 'Menubar', 'TopLevelMixin', 'TopLevel', 'Application',
           'Dialog', 'SaveDialog', 'DragPackage', 'WidgetMoveEvent',
           'name_mangle', 'make_widget', 'hadjust', 'build_info', 'wrap',
           'has_webkit']

# path to our icons
icondir = os.path.split(ginga.icons.__file__)[0]

class WidgetError(Exception):
    """For errors thrown in this module."""
    pass

# (see TabWidget)
_widget_move_event = None
_app = None

# BASE

class WidgetBase(Callback.Callbacks):

    def __init__(self):
        super(WidgetBase, self).__init__()

        self.widget = None
        # external data can be attached here
        self.extdata = Bunch.Bunch()

    def get_widget(self):
        return self.widget

    def set_tooltip(self, text):
        self.widget.set_tooltip_text(text)

    def get_enabled(self):
        self.widget.get_sensitive()

    def set_enabled(self, tf):
        self.widget.set_sensitive(tf)

    def get_size(self):
        try:
            rect = self.widget.get_allocation()
            # x, y = rect.x, rect.y
            wd, ht = rect.width, rect.height

        except Exception as e:
            # window maybe isn't realized yet--try other ways
            min_req, nat_req = self.widget.get_preferred_size()
            wd, ht = nat_req.width, nat_req.height
            # req = self.widget.get_size_request()
            # wd, ht = req

            # wd, ht = max(1, wd), max(1, ht)
        return wd, ht

    def get_pos(self):
        rect = self.widget.get_allocation()
        x, y = rect.x, rect.y
        return x, y

    def get_app(self):
        return _app

    def delete(self):
        self.widget.destroy()
        self.widget = None

    def show(self):
        # self.widget.show()
        self.widget.show_all()

    def hide(self):
        self.widget.hide()

    def is_visible(self):
        return self.widget.get_visible()

    def focus(self):
        self.widget.grab_focus()

    def resize(self, width, height):
        self.widget.set_size_request(width, height)

        # hackish way to allow the widget to be resized down again later
        # NOTE: this may cause some problems for sizing certain widgets
        if width > 0 and height > 0:
            GObject.idle_add(self.widget.set_size_request, -1, -1)

    def get_font(self, font_family, point_size):
        font = GtkHelp.get_font(font_family, point_size)
        return font

    def cfg_expand(self, horizontal='fixed', vertical='fixed'):
        # this is for compatibility with Qt widgets
        pass

# BASIC WIDGETS

class TextEntry(WidgetBase):
    def __init__(self, text='', editable=True):
        super(TextEntry, self).__init__()

        w = Gtk.Entry()
        w.set_text(text)
        w.set_editable(editable)
        w.connect('key-press-event', self._key_press_event)
        w.connect('activate', self._cb_redirect)
        self.widget = w

        self.history = LineHistory.LineHistory()

        self.enable_callback('activated')

    def _cb_redirect(self, *args):
        self.history.append(self.get_text())
        self.make_callback('activated')

    def _key_press_event(self, widget, event):
        keyname = Gdk.keyval_name(event.keyval)
        if keyname == 'Up':
            try:
                text = self.history.prev()
                self.set_text(text)
                self.widget.set_position(len(text))
            except ValueError:
                pass
            return True
        elif keyname == 'Down':
            try:
                text = self.history.next()
                self.set_text(text)
                self.widget.set_position(len(text))
            except ValueError:
                pass
            return True
        return False

    def get_text(self):
        return self.widget.get_text()

    def set_text(self, text):
        self.widget.set_text(text)

    def set_editable(self, tf):
        self.widget.set_editable(tf)

    def set_font(self, font, size=10):
        if isinstance(font, str):
            font = self.get_font(font, size)
        self.widget.modify_font(font)

    def set_length(self, numchars):
        # this only sets the visible length of the widget
        self.widget.set_width_chars(numchars)
        pass

class TextEntrySet(WidgetBase):
    def __init__(self, text='', editable=True):
        super(TextEntrySet, self).__init__()

        hbox = Gtk.HBox()
        hbox.set_spacing(4)
        w = Gtk.Entry()
        w.set_text(text)
        w.set_editable(editable)
        hbox.pack_start(w, True, True, 0)
        w.connect('activate', self._cb_redirect)
        self.entry = w
        w = Gtk.Button('Set')
        w.connect('clicked', self._cb_redirect)
        hbox.pack_start(w, False, False, 0)
        self.btn = w
        self.widget = hbox

        self.enable_callback('activated')

    def _cb_redirect(self, *args):
        self.make_callback('activated')

    def get_text(self):
        return self.entry.get_text()

    def set_text(self, text):
        self.entry.set_text(text)

    def set_editable(self, tf):
        self.entry.set_editable(tf)

    def set_font(self, font, size=10):
        if isinstance(font, str):
            font = self.get_font(font, size)
        self.widget.modify_font(font)

    def set_length(self, numchars):
        # self.widget.set_width_chars(numchars)
        pass

    def set_enabled(self, tf):
        super(TextEntrySet, self).set_enabled(tf)
        self.entry.set_sensitive(tf)

class TextArea(WidgetBase):
    def __init__(self, wrap=False, editable=False):
        super(TextArea, self).__init__()

        tw = Gtk.TextView()
        if wrap:
            tw.set_wrap_mode(Gtk.WrapMode.WORD)
        else:
            tw.set_wrap_mode(Gtk.WrapMode.NONE)
        tw.set_editable(editable)
        self.tw = tw

        # this widget has a built in ScrollArea to match Qt functionality
        sw = Gtk.ScrolledWindow()
        sw.set_border_width(2)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        sw.add(self.tw)
        self.widget = sw

        self.histlimit = 0

    def append_text(self, text, autoscroll=True):
        buf = self.tw.get_buffer()
        end = buf.get_end_iter()
        buf.insert(end, text)

        if self.histlimit > 0:
            self._history_housekeeping()
        if not autoscroll:
            return

        end = buf.get_end_iter()
        mark = buf.get_insert()
        # self.tw.scroll_to_iter(end, 0.5)
        # NOTE: this was causing a segfault if the text widget is
        # not mapped yet!  Seems to be fixed in recent versions of
        # gtk
        buf.move_mark(mark, end)
        res = self.tw.scroll_to_mark(mark, 0.2, False, 0.0, 0.0)  # noqa

    def get_text(self):
        buf = self.tw.get_buffer()
        return buf.get_text()

    def _history_housekeeping(self):
        # remove some lines to keep us within our history limit
        buf = self.tw.get_buffer()
        numlines = buf.get_line_count()
        if numlines > self.histlimit:
            rmcount = int(numlines - self.histlimit)
            start = buf.get_iter_at_line(0)
            end = buf.get_iter_at_line(rmcount)
            buf.delete(start, end)

    def clear(self):
        buf = self.tw.get_buffer()
        start = buf.get_start_iter()
        end = buf.get_end_iter()
        buf.delete(start, end)

    def set_text(self, text):
        self.clear()
        self.append_text(text)

    def set_limit(self, numlines):
        self.histlimit = numlines
        self._history_housekeeping()

    def set_editable(self, tf):
        self.tw.set_editable(tf)

    def set_font(self, font, size=10):
        if isinstance(font, str):
            font = self.get_font(font, size)
        self.tw.modify_font(font)

    def set_wrap(self, tf):
        if tf:
            self.tw.set_wrap_mode(Gtk.WrapMode.WORD)
        else:
            self.tw.set_wrap_mode(Gtk.WrapMode.NONE)

class Label(WidgetBase):
    def __init__(self, text='', halign='left', style='normal', menu=None):
        super(Label, self).__init__()

        label = Gtk.Label(text)
        evbox = Gtk.EventBox()
        evbox.set_border_width(0)
        evbox.props.visible_window = False
        evbox.add(label)

        if halign == 'left':
            label.set_justify(Gtk.Justification.LEFT)
        elif halign == 'center':
            label.set_justify(Gtk.Justification.CENTER)
        elif halign == 'right':
            label.set_justify(Gtk.Justification.RIGHT)

        evbox.connect("button_press_event", self._cb_redirect)
        self.enable_callback('activated')
        evbox.connect("button_release_event", self._cb_redirect2)
        self.enable_callback('released')

        self.label = label
        self.menu = menu
        self.evbox = evbox
        self.widget = evbox
        if style == 'clickable':
            fr = Gtk.Frame()
            fr.set_shadow_type(Gtk.ShadowType.OUT)
            evbox.props.visible_window = True
            fr.add(evbox)
            self.frame = fr
            self.widget = fr

    def _cb_redirect(self, widget, event):
        # event.button, event.x, event.y
        if event.button == 1:
            self.make_callback('activated')
            return True

        elif event.button == 3 and self.menu is not None:
            menu_w = self.menu.get_widget()
            if menu_w.get_sensitive():
                return menu_w.popup(None, None, None, None,
                                    event.button, event.time)
        return False

    def _cb_redirect2(self, widget, event):
        if event.button == 1:
            self.make_callback('released')
            return True

        return False

    def get_text(self):
        return self.label.get_text()

    def set_text(self, text):
        self.label.set_text(text)

    def set_font(self, font, size=10):
        if isinstance(font, str):
            font = self.get_font(font, size)
        self.label.modify_font(font)

    def set_color(self, fg=None, bg=None):
        if bg is not None:
            GtkHelp.modify_bg(self.evbox, bg)
        if fg is not None:
            self.label.modify_fg(Gtk.StateType.NORMAL, Gdk.color_parse(fg))

class Button(WidgetBase):
    def __init__(self, text=''):
        super(Button, self).__init__()

        w = Gtk.Button(text)
        self.widget = w
        w.connect('clicked', self._cb_redirect)

        self.enable_callback('activated')

    def set_text(self, text):
        self.widget.set_label(text)

    def get_text(self):
        return self.widget.get_label()

    def _cb_redirect(self, *args):
        self.make_callback('activated')

class ComboBox(WidgetBase):
    def __init__(self, editable=False):
        super(ComboBox, self).__init__()

        cb = GtkHelp.ComboBox(has_entry=editable)
        liststore = Gtk.ListStore(GObject.TYPE_STRING)
        cb.set_model(liststore)
        cell = Gtk.CellRendererText()
        cb.pack_start(cell, True)
        cb.add_attribute(cell, 'text', 0)
        if editable:
            cb.set_entry_text_column(0)
        self.widget = cb
        self.widget.sconnect('changed', self._cb_redirect)

        self.enable_callback('activated')

    def _cb_redirect(self, widget):
        idx = widget.get_active()
        self.make_callback('activated', idx)

    def insert_alpha(self, text):
        model = self.widget.get_model()
        tup = (text, )
        j = 0
        for i in range(len(model)):
            j = i
            if model[i][0] > text:
                model.insert(j, tup)
                return
        model.insert(j + 1, tup)

    def append_text(self, text):
        model = self.widget.get_model()
        tup = (text, )
        idx = len(model)
        model.insert(idx, tup)

    def insert_text(self, idx, text):
        model = self.widget.get_model()
        tup = (text, )
        model.insert(idx, tup)

    def delete_alpha(self, text):
        model = self.widget.get_model()
        for i in range(len(model)):
            if model[i][0] == text:
                del model[i]
                return

    def get_alpha(self, idx):
        model = self.widget.get_model()
        text = model[idx][0]
        return text

    def clear(self):
        model = self.widget.get_model()
        model.clear()
        if self.widget.get_has_entry():
            entry = self.widget.get_entry()
            entry.set_text('')

    def set_text(self, text):
        model = self.widget.get_model()
        for i in range(len(model)):
            if model[i][0] == text:
                self.widget.set_active(i)
                return

        if self.widget.get_has_entry():
            entry = self.widget.get_child()
            entry.set_text(text)

    # to be deprecated someday
    show_text = set_text

    def set_index(self, index):
        self.widget.set_active(index)

    def get_index(self):
        return self.widget.get_active()

    def get_text(self):
        if self.widget.get_has_entry():
            entry = self.widget.get_child()
            return entry.get_text()

        idx = self.get_index()
        return self.get_alpha(idx)

class SpinBox(WidgetBase):
    def __init__(self, dtype=int):
        super(SpinBox, self).__init__()

        self.dtype = dtype
        self.widget = GtkHelp.SpinButton()
        self.widget.sconnect('value-changed', self._cb_redirect)

        self.enable_callback('value-changed')

    def _cb_redirect(self, w):
        val = self.dtype(w.get_value())
        self.make_callback('value-changed', val)

    def get_value(self):
        return self.dtype(self.widget.get_value())

    def set_value(self, val):
        self.widget.set_value(val)

    def set_decimals(self, num):
        self.widget.set_digits(num)

    def set_limits(self, minval, maxval, incr_value=1):
        adj = self.widget.get_adjustment()
        adj.configure(minval, minval, maxval, incr_value, incr_value, 0)

class Slider(WidgetBase):
    def __init__(self, orientation='horizontal', dtype=int, track=False):
        super(Slider, self).__init__()

        # NOTE: parameter dtype is ignored for now for gtk3

        if orientation == 'horizontal':
            w = GtkHelp.HScale()
            # TEMP: hack because scales don't seem to expand as expected
            w.set_size_request(200, -1)
        else:
            w = GtkHelp.VScale()
            w.set_size_request(-1, 200)
        self.widget = w

        w.set_draw_value(True)
        w.set_value_pos(Gtk.PositionType.BOTTOM)
        self.set_tracking(track)
        w.sconnect('value-changed', self._cb_redirect)

        self.enable_callback('value-changed')

    def _cb_redirect(self, range):
        val = range.get_value()
        self.make_callback('value-changed', val)

    def get_value(self):
        return self.widget.get_value()

    def set_value(self, val):
        self.widget.set_value(val)

    def set_tracking(self, tf):
        if tf:
            # self.widget.set_update_policy(Gtk.UPDATE_CONTINUOUS)
            pass
        else:
            # self.widget.set_update_policy(Gtk.UPDATE_DISCONTINUOUS)
            pass

    def set_limits(self, minval, maxval, incr_value=1):
        adj = self.widget.get_adjustment()
        adj.configure(minval, minval, maxval, incr_value, incr_value, 0)

class Dial(WidgetBase):
    def __init__(self, dtype=float, wrap=False, track=False):
        super(Dial, self).__init__()

        w = GtkHelp.ValueDial()
        self.widget = w

        w.draw_value = False
        w.wrap = wrap
        w.set_tracking(track)
        w.connect('value-changed', self._cb_redirect)
        self.dtype = dtype

        self.enable_callback('value-changed')

    def _cb_redirect(self, dial, val):
        ext_val = self.dtype(val)
        self.make_callback('value-changed', ext_val)

    def get_value(self):
        int_val = self.widget.get_value()
        return self.dtype(int_val)

    def set_value(self, val):
        self.widget.set_value(val)

    def set_tracking(self, tf):
        self.widget.set_tracking(tf)

    def set_limits(self, minval, maxval, incr_value=1):
        self.widget.set_limits(minval, maxval, incr_value)

class ScrollBar(WidgetBase):
    def __init__(self, orientation='horizontal'):
        super(ScrollBar, self).__init__()

        if orientation == 'horizontal':
            self.widget = Gtk.HScrollbar()
        else:
            self.widget = Gtk.VScrollbar()
        self.widget.set_range(0.0, 100.0)
        self.widget.connect('value-changed', self._cb_redirect)

        self.enable_callback('activated')

    def set_value(self, value):
        flt_val = value * 100.0
        self.widget.set_value(flt_val)

    def get_value(self):
        return self.widget.get_value() / 100.0

    def _cb_redirect(self, range):
        val = range.get_value() / 100.0
        self.make_callback('activated', val)

class CheckBox(WidgetBase):
    def __init__(self, text=''):
        super(CheckBox, self).__init__()

        self.widget = GtkHelp.CheckButton(text)
        self.widget.sconnect('toggled', self._cb_redirect)

        self.enable_callback('activated')

    def _cb_redirect(self, widget):
        val = widget.get_active()
        self.make_callback('activated', val)

    def set_state(self, tf):
        self.widget.set_active(tf)

    def get_state(self):
        return self.widget.get_active()

class ToggleButton(WidgetBase):
    def __init__(self, text=''):
        super(ToggleButton, self).__init__()

        w = GtkHelp.ToggleButton(text)
        w.set_mode(True)
        self.widget = w
        self.widget.sconnect('toggled', self._cb_redirect)

        self.enable_callback('activated')

    def _cb_redirect(self, widget):
        val = widget.get_active()
        self.make_callback('activated', val)

    def set_state(self, tf):
        self.widget.set_active(tf)

    def get_state(self):
        return self.widget.get_active()

class RadioButton(WidgetBase):
    def __init__(self, text='', group=None):
        super(RadioButton, self).__init__()

        if group is not None:
            group = group.get_widget()
            self.widget = GtkHelp.RadioButton.new_with_label_from_widget(group,
                                                                         text)
        else:
            self.widget = GtkHelp.RadioButton.new_with_label(None, text)
        self.widget.connect('toggled', self._cb_redirect)

        self.enable_callback('activated')

    def _cb_redirect(self, widget):
        val = widget.get_active()
        self.make_callback('activated', val)

    def set_state(self, tf):
        self.widget.set_active(tf)

    def get_state(self):
        return self.widget.get_active()

class Image(WidgetBase):
    def __init__(self, native_image=None, style='normal', menu=None):
        super(Image, self).__init__()

        if native_image is None:
            native_image = Gtk.Image()
        self.image = native_image
        self.image.set_property("has-tooltip", True)
        evbox = Gtk.EventBox()
        evbox.add(self.image)
        evbox.connect("button-press-event", self._cb_redirect1)
        evbox.connect("button-release-event", self._cb_redirect2)
        self._action = None
        self.menu = menu
        self.widget = evbox

        self.enable_callback('activated')

    def _cb_redirect1(self, widget, event):
        if event.type == Gdk.EventType.BUTTON_PRESS:
            if event.button == 1:
                self._action = 'click'

            elif event.button == 3 and self.menu is not None:
                menu_w = self.menu.get_widget()
                if menu_w.get_sensitive():
                    return menu_w.popup(None, None, None, None,
                                        event.button, event.time)

    def _cb_redirect2(self, widget, event):
        if event.type == Gdk.EventType.BUTTON_RELEASE:
            if (event.button == 1) and (self._action == 'click'):
                self._action = None
                self.make_callback('activated')

    def _set_image(self, native_image):
        self.image.set_from_pixbuf(native_image.get_pixbuf())

    def load_file(self, img_path, format=None):
        # format ignored at present
        pixbuf = GtkHelp.pixbuf_new_from_file(img_path)
        self.image.set_from_pixbuf(pixbuf)

class ProgressBar(WidgetBase):
    def __init__(self):
        super(ProgressBar, self).__init__()

        w = Gtk.ProgressBar()
        # GTK3
        # w.set_orientation(Gtk.Orientation.HORIZONTAL)
        # w.set_inverted(False)
        self.widget = w

    def set_value(self, pct):
        pct = float(pct)
        self.widget.set_fraction(pct)
        self.widget.set_text("%.2f %%" % (pct * 100.0))

class StatusBar(WidgetBase):
    def __init__(self):
        super(StatusBar, self).__init__()

        sbar = Gtk.Statusbar()
        self.ctx_id = None
        self.widget = sbar
        self.statustask = None

    def clear_message(self):
        self.statustask = None
        if self.ctx_id is not None:
            try:
                self.widget.remove_all(self.ctx_id)
            except Exception:
                pass
            self.ctx_id = None

    def set_message(self, msg_str, duration=10.0):
        try:
            if self.ctx_id is not None:
                self.widget.remove_all(self.ctx_id)
        except Exception:
            pass
        self.ctx_id = self.widget.get_context_id('status')
        self.widget.push(self.ctx_id, msg_str)

        # remove message in about `duration` seconds
        if self.statustask is not None:
            GObject.source_remove(self.statustask)
            self.statustask = None
        if duration > 0.0:
            self.statustask = GObject.timeout_add(int(1000 * duration),
                                                  self.clear_message)

class TreeView(WidgetBase):
    def __init__(self, auto_expand=False, sortable=False, selection='single',
                 use_alt_row_color=False, dragable=False):
        super(TreeView, self).__init__()

        self.auto_expand = auto_expand
        self.sortable = sortable
        self.selection = selection
        self.dragable = dragable
        self.levels = 1
        self.leaf_key = None
        self.leaf_idx = 0
        self.columns = []
        self.datakeys = []
        # shadow index
        self.shadow = {}

        # this widget has a built in ScrollArea to match Qt functionality
        sw = Gtk.ScrolledWindow()
        sw.set_border_width(2)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.widget = sw

        if self.dragable:
            tv = GtkHelp.MultiDragDropTreeView()
            # enable drag from this widget
            targets = [("text/plain", 0, GtkHelp.DND_TARGET_TYPE_TEXT),
                       ("text/uri-list", 0, GtkHelp.DND_TARGET_TYPE_URIS)]
            tv.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK,
                                        targets, Gdk.DragAction.COPY)
            tv.connect("drag-data-get", self._start_drag)
        else:
            tv = Gtk.TreeView()

        self.tv = tv
        sw.add(self.tv)
        tv.connect('cursor-changed', self._selection_cb)
        tv.connect('row-activated', self._cb_redirect)
        # needed to get alternating row colors
        if use_alt_row_color:
            tv.set_rules_hint(True)
        if self.selection == 'multiple':
            # enable multiple selection
            treeselection = tv.get_selection()
            treeselection.set_mode(Gtk.SelectionMode.MULTIPLE)

        for cbname in ('selected', 'activated', 'drag-start'):
            self.enable_callback(cbname)

    def setup_table(self, columns, levels, leaf_key):
        self.clear()

        self.columns = columns
        self.levels = levels
        self.leaf_key = leaf_key

        # create the column headers
        if not isinstance(columns[0], str):
            # columns specifies a mapping
            headers = [col[0] for col in columns]
            datakeys = [col[1] for col in columns]
        else:
            headers = datakeys = columns

        self.datakeys = datakeys
        self.leaf_idx = datakeys.index(self.leaf_key)
        # make sort functions
        self.cell_sort_funcs = []
        for kwd in self.datakeys:
            self.cell_sort_funcs.append(self._mksrtfnN(kwd))

        # Remove old columns, if any
        for col in list(self.tv.get_columns()):
            self.tv.remove_column(col)

        # Set up headers
        for n in range(0, len(self.columns)):
            kwd = self.datakeys[n]
            if kwd == 'icon':
                cell = Gtk.CellRendererPixbuf()
            else:
                cell = Gtk.CellRendererText()
            cell.set_padding(2, 0)
            header = headers[n]
            tvc = Gtk.TreeViewColumn(header, cell)
            tvc.set_resizable(True)
            if self.sortable:
                tvc.connect('clicked', self.sort_cb, n)
                tvc.set_clickable(True)
            if n == 0:
                fn_data = self._mkcolfn0(kwd)
                # cell.set_property('xalign', 1.0)
            else:
                fn_data = self._mkcolfnN(kwd)
            tvc.set_cell_data_func(cell, fn_data)
            self.tv.append_column(tvc)

        treemodel = Gtk.TreeStore(object)
        self.tv.set_fixed_height_mode(False)
        self.tv.set_model(treemodel)
        # This speeds up rendering of TreeViews
        self.tv.set_fixed_height_mode(True)

    def set_tree(self, tree_dict):
        self.clear()

        model = Gtk.TreeStore(object)
        self._add_tree(model, tree_dict)

    def add_tree(self, tree_dict):
        model = self.tv.get_model()
        self._add_tree(model, tree_dict)

    def _add_tree(self, model, tree_dict):

        # Hack to get around slow TreeView scrolling with large lists
        self.tv.set_fixed_height_mode(False)

        for key in tree_dict:
            self._add_subtree(1, self.shadow,
                              model, None, key, tree_dict[key])

        self.tv.set_model(model)

        self.tv.set_fixed_height_mode(True)

        # User wants auto expand?
        if self.auto_expand:
            self.tv.expand_all()

    def _add_subtree(self, level, shadow, model, parent_item, key, node):

        if level >= self.levels:
            # leaf node
            try:
                bnch = shadow[key]
                item_iter = bnch.item
                # TODO: update leaf item

            except KeyError:
                # new item
                item_iter = model.append(parent_item, [node])
                shadow[key] = Bunch.Bunch(node=node, item=item_iter,
                                          terminal=True)

        else:
            try:
                # node already exists
                bnch = shadow[key]
                item = bnch.item
                d = bnch.node

            except KeyError:
                # new node
                item = model.append(None, [str(key)])
                d = {}
                shadow[key] = Bunch.Bunch(node=d, item=item, terminal=False)

            # recurse for non-leaf interior node
            for key in node:
                self._add_subtree(level + 1, d, model, item, key, node[key])

    def _selection_cb(self, treeview):
        path, column = treeview.get_cursor()
        if path is None:
            return
        model = treeview.get_model()
        item = model.get_iter(path)
        res_dict = {}
        self._get_item(res_dict, item)
        self.make_callback('selected', res_dict)

    def _cb_redirect(self, treeview, path, column):
        model = treeview.get_model()
        item = model.get_iter(path)
        res_dict = {}
        self._get_item(res_dict, item)
        self.make_callback('activated', res_dict)

    def _get_path(self, item):
        if item is None:
            return []

        model = self.tv.get_model()
        if not model.iter_has_child(item):
            # child node, so append my name to parent's path
            path_rest = self._get_path(model.iter_parent(item))
            d = model.get_value(item, 0)
            if isinstance(d, str):
                myname = d
            else:
                myname = d[self.leaf_key]
            path_rest.append(myname)
            return path_rest

        # non-leaf node case
        myname = model.get_value(item, 0)
        path_rest = self._get_path(model.iter_parent(item))
        path_rest.append(myname)
        return path_rest

    def _get_item(self, res_dict, item):
        # from the model iter `item`, return the item via a path
        # in the dictionary `res_dict`
        path = self._get_path(item)
        d, s = res_dict, self.shadow
        for name in path[:-1]:
            d = d.setdefault(name, {})
            s = s[name].node

        dst_key = path[-1]
        d[dst_key] = s[dst_key].node

    def get_selected(self):
        treeselection = self.tv.get_selection()
        model, pathlist = treeselection.get_selected_rows()
        res_dict = {}
        for path in pathlist:
            item = model.get_iter(path)
            self._get_item(res_dict, item)
        return res_dict

    def clear(self):
        model = Gtk.TreeStore(object)
        self.tv.set_model(model)
        self.shadow = {}

    def clear_selection(self):
        treeselection = self.tv.get_selection()
        treeselection.unselect_all()

    def select_path(self, path, state=True):
        treeselection = self.tv.get_selection()
        item = self._path_to_item(path)
        if state:
            treeselection.select_iter(item)
        else:
            treeselection.unselect_iter(item)

    def highlight_path(self, path, onoff, font_color='green'):
        item = self._path_to_item(path)  # noqa
        # TODO

    def _path_to_item(self, path):
        s = self.shadow
        for name in path[:-1]:
            s = s[name].node
        item = s[path[-1]].item
        return item

    def scroll_to_path(self, path):
        item = self._path_to_item(path)
        model = self.tv.get_model()
        treepath = model.get_path(item)
        self.tv.scroll_to_cell(treepath, use_align=True, row_align=0.5)

    def scroll_to_end(self):
        model = self.tv.get_model()
        num_rows = model.iter_n_children()
        item = model.iter_nth_child(None, num_rows - 1)
        treepath = model.get_path(item)
        self.tv.scroll_to_cell(treepath, use_align=True, row_align=0.5)

    def sort_on_column(self, i):
        model = self.tv.get_model()
        model.set_sort_column_id(i, Gtk.SortType.ASCENDING)

    def set_column_width(self, i, width):
        col = self.tv.get_column(i)
        col.set_max_width(width)

    def set_column_widths(self, lwidths):
        for i, width in enumerate(lwidths):
            if width is not None:
                self.set_column_width(i, width)

    def set_optimal_column_widths(self):
        self.tv.columns_autosize()

    def get_column_widths(self):
        res = []
        for i, _ in enumerate(self.columns):
            col = self.tv.get_column(i)
            res.append(col.get_width())
        return res

    def sort_cb(self, column, idx):
        treeview = column.get_tree_view()
        model = treeview.get_model()
        model.set_sort_column_id(idx, Gtk.SortType.ASCENDING)
        fn = self.cell_sort_funcs[idx]
        model.set_sort_func(idx, fn)
        return True

    def _mksrtfnN(self, idx):
        def fn(*args):
            model, iter1, iter2 = args[:3]
            bnch1 = model.get_value(iter1, 0)
            bnch2 = model.get_value(iter2, 0)
            if isinstance(bnch1, str):
                if isinstance(bnch2, str):
                    s1, s2 = bnch1.lower(), bnch2.lower()
                    if s1 < s2:
                        return -1
                    if s1 > s2:
                        return 1
                return 0
            val1, val2 = bnch1[idx], bnch2[idx]
            if isinstance(val1, str):
                val1, val2 = val1.lower(), val2.lower()
                if val1 < val2:
                    return -1
                if val1 > val2:
                    return 1
            return 0
        return fn

    def _mkcolfn0(self, idx):
        def fn(*args):
            column, cell, model, iter = args[:4]
            bnch = model.get_value(iter, 0)
            if isinstance(bnch, str):
                cell.set_property('text', bnch)
            elif isinstance(bnch, GdkPixbuf.Pixbuf):
                cell.set_property('pixbuf', bnch)
            elif isinstance(bnch[idx], GdkPixbuf.Pixbuf):
                cell.set_property('pixbuf', bnch[idx])
            else:
                cell.set_property('text', bnch[idx])
        return fn

    def _mkcolfnN(self, idx):
        def fn(*args):
            column, cell, model, iter = args[:4]
            bnch = model.get_value(iter, 0)
            if isinstance(bnch, str):
                cell.set_property('text', '')
            elif isinstance(bnch, GdkPixbuf.Pixbuf):
                cell.set_property('text', '')
            elif isinstance(bnch[idx], GdkPixbuf.Pixbuf):
                cell.set_property('pixbuf', bnch[idx])
            else:
                cell.set_property('text', str(bnch[idx]))
        return fn

    def _start_drag(self, treeview, context, selection,
                    info, timestamp):
        res_dict = self.get_selected()
        drag_pkg = DragPackage(self.tv, selection)
        self.make_callback('drag-start', drag_pkg, res_dict)
        drag_pkg.start_drag()

class WebView(WidgetBase):
    def __init__(self):
        if not has_webkit:
            raise NotImplementedError("Missing webkit")

        super(WebView, self).__init__()
        self.widget = WebKit.WebView()

    def load_url(self, url):
        self.widget.open(url)

    def load_html_string(self, html_string):
        self.widget.load_string(html_string, 'text/html', 'utf-8', 'file://')

    def go_back(self):
        self.widget.go_back()

    def go_forward(self):
        self.widget.go_forward()

    def reload_page(self):
        self.widget.reload()

    def stop_loading(self):
        self.widget.stop_loading()

# CONTAINERS

class ContainerBase(WidgetBase):
    def __init__(self):
        super(ContainerBase, self).__init__()
        self.children = []

        for name in ['widget-added', 'widget-removed']:
            self.enable_callback(name)

    def add_ref(self, ref):
        # TODO: should this be a weakref?
        self.children.append(ref)

    def _remove(self, childw, delete=False):
        self.widget.remove(childw)
        if delete:
            childw.destroy()

    def remove(self, child, delete=False):
        if child not in self.children:
            raise KeyError("Widget is not a child of this container")
        self.children.remove(child)

        self._remove(child.get_widget(), delete=delete)
        self.make_callback('widget-removed', child)

    def remove_all(self, delete=False):
        for child in list(self.children):
            self.remove(child, delete=delete)

    def get_children(self):
        return self.children

    def num_children(self):
        return len(self.children)

    def _get_native_children(self):
        return [child.get_widget() for child in self.children]

    def _get_native_index(self, nchild):
        l = self._get_native_children()
        return l.index(nchild)

    def _native_to_child(self, nchild):
        idx = self._get_native_index(nchild)
        return self.children[idx]

    def set_margins(self, left, right, top, bottom):
        # TODO: can this be made more accurate?
        self.widget.set_border_width(left)

    def set_border_width(self, pix):
        self.widget.set_border_width(pix)

class Box(ContainerBase):
    def __init__(self, orientation='horizontal'):
        super(Box, self).__init__()

        if orientation == 'horizontal':
            self.widget = Gtk.HBox()
        else:
            self.widget = Gtk.VBox()

    def set_spacing(self, val):
        self.widget.set_spacing(val)

    def insert_widget(self, idx, child, stretch=0.0):
        child_w = child.get_widget()
        # TODO: can this be made more accurate?
        expand = (float(stretch) > 0.0)
        self.widget.pack_start(child_w, expand, True, 0)
        self.widget.reorder_child(child_w, idx)
        self.children.insert(idx, child)
        self.widget.show_all()
        self.make_callback('widget-added', child)

    def add_widget(self, child, stretch=0.0):
        self.add_ref(child)
        child_w = child.get_widget()
        # TODO: can this be made more accurate?
        expand = (float(stretch) > 0.0)
        self.widget.pack_start(child_w, expand, True, 0)
        self.widget.show_all()
        self.make_callback('widget-added', child)

class VBox(Box):
    def __init__(self):
        super(VBox, self).__init__(orientation='vertical')

class HBox(Box):
    def __init__(self):
        super(HBox, self).__init__(orientation='horizontal')

class Frame(ContainerBase):
    def __init__(self, title=None):
        super(Frame, self).__init__()

        fr = Gtk.Frame(label=title)
        fr.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        fr.set_label_align(0.10, 0.5)
        self.widget = fr

    def set_widget(self, child):
        self.remove_all()
        self.add_ref(child)
        self.widget.add(child.get_widget())
        self.widget.show_all()

    def set_text(self, text):
        w = self.get_widget()
        lbl = w.get_label_widget()
        lbl.set_text(text)

class Expander(ContainerBase):
    r_arrow = None
    d_arrow = None

    def __init__(self, title=None, notoggle=False):
        super(Expander, self).__init__()

        vbox = VBox()
        vbox.set_margins(0, 0, 0, 0)
        vbox.set_spacing(0)
        self.widget = vbox.get_widget()
        self._vbox = vbox
        if Expander.r_arrow is None:
            iconpath = os.path.join(icondir, 'triangle-right-48.png')
            Expander.r_arrow = GtkHelp.pixbuf_new_from_file_at_size(iconpath,
                                                                    12, 12)
        if Expander.d_arrow is None:
            iconpath = os.path.join(icondir, 'triangle-down-48.png')
            Expander.d_arrow = GtkHelp.pixbuf_new_from_file_at_size(iconpath,
                                                                    12, 12)
        self._d_arrow = Gtk.Image.new_from_pixbuf(Expander.d_arrow)
        self._r_arrow = Gtk.Image.new_from_pixbuf(Expander.r_arrow)

        self.toggle = None
        if not notoggle:
            toggle = ToggleButton(title)
            self.toggle = toggle
            toggle_w = toggle.get_widget()
            toggle_w.set_always_show_image(True)
            r_arrow = Gtk.Image.new_from_pixbuf(Expander.r_arrow)
            toggle_w.set_image(r_arrow)
            toggle.add_callback('activated', self._toggle_widget)
            vbox.add_widget(toggle, stretch=0)

        self.content = None

        for name in ('opened', 'closed'):
            self.enable_callback(name)

    def set_widget(self, child, stretch=1):
        if self.content is not None:
            self.widget.remove(self.content)
        self.content = child

    def expand(self, tf):
        children = self._vbox.get_children()
        if tf:
            if self.content is None or self.content in children:
                return
            if self.toggle is not None:
                self.toggle.get_widget().set_image(self._d_arrow)
            self._vbox.add_widget(self.content, stretch=1)
            self.make_callback('opened')

        else:
            if self.content is None or self.content not in children:
                return
            if self.toggle is not None:
                self.toggle.get_widget().set_image(self._r_arrow)
            self._vbox.remove(self.content)
            self.make_callback('closed')

    def _toggle_widget(self, w, tf):
        self.expand(tf)

class TabWidget(ContainerBase):
    def __init__(self, tabpos='top', reorderable=False, detachable=True,
                 group=0):
        super(TabWidget, self).__init__()

        self.reorderable = reorderable
        self.detachable = detachable

        nb = GtkHelp.Notebook()
        # nb = Gtk.Notebook()
        nb.set_show_border(False)
        nb.set_scrollable(True)
        # Allows drag-and-drop between notebooks
        # nb.set_group_id(group)  # in gtk3?
        if self.detachable:
            nb.connect("create-window", self._tab_detach_cb)
        nb.connect("page-added", self._tab_insert_cb)
        nb.connect("page-removed", self._tab_remove_cb)
        # contrary to some other widgets, we want the "tab changed" event
        # when the index is switched programmatically as well as by user
        ## nb.sconnect("switch-page", self._cb_redirect)
        nb.connect("switch-page", self._cb_redirect)
        self.widget = nb
        self.set_tab_position(tabpos)

        for name in ('page-switch', 'page-close', 'page-move', 'page-detach'):
            self.enable_callback(name)

    def set_tab_position(self, tabpos):
        nb = self.widget
        if tabpos == 'top':
            nb.set_tab_pos(Gtk.PositionType.TOP)
        elif tabpos == 'bottom':
            nb.set_tab_pos(Gtk.PositionType.BOTTOM)
        elif tabpos == 'left':
            nb.set_tab_pos(Gtk.PositionType.LEFT)
        elif tabpos == 'right':
            nb.set_tab_pos(Gtk.PositionType.RIGHT)

    def _tab_detach_cb(self, source, nchild_w, x, y):
        child = self._native_to_child(nchild_w)
        # remove child
        # (native widget already has been removed by gtk)
        self.children.remove(child)

        # nchild_w.unparent()
        self.make_callback('page-detach', child)

    def _tab_insert_cb(self, nbw, nchild_w, page_num):
        global _widget_move_event
        if _widget_move_event is not None:
            event, _widget_move_event = _widget_move_event, None
            already_here = nchild_w in self._get_native_children()
            if not already_here and event.child.get_widget() == nchild_w:
                child = event.child
                # remove child from src tab
                # (native widget already has been removed by gtk)
                event.src_widget.children.remove(child)
                # add child to us
                # (native widget already has been added by gtk)
                self.add_ref(child)
                self.make_callback('page-move', event.src_widget, child)

    def _tab_remove_cb(self, nbw, nchild_w, page_num):
        global _widget_move_event
        try:
            child = self._native_to_child(nchild_w)
            _widget_move_event = WidgetMoveEvent(self, child)
        except ValueError:
            # we were triggered by a removal that is not a move
            pass

    def _cb_redirect(self, nbw, gptr, index):
        child = self.index_to_widget(index)
        self.make_callback('page-switch', child)

    def _cb_select(self, widget, event, child):
        self.make_callback('page-switch', child)

    def add_widget(self, child, title=''):
        self.add_ref(child)
        child_w = child.get_widget()
        label = Gtk.Label(title)
        evbox = Gtk.EventBox()
        evbox.props.visible_window = True
        evbox.add(label)
        evbox.show_all()
        evbox.connect("button-press-event", self._cb_select, child)
        self.widget.append_page(child_w, evbox)
        if self.reorderable:
            self.widget.set_tab_reorderable(child_w, True)
        if self.detachable:
            self.widget.set_tab_detachable(child_w, True)
        self.widget.show_all()
        # attach title to child
        child.extdata.tab_title = title
        self.make_callback('widget-added', child)

    def get_index(self):
        return self.widget.get_current_page()

    def set_index(self, idx):
        self.widget.set_current_page(idx)

    def index_of(self, child):
        widget = child.get_widget()
        if widget is None:
            return -1
        return self.widget.page_num(widget)

    def index_to_widget(self, idx):
        """Returns child corresponding to `idx`"""
        nchild = self.widget.get_nth_page(idx)
        return self._native_to_child(nchild)

    def highlight_tab(self, idx, tf):
        nchild = self.widget.get_nth_page(idx)
        evbox = self.widget.get_tab_label(nchild)
        if tf:
            GtkHelp.modify_bg(evbox, 'palegreen')
        else:
            GtkHelp.modify_bg(evbox, None)

class StackWidget(TabWidget):
    def __init__(self):
        super(StackWidget, self).__init__()

        nb = self.widget
        # nb.set_scrollable(False)
        nb.set_show_tabs(False)
        nb.set_show_border(False)

class MDIWidget(ContainerBase):

    def __init__(self, tabpos='top', mode='tabs'):
        super(MDIWidget, self).__init__()

        self.mode = 'mdi'
        self.true_mdi = True

        # TODO: currently scrollbars are only partially working
        sw = Gtk.ScrolledWindow()
        sw.set_border_width(2)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.widget = sw
        w = GtkHelp.MDIWidget()
        self.mdi_w = w
        # Monkey patching the internal callbacks so that we can make
        # the correct callbacks
        w._move_page = w.move_page
        w.move_page = self._window_moved
        w._resize_page = w.resize_page
        w.resize_page = self._window_resized
        w._set_current_page = w.set_current_page
        w.set_current_page = self._set_current_page

        sw.set_hadjustment(self.mdi_w.get_hadjustment())
        sw.set_vadjustment(self.mdi_w.get_vadjustment())
        sw.add(self.mdi_w)

        for name in ('page-switch', 'page-close'):
            self.enable_callback(name)

    def get_mode(self):
        return self.mode

    def set_mode(self, mode):
        pass

    def add_widget(self, child, title=''):
        self.add_ref(child)
        subwin = MDIWindow(self, child, title=title)
        subwin.add_callback('close', self._window_close, child)

        self.make_callback('widget-added', child)
        return subwin

    def _remove(self, childw, delete=False):
        self.mdi_w.remove(childw)
        if delete:
            childw.destroy()

    def _window_resized(self, subwin, wd, ht):
        self.mdi_w._resize_page(subwin, wd, ht)

        # save size
        nchild = subwin.widget
        child = self._native_to_child(nchild)
        child.extdata.mdi_size = (wd, ht)
        return True

    def _window_moved(self, subwin, x, y):
        self.mdi_w._move_page(subwin, x, y)

        # save position
        nchild = subwin.widget
        child = self._native_to_child(nchild)
        child.extdata.mdi_pos = (x, y)
        return True

    def _window_close(self, subwin, child):
        return self.make_callback('page-close', child)

    def _set_current_page(self, idx):
        _idx = self.mdi_w.get_current_page()
        self.mdi_w._set_current_page(idx)
        if _idx != idx:
            child = self.index_to_widget(idx)
            self.make_callback('page-switch', child)

    def get_index(self):
        return self.mdi_w.get_current_page()

    def set_index(self, idx):
        self.mdi_w.set_current_page(idx)

    def index_of(self, child):
        return self.mdi_w.page_num(child.get_widget())

    def index_to_widget(self, idx):
        """Returns child corresponding to `idx`"""
        nchild = self.mdi_w.get_nth_page(idx)
        return self._native_to_child(nchild)

    def tile_panes(self):
        self.mdi_w.tile_pages()

    def cascade_panes(self):
        self.mdi_w.cascade_pages()

    def use_tabs(self, tf):
        pass

class MDIWindow(WidgetBase):
    def __init__(self, parent, child, title=''):
        """NOTE: this widget is not meant to be instantiated except *inside*
        of MDIWidget implementation.
        """
        WidgetBase.__init__(self)
        self.parent = parent
        mdi_w = parent.mdi_w

        # does child have a previously saved size?
        size = child.extdata.get('mdi_size', None)
        if size is not None:
            wd, ht = size
            child.resize(wd, ht)

        child_w = child.get_widget()
        label = Gtk.Label(title)
        subwin = GtkHelp.MDISubWindow(child_w, label)
        self.widget = subwin
        # attach title to child
        child.extdata.tab_title = title

        self.enable_callback('close')
        subwin.add_callback('close', self._window_close)

        # does child have a previously saved position?
        pos = child.extdata.get('mdi_pos', None)
        if pos is not None:
            subwin.x, subwin.y = pos

        mdi_w.add_subwin(subwin)

    def get_pos(self):
        return self.widget.x, self.widget.y

    def raise_(self):
        self.widget.raise_()

    def lower(self):
        self.widget.lower()

    def focus(self):
        self.widget.focus()

    def move(self, x, y):
        self.parent.mdi_w.move_page(self.widget, x, y)

    def resize(self, wd, ht):
        self.parent.mdi_w.resize_page(self.widget, wd, ht)

    def maximize(self):
        self.parent.mdi_w.maximize_page(self.widget)

    def unmaximize(self):
        raise WidgetError("this call not available for MDIWindow")

    def fullscreen(self):
        raise WidgetError("this call not available for MDIWindow")

    def unfullscreen(self):
        raise WidgetError("this call not available for MDIWindow")

    def is_fullscreen(self):
        raise WidgetError("this call not available for MDIWindow")

    def iconify(self):
        self.parent.mdi_w.minimize_page(self.widget)

    def uniconify(self):
        raise WidgetError("this call not available for MDIWindow")

    def set_title(self, title):
        self.widget.label.set_text(title)

    def _window_close(self, subwin):
        return self.make_callback('close')

class ScrollArea(ContainerBase):
    def __init__(self):
        super(ScrollArea, self).__init__()

        sw = Gtk.ScrolledWindow()
        sw.set_border_width(2)
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        self.widget = sw

        self.enable_callback('configure')
        sw.connect("size_allocate", self._resize_cb)

    def _resize_cb(self, widget, allocation):
        rect = widget.get_allocation()
        # x, y = rect.x, rect.y
        width, height = rect.width, rect.height
        self.make_callback('configure', width, height)
        return True

    def set_widget(self, child):
        self.remove_all()
        self.add_ref(child)
        self.widget.add_with_viewport(child.get_widget())
        self.widget.show_all()

    def scroll_to_end(self, vertical=True, horizontal=False):
        if vertical:
            adj_w = self.widget.get_vadjustment()
            maxv = adj_w.get_upper()
            adj_w.set_value(maxv)
        if horizontal:
            adj_w = self.widget.get_hadjustment()
            maxv = adj_w.get_upper()
            adj_w.set_value(maxv)

class Splitter(ContainerBase):
    def __init__(self, orientation='horizontal', thumb_px=8):
        super(Splitter, self).__init__()

        # thumb_px ignored in this version
        self.orientation = orientation
        self.widget = self._get_pane()
        self.panes = [self.widget]

    def _get_pane(self):
        if self.orientation == 'horizontal':
            w = Gtk.HPaned()
        else:
            w = Gtk.VPaned()
        w.set_wide_handle(True)
        return w

    def add_widget(self, child):
        self.add_ref(child)
        child_w = child.get_widget()

        # without a Frame it can be difficult to see the divider
        frame_w = Gtk.Frame()
        #frame_w.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        frame_w.set_shadow_type(Gtk.ShadowType.NONE)
        frame_w.add(child_w)

        if len(self.children) == 1:
            self.widget.pack1(frame_w)

        else:
            last = self.widget
            if len(self.panes) > 0:
                last = self.panes[-1]

            w = self._get_pane()
            self.panes.append(w)

            w.pack1(frame_w)
            last.pack2(w)

        self.widget.show_all()
        self.make_callback('widget-added', child)

    def _get_sizes(self, pane):
        rect = pane.get_allocation()
        if self.orientation == 'horizontal':
            total = rect.width
        else:
            total = rect.height
        pos = pane.get_position()
        return (pos, total)

    def get_sizes(self):
        res = []
        if len(self.panes) > 0:
            for pane in self.panes[:-1]:
                pos, total = self._get_sizes(pane)
                res.append(pos)
            pane = self.panes[-1]
            pos, total = self._get_sizes(pane)
            res.append(total)
        return res

    def set_sizes(self, sizes):
        for i, pos in enumerate(sizes):
            pane = self.panes[i]
            pane.set_position(pos)

class Splitter2(ContainerBase):
    def __init__(self, orientation='horizontal', thumb_px=8):
        super(Splitter, self).__init__()

        self.orientation = orientation
        self.widget = GtkHelp.Splitter(orientation=self.orientation,
                                       thumb_px=thumb_px)

    def add_widget(self, child):
        self.add_ref(child)
        child_w = child.get_widget()

        # without a Frame it can be difficult to see the divider
        frame_w = Gtk.Frame()
        #frame_w.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        frame_w.set_shadow_type(Gtk.ShadowType.NONE)
        frame_w.add(child_w)

        self.widget.add_widget(frame_w)
        self.widget.show_all()
        self.make_callback('widget-added', child)

    def get_sizes(self):
        return self.widget.get_sizes()

    def set_sizes(self, sizes):
        self.widget.set_sizes(sizes)

class GridBox(ContainerBase):
    def __init__(self, rows=1, columns=1):
        super(GridBox, self).__init__()

        w = Gtk.Table(rows=rows, columns=columns)
        self.widget = w
        self.num_rows = rows
        self.num_cols = columns

    def resize_grid(self, rows, columns):
        self.num_rows = rows
        self.num_cols = columns
        self.widget.resize(rows, columns)

    def set_row_spacing(self, val):
        self.widget.set_row_spacings(val)

    def set_column_spacing(self, val):
        self.widget.set_col_spacings(val)

    def set_spacing(self, val):
        self.set_row_spacing(val)
        self.set_column_spacing(val)

    def add_widget(self, child, row, col, stretch=0):
        resize = False
        if row > self.num_rows:
            resize = True
            self.num_rows = row
        if col > self.num_cols:
            resize = True
            self.num_cols = col
        if resize:
            self.resize_grid(self.num_rows, self.num_cols)

        self.add_ref(child)
        w = child.get_widget()
        if stretch > 0:
            xoptions = (Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.SHRINK |
                        Gtk.AttachOptions.FILL)
            yoptions = (Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.SHRINK |
                        Gtk.AttachOptions.FILL)
        else:
            xoptions = (Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK)
            yoptions = (Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK)
        self.widget.attach(w, col, col + 1, row, row + 1,
                           xoptions=xoptions, yoptions=yoptions,
                           xpadding=0, ypadding=0)
        self.widget.show_all()
        self.make_callback('widget-added', child)

class Toolbar(ContainerBase):
    def __init__(self, orientation='horizontal'):
        super(Toolbar, self).__init__()

        w = Gtk.Toolbar()
        w.set_style(Gtk.ToolbarStyle.ICONS)
        if orientation == 'horizontal':
            w.set_orientation(Gtk.Orientation.HORIZONTAL)
        else:
            w.set_orientation(Gtk.Orientation.VERTICAL)
        self.widget = w

    def add_action(self, text, toggle=False, iconpath=None, iconsize=None):
        if toggle:
            child = ToggleButton(text)
        else:
            child = Button(text)

        if iconpath is not None:
            if iconsize is not None:
                wd, ht = iconsize
            else:
                scale_f = _app.screen_res / 96.0
                px = int(scale_f * 24)
                wd, ht = px, px
            pixbuf = GtkHelp.pixbuf_new_from_file_at_size(iconpath, wd, ht)
            if pixbuf is not None:
                image = Gtk.Image.new_from_pixbuf(pixbuf)
                child.get_widget().set_image(image)

        self.add_widget(child)
        return child

    def add_widget(self, child):
        # gtk3 says to add a generic widget using ToolItem.new()
        tool_w = Gtk.ToolItem.new()
        w = child.get_widget()
        tool_w.add(w)
        w.show()
        tool = ContainerBase()
        tool.widget = tool_w
        tool_w.show()
        tool.add_ref(child)
        self.add_ref(tool)
        self.widget.insert(tool_w, -1)
        self.make_callback('widget-added', child)
        return tool

    def add_menu(self, text, menu=None, mtype='tool'):
        if menu is None:
            menu = Menu()
        if mtype == 'tool':
            child = self.add_action(text)
        else:
            child = Label(text, style='clickable', menu=menu)
            self.add_widget(child)
            child.add_callback('released', lambda w: menu.hide())

        child.add_callback('activated', lambda w: menu.popup())
        return menu

    def add_separator(self):
        sep_w = Gtk.SeparatorToolItem()
        sep = wrap(sep_w)
        self.widget.insert(sep_w, -1)
        self.add_ref(sep)

class MenuAction(WidgetBase):
    def __init__(self, text=None, checkable=False):
        super(MenuAction, self).__init__()

        self.text = text
        self.checkable = checkable

        if checkable:
            self.widget = Gtk.CheckMenuItem(label=text)
            self.widget.connect('toggled', self._cb_redirect)
        else:
            self.widget = Gtk.MenuItem(label=text)
            self.widget.connect('activate', self._cb_redirect)
        self.widget.show()

        self.enable_callback('activated')

    def set_state(self, tf):
        if not self.checkable:
            raise ValueError("Not a checkable menu item")
        self.widget.set_active(tf)

    def get_state(self):
        return self.widget.get_active()

    def _cb_redirect(self, *args):
        if self.checkable:
            tf = self.widget.get_active()
            self.make_callback('activated', tf)
        else:
            self.make_callback('activated')

class Menu(ContainerBase):
    def __init__(self):
        super(Menu, self).__init__()

        self.widget = Gtk.Menu()
        self.menus = Bunch.Bunch(caseless=True)
        self.widget.show()

    def add_widget(self, child):
        menuitem_w = child.get_widget()
        self.widget.append(menuitem_w)
        self.add_ref(child)
        # self.widget.show_all()
        self.make_callback('widget-added', child)

    def add_name(self, name, checkable=False):
        child = MenuAction(text=name, checkable=checkable)
        self.add_widget(child)
        return child

    def add_menu(self, name):
        item_w = Gtk.MenuItem(label=name)
        child = Menu()
        self.add_ref(child)
        self.menus[name] = child
        item_w.set_submenu(child.get_widget())
        self.widget.append(item_w)
        item_w.show()
        return child

    def get_menu(self, name):
        return self.menus[name]

    def add_separator(self):
        sep = Gtk.SeparatorMenuItem()
        self.widget.append(sep)
        sep.show()

    def popup(self, widget=None):
        menu = self.widget
        menu.show_all()
        now = int(0)
        if menu.get_sensitive():
            menu.popup(None, None, None, None, 0, now)

class Menubar(ContainerBase):
    def __init__(self):
        super(Menubar, self).__init__()

        self.widget = Gtk.MenuBar()
        self.menus = Bunch.Bunch(caseless=True)

    def add_widget(self, child, name):
        if not isinstance(child, Menu):
            raise ValueError("child widget needs to be a Menu object")
        item_w = Gtk.MenuItem(label=name)
        item_w.set_submenu(child.get_widget())
        self.add_ref(child)
        self.widget.append(item_w)
        self.menus[name] = child
        item_w.show()
        self.make_callback('widget-added', child)
        return child

    def add_name(self, name):
        item_w = Gtk.MenuItem(label=name)
        child = Menu()
        self.add_ref(child)
        self.menus[name] = child
        item_w.set_submenu(child.get_widget())
        self.widget.append(item_w)
        item_w.show()
        return child

    def get_menu(self, name):
        return self.menus[name]

class TopLevelMixin(object):

    def __init__(self, title=None):
        self._fullscreen = False

        self.widget.connect("destroy", self._quit)
        self.widget.connect("delete_event", self._close_event)
        self.widget.connect("window_state_event", self._window_event)
        self.widget.connect("configure-event", self._configure_event)

        if title is not None:
            self.widget.set_title(title)

        self.enable_callback('close')

    def show(self):
        self.widget.show_all()

    def hide(self):
        self.widget.hide()

    def _quit(self, *args):
        self.close()

    def _close_event(self, widget, event):
        try:
            self.close()

        finally:
            # don't automatically destroy window
            return True

    def _window_event(self, widget, event):
        if ((event.changed_mask & Gdk.WindowState.FULLSCREEN) or
                (event.changed_mask & Gdk.WindowState.MAXIMIZED)):
            self._fullscreen = True
        else:
            self._fullscreen = False

    def _configure_event(self, widget, event):
        x, y, width, height = event.x, event.y, event.width, event.height
        x, y = self.widget.translate_coordinates(self.widget, x, y)
        self.extdata.setvals(x=x, y=y, width=width, height=height)
        return False

    def close(self):
        # try:
        #     self.widget.destroy()
        # except Exception as e:
        #     pass
        # self.widget = None

        self.make_callback('close')

    def get_size(self):
        try:
            rect = self.widget.get_allocation()
            # x, y = rect.x, rect.y
            wd, ht = rect.width, rect.height

        except Exception as e:
            # window maybe isn't realized yet--try other ways
            # req = self.widget.get_size_request()
            # wd, ht = req
            min_req, nat_req = self.widget.get_preferred_size()
            wd, ht = nat_req.width, nat_req.height
            ed = self.extdata
            wd, ht = ed.get('width', wd), ed.get('height', ht)

        return wd, ht

    def get_pos(self):
        res = None
        window = self.widget.get_window()
        if window is not None:
            res = window.get_origin()
            if isinstance(res, tuple) and len(res) == 2:
                return res

        ed = self.extdata
        x, y = ed.get('x', None), ed.get('y', None)
        return x, y

    def raise_(self):
        window = self.widget.get_window()
        if window is not None:
            window.raise_()

    def lower(self):
        window = self.widget.get_window()
        if window is not None:
            window.lower()

    def focus(self):
        window = self.widget.get_window()
        if window is not None:
            window.focus()

    def move(self, x, y):
        window = self.widget.get_window()
        if window is not None:
            window.move(x, y)

    def maximize(self):
        window = self.widget.get_window()
        if window is not None:
            window.maximize()

    def unmaximize(self):
        window = self.widget.get_window()
        if window is not None:
            window.unmaximize()

    def is_maximized(self):
        window = self.widget.get_window()
        mask = Gdk.WindowState.MAXIMIZED
        return window.get_state() & mask != 0

    def fullscreen(self):
        window = self.widget.get_window()
        if window is not None:
            window.fullscreen()

    def unfullscreen(self):
        window = self.widget.get_window()
        if window is not None:
            window.unfullscreen()

    def is_fullscreen(self):
        return self._fullscreen

    def iconify(self):
        window = self.widget.get_window()
        if window is not None:
            window.iconify()

    def uniconify(self):
        window = self.widget.get_window()
        if window is not None:
            window.deiconify()

    def set_title(self, title):
        self.widget.set_title(title)

class TopLevel(TopLevelMixin, ContainerBase):

    def __init__(self, title=None):
        ContainerBase.__init__(self)

        self._fullscreen = False

        widget = GtkHelp.TopLevel()
        self.widget = widget
        widget.set_border_width(0)

        TopLevelMixin.__init__(self, title=title)

    def set_widget(self, child):
        self.add_ref(child)
        child_w = child.get_widget()
        self.widget.add(child_w)

class Application(Callback.Callbacks):

    def __init__(self, logger=None, settings=None):
        global _app
        super(Application, self).__init__()

        self.logger = logger
        if settings is None:
            settings = Settings.SettingGroup(logger=self.logger)
        self.settings = settings
        self.settings.add_defaults(font_scaling_factor=None)

        self.window_list = []
        self.window_dict = {}
        self.wincnt = 0

        try:
            display = Gdk.Display.get_default()
            screen = display.get_default_screen()
            window = screen.get_active_window()
            monitor = screen.get_monitor_at_window(window)

            g = screen.get_monitor_geometry(monitor)
            self.screen_ht = g.height
            self.screen_wd = g.width

            self.screen_res = screen.get_resolution()

            scale = self.settings.get('font_scaling_factor', None)
            if scale is None:
                # hack for Gtk--scale fonts on HiDPI displays
                scale = self.screen_res / 72.0
            self.logger.debug("setting default font_scaling_factor={}".format(scale))
            from ginga.fonts import font_asst
            font_asst.default_scaling_factor = scale
        except Exception as e:
            self.screen_wd = 1600
            self.screen_ht = 1200
            self.screen_res = 96
        # self.logger.debug("screen dimensions %dx%d" % (
        #     self.screen_wd, self.screen_ht))

        _app = self

        # supposedly needed for GObject < 3.10.2
        GObject.threads_init()
        # self._time_save = time.time()

        for name in ('shutdown', ):
            self.enable_callback(name)

        # Set up Gtk style
        GtkHelp.set_default_style()

    def get_screen_size(self):
        return (self.screen_wd, self.screen_ht)

    def process_events(self):

        while Gtk.events_pending():
            try:
                Gtk.main_iteration()
                # TEMP: to help solve the issue of gtk3 events getting
                # lost--we want to know whether the process_event loop
                # is running, so ping periodically if events are showing
                # up
                # cur_time = time.time()
                # if cur_time - self._time_save > 10.0:
                #     self.logger.info("process_events ping!")
                #     self._time_save = cur_time

            except Exception as e:
                self.logger.error("Exception in main_iteration() loop: %s" %
                                  (str(e)))

    def process_end(self):
        pass

    def add_window(self, window, wid=None):
        if wid is None:
            wid = 'win%d' % (self.wincnt)
            self.wincnt += 1
        window.wid = wid
        window.url = ''
        window.app = self

        self.window_dict[wid] = window

    def get_window(self, wid):
        return self.window_dict[wid]

    def has_window(self, wid):
        return wid in self.window_dict

    def get_wids(self):
        return list(self.window_dict.keys())

    def make_window(self, title=None):
        w = TopLevel(title=title)
        self.add_window(w)
        return w

    def make_timer(self):
        return GtkHelp.Timer()

    def mainloop(self):
        Gtk.main()

    def quit(self):
        Gtk.main_quit()

class Dialog(TopLevelMixin, WidgetBase):

    def __init__(self, title='', flags=0, buttons=[],
                 parent=None, modal=False):
        WidgetBase.__init__(self)

        if parent is not None:
            self.parent = parent.get_widget()
        else:
            self.parent = None

        button_list = []
        for name, val in buttons:
            button_list.extend([name, val])

        self.widget = Gtk.Dialog(title=title, flags=flags,
                                 buttons=tuple(button_list))
        self.widget.set_modal(modal)

        TopLevelMixin.__init__(self, title=title)

        self.content = VBox()
        self.content.set_border_width(0)
        content = self.widget.get_content_area()
        content.pack_start(self.content.get_widget(), True, True, 0)

        self.widget.connect("response", self._cb_redirect)

        self.enable_callback('activated')

    def _cb_redirect(self, w, val):
        self.make_callback('activated', val)

    def get_content_area(self):
        return self.content

class SaveDialog(object):
    def __init__(self, title='Save File', selectedfilter=None):
        action = Gtk.FileChooserAction.SAVE
        buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                   Gtk.STOCK_SAVE, Gtk.ResponseType.OK)

        self.widget = Gtk.FileChooserDialog(title=title, action=action,
                                            buttons=buttons)
        self.selectedfilter = selectedfilter

        if selectedfilter is not None:
            self._add_filter(selectedfilter)

    def _add_filter(self, selectedfilter):
        filtr = Gtk.FileFilter()
        filtr.add_pattern(selectedfilter)
        if 'png' in selectedfilter:
            filtr.set_name('Image (*.png)')
            self.selectedfilter = '.png'
        elif 'avi' in selectedfilter:
            filtr.set_name('Movie (*.avi)')
            self.selectedfilter = '.avi'
        elif 'npz' in selectedfilter:
            filtr.set_name('Numpy Compressed Archive (*.npz)')
            self.selectedfilter = '.npz'
        self.widget.add_filter(filtr)

    def get_path(self):
        response = self.widget.run()

        if response == Gtk.ResponseType.OK:
            path = self.widget.get_filename()
            if (self.selectedfilter is not None and
                    not path.endswith(self.selectedfilter)):
                path += self.selectedfilter
            self.widget.destroy()
            return path
        elif response == Gtk.ResponseType.CANCEL:
            self.widget.destroy()
            return None

class DragPackage(object):
    def __init__(self, src_widget, selection):
        self.src_widget = src_widget
        self._selection = selection

    def set_urls(self, urls):
        self._selection.set_uris(urls)

    def set_text(self, text):
        self._selection.set_text(text, len(text))

    def start_drag(self):
        pass

class WidgetMoveEvent(object):
    def __init__(self, src_widget, child):
        self.src_widget = src_widget
        self.child = child
        self._result = False

    def accept(self):
        self._result = True

    def reject(self):
        self._result = False

# MODULE FUNCTIONS

def name_mangle(name, pfx=''):
    newname = []
    for c in name.lower():
        if not (c.isalpha() or c.isdigit() or (c == '_')):
            newname.append('_')
        else:
            newname.append(c)
    return pfx + ''.join(newname)

def make_widget(title, wtype):
    if wtype == 'label':
        w = Label(title)
        w.label.set_alignment(0.95, 0.5)
    elif wtype == 'llabel':
        w = Label(title)
        w.label.set_alignment(0.05, 0.95)
    elif wtype == 'entry':
        w = TextEntry()
        # w.get_widget().set_width_chars(12)
    elif wtype == 'entryset':
        w = TextEntrySet()
    elif wtype == 'combobox':
        w = ComboBox()
    elif wtype == 'spinbutton':
        w = SpinBox(dtype=int)
    elif wtype == 'spinfloat':
        w = SpinBox(dtype=float)
    elif wtype == 'vbox':
        w = VBox()
    elif wtype == 'hbox':
        w = HBox()
    elif wtype in ('hslider', 'hscale'):
        w = Slider(orientation='horizontal')
    elif wtype in ('vslider', 'vscale'):
        w = Slider(orientation='vertical')
    elif wtype in ('checkbox', 'checkbutton'):
        w = CheckBox(title)
    elif wtype == 'radiobutton':
        w = RadioButton(title)
    elif wtype == 'togglebutton':
        w = ToggleButton(title)
    elif wtype == 'button':
        w = Button(title)
    elif wtype == 'spacer':
        w = Label('')
    elif wtype == 'textarea':
        w = TextArea(editable=True)
    elif wtype == 'toolbar':
        w = Toolbar()
    elif wtype == 'progress':
        w = ProgressBar()
    elif wtype == 'menubar':
        w = Menubar()
    elif wtype == 'dial':
        w = Dial()
    else:
        raise ValueError("Bad wtype=%s" % wtype)
    return w

def hadjust(w, orientation):
    """Ostensibly, a function to reduce the vertical footprint of a widget
    that is normally used in a vertical stack (usually a Splitter), when it
    is instead used in a horizontal orientation.
    """
    if orientation != 'horizontal':
        return w
    # This currently does not seem to be needed for most plugins that are
    # coded to flow either vertically or horizontally and, in fact, reduces
    # the visual asthetic somewhat.
    ## spl = Splitter(orientation='vertical')
    ## spl.add_widget(w)
    ## spl.add_widget(Label(''))
    ## return spl
    return w

def build_info(captions, orientation='vertical'):
    vbox = Gtk.VBox(spacing=2)

    numrows = len(captions)
    numcols = reduce(lambda acc, tup: max(acc, len(tup)), captions, 0)
    if (numcols % 2) != 0:
        raise ValueError("Column spec is not an even number")
    numcols = int(numcols // 2)
    table = Gtk.Table(rows=numrows, columns=numcols)
    table.set_row_spacings(2)
    table.set_col_spacings(4)
    vbox.pack_start(table, False, False, 0)

    wb = Bunch.Bunch()
    row = 0
    for tup in captions:
        col = 0
        while col < numcols:
            idx = col * 2
            if idx < len(tup):
                title, wtype = tup[idx:idx + 2]
                if not title.endswith(':'):
                    name = name_mangle(title)
                else:
                    name = name_mangle('lbl_' + title[:-1])
                w = make_widget(title, wtype)
                table.attach(w.get_widget(), col, col + 1, row, row + 1,
                             xoptions=Gtk.AttachOptions.FILL,
                             yoptions=Gtk.AttachOptions.FILL,
                             xpadding=1, ypadding=1)
                wb[name] = w
            col += 1
        row += 1

    vbox.show_all()

    w = wrap(vbox)
    w = hadjust(w, orientation=orientation)

    return w, wb

def wrap(native_widget):
    wrapper = WidgetBase()
    wrapper.widget = native_widget
    return wrapper

# END

redpajama_set_name: RedPajamaGithub


Anyone involved in security duties, and in particular anyone who may have to use a degree of force during the course of their work, must have a basic working knowledge of the laws and powers with regards to the varying degrees of assault. This basic understanding is essential for door supervisors, so that they can ensure that they use only a reasonable amount of force when necessary, and can act appropriately when witnessing or having to deal with an allegation of assault.
You will, of course, have heard of the term assault and battery.An assault is defined as any act where a person intentionally or recklessly causes another person to apprehend (fear) immediate and unlawful violence. The definition of battery is where a person intentionally or recklessly applies unlawful force to another person. If force is actually applied unlawfully or without the consent of the person being assaulted, then the assault becomes a battery.
There are various different charges that can be brought against someone who assaults another, depending on the seriousness of the injuries inflicted.
A common assault is the least serious type of assault, where either no actual injuries are caused, or when the injuries are of only a very minor nature. A push to the body or a slap around the face would come under this category, even though no actual injuries are sustained. The minor injuries covered by this offence include grazes, scratches, minor bruises, swelling, reddening, black eyes and minor superficial cuts. Even making a threat to assault someone can come under this category.
The offence of common assault is not an indictable offence, but is actionable through either the Magistrates Court by way of the injured party applying for a summons at court, or through the civil courts. The maximum penalty for anyone found guilty of committing such an assault is up to a £5,000 fine or up to 6 months imprisonment.
Doorstaff have no powers of arrest if they witness a common assault, but can obviously eject the offending customer from the premises. The police can also be called to give the appropriate advice to the victim if required. An arrest for "breach of the peace" may be considered, however, if it is necessary to prevent a more serious assault being committed.
If the injury sustained as the result of an assault is of a more serious nature, then a charge of assault occasioning actual bodily harm may be more suitable. This offence covers injuries such as broken or lost teeth, temporary loss of a sensory function such as consciousness, extensive or multiple bruising, minor cuts or minor fractures.
Anyone found guilty of committing a Sec.47 assault can be sentenced with up to a £5,000 fine or 6 month in prison at Magistrates Court, or up to 5 years in prison at a Crown Court.
Because of the possible sentence involved, this offence is an indictable offence and is therefore arrestable for door supervisors.
Doorstaff therefore, have the power to arrest anyone they see committing an assault occasioning actual bodily harm, or anyone they have reasonable grounds to suspect to have committed such an assault. If, for example, you come across a male repeatedly punching and kicking another person in the premises, and you can see that the victim has sustained multiple cuts and bruises, then you have the power to arrest the assailant and call the police.
"Whosoever shall unlawfully and maliciously, wound or inflict any grievous bodily harm upon any other person, either with or without any weapon or instrument, shall be liable to imprisonment".
A more serious offence yet is an assault whereby someone is occasioned grievous bodily harm. Injuries that fall under this section include the permanent loss of a sensory function (coma), visible disfigurement, broken or displaced limbs or bones, substantial blood loss and any injury resulting in lengthy treatment or incapacity.
This offence merely requires an unlawful or malicious act resulting in either a wounding or grievous bodily harm. The maximum penalties are the same for this offence as they are for assaults occasioning actual bodily harm, ie 5 years imprisonment, though sentences are usually harsher because of the nature of the injuries involved. Similarly it is also an indictable offence, and so door supervisors can detain the offender and call the police.
(Sec. 18 Offences Against the Person Act 1861).
Under this section, the offender has to intend to cause these serious injuries, or intend to resist or prevent the arrest of any person. Although the injuries under this section are the same as in a Sec.20 assault, it is considered far more serious because of the specific intent. Because of this, the maximum sentence for anyone found guilty of this offence at Crown Court is life imprisonment. This is obviously an indictable offence as well.
Both indecent assault on a male and a female carry sentences of up to 10 years imprisonment at Crown Court and 6 months or a £5,000 fine at Magistrates Court. Both are indictable offences.
Indecent assaults are any assaults accompanied by circumstances of indecency on the part of the offender towards the person being assaulted. The word indecent has no legal definition, but its generally accepted meaning is 'offensive to modesty, impure, obscene or unchaste behaviour in sexual matters'. It is ultimately for the court to decide whether any particular act is indecent or not, although in most circumstances it will be obvious.
Door supervisors should assist the police if they visit the venue or are called to deal with an incident inside. There are two offences relating specifically to police officers that door supervisors need to be aware of.
It is an offence for a person to assault a constable in the execution of his duty, or anyone assisting the constable in the execution of those duties.
Any person who resists or wilfully obstructs a constable in the execution of his duty, or anyone assisting the constable in the execution of those duties, commits an offence.
It is an offence to assault, resist, obstruct, molest or hinder a constable in the execution of his duty, or a person assisting him in the execution of his duty. It is also an offence to rescue or attempt to rescue or assist or attempt to assist the escape of any person in custody.
As a door supervisor working at licensed premises, you will occasionally have to resort to the use of force when carrying out your lawful duties. There are times when the law allows you to use force, such as when ejecting unruly customers who refuse to leave when requested, or in self defence or in defence of another, or when effecting a lawful arrest or preventing a crime from taking place. In these circumstances you may use force, providing that such force is reasonable and necessary in those circumstances, and that you do not use more force than is required to effect your purpose.
If, having lawfully physically ejected a customer from the premises, you then continue to use force on him, either to "teach him a lesson" or to "stop him trying again", then you most certainly commit an assault and make yourself liable to arrest.
With regards to door supervisors carrying weapons, any such use of force with a weapon would certainly be an assault and is very likely to lead to arrest and prosecution. The courts would not look kindly on any member of a security team using a weapon to inflict injury on a member of the public.
Door supervisors also have specific powers of arrest to help them deal with assaults that occur on their premises, and should use them when the need arises.
Assault in Scotland can be defined as a criminal attack on the person of another, whether or not actual injury is inflicted.
Any type of assault is a crime under Common Law in Scotland, and so door stewards do have the necessary powers of arrest in most circumstances, particularly when there are serious injuries. Very minor assaults where there is no injury would normally be dealt with by way of ejection.


This page is part of the Invented Games section of pagat.com. It is a collection of variations of the traditional card game Bridge.
This game was invented by Jeff Rubens and published in the June 1975 issue of The Bridge World.
It is played exactly like Contract Bridge, except that in the play, any deuce (two) may be played to any trick.
So the rule is that if you can follow suit to the lead you must either do so or play a deuce. If you cannot follow suit you may play any card.
Deuces have no special power to win tricks - the normal rules apply in that respect. But of course, if you hold the 2 of trumps, you can ruff with it and win the trick (if no one else ruffs higher), even if you have cards in the suit led.
Leading a deuce has no special effect. Players who have the suit of the deuce must either follow suit or play another deuce, as usual.
A few years back I acquired a Five Crowns set from a local game dealership. While I was mildly disappointed in the default game, I always felt as though there would be a great deal of potential for the adaptation of other games to utilize the Five Crowns deck (or at least half of a Five Crowns deck). I recently found my set again, and since I was already working up a set of rules for a Solitaire version of Bridge and still hadn't been able to find many game variants online that utilize a Five Crowns Deck, I decided to see if I could hammer out a version of Bridge. The following document is more of a set of guidelines than an actual set of rules, but it should make it possible to add some spice to the King of Card Games.
PLAYERS: Four, but in fact the rules below are adaptable to any form and any variant of standard Contract Bridge, including variants for other numbers of players.
EQUIPMENT: From a Five Crowns set, you will need one copy of each ranked and suited card and one Joker, forming a deck of 56 cards with five suits and eleven cards in each suit.
RANK AND VALUE OF THE CARDS: The cards are valued the same as they ordinarily are in Contract Bridge; cards in each suit (crowns, spades, hearts, diamonds, clubs) are ranked from high to low KQT9876543 (T=10). One suit during play is selected to be "trump", and cards of that suit rank higher than the other suits. The Joker is valued as the "two of trumps", the lowest card of the trump suit.
THE DEAL: Cards are dealt as in all standard variants of bridge, except that players receive 14 cards for their hands instead of 13.
THE BID: Bidding is as per normal rules, except that the Stars suit ranks above Spades and below No Trump during the bid. Book is seven tricks and the lowest bid permitted is Zero No Trump (a bid to take seven tricks without trump).
THE PLAY: Gameplay occurs normally; players must follow suit if able, if not, they may play any other card. The highest trump played wins the trick, or if no trump card was played, the highest card of the suit led wins. A full hand consists of fourteen tricks.
In a No Trump contract the joker can be played to any trick but cannot win the trick. If the joker is led, the second player can play any card and that card determines the suit of the trick. The winner of a trick to which the joker was played must (if possible) lead a different suit to the next trick.
SCORING: All scoring for the game is the same, except that a contract in the Stars suit is scored the same as a No Trump contract. Zero No Trump bid and made scores 10 points below the line with 30 above for each overtrick.
Variant: Players may agree to use the following rule on the play of the Joker in No Trumps. In No Trumps, the joker may be used as the two of any suit. The suit is nominated by the holder (or the declarer if the joker is in dummy) at the moment when the two is played. The nominated suit cannot be one in which the joker holder has previously shown void.


text:
VERSIONS OF US - FICTION AWARD
Kelsie Bennett
They're laid out on the hood of Kara's grandfather's '67 Impala, the classic cherry red type that could hold its own in a car auction if its paint job wasn't so scratched and if Kara and Selene didn't spend every Saturday night slowly pressing dents into it. Selene is smoking slower than usual — the stars are really out tonight, and she wants to watch them wink before her grey cloud gets in the way. She doesn't know anything about constellations, but that doesn't change the fact that they're beautiful.
"I went to this lecture last week," Kara says, and Selene lolls her head to the side to watch the words come out in a puff of smoke. "About, like, the multiverse? I think it was for astronomy and astrophysics. Real interesting."
She sticks her cigarette back in her mouth after that, and Selene gets stuck staring at her, all long black hair and spindly limbs and rough edges, even though she knows Kara won't continue until Selene acknowledges what she's said. Kara's always got off-the-wall stuff like this on her mind; the new apartment she moved into last spring is right off of the Princeton campus, and she's constantly sneaking into lecture halls even though she isn't enrolled. Selene's tried to talk some sense into her before, telling her that if she's so interested in what these professors have to say she should just buck up and apply already, or at least look into a community college. The advice always goes ignored. It frustrates Selene, because Kara could do so much better than a barista job and open-mic nights. Deep down, though, she thinks she gets it — Kara just wants to know things for herself. She doesn't want to have to prove it to anyone else.
"Tell me about it, then," Selene says finally, since Kara is almost down to her cigarette's filter and making no moves towards grabbing a new one. Selene's own smoke is smoldering in the ashtray they'd brought out, forgotten.
"Turns out the multiverse and the universe are different things. Which I guess I should have expected, but." Kara finally wisens up and puts out her stub of a cigarette, and once the smoke dissipates, Selene has no choice but to look her in the eye.
"So the universe, in theory, means everything, right? But we don't really use it that way. When we talk about the universe, we usually just mean the stuff that our Big Bang created. Because we're narcissists, or whatever."
"How do you remember all this?" Selene can't help but ask. "Do you take notes at these things?"
"What? No." Kara blinks at her, then seems to reconsider. "Sometimes I bring a notebook so it looks like I'm supposed to be there, but no, not usually. I don't write anything down. It just sticks."
"Right, sorry." Selene doesn't understand how Kara's mind works, not in all the years since they'd met in high school and certainly not now. That's probably how they've put up with each other for so long. "So, the multiverse?"
"The multiverse is a group of all those universes," Kara says. "Like, everything our Big Bang made, and everything every other Big Bang made in every nook and cranny of space. And since it's always getting bigger, the multiverse could basically be limitless."
"That's a lot of aliens." Selene squints up at the stars. She imagines them moving like UFOs.
"Yeah, we talked about that." Kara's eyes are wide as she stares up, like she expects the sky to open up and swallow her whole. It's incredible, how she never hides her wonder. "There could be aliens that look just like us. More likely than not, actually."
"You're talking about parallel universes." Selene's watched the Discovery Channel before, okay.
"Yeah." Kara grins at her, and that alone sends a rush of satisfaction through Selene that makes her toes curl. "God, I love that idea. All those possibilities branching off of what we know? Off of each other? There could be so much out there, and the version we're living is just one of the infinite sides of the dice."
"Die. Singular," Selene corrects automatically, then feels like a jerk. She looks away and reaches for her pack to distract herself. She's less twitchy after she lights up and takes a drag. "You really think there are parallels of us?"
"Wouldn't it be kind of self-centered if I didn't?"
Kara is probably the first person ever to tie egotism and parallel universe theory together, and Selene takes a second to
listen to her own heartbeat in her ears, because God. Kara shouldn't still be fascinating after all this time. She shouldn't.
"The professor didn't talk about this at the lecture, but." When their eyes meet again, there's something imploring in Kara's, and Selene recognizes the shift in her voice. This is the part where Kara runs out of facts and turns to her own mind instead, where she becomes vulnerable. This is the part where Selene gets to see the side of her that she bottles up until Saturday nights, all the thoughts she keeps close to her chest and under her pillow and tangled up in the wires of her own head. "I think there's something nihilistic to that. We could get struck by lightning and turn into superheroes and save the world here, but in the universe next door, we might be grocery baggers. Whatever we do, we're doing it differently somewhere else. So however this ends up, it doesn't really matter."
As soon as she's done talking, she looks a little lost, and Selene has to ball up the hem of her shirt in her fist to keep from reaching out and touching her. It's easier once the expression wipes clear, but the wisp of hair falling over her eye is still ridiculously tempting. Selene bites the inside of her cheek.
It takes her a minute to realize Kara is waiting for her to speak. She feels heat on the back of her neck and hopes her silence wasn't too obvious. "Yeah, that could be true."
She must not pull it off, or maybe Kara's known her too long. "You think it's bullshit."
"I guess I'm more of an optimist than you." Which is something Selene never thought she'd say. It's easier, though, sweeter on her tongue than what she really feels: that parallel universes or not, lightning-struck superheroes or not, what they have here matters. Selene doesn't know how it possibly couldn't, not with the toes of Kara's battered Converse swinging just out of reach of Selene's own and the way her eyes look like liquid gold in the light of Selene's cherry, not with the end-of-autumn breeze giving them goosebumps and how Kara is curled in on herself against it, almost post-coital.
This matters, whether Kara likes it or not. The other versions of them will just have to deal with it.
"If you could look through the mirror," Kara asks, "what do you think our parallels are doing?"
Selene's mind immediately flicks through a dozen different scenarios she can't fathom saying out loud. "Desk jobs, probably," she says, and hopes it won't get called out for the cover it is.
Kara lets out a little snort even though it was a bad joke, and Selene wonders, wildly, if there's any parallel version of herself that isn't as desperately in love. The thought makes her heart clench. It's not fair. It's just not fair.
Maybe there are versions of them where things went the right way. Versions where Kara dated boys in high school, and maybe Selene tried that out too, but they still went to prom together and made out in the backseat of this very Impala. Versions where they didn't even meet until they were thirty, but then it was love at first sight, and they got married in a tiny field in upstate New York and honeymooned in Paris. Versions where they were each other's girl-next-door, and then came knocking that sticky-sweet summer romance. Versions where Selene somehow plucked up the courage and said something years ago like she should have, instead of watching every guy come and go from Kara's life and knowing it's too late.
She doesn't even need that. She'd readily take a version where she never falls in the first place. So long as it stops her throat from going dry every time Kara so much as smiles.
She must be making a face, because Kara says, "What're you thinking about?"
"That it's fucking cold out," Selene answers, because she's never going to be brave. Not in this universe, not in the next. "Drive me home?"
LIST ITEM TITLE
Item Subtitle
This is your item description. Use this space to add a description of the services, products, team members or any other items you want to highlight on your site. Have a lot to say? Easily turn any item into a full page by clicking 'Create a page from this item' in the edit panel.

redpajama_set_name: RedPajamaCommonCrawl


Curling is a part of who I am. If it wasn't for curling I would not have met many of my friends, I wouldn't have been able to travel the world, and I wouldn't be the same person I am today. The previous six months of my life leading up to these championships have given me memories for a lifetime that I would never have had if it weren't for the sport of curling.


text:
Hålland är en småort i Undersåkers distrikt (Undersåkers socken) i Jämtland, Åre kommun, belägen längs med E14 mellan Järpen och Undersåker, knappt två mil öster om Åre.

Orten är känd bland annat för Åredalens folkhögskola, som fram till skolans 75-årsjubileum 2014 hette Hållands folkhögskola.

Strax utanför Hålland finns även Ristafallet, ett av Sveriges största vattenfall.

Hålland på film
En del av filmen Ronja Rövardotter spelades in här vid Ristafallet.

Källor

Orter i Åre kommun
Småorter i Sverige

redpajama_set_name: RedPajamaWikipedia


text:
package com.intellij.spring.model.xml.highlighting;

import com.intellij.spring.facet.SpringFileSet;
import com.intellij.spring.model.xml.SpringHighlightingTestCase;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import org.jetbrains.annotations.NonNls;

public class RequiredPropertyTest extends SpringHighlightingTestCase<JavaModuleFixtureBuilder> {

  protected boolean isWithTestSources() {
    return false;
  }

  protected void configureModule(final JavaModuleFixtureBuilder moduleBuilder) throws Exception {
    super.configureModule(moduleBuilder);

    moduleBuilder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15);

    addSpring_2_5_Library(moduleBuilder);
  }

  public void testRequiredAnnotation() throws Throwable {
    final SpringFileSet fileSet = configureFileSet();
    addFileToSet(fileSet, "required_prop.xml");
    myFixture.testHighlighting(true, false, false, "RequiredBean.java");
  }

  public void testRequiredProperty() throws Throwable {
    final SpringFileSet fileSet = configureFileSet();
    addFileToSet(fileSet, "required.xml");
    myFixture.copyFileToProject("AnotherBean.java");
    myFixture.testHighlighting(true, false, false, "required.xml");
  }

  @NonNls
  public String getBasePath() {
    return "/svnPlugins/spring/spring-tests/testData/highlighting/required/";
  }
}