<commit_before>"""
Tray icon for udiskie.
"""

from collections import namedtuple
from functools import partial
from itertools import chain

from gi.repository import Gtk

from udiskie.common import setdefault

__all__ = ['UdiskieMenu', 'SmartUdiskieMenu', 'TrayIcon']

# data structs containing the menu hierarchy:
Node = namedtuple('Node', ['root', 'branches', 'device', 'label', 'methods'])
Action = namedtuple('Action', ['label', 'device', 'method'])
Branch = namedtuple('Branch', ['label', 'groups'])

class UdiskieMenu(object):

    """
    Builder for udiskie menus.

    Objects of this class generate action menus when being called.
    """

    _menu_icons = {
        'browse': Gtk.STOCK_OPEN,
        'mount': 'udiskie-mount',
        'unmount': 'udiskie-unmount',
        'unlock': 'udiskie-unlock',
        'lock': 'udiskie-lock',
        'eject': 'udiskie-eject',
        'detach': 'udiskie-detach',
        'quit': Gtk.STOCK_QUIT, }

    _menu_labels = {
        'browse': 'Browse %s',
        'mount': 'Mount %s',
        'unmount': 'Unmount %s',
        'unlock': 'Unlock %s',
        'lock': 'Lock %s',
        'eject': 'Eject %s',
        'detach': 'Unpower %s',
        'quit': 'Quit', }

    def __init__(self, mounter, actions={}):
        """
        Initialize a new menu maker.

        :param object mounter: mount operation provider
        :param dict actions: actions for menu items
        :returns: a new menu maker
        :rtype: cls

        Required keys for the ``_menu_labels``, ``_menu_icons`` and
        ``actions`` dictionaries are:

            - browse    Open mount location
            - mount     Mount a device
            - unmount   Unmount a device
            - unlock    Unlock a LUKS device
            - lock      Lock a LUKS device
            - eject     Eject a drive
            - detach    Detach (power down) a drive
            - quit      Exit the application

        NOTE: If using a main loop other than ``Gtk.main`` the 'quit' action
        must be customized.
        """
        self._mounter = mounter
        setdefault(actions, {
            'browse': mounter.browse,
            'mount': mounter.mount,
            'unmount': mounter.unmount,
            'unlock': mounter.unlock,
            'lock': partial(mounter.remove, force=True),
            'eject': partial(mounter.eject, force=True),
            'detach': partial(mounter.detach, force=True),
            'quit': Gtk.main_quit, })
        self._actions = actions

    def __call__(self):
        """
        Create menu for udiskie mount operations.

        :returns: a new menu
        :rtype: Gtk.Menu
        """
        # create actions items
        menu = self._branchmenu(self._prepare_menu(self.detect()).groups)
        # append menu item for closing the application
        if self._actions.get('quit'):
            if len(menu) > 0:
                menu.append(Gtk.SeparatorMenuItem())
            menu.append(self._actionitem('quit'))
        return menu

    def detect(self):
        """
        Detect all currently known devices.

        :returns: root of device hierarchy
        :rtype: Node
        """
        root = Node(None, [], None, "", [])
        device_nodes = dict(map(self._device_node,
                                self._mounter.get_all_handleable()))
        # insert child devices as branches into their roots:
        for object_path, node in device_nodes.items():
            device_nodes.get(node.root, root).branches.append(node)
        return root

    def _branchmenu(self, groups):
        """
        Create a menu from the given node.

        :param Branch groups: contains information about the menu
        :returns: a new menu object holding all groups of the node
        :rtype: Gtk.Menu
        """
        menu = Gtk.Menu()
        separate = False
        for group in groups:
            if len(group) > 0:
                if separate:
                    menu.append(Gtk.SeparatorMenuItem())
                separate = True
            for node in group:
                if isinstance(node, Action):
                    menu.append(self._actionitem(
                        node.method,
                        feed=[node.label],
                        bind=[node.device]))
                elif isinstance(node, Branch):
                    menu.append(self._menuitem(
                        node.label,
                        icon=None,
                        onclick=self._branchmenu(node.groups)))
                else:
                    raise ValueError("Invalid node!")
        return menu

    def _menuitem(self, label, icon, onclick):
        """
        Create a generic menu item.

        :param str label: text
        :param Gtk.Image icon: icon (may be ``None``)
        :param onclick: onclick handler, either a callable or Gtk.Menu
        :returns: the menu item object
        :rtype: Gtk.MenuItem
        """
        if icon is None:
            item = Gtk.MenuItem()
        else:
            try:
                item = Gtk.ImageMenuItem(stock_id=icon)
            except TypeError:
                item = Gtk.ImageMenuItem()
                item.set_image(icon)
        if label is not None:
            item.set_label(label)
        if isinstance(onclick, Gtk.Menu):
            item.set_submenu(onclick)
        else:
            item.connect('activate', onclick)
        return item

    def _actionitem(self, action, feed=(), bind=()):
        """
        Create a menu item for the specified action.

        :param str action: name of the action
        :param tuple feed: parameters for the label text
        :param tuple bind: parameters for the onclick handler
        :returns: the menu item object
        :rtype: Gtk.MenuItem
        """
        return self._menuitem(
            self._menu_labels[action] % tuple(feed),
            self._get_icon(action),
            lambda _: self._actions[action](*bind))

    def _device_node(self, device):
        """Create an empty menu node for the specified device."""
        label = device.id_label or device.device_presentation
        # determine available methods
        methods = []
        def append(method):
            if self._actions[method]:
                methods.append(method)
        if device.is_filesystem:
            if device.is_mounted:
                append('browse')
                append('unmount')
            else:
                append('mount')
        elif device.is_crypto:
            if device.is_unlocked:
                append('lock')
            else:
                append('unlock')
        if device.is_ejectable and device.has_media:
            append('eject')
        if device.is_detachable:
            append('detach')
        # find the root device:
        if device.is_partition:
            root = device.partition_slave.object_path
        elif device.is_luks_cleartext:
            root = device.luks_cleartext_slave.object_path
        else:
            root = None
        # in this first step leave branches empty
        return device.object_path, Node(root, [], device, label, methods)

    def _prepare_menu(self, node):
        """
        Prepare the menu hierarchy from the given device tree.

        :param Node node: root node of device hierarchy
        :returns: menu hierarchy
        :rtype: Branch
        """
        return Branch(
            label=node.label,
            groups=[
                [self._prepare_menu(branch)
                 for branch in node.branches],
                [Action(node.label, node.device, method)
                 for method in node.methods],
            ])

    def _get_icon(self, name):
        """
        Load menu icons dynamically.

        :param str name: name of the menu item
        :returns: the loaded icon
        :rtype: Gtk.Image
        """
        return Gtk.Image.new_from_icon_name(self._menu_icons[name],
                                            Gtk.IconSize.MENU)

class SmartUdiskieMenu(UdiskieMenu):

    def _actions_group(self, node, presentation):
        """
        Create the actions group for the specified device node.

        :param Node node: device
        :param str presentation: node label
        """
        return [Action(presentation, node.device, method)
                for method in node.methods]

    def _leaves_group(self, node, outer_methods, presentation):
        """
        Create groups for the specified node.

        :param Node node: device
        :param list outer_methods: mix-in methods of root device
        :param str presentation: node label
        """
        if not presentation or (node.device.is_mounted or
                                not node.device.is_luks_cleartext):
            presentation = node.label
        if node.branches:
            return chain.from_iterable(
                self._leaves_group(
                    branch,
                    self._actions_group(node, presentation) + outer_methods,
                    presentation)
                for branch in node.branches)
        elif len(node.methods) + len(outer_methods) > 0:
            return Branch(
                label=presentation,
                groups=[list(chain(self._actions_group(node, presentation),
                                   outer_methods))]),
        else:
            return ()

    def _prepare_menu(self, node):
        """Overrides UdiskieMenu._prepare_menu."""
        return Branch(
            label=node.label,
            groups=[list(self._leaves_group(node, [], ""))])

class TrayIcon(object):

    """Default TrayIcon class."""

    def __init__(self, menumaker, statusicon=None):
        """
        Create and show a simple Gtk.StatusIcon.

        :param UdiskieMenu menumaker: menu factory
        :param Gtk.StatusIcon statusicon: status icon
        """
        self._icon = statusicon or self._create_statusicon()
        self._menu = menumaker
        self._conn_left = None
        self._conn_right = None
        self.show()

    @classmethod
    def _create_statusicon(self):
        """Return a new Gtk.StatusIcon."""
        statusicon = Gtk.StatusIcon()
        statusicon.set_from_stock(Gtk.STOCK_CDROM)
        statusicon.set_tooltip_text("udiskie")
        return statusicon

    @property
    def visible(self):
        """Return visibility state of icon."""
        return bool(self._conn_left)

    def show(self, show=True):
        """Show or hide the tray icon."""
        if show:
            if not self.visible:
                self._show()
        else:
            self.hide()

    def hide(self):
        """Hide the tray icon."""
        if self.visible:
            self._hide()

    def _show(self):
        """Show the tray icon."""
        self._icon.set_visible(True)
        self._conn_left = self._icon.connect("activate", self._left_click_event)
        self._conn_right = self._icon.connect("popup-menu", self._right_click_event)

    def _hide(self):
        """Hide the tray icon."""
        self._icon.set_visible(False)
        self._icon.disconnect(self._conn_left)
        self._icon.disconnect(self._conn_right)
        self._conn_left = None
        self._conn_right = None

    def create_context_menu(self):
        """Create the context menu."""
        return self._menu()

    def _left_click_event(self, icon):
        """Handle a left click event (show the menu)."""
        m = self.create_context_menu()
        m.show_all()
        m.popup(parent_menu_shell=None,
                parent_menu_item=None,
                func=icon.position_menu,
                data=icon,
                button=0,
                activate_time=Gtk.get_current_event_time())
        # need to store reference or menu will be destroyed before showing:
        self._m = m

    def _right_click_event(self, icon, button, time):
        """Handle a right click event (show the menu)."""
        m = self.create_context_menu()
        m.show_all()
        m.popup(parent_menu_shell=None,
                parent_menu_item=None,
                func=icon.position_menu,
                data=icon,
                button=button,
                activate_time=time)
        # need to store reference or menu will be destroyed before showing:
        self._m = m

class AutoTray(TrayIcon):

    """
    TrayIcon that automatically hides.

    The menu has no 'Quit' item, and the tray icon will automatically hide
    if there is no action available.
    """

    def __init__(self, menumaker):
        """
        Create and automatically set visibility of a new status icon.

        Overrides TrayIcon.__init__.
        """
        # The reason to overwrite TrayIcon.__init__ is that the AutoTray
        # icon may need to be hidden at initialization time. When creating a
        # Gtk.StatusIcon, it will initially be visible, creating a minor
        # nuisance.
        self._icon = None
        self._menu = menumaker
        self._conn_left = None
        self._conn_right = None
        # Okay, the following is BAD:
        menumaker._actions['quit'] = None
        menumaker._mounter.udisks.connect_all(self)
        self.show(self.has_menu())

    def _show(self):
        """Extends TrayIcon._show: create a new status icon."""
        self._icon = self._create_statusicon()
        super(AutoTray, self)._show()

    def _hide(self):
        """Extends TrayIcon._hide: forget the status icon."""
        super(AutoTray, self)._hide()
        self._icon = None

    def has_menu(self):
        """Check if a menu action is available."""
        return any(self._menu._prepare_menu(self._menu.detect()).groups)

    def device_changed(self, old_state, new_state):
        """Update visibility."""
        self.show(self.has_menu())

    def device_added(self, device):
        """Update visibility."""
        self.show(self.has_menu())

    def device_removed(self, device):
        """Update visibility."""
        self.show(self.has_menu())
