# (c) Copyright 2012-2015, CodeWeavers, Inc.
import collections
import os
import subprocess
import traceback
import gobject
gobject.threads_init()
import gtk
import pango
import bottlequery
import cxfsnotifier
import cxlog
import cxmenu
import cxproduct
import cxutils
import demoutils
import bottlecollection
import bottlemanagement
import cxguitools
import cxinstallerui
import cxprefsui
import cxregisterui
import cxrunui
import distversion
import packagebottledialog
import pyop
from cxutils import cxgettext as _
# Global dialog management
DIALOG = None
def open_or_show(restore=None):
# pylint: disable=W0603
global DIALOG
if DIALOG is None:
DIALOG = CrossOver(restore)
else:
DIALOG.present()
DIALOG.handle_arguments(restore)
return DIALOG
FAVORITES = 'FAVORITES'
CONTROL_PANEL = 'CONTROL_PANEL'
RUN_COMMAND = 'RUN_COMMAND'
ICON_VIEW = 'ICON_VIEW'
TREE_VIEW = 'TREE_VIEW'
_LARGE_ICON_SIZE = 48
_SMALL_ICON_SIZE = 16
def _icon_size_list(width, height):
return ('%sx%s' % (width, height), '256x256', '128x128', '64x64', '48x48', '32x32', '16x16', '')
def _get_icon(icon_filename, width, height):
icon = None
if icon_filename:
try:
icon = gtk.gdk.pixbuf_new_from_file(icon_filename)
except gobject.GError:
cxlog.warn("couldn't load icon file %s:\n%s" % (cxlog.debug_str(icon_filename), traceback.format_exc()))
if icon is None:
icon = cxguitools.get_std_icon('cxrun', _icon_size_list(width, height))
if icon is not None:
icon = icon.scale_simple(height, width, gtk.gdk.INTERP_BILINEAR)
return icon
ControlPanelApplet = collections.namedtuple('ControlPanelApplet', ('bottle', 'info'))
class CrossOver(object):
"""This is the CrossOver application"""
ccenter = None
def __init__(self, restore=None):
# Load & show the main CrossOver window
self.bottles = bottlecollection.sharedCollection(('basic',))
self.bottles.addChangeDelegate(self)
self.bottles.addBottleChangeDelegate(self)
toolbar_style = """
style "bottlebar"
{
GtkToolbar::button-relief = GTK_RELIEF_NORMAL
GtkToolbar::shadow-type = GTK_SHADOW_NONE
GtkToolbar::internal-padding = 4
GtkToolbar::space-size = 4
}
widget "*.*.BottleListToolbar" style "bottlebar"
"""
gtk.rc_parse_string(toolbar_style)
# Import widget modules so gtk.Builder() finds them
import placeholderentry # pylint: disable=W0612
self.crossover_gui = gtk.Builder()
self.crossover_gui.set_translation_domain("crossover")
self.crossover_gui.add_from_file(cxguitools.get_ui_path("crossover"))
self.crossover_gui.connect_signals(self)
self.main_window = self.crossover_gui.get_object("MainWindow")
self.aboutDialog = None
self.mainMenuBar = None
self.buttonDict = None
self.search_string = None
self.expanded_rows = None
self.update_applist_source = None
# This doesn't seem to be set by the builder xml?
self.crossover_gui.get_object("SearchEntry").set_property("secondary-icon-sensitive", False)
self.shutdown_failed_bottles = set() # bottles for which the last shutdown attempt failed
self.updating_description_bottles = set() # bottles for which we are updating the description
self.updating_default_checkbox = False
self.updating_csmt_checkbox = False
self.updating_dxvk_checkbox = False
self.bottle_menu_open = False
self.drag_start_x = None
self.drag_start_y = None
self.drag_obj = None
self.drag_pixbuf = None
global_config = cxproduct.get_config()
favorites_str = global_config["OfficeSetup"].get("Favorites")
if favorites_str:
self.favorites = set(favorites_str.decode('unicode_escape').split(u'\0'))
else:
self.favorites = set()
size = global_config["OfficeSetup"].get("BottleManagerSize")
if size:
width = height = None
if size.count('x') == 1:
widthstr, heightstr = size.split('x', 1)
if widthstr.isdigit() and heightstr.isdigit():
width = int(widthstr)
height = int(heightstr)
self.main_window.set_default_size(width, height)
if width is None:
cxlog.warn("Invalid BottleManagerSize %s" % cxlog.debug_str(size))
size = global_config["OfficeSetup"].get("BottleSidebarSize")
if size and size.isdigit():
self.crossover_gui.get_object("BottlePaned").set_position(int(size))
self._pane_size = None
self.crossover_gui.get_object("BottlePaned").connect('notify::position', self.on_bottlepane_move)
self.crossover_gui.get_object("BottleDescriptionText").get_buffer().connect('changed', self.on_description_changed)
self.crossover_gui.get_object("BottleDescriptionText").get_buffer().connect('insert-text', self.on_description_insert_text)
icon_theme = gtk.icon_theme_get_default()
try:
folder_icon = icon_theme.load_icon('folder', _SMALL_ICON_SIZE, gtk.ICON_LOOKUP_USE_BUILTIN)
self.folder_icon = folder_icon.scale_simple(_SMALL_ICON_SIZE, _SMALL_ICON_SIZE, gtk.gdk.INTERP_BILINEAR)
except: # pylint: disable=W0702
# No folder icon in theme?
self.folder_icon = None
toolbar_icon_size = gtk.icon_size_lookup(gtk.settings_get_default().get_property('gtk-toolbar-icon-size'))
try:
icon_theme.load_icon('emblem-favorite', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
self.crossover_gui.get_object("ToggleFavoriteButton").set_icon_name('emblem-favorite')
except: # pylint: disable=W0702
pass
self.notifier_bottles = set()
# do some fussing with the window layout before drawing.
iconImage = self.crossover_gui.get_object("CrossOverIconImage")
iconImage.set_from_file(os.path.join(cxutils.CX_ROOT,
"share",
"images",
"welcomeCrossOverIcon.png"))
self.selected_bottle = None
self.editing_bottle = None
self.view_with_selection = None
self.view = global_config["OfficeSetup"].get("MainWindowView")
if self.view not in (ICON_VIEW, TREE_VIEW):
self.view = ICON_VIEW
self.bottle_list_store = gtk.ListStore(object, str, str, object) #name, display markup, sort key, object
self.add_builtin_bottle_items()
self.update_bottle_list()
self.bottle_list_store.set_sort_column_id(2, gtk.SORT_ASCENDING)
bottle_list_view = self.crossover_gui.get_object("BottleListView")
bottle_list_view.set_model(self.bottle_list_store)
bottle_label = gtk.CellRendererText()
bottle_label.set_property('ellipsize', pango.ELLIPSIZE_END)
bottle_label.set_property('ellipsize-set', True)
bottle_label.connect('edited', self.bottle_name_edited)
bottle_label.connect('editing-started', self.bottle_name_edit_begin)
bottle_label.connect('editing-canceled', self.bottle_name_edit_cancel)
bottle_column = gtk.TreeViewColumn("Name", bottle_label, markup=1)
bottle_list_view.append_column(bottle_column)
self.bottle_label = bottle_label
self.bottle_column = bottle_column
bottle_list_view.get_selection().select_path((1,))
bottle_list_view.get_selection().connect("changed", self.on_BottleListView_select)
self.bottle_pane_showing = global_config["OfficeSetup"].get("ShowBottleSidebar") == '1'
self.installed_applications_store = gtk.ListStore(str)
self.installed_applications_store.set_sort_column_id(0, gtk.SORT_ASCENDING)
installed_applications_view = self.crossover_gui.get_object("BottleApplicationsView")
installed_applications_view.set_model(self.installed_applications_store)
app_label = gtk.CellRendererText()
app_label.set_property('ellipsize', pango.ELLIPSIZE_END)
app_label.set_property('ellipsize-set', True)
app_column = gtk.TreeViewColumn("Name", app_label, text=0)
installed_applications_view.append_column(app_column)
# item list store columns:
# 0: object
# 1: display markup
# 2: sort key
# 3: tooltip
# 4: identity key (for favorites, and checking if an item is the same as one in the store)
# 5: large icon
# 6: bottle name
# 7: small icon
# 8: bottle sort key
self.programs_list_store = gtk.ListStore(object, str, str, str, object, gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, str)
self.programs_list_store.set_sort_column_id(2, gtk.SORT_ASCENDING)
programs_icon_view = self.crossover_gui.get_object("ProgramsSectionIconView")
programs_icon_view.set_model(self.programs_list_store)
self.favorites_list_store = gtk.ListStore(object, str, str, str, object, gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, str)
self.favorites_list_store.set_sort_column_id(2, gtk.SORT_ASCENDING)
favorites_icon_view = self.crossover_gui.get_object("FavoritesSectionIconView")
favorites_icon_view.set_model(self.favorites_list_store)
self.control_panel_list_store = gtk.ListStore(object, str, str, str, object, gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, str)
self.control_panel_list_store.set_sort_column_id(2, gtk.SORT_ASCENDING)
control_panel_icon_view = self.crossover_gui.get_object("ControlPanelSectionIconView")
control_panel_icon_view.set_model(self.control_panel_list_store)
self.launchers_tree_store = gtk.TreeStore(object, str, str, str, object, gtk.gdk.Pixbuf, str, gtk.gdk.Pixbuf, str)
self.launchers_tree_store.set_sort_column_id(2, gtk.SORT_ASCENDING)
launchers_tree_view = self.crossover_gui.get_object("LaunchersTreeView")
launchers_tree_view.set_model(self.launchers_tree_store)
launchers_tree_view.set_tooltip_column(3)
launchers_name = gtk.TreeViewColumn(_("Name"))
icon = gtk.CellRendererPixbuf()
icon.set_fixed_size(_SMALL_ICON_SIZE, _SMALL_ICON_SIZE)
launchers_name.pack_start(icon, expand=False)
launchers_name.add_attribute(icon, 'pixbuf', 7)
label = gtk.CellRendererText()
label.set_property('ellipsize', pango.ELLIPSIZE_END)
label.set_property('ellipsize-set', True)
launchers_name.pack_start(label)
launchers_name.add_attribute(label, 'markup', 1)
launchers_name.set_resizable(True)
launchers_name.set_expand(True)
launchers_name.set_clickable(True)
launchers_name.set_sort_column_id(2)
launchers_tree_view.append_column(launchers_name)
bottle_column = gtk.TreeViewColumn(_("Bottle"))
bottle_label = gtk.CellRendererText()
bottle_label.set_property('ellipsize', pango.ELLIPSIZE_END)
bottle_label.set_property('ellipsize-set', True)
bottle_column.pack_start(bottle_label)
bottle_column.add_attribute(bottle_label, 'text', 6)
bottle_column.set_resizable(True)
bottle_column.set_expand(False)
bottle_column.set_clickable(True)
bottle_column.set_min_width(170) # not ideal, but the only way I could get this to have a reasonable width
bottle_column.set_sort_column_id(8)
launchers_tree_view.append_column(bottle_column)
launchers_tree_view.get_selection().connect('changed', self.on_TreeSelection_changed)
if distversion.HAS_MULTI_USER and \
(cxproduct.is_root_install() or global_config["OfficeSetup"].get("NonRootPublish", "0") == "1"):
self.crossover_gui.get_object("PublishBottleMenu").show()
self.crossover_gui.get_object("PublishBottleCMenu").show()
self.crossover_gui.get_object("UpdatePublishedBottleMenu").show()
self.crossover_gui.get_object("UpdatePublishedBottleCMenu").show()
else:
self.crossover_gui.get_object("PublishBottleMenu").hide()
self.crossover_gui.get_object("PublishBottleCMenu").hide()
self.crossover_gui.get_object("UpdatePublishedBottleMenu").hide()
self.crossover_gui.get_object("UpdatePublishedBottleCMenu").hide()
self.crossover_gui.get_object("BottlePropertiesSidebar").hide()
cxfsnotifier.add_observer(
os.path.join(cxutils.CX_ROOT, "etc", "license.txt"),
self.on_license_changed)
cxfsnotifier.add_observer(
os.path.join(cxutils.CX_ROOT, "etc", "license.sig"),
self.on_license_changed)
self.check_registration()
self.toggle_bottle_pane(self.bottle_pane_showing)
self.set_view(self.view)
self.updateAppList()
self.update_file_menu()
self.main_window.show()
self.handle_arguments(restore)
def handle_arguments(self, restore=None):
if restore is not None:
RestoreArchiveController(self, cxutils.uri_to_path(restore))
def present(self):
self.main_window.present()
def check_registration(self):
license_file = os.path.join(cxutils.CX_ROOT, "etc", "license.txt")
sig_file = os.path.join(cxutils.CX_ROOT, "etc", "license.sig")
(isdemo, _username, _date, _licenseid, _revoked) = demoutils.demo_status(license_file, sig_file)
self.crossover_gui.get_object("UnlockCrossOverMenu").set_property('visible', isdemo)
def on_license_changed(self, _event, _path, _observer_data):
gobject.idle_add(self.check_registration)
def on_ToggleShowBottlesButton_style_set(self, widget, _previous_style):
toolbar_icon_size = gtk.icon_size_lookup(gtk.settings_get_default().get_property('gtk-toolbar-icon-size'))
bottles_icon = cxguitools.get_std_icon('bottles', ('%sx%s' % toolbar_icon_size, '24x24'))
if bottles_icon:
bottles_icon = cxguitools.recolor_pixbuf(bottles_icon, widget.style.fg[gtk.STATE_NORMAL])
bottles_icon = bottles_icon.scale_simple(toolbar_icon_size[0], toolbar_icon_size[1], gtk.gdk.INTERP_BILINEAR)
bottles_image = gtk.Image()
bottles_image.set_from_pixbuf(bottles_icon)
widget.set_icon_widget(bottles_image)
bottles_image.show()
def on_ViewAsIconsButton_style_set(self, widget, _previous_style):
toolbar_icon_size = gtk.icon_size_lookup(gtk.settings_get_default().get_property('gtk-toolbar-icon-size'))
icon_theme = gtk.icon_theme_get_default()
try:
icon_theme.load_icon('view-grid', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
icon_theme.load_icon('view-list', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
widget.set_icon_name('view-grid')
except: # pylint: disable=W0702
try:
icon_theme.load_icon('view-list-symbolic', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
button_icon = icon_theme.load_icon('view-grid-symbolic', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
button_icon = cxguitools.recolor_pixbuf(button_icon, widget.style.fg[gtk.STATE_NORMAL], symbolic=True)
button_icon = button_icon.scale_simple(toolbar_icon_size[0], toolbar_icon_size[1], gtk.gdk.INTERP_BILINEAR)
button_image = gtk.Image()
button_image.set_from_pixbuf(button_icon)
widget.set_icon_widget(button_image)
button_image.show()
except: # pylint: disable=W0702
button_icon = cxguitools.get_std_icon('cxviewicons', ('%sx%s' % toolbar_icon_size, '24x24'))
if button_icon:
button_icon = cxguitools.recolor_pixbuf(button_icon, widget.style.text[gtk.STATE_NORMAL])
button_icon = button_icon.scale_simple(toolbar_icon_size[0], toolbar_icon_size[1], gtk.gdk.INTERP_BILINEAR)
button_image = gtk.Image()
button_image.set_from_pixbuf(button_icon)
widget.set_icon_widget(button_image)
button_image.show()
def on_ViewAsTreeButton_style_set(self, widget, _previous_style):
toolbar_icon_size = gtk.icon_size_lookup(gtk.settings_get_default().get_property('gtk-toolbar-icon-size'))
icon_theme = gtk.icon_theme_get_default()
try:
icon_theme.load_icon('view-list', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
icon_theme.load_icon('view-grid', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
widget.set_icon_name('view-list')
except: # pylint: disable=W0702
try:
icon_theme.load_icon('view-grid-symbolic', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
button_icon = icon_theme.load_icon('view-list-symbolic', toolbar_icon_size[0], gtk.ICON_LOOKUP_USE_BUILTIN)
button_icon = cxguitools.recolor_pixbuf(button_icon, widget.style.fg[gtk.STATE_NORMAL], symbolic=True)
button_icon = button_icon.scale_simple(toolbar_icon_size[0], toolbar_icon_size[1], gtk.gdk.INTERP_BILINEAR)
button_image = gtk.Image()
button_image.set_from_pixbuf(button_icon)
widget.set_icon_widget(button_image)
button_image.show()
except: # pylint: disable=W0702
button_icon = cxguitools.get_std_icon('cxviewlist', ('%sx%s' % toolbar_icon_size, '24x24'))
if button_icon:
button_icon = button_icon.scale_simple(toolbar_icon_size[0], toolbar_icon_size[1], gtk.gdk.INTERP_BILINEAR)
button_icon = cxguitools.recolor_pixbuf(button_icon, widget.style.fg[gtk.STATE_NORMAL])
button_image = gtk.Image()
button_image.set_from_pixbuf(button_icon)
widget.set_icon_widget(button_image)
button_image.show()
def can_use_bottle(self, bottle_name):
try:
bottle = self.bottles.bottleObject(bottle_name)
except KeyError:
return False
else:
for status in bottle.status_overrides:
if status not in (bottle.STATUS_INIT, bottle.STATUS_UPGRADE,
bottle.STATUS_READY, bottle.STATUS_DEFAULTING,
bottle.STATUS_DOWN):
return False
return True
def get_bottle_status_text(self, bottle):
for status in bottle.status_overrides:
if status not in (bottle.STATUS_INIT, bottle.STATUS_UPGRADE,
bottle.STATUS_READY, bottle.STATUS_DEFAULTING,
bottle.STATUS_DOWN):
return status
return None
def get_bottle_markup(self, bottle, indent=" "):
html_text = cxutils.html_escape(bottle.name)
if bottle.is_default:
html_text = '<b>' + html_text + '</b>'
status_text = self.get_bottle_status_text(bottle)
if status_text:
html_text += (" (%s)" % cxutils.html_escape(status_text))
return indent + html_text
def _get_iconview_cell_renderers(self, show_bottle):
result = []
icon = gtk.CellRendererPixbuf()
icon.set_fixed_size(_LARGE_ICON_SIZE, _LARGE_ICON_SIZE)
result.append((icon, (('pixbuf', 5),)))
label = gtk.CellRendererText()
label.set_property('width', _LARGE_ICON_SIZE * 3)
label.set_property('wrap-width', _LARGE_ICON_SIZE * 3)
label.set_property('wrap-mode', pango.WRAP_WORD_CHAR)
label.set_property('alignment', pango.ALIGN_CENTER)
result.append((label, (('markup', 1),)))
if show_bottle:
label = gtk.CellRendererText()
label.set_property('width', _LARGE_ICON_SIZE * 3)
label.set_property('ellipsize', pango.ELLIPSIZE_END)
label.set_property('ellipsize-set', True)
label.set_property('scale', 0.8)
label.set_property('scale-set', True)
label.set_property('alignment', pango.ALIGN_CENTER)
result.append((label, (('text', 6),)))
return result
def _get_iconview_item_box(self, iconview, path):
# How is this not part of the API?
model = iconview.get_model()
item_sizes = []
max_width = 0
show_bottle = not self.selected_bottle or iconview == self.crossover_gui.get_object("FavoritesSectionIconView") or self.bottles.bottleCount() == 1
cell_renderers = self._get_iconview_cell_renderers(show_bottle)
num_items = 0
iterator = model.get_iter_first()
while iterator:
item_height = 0
item_width = 0
for renderer, attributes in cell_renderers:
for attribute, column in attributes:
renderer.set_property(attribute, model.get_value(iterator, column))
_xofs, _yofs, width, height = renderer.get_size(iconview)
item_width = max(item_width, width)
item_height += height
item_sizes.append((item_width, item_height))
max_width = max(item_width, max_width)
iterator = model.iter_next(iterator)
num_items += 1
margin = iconview.get_margin()
padding = iconview.get_property('item-padding')
row_spacing = iconview.get_row_spacing()
column_spacing = iconview.get_column_spacing()
focus_line_width = iconview.style_get_property('focus-line-width')
allocation = iconview.get_allocation()
num_columns = max(1, (allocation.width - margin * 2 + column_spacing) // (max_width + padding * 2 + column_spacing + focus_line_width * 2))
y = margin
for i in range(0, path[0] + 1, num_columns):
x = margin
row_height = 0
for j in range(num_columns):
if i + j < num_items:
row_height = max(row_height, item_sizes[i + j][1])
if i + j < path[0]:
x += item_sizes[i + j][0] + padding * 2 + column_spacing + focus_line_width * 2
if i + num_columns <= path[0]:
y += row_height + padding * 2 + row_spacing + focus_line_width * 2
return x, y, item_sizes[path[0]][0]+padding*2+focus_line_width*2, row_height+padding*2+focus_line_width*2
def setup_icon_view(self, icon_view, show_bottle):
icon_view.clear()
for renderer, attributes in self._get_iconview_cell_renderers(show_bottle):
icon_view.pack_start(renderer)
for attribute, column in attributes:
icon_view.add_attribute(renderer, attribute, column)
icon_view.set_tooltip_column(3)
def add_builtin_bottle_items(self):
newrow = self.bottle_list_store.append()
self.bottle_list_store.set_value(newrow, 0, 0)
self.bottle_list_store.set_value(newrow, 1, "<b>"+_("Collections")+"</b>")
self.bottle_list_store.set_value(newrow, 2, "A")
newrow = self.bottle_list_store.append()
self.bottle_list_store.set_value(newrow, 0, 1)
self.bottle_list_store.set_value(newrow, 1, " "+_("All Bottles"))
self.bottle_list_store.set_value(newrow, 2, "B")
newrow = self.bottle_list_store.append()
self.bottle_list_store.set_value(newrow, 0, 2)
self.bottle_list_store.set_value(newrow, 1, "<b>"+_("Bottles")+"</b>")
self.bottle_list_store.set_value(newrow, 2, "C")
def update_bottle_list(self):
current_bottles = set(self.bottles.bottles())
seen_bottles = set()
for bottle in current_bottles - self.notifier_bottles:
for path in (cxproduct.get_bottle_path(), cxproduct.get_managed_bottle_path()):
for dirpath in path.split(":"):
cxfsnotifier.add_observer(
os.path.join(dirpath, bottle.name, 'cxmenu.conf'),
self.on_cxmenuconf_changed)
for bottle in self.notifier_bottles - current_bottles:
for path in (cxproduct.get_bottle_path(), cxproduct.get_managed_bottle_path()):
for dirpath in path.split(":"):
cxfsnotifier.remove_observer(
os.path.join(dirpath, bottle.name, 'cxmenu.conf'),
self.on_cxmenuconf_changed)
self.notifier_bottles = current_bottles.copy()
if self.selected_bottle and self.selected_bottle not in current_bottles:
self.selected_bottle = None
self.crossover_gui.get_object("BottleListView").get_selection().select_path((1,))
any_published_bottles = any(bottle.is_managed for bottle in current_bottles)
seen_published_section = False
iterator = self.bottle_list_store.get_iter_first()
while iterator:
iter_next = self.bottle_list_store.iter_next(iterator)
current_sort = self.bottle_list_store.get_value(iterator, 2)
bottle_obj = self.bottle_list_store.get_value(iterator, 3)
if current_sort == 'C':
# Private bottles list, or maybe just bottles
current_markup = self.bottle_list_store.get_value(iterator, 1)
if any_published_bottles:
new_markup = "<b>"+_("Private Bottles")+"</b>"
else:
new_markup = "<b>"+_("Bottles")+"</b>"
if not isinstance(new_markup, str):
new_markup = new_markup.encode('utf8')
if current_markup != new_markup:
self.bottle_list_store.set_value(iterator, 1, new_markup)
elif current_sort == 'F':
seen_published_section = True
if not any_published_bottles:
self.bottle_list_store.remove(iterator)
elif bottle_obj is None or bottle_obj in seen_bottles:
pass
elif bottle_obj in current_bottles:
current_name = self.bottle_list_store.get_value(iterator, 0)
if current_name != bottle_obj.name:
self.bottle_list_store.set_value(iterator, 0, bottle_obj.name)
current_markup = self.bottle_list_store.get_value(iterator, 1)
new_markup = self.get_bottle_markup(bottle_obj)
if not isinstance(new_markup, str):
new_markup = new_markup.encode('utf8')
if current_markup != new_markup:
self.bottle_list_store.set_value(iterator, 1, new_markup)
current_sort = self.bottle_list_store.get_value(iterator, 2)
if bottle_obj.is_managed:
if bottle_obj.is_default:
new_sort = "G"
else:
new_sort = "H"+bottle_obj.name
else:
if bottle_obj.is_default:
new_sort = "D"
else:
new_sort = "E"+bottle_obj.name
if not isinstance(new_sort, str):
new_sort = new_sort.encode('utf8')
if current_sort != new_sort:
self.bottle_list_store.set_value(iterator, 2, new_sort)
current_bottles.remove(bottle_obj)
seen_bottles.add(bottle_obj)
else:
self.bottle_list_store.remove(iterator)
iterator = iter_next
for bottle in current_bottles:
newrow = self.bottle_list_store.append()
self.bottle_list_store.set_value(newrow, 0, bottle.name)
self.bottle_list_store.set_value(newrow, 1, self.get_bottle_markup(bottle))
if bottle.is_managed:
if bottle.is_default:
self.bottle_list_store.set_value(newrow, 2, "G")
else:
self.bottle_list_store.set_value(newrow, 2, "H"+bottle.name)
else:
if bottle.is_default:
self.bottle_list_store.set_value(newrow, 2, "D")
else:
self.bottle_list_store.set_value(newrow, 2, "E"+bottle.name)
self.bottle_list_store.set_value(newrow, 3, bottle)
if any_published_bottles and not seen_published_section:
newrow = self.bottle_list_store.append()
self.bottle_list_store.set_value(newrow, 0, 3)
self.bottle_list_store.set_value(newrow, 1, "<b>"+_("Published Bottles")+"</b>")
self.bottle_list_store.set_value(newrow, 2, "F")
def on_BottleListView_popup_menu(self, _widget):
menu = self.crossover_gui.get_object("BottleContextMenu")
if menu:
# FIXME: position menu based on the selection location?
menu.popup(None, None, None, 0, 0)
return True # popped up
return False
def on_BottleListView_button_press_event(self, treeview, event):
if event.button == 3: # right click
path = treeview.get_path_at_pos(int(event.x), int(event.y))
if path is None:
return False # propagate
path, _column, _x, _y = path
treeview.get_selection().select_path(path)
menu = self.crossover_gui.get_object("BottleContextMenu")
if menu:
menu.popup(None, None, None, event.button, event.time)
return True
return False # propagate
def bottle_button_pos(self, menu):
_menu_width, menu_height = menu.size_request()
button = self.crossover_gui.get_object("BottleMenuButton")
button_x, button_y = button.window.get_origin()
button_allocation = button.get_allocation()
button_x += button_allocation.x
button_y += button_allocation.y
button_height = button_allocation.height
button_screen = button.window.get_screen()
monitor_frame = button_screen.get_monitor_geometry(button_screen.get_monitor_at_window(button.window))
fits_below = (button_y + button_height + menu_height <= monitor_frame.y + monitor_frame.height)
fits_above = (button_y - menu_height >= monitor_frame.y)
if fits_below:
return button_x, button_y + button_height, True
if fits_above:
return button_x, button_y - menu_height, True
return button_x, button_y + button_height, False
def on_BottleMenuButton_button_press_event(self, widget, event):
if self.bottle_menu_open or event.button != 1:
return False # propagate
menu = self.crossover_gui.get_object("BottleContextMenu")
menu.popup(None, None, self.bottle_button_pos, event.button, event.time)
self.bottle_menu_open = True
widget.set_active(True)
return True
def on_BottleMenuButton_clicked(self, widget):
if self.bottle_menu_open or not widget.get_active():
return
menu = self.crossover_gui.get_object("BottleContextMenu")
menu.popup(None, None, self.bottle_button_pos, 0, 0)
self.bottle_menu_open = True
def on_BottleContextMenu_deactivate(self, _menushell):
self.bottle_menu_open = False
button = self.crossover_gui.get_object("BottleMenuButton")
button.set_active(False)
def bottle_name_insert_text(self, caller, new_text, _length, _user_data):
# pylint: disable=R0201
name = caller.get_text()
position = caller.get_position()
name = name[:position] + new_text + name[position:]
if not cxutils.is_valid_bottlename(name):
caller.emit_stop_by_name("insert-text")
def bottle_name_edit_begin(self, cellrenderer, editable, path):
editable.connect("insert-text", self.bottle_name_insert_text)
editing_bottle = self.bottle_list_store.get_value(self.bottle_list_store.get_iter(path), 3)
if not editing_bottle or not self.can_use_bottle(editing_bottle.name):
cellrenderer.stop_editing(True)
return
self.editing_bottle = editing_bottle
editable.set_text(cxutils.sanitize_bottlename(editing_bottle.name))
def bottle_name_edit_cancel(self, _cellrenderer):
self.editing_bottle = None
def bottle_name_edited(self, _cellrenderer, _path, new_text):
if not self.can_use_bottle(self.editing_bottle.name):
return
old_text = self.editing_bottle.name
new_text = new_text.decode('utf8')
if old_text != new_text:
op = RenameBottleOperation(self.editing_bottle, new_text, self)
pyop.sharedOperationQueue.enqueue(op)
self.editing_bottle = None
def on_RenameBottleMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name):
return
iterator = self.bottle_list_store.get_iter_first()
while iterator:
bottle_obj = self.bottle_list_store.get_value(iterator, 3)
if bottle_obj == self.selected_bottle:
path = self.bottle_list_store.get_path(iterator)
break
else:
iterator = self.bottle_list_store.iter_next(iterator)
else:
return
self.crossover_gui.get_object("BottleListView").set_cursor_on_cell(path, self.bottle_column, self.bottle_label, True)
def on_OpenCDriveMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name):
return
environ = bottlequery.get_win_environ(self.selected_bottle.name, ("SystemDrive",))
drive = bottlequery.expand_win_string(environ, "%SystemDrive%")
drive = bottlequery.get_native_path(self.selected_bottle.name, drive)
args = ["xdg-open", drive]
try:
subprocess.Popen(args, close_fds=True)
except: # pylint: disable=W0702
traceback.print_exc()
def on_ExportArchiveMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name):
return
bottle = self.selected_bottle
file_chooser = gtk.FileChooserDialog(_("Choose an Archive Name and Location"), self.main_window, gtk.FILE_CHOOSER_ACTION_SAVE)
cxguitools.add_filters(file_chooser, cxguitools.FILTERS_CXARCHIVES)
file_chooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
file_chooser.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_OK)
file_chooser.set_current_name(bottle.name + ".cxarchive")
file_chooser.set_do_overwrite_confirmation(True)
cxguitools.set_default_extension(file_chooser, 'cxarchive')
if file_chooser.run() == gtk.RESPONSE_OK:
filename = file_chooser.get_filename()
archiveOp = ArchiveBottleOperation(bottle, filename)
pyop.sharedOperationQueue.enqueue(archiveOp)
file_chooser.destroy()
def on_PublishBottleMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name):
return
PublishBottleController(self, self.selected_bottle.name)
def on_UpdatePublishedBottleMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name) or \
self.selected_bottle.up_to_date:
return
pyop.sharedOperationQueue.enqueue(UpgradeBottleOperation(self.selected_bottle))
def on_CreatePackageMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name):
return
if ' ' in self.selected_bottle.name:
cxguitools.CXMessageDlg(
_("The bottle '%s' cannot be packaged because it has a space in the name. Please rename it and try again.") % self.selected_bottle.name,
buttons=gtk.BUTTONS_OK,
parent=self.main_window,
message_type=gtk.MESSAGE_ERROR)
return
packagebottledialog.PackageBottleController(self.selected_bottle)
def on_DefaultBottleMenu_activate(self, menuitem):
if self.updating_default_checkbox or not self.selected_bottle:
return
if menuitem.get_active() != self.selected_bottle.is_default:
setDefaultBottleOp = SetDefaultBottleOperation(self.selected_bottle, menuitem.get_active())
pyop.sharedOperationQueue.enqueue(setDefaultBottleOp)
def on_CsmtToggleMenu_activate(self, menuitem):
if self.updating_csmt_checkbox or not self.selected_bottle:
return
if menuitem.get_active() != (self.selected_bottle.is_csmt_disabled_state == self.selected_bottle.STATUS_CSMT_ENABLED):
setCsmtOp = SetCsmtOperation(self.selected_bottle, menuitem.get_active())
pyop.sharedOperationQueue.enqueue(setCsmtOp)
def on_DxvkToggleMenu_activate(self, menuitem):
if self.updating_dxvk_checkbox or not self.selected_bottle:
return
if menuitem.get_active() != (self.selected_bottle.is_dxvk_enabled_state == self.selected_bottle.STATUS_DXVK_ENABLED):
setDxvkOp = SetDxvkOperation(self.selected_bottle, menuitem.get_active())
pyop.sharedOperationQueue.enqueue(setDxvkOp)
def on_ResetWebBrowserMenu_activate(self, _menuitem):
if not self.selected_bottle:
return
bottlequery.set_native_browser(self.selected_bottle.name)
def on_BottleInstallSoftwareMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name):
return
cxinstallerui.open_or_show(bottle=self.selected_bottle.name)
def on_RunCommandMenu_activate(self, _menuitem=None):
if self.selected_bottle:
cxrunui.open_or_show(self.selected_bottle.name)
else:
cxrunui.open_or_show(None)
def on_QuitBottleMenu_activate(self, _menuitem):
if not self.selected_bottle or not self.can_use_bottle(self.selected_bottle.name):
return
quitBottleOp = bottlecollection.QuitBottleOperation([self.selected_bottle.name], False, self)
pyop.sharedOperationQueue.enqueue(quitBottleOp)
def on_ForceQuitBottleMenu_activate(self, _menuitem):
if not self.selected_bottle:
return
quitBottleOp = bottlecollection.QuitBottleOperation([self.selected_bottle.name], True, self)
pyop.sharedOperationQueue.enqueue(quitBottleOp)
def on_BottlePropertiesMenu_activate(self, _menuitem):
self.crossover_gui.get_object("BottlePropertiesSidebar").show()
def on_BottlePropertiesClose_clicked(self, _menuitem):
self.crossover_gui.get_object("BottlePropertiesSidebar").hide()
def on_BottlePropertiesSidebar_show(self, _widget):
self.updateAppList()
def opFinished(self, operation):
if isinstance(operation, bottlecollection.QuitBottleOperation):
for bottle in operation.bottleList:
if operation.succeeded:
self.shutdown_failed_bottles.discard(bottle)
else:
self.shutdown_failed_bottles.add(bottle)
self.update_bottle_list()
self.updateAppList()
def update_item_list(self, treestore, contents, parent_iterator=None):
result = False
contents = contents.copy()
any_preserved_contents = False
dummy_row = None
if parent_iterator:
iterator = treestore.iter_children(parent_iterator)
else:
iterator = treestore.get_iter_first()
while iterator:
key = treestore.get_value(iterator, 4)
if key in contents:
any_preserved_contents = True
treestore.set_value(iterator, 0, contents[key])
if isinstance(contents[key], dict):
if self.update_item_list(treestore, contents[key], iterator):
result = True
del contents[key]
iterator = treestore.iter_next(iterator)
else:
result = True
if not any_preserved_contents and parent_iterator and \
contents and not treestore.iter_next(iterator):
# If we remove all the items and then add other items, GTK
# will collapse the section, so add a dummy item to prevent this.
dummy_row = treestore.append(parent_iterator)
if not treestore.remove(iterator) or dummy_row:
# no more rows
break
if contents:
result = True
for (key, obj) in contents.iteritems():
if isinstance(treestore, gtk.TreeStore):
newrow = treestore.append(parent_iterator)
else:
newrow = treestore.append()
treestore.set_value(newrow, 0, obj)
treestore.set_value(newrow, 4, key)
if isinstance(obj, dict):
if key.startswith('folder/'):
name = key.split('/')[1]
treestore.set_value(newrow, 1, cxutils.html_escape(name))
treestore.set_value(newrow, 2, 'B'+name)
treestore.set_value(newrow, 8, 'B'+name)
elif key == FAVORITES:
treestore.set_value(newrow, 1, cxutils.html_escape(_("Favorites")))
treestore.set_value(newrow, 2, 'A')
treestore.set_value(newrow, 8, 'A')
elif key == CONTROL_PANEL:
treestore.set_value(newrow, 1, cxutils.html_escape(_("Control Panels")))
treestore.set_value(newrow, 2, 'D')
treestore.set_value(newrow, 8, 'D')
treestore.set_value(newrow, 3, '')
treestore.set_value(newrow, 5, None) # Folder large icon is unused
treestore.set_value(newrow, 6, '')
treestore.set_value(newrow, 7, self.folder_icon)
self.update_item_list(treestore, obj, newrow)
elif isinstance(obj, cxmenu.MenuItem):
treestore.set_value(newrow, 1, cxutils.html_escape(obj.menu_name()))
treestore.set_value(newrow, 2, 'C'+obj.menu_name())
treestore.set_value(newrow, 3, obj.parent.bottlename)
treestore.set_value(newrow, 5, _get_icon(
obj.get_iconfile(_icon_size_list(_LARGE_ICON_SIZE, _LARGE_ICON_SIZE), 'cxrun'),
_LARGE_ICON_SIZE, _LARGE_ICON_SIZE))
treestore.set_value(newrow, 6, obj.parent.bottlename)
treestore.set_value(newrow, 7, _get_icon(
obj.get_iconfile(_icon_size_list(_SMALL_ICON_SIZE, _SMALL_ICON_SIZE), 'cxrun'),
_SMALL_ICON_SIZE, _SMALL_ICON_SIZE))
treestore.set_value(newrow, 8, 'C'+obj.parent.bottlename+'/'+obj.menu_name())
elif isinstance(obj, ControlPanelApplet):
treestore.set_value(newrow, 1, cxutils.html_escape(obj.info['name']))
treestore.set_value(newrow, 2, 'C'+obj.info['name'])
treestore.set_value(newrow, 3, obj.info['description'])
treestore.set_value(newrow, 5, _get_icon(obj.info['icon'], _LARGE_ICON_SIZE, _LARGE_ICON_SIZE))
treestore.set_value(newrow, 6, obj.bottle.name)
treestore.set_value(newrow, 7, _get_icon(obj.info['icon'], _SMALL_ICON_SIZE, _SMALL_ICON_SIZE))
treestore.set_value(newrow, 8, 'C'+obj.bottle.name+'/'+obj.info['name'])
elif obj == RUN_COMMAND:
treestore.set_value(newrow, 1, _(u"Run Command\u2026"))
treestore.set_value(newrow, 2, 'E')
treestore.set_value(newrow, 3, '')
treestore.set_value(newrow, 5, _get_icon(None, _LARGE_ICON_SIZE, _LARGE_ICON_SIZE))
treestore.set_value(newrow, 6, '')
treestore.set_value(newrow, 7, _get_icon(None, _SMALL_ICON_SIZE, _SMALL_ICON_SIZE))
treestore.set_value(newrow, 8, 'E')
if dummy_row:
treestore.remove(dummy_row)
return result
def is_favorite(self, key):
return cxutils.string_to_unicode(key) in self.favorites
def save_favorites_config(self):
cxproduct.save_setting('OfficeSetup', 'Favorites',
u'\0'.join(self.favorites).encode('unicode_escape'))
def add_favorite(self, key):
self.favorites.add(cxutils.string_to_unicode(key))
self.update_file_menu()
self.updateAppList()
self.save_favorites_config()
def remove_favorite(self, key):
self.favorites.discard(cxutils.string_to_unicode(key))
self.update_file_menu()
self.updateAppList()
self.save_favorites_config()
def get_selected_item(self):
if isinstance(self.view_with_selection, gtk.IconView):
selected_items = self.view_with_selection.get_selected_items()
if selected_items:
path = selected_items[0]
model = self.view_with_selection.get_model()
iterator = model.get_iter(path)
return model.get_value(iterator, 0)
elif isinstance(self.view_with_selection, gtk.TreeView):
model, iterator = self.view_with_selection.get_selection().get_selected()
if iterator:
return model.get_value(iterator, 0)
return None
def get_selected_key(self):
if isinstance(self.view_with_selection, gtk.IconView):
selected_items = self.view_with_selection.get_selected_items()
if selected_items:
path = selected_items[0]
model = self.view_with_selection.get_model()
iterator = model.get_iter(path)
return model.get_value(iterator, 4)
elif isinstance(self.view_with_selection, gtk.TreeView):
model, iterator = self.view_with_selection.get_selection().get_selected()
if iterator:
return model.get_value(iterator, 4)
return None
def update_file_menu(self):
obj = self.get_selected_item()
name = None
if isinstance(obj, cxmenu.MenuItem):
name = obj.menu_name()
elif obj == RUN_COMMAND:
name = _(u"Run Command\u2026")
if isinstance(obj, cxmenu.MenuItem) or obj == RUN_COMMAND:
self.crossover_gui.get_object("OpenAppMenuItem").set_label(_("Open '%s'") % name)
self.crossover_gui.get_object("OpenAppMenuItem").set_sensitive(True)
else:
self.crossover_gui.get_object("OpenAppMenuItem").set_label(_("Open"))
self.crossover_gui.get_object("OpenAppMenuItem").set_sensitive(False)
if isinstance(obj, cxmenu.MenuItem) and obj.type == 'windows':
self.crossover_gui.get_object("RunWithOptionsMenuItem").set_label(_(u"Run '%s' with Options\u2026") % name)
self.crossover_gui.get_object("RunWithOptionsMenuItem").set_sensitive(True)
else:
self.crossover_gui.get_object("RunWithOptionsMenuItem").set_label(_(u"Run with Options\u2026"))
self.crossover_gui.get_object("RunWithOptionsMenuItem").set_sensitive(False)
if not obj or isinstance(obj, dict):
self.crossover_gui.get_object("ToggleFavoriteMenuItem").set_label(_("Add to Favorites"))
self.crossover_gui.get_object("ToggleFavoriteMenuItem").set_sensitive(False)
self.crossover_gui.get_object("ToggleFavoriteButton").set_tooltip_text(_("Add to Favorites"))
self.crossover_gui.get_object("ToggleFavoriteButton").set_sensitive(False)
else:
key = self.get_selected_key()
if self.is_favorite(key):
self.crossover_gui.get_object("ToggleFavoriteMenuItem").set_label(_("Remove '%s' from Favorites") % name)
self.crossover_gui.get_object("ToggleFavoriteButton").set_tooltip_text(_("Remove '%s' from Favorites") % name)
self.crossover_gui.get_object("ToggleFavoriteButton").set_active(True)
else:
self.crossover_gui.get_object("ToggleFavoriteMenuItem").set_label(_("Add '%s' to Favorites") % name)
self.crossover_gui.get_object("ToggleFavoriteButton").set_tooltip_text(_("Add '%s' to Favorites") % name)
self.crossover_gui.get_object("ToggleFavoriteButton").set_active(False)
self.crossover_gui.get_object("ToggleFavoriteMenuItem").set_sensitive(True)
self.crossover_gui.get_object("ToggleFavoriteButton").set_sensitive(True)
def build_item_popup_menu(self):
obj = self.get_selected_item()
if not obj:
return None
result = gtk.Menu()
open_item = gtk.MenuItem(_("Open"))
open_item.show()
open_item.connect('activate', self.on_OpenAppMenuItem_activate)
result.append(open_item)
run_item = gtk.MenuItem(_(u"Run with Options\u2026"))
run_item.show()
run_item.connect('activate', self.on_RunWithOptionsMenuItem_activate)
result.append(run_item)
sep = gtk.SeparatorMenuItem()
sep.show()
result.append(sep)
fav_item = gtk.MenuItem()
fav_item.show()
fav_item.connect('activate', self.on_ToggleFavoriteMenuItem_activate)
result.append(fav_item)
if isinstance(obj, cxmenu.MenuItem) or obj == RUN_COMMAND:
open_item.set_sensitive(True)
else:
open_item.set_sensitive(False)
if isinstance(obj, cxmenu.MenuItem) and obj.type == 'windows':
run_item.set_sensitive(True)
else:
run_item.set_sensitive(False)
if isinstance(obj, dict):
fav_item.set_label(_("Add to Favorites"))
fav_item.set_sensitive(False)
else:
key = self.get_selected_key()
if self.is_favorite(key):
fav_item.set_label(_("Remove from Favorites"))
else:
fav_item.set_label(_("Add to Favorites"))
fav_item.set_sensitive(True)
return result
def on_IconView_popup_menu(self, iconview):
if iconview is not self.view_with_selection:
return False
menu = self.build_item_popup_menu()
if menu:
# FIXME: position menu based on the selection location?
menu.popup(None, None, None, 0, 0)
return True # popped up
return False
def on_TreeView_popup_menu(self, treeview):
if treeview is not self.view_with_selection:
return False
menu = self.build_item_popup_menu()
if menu:
# FIXME: position menu based on the selection location?
menu.popup(None, None, None, 0, 0)
return True # popped up
return False
def on_IconView_button_press_event(self, iconview, event):
if event.button == 3: # right click
path = iconview.get_path_at_pos(int(event.x), int(event.y))
if path is None:
return False # propagate
iconview.select_path(path)
menu = self.build_item_popup_menu()
if menu:
menu.popup(None, None, None, event.button, event.time)
return True
elif event.button == 1: # left click
path = iconview.get_path_at_pos(int(event.x), int(event.y))
if path is None:
self.drag_start_x = self.drag_start_y = None
self.drag_obj = None
self.drag_pixbuf = None
return False # propagate
model = iconview.get_model()
iterator = model.get_iter(path)
obj = model.get_value(iterator, 0)
if isinstance(obj, cxmenu.MenuItem):
self.drag_start_x = int(event.x)
self.drag_start_y = int(event.y)
self.drag_obj = obj
self.drag_pixbuf = model.get_value(iterator, 5)
else:
self.drag_start_x = self.drag_start_y = None
self.drag_obj = None
self.drag_pixbuf = None
return False # propagate
def on_TreeView_button_press_event(self, treeview, event):
if event.button == 3: # right click
path = treeview.get_path_at_pos(int(event.x), int(event.y))
if path is None:
return False # propagate
path, _column, _x, _y = path
treeview.get_selection().select_path(path)
menu = self.build_item_popup_menu()
if menu:
menu.popup(None, None, None, event.button, event.time)
return True
elif event.button == 1: # left click
path = treeview.get_path_at_pos(int(event.x), int(event.y))
if path is None:
self.drag_start_x = self.drag_start_y = None
self.drag_obj = None
self.drag_pixbuf = None
return False # propagate
path, _column, _x, _y = path
model = treeview.get_model()
iterator = model.get_iter(path)
obj = model.get_value(iterator, 0)
if isinstance(obj, cxmenu.MenuItem):
self.drag_start_x = int(event.x)
self.drag_start_y = int(event.y)
self.drag_obj = obj
self.drag_pixbuf = model.get_value(iterator, 5)
else:
self.drag_start_x = self.drag_start_y = None
self.drag_obj = None
self.drag_pixbuf = None
return False # propagate
def on_TreeView_motion_notify_event(self, treeview, event):
if (event.state & gtk.gdk.BUTTON1_MASK) and self.drag_start_x is not None:
if treeview.drag_check_threshold(self.drag_start_x, self.drag_start_y, int(event.x), int(event.y)):
context = treeview.drag_begin(gtk.target_list_add_uri_targets(),
gtk.gdk.ACTION_COPY,
1,
event)
context.set_icon_pixbuf(self.drag_pixbuf, 0, 0)
self.drag_start_x = self.drag_start_y = None
def on_TreeView_drag_end(self, _treeview, _drag_context):
self.drag_start_x = self.drag_start_y = None
self.drag_obj = None
self.drag_pixbuf = None
def on_TreeView_drag_data_get(self, _treeview, _drag_context, selection_data, _info, _timestamp):
path = self.drag_obj.launcher_path
if not os.path.exists(path):
self.drag_obj.parent.install_menus()
selection_data.set_uris([cxutils.path_to_uri(self.drag_obj.launcher_path)])
def activate_item(self, obj):
if isinstance(obj, cxmenu.MenuItem):
obj.start()
elif isinstance(obj, ControlPanelApplet):
obj.bottle.launch_control_panel_applet(obj.info['exe'])
elif obj == RUN_COMMAND:
self.on_RunCommandMenu_activate()
def on_item_activate(self, view, path, _column=None):
model = view.get_model()
obj = model.get_value(model.get_iter(path), 0)
if isinstance(obj, dict):
if view.row_expanded(path):
view.collapse_row(path)
else:
view.expand_row(path, False)
else:
self.activate_item(obj)
def on_OpenAppMenuItem_activate(self, _menuitem):
self.activate_item(self.get_selected_item())
def on_RunWithOptionsMenuItem_activate(self, _menuitem):
item = self.get_selected_item()
lnkfile = item.lnkfile
if not lnkfile:
return
lnkfile = lnkfile.replace('/', '\\')
lnkfile = cxutils.argvtocmdline((lnkfile,))
bottle = item.parent.bottlename
cxrunui.RunCommandController(bottle).set_command(lnkfile)
def on_ToggleFavoriteMenuItem_activate(self, _menuitem):
key = self.get_selected_key()
if self.is_favorite(key):
self.remove_favorite(key)
else:
self.add_favorite(key)
def on_ToggleFavoriteButton_toggled(self, button):
key = self.get_selected_key()
if not key:
return
if self.is_favorite(key) and not button.get_active():
self.remove_favorite(key)
elif not self.is_favorite(key) and button.get_active():
self.add_favorite(key)
def selection_updated(self, view):
if self.view_with_selection is not view:
if isinstance(self.view_with_selection, gtk.IconView):
self.view_with_selection.unselect_all()
elif isinstance(self.view_with_selection, gtk.TreeView):
self.view_with_selection.get_selection().unselect_all()
self.view_with_selection = view
def on_IconView_selection_changed(self, view):
if view.get_selected_items():
self.selection_updated(view)
_x, y, _w, h = self._get_iconview_item_box(view, view.get_selected_items()[0])
y = y + view.get_allocation().y
self.crossover_gui.get_object("SectionsScrolledWindow").get_vadjustment().clamp_page(y, y+h)
self.update_file_menu()
def on_TreeSelection_changed(self, treeselection):
_model, iterator = treeselection.get_selected()
if iterator:
self.selection_updated(treeselection.get_tree_view())
self.update_file_menu()
def update_applications_list(self):
applications = set()
for package in self.selected_bottle.installed_packages.itervalues():
if isinstance(package.name, str):
applications.add(package.name)
else:
applications.add(package.name.encode('utf8'))
iterator = self.installed_applications_store.get_iter_first()
while iterator:
name = self.installed_applications_store.get_value(iterator, 0)
if name in applications:
applications.remove(name)
iterator = self.installed_applications_store.iter_next(iterator)
else:
if not self.installed_applications_store.remove(iterator):
# no more rows
break
for name in applications:
self.installed_applications_store.append((name,))
def _add_expanded_row(self, treeview, path, rows):
model = treeview.get_model()
for i in range(len(path)):
iterator = model.get_iter(path[0:i+1])
key = model.get_value(iterator, 4)
if key not in rows:
rows[key] = {}
rows = rows[key]
def _expand_rows(self, treeview, rows, parent_iterator):
if not rows:
return
treestore = treeview.get_model()
if parent_iterator:
iterator = treestore.iter_children(parent_iterator)
else:
iterator = treestore.get_iter_first()
while iterator:
key = treestore.get_value(iterator, 4)
if key in rows:
path = treestore.get_path(iterator)
treeview.expand_row(path, False)
self._expand_rows(treeview, rows[key], iterator)
iterator = treestore.iter_next(iterator)
def updateAppList(self):
new_search_string = self.crossover_gui.get_object("SearchEntry").get_real_text()
if new_search_string and not self.search_string:
expanded_rows = {}
self.crossover_gui.get_object("LaunchersTreeView").map_expanded_rows(self._add_expanded_row, expanded_rows)
self.expanded_rows = expanded_rows
self.search_string = new_search_string.lower()
programs_section_contents = {}
favorites_section_contents = {}
control_panel_contents = {}
tree_contents = {}
needed_bottle_updates = {}
filtered_favorites = False # whether there are any favorites filtered by the search string
all_bottles = set(bottlequery.get_bottle_list())
if self.selected_bottle is None:
bottle_names = all_bottles
else:
bottle_names = set([self.selected_bottle.name])
# Update the 'default bottle' checkbox to match the selected bottle
self.updating_default_checkbox = True
cond = self.selected_bottle is not None and \
self.selected_bottle.is_default
for item in ("DefaultBottleMenu", "DefaultBottleCMenu"):
self.crossover_gui.get_object(item).set_active(cond)
self.updating_default_checkbox = False
# Disable the bottle-specific menus if no bottle is selected
cond = self.selected_bottle is not None and \
self.can_use_bottle(self.selected_bottle.name)
for item in ("DuplicateBottleAMenu",
"DuplicateBottleMenu", "DuplicateBottleCMenu",
"RemoveBottleButton",
"RemoveBottleMenu", "RemoveBottleCMenu",
"OpenCDriveMenu", "OpenCDriveCMenu",
"ExportArchiveMenu", "ExportArchiveCMenu",
"CreatePackageMenu", "CreatePackageCMenu",
"ResetWebBrowserMenu", "ResetWebBrowserCMenu",
"QuitBottleMenu", "QuitBottleCMenu"):
self.crossover_gui.get_object(item).set_sensitive(cond)
# Only allow force-quitting if a regular quit has been attempted
cond = self.selected_bottle is not None and \
(self.selected_bottle.STATUS_DOWNING in self.selected_bottle.status_overrides or
self.selected_bottle.name in self.shutdown_failed_bottles)
for item in ("ForceQuitBottleMenu", "ForceQuitBottleCMenu"):
self.crossover_gui.get_object(item).set_sensitive(cond)
self.updating_csmt_checkbox = True
self.updating_dxvk_checkbox = True
if not self.selected_bottle or \
self.selected_bottle.is_csmt_disabled_state == self.selected_bottle.STATUS_CSMT_UNKNOWN:
self.crossover_gui.get_object("CsmtToggleMenu").set_sensitive(False)
self.crossover_gui.get_object("CsmtToggleMenu").set_active(False)
self.crossover_gui.get_object("CsmtToggleCMenu").set_sensitive(False)
self.crossover_gui.get_object("CsmtToggleCMenu").set_active(False)
elif self.selected_bottle.is_csmt_disabled_state == self.selected_bottle.STATUS_CSMT_ENABLED:
self.crossover_gui.get_object("CsmtToggleMenu").set_sensitive(True)
self.crossover_gui.get_object("CsmtToggleMenu").set_active(True)
self.crossover_gui.get_object("CsmtToggleCMenu").set_sensitive(True)
self.crossover_gui.get_object("CsmtToggleCMenu").set_active(True)
elif self.selected_bottle.is_csmt_disabled_state == self.selected_bottle.STATUS_CSMT_DISABLED:
self.crossover_gui.get_object("CsmtToggleMenu").set_sensitive(True)
self.crossover_gui.get_object("CsmtToggleMenu").set_active(False)
self.crossover_gui.get_object("CsmtToggleCMenu").set_sensitive(True)
self.crossover_gui.get_object("CsmtToggleCMenu").set_active(False)
self.updating_csmt_checkbox = False
if not self.selected_bottle or \
self.selected_bottle.is_dxvk_enabled_state == self.selected_bottle.STATUS_DXVK_UNKNOWN:
self.crossover_gui.get_object("DxvkToggleMenu").set_sensitive(False)
self.crossover_gui.get_object("DxvkToggleMenu").set_active(False)
self.crossover_gui.get_object("DxvkToggleCMenu").set_sensitive(False)
self.crossover_gui.get_object("DxvkToggleCMenu").set_active(False)
self.crossover_gui.get_object("DxvkToggleCMenu").set_tooltip_text(_("Install DXVK via CrossTie to enable this menu entry"))
elif self.selected_bottle.is_dxvk_enabled_state == self.selected_bottle.STATUS_DXVK_ENABLED:
self.crossover_gui.get_object("DxvkToggleMenu").set_sensitive(True)
self.crossover_gui.get_object("DxvkToggleMenu").set_active(True)
self.crossover_gui.get_object("DxvkToggleCMenu").set_sensitive(True)
self.crossover_gui.get_object("DxvkToggleCMenu").set_active(True)
self.crossover_gui.get_object("DxvkToggleCMenu").set_tooltip_text(None)
elif self.selected_bottle.is_dxvk_enabled_state == self.selected_bottle.STATUS_DXVK_DISABLED:
self.crossover_gui.get_object("DxvkToggleMenu").set_sensitive(True)
self.crossover_gui.get_object("DxvkToggleMenu").set_active(False)
self.crossover_gui.get_object("DxvkToggleCMenu").set_sensitive(True)
self.crossover_gui.get_object("DxvkToggleCMenu").set_active(False)
self.crossover_gui.get_object("DxvkToggleCMenu").set_tooltip_text(None)
self.updating_dxvk_checkbox = False
# Only allow updating if the current bottle is managed and out-of-date
cond = self.selected_bottle is not None and \
self.selected_bottle.is_managed and \
not self.selected_bottle.up_to_date and \
self.can_use_bottle(self.selected_bottle.name)
for item in ("UpdatePublishedBottleMenu", "UpdatePublishedBottleCMenu"):
self.crossover_gui.get_object(item).set_sensitive(cond)
# Disable the operations that cannot be performed on managed bottles
cond = self.selected_bottle is not None and \
not self.selected_bottle.is_managed and \
self.can_use_bottle(self.selected_bottle.name)
for item in ("RenameBottleMenu", "RenameBottleCMenu",
"PublishBottleMenu", "PublishBottleCMenu",
"BottleInstallSoftwareMenu", "BottleInstallSoftwareCMenu"):
self.crossover_gui.get_object(item).set_sensitive(cond)
self.bottle_label.set_property('editable', cond)
self.crossover_gui.get_object("BottleDescriptionText").set_editable(cond)
if self.selected_bottle:
self.crossover_gui.get_object("DuplicateBottleMenu").set_label(_(u"Duplicate '%s'\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("DuplicateBottleAMenu").set_label(_(u"Duplicate '%s'\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("RemoveBottleMenu").set_label(_(u"Delete '%s'\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("RenameBottleMenu").set_label(_(u"Rename '%s'\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("BottleInstallSoftwareMenu").set_label(_(u"Install Software into '%s'\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("ExportArchiveMenu").set_label(_(u"Export '%s' to Archive\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("PublishBottleMenu").set_label(_(u"Publish '%s'\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("CreatePackageMenu").set_label(_(u"Create Package from '%s'\u2026") % self.selected_bottle.name)
self.crossover_gui.get_object("QuitBottleMenu").set_label(_(u"Quit '%s'") % self.selected_bottle.name)
self.crossover_gui.get_object("ForceQuitBottleMenu").set_label(_(u"Force Quit '%s'") % self.selected_bottle.name)
self.crossover_gui.get_object("DefaultBottleMenu").set_sensitive(True)
self.crossover_gui.get_object("DefaultBottleCMenu").set_sensitive(True)
self.crossover_gui.get_object("BottleTypeLabel").set_sensitive(True)
self.crossover_gui.get_object("BottleTypeValue").set_sensitive(True)
self.crossover_gui.get_object("BottleTypeValue").set_text(bottlemanagement.get_template_name(self.selected_bottle.bottletemplate))
self.crossover_gui.get_object("BottleArchValue").set_sensitive(True)
if self.selected_bottle.arch == 'win64':
self.crossover_gui.get_object("BottleArchValue").set_text(_("64-bit"))
else:
self.crossover_gui.get_object("BottleArchValue").set_text(_("32-bit"))
self.crossover_gui.get_object("BottleStatusLabel").set_sensitive(True)
self.crossover_gui.get_object("BottleStatusValue").set_sensitive(True)
self.crossover_gui.get_object("BottleStatusValue").set_text(self.selected_bottle.status)
self.crossover_gui.get_object("BottleDescriptionLabel").set_sensitive(True)
self.crossover_gui.get_object("BottleDescriptionText").set_sensitive(True)
description_buffer = self.crossover_gui.get_object("BottleDescriptionText").get_buffer()
ui_description = description_buffer.get_text(description_buffer.get_start_iter(), description_buffer.get_end_iter())
if ui_description != self.selected_bottle.current_description:
description_buffer.set_text(self.selected_bottle.current_description)
self.crossover_gui.get_object("BottleApplicationsLabel").set_sensitive(True)
self.crossover_gui.get_object("BottleApplicationsView").set_sensitive(True)
if self.crossover_gui.get_object("BottlePropertiesSidebar").get_property('visible'):
if self.selected_bottle.installed_packages_ready:
self.crossover_gui.get_object("BottleApplicationsLabel").set_text(_("Installed Applications:"))
self.update_applications_list()
else:
self.crossover_gui.get_object("BottleApplicationsLabel").set_text(_(u"Installed Applications (Loading\u2026):"))
self.installed_applications_store.clear()
if not self.selected_bottle.installed_packages_loading:
needed_bottle_updates[self.selected_bottle] = needed_bottle_updates.get(self.selected_bottle, []) + ['applications']
if not self.selected_bottle.is_csmt_disabled_ready and \
not self.selected_bottle.is_csmt_disabled_loading:
needed_bottle_updates[self.selected_bottle] = needed_bottle_updates.get(self.selected_bottle, []) + ['csmt']
if not self.selected_bottle.is_dxvk_enabled_ready and \
not self.selected_bottle.is_dxvk_enabled_loading:
needed_bottle_updates[self.selected_bottle] = needed_bottle_updates.get(self.selected_bottle, []) + ['dxvk']
else:
self.crossover_gui.get_object("DuplicateBottleMenu").set_label(_(u"Duplicate Bottle\u2026"))
self.crossover_gui.get_object("DuplicateBottleAMenu").set_label(_(u"Duplicate Bottle\u2026"))
self.crossover_gui.get_object("RemoveBottleMenu").set_label(_(u"Delete Bottle\u2026"))
self.crossover_gui.get_object("RenameBottleMenu").set_label(_(u"Rename Bottle\u2026"))
self.crossover_gui.get_object("BottleInstallSoftwareMenu").set_label(_(u"Install Software into Bottle\u2026"))
self.crossover_gui.get_object("ExportArchiveMenu").set_label(_(u"Export Bottle to Archive\u2026"))
self.crossover_gui.get_object("PublishBottleMenu").set_label(_(u"Publish Bottle\u2026"))
self.crossover_gui.get_object("CreatePackageMenu").set_label(_(u"Create Package from Bottle\u2026"))
self.crossover_gui.get_object("QuitBottleMenu").set_label(_("Quit Bottle"))
self.crossover_gui.get_object("ForceQuitBottleMenu").set_label(_("Force Quit Bottle"))
self.crossover_gui.get_object("DefaultBottleMenu").set_sensitive(False)
self.crossover_gui.get_object("DefaultBottleCMenu").set_sensitive(False)
self.crossover_gui.get_object("BottleTypeLabel").set_sensitive(False)
self.crossover_gui.get_object("BottleTypeValue").set_sensitive(False)
self.crossover_gui.get_object("BottleTypeValue").set_text('')
self.crossover_gui.get_object("BottleArchValue").set_sensitive(False)
self.crossover_gui.get_object("BottleArchValue").set_text('')
self.crossover_gui.get_object("BottleStatusLabel").set_sensitive(False)
self.crossover_gui.get_object("BottleStatusValue").set_sensitive(False)
self.crossover_gui.get_object("BottleStatusValue").set_text('')
self.crossover_gui.get_object("BottleDescriptionLabel").set_sensitive(False)
self.crossover_gui.get_object("BottleDescriptionText").set_sensitive(False)
self.crossover_gui.get_object("BottleDescriptionText").get_buffer().set_text('')
self.crossover_gui.get_object("BottleApplicationsLabel").set_sensitive(False)
self.crossover_gui.get_object("BottleApplicationsLabel").set_text(_("Installed Applications:"))
self.crossover_gui.get_object("BottleApplicationsView").set_sensitive(False)
self.installed_applications_store.clear()
# find other bottles we have to scan to get favorites
programs_bottles = bottle_names.copy()
control_panel_bottles = set()
if self.selected_bottle:
control_panel_bottles.add(self.selected_bottle.name)
for key in self.favorites:
if key.startswith('cxmenu/'):
bottle_name = key.split('/', 2)[1]
if bottle_name in all_bottles:
programs_bottles.add(bottle_name)
elif key.startswith('controlpanel/'):
bottle_name = key.split('/', 2)[1]
if bottle_name in all_bottles:
control_panel_bottles.add(bottle_name)
for bottle_name in programs_bottles:
if not self.can_use_bottle(bottle_name):
continue
prefs = cxmenu.MenuPrefs(bottle_name, True)
prefs.read_config()
for app in prefs.data:
if app.startswith('Desktop'):
continue
if prefs.data[app].mode != 'install':
continue
key = "cxmenu/" + bottle_name + '/' + app
if self.search_string and self.search_string not in prefs.data[app].menu_name().lower():
if self.is_favorite(key):
filtered_favorites = True
continue
if self.is_favorite(key):
favorites_section_contents[key] = prefs.data[app]
if bottle_name not in bottle_names:
continue
programs_section_contents[key] = prefs.data[app]
path = app.split('/', 1)[1]
if path.startswith('Programs/'):
path = path.split('/', 1)[1]
if path.endswith(('.lnk', '.url')):
path = path.rsplit('.', 1)[0]
tree = tree_contents
for part in path.split('/')[:-1]:
part_key = 'folder/' + part
if part_key not in tree:
tree[part_key] = {}
tree = tree[part_key]
tree[key] = prefs.data[app]
for bottle_name in control_panel_bottles:
if not self.can_use_bottle(bottle_name):
continue
bottle_obj = self.bottles.bottleObject(bottle_name)
if not bottle_obj.control_panel_ready:
if not bottle_obj.control_panel_loading:
needed_bottle_updates[bottle_obj] = needed_bottle_updates.get(bottle_obj, []) + ['controlpanel']
continue
for applet_info in bottle_obj.control_panel_table:
key = "controlpanel/" + bottle_name + '/' + applet_info['exe']
if self.search_string and self.search_string not in applet_info['name'].lower():
if self.is_favorite(key):
filtered_favorites = True
continue
applet = ControlPanelApplet(bottle_obj, applet_info)
if self.is_favorite(key):
favorites_section_contents[key] = applet
if bottle_obj != self.selected_bottle:
continue
control_panel_contents[key] = applet
if not self.search_string or self.search_string in _(u"Run Command\u2026").lower():
if self.is_favorite(RUN_COMMAND):
favorites_section_contents[RUN_COMMAND] = RUN_COMMAND
programs_section_contents[RUN_COMMAND] = RUN_COMMAND
tree_contents[RUN_COMMAND] = RUN_COMMAND
elif self.is_favorite(RUN_COMMAND):
filtered_favorites = True
if favorites_section_contents:
tree_contents[FAVORITES] = favorites_section_contents
if control_panel_contents:
tree_contents[CONTROL_PANEL] = control_panel_contents
added_first_favorite = (self.favorites_list_store.get_iter_first() is None) and favorites_section_contents
self.update_item_list(self.favorites_list_store, favorites_section_contents)
self.update_item_list(self.programs_list_store, programs_section_contents)
self.update_item_list(self.control_panel_list_store, control_panel_contents)
items_changed = self.update_item_list(self.launchers_tree_store, tree_contents)
if added_first_favorite:
# expand favorites folder
treeview = self.crossover_gui.get_object("LaunchersTreeView")
iterator = self.launchers_tree_store.get_iter_first()
while iterator:
key = self.launchers_tree_store.get_value(iterator, 4)
if key == FAVORITES:
treeview.expand_row(self.launchers_tree_store.get_path(iterator), False)
break
iterator = self.launchers_tree_store.iter_next(iterator)
if items_changed:
# expand programs folders as long as they have no sibling folders or shortcuts
depth = 0
tree = tree_contents
while tree:
sub_item = None
for key, item in tree.iteritems():
if key.startswith(('folder/', 'cxmenu/')):
if sub_item:
sub_item = None
break
sub_item = item
if isinstance(sub_item, dict):
depth = depth + 1
tree = sub_item
else:
break
iterator = None
treeview = self.crossover_gui.get_object("LaunchersTreeView")
while depth:
iterator = self.launchers_tree_store.iter_children(iterator)
while iterator:
key = self.launchers_tree_store.get_value(iterator, 4)
if key.startswith('folder/'):
treeview.expand_row(self.launchers_tree_store.get_path(iterator), False)
break
iterator = self.launchers_tree_store.iter_next(iterator)
depth = depth - 1
if self.expanded_rows is not None and not self.search_string:
treeview = self.crossover_gui.get_object("LaunchersTreeView")
treeview.collapse_all()
self._expand_rows(treeview, self.expanded_rows, None)
self.expanded_rows = None
elif self.search_string:
self.crossover_gui.get_object("LaunchersTreeView").expand_all()
if bottle_names:
self.crossover_gui.get_object("CrossOverLogo").hide()
self.crossover_gui.get_object("LaunchersBox").show()
self.setup_icon_view(self.crossover_gui.get_object("ProgramsSectionIconView"),
show_bottle=len(bottle_names) > 1)
self.setup_icon_view(self.crossover_gui.get_object("FavoritesSectionIconView"),
show_bottle=True)
self.setup_icon_view(self.crossover_gui.get_object("ControlPanelSectionIconView"),
show_bottle=False)
else:
self.crossover_gui.get_object("CrossOverLogo").show()
self.crossover_gui.get_object("LaunchersBox").hide()
if favorites_section_contents:
self.crossover_gui.get_object("FavoritesSectionLabel").show()
self.crossover_gui.get_object("FavoritesSectionLabel").set_text(_("Favorites"))
self.crossover_gui.get_object("FavoritesSectionIconView").show()
elif filtered_favorites:
self.crossover_gui.get_object("FavoritesSectionLabel").show()
self.crossover_gui.get_object("FavoritesSectionLabel").set_text(_("Favorites (No Matches)"))
self.crossover_gui.get_object("FavoritesSectionIconView").show()
else:
self.crossover_gui.get_object("FavoritesSectionLabel").hide()
self.crossover_gui.get_object("FavoritesSectionIconView").hide()
if control_panel_contents:
self.crossover_gui.get_object("ControlPanelSectionLabel").show()
self.crossover_gui.get_object("ControlPanelSectionIconView").show()
self.crossover_gui.get_object("ControlPanelSectionLabel").set_text(_("Control Panels"))
elif self.selected_bottle and self.selected_bottle.control_panel_loading:
self.crossover_gui.get_object("ControlPanelSectionLabel").show()
self.crossover_gui.get_object("ControlPanelSectionIconView").show()
self.crossover_gui.get_object("ControlPanelSectionLabel").set_text(_(u"Control Panels (Loading\u2026)"))
elif self.selected_bottle:
self.crossover_gui.get_object("ControlPanelSectionLabel").show()
self.crossover_gui.get_object("ControlPanelSectionIconView").show()
self.crossover_gui.get_object("ControlPanelSectionLabel").set_text(_("Control Panels (No Matches)"))
else:
self.crossover_gui.get_object("ControlPanelSectionLabel").hide()
self.crossover_gui.get_object("ControlPanelSectionIconView").hide()
if programs_section_contents:
self.crossover_gui.get_object("ProgramsSectionLabel").set_text(_("Programs"))
else:
self.crossover_gui.get_object("ProgramsSectionLabel").set_text(_("Programs (No Matches)"))
for bottle, updates in needed_bottle_updates.iteritems():
self.bottles.queue_bottle_updates((bottle,), updates)
def update_applist_idle(self):
self.updateAppList()
self.update_applist_source = None
return False
def on_SearchEntry_changed(self, entry, text):
if not self.update_applist_source:
self.update_applist_source = gobject.idle_add(self.update_applist_idle)
if text:
entry.set_property('secondary-icon-sensitive', True)
else:
entry.set_property('secondary-icon-sensitive', False)
def on_SearchEntry_icon_release(self, entry, icon_pos, _event):
if icon_pos == gtk.ENTRY_ICON_SECONDARY:
entry.set_text('')
def bottleCollectionChanged(self):
self.update_bottle_list()
self.updateAppList()
def bottleChanged(self, _bottle):
# This can be called from outside the main thread
gobject.idle_add(self.bottleCollectionChanged)
def on_cxmenuconf_changed(self, _event, _path, _observer_data):
gobject.idle_add(self.bottleCollectionChanged)
def on_ShowHelp_activate(self, _unused):
# pylint: disable=R0201
cxguitools.show_help("index.html")
def on_description_changed(self, text_buffer):
if not self.crossover_gui.get_object("BottleDescriptionText").get_editable():
return
if not self.selected_bottle:
return
new_description = text_buffer.get_text(text_buffer.get_start_iter(), text_buffer.get_end_iter())
self.selected_bottle.change_description(new_description)
if self.selected_bottle not in self.updating_description_bottles and \
not self.selected_bottle.is_description_written():
pyop.sharedOperationQueue.enqueue(UpdateDescriptionOperation(self.selected_bottle, self))
def on_description_insert_text(self, text_buffer, _iter, text, _length):
if '\n' in text:
text_buffer.stop_emission('insert-text')
def on_ShowAboutCrossOver_activate(self, _caller):
if self.aboutDialog is None:
versionString = "%s %s" % (distversion.PRODUCT_NAME,
distversion.PUBLIC_VERSION)
self.aboutDialog = gtk.Dialog(title=_("About CrossOver"),
parent=self.main_window,
flags=gtk.DIALOG_NO_SEPARATOR | gtk.DIALOG_DESTROY_WITH_PARENT,
buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
versionLabel = gtk.Label(versionString)
versionLabel.show()
crossOverIcon = gtk.Image()
crossOverIcon.set_from_file(os.path.join(cxutils.CX_ROOT,
"share",
"images",
"welcomeCrossOverIcon.png"))
crossOverIcon.show()
codeWeaversLink = gtk.Label()
codeWeaversLink.set_markup("<a href=\"http://www.codeweavers.com\">CodeWeavers, Inc.</a>")
codeWeaversLink.show()
# pylint is unable to figure out the
# correct class of the dialog's vbox
self.aboutDialog.vbox.pack_start(crossOverIcon) # pylint: disable=E1101
self.aboutDialog.vbox.pack_start(versionLabel) # pylint: disable=E1101
self.aboutDialog.vbox.pack_start(codeWeaversLink) # pylint: disable=E1101
self.aboutDialog.vbox.show() # pylint: disable=E1101
self.aboutDialog.connect("response", self.on_HideAboutCrossOver_activate)
self.aboutDialog.present()
def on_SystemInformation_activate(self, _caller):
# pylint: disable=R0201
import systeminfo
systeminfo.SystemInfoDialog()
def on_License_activate(self, _caller):
# pylint: disable=R0201
import systeminfo
systeminfo.LicenseDialog()
def on_HideAboutCrossOver_activate(self, _unused, _unused_2):
# pylint: disable=R0201
self.aboutDialog.destroy()
self.aboutDialog = None
def on_DoRegistration_activate(self, _unused):
# pylint: disable=R0201
cxregisterui.open_or_show()
def on_PreferencesMenuItem_activate(self, _unused):
# pylint: disable=R0201
cxprefsui.open_or_show()
def on_InstallWindowsSoftware_clicked(self, _unused):
dlg = cxinstallerui.open_or_show()
dlg.add_observer(cxinstallerui.INSTALL_FINISHED, self.installWizardDidFinishInstalling)
def installWizardDidFinishInstalling(self, _event, _data):
self.updateAppList()
def quit_crossover_requested(self, _unused, _unused2=None):
self.main_window.destroy()
def on_MainWindow_configure_event(self, _widget, _event):
# get_size doesn't work in on_destroy
self._size = self.main_window.get_size()
def on_bottlepane_move(self, _obj, _propertyspec):
if self.crossover_gui.get_object("BottlePaneVBox").get_property('visible'):
self._pane_size = self.crossover_gui.get_object("BottlePaned").get_position()
def on_destroy(self, _unused):
global DIALOG # pylint: disable=W0603
DIALOG = None
cxfsnotifier.remove_observer(
os.path.join(cxutils.CX_ROOT, "etc", "license.txt"),
self.on_license_changed)
cxfsnotifier.remove_observer(
os.path.join(cxutils.CX_ROOT, "etc", "license.sig"),
self.on_license_changed)
self.bottles.removeChangeDelegate(self)
if self._size:
cxproduct.save_setting('OfficeSetup', 'BottleManagerSize',
'%sx%s' % self._size)
if self._pane_size:
cxproduct.save_setting('OfficeSetup', 'BottleSidebarSize',
str(self._pane_size))
cxproduct.save_setting('OfficeSetup', 'ShowBottleSidebar',
'1' if self.bottle_pane_showing else '')
cxproduct.save_setting('OfficeSetup', 'MainWindowView', self.view)
cxguitools.toplevel_quit()
def on_ToggleShowBottles_activate(self, _unused):
self.toggle_bottle_pane()
def on_ToggleShowBottlesButton_activate(self, widget):
active = widget.get_active()
if active != self.bottle_pane_showing:
self.toggle_bottle_pane(active)
def on_BottleListView_select(self, selection):
model, iterator = selection.get_selected()
bottle = model.get_value(iterator, 3)
if bottle is None:
selection.select_path((1,))
if self.selected_bottle is not bottle:
self.selected_bottle = bottle
self.updateAppList()
def on_BottleListView_query_tooltip(self, treeview, x, y, keyboard_mode, tooltip):
if keyboard_mode:
return False # FIXME
res = treeview.get_path_at_pos(x, y)
if not res:
return False
path, _column, _xofs, _yofs = res
iterator = self.bottle_list_store.get_iter(path)
bottle = self.bottle_list_store.get_value(iterator, 3)
if bottle is None:
return False
tooltip.set_markup(self.get_bottle_markup(bottle, indent=''))
return True
def toggle_bottle_pane(self, show=None):
if show is None:
show = not self.bottle_pane_showing
self.bottle_pane_showing = show
if show:
self.crossover_gui.get_object("ToggleShowBottlesMenu").set_label(
_("Hide Bottles"))
self.crossover_gui.get_object("ToggleShowBottlesButton").set_active(True)
self.crossover_gui.get_object("BottlePaneVBox").show()
else:
self.crossover_gui.get_object("ToggleShowBottlesMenu").set_label(
_("Show Bottles"))
self.crossover_gui.get_object("ToggleShowBottlesButton").set_active(False)
self.crossover_gui.get_object("BottlePaneVBox").hide()
if self.selected_bottle:
self.selected_bottle = None
self.crossover_gui.get_object("BottleListView").get_selection().select_path((1,))
self.updateAppList()
def set_view(self, view):
if self.view is None:
# Hack to avoid recursively calling this while modifying the gui
return
self.view = None
self.crossover_gui.get_object("SectionsScrolledWindow").set_property('visible', view != TREE_VIEW)
self.crossover_gui.get_object("TreeViewScrolledWindow").set_property('visible', view == TREE_VIEW)
self.crossover_gui.get_object("ViewAsIconsMenu").set_active(view == ICON_VIEW)
self.crossover_gui.get_object("ViewAsIconsButton").set_active(view == ICON_VIEW)
self.crossover_gui.get_object("ViewAsTreeMenu").set_active(view == TREE_VIEW)
self.crossover_gui.get_object("ViewAsTreeButton").set_active(view == TREE_VIEW)
self.crossover_gui.get_object("LaunchersTreeView").columns_autosize()
self.view = view
def on_ViewAsIcons_activate(self, _obj):
self.set_view(ICON_VIEW)
def on_ViewAsTree_activate(self, _obj):
self.set_view(TREE_VIEW)
def on_AddBottle_clicked(self, _button):
NewBottleController(self)
def on_DuplicateBottleMenu_activate(self, _button):
if self.selected_bottle:
NewBottleController(self, bottle_to_copy=self.selected_bottle.name)
def on_ImportArchiveMenu_activate(self, _menu):
RestoreArchiveController(self)
def on_RemoveBottle_clicked(self, _caller):
selectedBottleName = self.selected_bottle.name
primary_text = _("Are you sure you want to delete the '%s' bottle?") % selectedBottleName
secondary_text = _("This will remove all Windows applications contained in that bottle, as well as any documents stored in its C: drive.")
buttons = ((gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
(gtk.STOCK_DELETE, gtk.RESPONSE_OK))
cxguitools.CXMessageDlg(primary=primary_text, secondary=secondary_text, button_array=buttons, response_function=self.delete_bottle_response, user_data=self.selected_bottle, message_type=gtk.MESSAGE_WARNING)
def delete_bottle_response(self, response, bottle):
if response == gtk.RESPONSE_OK:
deleteBottleOp = DeleteBottleOperation(bottle, self)
pyop.sharedOperationQueue.enqueue(deleteBottleOp)
class NewBottleController(object):
def __init__(self, main_window, bottle_to_copy=None):
self.main_window = main_window
self.timeout_source = None
self.modaldialogxml = gtk.Builder()
self.modaldialogxml.set_translation_domain("crossover")
self.modaldialogxml.add_from_file(cxguitools.get_ui_path("newbottledialog"))
self.modaldialogxml.connect_signals(self)
newbottledlg = self.modaldialogxml.get_object("NewBottleDialog")
newbottledlg.connect('destroy', self.on_destroy)
self.bottle_to_copy = bottle_to_copy
templatewidget = self.modaldialogxml.get_object("bottletype")
templatelabel = self.modaldialogxml.get_object("bottletypelabel")
caption = self.modaldialogxml.get_object("newbottlecaption")
bottleNameWidget = self.modaldialogxml.get_object("newbottlename")
if bottle_to_copy:
newbottledlg.set_title(_("Copy a Bottle"))
caption.set_text(_("This will copy the '%(bottle)s' bottle. The copy will be named:") % {'bottle': bottle_to_copy})
templatewidget.hide()
templatelabel.hide()
self.modaldialogxml.get_object("bottlenamelabel").hide()
copyBottleName = _("Copy of %(bottle)s") % {'bottle': bottle_to_copy}
# Eliminate spaces from the bottle being copied while we're at it
copyBottleName = copyBottleName.replace(' ', '_')
bottleNameWidget.set_text(copyBottleName)
else:
caption.hide()
liststore = gtk.ListStore(str, str) # display name, template
templatewidget.set_model(liststore)
index = 0
for template in sorted(bottlemanagement.template_list(), None,
bottlemanagement.get_template_key):
display_name = bottlemanagement.get_template_name(template, True)
new_row = liststore.append()
liststore.set_value(new_row, 0, display_name)
liststore.set_value(new_row, 1, template)
if template == "win7":
templatewidget.set_active(index)
index += 1
cell = gtk.CellRendererText()
templatewidget.pack_start(cell, True)
templatewidget.add_attribute(cell, "text", 0)
bottle_name_hint = _("New Bottle")
bottle_name_hint = bottle_name_hint.replace(' ', '_')
bottleNameWidget.set_text(bottlecollection.unique_bottle_name(bottle_name_hint))
progbar = self.modaldialogxml.get_object("NewBottleProgbar")
progbar.hide()
newbottledlg.show()
def bottle_name_delete_text(self, caller, start, stop):
# pylint: disable=R0201
name = caller.get_text()
name = name[:start] + name[stop:]
if not cxutils.is_valid_bottlename(name):
caller.emit_stop_by_name("delete-text")
else:
okButton = self.modaldialogxml.get_object("okbutton1")
okButton.set_sensitive(not bottlequery.is_existing_bottle(name))
def bottle_name_insert_text(self, caller, new_text, _length, _user_data):
# pylint: disable=R0201
name = caller.get_text()
position = caller.get_position()
name = name[:position] + new_text + name[position:]
if not cxutils.is_valid_bottlename(name):
caller.emit_stop_by_name("insert-text")
else:
okButton = self.modaldialogxml.get_object("okbutton1")
okButton.set_sensitive(not bottlequery.is_existing_bottle(name))
def add_bottle_ok(self, _caller):
dlgWidget = self.modaldialogxml.get_object("NewBottleDialog")
bottleNameWidget = self.modaldialogxml.get_object("newbottlename")
progbar = self.modaldialogxml.get_object("NewBottleProgbar")
if self.bottle_to_copy is None:
templatewidget = self.modaldialogxml.get_object("bottletype")
templatemodel = templatewidget.get_model()
template = templatemodel.get_value(templatewidget.get_active_iter(), 1)
else:
template = None
newBottleOp = NewBottleOperation(bottleNameWidget.get_text().decode('utf8'), template, self, self.bottle_to_copy)
progbar.show()
dlgWidget.set_sensitive(False)
self.timeout_source = gobject.timeout_add(100, self.pulse)
pyop.sharedOperationQueue.enqueue(newBottleOp)
def pulse(self):
self.modaldialogxml.get_object("NewBottleProgbar").pulse()
return True
def add_bottle_cancel(self, _caller):
newbottledlg = self.modaldialogxml.get_object("NewBottleDialog")
newbottledlg.destroy()
def AddBottleFinished(self, op):
dlgWidget = self.modaldialogxml.get_object("NewBottleDialog")
progbar = self.modaldialogxml.get_object("NewBottleProgbar")
gobject.source_remove(self.timeout_source)
progbar.hide()
dlgWidget.set_sensitive(True)
if not op.exitStatus[0]:
cxguitools.CXMessageDlg(primary=_("Could not create bottle"), secondary=op.exitStatus[1], message_type=gtk.MESSAGE_ERROR)
else:
bottlecollection.sharedCollection().refresh()
dlgWidget.destroy()
def on_destroy(self, _unused):
cxguitools.toplevel_quit()
class NewBottleOperation(pyop.PythonOperation):
def __init__(self, bottle_name, template, new_bottle_controller, bottle_to_copy=None, publish=False):
pyop.PythonOperation.__init__(self)
self.exitStatus = None
self.bottle_to_copy = bottle_to_copy
self.new_bottle_name = bottle_name
self.template = template
self.publish = publish
self.new_bottle_controller = new_bottle_controller
def __unicode__(self):
return "NewBottleOperation for " + self.new_bottle_name
def enqueued(self):
bottlecollection.sharedCollection().add_ignored_bottle(self.new_bottle_name)
pyop.PythonOperation.enqueued(self)
def main(self):
if self.publish:
self.exitStatus = bottlemanagement.publish_bottle(self.new_bottle_name, self.bottle_to_copy)
elif self.bottle_to_copy:
self.exitStatus = bottlemanagement.copy_bottle(self.new_bottle_name, self.bottle_to_copy)
else:
self.exitStatus = bottlemanagement.create_bottle(self.new_bottle_name, self.template)
def finish(self):
bottlecollection.sharedCollection().remove_ignored_bottle(self.new_bottle_name)
self.new_bottle_controller.AddBottleFinished(self)
pyop.PythonOperation.finish(self)
class RestoreArchiveController(object):
def __init__(self, main_window, archive=None):
self.main_window = main_window
self.timeout_source = None
self.modaldialogxml = gtk.Builder()
self.modaldialogxml.set_translation_domain("crossover")
self.modaldialogxml.add_from_file(cxguitools.get_ui_path("bottlearchivepicker"))
self.modaldialogxml.connect_signals(self)
pickerdlg = self.modaldialogxml.get_object("BottleArchivePicker")
cxguitools.add_filters(pickerdlg, cxguitools.FILTERS_CXARCHIVES)
if archive:
pickerdlg.set_filename(archive)
self.suggestBottleName(archive)
progbar = self.modaldialogxml.get_object("RestoringProgbar")
progbar.hide()
pickerdlg.show()
def cancel(self, _caller):
pickerdlg = self.modaldialogxml.get_object("BottleArchivePicker")
pickerdlg.destroy()
def pulse(self):
self.modaldialogxml.get_object("RestoringProgbar").pulse()
return True
def open(self, _caller):
pickerdlg = self.modaldialogxml.get_object("BottleArchivePicker")
bottleNameWidget = self.modaldialogxml.get_object("newbottlename")
progbar = self.modaldialogxml.get_object("RestoringProgbar")
archiveFilename = pickerdlg.get_filename()
newBottleOp = RestoreBottleOperation(bottleNameWidget.get_text().decode('utf8'), archiveFilename, self)
progbar.show()
pickerdlg.set_sensitive(False)
self.timeout_source = gobject.timeout_add(100, self.pulse)
pyop.sharedOperationQueue.enqueue(newBottleOp)
def file_selection_changed(self, _caller):
pickerdlg = self.modaldialogxml.get_object("BottleArchivePicker")
selectedFilename = pickerdlg.get_filename()
self.suggestBottleName(selectedFilename)
def suggestBottleName(self, archiveFile):
openButton = self.modaldialogxml.get_object("openbutton")
bottleNameWidget = self.modaldialogxml.get_object("newbottlename")
if archiveFile and not os.path.isdir(archiveFile):
root = os.path.splitext(os.path.basename(archiveFile))[0]
bottleNameWidget.set_text(bottlequery.unique_bottle_name(root))
openButton.set_sensitive(True)
else:
bottleNameWidget.set_text("")
openButton.set_sensitive(False)
def bottle_name_delete_text(self, caller, start, stop):
# pylint: disable=R0201
name = caller.get_text()
name = name[:start] + name[stop:]
if not cxutils.is_valid_bottlename(name):
caller.emit_stop_by_name("delete-text")
else:
okButton = self.modaldialogxml.get_object("openbutton")
okButton.set_sensitive(not bottlequery.is_existing_bottle(name))
def bottle_name_insert_text(self, caller, new_text, _length, _user_data):
# pylint: disable=R0201
name = caller.get_text()
position = caller.get_position()
name = name[:position] + new_text + name[position:]
if not cxutils.is_valid_bottlename(name):
caller.emit_stop_by_name("insert-text")
else:
okButton = self.modaldialogxml.get_object("openbutton")
okButton.set_sensitive(not bottlequery.is_existing_bottle(name))
def RestoreBottleFinished(self, op):
pickerdlg = self.modaldialogxml.get_object("BottleArchivePicker")
progbar = self.modaldialogxml.get_object("RestoringProgbar")
gobject.source_remove(self.timeout_source)
progbar.hide()
pickerdlg.set_sensitive(True)
if not op.exitStatus[0]:
cxguitools.CXMessageDlg(primary=_("Could not restore bottle"), secondary=op.exitStatus[1], message_type=gtk.MESSAGE_ERROR)
else:
bottlecollection.sharedCollection().refresh()
pickerdlg.destroy()
class RestoreBottleOperation(pyop.PythonOperation):
def __init__(self, bottle_name, archive_file, controller_window):
pyop.PythonOperation.__init__(self)
self.exitStatus = None
self.archive_file = archive_file
self.bottle_name = bottle_name
self.controller_window = controller_window
def __unicode__(self):
return "RestoreBottleOperation for " + self.bottle_name
def enqueued(self):
bottlecollection.sharedCollection().add_ignored_bottle(self.bottle_name)
pyop.PythonOperation.enqueued(self)
def main(self):
self.exitStatus = bottlemanagement.restore_bottle(self.bottle_name, self.archive_file)
def finish(self):
bottlecollection.sharedCollection().remove_ignored_bottle(self.bottle_name)
self.controller_window.RestoreBottleFinished(self)
pyop.PythonOperation.finish(self)
class PublishBottleController(object):
def __init__(self, main_window, orig_bottle_name=None):
self.main_window = main_window
self.timeout_source = None
self.modaldialogxml = gtk.Builder()
self.modaldialogxml.set_translation_domain("crossover")
self.modaldialogxml.add_from_file(cxguitools.get_ui_path("publishbottledialog"))
self.modaldialogxml.connect_signals(self)
publishbottledlg = self.modaldialogxml.get_object("PublishBottleDialog")
self.orig_bottle_name = orig_bottle_name
publishedNameEntry = self.modaldialogxml.get_object("publishedNameEntry")
publishedNameEntry.set_text(_("published_%(bottlename)s") % {'bottlename': orig_bottle_name})
progbar = self.modaldialogxml.get_object("publishBottleProgbar")
progbar.hide()
publishbottledlg.show()
def pulse(self):
self.modaldialogxml.get_object("publishBottleProgbar").pulse()
return True
def publish_bottle_ok(self, _caller):
dlgWidget = self.modaldialogxml.get_object("PublishBottleDialog")
bottleNameWidget = self.modaldialogxml.get_object("publishedNameEntry")
progbar = self.modaldialogxml.get_object("publishBottleProgbar")
name = bottleNameWidget.get_text().decode('utf8')
if bottlequery.is_existing_bottle(name):
cxguitools.CXMessageDlg(primary=_("Could not publish bottle"), secondary=_("A bottle with this name already exists."), message_type=gtk.MESSAGE_ERROR)
return
newBottleOp = NewBottleOperation(name, "", self, self.orig_bottle_name, True)
progbar.show()
dlgWidget.set_sensitive(False)
self.timeout_source = gobject.timeout_add(100, self.pulse)
pyop.sharedOperationQueue.enqueue(newBottleOp)
def publish_bottle_cancel(self, _caller):
publishbottledlg = self.modaldialogxml.get_object("PublishBottleDialog")
publishbottledlg.destroy()
def AddBottleFinished(self, op):
dlgWidget = self.modaldialogxml.get_object("PublishBottleDialog")
progbar = self.modaldialogxml.get_object("publishBottleProgbar")
gobject.source_remove(self.timeout_source)
progbar.hide()
dlgWidget.set_sensitive(True)
if not op.exitStatus[0]:
cxguitools.CXMessageDlg(primary=_("Could not publish bottle"), secondary=op.exitStatus[1], message_type=gtk.MESSAGE_ERROR)
else:
bottlecollection.sharedCollection().refresh()
dlgWidget.destroy()
class DeleteBottleOperation(pyop.PythonOperation):
def __init__(self, bottle, main_window):
pyop.PythonOperation.__init__(self)
self.bottle_name = bottle.name
self.bottle = bottle
self.main_window = main_window
self.deleted = False
def __unicode__(self):
return "DeleteBottleOperation for " + self.bottle_name
def enqueued(self):
self.bottle.add_status_override(self.bottle.STATUS_DELETING)
self.bottle.marked_for_death = True
pyop.PythonOperation.enqueued(self)
def main(self):
if self.bottle.is_managed:
bottlemanagement.delete_bottle(self.bottle_name, True)
else:
bottlemanagement.delete_bottle(self.bottle_name, False)
self.deleted = True
def finish(self):
self.bottle.marked_for_death = False
self.bottle.remove_status_override(self.bottle.STATUS_DELETING)
if self.deleted:
bottlecollection.sharedCollection().refresh()
pyop.PythonOperation.finish(self)
class RenameBottleOperation(pyop.PythonOperation):
def __init__(self, bottle_obj, new_name, main_window):
pyop.PythonOperation.__init__(self)
self.bottle = bottle_obj
self.new_name = new_name
self.main_window = main_window
self.success = False
self.err = None
def __unicode__(self):
return "RenameBottleOperation for " + self.bottle.name
def enqueued(self):
self.bottle.changeablename = self.new_name
self.bottle.pre_rename()
bottlecollection.sharedCollection().add_ignored_bottle(self.new_name)
#self.main_window.update_bottle_list()
pyop.PythonOperation.enqueued(self)
def main(self):
(self.success, self.err) = self.bottle.rename()
def finish(self):
self.bottle.post_rename(self.success)
bottlecollection.sharedCollection().remove_ignored_bottle(self.new_name)
if self.success:
bottlecollection.sharedCollection().refresh()
pyop.PythonOperation.finish(self)
class ArchiveBottleOperation(pyop.PythonOperation):
def __init__(self, bottle, archive_path):
pyop.PythonOperation.__init__(self)
self.exitStatus = None
self.bottle = bottle
self.archive_path = archive_path
def __unicode__(self):
return "ArchiveBottleOperation for " + self.bottle.name
def enqueued(self):
self.bottle.add_status_override(self.bottle.STATUS_ARCHIVING)
pyop.PythonOperation.enqueued(self)
def main(self):
self.exitStatus = bottlecollection.sharedCollection().archiveBottle(self.bottle.name, self.archive_path)
def finish(self):
if not self.exitStatus[0]:
cxguitools.CXMessageDlg(primary=_("An error occurred while archiving %s") % self.bottle.name, secondary=self.exitStatus[1], message_type=gtk.MESSAGE_ERROR)
self.bottle.remove_status_override(self.bottle.STATUS_ARCHIVING)
pyop.PythonOperation.finish(self)
class UpgradeBottleOperation(pyop.PythonOperation):
def __init__(self, bottle):
pyop.PythonOperation.__init__(self)
self.bottle = bottle
def __unicode__(self):
return "UpgradeBottleOperation for " + self.bottle.name
def enqueued(self):
self.bottle.add_status_override(self.bottle.STATUS_UPGRADING)
pyop.PythonOperation.enqueued(self)
def main(self):
wine = os.path.join(cxutils.CX_ROOT, "bin", "wine")
if cxproduct.is_root_install():
cxsu = os.path.join(cxutils.CX_ROOT, "bin", "cxsu")
cxsu_args = [cxsu, '--ignore-home']
else:
cxsu_args = []
args = cxsu_args + [wine, "--bottle", self.bottle.name,
"--scope", "managed", "--ux-app", "true"]
cxutils.run(args)
def finish(self):
self.bottle.refresh_up_to_date()
self.bottle.remove_status_override(self.bottle.STATUS_UPGRADING)
pyop.PythonOperation.finish(self)
class SetDefaultBottleOperation(pyop.PythonOperation):
def __init__(self, bottle, state):
pyop.PythonOperation.__init__(self)
self.state = state
self.bottle = bottle
self.bottle_name = bottle.name
def __unicode__(self):
return "SetDefaultBottleOperation for " + self.bottle_name
def enqueued(self):
self.bottle.add_status_override(self.bottle.STATUS_DEFAULTING)
pyop.PythonOperation.enqueued(self)
def main(self):
bottlemanagement.set_default_bottle(self.bottle_name, self.state)
def finish(self):
bottlecollection.sharedCollection().refreshDefaultBottle()
self.bottle.remove_status_override(self.bottle.STATUS_DEFAULTING)
pyop.PythonOperation.finish(self)
class SetCsmtOperation(pyop.PythonOperation):
def __init__(self, bottle, enabled):
pyop.PythonOperation.__init__(self)
self.enabled = enabled
self.bottle = bottle
self.bottle_name = bottle.name
def __unicode__(self):
return "SetCsmtOperation for " + self.bottle_name
def enqueued(self):
pyop.PythonOperation.enqueued(self)
def main(self):
if self.enabled:
self.bottle.enable_csmt()
else:
self.bottle.disable_csmt()
def finish(self):
pyop.PythonOperation.finish(self)
class SetDxvkOperation(pyop.PythonOperation):
def __init__(self, bottle, enabled):
pyop.PythonOperation.__init__(self)
self.enabled = enabled
self.bottle = bottle
self.bottle_name = bottle.name
def __unicode__(self):
return "SetDxvkOperation for " + self.bottle_name
def enqueued(self):
pyop.PythonOperation.enqueued(self)
def main(self):
if self.enabled:
self.bottle.enable_dxvk()
else:
self.bottle.disable_dxvk()
def finish(self):
pyop.PythonOperation.finish(self)
class UpdateDescriptionOperation(pyop.PythonOperation):
def __init__(self, bottle, main_window):
pyop.PythonOperation.__init__(self)
self.bottle = bottle
self.bottle_name = bottle.name
self.main_window = main_window
self.succeeded = False
def __unicode__(self):
return "UpdateDescriptionOperation for " + self.bottle_name
def enqueued(self):
pyop.PythonOperation.enqueued(self)
self.main_window.updating_description_bottles.add(self.bottle)
self.succeeded = False
def main(self):
self.bottle.write_description()
self.succeeded = True
def finish(self):
pyop.PythonOperation.finish(self)
if self.succeeded:
if self.bottle.is_description_written():
self.main_window.updating_description_bottles.remove(self.bottle)
else:
pyop.sharedOperationQueue.enqueue(self)