Source

pida-main / pida / core / actions.py

Full commit
# -*- coding: utf-8 -*-
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
"""
    Action support for PIDA services.

    :copyright: 2005-2008 by The PIDA Project
    :license: GPL 2 or later (see README/COPYING/LICENSE)
"""

import gtk
from .base import BaseConfig
from .environment import settings_dir
from pida.utils import json


accelerator_group = gtk.AccelGroup()
accelerator_group.lock()
# the global accelerator group will be added to detached windows as well
global_accelerator_group = gtk.AccelGroup()
global_accelerator_group.lock()


class ActionsConfig(BaseConfig):
    # this inherits from options in order to ease storing the mapping betwen
    # actions and accels (keyboard shortcuts)
    """
    The base action configurator.

    Services using actions should subclass this, and set their actions_config
    class attribute to the class. It will be instantiated on service activation
    with the service instance passed as the parameter to the constructor. The
    service will be available as the svc attribute in the configurator
    instance.
    """
    ext = '.keys.json'
    actions = ()
    accels = {}
    global_accels = {}

    def create(self):
        """
        Called to initialize this configurator.

        Will initialise attributes, call create_actions, then register the
        actions and the ui definitions with the Boss.
        """
        self.name = self.svc.get_name() + self.ext
        self.group = gtk.ActionGroup(self.svc.get_name())
        self._defaults = {}
        self.create_actions()
        # call the real register_options after creating all actions
        self.subscribe_keyboard_shortcuts()
        if self.svc.boss is not None:
            self.ui_merge_id = self.svc.boss.add_action_group_and_ui(
                self.group,
                self.svc.__class__.__module__,
                'uidef/%s.xml' % self.svc.get_name(),
            )

    def create_actions(self):
        """
        Called to create the actions.

        Create your service actions actions here. Each action should be created
        with a call to create_action. These actions will be added to the action
        group for the service, and can be used for any purpose.
        """

        for act in self.actions:
            self.group.add_action(act)

            name = act.get_name()
            callback = getattr(self, 'on_' + name, None)
            if callback: act.connect('activate', callback)

            if name in self.accels:
                self._create_key_option(act, self.accels[name])
            elif name in self.global_accels:
                self._create_key_option(act, self.global_accels[name],
                                        global_=True)


    def remove_action(self, action):
        """
        Removes a Action from ActionManager

        :param action: Action instance
        """
        self.group.remove_action(action)

    def remove_actions(self):
        self.svc.boss.remove_action_group_and_ui(
            self.group,
            self.ui_merge_id)

    def create_action(self, name, atype, label, tooltip, stock_id,
                      callback=None, accel=None, global_=False):
        """
        Create an action for this service.

        :param name:
            The unique name for the action. This must be unique for the
            service, so choose names wisely. For example:
            `show_project_properties`
        :param atype:
            This is the type of action, and maps directly to a type of
            gtk.Action. Types include:
            - gtk.Action
            - gtk.ToggleAction
            - gtk.RadioAction
            - pida.ui.actions.PidaMenuToolAction
              A custom Action which contains a dropdown menu
              when rendered as a tool item
            - pida.ui.actions.PidaRememberToggle: Toggle that will be remembered
        :param label:
            The label to display on proxies of the action.
        :param toolip:
            The tool tip to display on proxies of the action.
        :param stock_id:
            The stock id of the icon to display on proxies of the action.
        :param callback:
            The callback function to be called when the action is activated.
            This function should take the action as a parameter.
        :param accel:
            The accelerator string set as the default accelerator for this
            action, or `None` for actions that do not need an accelerator.
            To be used these actions must be proxied as items in one of the
            menus or toolbars.
        """
        act = atype(name=name, label=label, tooltip=tooltip, stock_id=stock_id)
        self.group.add_action(act)
        if callback is not None:
            act.connect('activate', callback)

        if accel is not None:
            self._create_key_option(act, accel, global_=global_)

        return act

    def _create_key_option(self, act, accel, global_=False):
        self._defaults[act.get_name()] = accel
        ag = global_accelerator_group if global_ else accelerator_group
        act.set_accel_group(ag)
        act.set_accel_path(self._create_accel_path(act.get_name()))
        act.connect_accelerator()

    def get_action(self, name):
        """
        Get the named action
        """
        return self.group.get_action(name)

    def get_action_group(self):
        """
        Get the action group
        """
        return self.group

    def get_keyboard_options(self):
        """
        Get the keyboard options.
        """
        return self._keyboard_options

    def _create_accel_path(self, name):
        return '<Actions>/%s' % name

    def _set_action_keypress(self, name, accel_string):
        keyval, modmask = gtk.accelerator_parse(accel_string)
        gtk.accel_map_change_entry(self._create_accel_path(name),
            keyval, modmask, True)

    def subscribe_keyboard_shortcuts(self):
        """
        Set the keyboard shortcuts for the actions with keyboard shortcuts
        enabled.
        """
        data = self._defaults.copy()
        data.update(json.load(settings_dir(self.name), fallback={}))
        self.current = data
        for name, accel in data.items():
            if name in self._defaults:
                self._set_action_keypress(name, accel)

    def set_value(self, name, accel):
        self.current[name] = accel
        self._set_action_keypress(name, accel)
        self.store_shortcuts()

    def store_shortcuts(self):
        data = json.load(settings_dir(self.name), fallback={})
        data.update(self.current)
        json.dump(data, settings_dir(self.name))

    def list_actions(self):
        """
        iterate the optionsitems
        """
        for action in self.group.list_actions():
            yield action