<commit_msg>Force menu image<commit_after>"""
Tray icon for udiskie.
"""

from collections import namedtuple
from functools import partial
from itertools import chain

from gi.repository import Gtk

from udiskie.common import setdefault

__all__ = ['UdiskieMenu', 'SmartUdiskieMenu', 'TrayIcon']

# data structs containing the menu hierarchy:
Node = namedtuple('Node', ['root', 'branches', 'device', 'label', 'methods'])
Action = namedtuple('Action', ['label', 'device', 'method'])
Branch = namedtuple('Branch', ['label', 'groups'])

class UdiskieMenu(object):

    """
    Builder for udiskie menus.

    Objects of this class generate action menus when being called.
    """

    _menu_icons = {
        'browse': Gtk.STOCK_OPEN,
        'mount': 'udiskie-mount',
        'unmount': 'udiskie-unmount',
        'unlock': 'udiskie-unlock',
        'lock': 'udiskie-lock',
        'eject': 'udiskie-eject',
        'detach': 'udiskie-detach',
        'quit': Gtk.STOCK_QUIT, }

    _menu_labels = {
        'browse': 'Browse %s',
        'mount': 'Mount %s',
        'unmount': 'Unmount %s',
        'unlock': 'Unlock %s',
        'lock': 'Lock %s',
        'eject': 'Eject %s',
        'detach': 'Unpower %s',
        'quit': 'Quit', }

    def __init__(self, mounter, actions={}):
        """
        Initialize a new menu maker.

        :param object mounter: mount operation provider
        :param dict actions: actions for menu items
        :returns: a new menu maker
        :rtype: cls

        Required keys for the ``_menu_labels``, ``_menu_icons`` and
        ``actions`` dictionaries are:

            - browse    Open mount location
            - mount     Mount a device
            - unmount   Unmount a device
            - unlock    Unlock a LUKS device
            - lock      Lock a LUKS device
            - eject     Eject a drive
            - detach    Detach (power down) a drive
            - quit      Exit the application

        NOTE: If using a main loop other than ``Gtk.main`` the 'quit' action
        must be customized.
        """
        self._mounter = mounter
        setdefault(actions, {
            'browse': mounter.browse,
            'mount': mounter.mount,
            'unmount': mounter.unmount,
            'unlock': mounter.unlock,
            'lock': partial(mounter.remove, force=True),
            'eject': partial(mounter.eject, force=True),
            'detach': partial(mounter.detach, force=True),
            'quit': Gtk.main_quit, })
        self._actions = actions

    def __call__(self):
        """
        Create menu for udiskie mount operations.

        :returns: a new menu
        :rtype: Gtk.Menu
        """
        # create actions items
        menu = self._branchmenu(self._prepare_menu(self.detect()).groups)
        # append menu item for closing the application
        if self._actions.get('quit'):
            if len(menu) > 0:
                menu.append(Gtk.SeparatorMenuItem())
            menu.append(self._actionitem('quit'))
        return menu

    def detect(self):
        """
        Detect all currently known devices.

        :returns: root of device hierarchy
        :rtype: Node
        """
        root = Node(None, [], None, "", [])
        device_nodes = dict(map(self._device_node,
                                self._mounter.get_all_handleable()))
        # insert child devices as branches into their roots:
        for object_path, node in device_nodes.items():
            device_nodes.get(node.root, root).branches.append(node)
        return root

    def _branchmenu(self, groups):
        """
        Create a menu from the given node.

        :param Branch groups: contains information about the menu
        :returns: a new menu object holding all groups of the node
        :rtype: Gtk.Menu
        """
        menu = Gtk.Menu()
        separate = False
        for group in groups:
            if len(group) > 0:
                if separate:
                    menu.append(Gtk.SeparatorMenuItem())
                separate = True
            for node in group:
                if isinstance(node, Action):
                    menu.append(self._actionitem(
                        node.method,
                        feed=[node.label],
                        bind=[node.device]))
                elif isinstance(node, Branch):
                    menu.append(self._menuitem(
                        node.label,
                        icon=None,
                        onclick=self._branchmenu(node.groups)))
                else:
                    raise ValueError("Invalid node!")
        return menu

    def _menuitem(self, label, icon, onclick):
        """
        Create a generic menu item.

        :param str label: text
        :param Gtk.Image icon: icon (may be ``None``)
        :param onclick: onclick handler, either a callable or Gtk.Menu
        :returns: the menu item object
        :rtype: Gtk.MenuItem
        """
        if icon is None:
            item = Gtk.MenuItem()
        else:
            try:
                item = Gtk.ImageMenuItem(stock_id=icon)
            except TypeError:
                item = Gtk.ImageMenuItem()
                item.set_image(icon)
            # I don't really care for the "show icons only for nouns, not
            # for verbs" policy:
            item.set_always_show_image(True)
        if label is not None:
            item.set_label(label)
        if isinstance(onclick, Gtk.Menu):
            item.set_submenu(onclick)
        else:
            item.connect('activate', onclick)
        return item

    def _actionitem(self, action, feed=(), bind=()):
        """
        Create a menu item for the specified action.

        :param str action: name of the action
        :param tuple feed: parameters for the label text
        :param tuple bind: parameters for the onclick handler
        :returns: the menu item object
        :rtype: Gtk.MenuItem
        """
        return self._menuitem(
            self._menu_labels[action] % tuple(feed),
            self._get_icon(action),
            lambda _: self._actions[action](*bind))

    def _device_node(self, device):
        """Create an empty menu node for the specified device."""
        label = device.id_label or device.device_presentation
        # determine available methods
        methods = []
        def append(method):
            if self._actions[method]:
                methods.append(method)
        if device.is_filesystem:
            if device.is_mounted:
                append('browse')
                append('unmount')
            else:
                append('mount')
        elif device.is_crypto:
            if device.is_unlocked:
                append('lock')
            else:
                append('unlock')
        if device.is_ejectable and device.has_media:
            append('eject')
        if device.is_detachable:
            append('detach')
        # find the root device:
        if device.is_partition:
            root = device.partition_slave.object_path
        elif device.is_luks_cleartext:
            root = device.luks_cleartext_slave.object_path
        else:
            root = None
        # in this first step leave branches empty
        return device.object_path, Node(root, [], device, label, methods)

    def _prepare_menu(self, node):
        """
        Prepare the menu hierarchy from the given device tree.

        :param Node node: root node of device hierarchy
        :returns: menu hierarchy
        :rtype: Branch
        """
        return Branch(
            label=node.label,
            groups=[
                [self._prepare_menu(branch)
                 for branch in node.branches],
                [Action(node.label, node.device, method)
                 for method in node.methods],
            ])

    def _get_icon(self, name):
        """
        Load menu icons dynamically.

        :param str name: name of the menu item
        :returns: the loaded icon
        :rtype: Gtk.Image
        """
        return Gtk.Image.new_from_icon_name(self._menu_icons[name],
                                            Gtk.IconSize.MENU)

class SmartUdiskieMenu(UdiskieMenu):

    def _actions_group(self, node, presentation):
        """
        Create the actions group for the specified device node.

        :param Node node: device
        :param str presentation: node label
        """
        return [Action(presentation, node.device, method)
                for method in node.methods]

    def _leaves_group(self, node, outer_methods, presentation):
        """
        Create groups for the specified node.

        :param Node node: device
        :param list outer_methods: mix-in methods of root device
        :param str presentation: node label
        """
        if not presentation or (node.device.is_mounted or
                                not node.device.is_luks_cleartext):
            presentation = node.label
        if node.branches:
            return chain.from_iterable(
                self._leaves_group(
                    branch,
                    self._actions_group(node, presentation) + outer_methods,
                    presentation)
                for branch in node.branches)
        elif len(node.methods) + len(outer_methods) > 0:
            return Branch(
                label=presentation,
                groups=[list(chain(self._actions_group(node, presentation),
                                   outer_methods))]),
        else:
            return ()

    def _prepare_menu(self, node):
        """Overrides UdiskieMenu._prepare_menu."""
        return Branch(
            label=node.label,
            groups=[list(self._leaves_group(node, [], ""))])

class TrayIcon(object):

    """Default TrayIcon class."""

    def __init__(self, menumaker, statusicon=None):
        """
        Create and show a simple Gtk.StatusIcon.

        :param UdiskieMenu menumaker: menu factory
        :param Gtk.StatusIcon statusicon: status icon
        """
        self._icon = statusicon or self._create_statusicon()
        self._menu = menumaker
        self._conn_left = None
        self._conn_right = None
        self.show()

    @classmethod
    def _create_statusicon(self):
        """Return a new Gtk.StatusIcon."""
        statusicon = Gtk.StatusIcon()
        statusicon.set_from_stock(Gtk.STOCK_CDROM)
        statusicon.set_tooltip_text("udiskie")
        return statusicon

    @property
    def visible(self):
        """Return visibility state of icon."""
        return bool(self._conn_left)

    def show(self, show=True):
        """Show or hide the tray icon."""
        if show:
            if not self.visible:
                self._show()
        else:
            self.hide()

    def hide(self):
        """Hide the tray icon."""
        if self.visible:
            self._hide()

    def _show(self):
        """Show the tray icon."""
        self._icon.set_visible(True)
        self._conn_left = self._icon.connect("activate", self._left_click_event)
        self._conn_right = self._icon.connect("popup-menu", self._right_click_event)

    def _hide(self):
        """Hide the tray icon."""
        self._icon.set_visible(False)
        self._icon.disconnect(self._conn_left)
        self._icon.disconnect(self._conn_right)
        self._conn_left = None
        self._conn_right = None

    def create_context_menu(self):
        """Create the context menu."""
        return self._menu()

    def _left_click_event(self, icon):
        """Handle a left click event (show the menu)."""
        m = self.create_context_menu()
        m.show_all()
        m.popup(parent_menu_shell=None,
                parent_menu_item=None,
                func=icon.position_menu,
                data=icon,
                button=0,
                activate_time=Gtk.get_current_event_time())
        # need to store reference or menu will be destroyed before showing:
        self._m = m

    def _right_click_event(self, icon, button, time):
        """Handle a right click event (show the menu)."""
        m = self.create_context_menu()
        m.show_all()
        m.popup(parent_menu_shell=None,
                parent_menu_item=None,
                func=icon.position_menu,
                data=icon,
                button=button,
                activate_time=time)
        # need to store reference or menu will be destroyed before showing:
        self._m = m

class AutoTray(TrayIcon):

    """
    TrayIcon that automatically hides.

    The menu has no 'Quit' item, and the tray icon will automatically hide
    if there is no action available.
    """

    def __init__(self, menumaker):
        """
        Create and automatically set visibility of a new status icon.

        Overrides TrayIcon.__init__.
        """
        # The reason to overwrite TrayIcon.__init__ is that the AutoTray
        # icon may need to be hidden at initialization time. When creating a
        # Gtk.StatusIcon, it will initially be visible, creating a minor
        # nuisance.
        self._icon = None
        self._menu = menumaker
        self._conn_left = None
        self._conn_right = None
        # Okay, the following is BAD:
        menumaker._actions['quit'] = None
        menumaker._mounter.udisks.connect_all(self)
        self.show(self.has_menu())

    def _show(self):
        """Extends TrayIcon._show: create a new status icon."""
        self._icon = self._create_statusicon()
        super(AutoTray, self)._show()

    def _hide(self):
        """Extends TrayIcon._hide: forget the status icon."""
        super(AutoTray, self)._hide()
        self._icon = None

    def has_menu(self):
        """Check if a menu action is available."""
        return any(self._menu._prepare_menu(self._menu.detect()).groups)

    def device_changed(self, old_state, new_state):
        """Update visibility."""
        self.show(self.has_menu())

    def device_added(self, device):
        """Update visibility."""
        self.show(self.has_menu())

    def device_removed(self, device):
        """Update visibility."""
        self.show(self.has_menu())
