Repository URL to install this package:
|
Version:
15.1.0-1 ▾
|
# (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'))