Skip to content

Commit

Permalink
Allow FOMODs/wizards to handle multiple packages
Browse files Browse the repository at this point in the history
Might be easier to view this commit with whitespace diff disabled.

This allows all interactive installers, including the 'smart' one, to
handle multiple selected packages. They will be processed in order,
with each package getting programmatically selected before the installer
is started. That way you can now select any arbitrary number of
packages, click Install and get the best installers out of wizards,
FOMODs and regular BAIN installation selected automatically.

Note: I dropped the unused `parentWindow = ''` from Installer_Wizard and
Installer_Fomod, no idea why that was there.

Mopy/bash/basher/gui_fomod.py:
Mopy/bash/basher/mod_links.py:
BusyCursor no longer lives in balt, don't rely on it being imported
there

Closes #551
  • Loading branch information
Infernio committed Dec 24, 2020
1 parent 63d08f3 commit a3057a7
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 95 deletions.
4 changes: 2 additions & 2 deletions Mopy/bash/basher/__init__.py
Expand Up @@ -2995,8 +2995,8 @@ def _update_fomod_state(self):
self.sp_label.visible = not has_fomod
# Same deal as above. Note that we need to do these always, otherwise
# the Sub-Packages list would stay disabled when switching installers
fomod_checked = has_fomod and inst_info.extras_dict.get(
u'fomod_active', False)
fomod_checked = bool(has_fomod and inst_info.extras_dict.get(
u'fomod_active', False))
self.fomod_btn.is_checked = fomod_checked
self.sp_btn.is_checked = not fomod_checked
self.gSubList.enabled = not fomod_checked
Expand Down
5 changes: 3 additions & 2 deletions Mopy/bash/basher/gui_fomod.py
Expand Up @@ -33,7 +33,8 @@
InstallerOption, InstallerPage
from ..gui import CENTER, CheckBox, HBoxedLayout, HLayout, Label, \
LayoutOptions, TextArea, VLayout, WizardDialog, EventResult, \
PictureWithCursor, RadioButton, ScrollableWindow, Stretch, Table
PictureWithCursor, RadioButton, ScrollableWindow, Stretch, Table, \
BusyCursor

class FomodInstallInfo(object):
__slots__ = (u'canceled', u'install_files', u'should_install')
Expand Down Expand Up @@ -429,7 +430,7 @@ def __init__(self, page_parent):
u'going to be installed.'))
check_output.on_checked.subscribe(self._on_switch_output)
# This can take a bit for very large FOMOD installs
with balt.BusyCursor():
with BusyCursor():
installer_output = self._page_parent.fomod_parser.get_fomod_files()
# Create the two alternative output displays and fill them with
# data from the FOMOD parser
Expand Down
191 changes: 101 additions & 90 deletions Mopy/bash/basher/installer_links.py
Expand Up @@ -166,48 +166,53 @@ def _enable(self):
return bool(self._installables) and super(_NoMarkerLink, self)._enable()

#------------------------------------------------------------------------------
class _Installer_AWizardLink(OneItemLink, _InstallerLink):
class _Installer_AWizardLink(_InstallerLink):
"""Base class for wizard links."""
def _perform_install(self, ui_refresh):
if self._selected_info.is_active: # If it's currently installed, anneal
title, do_it = _(u'Annealing...'), self.idata.bain_anneal
def _perform_install(self, sel_package, ui_refresh):
if sel_package.is_active: # If it's currently installed, anneal
title = _(u'Annealing...')
do_it = self.window.data_store.bain_anneal
else: # Install if it's not installed
title, do_it = _(u'Installing...'), self.idata.bain_install
title = _(u'Installing...')
do_it = self.window.data_store.bain_install
with balt.Progress(title, u'\n'+u' '*60) as progress:
do_it(self.selected, ui_refresh, progress)
do_it([GPath(sel_package.archive)], ui_refresh, progress)

class Installer_Fomod(_Installer_AWizardLink):
"""Runs the FOMOD installer"""
parentWindow = ''
_text = _(u'FOMOD Installer...')
_help = _(u'Run the FOMOD installer.')

def _enable(self):
is_single = super(Installer_Fomod, self)._enable()
return is_single and bool(self._selected_info.has_fomod_conf)
return super(Installer_Fomod, self)._enable() and all(
i.has_fomod_conf for i in self.iselected_infos())