<|endoftext|>


<commit_before># vim:set et sts=4 sw=4:
#
# ibus - The Input Bus
#
# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or(at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA

import gtk
import gtk.gdk as gdk
import gobject
import ibus
import icon as _icon
import os
import sys
import signal
from os import path
from ibus import interface
from languagebar import LanguageBar
from candidatepanel import CandidatePanel

from gettext import dgettext
_  = lambda a : dgettext("ibus", a)
N_ = lambda a : a

def show_uri(screen, link):
    try:
        gtk.show_uri(screen, link, 0)
    except:
        print >> sys.stderr, "pygtk do not support show_uri"

def url_hook(about, link, user_data):
    show_uri(about.get_screen(), link)

def email_hook(about, email, user_data):
    show_uri(about.get_screen(), "mailto:%s" % email)

gtk.about_dialog_set_url_hook(url_hook, None)
gtk.about_dialog_set_email_hook(email_hook, None)

class Panel(ibus.PanelBase):
    def __init__ (self, bus):
        super(Panel, self).__init__(bus)
        self.__bus = bus
        self.__config = self.__bus.get_config()
        self.__focus_ic = None
        self.__setup_pid = 0
        self.__prefix = os.getenv("IBUS_PREFIX")
        self.__data_dir = path.join(self.__prefix, "share", "ibus")
        self.__icons_dir = path.join(self.__data_dir, "icons")
        self.__ibus_icon = path.join(self.__icons_dir, "ibus.svg")
        self.__setup_cmd = path.join(self.__prefix, "bin", "ibus-setup")

        # hanlder signal
        signal.signal(signal.SIGCHLD, self.__sigchld_cb)

        # connect bus signal
        self.__config.connect("value-changed", self.__config_value_changed_cb)
        self.__config.connect("reloaded", self.__config_reloaded_cb)
        # self.__bus.config_add_watch("panel")

        # add icon search path
        icon_theme = gtk.icon_theme_get_default()
        icon_theme.prepend_search_path(self.__icons_dir)

        self.__language_bar = LanguageBar()
        self.__language_bar.connect("property-activate",
                        lambda widget, prop_name, prop_state: self.property_activate(prop_name, prop_state))
        self.__language_bar.connect("get-im-menu",
                        self.__get_im_menu_cb)
        self.__language_bar.focus_out()
        self.__language_bar.show_all()

        self.__candidate_panel = CandidatePanel()
        self.__candidate_panel.connect("cursor-up",
                        lambda widget: self.cursor_up())
        self.__candidate_panel.connect("cursor-down",
                        lambda widget: self.cursor_down())

        self.__status_icon = gtk.StatusIcon()
        self.__status_icon.connect("popup-menu", self.__status_icon_popup_menu_cb)
        self.__status_icon.connect("activate", self.__status_icon_activate_cb)
        self.__status_icon.set_from_file(self.__ibus_icon)
        self.__status_icon.set_tooltip(_("IBus input method framework"))
        self.__status_icon.set_visible(True)

        self.__config_load_lookup_table_orientation()
        self.__config_load_show()
        self.__config_load_custom_font()
        # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0)

    def set_cursor_location(self, x, y, w, h):
        self.__candidate_panel.set_cursor_location(x + w, y + h)

    def update_preedit_text(self, text, cursor_pos, visible):
        self.__candidate_panel.update_preedit_text(text, cursor_pos, visible)

    def show_preedit_text(self):
        self.__candidate_panel.show_preedit_text()

    def hide_preedit_text(self):
        self.__candidate_panel.hide_preedit_text()

    def update_auxiliary_text(self, text, visible):
        self.__candidate_panel.update_auxiliary_text(text, visible)

    def show_auxiliary_text(self):
        self.__candidate_panel.show_auxiliary_text()

    def hide_auxiliary_text(self):
        self.__candidate_panel.hide_auxiliary_text()

    def update_lookup_table(self, lookup_table, visible):
        self.__candidate_panel.update_lookup_table(lookup_table, visible)

    def show_lookup_table(self):
        self.__candidate_panel.show_lookup_table()

    def hide_lookup_table(self):
        self.__candidate_panel.hide_lookup_table()

    def page_up_lookup_table(self):
        self.__candidate_panel.page_up_lookup_table()

    def page_down_lookup_table(self):
        self.__candidate_panel.page_down_lookup_table()

    def cursor_up_lookup_table(self):
        self.__candidate_panel.cursor_up_lookup_table()

    def cursor_down_lookup_table(self):
        self.__candidate_panel.cursor_down_lookup_table()

    def show_candidate_window(self):
        self.__candidate_panel.show_all()

    def hide_candidate_window(self):
        self.__candidate_panel.hide_all()

    def show_language_bar(self):
        self.__language_bar.show_all()

    def hide_language_bar(self):
        self.__language_bar.hide_all()

    def register_properties(self, props):
        self.__language_bar.register_properties(props)

    def update_property(self, prop):
        self.__language_bar.update_property(prop)

    def get_status_icon(self):
        return self.__status_icon

    def __set_im_icon(self, icon_name):
        if not icon_name:
            icon_name = "engine-default"
        self.__language_bar.set_im_icon(icon_name)
        if icon_name.startswith("/"):
            self.__status_icon.set_from_file(icon_name)
        else:
            self.__status_icon.set_from_icon_name(icon_name)

    def focus_in(self, ic):
        self.reset()
        self.__focus_ic = ibus.InputContext(self.__bus, ic)
        enabled = self.__focus_ic.is_enabled()
        self.__language_bar.set_enabled(enabled)

        if not enabled:
            self.__set_im_icon(self.__ibus_icon)
        else:
            engine = self.__focus_ic.get_engine()
            if engine:
                self.__set_im_icon(engine.icon)
            else:
                self.__set_im_icon(self.__ibus_icon)
        self.__language_bar.focus_in()

    def focus_out(self, ic):
        self.reset()
        self.__focus_ic = None
        self.__language_bar.set_enabled(False)
        self.__language_bar.focus_out()
        self.__set_im_icon(self.__ibus_icon)

    def state_changed(self):
        if not self.__focus_ic:
            return

        enabled = self.__focus_ic.is_enabled()
        self.__language_bar.set_enabled(enabled)

        if enabled == False:
            self.reset()
            self.__set_im_icon(self.__ibus_icon)
        else:
            engine = self.__focus_ic.get_engine()
            if engine:
                self.__set_im_icon(engine.icon)
            else:
                self.__set_im_icon(self.__ibus_icon)

    def reset(self):
        self.__candidate_panel.reset()
        self.__language_bar.reset()

    def start_setup(self):
        self.__start_setup()

    def do_destroy(self):
        gtk.main_quit()

    def __config_load_lookup_table_orientation(self):
        value = self.__config.get_value("panel", "lookup_table_orientation", 0)
        if value != 0 and value != 1:
            value = 0
        if value == 0:
            self.__candidate_panel.set_orientation(gtk.ORIENTATION_HORIZONTAL)
        else:
            self.__candidate_panel.set_orientation(gtk.ORIENTATION_VERTICAL)

    def __config_load_show(self):
        show = self.__config.get_value("panel", "show", 1)
        self.__language_bar.set_show(show)

    def __config_load_custom_font(self):
        use_custom_font = self.__config.get_value("panel", "use_custom_font", False)
        font_name = gtk.settings_get_default().get_property("gtk-font-name")
        font_name = unicode(font_name, "utf-8")
        custom_font =  self.__config.get_value("panel", "custom_font", font_name)
        style_string = 'style "custom-font" { font_name="%s" }\n' \
            'class "IBusPanelLabel" style "custom-font"\n'
        if use_custom_font:
            style_string = style_string % custom_font
            gtk.rc_parse_string(style_string)
        else:
            style_string = style_string % ""
            gtk.rc_parse_string(style_string)

        settings = gtk.settings_get_default()
        gtk.rc_reset_styles(settings)

    def __config_value_changed_cb(self, bus, section, name, value):
        if section != "panel":
            return
        if name == "lookup_table_orientation":
            self.__config_load_lookup_table_orientation()
        elif name == "show":
            self.__config_load_show()
        elif name == "use_custom_font" or name == "custom_font":
            self.__config_load_custom_font()

    def __config_reloaded_cb(self, bus):
        pass

    def __create_sys_menu(self):
        menu = gtk.Menu()
        item = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
        item.connect("activate",
            self.__sys_menu_item_activate_cb, gtk.STOCK_PREFERENCES)
        menu.add(item)
        item = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
        item.connect("activate",
            self.__sys_menu_item_activate_cb, gtk.STOCK_ABOUT)
        menu.add(item)
        menu.add(gtk.SeparatorMenuItem())
        item = gtk.MenuItem(_("Restart"))
        item.connect("activate",
            self.__sys_menu_item_activate_cb, "Restart")
        menu.add(item)
        item = gtk.ImageMenuItem(gtk.STOCK_QUIT)
        item.connect("activate",
            self.__sys_menu_item_activate_cb, gtk.STOCK_QUIT)
        menu.add(item)

        menu.show_all()
        menu.set_take_focus(False)
        return menu

    def __create_im_menu(self):
        menu = gtk.Menu()
        engines = self.__bus.list_active_engines()

        tmp = {}
        for engine in engines:
            lang = ibus.get_language_name(engine.language)
            if lang not in tmp:
                tmp[lang] = []
            tmp[lang].append(engine)

        langs = tmp.keys()
        other = tmp.get(_("Other"), [])
        if _("Other") in tmp:
            langs.remove(_("Other"))
            langs.append(_("Other"))

        size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
        for lang in langs:
            if len(tmp[lang]) == 1:
                engine = tmp[lang][0]
                item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
                if engine.icon:
                    item.set_image (_icon.IconWidget(engine.icon, size[0]))
                else:
                    item.set_image (_icon.IconWidget("engine-default", size[0]))
                item.connect("activate", self.__im_menu_item_activate_cb, engine)
                menu.add(item)
            else:
                item = gtk.MenuItem(lang)
                menu.add(item)
                submenu = gtk.Menu()
                item.set_submenu(submenu)
                for engine in tmp[lang]:
                    item = gtk.ImageMenuItem(engine.longname)
                    if engine.icon:
                        item.set_image (_icon.IconWidget(engine.icon, size[0]))
                    else:
                        item.set_image (_icon.IconWidget("engine-default", size[0]))
                    item.connect("activate", self.__im_menu_item_activate_cb, engine)
                    submenu.add(item)

        item = gtk.ImageMenuItem(_("No input method"))
        item.set_image (_icon.IconWidget("gtk-close", size[0]))
        item.connect("activate", self.__im_menu_item_activate_cb, None)
        menu.add (item)

        menu.show_all()
        menu.set_take_focus(False)
        return menu

    def __get_im_menu_cb(self, languagebar):
        menu = self.__create_im_menu()
        return menu

    def __status_icon_popup_menu_cb(self, status_icon, button, active_time):
        menu = self.__create_sys_menu()
        menu.popup(None, None,
                gtk.status_icon_position_menu,
                button,
                active_time,
                self.__status_icon)

    def __status_icon_activate_cb(self, status_icon):
        if not self.__focus_ic:
            return
        menu = self.__create_im_menu()
        menu.popup(None, None,
                gtk.status_icon_position_menu,
                0,
                gtk.get_current_event_time(),
                self.__status_icon)

    def __im_menu_item_activate_cb(self, item, engine):
        if engine:
            self.__focus_ic.set_engine(engine)
        else:
            self.__focus_ic.disable()

    def __sys_menu_item_activate_cb(self, item, command):
        if command == gtk.STOCK_PREFERENCES:
            self.__start_setup()
        elif command == gtk.STOCK_ABOUT:
            about_dialog = gtk.AboutDialog()
            about_dialog.set_program_name("IBus")
            about_dialog.set_version(ibus.get_version())
            about_dialog.set_copyright(ibus.get_copyright())
            about_dialog.set_license(ibus.get_license())
            about_dialog.set_comments(_("IBus is an intelligent input bus for Linux/Unix."))
            about_dialog.set_website("http://code.google.com/p/ibus")
            about_dialog.set_authors(["Huang Peng <shawn.p.huang@gmail.com>"])
            about_dialog.set_documenters(["Huang Peng <shawn.p.huang@gmail.com>"])
            about_dialog.set_translator_credits(_("translator-credits"))
            about_dialog.set_logo_icon_name("ibus")
            about_dialog.run()
            about_dialog.destroy()
        elif command == gtk.STOCK_QUIT:
            self.__bus.exit(False)
        elif command == "Restart":
            self.__bus.exit(True)
        else:
            print >> sys.stderr, "Unknown command %s" % command

    def __sigchld_cb(self, sig, sf):
        pid, status = os.wait()
        if self.__setup_pid == pid:
            self.__setup_pid = 0

    def __start_setup(self):
        if self.__setup_pid != 0:
            pid, state = os.waitpid(self.__setup_pid, os.P_NOWAIT)
            if pid != self.__setup_pid:
                os.kill(self.__setup_pid, signal.SIGUSR1)
                return
            self.__setup_pid = 0
        self.__setup_pid = os.spawnl(os.P_NOWAIT, self.__setup_cmd, "ibus-setup")

