Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

beebox / crossover   deb

Repository URL to install this package:

Version: 18.5.0-1 

/ opt / cxoffice / lib / python / cxassoc.py

# (c) Copyright 2009-2011. CodeWeavers, Inc.

import UserDict
import os

import bottlequery

import cxconfig
import cxutils
import cxlog


class Error(Exception):
    pass


def cxassoc_path():
    return os.path.join(cxutils.CX_ROOT, "bin", "cxassoc")

def run_cxassoc(args, grab_stdout=False, background=False):
    if grab_stdout:
        stdout = cxutils.GRAB
    else:
        stdout = None

    if background:
        stderr = None
    else:
        stderr = cxutils.GRAB

    retcode, out, err = cxutils.run((cxassoc_path(),)+tuple(args), stderr=stderr, stdout=stdout, background=background)

    if retcode:
        raise Error(err)
    else:
        return out

class Association(object):
    """A single extension/appname/verbname set, which corresponds to one
    section in the cxassoc.conf file.
    """

    def __init__(self, parent, section):
        """Initializes an Association object based on the specified
        cxassoc.conf section.
        """
        self.parent = parent

        # The section name in cxassoc.conf, i.e. the EAssoc.
        self.eassoc = section.name

        # The extension, of the form '.exe'.
        self.extension = self.eassoc.split('/', 1)[0]

        # The mode currently stored in cxassoc.conf, one of
        # ('ignore', 'mime', 'alternative', 'default')
        self.current_mode = section.get('mode', 'ignore')
        if self.current_mode == 'alternate':
            # For compatibility with pre-5.0.3 files
            self.current_mode = 'alternative'

        # The new mode, to be committed by CXAssocPrefs.commit().
        self.new_mode = self.current_mode

        # The MIME type in the windows registry, or a made up type if the
        # registry has none.
        self.mimetype = section.get('mimetype') or ('application/x-crossover-%s' % self.extension.lstrip('.').lower())

        # The file type description in the windows registry, may be an empty
        # string.
        self.description = section.get('description', '')

        # The appname in the windows registry.
        self.appname = section.get('appname', '')

        if '/' in self.eassoc:
            # The internal verb name. Common internal verb names include:
            #  'open', 'edit', 'opennew', 'play', 'preview', and 'print'
            self.verb = self.eassoc.rsplit('/', 1)[1]
            # A string of the form '/app/verb' that identifies the action
            # taken by this association, or '' if it's the default in the
            # windows registry.
            self.action = self.eassoc[self.eassoc.index('/'):]
        else:
            self.verb = 'open'
            self.action = ''

        # The user-friendly verb name in the windows registry, or the internal
        # verb name if necessary.
        self.verbname = section.get('verbname', self.verb)

        # The configured file name of the icon if there is one use
        # get_iconfilename to get the full path.
        self.iconname = bottlequery.expand_unix_string(self.parent.bottlename, section.get('icon', ''))

    def get_iconfilename(self):
        """Returns the full path of the icon for this association, or '' if
        there is no icon.
        """
        if self.iconname and not self.iconname.startswith('/'):
            # resolve the path if it's relative
            wineprefix = bottlequery.get_prefix_for_bottle(self.parent.bottlename)
            return os.path.join(wineprefix, 'windata', 'Associations', self.iconname)
        return self.iconname


    def set_mode(self, new_mode):
        # pylint: disable=E1101
        self.parent.set_mode(self.eassoc, new_mode)