@balt.conversation
def Execute(self):
with balt.BusyCursor():
sel_installer = self._selected_info
try:
fm_wizard = InstallerFomod(self.window, sel_installer)
except CancelError:
return
fm_wizard.ensureDisplayed()
# Run the FOMOD installer
ret = fm_wizard.run_fomod()
if ret.canceled:
return
ui_refresh = [False, False]
idetails = self.iPanel.detailsPanel
# Switch the GUI to FOMOD mode and pass the selected files to BAIN
idetails.set_fomod_mode(fomod_enabled=True)
sel_installer.extras_dict[u'fomod_dict'] = ret.install_files
try:
idetails.refreshCurrent(sel_installer)
if ret.should_install:
self._perform_install(ui_refresh)
# Use list() since we're going to deselect packages
for sel_package in list(self.iselected_infos()):
with BusyCursor():
# Select the package we want to install - posts events to
# set details and update GUI
self.window.SelectItem(GPath(sel_package.archive))
try:
fm_wizard = InstallerFomod(self.window, sel_package)
except CancelError:
continue
fm_wizard.ensureDisplayed()
# Run the FOMOD installer
ret = fm_wizard.run_fomod()
if ret.canceled:
continue
# Switch the GUI to FOMOD mode and pass selected files to BAIN
idetails.set_fomod_mode(fomod_enabled=True)
sel_package.extras_dict[u'fomod_dict'] = ret.install_files
idetails.refreshCurrent(sel_package)
if ret.should_install:
self._perform_install(sel_package, ui_refresh)
finally:
self.iPanel.RefreshUIMods(*ui_refresh)

Expand All @@ -228,8 +233,6 @@ def Execute(self): self._selected_info.open_wizard()

class Installer_Wizard(_Installer_AWizardLink):
"""Runs the install wizard to select subpackages and plugin filtering"""
parentWindow = ''

def __init__(self, bAuto):
super(Installer_Wizard, self).__init__()
self.bAuto = bAuto
Expand All @@ -239,57 +242,63 @@ def __init__(self, bAuto):
) if self.bAuto else _(u"Run the install wizard.")

def _enable(self):
single_item = super(Installer_Wizard, self)._enable()
return single_item and self._selected_info.hasWizard != False
return super(Installer_Wizard, self)._enable() and all(
i.hasWizard for i in self.iselected_infos())

@balt.conversation
def Execute(self):
##: Investigate why we have so many refreshCurrents in here. At least
# the first one seems pointless?
with BusyCursor():
installer = self._selected_info
idetails = self.iPanel.detailsPanel
idetails.refreshCurrent(installer)
try:
wizard = InstallerWizard(self.window, self._selected_info,
self.bAuto)
except CancelError:
return
wizard.ensureDisplayed()
ret = wizard.Run()
if ret.canceled:
idetails.refreshCurrent(installer)
return
installer.resetAllEspmNames()
# Switch away from FOMOD mode, then check the sub-packages that were
# selected by the wizard
idetails.set_fomod_mode(fomod_enabled=False)
for index in xrange(len(installer.subNames[1:])):
select = installer.subNames[index + 1] in ret.select_sub_packages
idetails.gSubList.lb_check_at_index(index, select)
installer.subActives[index + 1] = select
idetails.refreshCurrent(installer)
#Check the espms that were selected by the wizard
espms = idetails.gEspmList.lb_get_str_items()
espms = [x.replace(u'&&',u'&') for x in espms]
installer.espmNots = set()
for index, espm in enumerate(idetails.espms):
if espms[index] in ret.select_plugins:
idetails.gEspmList.lb_check_at_index(index, True)
else:
idetails.gEspmList.lb_check_at_index(index, False)
installer.espmNots.add(espm)
idetails.refreshCurrent(installer)
#Rename the espms that need renaming
for oldName in ret.rename_plugins:
installer.setEspmName(oldName, ret.rename_plugins[oldName])
idetails.refreshCurrent(installer)
#Install if necessary
##: Investigate why we have so many refreshCurrents in here.
# Installer_Fomod has just one!
ui_refresh = [False, False]
idetails = self.iPanel.detailsPanel
try:
if ret.should_install:
self._perform_install(ui_refresh)
self._apply_tweaks(installer, ret, ui_refresh)
# Use list() since we're going to deselect packages
for sel_package in list(self.iselected_infos()):
with BusyCursor():
# Select the package we want to install - posts events to
# set details and update GUI
self.window.SelectItem(GPath(sel_package.archive))
idetails.refreshCurrent(sel_package)
try:
wizard = InstallerWizard(self.window, sel_package,
self.bAuto)
except CancelError:
return
wizard.ensureDisplayed()
ret = wizard.Run()
if ret.canceled:
idetails.refreshCurrent(sel_package)
continue
sel_package.resetAllEspmNames()
# Switch away from FOMOD mode, then check the sub-packages that
# were selected by the wizard
idetails.set_fomod_mode(fomod_enabled=False)
for index in xrange(len(sel_package.subNames[1:])):
select = (sel_package.subNames[index + 1] in
ret.select_sub_packages)
idetails.gSubList.lb_check_at_index(index, select)
sel_package.subActives[index + 1] = select
idetails.refreshCurrent(sel_package)
# Check the plugins that were selected by the wizard
espms = idetails.gEspmList.lb_get_str_items()
espms = [x.replace(u'&&',u'&') for x in espms]
sel_package.espmNots = set()
for index, espm in enumerate(idetails.espms):
if espms[index] in ret.select_plugins:
idetails.gEspmList.lb_check_at_index(index, True)
else:
idetails.gEspmList.lb_check_at_index(index, False)
sel_package.espmNots.add(espm)
idetails.refreshCurrent(sel_package)
#Rename the espms that need renaming
for oldName in ret.rename_plugins:
sel_package.setEspmName(oldName,
ret.rename_plugins[oldName])
idetails.refreshCurrent(sel_package)
#Install if necessary
if ret.should_install:
self._perform_install(sel_package, ui_refresh)
self._apply_tweaks(sel_package, ret, ui_refresh)
finally:
self.iPanel.RefreshUIMods(*ui_refresh)