gobject.type_register(Panel, "IBusPanel")
<commit_msg>Catch exception __sigchld_cb in panel.py<commit_after># vim:set et sts=4 sw=4:
#
# ibus - The Input Bus
#
# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or(at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA

import gtk
import gtk.gdk as gdk
import gobject
import ibus
import icon as _icon
import os
import sys
import signal
from os import path
from ibus import interface
from languagebar import LanguageBar
from candidatepanel import CandidatePanel

from gettext import dgettext
_  = lambda a : dgettext("ibus", a)
N_ = lambda a : a

def show_uri(screen, link):
    try:
        gtk.show_uri(screen, link, 0)
    except:
        print >> sys.stderr, "pygtk do not support show_uri"

def url_hook(about, link, user_data):
    show_uri(about.get_screen(), link)

def email_hook(about, email, user_data):
    show_uri(about.get_screen(), "mailto:%s" % email)

gtk.about_dialog_set_url_hook(url_hook, None)
gtk.about_dialog_set_email_hook(email_hook, None)

class Panel(ibus.PanelBase):
    def __init__ (self, bus):
        super(Panel, self).__init__(bus)
        self.__bus = bus
        self.__config = self.__bus.get_config()
        self.__focus_ic = None
        self.__setup_pid = 0
        self.__prefix = os.getenv("IBUS_PREFIX")
        self.__data_dir = path.join(self.__prefix, "share", "ibus")
        self.__icons_dir = path.join(self.__data_dir, "icons")
        self.__ibus_icon = path.join(self.__icons_dir, "ibus.svg")
        self.__setup_cmd = path.join(self.__prefix, "bin", "ibus-setup")

        # hanlder signal
        signal.signal(signal.SIGCHLD, self.__sigchld_cb)

        # connect bus signal
        self.__config.connect("value-changed", self.__config_value_changed_cb)
        self.__config.connect("reloaded", self.__config_reloaded_cb)
        # self.__bus.config_add_watch("panel")

        # add icon search path
        icon_theme = gtk.icon_theme_get_default()
        icon_theme.prepend_search_path(self.__icons_dir)

        self.__language_bar = LanguageBar()
        self.__language_bar.connect("property-activate",
                        lambda widget, prop_name, prop_state: self.property_activate(prop_name, prop_state))
        self.__language_bar.connect("get-im-menu",
                        self.__get_im_menu_cb)
        self.__language_bar.focus_out()
        self.__language_bar.show_all()

        self.__candidate_panel = CandidatePanel()
        self.__candidate_panel.connect("cursor-up",
                        lambda widget: self.cursor_up())
        self.__candidate_panel.connect("cursor-down",
                        lambda widget: self.cursor_down())

        self.__status_icon = gtk.StatusIcon()
        self.__status_icon.connect("popup-menu", self.__status_icon_popup_menu_cb)
        self.__status_icon.connect("activate", self.__status_icon_activate_cb)
        self.__status_icon.set_from_file(self.__ibus_icon)
        self.__status_icon.set_tooltip(_("IBus input method framework"))
        self.__status_icon.set_visible(True)

        self.__config_load_lookup_table_orientation()
        self.__config_load_show()
        self.__config_load_custom_font()
        # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0)

    def set_cursor_location(self, x, y, w, h):
        self.__candidate_panel.set_cursor_location(x + w, y + h)

    def update_preedit_text(self, text, cursor_pos, visible):
        self.__candidate_panel.update_preedit_text(text, cursor_pos, visible)

    def show_preedit_text(self):
        self.__candidate_panel.show_preedit_text()

    def hide_preedit_text(self):
        self.__candidate_panel.hide_preedit_text()

    def update_auxiliary_text(self, text, visible):
        self.__candidate_panel.update_auxiliary_text(text, visible)

    def show_auxiliary_text(self):
        self.__candidate_panel.show_auxiliary_text()

    def hide_auxiliary_text(self):
        self.__candidate_panel.hide_auxiliary_text()

    def update_lookup_table(self, lookup_table, visible):
        self.__candidate_panel.update_lookup_table(lookup_table, visible)

    def show_lookup_table(self):
        self.__candidate_panel.show_lookup_table()

    def hide_lookup_table(self):
        self.__candidate_panel.hide_lookup_table()

    def page_up_lookup_table(self):
        self.__candidate_panel.page_up_lookup_table()

    def page_down_lookup_table(self):
        self.__candidate_panel.page_down_lookup_table()

    def cursor_up_lookup_table(self):
        self.__candidate_panel.cursor_up_lookup_table()

    def cursor_down_lookup_table(self):
        self.__candidate_panel.cursor_down_lookup_table()

    def show_candidate_window(self):
        self.__candidate_panel.show_all()

    def hide_candidate_window(self):
        self.__candidate_panel.hide_all()

    def show_language_bar(self):
        self.__language_bar.show_all()

    def hide_language_bar(self):
        self.__language_bar.hide_all()

    def register_properties(self, props):
        self.__language_bar.register_properties(props)

    def update_property(self, prop):
        self.__language_bar.update_property(prop)

    def get_status_icon(self):
        return self.__status_icon

    def __set_im_icon(self, icon_name):
        if not icon_name:
            icon_name = "engine-default"
        self.__language_bar.set_im_icon(icon_name)
        if icon_name.startswith("/"):
            self.__status_icon.set_from_file(icon_name)
        else:
            self.__status_icon.set_from_icon_name(icon_name)

    def focus_in(self, ic):
        self.reset()
        self.__focus_ic = ibus.InputContext(self.__bus, ic)
        enabled = self.__focus_ic.is_enabled()
        self.__language_bar.set_enabled(enabled)

        if not enabled:
            self.__set_im_icon(self.__ibus_icon)
        else:
            engine = self.__focus_ic.get_engine()
            if engine:
                self.__set_im_icon(engine.icon)
            else:
                self.__set_im_icon(self.__ibus_icon)
        self.__language_bar.focus_in()

    def focus_out(self, ic):
        self.reset()
        self.__focus_ic = None
        self.__language_bar.set_enabled(False)
        self.__language_bar.focus_out()
        self.__set_im_icon(self.__ibus_icon)

    def state_changed(self):
        if not self.__focus_ic:
            return

        enabled = self.__focus_ic.is_enabled()
        self.__language_bar.set_enabled(enabled)

        if enabled == False:
            self.reset()
            self.__set_im_icon(self.__ibus_icon)
        else:
            engine = self.__focus_ic.get_engine()
            if engine:
                self.__set_im_icon(engine.icon)
            else:
                self.__set_im_icon(self.__ibus_icon)

    def reset(self):
        self.__candidate_panel.reset()
        self.__language_bar.reset()

    def start_setup(self):
        self.__start_setup()

    def do_destroy(self):
        gtk.main_quit()

    def __config_load_lookup_table_orientation(self):
        value = self.__config.get_value("panel", "lookup_table_orientation", 0)
        if value != 0 and value != 1:
            value = 0
        if value == 0:
            self.__candidate_panel.set_orientation(gtk.ORIENTATION_HORIZONTAL)
        else:
            self.__candidate_panel.set_orientation(gtk.ORIENTATION_VERTICAL)

    def __config_load_show(self):
        show = self.__config.get_value("panel", "show", 1)
        self.__language_bar.set_show(show)

    def __config_load_custom_font(self):
        use_custom_font = self.__config.get_value("panel", "use_custom_font", False)
        font_name = gtk.settings_get_default().get_property("gtk-font-name")
        font_name = unicode(font_name, "utf-8")
        custom_font =  self.__config.get_value("panel", "custom_font", font_name)
        style_string = 'style "custom-font" { font_name="%s" }\n' \
            'class "IBusPanelLabel" style "custom-font"\n'
        if use_custom_font:
            style_string = style_string % custom_font
            gtk.rc_parse_string(style_string)
        else:
            style_string = style_string % ""
            gtk.rc_parse_string(style_string)

        settings = gtk.settings_get_default()
        gtk.rc_reset_styles(settings)

    def __config_value_changed_cb(self, bus, section, name, value):
        if section != "panel":
            return
        if name == "lookup_table_orientation":
            self.__config_load_lookup_table_orientation()
        elif name == "show":
            self.__config_load_show()
        elif name == "use_custom_font" or name == "custom_font":
            self.__config_load_custom_font()

    def __config_reloaded_cb(self, bus):
        pass

    def __create_sys_menu(self):
        menu = gtk.Menu()
        item = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
        item.connect("activate",
            self.__sys_menu_item_activate_cb, gtk.STOCK_PREFERENCES)
        menu.add(item)
        item = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
        item.connect("activate",
            self.__sys_menu_item_activate_cb, gtk.STOCK_ABOUT)
        menu.add(item)
        menu.add(gtk.SeparatorMenuItem())
        item = gtk.MenuItem(_("Restart"))
        item.connect("activate",
            self.__sys_menu_item_activate_cb, "Restart")
        menu.add(item)
        item = gtk.ImageMenuItem(gtk.STOCK_QUIT)
        item.connect("activate",
            self.__sys_menu_item_activate_cb, gtk.STOCK_QUIT)
        menu.add(item)

        menu.show_all()
        menu.set_take_focus(False)
        return menu

    def __create_im_menu(self):
        menu = gtk.Menu()
        engines = self.__bus.list_active_engines()

        tmp = {}
        for engine in engines:
            lang = ibus.get_language_name(engine.language)
            if lang not in tmp:
                tmp[lang] = []
            tmp[lang].append(engine)

        langs = tmp.keys()
        other = tmp.get(_("Other"), [])
        if _("Other") in tmp:
            langs.remove(_("Other"))
            langs.append(_("Other"))

        size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
        for lang in langs:
            if len(tmp[lang]) == 1:
                engine = tmp[lang][0]
                item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
                if engine.icon:
                    item.set_image (_icon.IconWidget(engine.icon, size[0]))
                else:
                    item.set_image (_icon.IconWidget("engine-default", size[0]))
                item.connect("activate", self.__im_menu_item_activate_cb, engine)
                menu.add(item)
            else:
                item = gtk.MenuItem(lang)
                menu.add(item)
                submenu = gtk.Menu()
                item.set_submenu(submenu)
                for engine in tmp[lang]:
                    item = gtk.ImageMenuItem(engine.longname)
                    if engine.icon:
                        item.set_image (_icon.IconWidget(engine.icon, size[0]))
                    else:
                        item.set_image (_icon.IconWidget("engine-default", size[0]))
                    item.connect("activate", self.__im_menu_item_activate_cb, engine)
                    submenu.add(item)

        item = gtk.ImageMenuItem(_("No input method"))
        item.set_image (_icon.IconWidget("gtk-close", size[0]))
        item.connect("activate", self.__im_menu_item_activate_cb, None)
        menu.add (item)

        menu.show_all()
        menu.set_take_focus(False)
        return menu

    def __get_im_menu_cb(self, languagebar):
        menu = self.__create_im_menu()
        return menu

    def __status_icon_popup_menu_cb(self, status_icon, button, active_time):
        menu = self.__create_sys_menu()
        menu.popup(None, None,
                gtk.status_icon_position_menu,
                button,
                active_time,
                self.__status_icon)

    def __status_icon_activate_cb(self, status_icon):
        if not self.__focus_ic:
            return
        menu = self.__create_im_menu()
        menu.popup(None, None,
                gtk.status_icon_position_menu,
                0,
                gtk.get_current_event_time(),
                self.__status_icon)

    def __im_menu_item_activate_cb(self, item, engine):
        if engine:
            self.__focus_ic.set_engine(engine)
        else:
            self.__focus_ic.disable()

    def __sys_menu_item_activate_cb(self, item, command):
        if command == gtk.STOCK_PREFERENCES:
            self.__start_setup()
        elif command == gtk.STOCK_ABOUT:
            about_dialog = gtk.AboutDialog()
            about_dialog.set_program_name("IBus")
            about_dialog.set_version(ibus.get_version())
            about_dialog.set_copyright(ibus.get_copyright())
            about_dialog.set_license(ibus.get_license())
            about_dialog.set_comments(_("IBus is an intelligent input bus for Linux/Unix."))
            about_dialog.set_website("http://code.google.com/p/ibus")
            about_dialog.set_authors(["Huang Peng <shawn.p.huang@gmail.com>"])
            about_dialog.set_documenters(["Huang Peng <shawn.p.huang@gmail.com>"])
            about_dialog.set_translator_credits(_("translator-credits"))
            about_dialog.set_logo_icon_name("ibus")
            about_dialog.run()
            about_dialog.destroy()
        elif command == gtk.STOCK_QUIT:
            self.__bus.exit(False)
        elif command == "Restart":
            self.__bus.exit(True)
        else:
            print >> sys.stderr, "Unknown command %s" % command

    def __sigchld_cb(self, sig, sf):
        try:
            pid, status = os.wait()
            if self.__setup_pid == pid:
                self.__setup_pid = 0
        except:
            pass

    def __start_setup(self):
        if self.__setup_pid != 0:
            pid, state = os.waitpid(self.__setup_pid, os.P_NOWAIT)
            if pid != self.__setup_pid:
                os.kill(self.__setup_pid, signal.SIGUSR1)
                return
            self.__setup_pid = 0
        self.__setup_pid = os.spawnl(os.P_NOWAIT, self.__setup_cmd, "ibus-setup")

