# (c) Copyright 2009-2011, 2015. CodeWeavers, Inc.
import os
import gobject
import cxdecorators
import cxproduct
import cxutils
import bottlequery
import bottlewrapper
import bottlemanagement
import pyop
ALL_INFO = ("basic", "controlpanel", "applications", "csmt", "dxvk")
focused_bottle = None
def sharedCollection(infoToTrack=()):
global sharedBottleCollection
try:
sharedBottleCollection.track_info(infoToTrack)
except NameError:
sharedBottleCollection = BottleCollection(infoToTrack)
return sharedBottleCollection
class BottleCollection(object):
bottleCollectionDict = None
def __init__(self, infoToTrack=ALL_INFO):
self.bottleCollectionDict = {}
self.ignored_bottles = []
self.defaultBottleName = ""
self._infoToTrack = set(infoToTrack)
self.changeDelegates = []
self.bottleChangeDelegates = []
self.refresh()
# Set up a timer so we can poll for newly-created bottles.
gobject.timeout_add(2000, self.refresh)
def queue_bottle_updates(self, bottles, info):
if 'basic' in info:
for bottle in bottles:
updateOp = RefreshBasicInfoOperation(bottle)
pyop.sharedOperationQueue.enqueue(updateOp)
if 'applications' in info or 'controlpanel' in info or 'csmt' in info or 'dxvk' in info:
for bottle in bottles:
#pylint: disable=R0204
updateOp = RefreshExtraInfoOperation(bottle, info)
pyop.sharedOperationQueue.enqueue(updateOp)
def refresh(self):
bottleList = bottlequery.get_bottle_list()
changes = False
removeList = []
# Handle renames
for bottleNameKey in self.bottleCollectionDict:
newName = self.bottleCollectionDict[bottleNameKey].name
if newName != bottleNameKey:
self.bottleCollectionDict[newName] = self.bottleCollectionDict.pop(bottleNameKey)
changes = True
# Remove deleted bottles
for bottleNameKey in self.bottleCollectionDict:
bottle = self.bottleCollectionDict[bottleNameKey]
bottleName = bottle.name
if bottleName not in bottleList and \
bottlewrapper.BottleWrapper.STATUS_RENAMING not in bottle.status_overrides:
changes = True
removeList.append(bottleNameKey)
for bottleNameKey in removeList:
self._removeBottle(bottleNameKey)
# Check for updates
bottles_to_update = []
for bottleNameKey in self.bottleCollectionDict:
if self.bottleCollectionDict[bottleNameKey].get_needs_update(updating=True):
bottles_to_update.append(self.bottleCollectionDict[bottleNameKey])
# Add new bottles
for bottleName in bottleList:
if bottleName not in self.bottleCollectionDict and \
bottleName not in self.ignored_bottles:
self._addBottle(bottleName)
self.bottleCollectionDict[bottleName].get_needs_update(updating=True)
bottles_to_update.append(self.bottleCollectionDict[bottleName])
changes = True
if bottles_to_update:
# FIXME: We don't really need to reload 'basic' info
# if this bottle was just added.
self.queue_bottle_updates(bottles_to_update, self._infoToTrack)
if changes:
for delegate in self.changeDelegates:
delegate.bottleCollectionChanged()
return True
def track_info(self, new_info):
if not self._infoToTrack.issuperset(new_info):
new_info = set(new_info)
new_info.difference_update(self._infoToTrack)
self._infoToTrack.update(new_info)
self.queue_bottle_updates(self.bottleCollectionDict.values(), self._infoToTrack)
def add_ignored_bottle(self, newname):
self.ignored_bottles.append(cxutils.expect_unicode(newname))
def remove_ignored_bottle(self, newname):
self.ignored_bottles.remove(cxutils.expect_unicode(newname))
def refreshDefaultBottle(self):
self.defaultBottleName = bottlequery.get_default_bottle()
for bottle in self.bottleCollectionDict.itervalues():
bottle.is_default = (bottle.name == self.defaultBottleName)
def getDefaultBottle(self):
return self.defaultBottleName
def bottleObject(self, inBottleName):
return self.bottleCollectionDict[cxutils.expect_unicode(inBottleName)]
def bottleList(self):
return self.bottleCollectionDict.keys()
def bottles(self):
return self.bottleCollectionDict.values()
def bottleCount(self):
return len(self.bottleCollectionDict)
def _addBottle(self, inBottleName):
inBottleName = cxutils.expect_unicode(inBottleName)
# Make sure this isn't already in the list.
for bottle in self.bottles():
if bottle.name == inBottleName:
return
bottleobj = bottlewrapper.BottleWrapper(inBottleName)
self.bottleCollectionDict[inBottleName] = bottleobj
for delegate in self.bottleChangeDelegates:
bottleobj.add_change_delegate(delegate)
self.refreshDefaultBottle()
def _removeBottle(self, inBottleName):
self.bottleCollectionDict.pop(cxutils.expect_unicode(inBottleName), None)
self.refreshDefaultBottle()
def addChangeDelegate(self, inDelegate):
self.changeDelegates.append(inDelegate)
def removeChangeDelegate(self, inDelegate):
self.changeDelegates.remove(inDelegate)
def addBottleChangeDelegate(self, inDelegate):
self.bottleChangeDelegates.append(inDelegate)
for bottleobj in self.bottles():
bottleobj.add_change_delegate(inDelegate)
def removeBottleChangeDelegate(self, inDelegate):
self.bottleChangeDelegates.remove(inDelegate)
for bottleobj in self.bottles():
bottleobj.remove_change_delegate(inDelegate)
def archiveBottle(self, inBottleName, inArchivePath):
inBottleName = cxutils.expect_unicode(inBottleName)
bottle_obj = self.bottleObject(inBottleName)
bottle_obj.add_status_override(bottle_obj.STATUS_ARCHIVING)
try:
rVal = bottlemanagement.archive_bottle(inBottleName, inArchivePath)
finally:
bottle_obj.remove_status_override(bottle_obj.STATUS_ARCHIVING)
return rVal
def boost_priority(bottleobj):
#pylint: disable=W0603
global focused_bottle
focused_bottle = bottleobj
class RefreshInfoOperation(pyop.PythonOperation):
def __init__(self, inBottle):
pyop.PythonOperation.__init__(self)
self.bottleObject = inBottle
def _get_priority(self):
if self.bottleObject is focused_bottle:
return 1
return -1
@cxdecorators.abstractmethod
def main(self):
# so pylint recognizes this as an abstract class
raise NotImplementedError()
priority = property(_get_priority)
class RefreshBasicInfoOperation(RefreshInfoOperation):
def __unicode__(self):
return " ".join("RefreshBasicInfoOperation for" + repr(self.bottleObject))
def enqueued(self):
pyop.PythonOperation.enqueued(self)
self.bottleObject.pre_load_basic_info()
def main(self):
self.bottleObject.load_basic_info()
def finish(self):
self.bottleObject.post_load_basic_info()
pyop.PythonOperation.finish(self)
class RefreshExtraInfoOperation(RefreshInfoOperation):
def __init__(self, inBottle, info):
RefreshInfoOperation.__init__(self, inBottle)
self.info = info
def __unicode__(self):
return " ".join("RefreshExtraInfoOperation for" + repr(self.bottleObject))
def enqueued(self):
pyop.PythonOperation.enqueued(self)
if 'applications' in self.info:
self.bottleObject.pre_load_installed_applications()
if 'controlpanel' in self.info:
self.bottleObject.pre_load_control_panel_info()
if 'csmt' in self.info:
self.bottleObject.pre_load_is_csmt_disabled()
if 'dxvk' in self.info:
self.bottleObject.pre_load_is_dxvk_enabled()
def main(self):
if 'applications' in self.info:
self.bottleObject.load_installed_applications()
if 'controlpanel' in self.info:
self.bottleObject.load_control_panel_info()
if 'csmt' in self.info:
self.bottleObject.load_is_csmt_disabled()
if 'dxvk' in self.info:
self.bottleObject.load_is_dxvk_enabled()
def finish(self):
if 'applications' in self.info:
self.bottleObject.post_load_installed_applications()
if 'controlpanel' in self.info:
self.bottleObject.post_load_control_panel_info()
if 'csmt' in self.info:
self.bottleObject.post_load_is_csmt_disabled()
if 'dxvk' in self.info:
self.bottleObject.post_load_is_dxvk_enabled()
pyop.PythonOperation.finish(self)
class QuitBottleOperation(pyop.PythonOperation):
def __init__(self, inBottleList, inForce=False, inFinishDelegate=None):
pyop.PythonOperation.__init__(self)
self.bottleList = [cxutils.expect_unicode(x) for x in inBottleList]
self.force = inForce
self.finishDelegate = inFinishDelegate
self.succeeded = True
def __unicode__(self):
return " ".join(["QuitBottleOperation for"] + self.bottleList)
def main(self):
for bottleName in self.bottleList:
bottleObj = sharedCollection().bottleObject(bottleName)
if self.force:
if not bottleObj.force_quit():
self.succeeded = False
else:
if not bottleObj.quit():
self.succeeded = False
bottleObj.cancelShutdown()
def finish(self):
if self.finishDelegate:
self.finishDelegate.opFinished(self)
pyop.PythonOperation.finish(self)
def unique_bottle_name(inBasename):
collection = sharedCollection(('basic',))
existingBottles = set()
existingBottles.update(collection.bottleList())
existingBottles.update(collection.ignored_bottles)
collision = True
candidate = inBasename
i = 1
while collision:
collision = False
for bottlename in existingBottles:
if candidate.lower() == bottlename.lower():
collision = True
break
if not collision:
wineprefix = os.path.join(cxproduct.get_bottle_path().split(":")[0], candidate)
if os.path.exists(wineprefix):
collision = True
if collision:
i += 1
candidate = inBasename + "-" + unicode(i)
return candidate