class CXAssocPrefs(object, UserDict.IterableUserDict):
    """A set of preferences for the associations in a bottle.

    As the modes of the preferences are changed, this class keeps the changes
    in memory. They can then be applied using commit()

    Changing one preference can have a side-effect of changing others. When
    this happens, the caller will be notified with the mode_changed()
    function.
    """

    def __init__(self, bottlename, managed):
        UserDict.IterableUserDict.__init__(self)
        self.bottlename = bottlename
        self.managed = managed

    def config_filename(self):
        return os.path.join(bottlequery.get_prefix_for_bottle(self.bottlename), 'cxassoc.conf')

    def read_config(self):
        """Reads the association information from cxassoc.conf."""
        config = cxconfig.get(self.config_filename())

        self.data.clear()
        for section in config.itervalues():
            self.data[section.name] = Association(self, section)


    def query_config(self):
        """Updates association states based on cxassoc --query"""

        if self.managed:
            scope = "managed"
        else:
            scope = "private"
        retcode, out, err = cxutils.run((cxassoc_path(), '--query', '--bottle', self.bottlename, '--scope', scope), stdout=cxutils.GRAB, stderr=cxutils.GRAB)

        if retcode:
            raise Error(err)

        config = cxconfig.Raw()
        config.read_string(out)

        for eassoc in self.data:
            if eassoc in config:
                if config[eassoc].get('default'):
                    new_mode = 'default'
                elif config[eassoc].get('alternative'):
                    new_mode = 'alternative'
                elif config[eassoc].get('mime'):
                    new_mode = 'mime'
                else:
                    new_mode = 'ignore'
                self.data[eassoc].current_mode = self.data[eassoc].new_mode = new_mode


    def refresh(self):
        """Reads the association information for this bottle."""
        self.read_config()
        self.query_config()


    def mode_changed(self, eassoc, newmode, user):
        """Called when the mode of an association has changed.

        user is True if the change was requested by a call to set_mode().
        user is False if the change was made automatically.

        Users should override this method.
        """
        pass


    def _set_mode(self, assoc, new_mode, user):
        if assoc.new_mode == new_mode:
            return

        cxlog.log_('assoc', 'Set mode for %s to %s (user=%s)' % (cxlog.to_str(assoc.eassoc), cxlog.to_str(new_mode), cxlog.to_str(user)))

        assoc.new_mode = new_mode
        self.mode_changed(assoc.eassoc, new_mode, user)


    def set_mode(self, eassoc, new_mode):
        """Set an association's mode, updating others as necessary."""
        assoc = self.data[eassoc]

        if new_mode not in ('ignore', 'mime', 'alternative', 'default'):
            raise ValueError("new_mode must be 'ignore', 'mime', 'alternative', or 'default'")

        self._set_mode(assoc, new_mode, True)

        mimetype = assoc.mimetype
        action = assoc.action

        # update other associations as necessary
        for other_assoc in self.data.values():
            if other_assoc.mimetype == mimetype:
                if other_assoc.action == action:
                    # mime type and action are identical, so the settings must
                    # be the same
                    self._set_mode(other_assoc, new_mode, False)
                else:
                    # mime type is the same but the action is different
                    if new_mode == 'ignore':
                        # not registering the mime type -> ignore
                        self._set_mode(other_assoc, 'ignore', False)
                    else:
                        if other_assoc.new_mode == 'ignore':
                            # registering the mime type -> the others can't
                            # be 'ignore'
                            self._set_mode(other_assoc, 'mime', False)
                        elif new_mode == 'default' and other_assoc.new_mode == 'default':
                            # this is 'default' -> other can't be 'default' too
                            self._set_mode(other_assoc, 'alternative', False)


    def commit(self):
        """Save and apply the changes using cxassoc."""

        # Find all changed associations, grouped by the new setting
        changes = {'ignore': [],
                   'mime': [],
                   'alternative': [],
                   'default': [],
                   'install': [],
                   'all': []}
        for assoc in self.data.values():
            if assoc.new_mode != assoc.current_mode:
                changes[assoc.new_mode].append(assoc.eassoc)

        mode_spec = []
        for mode in ('ignore', 'mime', 'alternative', 'default'):
            if changes[mode]:
                mode_spec.append("%s=%s" % (mode, ":".join(changes[mode])))
                changes['all'].extend(changes[mode])
                if mode != 'ignore':
                    changes['install'].extend(changes[mode])

        if changes['all']:
            # Apply all the changes in one go
            cmd = [cxassoc_path(), '--bottle', self.bottlename,
                   '--mode', ";".join(mode_spec).replace(".", "\\."),
                   '--mode-filter', ':'.join(changes['all'])]
            if changes['install']:
                cmd.extend(('--install', '--install-filter',
                            ':'.join(changes['install'])))
            if changes['ignore']:
                cmd.extend(('--uninstall', '--uninstall-filter',
                            ':'.join(changes['ignore'])))

            retcode, _out, err = cxutils.run(cmd, stderr=cxutils.GRAB)
            if retcode:
                raise Error(err)

            for eassoc in changes['all']:
                self.data[eassoc].current_mode = self.data[eassoc].new_mode


    def revert(self):
        """Discard local changes."""
        raise cxutils.not_yet_implemented()

    def recreate_assocs(self):
        run_cxassoc(('--bottle', self.bottlename, '--sync',
                     '--mode', 'mime', '--removeall', '--install'))