gobject.type_register(Panel, "IBusPanel")
<|endoftext|>


<commit_before>import re, os, sys, time, sha, base64, threading, Queue

from pony.thirdparty import sqlite

ip_re = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')

def set_user(login, ip=None):
    if '\x00' in login: raise ValueError('Login must not contains null bytes')
    if ip and not ip_re.match(ip): raise ValueError('Ivalid IP value: %s' % ip)
    minute = long(time.time()) // 60
    return _make_session(login, minute, minute, ip)

def get_user(session_data, ip=None, max_last=20, max_first=24*60):
    mcurrent = long(time.time()) // 60
    try:
        data = base64.b64decode(session_data)
        login, mfirst_str, mlast_str, hashcode = data.split('\x00', 3)
        mfirst = int(mfirst_str, 16)
        mlast = int(mlast_str, 16)
        if mfirst < mcurrent - max_first: return False, None, None
        if mlast < mcurrent - max_last: return False, None, None
        if mlast > mcurrent + 2: return False, None, None
        secret = get_secret(mlast)
        if secret is None: return False, None, None
        shaobject = sha.new(login)
        shaobject.update(mfirst_str)
        shaobject.update(mlast_str)
        shaobject.update(secret)
        if hashcode != shaobject.digest():
            if not ip: return False, None, None
            shaobject.update(ip)
            if hashcode != shaobject.digest(): return False, None, None
        else: ip = None
        if mlast == mcurrent: return True, login, session_data
        return True, login, _make_session(login, mfirst, mcurrent, ip)
    except:
        return False, None, None

def _make_session(login, mfirst, mcurrent, ip=None):
    secret = get_secret(mcurrent)
    mfirst_str = '%x' % mfirst
    mcurrent_str = '%x' % mcurrent
    shaobject = sha.new(login)
    shaobject.update(mfirst_str)
    shaobject.update(mcurrent_str)
    shaobject.update(secret)
    if ip: shaobject.update(ip)
    data = '\x00'.join([ login, mfirst_str, mcurrent_str, shaobject.digest() ])
    return base64.b64encode(data)

secret_cache = {}
queue = Queue.Queue()

class Local(threading.local):
    def __init__(self):
        self.lock = threading.Lock()
        self.lock.acquire()

local = Local()

def get_secret(minute):
    secret = secret_cache.get(minute)
    if secret is None:
        queue.put((minute, local.lock))
        local.lock.acquire()
        secret = secret_cache[minute]
    return secret

def get_sessiondb_name():
    main = sys.modules['__main__']
    try: script_name = main.__file__
    except AttributeError:  # interactive mode
        return ':memory:'   # in-memory database
    head, tail = os.path.split(script_name)
    if tail == '__init__.py': return head + '-sessions.sqlite'
    else:
        root, ext = os.path.splitext(script_name)
        return root + '-sessions.sqlite'

sql_create = """
create table if not exists time_secrets (
    minute integer primary key,
    secret binary not null
    );
"""

class AuthThread(threading.Thread):
    def run(self):
        con = self.connnection = sqlite.connect(get_sessiondb_name())
        con.executescript(sql_create)
        for minute, secret in con.execute('select * from time_secrets'):
            secret_cache[minute] = str(secret)
        self.connnection.commit()
        while True:
            x = queue.get()
            if x is None: break
            minute, lock = x
            if minute in secret_cache:
                lock.release()
                continue
            sql_select = 'select secret from time_secrets where minute = ?'
            row = con.execute(sql_select, [minute]).fetchone()
            if row is not None:
                con.commit()
                secret_cache[minute] = str(row[0])
                lock.release()
                continue
            current_minute = long(time.time()) // 60
            con.execute('delete from time_secrets where minute < ?',
                        [ current_minute - 24*60 ])
            secret = os.urandom(32)
            con.execute('insert into time_secrets values(?, ?)',
                        [ minute, buffer(secret) ])
            con.commit()
            secret_cache[minute] = secret
            lock.release()

auth_thread = AuthThread()
auth_thread.start()
<commit_msg>get_user() now extract login name even for outdated or incorrect cookie<commit_after>import re, os, sys, time, sha, base64, threading, Queue

from pony.thirdparty import sqlite

ip_re = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')

def set_user(login, ip=None):
    if '\x00' in login: raise ValueError('Login must not contains null bytes')
    if ip and not ip_re.match(ip): raise ValueError('Ivalid IP value: %s' % ip)
    minute = long(time.time()) // 60
    return _make_session(login, minute, minute, ip)

def get_user(session_data, ip=None, max_last=20, max_first=24*60):
    mcurrent = long(time.time()) // 60
    login = None
    try:
        data = base64.b64decode(session_data)
        login, mfirst_str, mlast_str, hashcode = data.split('\x00', 3)
        mfirst = int(mfirst_str, 16)
        mlast = int(mlast_str, 16)
        if (mfirst < mcurrent - max_first or
            mlast < mcurrent - max_last or
            mlast > mcurrent + 2): return False, login, None
        secret = get_secret(mlast)
        if secret is None: return False, login, None
        shaobject = sha.new(login)
        shaobject.update(mfirst_str)
        shaobject.update(mlast_str)
        shaobject.update(secret)
        if hashcode != shaobject.digest():
            if not ip: return False, login, None
            shaobject.update(ip)
            if hashcode != shaobject.digest(): return False, login, None
        else: ip = None
        if mlast == mcurrent: return True, login, session_data
        return True, login, _make_session(login, mfirst, mcurrent, ip)
    except:
        return False, login, None

def _make_session(login, mfirst, mcurrent, ip=None):
    secret = get_secret(mcurrent)
    mfirst_str = '%x' % mfirst
    mcurrent_str = '%x' % mcurrent
    shaobject = sha.new(login)
    shaobject.update(mfirst_str)
    shaobject.update(mcurrent_str)
    shaobject.update(secret)
    if ip: shaobject.update(ip)
    data = '\x00'.join([ login, mfirst_str, mcurrent_str, shaobject.digest() ])
    return base64.b64encode(data)

secret_cache = {}
queue = Queue.Queue()

class Local(threading.local):
    def __init__(self):
        self.lock = threading.Lock()
        self.lock.acquire()

local = Local()

def get_secret(minute):
    secret = secret_cache.get(minute)
    if secret is None:
        queue.put((minute, local.lock))
        local.lock.acquire()
        secret = secret_cache[minute]
    return secret

def get_sessiondb_name():
    main = sys.modules['__main__']
    try: script_name = main.__file__
    except AttributeError:  # interactive mode
        return ':memory:'   # in-memory database
    head, tail = os.path.split(script_name)
    if tail == '__init__.py': return head + '-sessions.sqlite'
    else:
        root, ext = os.path.splitext(script_name)
        return root + '-sessions.sqlite'

sql_create = """
create table if not exists time_secrets (
    minute integer primary key,
    secret binary not null
    );
"""

class AuthThread(threading.Thread):
    def run(self):
        con = self.connnection = sqlite.connect(get_sessiondb_name())
        con.executescript(sql_create)
        for minute, secret in con.execute('select * from time_secrets'):
            secret_cache[minute] = str(secret)
        self.connnection.commit()
        while True:
            x = queue.get()
            if x is None: break
            minute, lock = x
            if minute in secret_cache:
                lock.release()
                continue
            sql_select = 'select secret from time_secrets where minute = ?'
            row = con.execute(sql_select, [minute]).fetchone()
            if row is not None:
                con.commit()
                secret_cache[minute] = str(row[0])
                lock.release()
                continue
            current_minute = long(time.time()) // 60
            con.execute('delete from time_secrets where minute < ?',
                        [ current_minute - 24*60 ])
            secret = os.urandom(32)
            con.execute('insert into time_secrets values(?, ?)',
                        [ minute, buffer(secret) ])
            con.commit()
            secret_cache[minute] = secret
            lock.release()

