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

Repository URL to install this package:

Details    
crossover / opt / cxoffice / lib / python / cxmenu.py
Size: Mime:
# (c) Copyright 2009-2011. CodeWeavers, Inc.

import UserDict
import os

import cxobjc
import cxconfig
import cxlog
import cxutils

import bottlequery

from cxutils import cxgettext as _

class Error(Exception):
    pass

class CXMenu(cxobjc.Proxy):
    pass

def cxmenu_path():
    return os.path.join(cxutils.CX_ROOT, "bin", "cxmenu")

def run_cxmenu(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((cxmenu_path(),)+tuple(args), stderr=stderr, stdout=stdout, background=background)

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


def get_menuroot(bottlename):
    config_filename = os.path.join(bottlequery.get_prefix_for_bottle(bottlename), 'cxbottle.conf')
    config = cxconfig.get(config_filename)

    return config['Bottle'].get('MenuRoot', '')


def set_menuroot(bottlename, new_root, update=True):
    wconfig = bottlequery.get_config(bottlename).get_save_config()
    wconfig.lock_file()
    wconfig['Bottle']['MenuRoot'] = new_root
    wconfig.save_and_unlock_file()

    if update and wconfig['Bottle'].get('MenuMode', '') == 'install':
        run_cxmenu(('--removeall', '--install', '--bottle', bottlename))


@cxobjc.method(CXMenu, 'localStartMenuPath')
def local_start_menu_path():
    return _("Start Menu")


@cxobjc.method(CXMenu, 'localDesktopPath')
def local_desktop_path():
    return _("Desktop")


@cxobjc.method(CXMenu, 'createShortcutForBottle_command_name_folder_localDesktopPath_andStartMenuPath_')
def create_custom_shortcut(bottle, command, name, folder, local_desktop, local_startmenu):

    command = cxutils.string_to_unicode(command)
    name = cxutils.string_to_unicode(name)
    folder = cxutils.string_to_unicode(folder)
    local_desktop = cxutils.string_to_unicode(local_desktop)
    local_startmenu = cxutils.string_to_unicode(local_startmenu)

    if folder.startswith(local_desktop):
        folder = folder[len(local_desktop):]
        location = '--desktop'
    else:
        folder = folder[len(local_startmenu):]
        location = '--startmenu'
    if folder and not folder.endswith('/'):
        folder += '/'

    argv = cxutils.cmdlinetoargv(command)
    target = argv.pop(0)

    # Convert arguments to windows paths if necessary
    for i in range(len(argv)):
        arg = argv[i]
        if os.path.isabs(arg) and os.path.exists(arg):
            arg = bottlequery.get_windows_path(bottle, arg)
            if arg != '':
                argv[i] = arg

    cmd = [os.path.join(cxutils.CX_ROOT, "bin", "wine"),
           '--bottle', bottle, '--wl-app', 'cxmklnk', '--',
           location, folder + name + '.lnk',
           '--target', target]
    args = cxutils.argvtocmdline(argv)
    if args:
        cmd.extend(('--args', args))
    workdir = cxutils.dirname(target)
    if workdir:
        cmd.extend(('--workdir', workdir))
    cxutils.run(cmd, background=True)
    # FIXME: What if it fails?


class MenuItem(object):
    """A single menu shortcut."""

    def __init__(self, parent, section):
        self.parent = parent

        # encoded path
        self.path = section.name

        # full path
        self.full_path = os.path.join(
            bottlequery.get_prefix_for_bottle(self.parent.bottlename),
            'desktopdata/cxmenu',
            self.path)

        # launcher path
        self.launcher_path = os.path.join(
            bottlequery.get_prefix_for_bottle(self.parent.bottlename),
            'desktopdata/cxmenu/Launchers',
            self.path+'.desktop')

        # 'install' or 'ignore'
        self.mode = section.get('Mode', 'ignore')

        # this mode will be set when MenuPrefs.commit() is called
        self.new_mode = self.mode

        # 'windows' or 'raw'
        self.type = section.get('Type', 'raw').lower()

        # optional description
        self.description = section.get('Description')

        # optional setting for inclusion in the main gui window
        # 'alwaysinclude' -- always include this shortcut in the
        #                    main launcher window
        # 'neverinclude'  -- never include this shortcut in the
        #                    main launcher window
        self.include_in_mainmenu = section.get('IncludeInMainMenu')
        if self.include_in_mainmenu:
            self.include_in_mainmenu = self.include_in_mainmenu.lower()

        # optional icon filename; use iconfile attribute instead
        self._config_icon = section.get('Icon')
        if self._config_icon:
            self._config_icon = bottlequery.expand_unix_string(self.parent.bottlename, self._config_icon)

    def make_neverinclude(self):
        config = cxconfig.get(self.parent.config_filename())
        config.lock_file()
        config[self.path]['IncludeInMainMenu'] = 'neverinclude'
        config.save_and_unlock_file()

    def make_alwaysinclude(self):
        config = cxconfig.get(self.parent.config_filename())
        config.lock_file()
        config[self.path]['IncludeInMainMenu'] = 'alwaysinclude'
        config.save_and_unlock_file()

    def menu_name(self):
        return os.path.basename(self.path).replace(".lnk", "")

    def get_iconfile(self, sizes=cxutils.S_MEDIUM, fallback='crossover'):
        path = None
        if self._config_icon:
            if '/' in self._config_icon:
                return self._config_icon
            root = os.path.join(
                bottlequery.get_prefix_for_bottle(self.parent.bottlename),
                'windata', 'cxmenu', 'icons', 'hicolor')
            path = cxutils.get_icon_path(root, 'apps', self._config_icon, sizes)
            if not path:
                root = os.path.join(cxutils.CX_ROOT, 'share', 'icons')
                path = cxutils.get_icon_path(root, '', self._config_icon, sizes)
        if not path:
            root = os.path.join(cxutils.CX_ROOT, 'share', 'icons')
            path = cxutils.get_icon_path(root, '', fallback, sizes)
        return path

    iconfile = property(get_iconfile)

    def start(self):
        run_cxmenu(('--bottle', self.parent.bottlename, '--start', self.path), background=True)

    def _get_lnkfile(self):
        if self.type != 'windows':
            return None
        elif self.path.startswith('StartMenu/'):
            return 'c:/Windows/Start Menu/' + self.path[10:]
        elif self.path.startswith('Desktop/'):
            return 'c:/Windows/Desktop/' + self.path[8:]
        elif '/' in self.path:
            if self.path.startswith('StartMenu.'):
                rest = self.path[10:]
                sep = '/'
            elif self.path.startswith('StartMenu_'):
                rest = self.path[10:]
                sep = '/Start Menu/'
            elif self.path.startswith('Desktop.'):
                rest = self.path[8:]
                sep = '/'
            elif self.path.startswith('Desktop_'):
                rest = self.path[8:]
                sep = '/Desktop/'
            else:
                cxlog.warn("cxmenu._get_lnkfile: unrecognized path %s" % self.path)
                return None
            dirname, basename = rest.split('/', 1)
            dirname = cxutils.unmangle(dirname)
            return '%s%s%s' % (dirname, sep, basename)
        else:
            cxlog.warn("cxmenu._get_lnkfile: unrecognized path %s" % self.path)
            return None

    lnkfile = property(_get_lnkfile)

class MenuPrefs(object, UserDict.IterableUserDict):
    """An editable menu configuration for a bottle"""

    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), 'cxmenu.conf')

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

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

    def query_config(self):
        """Update menu settings from cxmenu --query."""
        if self.managed:
            scope = "managed"
        else:
            scope = "private"
        data = run_cxmenu(('--query', '--bottle', self.bottlename, '--scope', scope), grab_stdout=True)

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

        for section in config.itervalues():
            path = section.name
            if path in self.data:
                if section.get('IDs'):
                    self.data[path].mode = 'install'
                else:
                    self.data[path].mode = 'ignore'

    def refresh(self):
        """Read the menu configuration for this bottle"""
        self.read_config()
        self.query_config()

        self.new_menuroot = self.menuroot = get_menuroot(self.bottlename)

    def fast_refresh(self):
        """Read the menu configuration for this bottle"""
        self.read_config()

        self.new_menuroot = self.menuroot = get_menuroot(self.bottlename)

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

        # Find all changed associations, grouped by the new setting
        changes = {'ignore': [],
                   'install': [],
                  }
        for menu in self.data.values():
            if menu.new_mode != menu.mode:
                changes[menu.new_mode].append(menu.path)

        if changes['install']:
            run_cxmenu(('--bottle', self.bottlename,
                        '--mode', 'install',
                        '--install',
                        '--filter', ':'.join(changes['install'])))
        if changes['ignore']:
            run_cxmenu(('--bottle', self.bottlename,
                        '--mode', 'ignore',
                        '--uninstall',
                        '--filter', ':'.join(changes['ignore'])))
        if self.new_menuroot != self.menuroot:
            #FIXME: Don't bother with --install and --uninstall in previous
            # calls, if we're going to do this.
            set_menuroot(self.bottlename, self.new_menuroot)
            self.menuroot = self.new_menuroot

        for path in self.data:
            self.data[path].mode = self.data[path].new_mode

    def install_menus(self):
        run_cxmenu(('--bottle', self.bottlename, '--install'))

    def recreate_menus(self):
        run_cxmenu(('--bottle', self.bottlename, '--sync',
                    '--mode', 'install', '--removeall', '--install'))