Expand Down Expand Up @@ -531,38 +540,40 @@ def _warn_mismatched_ini_tweaks_created(self, new_tweaks):
u'\n' + u'\n'.join([u' * %s\n' % x.stail for (x, y) in new_tweaks])
self._showInfo(msg, title=_(u'INI Tweaks'))

##: Would be nice to have this work for multiple installers, but wizards use
# self.iPanel.detailsPanel, which won't be set correctly when multiple
# installers are selected
class Installer_InstallSmart(_NoMarkerLink, OneItemLink):
class Installer_InstallSmart(_NoMarkerLink):
"""A 'smart' installer for new users. Uses wizards and FOMODs if present,
then falls back to regular install if that isn't possible."""
_text = _(u'Install...')
_help = _(u'Installs selected installer(s), preferring a visual method if '
u'available.')

def _try_installer(self, inst_instance):
def _try_installer(self, sel_package, inst_instance):
"""Checks if the specified installer link is enabled and, if so, runs
it.
:type inst_instance: EnabledLink"""
inst_instance._initData(self.window, self.selected)
inst_instance._initData(self.window, [GPath(sel_package.archive)])
if inst_instance._enable():
inst_instance.Execute()
return True
return False

def Execute(self):
##: Not the best implementation. It is pretty readable and obvious though
# Look for a BAIN wizard first, best integration with BAIN (duh)
if self._try_installer(Installer_Wizard(bAuto=False)): return
# Next, look for an FOMOD wizard - not quite as good, but at least it's
# visual
if self._try_installer(Installer_Fomod()): return
# Finally, fall back to the regular 'install' method
self._try_installer(Installer_Install())

class Installer_ListStructure(OneItemLink, _InstallerLink): # Provided by Waruddar
##: Not the best implementation, pretty readable and obvious though
inst_wiz = Installer_Wizard(bAuto=False)
inst_fomod = Installer_Fomod()
inst_regular = Installer_Install()
# Use list() since the interactive installers can change selection
for sel_package in list(self.iselected_infos()):
# Look for a BAIN wizard first, best integration with BAIN (duh)
if self._try_installer(sel_package, inst_wiz): continue
# Next, look for an FOMOD wizard - not quite as good, but at least
# it's visual
if self._try_installer(sel_package, inst_fomod): continue
# Finally, fall back to the regular 'Install Current' method
self._try_installer(sel_package, inst_regular)

class Installer_ListStructure(OneItemLink, _InstallerLink):
"""Copies folder structure of installer to clipboard."""
_text = _(u"List Structure...")
_help = _(u'Displays the folder structure of the selected installer (and '
Expand Down
2 changes: 1 addition & 1 deletion Mopy/bash/basher/mod_links.py
Expand Up @@ -1374,7 +1374,7 @@ def Execute(self):
do_save_lo = False
add_esm_flag = self._target_ext in (u'.esm', u'.esl')
add_esl_flag = self._target_ext == u'.esl'
with balt.BusyCursor(): # ONAM generation can take a bit
with BusyCursor(): # ONAM generation can take a bit
for curName, minfo in self.iselected_pairs():
newName = curName.root + self._target_ext
if newName == curName: continue
Expand Down

0 comments on commit a3057a7

Please sign in to comment.