auth_thread = AuthThread()
auth_thread.start()
<|endoftext|>


<commit_before>"""
Stuff related to loading and rendering a level.
"""

# This code is so you can run the samples without installing the package
import math
import os
import random
import sys

import pyglet
from pyglet.window import key

from pytmx import tmxloader
import pymunk

import cocos
from cocos import tiles, actions, layer, sprite
from cocos.director import director

import settings

from collections import namedtuple

Vector = namedtuple('Vector', 'x y')

def main():

    pyglet.resource.path = settings.RESOURCE_PATH
    pyglet.resource.reindex()

    director.init(width=640, height=320, do_not_scale=True, resizable=True)

    scroller = layer.ScrollingManager()
    tmx = tiles.load('underground-level1.tmx')
    tilemap = tmx['map']

    tmx = tmxloader.load_tmx(pyglet.resource.file('underground-level1.tmx'))
    triggers = tmx['triggers']

    # physics,yo
    space = pymunk.Space()
    space.gravity = pymunk.Vec2d(0.0, settings.GRAVITY)

    # set up our hero cart.
    startcell, = triggers.find('player')
    cart_image = pyglet.resource.image("cart.png")
    pos = Vector(startcell.x + cart_image.width // 2, startcell.y + cart_image.height // 2)
    hero = make_sprite(image=cart_image, position=(pos.x, tilemap.px_height - pos.y))
    space.add(hero.body, hero.shape)

    # set up our blockers.
    blockers = triggers.find('blocker')
    walls = []
    static_body = pymunk.Body()
    for blocker in blockers:
        # turn the four corners of the 'blocker objects' defined in tilemap into rigid walls.
        c1 = Vector(blocker.x, tilemap.px_height - blocker.y)
        c2 = Vector(blocker.x + blocker.width, tilemap.px_height - blocker.y)
        c3 = Vector(blocker.x + blocker.width, (tilemap.px_height - blocker.y - blocker.height))
        c4 = Vector(blocker.x, (tilemap.px_height - blocker.y - blocker.height))

        # a wall is a block made from four lines joining the four corners.
        walls.extend([pymunk.Segment(static_body, c1, c2, 0),
                      pymunk.Segment(static_body, c2, c3, 0),
                      pymunk.Segment(static_body, c3, c4, 0),
                      pymunk.Segment(static_body, c4, c1, 0)])

    [setattr(wall, 'friction', 0.1) for wall in walls]
    space.add(walls)

    scroller.add(tilemap)

    main_scene = cocos.scene.Scene(scroller)
    main_scene.add(hero)

    def update(dt):
        space.step(dt)
        hero.set_position(*hero.body.position)

    pyglet.clock.schedule(update)

    keyboard = key.KeyStateHandler()
    director.window.push_handlers(keyboard)

    def on_key_press(key, modifier):
        """K left J right"""
        try:
            impulse = impulses[key]
        except KeyError:
            return

        print impulse
        hero.body.apply_impulse(impulse)

    director.window.push_handlers(on_key_press)

    director.run(main_scene)

impulses = {
    pyglet.window.key.W: Vector(0, 3000),
    pyglet.window.key.S: Vector(0, -1000),
    pyglet.window.key.D: Vector(4000, 0),
    pyglet.window.key.A: Vector(-4000, 0),
}
def make_sprite(**kwargs):
    # taken from Karl's __main__ code.
    _sprite = sprite.Sprite(**kwargs)
    angle = random.random() * math.pi
    vs = [(-75,-39), (-75,39), (75,39), (75, -39)]
    mass = 20
    moment = pymunk.moment_for_poly(mass, vs)
    body = pymunk.Body(mass, moment)
    shape = pymunk.Poly(body, vs)
    shape.friction = 0.5
    body.position = _sprite.position
    body.angle = angle

    _sprite.shape = shape
    _sprite.body = body
    return _sprite

if __name__ == '__main__':
    main()<commit_msg>removed debug print. so junky.<commit_after>"""
Stuff related to loading and rendering a level.
"""

# This code is so you can run the samples without installing the package
import math
import os
import random
import sys

import pyglet
from pyglet.window import key

from pytmx import tmxloader
import pymunk

import cocos
from cocos import tiles, actions, layer, sprite
from cocos.director import director

import settings

from collections import namedtuple

Vector = namedtuple('Vector', 'x y')

def main():

    pyglet.resource.path = settings.RESOURCE_PATH
    pyglet.resource.reindex()

    director.init(width=640, height=320, do_not_scale=True, resizable=True)

    scroller = layer.ScrollingManager()
    tmx = tiles.load('underground-level1.tmx')
    tilemap = tmx['map']

    tmx = tmxloader.load_tmx(pyglet.resource.file('underground-level1.tmx'))
    triggers = tmx['triggers']

    # physics,yo
    space = pymunk.Space()
    space.gravity = pymunk.Vec2d(0.0, settings.GRAVITY)

    # set up our hero cart.
    startcell, = triggers.find('player')
    cart_image = pyglet.resource.image("cart.png")
    pos = Vector(startcell.x + cart_image.width // 2, startcell.y + cart_image.height // 2)
    hero = make_sprite(image=cart_image, position=(pos.x, tilemap.px_height - pos.y))
    space.add(hero.body, hero.shape)

    # set up our blockers.
    blockers = triggers.find('blocker')
    walls = []
    static_body = pymunk.Body()
    for blocker in blockers:
        # turn the four corners of the 'blocker objects' defined in tilemap into rigid walls.
        c1 = Vector(blocker.x, tilemap.px_height - blocker.y)
        c2 = Vector(blocker.x + blocker.width, tilemap.px_height - blocker.y)
        c3 = Vector(blocker.x + blocker.width, (tilemap.px_height - blocker.y - blocker.height))
        c4 = Vector(blocker.x, (tilemap.px_height - blocker.y - blocker.height))

        # a wall is a block made from four lines joining the four corners.
        walls.extend([pymunk.Segment(static_body, c1, c2, 0),
                      pymunk.Segment(static_body, c2, c3, 0),
                      pymunk.Segment(static_body, c3, c4, 0),
                      pymunk.Segment(static_body, c4, c1, 0)])

    [setattr(wall, 'friction', 0.1) for wall in walls]
    space.add(walls)

    scroller.add(tilemap)

    main_scene = cocos.scene.Scene(scroller)
    main_scene.add(hero)

    def update(dt):
        space.step(dt)
        hero.set_position(*hero.body.position)

    pyglet.clock.schedule(update)

    keyboard = key.KeyStateHandler()
    director.window.push_handlers(keyboard)

    def on_key_press(key, modifier):
        """K left J right"""
        try:
            impulse = impulses[key]
        except KeyError:
            return

        hero.body.apply_impulse(impulse)

    director.window.push_handlers(on_key_press)

    director.run(main_scene)

impulses = {
    pyglet.window.key.W: Vector(0, 3000),
    pyglet.window.key.S: Vector(0, -1000),
    pyglet.window.key.D: Vector(4000, 0),
    pyglet.window.key.A: Vector(-4000, 0),
}
def make_sprite(**kwargs):
    # taken from Karl's __main__ code.
    _sprite = sprite.Sprite(**kwargs)
    angle = random.random() * math.pi
    vs = [(-75,-39), (-75,39), (75,39), (75, -39)]
    mass = 20
    moment = pymunk.moment_for_poly(mass, vs)
    body = pymunk.Body(mass, moment)
    shape = pymunk.Poly(body, vs)
    shape.friction = 0.5
    body.position = _sprite.position
    body.angle = angle

    _sprite.shape = shape
    _sprite.body = body
    return _sprite

if __name__ == '__main__':
    main()<|endoftext|>


<commit_before>import bz2
import glob
import numpy as np
import os.path as op
import scipy.integrate as si
import scipy.misc as sm
import scipy.special as ss
import scipy.stats as st

class Posterior(object):
    """Class representing a posterior object for the selection-effects
    model of the observed Kepler planet number density in the
    period-radius plane.

    """

    def __init__(self, candidates, systems, lower_left=None, upper_right=None):
        r"""Initialise the posterior object.

        :param candidates: An array with the columns

           * ``Kepler_ID``
           * ``Period``, period in years of candidate.
           * ``Radius``, radius in Earth radii of candidate.
           * ``Stellar_Radius``, radius in Solar radii of host star.
           * ``Stellar_Mass``, mass in Solar masses of host star.
           * ``SNR0``, SNR of 1 REarth planet in orbit at 1 yr.

          giving the properties of the identified planetary
          candidates.

        :param systems: An array with the columns

           * ``Kepler_ID``
           * ``Radius`` giving the radius in Solar radii
           * ``Mass`` giving the mass in Solar masses
           * ``SNR0`` giving the SNR of a 1 REarth planet in orbit at 1 yr.

          giving the properties of the stars in the observing program
          involved in the search.

        :param lower_left: The lower left boundary of the background
          distribution in the :math:`\ln(P)`-:math:`\ln(R)` plane.  If
          ``None``, then the most extreme candidate point will be
          used.

        :param upper_right: See ``lower_left``.

        """
        self.candidates = candidates
        self.systems = systems
        self.pts = np.log(np.column_stack((candidates['Period'], candidates['Radius'])))

        if lower_left is None:
            self.lower_left = np.min(self.pts, axis=0)
        else:
            self.lower_left = lower_left

        if upper_right is None:
            self.upper_right = np.max(self.pts, axis=0)
        else:
            self.upper_right = upper_right

    @property
    def dtype(self):
        """Gives the numpy datatype of the parameters.

        """

        return np.dtype([('R', np.float),
                         ('Rb', np.float),
                         ('mu', np.float, 2),
                         ('sigma', np.float, 2),
                         ('theta', np.float),
                         ('log_snr_min', np.float),
                         ('log_snr_max', np.float),
                         ('gamma', np.float, 2)])

    @property
    def pnames(self):
        """Gives LaTeX names for the parameters.

        """

        return [r'$R$', r'$R_b$',
                r'$\mu_P$', r'$\mu_R$',
                r'$\sigma_1$', r'$\sigma_2$',
                r'$\theta$',
                r'$\log\left(\rho_\mathrm{min}\right)$',
                r'$\log\left(\rho_\mathrm{max}\right)$',
                r'$\gamma_P$', r'$\gamma_R$']

    @property
    def nparams(self):
        """The number of parameters."""
        return 11

    def to_params(self, p):
        """Converts the array ``p`` to a named-array representing parameters.

        """
        return p.view(self.dtype).squeeze()

    def covariance_matrix(self, p, inv=False):
        """Returns the covariance matrix (or inverse, if ``inv=True``)
        corresponding to the parameters ``p``.

        """
        p = self.to_params(p)

        if inv:
            d = np.diag(1.0/(p['sigma']*p['sigma']))
        else:
            d = np.diag(p['sigma']*p['sigma'])

        ct = np.cos(p['theta'])
        st = np.sin(p['theta'])

        r = np.array([[ct, -st], [st, ct]])

        return np.dot(r, np.dot(d, r.T))

    def foreground_density(self, p, ps, rs):
        """Returns the true foreground density for the given parameters and
        planet periods and radii (in years and Earth radii, respectively).

        """
        p = self.to_params(p)

        pts = np.log(np.column_stack((ps, rs)))

        cm = self.covariance_matrix(p, inv=True)

        xs = pts - p['mu']

        return 1.0/(2.0*np.pi*np.prod(p['sigma']))*np.exp(-0.5*np.sum(xs*np.dot(cm, xs.T).T, axis=1))

    def background_density(self, p, ps, rs):
        """Return the background density at the given points in the
        period-radius plane.

        """

        p = self.to_params(p)

        ps = np.atleast_1d(ps)
        rs = np.atleast_1d(rs)

        pts = np.log(np.column_stack((ps, rs)))

        center = 0.5*(self.lower_left + self.upper_right)
        dx = self.upper_right - self.lower_left
        V = np.prod(dx)

        rhos = 1.0/V*(1.0 + np.dot(p['gamma'], (pts - center).T))

        sel = (pts < self.lower_left) | (pts > self.upper_right)
        sel = sel[:,0] | sel[:,1]

        rhos[sel] = 0.0
        return rhos

    def log_snr(self, ps, rs, snr0s):
        """Returns the log of the SNR for the given periods and radii around
        stars with the given SNR0s.

        """
        return 2.0*np.log(rs) - 1.0/3.0*np.log(ps) + np.log(snr0s)

    @property
    def transit_selection_factor(self):
        """Probability of transit in a 1yr orbit about the sun.

        """
        return 0.001603891301 # Probability of detecting in yr orbit
                              # around sun.

    def ptransit(self, ps, ms, rs):
        """Returns the probability of a transit at the given periods around
        stars of the given masses and radii.

        """
        return self.transit_selection_factor*rs/(ms*np.square(ps))**(1.0/3.0)

    def pdetect(self, p, ps, rs, snr0s):
        """Returns the detection probability assuming that a planet transits
        given parameters ``p``, periods ``ps``, planetary radii
        ``rs``, and stellar SNRs ``snr0s``.

        """
        p = self.to_params(p)

        log_snrs = self.log_snr(ps, rs, snr0s)
        log_snrs = np.atleast_1d(log_snrs)

        pdets = np.zeros(log_snrs.shape)

        sel = (p['log_snr_min'] < log_snrs) & (log_snrs <= p['log_snr_max'])
        pdets[sel] = (log_snrs[sel]-p['log_snr_min'])/(p['log_snr_max'] - p['log_snr_min'])

        sel = (p['log_snr_max'] < log_snrs)
        pdets[sel] = 1.0

        return pdets

    def gaussian_selection_integral(self, mu, sigma, xmin, xmax):
        r"""Evaluates

        .. math::

          \int_{x_\mathrm{min}}^{x_\mathrm{max}} dx\, \phi(x) \frac{x-x_\mathrm{min}}{x_\mathrm{max}-x_\mathrm{min}} + \int_{x_\mathrm{max}}^\infty dx \, \phi(x)

        """

        dx = xmax - xmin
        edenom = np.sqrt(2.0)*sigma

        mu_term = 0.5*mu*(ss.erf((mu-xmin)/edenom) - ss.erf((mu-xmax)/edenom))
        xterm = 0.5*(xmax*ss.erf((mu-xmax)/edenom) - xmin*ss.erf((mu-xmin)/edenom))
        sigma_term = 1.0/np.sqrt(2.0*np.pi)*sigma*(np.exp(-0.5*np.square((mu-xmin)/sigma)) - np.exp(-0.5*np.square((mu-xmax)/sigma)))

        return 1.0/dx*(mu_term + xterm + sigma_term) + 0.5

    def alpha(self, p, ms, rs, snr0s):
        """Returns the average probability over the planetary period-radius
        distribution of detecting a planet (transit probability times
        detection probability) about stars with the given masses,
        radii, and SNRs.

        """

        p = self.to_params(p)

        mu = p['mu']
        cm = self.covariance_matrix(p)
        gm = self.covariance_matrix(p, inv=True)

        gm_det = gm[0,0]*gm[1,1]-gm[0,1]*gm[0,1]

        mu_new = mu - np.array([2.0/3.0*gm[1,1]/gm_det, -2.0/3.0*gm[0,1]/gm_det])

        lognorm_norm = np.exp(-2.0/3.0*mu[0] + 2.0/9.0*gm[1,1]/gm_det)
        geom_factor = self.transit_selection_factor*rs/ms**(1.0/3.0)

        log_snr_mean = 2.0*mu_new[1] - 1.0/3.0*mu_new[0] + np.log(snr0s)
        log_snr_sigma = np.sqrt(4.0*cm[1,1] - 4.0/3.0*cm[0,1] + 1.0/9.0*cm[0,0])

        return lognorm_norm*geom_factor*self.gaussian_selection_integral(log_snr_mean, log_snr_sigma, p['log_snr_min'], p['log_snr_max'])

    def __call__(self, p):
        """Returns the posterior evaluated at ``p``.

        """

        p = self.to_params(p)

        dx, dy = self.upper_right - self.lower_left
        gx, gy = p['gamma']

        # Priors bounds
        if p['R'] <= 0 or p['Rb'] <= 0:
            return np.NINF
        if np.any(p['mu'] < np.min(self.pts, axis=0)) or \
           np.any(p['mu'] > np.max(self.pts, axis=0)):
            return np.NINF
        if np.any(p['sigma'] <= 0):
            return np.NINF
        if p['theta'] < 0 or p['theta'] > np.pi/2.0:
            return np.NINF
        if p['log_snr_max'] <= p['log_snr_min']:
            return np.NINF
        if 1.0 - 0.5*np.abs(dx*gx) - 0.5*np.abs(dy*gy) < 0:
            return np.NINF

        alphas = self.alpha(p, self.systems['Mass'], self.systems['Radius'], self.systems['SNR0'])

        if np.any(alphas > 1):
            return np.NINF

        Rtotal = p['R']*np.sum(alphas) + p['Rb']*self.systems.shape[0]
        rhos = p['R']*self.pdetect(p, self.candidates['Period'], self.candidates['Radius'], self.candidates['SNR0'])*self.ptransit(self.candidates['Period'], self.candidates['Stellar_Mass'], self.candidates['Stellar_Radius'])*self.foreground_density(p, self.candidates['Period'], self.candidates['Radius']) + p['Rb']*self.background_density(p, self.candidates['Period'], self.candidates['Radius'])

        ll = np.sum(np.log(rhos)) - Rtotal

        lp = -np.sum(np.log(p['sigma'])) - 0.5*np.square(p['log_snr_min'] - np.log(3.0)) - 0.5*np.square(p['log_snr_max'] - np.log(11.0)) - 0.5*(np.log(p['R']*np.sum(alphas)) + np.log(p['Rb']))

        return ll + lp

    def draw_background(self, p, N):
        """Draw ``N`` systems from the background distribution specified by
        parameters ``p``.

        """

        p = self.to_params(p)

        dx, dy = self.upper_right - self.lower_left
        gx, gy = p['gamma']
        V = dx*dy

        pmax = 1.0/V*(1.0 + 0.5*np.abs(dx*gx) + 0.5*np.abs(dy*gy))

        pts = []
        while len(pts) < N:
            x,y,z = np.random.random(size=3)

            x = self.lower_left[0] + dx*x
            y = self.lower_left[1] + dy*y
            z = z*pmax

            x = np.exp(x)
            y = np.exp(y)

            if z < self.background_density(p, x, y):
                pts.append(np.array([x,y]))

        return np.array(pts)

    def draw(self, p0, ids, masses, radii, snr0s):
        """Draw planets and background "detections" around the stars with the
        given masses, radii, and SNRs as described by the model
        parameters ``p0``.

        """

        p0 = self.to_params(p0)

        fs = []
        bs = []

        mu = p0['mu']
        cm = self.covariance_matrix(p0)

        # Draw foreground events
        n = np.random.poisson(p0['R']*ids.shape[0])
        pts = np.random.multivariate_normal(mean=mu, cov=cm, size=n)
        inds = np.random.randint(ids.shape[0], size=n)
        PS = np.exp(pts[:,0])
        RS = np.exp(pts[:,1])
        psel = self.pdetect(p0, PS, RS, snr0s[inds])*self.ptransit(PS, masses[inds], radii[inds])
        sel = np.random.random(size=n) < psel

        fps = PS[sel]
        frs = RS[sel]
        fids = ids[inds][sel]
        fmasses = masses[inds][sel]
        fradii = radii[inds][sel]
        fsnr0s = snr0s[inds][sel]

        # Draw background events
        n = np.random.poisson(p0['Rb']*ids.shape[0])
        ps_rs = self.draw_background(p0, n)
        inds = np.random.randint(ids.shape[0], size=n)

        syss = []

        for fp, fr, fid, fm, frad, fs in zip(fps, frs, fids, fmasses, fradii, fsnr0s):
            syss.append((int(fid), fp, fr, frad, fm, fs))

        for bp, br, bi in zip(ps_rs[:,0], ps_rs[:,1], inds):
            syss.append((int(ids[bi]), bp, br, radii[bi], masses[bi], snr0s[bi]))

        candidates = np.array(syss, dtype=np.dtype([('Kepler_ID', np.int),
                                                    ('Period', np.float),
                                                    ('Radius', np.float),
                                                    ('Stellar_Radius', np.float),
                                                    ('Stellar_Mass', np.float),
                                                    ('SNR0', np.float)]))

        return candidates

    def gradient(self, p):
        """Returns a numerical approximation to the gradient of the posterior
        at the point ``p``.

        """

        g = []
        for i in range(self.nparams):
            def f(x):
                pc = p.copy()
                pc[i] = x
                return self.__call__(pc)
            g.append(sm.derivative(f, p[i], dx=1e-5))

        return np.array(g)
<commit_msg>Added Pmin to posterior.py<commit_after>import bz2
import glob
import numpy as np
import os.path as op
import scipy.integrate as si
import scipy.misc as sm
import scipy.special as ss
import scipy.stats as st

class Posterior(object):
    """Class representing a posterior object for the selection-effects
    model of the observed Kepler planet number density in the
    period-radius plane.

    """

    def __init__(self, candidates, systems, lower_left=None, upper_right=None):
        r"""Initialise the posterior object.

        :param candidates: An array with the columns

           * ``Kepler_ID``
           * ``Period``, period in years of candidate.
           * ``Radius``, radius in Earth radii of candidate.
           * ``Stellar_Radius``, radius in Solar radii of host star.
           * ``Stellar_Mass``, mass in Solar masses of host star.
           * ``SNR0``, SNR of 1 REarth planet in orbit at 1 yr.

          giving the properties of the identified planetary
          candidates.

        :param systems: An array with the columns

           * ``Kepler_ID``
           * ``Radius`` giving the radius in Solar radii
           * ``Mass`` giving the mass in Solar masses
           * ``SNR0`` giving the SNR of a 1 REarth planet in orbit at 1 yr.

          giving the properties of the stars in the observing program
          involved in the search.

        :param lower_left: The lower left boundary of the background
          distribution in the :math:`\ln(P)`-:math:`\ln(R)` plane.  If
          ``None``, then the most extreme candidate point will be
          used.

        :param upper_right: See ``lower_left``.

        """
        self.candidates = candidates
        self.systems = systems
        self.pts = np.log(np.column_stack((candidates['Period'], candidates['Radius'])))

        if lower_left is None:
            self.lower_left = np.min(self.pts, axis=0)
        else:
            self.lower_left = lower_left

        if upper_right is None:
            self.upper_right = np.max(self.pts, axis=0)
        else:
            self.upper_right = upper_right

    @property
    def dtype(self):
        """Gives the numpy datatype of the parameters.

        """

        return np.dtype([('R', np.float),
                         ('Rb', np.float),
                         ('mu', np.float, 2),
                         ('sigma', np.float, 2),
                         ('theta', np.float),
                         ('log_snr_min', np.float),
                         ('log_snr_max', np.float),
                         ('gamma', np.float, 2),
                         ('Pmin', np.float)])

    @property
    def pnames(self):
        """Gives LaTeX names for the parameters.

        """

        return [r'$R$', r'$R_b$',
                r'$\mu_P$', r'$\mu_R$',
                r'$\sigma_1$', r'$\sigma_2$',
                r'$\theta$',
                r'$\log\left(\rho_\mathrm{min}\right)$',
                r'$\log\left(\rho_\mathrm{max}\right)$',
                r'$\gamma_P$', r'$\gamma_R$', r'$P_\mathrm{min}$']

    @property
    def nparams(self):
        """The number of parameters."""
        return 11

    def to_params(self, p):
        """Converts the array ``p`` to a named-array representing parameters.

        """
        return p.view(self.dtype).squeeze()

    def covariance_matrix(self, p, inv=False):
        """Returns the covariance matrix (or inverse, if ``inv=True``)
        corresponding to the parameters ``p``.

        """
        p = self.to_params(p)

        if inv:
            d = np.diag(1.0/(p['sigma']*p['sigma']))
        else:
            d = np.diag(p['sigma']*p['sigma'])

        ct = np.cos(p['theta'])
        st = np.sin(p['theta'])

        r = np.array([[ct, -st], [st, ct]])

        return np.dot(r, np.dot(d, r.T))

    def foreground_density(self, p, ps, rs):
        """Returns the true foreground density for the given parameters and
        planet periods and radii (in years and Earth radii, respectively).

        """
        p = self.to_params(p)

        pts = np.log(np.column_stack((ps, rs)))

        cm = self.covariance_matrix(p, inv=True)

        xs = pts - p['mu']

        return 1.0/(2.0*np.pi*np.prod(p['sigma']))*np.exp(-0.5*np.sum(xs*np.dot(cm, xs.T).T, axis=1))

    def background_density(self, p, ps, rs):
        """Return the background density at the given points in the
        period-radius plane.

        """

        p = self.to_params(p)

        ps = np.atleast_1d(ps)
        rs = np.atleast_1d(rs)

        pts = np.log(np.column_stack((ps, rs)))

        lower_left = self.lower_left.copy()
        lower_left[0] = np.log(p['Pmin'])

        center = 0.5*(lower_left + self.upper_right)
        dx = self.upper_right - lower_left
        V = np.prod(dx)

        rhos = 1.0/V*(1.0 + np.dot(p['gamma'], (pts - center).T))

        sel = (pts < lower_left) | (pts > self.upper_right)
        sel = sel[:,0] | sel[:,1]

        rhos[sel] = 0.0
        return rhos

    def log_snr(self, ps, rs, snr0s):
        """Returns the log of the SNR for the given periods and radii around
        stars with the given SNR0s.

        """
        return 2.0*np.log(rs) - 1.0/3.0*np.log(ps) + np.log(snr0s)

    @property
    def transit_selection_factor(self):
        """Probability of transit in a 1yr orbit about the sun.

        """
        return 0.001603891301 # Probability of detecting in yr orbit
                              # around sun.

    def ptransit(self, ps, ms, rs):
        """Returns the probability of a transit at the given periods around
        stars of the given masses and radii.

        """
        return self.transit_selection_factor*rs/(ms*np.square(ps))**(1.0/3.0)

    def pdetect(self, p, ps, rs, snr0s):
        """Returns the detection probability assuming that a planet transits
        given parameters ``p``, periods ``ps``, planetary radii
        ``rs``, and stellar SNRs ``snr0s``.

        """
        p = self.to_params(p)

        log_snrs = self.log_snr(ps, rs, snr0s)
        log_snrs = np.atleast_1d(log_snrs)

        pdets = np.zeros(log_snrs.shape)

        sel = (p['log_snr_min'] < log_snrs) & (log_snrs <= p['log_snr_max'])
        pdets[sel] = (log_snrs[sel]-p['log_snr_min'])/(p['log_snr_max'] - p['log_snr_min'])

        sel = (p['log_snr_max'] < log_snrs)
        pdets[sel] = 1.0

        return pdets

    def gaussian_selection_integral(self, mu, sigma, xmin, xmax):
        r"""Evaluates

        .. math::

          \int_{x_\mathrm{min}}^{x_\mathrm{max}} dx\, \phi(x) \frac{x-x_\mathrm{min}}{x_\mathrm{max}-x_\mathrm{min}} + \int_{x_\mathrm{max}}^\infty dx \, \phi(x)

        """

        dx = xmax - xmin
        edenom = np.sqrt(2.0)*sigma

        mu_term = 0.5*mu*(ss.erf((mu-xmin)/edenom) - ss.erf((mu-xmax)/edenom))
        xterm = 0.5*(xmax*ss.erf((mu-xmax)/edenom) - xmin*ss.erf((mu-xmin)/edenom))
        sigma_term = 1.0/np.sqrt(2.0*np.pi)*sigma*(np.exp(-0.5*np.square((mu-xmin)/sigma)) - np.exp(-0.5*np.square((mu-xmax)/sigma)))

        return 1.0/dx*(mu_term + xterm + sigma_term) + 0.5

    def alpha(self, p, ms, rs, snr0s):
        """Returns the average probability over the planetary period-radius
        distribution of detecting a planet (transit probability times
        detection probability) about stars with the given masses,
        radii, and SNRs.

        """

        p = self.to_params(p)

        mu = p['mu']
        cm = self.covariance_matrix(p)
        gm = self.covariance_matrix(p, inv=True)

        gm_det = gm[0,0]*gm[1,1]-gm[0,1]*gm[0,1]

        mu_new = mu - np.array([2.0/3.0*gm[1,1]/gm_det, -2.0/3.0*gm[0,1]/gm_det])

        lognorm_norm = np.exp(-2.0/3.0*mu[0] + 2.0/9.0*gm[1,1]/gm_det)
        geom_factor = self.transit_selection_factor*rs/ms**(1.0/3.0)

        log_snr_mean = 2.0*mu_new[1] - 1.0/3.0*mu_new[0] + np.log(snr0s)
        log_snr_sigma = np.sqrt(4.0*cm[1,1] - 4.0/3.0*cm[0,1] + 1.0/9.0*cm[0,0])

        return lognorm_norm*geom_factor*self.gaussian_selection_integral(log_snr_mean, log_snr_sigma, p['log_snr_min'], p['log_snr_max'])

    def __call__(self, p):
        """Returns the posterior evaluated at ``p``.

        """

        p = self.to_params(p)

        lower_left = self.lower_left.copy()
        lower_left[0] = np.log(p['Pmin'])

        dx, dy = self.upper_right - lower_left
        gx, gy = p['gamma']

        # Priors bounds
        if p['R'] <= 0 or p['Rb'] <= 0:
            return np.NINF
        if np.any(p['mu'] < np.min(self.pts, axis=0)) or \
           np.any(p['mu'] > np.max(self.pts, axis=0)):
            return np.NINF
        if np.any(p['sigma'] <= 0):
            return np.NINF
        if p['theta'] < 0 or p['theta'] > np.pi/2.0:
            return np.NINF
        if p['log_snr_max'] <= p['log_snr_min']:
            return np.NINF
        if 1.0 - 0.5*np.abs(dx*gx) - 0.5*np.abs(dy*gy) < 0:
            return np.NINF
        if np.log(p['Pmin']) > self.upper_right[0]:
            return np.NINF

        alphas = self.alpha(p, self.systems['Mass'], self.systems['Radius'], self.systems['SNR0'])

        if np.any(alphas > 1):
            return np.NINF

        Rtotal = p['R']*np.sum(alphas) + p['Rb']*self.systems.shape[0]
        rhos = p['R']*self.pdetect(p, self.candidates['Period'], self.candidates['Radius'], self.candidates['SNR0'])*self.ptransit(self.candidates['Period'], self.candidates['Stellar_Mass'], self.candidates['Stellar_Radius'])*self.foreground_density(p, self.candidates['Period'], self.candidates['Radius']) + p['Rb']*self.background_density(p, self.candidates['Period'], self.candidates['Radius'])

        ll = np.sum(np.log(rhos)) - Rtotal

        lp = -np.sum(np.log(p['sigma'])) - 0.5*np.square(p['log_snr_min'] - np.log(3.0)) - 0.5*np.square(p['log_snr_max'] - np.log(11.0)) - 0.5*(np.log(p['R']*np.sum(alphas)) + np.log(p['Rb']))

        return ll + lp

    def draw_background(self, p, N):
        """Draw ``N`` systems from the background distribution specified by
        parameters ``p``.

        """

        p = self.to_params(p)

        lower_left = self.lower_left.copy()
        lower_left[0] = np.log(p['Pmin'])

        dx, dy = self.upper_right - lower_left
        gx, gy = p['gamma']
        V = dx*dy

        pmax = 1.0/V*(1.0 + 0.5*np.abs(dx*gx) + 0.5*np.abs(dy*gy))

        pts = []
        while len(pts) < N:
            x,y,z = np.random.random(size=3)

            x = lower_left[0] + dx*x
            y = lower_left[1] + dy*y
            z = z*pmax

            x = np.exp(x)
            y = np.exp(y)

            if z < self.background_density(p, x, y):
                pts.append(np.array([x,y]))

        return np.array(pts)

    def draw(self, p0, ids, masses, radii, snr0s):
        """Draw planets and background "detections" around the stars with the
        given masses, radii, and SNRs as described by the model
        parameters ``p0``.

        """

        p0 = self.to_params(p0)

        fs = []
        bs = []

        mu = p0['mu']
        cm = self.covariance_matrix(p0)

        # Draw foreground events
        n = np.random.poisson(p0['R']*ids.shape[0])
        pts = np.random.multivariate_normal(mean=mu, cov=cm, size=n)
        inds = np.random.randint(ids.shape[0], size=n)
        PS = np.exp(pts[:,0])
        RS = np.exp(pts[:,1])
        psel = self.pdetect(p0, PS, RS, snr0s[inds])*self.ptransit(PS, masses[inds], radii[inds])
        sel = np.random.random(size=n) < psel

        fps = PS[sel]
        frs = RS[sel]
        fids = ids[inds][sel]
        fmasses = masses[inds][sel]
        fradii = radii[inds][sel]
        fsnr0s = snr0s[inds][sel]

        # Draw background events
        n = np.random.poisson(p0['Rb']*ids.shape[0])
        ps_rs = self.draw_background(p0, n)
        inds = np.random.randint(ids.shape[0], size=n)

        syss = []

        for fp, fr, fid, fm, frad, fs in zip(fps, frs, fids, fmasses, fradii, fsnr0s):
            syss.append((int(fid), fp, fr, frad, fm, fs))

        for bp, br, bi in zip(ps_rs[:,0], ps_rs[:,1], inds):
            syss.append((int(ids[bi]), bp, br, radii[bi], masses[bi], snr0s[bi]))

        candidates = np.array(syss, dtype=np.dtype([('Kepler_ID', np.int),
                                                    ('Period', np.float),
                                                    ('Radius', np.float),
                                                    ('Stellar_Radius', np.float),
                                                    ('Stellar_Mass', np.float),
                                                    ('SNR0', np.float)]))

        return candidates

    def gradient(self, p):
        """Returns a numerical approximation to the gradient of the posterior
        at the point ``p``.

        """

        g = []
        for i in range(self.nparams):
            def f(x):
                pc = p.copy()
                pc[i] = x
                return self.__call__(pc)
            g.append(sm.derivative(f, p[i], dx=1e-5))

        return np.array(g)
<|endoftext|>