", desc)
- desc = "" + desc + ""
+ desc = "{}".format(
+ bgcolor.GetAsString(wx.C2S_CSS_SYNTAX),
+ fgcolor.GetAsString(wx.C2S_CSS_SYNTAX),
+ desc
+ )
self.description.SetPage(desc)
mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0)
self.Layout()
+
+ self.description.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
+ self.description.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
+
+ self.popupMenu = wx.Menu()
+ copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
+ self.popupMenu.Append(copyItem)
+ self.popupMenu.Bind(wx.EVT_MENU, self.menuClickHandler, copyItem)
+
+ def onPopupMenu(self, event):
+ self.PopupMenu(self.popupMenu)
+
+ def menuClickHandler(self, event):
+ selectedMenuItem = event.GetId()
+ if selectedMenuItem == 1: # Copy was chosen
+ self.copySelectionToClipboard()
+
+ def onKeyDown(self, event):
+ keyCode = event.GetKeyCode()
+ # Ctrl + C
+ if keyCode == 67 and event.ControlDown():
+ self.copySelectionToClipboard()
+ # Ctrl + A
+ if keyCode == 65 and event.ControlDown():
+ self.description.SelectAll()
+
+ def copySelectionToClipboard(self):
+ selectedText = self.description.SelectionToText()
+ if selectedText == '': # if no selection, copy all content
+ selectedText = self.description.ToText()
+ if wx.TheClipboard.Open():
+ wx.TheClipboard.SetData(wx.TextDataObject(selectedText))
+ wx.TheClipboard.Close()
diff --git a/gui/builtinItemStatsViews/itemEffects.py b/gui/builtinItemStatsViews/itemEffects.py
index e586786a6..37b37c608 100644
--- a/gui/builtinItemStatsViews/itemEffects.py
+++ b/gui/builtinItemStatsViews/itemEffects.py
@@ -45,7 +45,7 @@ class ItemEffects(wx.Panel):
self.effectList.SetColumnWidth(4, 40)
item = self.item
- effects = item.effects
+ self.effects = effects = item.effects
names = list(effects.keys())
names.sort()
@@ -100,14 +100,15 @@ class ItemEffects(wx.Panel):
self.RefreshValues(event)
- @staticmethod
- def OnRightClick(event):
+ def OnRightClick(self, event):
"""
Debug use: open effect file with default application.
If effect file does not exist, create it
"""
- file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower())
+ effect = self.effects[event.GetText()]
+
+ file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % effect.handlerName)
if not os.path.isfile(file_):
open(file_, 'a').close()
diff --git a/gui/builtinItemStatsViews/itemMutator.py b/gui/builtinItemStatsViews/itemMutator.py
new file mode 100644
index 000000000..dc91453fa
--- /dev/null
+++ b/gui/builtinItemStatsViews/itemMutator.py
@@ -0,0 +1,193 @@
+# noinspection PyPackageRequirements
+import random
+
+import wx
+from logbook import Logger
+
+import gui.globalEvents as GE
+import gui.mainFrame
+from gui.bitmap_loader import BitmapLoader
+from service.fit import Fit
+from .attributeSlider import AttributeSlider, EVT_VALUE_CHANGED
+from .itemAttributes import ItemParams
+
+pyfalog = Logger(__name__)
+
+
+class ItemMutator(wx.Panel):
+
+ def __init__(self, parent, stuff, item):
+ wx.Panel.__init__(self, parent)
+ self.stuff = stuff
+ self.item = item
+ self.timer = None
+ self.activeFit = gui.mainFrame.MainFrame.getInstance().getActiveFit()
+
+ font = parent.GetFont()
+ font.SetWeight(wx.BOLD)
+
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+ sourceItemsSizer = wx.BoxSizer(wx.HORIZONTAL)
+ sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.item.iconID, self, "icons"), 0, wx.LEFT, 5)
+ sourceItemsSizer.Add(BitmapLoader.getStaticBitmap(stuff.mutaplasmid.item.iconID, self, "icons"), 0, wx.LEFT, 0)
+ sourceItemShort = "{} {}".format(stuff.mutaplasmid.item.name.split(" ")[0], stuff.baseItem.name)
+ sourceItemText = wx.StaticText(self, wx.ID_ANY, sourceItemShort)
+ sourceItemText.SetFont(font)
+ sourceItemsSizer.Add(sourceItemText, 0, wx.LEFT, 10)
+ mainSizer.Add(sourceItemsSizer, 0, wx.TOP | wx.EXPAND, 10)
+
+ mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
+
+ self.goodColor = wx.Colour(96, 191, 0)
+ self.badColor = wx.Colour(255, 64, 0)
+
+ self.event_mapping = {}
+
+ for m in sorted(stuff.mutators.values(), key=lambda x: x.attribute.displayName):
+ # Format: [raw value, modifier applied to base raw value, display value]
+ range1 = (m.minValue, m.attribute.unit.SimplifyValue(m.minValue))
+ range2 = (m.maxValue, m.attribute.unit.SimplifyValue(m.maxValue))
+
+ # minValue/maxValue do not always correspond to min/max, because these are
+ # just base value multiplied by minMod/maxMod, and in case base is negative
+ # minValue is actually bigger than maxValue
+ if range1[0] <= range2[0]:
+ minRange = range1
+ maxRange = range2
+ else:
+ minRange = range2
+ maxRange = range1
+
+ if (m.highIsGood and minRange[0] >= maxRange[0]) or (not m.highIsGood and minRange[0] <= maxRange[0]):
+ betterRange = minRange
+ worseRange = maxRange
+ else:
+ betterRange = maxRange
+ worseRange = minRange
+
+ if minRange[1] >= maxRange[1]:
+ displayMaxRange = minRange
+ displayMinRange = maxRange
+ else:
+ displayMaxRange = maxRange
+ displayMinRange = minRange
+
+ # If base value is outside of mutation range, make sure that center of slider
+ # corresponds to the value which is closest available to actual base value. It's
+ # how EVE handles it
+ if minRange[0] <= m.baseValue <= maxRange[0]:
+ sliderBaseValue = m.baseValue
+ else:
+ sliderBaseValue = max(minRange[0], min(maxRange[0], m.baseValue))
+
+ headingSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ headingSizer.Add(BitmapLoader.getStaticBitmap(m.attribute.iconID, self, "icons"), 0, wx.RIGHT, 10)
+
+ displayName = wx.StaticText(self, wx.ID_ANY, m.attribute.displayName)
+ displayName.SetFont(font)
+
+ headingSizer.Add(displayName, 3, wx.ALL | wx.EXPAND, 0)
+
+ worseVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(worseRange[0]), rounding='dec')
+ worseText = wx.StaticText(self, wx.ID_ANY, worseVal)
+ worseText.SetForegroundColour(self.badColor)
+
+ betterVal = ItemParams.FormatValue(*m.attribute.unit.PreformatValue(betterRange[0]), rounding='dec')
+ betterText = wx.StaticText(self, wx.ID_ANY, betterVal)
+ betterText.SetForegroundColour(self.goodColor)
+
+ headingSizer.Add(worseText, 0, wx.ALL | wx.EXPAND, 0)
+ headingSizer.Add(wx.StaticText(self, wx.ID_ANY, " ─ "), 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 5)
+ headingSizer.Add(betterText, 0, wx.RIGHT | wx.EXPAND, 10)
+
+ mainSizer.Add(headingSizer, 0, wx.ALL | wx.EXPAND, 5)
+
+ slider = AttributeSlider(parent=self,
+ baseValue=m.attribute.unit.SimplifyValue(sliderBaseValue),
+ minValue=displayMinRange[1],
+ maxValue=displayMaxRange[1],
+ inverse=displayMaxRange is worseRange)
+ slider.SetValue(m.attribute.unit.SimplifyValue(m.value), False)
+ slider.Bind(EVT_VALUE_CHANGED, self.changeMutatedValue)
+ self.event_mapping[slider] = m
+ mainSizer.Add(slider, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
+
+ mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.ALL | wx.EXPAND, 5)
+
+ mainSizer.AddStretchSpacer()
+
+ self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
+ mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
+
+ bSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ self.refreshBtn = wx.Button(self, wx.ID_ANY, "Reset defaults", wx.DefaultPosition, wx.DefaultSize, 0)
+ bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
+ self.refreshBtn.Bind(wx.EVT_BUTTON, self.resetMutatedValues)
+
+ self.randomBtn = wx.Button(self, wx.ID_ANY, "Random stats", wx.DefaultPosition, wx.DefaultSize, 0)
+ bSizer.Add(self.randomBtn, 0, wx.ALIGN_CENTER_VERTICAL)
+ self.randomBtn.Bind(wx.EVT_BUTTON, self.randomMutatedValues)
+
+ mainSizer.Add(bSizer, 0, wx.RIGHT | wx.LEFT | wx.EXPAND, 0)
+
+ self.SetSizer(mainSizer)
+ self.Layout()
+
+ def changeMutatedValue(self, evt):
+ m = self.event_mapping[evt.Object]
+ value = evt.Value
+ value = m.attribute.unit.ComplicateValue(value)
+ sFit = Fit.getInstance()
+
+ sFit.changeMutatedValue(m, value)
+ if self.timer:
+ self.timer.Stop()
+ self.timer = None
+
+ for x in self.Parent.Children:
+ if isinstance(x, ItemParams):
+ x.RefreshValues(None)
+ break
+ self.timer = wx.CallLater(1000, self.callLater)
+
+ def resetMutatedValues(self, evt):
+ sFit = Fit.getInstance()
+
+ for slider, m in self.event_mapping.items():
+ value = sFit.changeMutatedValue(m, m.baseValue)
+ value = m.attribute.unit.SimplifyValue(value)
+ slider.SetValue(value)
+
+ evt.Skip()
+
+ def randomMutatedValues(self, evt):
+ sFit = Fit.getInstance()
+
+ for slider, m in self.event_mapping.items():
+ value = random.uniform(m.minValue, m.maxValue)
+ value = sFit.changeMutatedValue(m, value)
+ value = m.attribute.unit.SimplifyValue(value)
+ slider.SetValue(value)
+
+ evt.Skip()
+
+ def callLater(self):
+ self.timer = None
+ sFit = Fit.getInstance()
+
+ # recalc the fit that this module affects. This is not necessarily the currently active fit
+ sFit.refreshFit(self.activeFit)
+
+ mainFrame = gui.mainFrame.MainFrame.getInstance()
+ activeFit = mainFrame.getActiveFit()
+
+ if activeFit != self.activeFit:
+ # if we're no longer on the fit this module is affecting, simulate a "switch fit" so that the active fit
+ # can be recalculated (if needed)
+ sFit.switchFit(activeFit)
+
+ # Send signal to GUI to update stats with current active fit
+ wx.PostEvent(mainFrame, GE.FitChanged(fitID=activeFit))
diff --git a/gui/builtinItemStatsViews/itemTraits.py b/gui/builtinItemStatsViews/itemTraits.py
index 12abd078d..1ea0514a5 100644
--- a/gui/builtinItemStatsViews/itemTraits.py
+++ b/gui/builtinItemStatsViews/itemTraits.py
@@ -13,5 +13,38 @@ class ItemTraits(wx.Panel):
self.traits = wx.html.HtmlWindow(self)
self.traits.SetPage(item.traits.traitText)
+ self.traits.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
+ self.traits.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
+
mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0)
self.Layout()
+
+ self.popupMenu = wx.Menu()
+ copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
+ self.popupMenu.Append(copyItem)
+ self.popupMenu.Bind(wx.EVT_MENU, self.menuClickHandler, copyItem)
+
+ def onPopupMenu(self, event):
+ self.PopupMenu(self.popupMenu)
+
+ def menuClickHandler(self, event):
+ selectedMenuItem = event.GetId()
+ if selectedMenuItem == 1: # Copy was chosen
+ self.copySelectionToClipboard()
+
+ def onKeyDown(self, event):
+ keyCode = event.GetKeyCode()
+ # Ctrl + C
+ if keyCode == 67 and event.ControlDown():
+ self.copySelectionToClipboard()
+ # Ctrl + A
+ if keyCode == 65 and event.ControlDown():
+ self.traits.SelectAll()
+
+ def copySelectionToClipboard(self):
+ selectedText = self.traits.SelectionToText()
+ if selectedText == '': # if no selection, copy all content
+ selectedText = self.traits.ToText()
+ if wx.TheClipboard.Open():
+ wx.TheClipboard.SetData(wx.TextDataObject(selectedText))
+ wx.TheClipboard.Close()
diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py
index 7a26cfed1..82454502a 100644
--- a/gui/builtinMarketBrowser/itemView.py
+++ b/gui/builtinMarketBrowser/itemView.py
@@ -1,16 +1,13 @@
import wx
-
-import config
-import gui.builtinMarketBrowser.pfSearchBox as SBox
-from gui.contextMenu import ContextMenu
-from gui.display import Display
-from service.attribute import Attribute
-from service.fit import Fit
-from gui.utils.staticHelpers import DragDropHelper
-
from logbook import Logger
-from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES, MAX_RECENTLY_USED_MODULES, ItemSelected
+import gui.builtinMarketBrowser.pfSearchBox as SBox
+from gui.builtinMarketBrowser.events import ItemSelected, MAX_RECENTLY_USED_MODULES, RECENTLY_USED_MODULES
+from gui.contextMenu import ContextMenu
+from gui.display import Display
+from gui.utils.staticHelpers import DragDropHelper
+from service.attribute import Attribute
+from service.fit import Fit
pyfalog = Logger(__name__)
diff --git a/gui/builtinMarketBrowser/marketTree.py b/gui/builtinMarketBrowser/marketTree.py
index 8b1286e50..f6c62732c 100644
--- a/gui/builtinMarketBrowser/marketTree.py
+++ b/gui/builtinMarketBrowser/marketTree.py
@@ -53,6 +53,7 @@ class MarketTree(wx.TreeCtrl):
# And add real market group contents
sMkt = self.sMkt
currentMktGrp = sMkt.getMarketGroup(self.GetItemData(root), eager="children")
+
for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp):
# If market should have items but it doesn't, do not show it
if sMkt.marketGroupValidityCheck(childMktGrp) is False:
diff --git a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py
index 458424dce..7c767c5fb 100644
--- a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py
+++ b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py
@@ -81,6 +81,11 @@ class PFContextMenuPref(PreferenceView):
rbSizerRow3.Add(self.rbBox7, 1, wx.TOP | wx.RIGHT, 5)
self.rbBox7.Bind(wx.EVT_RADIOBOX, self.OnSetting7Change)
+ self.rbBox8 = wx.RadioBox(panel, -1, "Fill with module", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
+ self.rbBox8.SetSelection(self.settings.get('moduleFill'))
+ rbSizerRow3.Add(self.rbBox8, 1, wx.TOP | wx.RIGHT, 5)
+ self.rbBox8.Bind(wx.EVT_RADIOBOX, self.OnSetting8Change)
+
mainSizer.Add(rbSizerRow3, 1, wx.ALL | wx.EXPAND, 0)
panel.SetSizer(mainSizer)
@@ -107,6 +112,9 @@ class PFContextMenuPref(PreferenceView):
def OnSetting7Change(self, event):
self.settings.set('project', event.GetInt())
+ def OnSetting8Change(self, event):
+ self.settings.set('moduleFill', event.GetInt())
+
def getImage(self):
return BitmapLoader.getBitmap("settings_menu", "gui")
diff --git a/gui/builtinPreferenceViews/pyfaEnginePreferences.py b/gui/builtinPreferenceViews/pyfaEnginePreferences.py
index 815e1b124..c41921393 100644
--- a/gui/builtinPreferenceViews/pyfaEnginePreferences.py
+++ b/gui/builtinPreferenceViews/pyfaEnginePreferences.py
@@ -4,8 +4,10 @@ import wx
from service.fit import Fit
from gui.bitmap_loader import BitmapLoader
+import gui.globalEvents as GE
from gui.preferenceView import PreferenceView
from service.settings import EOSSettings
+import gui.mainFrame
logger = logging.getLogger(__name__)
@@ -21,6 +23,7 @@ class PFFittingEnginePref(PreferenceView):
# noinspection PyAttributeOutsideInit
def populatePanel(self, panel):
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
mainSizer = wx.BoxSizer(wx.VERTICAL)
@@ -98,6 +101,9 @@ class PFFittingEnginePref(PreferenceView):
def OnCBGlobalForceReloadStateChange(self, event):
self.sFit.serviceFittingOptions["useGlobalForceReload"] = self.cbGlobalForceReload.GetValue()
+ fitID = self.mainFrame.getActiveFit()
+ self.sFit.refreshFit(fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
def OnCBStrictSkillLevelsChange(self, event):
self.engine_settings.set("strictSkillLevels", self.cbStrictSkillLevels.GetValue())
diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py
index 029003a83..39125ed38 100644
--- a/gui/builtinPreferenceViews/pyfaEsiPreferences.py
+++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py
@@ -1,17 +1,13 @@
# noinspection PyPackageRequirements
import wx
-from gui.preferenceView import PreferenceView
-from gui.bitmap_loader import BitmapLoader
-
import gui.mainFrame
-
+from gui.bitmap_loader import BitmapLoader
+from gui.preferenceView import PreferenceView
from service.settings import EsiSettings
-# noinspection PyPackageRequirements
-from wx.lib.intctrl import IntCtrl
-from service.esi import Esi
+# noinspection PyPackageRequirements
class PFEsiPref(PreferenceView):
@@ -43,23 +39,65 @@ class PFEsiPref(PreferenceView):
rbSizer = wx.BoxSizer(wx.HORIZONTAL)
self.rbMode = wx.RadioBox(panel, -1, "Login Authentication Method", wx.DefaultPosition, wx.DefaultSize,
['Local Server', 'Manual'], 1, wx.RA_SPECIFY_COLS)
- self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to with information about the character login.")
- self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application to allow for character login. Use this if having issues with the local server.")
- # self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize,
- # ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS)
+ self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to"
+ " with information about the character login.")
+ self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application "
+ "to allow for character login. Use this if having issues with the local server.")
+
+ self.rbSsoMode = wx.RadioBox(panel, -1, "SSO Mode", wx.DefaultPosition, wx.DefaultSize,
+ ['pyfa.io', 'Custom application'], 1, wx.RA_SPECIFY_COLS)
+ self.rbSsoMode.SetItemToolTip(0, "This options routes SSO Logins through pyfa.io, allowing you to easily login "
+ "without any configuration. When in doubt, use this option.")
+ self.rbSsoMode.SetItemToolTip(1, "This option goes through EVE SSO directly, but requires more configuration. Use "
+ "this is pyfa.io is blocked for some reason, or if you do not wish to route data throguh pyfa.io.")
self.rbMode.SetSelection(self.settings.get('loginMode'))
- # self.rbServer.SetSelection(self.settings.get('server'))
+ self.rbSsoMode.SetSelection(self.settings.get('ssoMode'))
+ rbSizer.Add(self.rbSsoMode, 1, wx.ALL, 5)
rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5)
- # rbSizer.Add(self.rbServer, 1, wx.ALL, 5)
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange)
- # self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange)
+ self.rbSsoMode.Bind(wx.EVT_RADIOBOX, self.OnSSOChange)
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0)
- timeoutSizer = wx.BoxSizer(wx.HORIZONTAL)
+ detailsTitle = wx.StaticText(panel, wx.ID_ANY, "Custom Application", wx.DefaultPosition, wx.DefaultSize, 0)
+ detailsTitle.Wrap(-1)
+ detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
+
+ mainSizer.Add(detailsTitle, 0, wx.ALL, 5)
+ mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0,
+ wx.EXPAND, 5)
+
+ fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0)
+ fgAddrSizer.AddGrowableCol(1)
+ fgAddrSizer.SetFlexibleDirection(wx.BOTH)
+ fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
+
+ self.stSetID = wx.StaticText(panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0)
+ self.stSetID.Wrap(-1)
+ fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
+
+ self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition,
+ wx.DefaultSize, 0)
+
+ fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
+
+ self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0)
+ self.stSetSecret.Wrap(-1)
+
+ fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
+
+ self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition,
+ wx.DefaultSize, 0)
+
+ fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
+
+ self.inputClientID.Bind(wx.EVT_TEXT, self.OnClientDetailChange)
+ self.inputClientSecret.Bind(wx.EVT_TEXT, self.OnClientDetailChange)
+
+ mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5)
# self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0)
# self.stTimout.Wrap(-1)
@@ -112,6 +150,7 @@ class PFEsiPref(PreferenceView):
# self.ToggleProxySettings(self.settings.get('loginMode'))
+ self.ToggleSSOMode(self.settings.get('ssoMode'))
panel.SetSizer(mainSizer)
panel.Layout()
@@ -121,14 +160,31 @@ class PFEsiPref(PreferenceView):
def OnModeChange(self, event):
self.settings.set('loginMode', event.GetInt())
- def OnServerChange(self, event):
- self.settings.set('server', event.GetInt())
+ def OnSSOChange(self, event):
+ self.settings.set('ssoMode', event.GetInt())
+ self.ToggleSSOMode(event.GetInt())
- def OnBtnApply(self, event):
+ def ToggleSSOMode(self, mode):
+ if mode:
+ self.stSetID.Enable()
+ self.inputClientID.Enable()
+ self.stSetSecret.Enable()
+ self.inputClientSecret.Enable()
+ self.rbMode.Disable()
+ else:
+ self.stSetID.Disable()
+ self.inputClientID.Disable()
+ self.stSetSecret.Disable()
+ self.inputClientSecret.Disable()
+ self.rbMode.Enable()
+
+ def OnClientDetailChange(self, evt):
self.settings.set('clientID', self.inputClientID.GetValue().strip())
self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip())
- sEsi = Esi.getInstance()
- sEsi.delAllCharacters()
+
+ # sEsi = Esi.getInstance()
+ # sEsi.delAllCharacters()
+ #
def getImage(self):
return BitmapLoader.getBitmap("eve", "gui")
diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py
index c0ca68a98..7ba91eff3 100644
--- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py
+++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py
@@ -3,12 +3,9 @@
# noinspection PyPackageRequirements
import wx
-import copy
-from gui.preferenceView import PreferenceView
from gui.bitmap_loader import BitmapLoader
-from gui.utils.color import CalculateTransition
-import gui.utils.draw as drawUtils
+from gui.preferenceView import PreferenceView
###########################################################################
diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py
index 412e1a988..51d32ab5d 100644
--- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py
+++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py
@@ -38,6 +38,10 @@ class PFGeneralPref(PreferenceView):
0)
mainSizer.Add(self.cbGlobalChar, 0, wx.ALL | wx.EXPAND, 5)
+ self.cbDefaultCharImplants = wx.CheckBox(panel, wx.ID_ANY, "Use character implants by default for new fits",
+ wx.DefaultPosition, wx.DefaultSize, 0)
+ mainSizer.Add(self.cbDefaultCharImplants, 0, wx.ALL | wx.EXPAND, 5)
+
self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, "Use global damage pattern", wx.DefaultPosition,
wx.DefaultSize, 0)
mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5)
@@ -72,10 +76,6 @@ class PFGeneralPref(PreferenceView):
self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, "Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.cbGaugeAnimation, 0, wx.ALL | wx.EXPAND, 5)
- self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, "Export loaded charges", wx.DefaultPosition,
- wx.DefaultSize, 0)
- mainSizer.Add(self.cbExportCharges, 0, wx.ALL | wx.EXPAND, 5)
-
self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, "Open fittings in a new page by default",
wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.cbOpenFitInNew, 0, wx.ALL | wx.EXPAND, 5)
@@ -119,6 +119,7 @@ class PFGeneralPref(PreferenceView):
self.sFit = Fit.getInstance()
self.cbGlobalChar.SetValue(self.sFit.serviceFittingOptions["useGlobalCharacter"])
+ self.cbDefaultCharImplants.SetValue(self.sFit.serviceFittingOptions["useCharacterImplantsByDefault"])
self.cbGlobalDmgPattern.SetValue(self.sFit.serviceFittingOptions["useGlobalDamagePattern"])
self.cbFitColorSlots.SetValue(self.sFit.serviceFittingOptions["colorFitBySlot"] or False)
self.cbRackSlots.SetValue(self.sFit.serviceFittingOptions["rackSlots"] or False)
@@ -128,7 +129,6 @@ class PFGeneralPref(PreferenceView):
self.cbShowTooltip.SetValue(self.sFit.serviceFittingOptions["showTooltip"] or False)
self.cbMarketShortcuts.SetValue(self.sFit.serviceFittingOptions["showMarketShortcuts"] or False)
self.cbGaugeAnimation.SetValue(self.sFit.serviceFittingOptions["enableGaugeAnimation"])
- self.cbExportCharges.SetValue(self.sFit.serviceFittingOptions["exportCharges"])
self.cbOpenFitInNew.SetValue(self.sFit.serviceFittingOptions["openFitInNew"])
self.chPriceSource.SetStringSelection(self.sFit.serviceFittingOptions["priceSource"])
self.chPriceSystem.SetStringSelection(self.sFit.serviceFittingOptions["priceSystem"])
@@ -136,6 +136,7 @@ class PFGeneralPref(PreferenceView):
self.intDelay.SetValue(self.sFit.serviceFittingOptions["marketSearchDelay"])
self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange)
+ self.cbDefaultCharImplants.Bind(wx.EVT_CHECKBOX, self.OnCBDefaultCharImplantsStateChange)
self.cbGlobalDmgPattern.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalDmgPatternStateChange)
self.cbFitColorSlots.Bind(wx.EVT_CHECKBOX, self.onCBGlobalColorBySlot)
self.cbRackSlots.Bind(wx.EVT_CHECKBOX, self.onCBGlobalRackSlots)
@@ -145,7 +146,6 @@ class PFGeneralPref(PreferenceView):
self.cbShowTooltip.Bind(wx.EVT_CHECKBOX, self.onCBShowTooltip)
self.cbMarketShortcuts.Bind(wx.EVT_CHECKBOX, self.onCBShowShortcuts)
self.cbGaugeAnimation.Bind(wx.EVT_CHECKBOX, self.onCBGaugeAnimation)
- self.cbExportCharges.Bind(wx.EVT_CHECKBOX, self.onCBExportCharges)
self.cbOpenFitInNew.Bind(wx.EVT_CHECKBOX, self.onCBOpenFitInNew)
self.chPriceSource.Bind(wx.EVT_CHOICE, self.onPricesSourceSelection)
self.chPriceSystem.Bind(wx.EVT_CHOICE, self.onPriceSelection)
@@ -187,6 +187,10 @@ class PFGeneralPref(PreferenceView):
self.sFit.serviceFittingOptions["useGlobalCharacter"] = self.cbGlobalChar.GetValue()
event.Skip()
+ def OnCBDefaultCharImplantsStateChange(self, event):
+ self.sFit.serviceFittingOptions["useCharacterImplantsByDefault"] = self.cbDefaultCharImplants.GetValue()
+ event.Skip()
+
def OnCBGlobalDmgPatternStateChange(self, event):
self.sFit.serviceFittingOptions["useGlobalDamagePattern"] = self.cbGlobalDmgPattern.GetValue()
event.Skip()
@@ -210,9 +214,6 @@ class PFGeneralPref(PreferenceView):
def onCBGaugeAnimation(self, event):
self.sFit.serviceFittingOptions["enableGaugeAnimation"] = self.cbGaugeAnimation.GetValue()
- def onCBExportCharges(self, event):
- self.sFit.serviceFittingOptions["exportCharges"] = self.cbExportCharges.GetValue()
-
def onCBOpenFitInNew(self, event):
self.sFit.serviceFittingOptions["openFitInNew"] = self.cbOpenFitInNew.GetValue()
diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py
index 4f9887f8a..526dc0930 100644
--- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py
+++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py
@@ -150,10 +150,10 @@ class PFNetworkPref(PreferenceView):
proxy = self.settings.autodetect()
if proxy is not None:
- addr, port = proxy
- txt = addr + ":" + str(port)
+ addr, port = proxy
+ txt = addr + ":" + str(port)
else:
- txt = "None"
+ txt = "None"
self.stPSAutoDetected.SetLabel("Auto-detected: " + txt)
self.stPSAutoDetected.Disable()
diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py
index 3d782fe5f..c0bd9c07b 100644
--- a/gui/builtinShipBrowser/fitItem.py
+++ b/gui/builtinShipBrowser/fitItem.py
@@ -2,28 +2,28 @@
import re
import time
-import config
import wx
from logbook import Logger
+import config
import gui.builtinShipBrowser.sfBrowserItem as SFItem
-import gui.globalEvents as GE
+import gui.fitCommands as cmd
import gui.mainFrame
import gui.utils.color as colorUtils
import gui.utils.draw as drawUtils
import gui.utils.fonts as fonts
-from .events import ImportSelected, SearchSelected, FitSelected, BoosterListUpdated, Stage3Selected, FitRenamed, FitRemoved
from gui.bitmap_loader import BitmapLoader
from gui.builtinShipBrowser.pfBitmapFrame import PFBitmapFrame
from service.fit import Fit
+from .events import BoosterListUpdated, FitRemoved, FitSelected, ImportSelected, SearchSelected, Stage3Selected
pyfalog = Logger(__name__)
class FitItem(SFItem.SFBrowserItem):
def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0, None), shipID=None,
- itemData=None,
+ itemData=None, graphicID=None,
id=wx.ID_ANY, pos=wx.DefaultPosition,
size=(0, 40), style=0):
@@ -51,7 +51,7 @@ class FitItem(SFItem.SFBrowserItem):
self.deleted = False
if shipID:
- self.shipBmp = BitmapLoader.getBitmap(str(shipID), "renders")
+ self.shipBmp = BitmapLoader.getBitmap(str(graphicID), "renders")
if not self.shipBmp:
self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big", "gui")
@@ -183,18 +183,14 @@ class FitItem(SFItem.SFBrowserItem):
if activeFit:
sFit = Fit.getInstance()
projectedFit = sFit.getFit(self.fitID)
- sFit.project(activeFit, projectedFit)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit))
- self.mainFrame.additionsPane.select("Projected")
+ if self.mainFrame.command.Submit(cmd.GuiAddProjectedCommand(activeFit, projectedFit.ID, 'fit')):
+ self.mainFrame.additionsPane.select("Projected")
def OnAddCommandFit(self, event):
activeFit = self.mainFrame.getActiveFit()
if activeFit:
- sFit = Fit.getInstance()
- commandFit = sFit.getFit(self.fitID)
- sFit.addCommandFit(activeFit, commandFit)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit))
- self.mainFrame.additionsPane.select("Command")
+ if self.mainFrame.command.Submit(cmd.GuiAddCommandCommand(activeFit, self.fitID)):
+ self.mainFrame.additionsPane.select("Command")
def OnMouseCaptureLost(self, event):
""" Destroy drag information (GH issue #479)"""
@@ -325,14 +321,12 @@ class FitItem(SFItem.SFBrowserItem):
self.Refresh()
def renameFit(self, event=None):
- sFit = Fit.getInstance()
self.tcFitName.Show(False)
self.editWasShown = 0
fitName = self.tcFitName.GetValue()
if fitName:
self.fitName = fitName
- sFit.renameFit(self.fitID, self.fitName)
- wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID))
+ self.mainFrame.command.Submit(cmd.GuiFitRenameCommand(self.fitID, self.fitName))
else:
self.tcFitName.SetValue(self.fitName)
diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py
index 16b7a4f24..bb30a040b 100644
--- a/gui/builtinShipBrowser/sfBrowserItem.py
+++ b/gui/builtinShipBrowser/sfBrowserItem.py
@@ -1,6 +1,7 @@
# noinspection PyPackageRequirements
import wx
import gui.utils.draw as drawUtils
+import gui.mainFrame
SB_ITEM_NORMAL = 0
SB_ITEM_SELECTED = 1
@@ -245,6 +246,7 @@ class SFBrowserItem(wx.Window):
self.highlighted = False
self.selected = False
self.bkBitmap = None
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.canBeDragged = False
@@ -311,6 +313,10 @@ class SFBrowserItem(wx.Window):
self.canBeDragged = mode
def OnLeftUp(self, event):
+ if self.mainFrame.supress_left_up:
+ wx.Yield()
+ self.mainFrame.supress_left_up = False
+ return
if self.HasCapture():
self.ReleaseMouse()
diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py
index 4c679fb8a..cf1de024c 100644
--- a/gui/builtinShipBrowser/shipItem.py
+++ b/gui/builtinShipBrowser/shipItem.py
@@ -8,17 +8,17 @@ import gui.mainFrame
import gui.utils.color as colorUtils
import gui.utils.draw as drawUtils
import gui.utils.fonts as fonts
-from .events import Stage3Selected, Stage2Selected, Stage1Selected, FitSelected
from gui.bitmap_loader import BitmapLoader
from gui.contextMenu import ContextMenu
from service.fit import Fit
from service.market import Market
+from .events import FitSelected, Stage3Selected
pyfalog = Logger(__name__)
class ShipItem(SFItem.SFBrowserItem):
- def __init__(self, parent, shipID=None, shipFittingInfo=("Test", "TestTrait", 2), itemData=None,
+ def __init__(self, parent, shipID=None, shipFittingInfo=("Test", "TestTrait", 2), itemData=None, graphicID=None,
id=wx.ID_ANY, pos=wx.DefaultPosition,
size=(0, 40), style=0):
SFItem.SFBrowserItem.__init__(self, parent, size=size)
@@ -36,8 +36,8 @@ class ShipItem(SFItem.SFBrowserItem):
self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL)
self.shipBmp = None
- if shipID:
- self.shipBmp = BitmapLoader.getBitmap(str(shipID), "renders")
+ if graphicID:
+ self.shipBmp = BitmapLoader.getBitmap(str(graphicID), "renders")
if not self.shipBmp:
self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big", "gui")
diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py
index 98d54d714..4c773c9f1 100644
--- a/gui/builtinStatsViews/firepowerViewFull.py
+++ b/gui/builtinStatsViews/firepowerViewFull.py
@@ -22,7 +22,8 @@ import wx
import gui.mainFrame
from gui.statsView import StatsView
from gui.bitmap_loader import BitmapLoader
-from gui.utils.numberFormatter import formatAmount
+from gui.utils.numberFormatter import formatAmount, roundToPrec
+from eos.utils.spoolSupport import SpoolType, SpoolOptions
from service.fit import Fit
@@ -129,7 +130,7 @@ class FirepowerViewFull(StatsView):
# Remove effective label
hsizer = self.headerPanel.GetSizer()
hsizer.Hide(self.stEff)
- #self.stEff.Destroy()
+ # self.stEff.Destroy()
# Get the new view
view = StatsView.getView("miningyieldViewFull")(self.parent)
@@ -148,27 +149,55 @@ class FirepowerViewFull(StatsView):
else:
self.stEff.Hide()
- stats = (("labelFullDpsWeapon", lambda: fit.weaponDPS, 3, 0, 0, "%s DPS", None),
- ("labelFullDpsDrone", lambda: fit.droneDPS, 3, 0, 0, "%s DPS", None),
- ("labelFullVolleyTotal", lambda: fit.totalVolley, 3, 0, 0, "%s", "Volley: %.1f"),
- ("labelFullDpsTotal", lambda: fit.totalDPS, 3, 0, 0, "%s", None))
- # See GH issue #
- # if fit is not None and fit.totalYield > 0:
- # self.miningyield.Show()
- # else:
- # self.miningyield.Hide()
+ def dpsToolTip(preSpool, fullSpool, prec, lowest, highest):
+ if roundToPrec(preSpool, prec) == roundToPrec(fullSpool, prec):
+ return ""
+ else:
+ return "Spool up: {}-{}".format(
+ formatAmount(preSpool, prec, lowest, highest),
+ formatAmount(fullSpool, prec, lowest, highest))
+
+ # TODO: fetch spoolup option
+ defaultSpoolValue = 1
+ stats = (
+ (
+ "labelFullDpsWeapon",
+ lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)).total,
+ lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).total,
+ lambda: fit.getWeaponDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).total,
+ 3, 0, 0, "{}{} DPS"),
+ (
+ "labelFullDpsDrone",
+ lambda: fit.getDroneDps().total,
+ lambda: fit.getDroneDps().total,
+ lambda: fit.getDroneDps().total,
+ 3, 0, 0, "{}{} DPS"),
+ (
+ "labelFullVolleyTotal",
+ lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)).total,
+ lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).total,
+ lambda: fit.getTotalVolley(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).total,
+ 3, 0, 0, "{}{}"),
+ (
+ "labelFullDpsTotal",
+ lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)).total,
+ lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).total,
+ lambda: fit.getTotalDps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).total,
+ 3, 0, 0, "{}{}"))
counter = 0
- for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
+ for labelName, val, preSpoolVal, fullSpoolVal, prec, lowest, highest, valueFormat in stats:
label = getattr(self, labelName)
- value = value() if fit is not None else 0
- value = value if value is not None else 0
- if self._cachedValues[counter] != value:
- valueStr = formatAmount(value, prec, lowest, highest)
- label.SetLabel(valueFormat % valueStr)
- tipStr = valueFormat % valueStr if altFormat is None else altFormat % value
- label.SetToolTip(wx.ToolTip(tipStr))
- self._cachedValues[counter] = value
+ val = val() if fit is not None else 0
+ preSpoolVal = preSpoolVal() if fit is not None else 0
+ fullSpoolVal = fullSpoolVal() if fit is not None else 0
+ if self._cachedValues[counter] != val:
+ tooltipText = dpsToolTip(preSpoolVal, fullSpoolVal, prec, lowest, highest)
+ label.SetLabel(valueFormat.format(
+ formatAmount(val, prec, lowest, highest),
+ "\u02e2" if tooltipText else ""))
+ label.SetToolTip(wx.ToolTip(tooltipText))
+ self._cachedValues[counter] = val
counter += 1
self.panel.Layout()
diff --git a/gui/builtinStatsViews/outgoingViewFull.py b/gui/builtinStatsViews/outgoingViewFull.py
index be24ee6af..3b6a6ce58 100644
--- a/gui/builtinStatsViews/outgoingViewFull.py
+++ b/gui/builtinStatsViews/outgoingViewFull.py
@@ -21,7 +21,35 @@
import wx
from gui.statsView import StatsView
from gui.bitmap_loader import BitmapLoader
-from gui.utils.numberFormatter import formatAmount
+from gui.utils.numberFormatter import formatAmount, roundToPrec
+from eos.utils.spoolSupport import SpoolType, SpoolOptions
+
+
+stats = [
+ (
+ "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Capacitor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Capacitor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Capacitor", 0),
+ 3, 0, 0),
+ (
+ "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Shield", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Shield", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Shield", 0),
+ 3, 0, 0),
+ (
+ "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Armor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Armor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Armor", 0),
+ 3, 0, 0),
+ (
+ "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", "Hull restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Hull", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Hull", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Hull", 0),
+ 3, 0, 0)]
class OutgoingViewFull(StatsView):
@@ -48,56 +76,46 @@ class OutgoingViewFull(StatsView):
contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0)
- counter = 0
-
- rr_list = [
- ("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."),
- ("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."),
- ("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."),
- ("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."),
- ]
-
- for outgoingType, label, image, tooltip in rr_list:
+ for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats:
baseBox = wx.BoxSizer(wx.VERTICAL)
baseBox.Add(BitmapLoader.getStaticBitmap("%s_big" % image, parent, "gui"), 0, wx.ALIGN_CENTER)
- if "Capacitor" in outgoingType:
- lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s")
- else:
- lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s")
-
+ lbl = wx.StaticText(parent, wx.ID_ANY, valueFormat.format(0, ""))
lbl.SetToolTip(wx.ToolTip(tooltip))
-
- setattr(self, "label%s" % outgoingType, lbl)
+ setattr(self, labelName, lbl)
baseBox.Add(lbl, 0, wx.ALIGN_CENTER)
self._cachedValues.append(0)
- counter += 1
sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
- # If we did anything intresting, we'd update our labels to reflect the new fit's stats here
- stats = [
- ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None),
- ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None),
- ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None),
- ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None),
- ]
+ def formatTooltip(text, preSpool, fullSpool, prec, lowest, highest):
+ if roundToPrec(preSpool, prec) == roundToPrec(fullSpool, prec):
+ return False, text
+ else:
+ return True, "{}\nSpool up: {}-{}".format(
+ text,
+ formatAmount(preSpool, prec, lowest, highest),
+ formatAmount(fullSpool, prec, lowest, highest))
+ # TODO: fetch spoolup option
+ defaultSpoolValue = 1
counter = 0
- for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
+ for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats:
label = getattr(self, labelName)
- value = value() if fit is not None else 0
- value = value if value is not None else 0
- if self._cachedValues[counter] != value:
- valueStr = formatAmount(value, prec, lowest, highest)
- label.SetLabel(valueFormat % valueStr)
- tipStr = valueFormat % valueStr if altFormat is None else altFormat % value
- label.SetToolTip(wx.ToolTip(tipStr))
- self._cachedValues[counter] = value
+ val = val(fit, defaultSpoolValue) if fit is not None else 0
+ preSpoolVal = preSpoolVal(fit) if fit is not None else 0
+ fullSpoolVal = fullSpoolVal(fit) if fit is not None else 0
+ if self._cachedValues[counter] != val:
+ hasSpool, tooltipText = formatTooltip(tooltip, preSpoolVal, fullSpoolVal, prec, lowest, highest)
+ label.SetLabel(valueFormat.format(
+ formatAmount(val, prec, lowest, highest),
+ "\u02e2" if hasSpool else ""))
+ label.SetToolTip(wx.ToolTip(tooltipText))
+ self._cachedValues[counter] = val
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
diff --git a/gui/builtinStatsViews/outgoingViewMinimal.py b/gui/builtinStatsViews/outgoingViewMinimal.py
index 21fe9bede..055a65e9e 100644
--- a/gui/builtinStatsViews/outgoingViewMinimal.py
+++ b/gui/builtinStatsViews/outgoingViewMinimal.py
@@ -20,7 +20,35 @@
# noinspection PyPackageRequirements
import wx
from gui.statsView import StatsView
-from gui.utils.numberFormatter import formatAmount
+from gui.utils.numberFormatter import formatAmount, roundToPrec
+from eos.utils.spoolSupport import SpoolType, SpoolOptions
+
+
+stats = [
+ (
+ "labelRemoteCapacitor", "Capacitor:", "{}{} GJ/s", "capacitorInfo", "Capacitor restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Capacitor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Capacitor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Capacitor", 0),
+ 3, 0, 0),
+ (
+ "labelRemoteShield", "Shield:", "{}{} HP/s", "shieldActive", "Shield restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Shield", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Shield", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Shield", 0),
+ 3, 0, 0),
+ (
+ "labelRemoteArmor", "Armor:", "{}{} HP/s", "armorActive", "Armor restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Armor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Armor", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Armor", 0),
+ 3, 0, 0),
+ (
+ "labelRemoteHull", "Hull:", "{}{} HP/s", "hullActive", "Hull restored",
+ lambda fit, spool: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, spool, False)).get("Hull", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 0, True)).get("Hull", 0),
+ lambda fit: fit.getRemoteReps(spoolOptions=SpoolOptions(SpoolType.SCALE, 1, True)).get("Hull", 0),
+ 3, 0, 0)]
class OutgoingViewMinimal(StatsView):
@@ -47,56 +75,46 @@ class OutgoingViewMinimal(StatsView):
contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0)
- counter = 0
-
- rr_list = [
- ("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."),
- ("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."),
- ("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."),
- ("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."),
- ]
-
- for outgoingType, label, image, tooltip in rr_list:
+ for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats:
baseBox = wx.BoxSizer(wx.VERTICAL)
- baseBox.Add(wx.StaticText(contentPanel, wx.ID_ANY, label), 0, wx.ALIGN_CENTER)
-
- if "Capacitor" in outgoingType:
- lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s")
- else:
- lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s")
+ baseBox.Add(wx.StaticText(contentPanel, wx.ID_ANY, labelDesc), 0, wx.ALIGN_CENTER)
+ lbl = wx.StaticText(parent, wx.ID_ANY, valueFormat.format(0, ""))
lbl.SetToolTip(wx.ToolTip(tooltip))
-
- setattr(self, "label%s" % outgoingType, lbl)
+ setattr(self, labelName, lbl)
baseBox.Add(lbl, 0, wx.ALIGN_CENTER)
self._cachedValues.append(0)
- counter += 1
sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
- # If we did anything intresting, we'd update our labels to reflect the new fit's stats here
- stats = [
- ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None),
- ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None),
- ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None),
- ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None),
- ]
+ def formatTooltip(text, preSpool, fullSpool, prec, lowest, highest):
+ if roundToPrec(preSpool, prec) == roundToPrec(fullSpool, prec):
+ return False, text
+ else:
+ return True, "{}\nSpool up: {}-{}".format(
+ text,
+ formatAmount(preSpool, prec, lowest, highest),
+ formatAmount(fullSpool, prec, lowest, highest))
+ # TODO: fetch spoolup option
+ defaultSpoolValue = 1
counter = 0
- for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
+ for labelName, labelDesc, valueFormat, image, tooltip, val, preSpoolVal, fullSpoolVal, prec, lowest, highest in stats:
label = getattr(self, labelName)
- value = value() if fit is not None else 0
- value = value if value is not None else 0
- if self._cachedValues[counter] != value:
- valueStr = formatAmount(value, prec, lowest, highest)
- label.SetLabel(valueFormat % valueStr)
- tipStr = valueFormat % valueStr if altFormat is None else altFormat % value
- label.SetToolTip(wx.ToolTip(tipStr))
- self._cachedValues[counter] = value
+ val = val(fit, defaultSpoolValue) if fit is not None else 0
+ preSpoolVal = preSpoolVal(fit) if fit is not None else 0
+ fullSpoolVal = fullSpoolVal(fit) if fit is not None else 0
+ if self._cachedValues[counter] != val:
+ hasSpool, tooltipText = formatTooltip(tooltip, preSpoolVal, fullSpoolVal, prec, lowest, highest)
+ label.SetLabel(valueFormat.format(
+ formatAmount(val, prec, lowest, highest),
+ "\u02e2" if hasSpool else ""))
+ label.SetToolTip(wx.ToolTip(tooltipText))
+ self._cachedValues[counter] = val
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py
index 9eb03ea22..767ad958f 100644
--- a/gui/builtinStatsViews/priceViewFull.py
+++ b/gui/builtinStatsViews/priceViewFull.py
@@ -129,15 +129,15 @@ class PriceViewFull(StatsView):
total_price = 0
- if (self.settings.get("ship")):
+ if self.settings.get("ship"):
total_price += ship_price
- if (self.settings.get("modules")):
+ if self.settings.get("modules"):
total_price += module_price
- if (self.settings.get("drones")):
+ if self.settings.get("drones"):
total_price += drone_price + fighter_price
- if (self.settings.get("cargo")):
+ if self.settings.get("cargo"):
total_price += cargo_price
- if (self.settings.get("character")):
+ if self.settings.get("character"):
total_price += booster_price + implant_price
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
diff --git a/gui/builtinStatsViews/priceViewMinimal.py b/gui/builtinStatsViews/priceViewMinimal.py
index ec1d5357f..8d6a5ce83 100644
--- a/gui/builtinStatsViews/priceViewMinimal.py
+++ b/gui/builtinStatsViews/priceViewMinimal.py
@@ -125,15 +125,15 @@ class PriceViewMinimal(StatsView):
total_price = 0
- if (self.settings.get("ship")):
+ if self.settings.get("ship"):
total_price += ship_price
- if (self.settings.get("modules")):
+ if self.settings.get("modules"):
total_price += module_price
- if (self.settings.get("drones")):
+ if self.settings.get("drones"):
total_price += drone_price + fighter_price
- if (self.settings.get("cargo")):
+ if self.settings.get("cargo"):
total_price += cargo_price
- if (self.settings.get("character")):
+ if self.settings.get("character"):
total_price += booster_price + implant_price
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py
index eadcf537f..ccc89f79f 100644
--- a/gui/builtinStatsViews/rechargeViewFull.py
+++ b/gui/builtinStatsViews/rechargeViewFull.py
@@ -63,15 +63,20 @@ class RechargeViewFull(StatsView):
# Add an empty label first for correct alignment.
sizerTankStats.Add(wx.StaticText(contentPanel, wx.ID_ANY, ""), 0)
- toolTipText = {"shieldPassive": "Passive shield recharge", "shieldActive": "Active shield boost",
- "armorActive": "Armor repair amount", "hullActive": "Hull repair amount"}
+ toolTipText = {
+ "shieldPassive": "Passive shield recharge",
+ "shieldActive": "Active shield boost",
+ "armorActive": "Armor repair amount",
+ "hullActive": "Hull repair amount"}
for tankType in ("shieldPassive", "shieldActive", "armorActive", "hullActive"):
bitmap = BitmapLoader.getStaticBitmap("%s_big" % tankType, contentPanel, "gui")
tooltip = wx.ToolTip(toolTipText[tankType])
bitmap.SetToolTip(tooltip)
sizerTankStats.Add(bitmap, 0, wx.ALIGN_CENTER)
- toolTipText = {"reinforced": "Reinforced", "sustained": "Sustained"}
+ toolTipText = {
+ "reinforced": "Reinforced",
+ "sustained": "Sustained"}
for stability in ("reinforced", "sustained"):
bitmap = BitmapLoader.getStaticBitmap("regen%s_big" % stability.capitalize(), contentPanel, "gui")
tooltip = wx.ToolTip(toolTipText[stability])
@@ -85,7 +90,6 @@ class RechargeViewFull(StatsView):
tankTypeCap = tankType[0].capitalize() + tankType[1:]
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.0", style=wx.ALIGN_RIGHT)
setattr(self, "labelTank%s%s" % (stability.capitalize(), tankTypeCap), lbl)
-
box = wx.BoxSizer(wx.HORIZONTAL)
box.Add(lbl, 0, wx.EXPAND)
@@ -115,9 +119,23 @@ class RechargeViewFull(StatsView):
unitlbl = getattr(self, "unitLabelTank%s%sActive" % (stability.capitalize(), name.capitalize()))
unitlbl.SetLabel(unit)
if tank is not None:
- lbl.SetLabel("%.1f" % tank["%sRepair" % name])
+ amount = tank["{}Repair".format(name)]
else:
- lbl.SetLabel("0.0")
+ amount = 0
+
+ if tank is not None and name == "armor":
+ preSpoolAmount = tank["armorRepairPreSpool"]
+ fullSpoolAmount = tank["armorRepairFullSpool"]
+ if round(preSpoolAmount, 1) != round(fullSpoolAmount, 1):
+ ttText = "Spool up: {:.1f}-{:.1f}".format(preSpoolAmount, fullSpoolAmount)
+ else:
+ ttText = ""
+ else:
+ ttText = ""
+
+ lbl.SetLabel("{:.1f}{}".format(amount, "\u02e2" if ttText else ""))
+ lbl.SetToolTip(wx.ToolTip(ttText))
+ unitlbl.SetToolTip(wx.ToolTip(ttText))
if fit is not None:
label = getattr(self, "labelTankSustainedShieldPassive")
diff --git a/gui/builtinViewColumns/ammoIcon.py b/gui/builtinViewColumns/ammoIcon.py
index 7459db6a4..647d235a0 100644
--- a/gui/builtinViewColumns/ammoIcon.py
+++ b/gui/builtinViewColumns/ammoIcon.py
@@ -43,7 +43,7 @@ class AmmoIcon(ViewColumn):
if stuff.charge is None:
return -1
else:
- iconFile = stuff.charge.icon.iconFile if stuff.charge.icon else ""
+ iconFile = stuff.charge.iconID if stuff.charge.iconID else ""
if iconFile:
return self.fittingView.imageList.GetImageIndex(iconFile, "icons")
else:
diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py
index 7ad8743d7..b36cf7132 100644
--- a/gui/builtinViewColumns/attributeDisplay.py
+++ b/gui/builtinViewColumns/attributeDisplay.py
@@ -41,7 +41,7 @@ class AttributeDisplay(ViewColumn):
iconFile = "pg_small"
iconType = "gui"
else:
- iconFile = info.icon.iconFile if info.icon else None
+ iconFile = info.iconID
iconType = "icons"
if iconFile:
self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType)
diff --git a/gui/builtinViewColumns/baseIcon.py b/gui/builtinViewColumns/baseIcon.py
index 8c09a9fa3..207d639c5 100644
--- a/gui/builtinViewColumns/baseIcon.py
+++ b/gui/builtinViewColumns/baseIcon.py
@@ -35,10 +35,10 @@ class BaseIcon(ViewColumn):
return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(),
"gui")
else:
- return self.loadIconFile(stuff.item.icon.iconFile if stuff.item.icon else "")
+ return self.loadIconFile(stuff.item.iconID or "")
item = getattr(stuff, "item", stuff)
- return self.loadIconFile(item.icon.iconFile if item.icon else "")
+ return self.loadIconFile(item.iconID)
def loadIconFile(self, iconFile):
if iconFile:
diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py
index 8ed1466ac..368dd74b6 100644
--- a/gui/builtinViewColumns/baseName.py
+++ b/gui/builtinViewColumns/baseName.py
@@ -28,7 +28,9 @@ from eos.saveddata.fighter import Fighter
from eos.saveddata.module import Module, Slot, Rack
from eos.saveddata.fit import Fit
from service.fit import Fit as FitSvc
+from service.market import Market
from gui.viewColumn import ViewColumn
+from gui.builtinContextMenus.whProjector import WhProjector
import gui.mainFrame
pyfalog = Logger(__name__)
@@ -73,10 +75,19 @@ class BaseName(ViewColumn):
if stuff.slot == Slot.MODE:
return '─ Tactical Mode ─'
else:
- return '─ {} Slots ─'.format(Slot.getName(stuff.slot).capitalize())
+ return '─ {} {} Slot{}─'.format(stuff.num, Slot.getName(stuff.slot).capitalize(), '' if stuff.num == 1 else 's')
else:
return ""
elif isinstance(stuff, Module):
+ if self.projectedView:
+ # check for projected abyssal name
+ name_check = stuff.item.name[0:-2]
+ type = WhProjector.abyssal_mapping.get(name_check, None)
+ if type:
+ sMkt = Market.getInstance()
+ type = sMkt.getItem(type)
+ return "{} {}".format(type.name, stuff.item.name[-1:])
+
if stuff.isEmpty:
return "%s Slot" % Slot.getName(stuff.slot).capitalize()
else:
diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py
index ef420c344..24f72b3a1 100644
--- a/gui/builtinViewColumns/maxRange.py
+++ b/gui/builtinViewColumns/maxRange.py
@@ -40,7 +40,7 @@ class MaxRange(ViewColumn):
info = sAttr.getAttributeInfo("maxRange")
self.info = info
if params["showIcon"]:
- iconFile = info.icon.iconFile if info.icon else None
+ iconFile = info.iconID
if iconFile:
self.imageId = fittingView.imageList.GetImageIndex(iconFile, "icons")
self.bitmap = BitmapLoader.getBitmap(iconFile, "icons")
@@ -60,7 +60,7 @@ class MaxRange(ViewColumn):
maxRange = stuff.maxRange if hasattr(stuff, "maxRange") else stuff.getModifiedItemAttr("maxRange", None)
falloff = stuff.falloff
- if falloff:
+ if falloff and falloff >= 5:
falloff = "+%sm" % formatAmount(falloff, 3, 0, 3)
else:
falloff = ""
diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py
index 668979c1f..e9bff3d56 100644
--- a/gui/builtinViewColumns/misc.py
+++ b/gui/builtinViewColumns/misc.py
@@ -27,7 +27,7 @@ from gui.viewColumn import ViewColumn
from gui.bitmap_loader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
from gui.utils.listFormatter import formatList
-from eos.saveddata.drone import Drone
+from eos.utils.spoolSupport import SpoolType, SpoolOptions
class Miscellanea(ViewColumn):
@@ -93,7 +93,9 @@ class Miscellanea(ViewColumn):
text = ""
tooltip = ""
elif max(doomsday_duration / doomsday_dottime, 1) > 1:
- text = "{0} dmg over {1} s".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3), doomsday_duration / 1000)
+ text = "{} over {}s".format(
+ formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 6),
+ formatAmount((doomsday_duration / 1000), 0, 0, 0))
tooltip = "Raw damage done over time"
else:
text = "{0} dmg".format(formatAmount(volley * (doomsday_duration / doomsday_dottime), 3, 0, 3))
@@ -108,6 +110,25 @@ class Miscellanea(ViewColumn):
text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3))
tooltip = "Tracking speed"
return text, tooltip
+ elif itemGroup == "Precursor Weapon":
+ info = []
+ trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed")
+ if trackingSpeed:
+ text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3))
+ tooltip = "tracking speed"
+ info.append((text, tooltip))
+ # TODO: fetch spoolup option
+ defaultSpoolValue = 1
+ spoolTime = stuff.getSpoolData(spoolOptions=SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False))[1]
+ if spoolTime:
+ text = "{0}s".format(formatAmount(spoolTime, 3, 0, 3))
+ tooltip = "spool up time"
+ info.append((text, tooltip))
+ if not info:
+ return "", None
+ text = ' | '.join(i[0] for i in info)
+ tooltip = ' and '.join(i[1] for i in info).capitalize()
+ return text, tooltip
elif itemCategory == "Subsystem":
slots = ("hi", "med", "low")
info = []
@@ -311,39 +332,61 @@ class Miscellanea(ViewColumn):
tooltip = "Sensor recalibration time"
return text, tooltip
elif itemGroup == "Remote Armor Repairer":
- repAmount = stuff.getModifiedItemAttr("armorDamageAmount")
- cycleTime = stuff.getModifiedItemAttr("duration")
- if not repAmount or not cycleTime:
+ rps = stuff.getRemoteReps(ignoreState=True)[1]
+ if not rps:
return "", None
- repPerSec = float(repAmount) * 1000 / cycleTime
- text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True))
+ text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True))
tooltip = "Armor repaired per second"
return text, tooltip
- elif itemGroup == "Remote Shield Booster":
- repAmount = stuff.getModifiedItemAttr("shieldBonus")
- cycleTime = stuff.cycleTime
- if not repAmount or not cycleTime:
+ elif itemGroup == "Mutadaptive Remote Armor Repairer":
+ # TODO: fetch spoolup option
+ defaultSpoolValue = 1
+ spoolOptDefault = SpoolOptions(SpoolType.SCALE, defaultSpoolValue, False)
+ spoolOptPre = SpoolOptions(SpoolType.SCALE, 0, True)
+ spoolOptFull = SpoolOptions(SpoolType.SCALE, 1, True)
+ rrType, rps = stuff.getRemoteReps(spoolOptions=spoolOptDefault, ignoreState=True)
+ rrTypePre, rpsPre = stuff.getRemoteReps(spoolOptions=spoolOptPre, ignoreState=True)
+ rrTypeFull, rpsFull = stuff.getRemoteReps(spoolOptions=spoolOptFull, ignoreState=True)
+ if not rps:
return "", None
- repPerSec = float(repAmount) * 1000 / cycleTime
- text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True))
+ text = []
+ tooltip = []
+ text.append("{}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True)))
+ tooltip.append("Armor repaired per second")
+ spoolTime = stuff.getSpoolData(spoolOptDefault)[1]
+ if spoolTime:
+ text.append("{}s".format(formatAmount(spoolTime, 3, 0, 3)))
+ tooltip.append("spool up time")
+ text = " | ".join(text)
+ tooltip = " and ".join(tooltip)
+ spoolTimePre = stuff.getSpoolData(spoolOptPre)[1]
+ spoolTimeFull = stuff.getSpoolData(spoolOptFull)[1]
+ if spoolTimePre != spoolTimeFull:
+ tooltip = "{}\nSpool up: {}-{} over {}s".format(
+ tooltip,
+ formatAmount(rpsPre, 3, 0, 3),
+ formatAmount(rpsFull, 3, 0, 3),
+ formatAmount(spoolTimeFull - spoolTimePre, 3, 0, 3))
+ return text, tooltip
+ elif itemGroup == "Remote Shield Booster":
+ rps = stuff.getRemoteReps(ignoreState=True)[1]
+ if not rps:
+ return "", None
+ text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True))
tooltip = "Shield transferred per second"
return text, tooltip
elif itemGroup == "Remote Capacitor Transmitter":
- repAmount = stuff.getModifiedItemAttr("powerTransferAmount")
- cycleTime = stuff.cycleTime
- if not repAmount or not cycleTime:
+ rps = stuff.getRemoteReps(ignoreState=True)[1]
+ if not rps:
return "", None
- repPerSec = float(repAmount) * 1000 / cycleTime
- text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True))
+ text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True))
tooltip = "Energy transferred per second"
return text, tooltip
elif itemGroup == "Remote Hull Repairer":
- repAmount = stuff.getModifiedItemAttr("structureDamageAmount")
- cycleTime = stuff.cycleTime
- if not repAmount or not cycleTime:
+ rps = stuff.getRemoteReps(ignoreState=True)[1]
+ if not rps:
return "", None
- repPerSec = float(repAmount) * 1000 / cycleTime
- text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True))
+ text = "{0}/s".format(formatAmount(rps, 3, 0, 3, forceSign=True))
tooltip = "Structure repaired per second"
return text, tooltip
elif itemGroup == "Gang Coordinator":
@@ -441,28 +484,11 @@ class Miscellanea(ViewColumn):
tooltip = "Mining Yield per second ({0} per hour)".format(formatAmount(minePerSec * 3600, 3, 0, 3))
return text, tooltip
elif itemGroup == "Logistic Drone":
- armorAmount = stuff.getModifiedItemAttr("armorDamageAmount")
- shieldAmount = stuff.getModifiedItemAttr("shieldBonus")
- hullAmount = stuff.getModifiedItemAttr("structureDamageAmount")
- repAmount = armorAmount or shieldAmount or hullAmount
- cycleTime = stuff.getModifiedItemAttr("duration")
- if not repAmount or not cycleTime:
+ repType, rps = stuff.getRemoteReps(ignoreState=True)
+ if not repType:
return "", None
- repPerSecPerDrone = repPerSec = float(repAmount) * 1000 / cycleTime
-
- if isinstance(stuff, Drone):
- repPerSec *= stuff.amount
-
- text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3))
- ttEntries = []
- if hullAmount is not None and repAmount == hullAmount:
- ttEntries.append("structure")
- if armorAmount is not None and repAmount == armorAmount:
- ttEntries.append("armor")
- if shieldAmount is not None and repAmount == shieldAmount:
- ttEntries.append("shield")
-
- tooltip = "{0} HP repaired per second\n{1} HP/s per drone".format(formatList(ttEntries).capitalize(), repPerSecPerDrone)
+ text = "{}/s".format(formatAmount(rps, 3, 0, 3))
+ tooltip = "{} HP repaired per second\n{} HP/s per drone".format(repType, formatAmount(rps / stuff.amount, 3, 0, 3))
return text, tooltip
elif itemGroup == "Energy Neutralizer Drone":
neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount")
@@ -478,7 +504,7 @@ class Miscellanea(ViewColumn):
text = "{0}s".format(cycleTime)
tooltip = "Spoolup time"
return text, tooltip
- elif itemGroup in ("Siege Module", "Cynosural Field"):
+ elif itemGroup in ("Siege Module", "Cynosural Field Generator"):
amt = stuff.getModifiedItemAttr("consumptionQuantity")
if amt:
typeID = stuff.getModifiedItemAttr("consumptionType")
diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py
index 728376bef..56901c172 100644
--- a/gui/builtinViewColumns/price.py
+++ b/gui/builtinViewColumns/price.py
@@ -22,6 +22,7 @@ import wx
from eos.saveddata.cargo import Cargo
from eos.saveddata.drone import Drone
+from eos.saveddata.price import PriceStatus
from service.price import Price as ServicePrice
from gui.viewColumn import ViewColumn
from gui.bitmap_loader import BitmapLoader
@@ -45,9 +46,15 @@ class Price(ViewColumn):
if stuff.isEmpty:
return ""
- price = stuff.item.price.price
+ priceObj = stuff.item.price
- if not price:
+ if not priceObj.isValid:
+ return False
+
+ # Fetch actual price as float to not modify its value on Price object
+ price = priceObj.price
+
+ if price == 0:
return ""
if isinstance(stuff, Drone) or isinstance(stuff, Cargo):
@@ -59,11 +66,13 @@ class Price(ViewColumn):
sPrice = ServicePrice.getInstance()
def callback(item):
- price = item.item.price
- text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else ""
- if price.failed:
- text += " (!)"
- colItem.SetText(text)
+ price = item[0]
+ textItems = []
+ if price.price:
+ textItems.append(formatAmount(price.price, 3, 3, 9, currency=True))
+ if price.status == PriceStatus.fail:
+ textItems.append("(!)")
+ colItem.SetText(" ".join(textItems))
display.SetItem(colItem)
diff --git a/gui/builtinViewColumns/propertyDisplay.py b/gui/builtinViewColumns/propertyDisplay.py
index b4faa177a..abd91f730 100644
--- a/gui/builtinViewColumns/propertyDisplay.py
+++ b/gui/builtinViewColumns/propertyDisplay.py
@@ -41,7 +41,7 @@ class PropertyDisplay(ViewColumn):
iconFile = "pg_small"
iconType = "gui"
else:
- iconFile = info.icon.iconFile if info.icon else None
+ iconFile = info.iconID if info.icon else None
iconType = "icons"
if iconFile:
self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType)
diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py
index 7e4eccb03..a0aea6043 100644
--- a/gui/builtinViews/fittingView.py
+++ b/gui/builtinViews/fittingView.py
@@ -21,27 +21,26 @@
import wx
# noinspection PyPackageRequirements
import wx.lib.newevent
-import gui.mainFrame
-from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED
+from logbook import Logger
+
+import gui.builtinViews.emptyView
import gui.display as d
-from gui.contextMenu import ContextMenu
-from gui.builtinShipBrowser.events import EVT_FIT_RENAMED, EVT_FIT_REMOVED, FitSelected, EVT_FIT_SELECTED
+import gui.fitCommands as cmd
+import gui.globalEvents as GE
+import gui.mainFrame
import gui.multiSwitch
from eos.saveddata.mode import Mode
-from eos.saveddata.module import Module, Slot, Rack
-from gui.builtinViewColumns.state import State
+from eos.saveddata.module import Module, Rack, Slot
from gui.bitmap_loader import BitmapLoader
-import gui.builtinViews.emptyView
-from logbook import Logger
+from gui.builtinMarketBrowser.events import ITEM_SELECTED
+from gui.builtinShipBrowser.events import EVT_FIT_REMOVED, EVT_FIT_RENAMED, EVT_FIT_SELECTED, FitSelected
+from gui.builtinViewColumns.state import State
from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED
-
+from gui.contextMenu import ContextMenu
+from gui.utils.staticHelpers import DragDropHelper
from service.fit import Fit
from service.market import Market
-from gui.utils.staticHelpers import DragDropHelper
-
-import gui.globalEvents as GE
-
pyfalog = Logger(__name__)
@@ -88,7 +87,7 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
def handleDrag(self, type, fitID):
if type == "fit":
- for page in self.multiSwitch.pages:
+ for page in self.multiSwitch._pages:
if isinstance(page, FittingView) and page.activeFitID == fitID:
index = self.multiSwitch.GetPageIndex(page)
self.multiSwitch.SetSelection(index)
@@ -148,6 +147,7 @@ class FittingView(d.Display):
self.mainFrame.Bind(EVT_FIT_RENAMED, self.fitRenamed)
self.mainFrame.Bind(EVT_FIT_REMOVED, self.fitRemoved)
self.mainFrame.Bind(ITEM_SELECTED, self.appendItem)
+ self.font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
@@ -222,12 +222,15 @@ class FittingView(d.Display):
wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID))
def Destroy(self):
+ # @todo: when wxPython 4.0.2 is release, https://github.com/pyfa-org/Pyfa/issues/1586#issuecomment-390074915
+ # Make sure to remove the shitty checks that I have to put in place for these handlers to ignore when self is None
print("+++++ Destroy " + repr(self))
- print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED))
- print(self.mainFrame.Unbind(GE.FIT_CHANGED))
- print(self.mainFrame.Unbind(EVT_FIT_RENAMED))
- print(self.mainFrame.Unbind(EVT_FIT_REMOVED))
- print(self.mainFrame.Unbind(ITEM_SELECTED))
+
+ # print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED))
+ # print(self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.fitChanged))
+ # print(self.mainFrame.Unbind(EVT_FIT_RENAMED, handler=self.fitRenamed ))
+ # print(self.mainFrame.Unbind(EVT_FIT_REMOVED, handler=self.fitRemoved))
+ # print(self.mainFrame.Unbind(ITEM_SELECTED, handler=self.appendItem))
d.Display.Destroy(self)
@@ -260,7 +263,9 @@ class FittingView(d.Display):
sel = []
row = self.GetFirstSelected()
while row != -1:
- sel.append(self.mods[self.GetItemData(row)])
+ mod = self.mods[self.GetItemData(row)]
+ if mod and not isinstance(mod, Rack):
+ sel.append(mod)
row = self.GetNextSelected(row)
return sel
@@ -291,6 +296,9 @@ class FittingView(d.Display):
"""
print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
pyfalog.debug("FittingView::fitRemoved")
+ if not self:
+ event.Skip()
+ return
if event.fitID == self.getActiveFit():
pyfalog.debug(" Deleted fit is currently active")
self.parent.DeletePage(self.parent.GetPageIndex(self))
@@ -311,6 +319,9 @@ class FittingView(d.Display):
event.Skip()
def fitRenamed(self, event):
+ if not self:
+ event.Skip()
+ return
fitID = event.fitID
if fitID == self.getActiveFit():
self.updateTab()
@@ -347,12 +358,19 @@ class FittingView(d.Display):
self.parent.SetPageTextIcon(pageIndex, text, bitmap)
def appendItem(self, event):
+ """
+ Adds items that are double clicks from the market browser. We handle both modules and ammo
+ """
+ if not self:
+ event.Skip()
+ return
if self.parent.IsActive(self):
itemID = event.itemID
fitID = self.activeFitID
if fitID is not None:
sFit = Fit.getInstance()
if sFit.isAmmo(itemID):
+ # If we've selected ammo, then apply to the selected module(s)
modules = []
sel = self.GetFirstSelected()
while sel != -1 and sel not in self.blanks:
@@ -362,17 +380,14 @@ class FittingView(d.Display):
sel = self.GetNextSelected(sel)
if len(modules) > 0:
- sFit.setAmmo(fitID, itemID, modules)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddChargeCommand(fitID, itemID, modules))
else:
- populate = sFit.appendModule(fitID, itemID)
- if populate is not None:
- self.slotsChanged()
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID, action="modadd", typeID=itemID))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddCommand(fitID, itemID))
event.Skip()
def removeItem(self, event):
+ """Double Left Click - remove module"""
if event.CmdDown():
return
row, _ = self.HitTest(event.Position)
@@ -386,36 +401,22 @@ class FittingView(d.Display):
def removeModule(self, modules):
"""Removes a list of modules from the fit"""
- sFit = Fit.getInstance()
-
if not isinstance(modules, list):
modules = [modules]
- positions = [mod.modPosition for mod in modules]
- result = sFit.removeModule(self.activeFitID, positions)
+ self.mainFrame.command.Submit(cmd.GuiModuleRemoveCommand(self.activeFitID, modules))
- if result is not None:
- self.slotsChanged()
- ids = {mod.item.ID for mod in modules}
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID, action="moddel", typeID=ids))
-
- def addModule(self, x, y, srcIdx):
- """Add a module from the market browser"""
+ def addModule(self, x, y, itemID):
+ """Add a module from the market browser (from dragging it)"""
dstRow, _ = self.HitTest((x, y))
if dstRow != -1 and dstRow not in self.blanks:
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
mod = self.mods[dstRow]
if not isinstance(mod, Module): # make sure we're not adding something to a T3D Mode
return
- moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].modPosition, srcIdx)
- if moduleChanged is None:
- # the new module doesn't fit in specified slot, try to simply append it
- wx.PostEvent(self.mainFrame, ItemSelected(itemID=srcIdx))
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="modadd", typeID=srcIdx))
+ self.mainFrame.command.Submit(cmd.GuiModuleAddCommand(fitID, itemID, self.mods[dstRow].modPosition))
def swapCargo(self, x, y, srcIdx):
"""Swap a module from cargo to fitting window"""
@@ -428,14 +429,11 @@ class FittingView(d.Display):
if not isinstance(module, Module):
return
- sFit = Fit.getInstance()
- fit = sFit.getFit(self.activeFitID)
- typeID = fit.cargo[srcIdx].item.ID
-
- sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.modPosition, srcIdx,
- mstate.CmdDown() and module.isEmpty)
-
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="modadd", typeID=typeID))
+ self.mainFrame.command.Submit(cmd.GuiCargoToModuleCommand(
+ self.mainFrame.getActiveFit(),
+ module.modPosition,
+ srcIdx,
+ mstate.CmdDown() and module.isEmpty))
def swapItems(self, x, y, srcIdx):
"""Swap two modules in fitting window"""
@@ -443,15 +441,9 @@ class FittingView(d.Display):
sFit = Fit.getInstance()
fit = sFit.getFit(self.activeFitID)
- if mstate.CmdDown():
- clone = True
- else:
- clone = False
-
dstRow, _ = self.HitTest((x, y))
if dstRow != -1 and dstRow not in self.blanks:
-
mod1 = fit.modules[srcIdx]
mod2 = self.mods[dstRow]
@@ -462,13 +454,11 @@ class FittingView(d.Display):
if mod1.slot != mod2.slot:
return
- if getattr(mod2, "modPosition") is not None:
- if clone and mod2.isEmpty:
- sFit.cloneModule(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
- else:
- sFit.swapModules(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
+ clone = mstate.CmdDown() and mod2.isEmpty
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
+ fitID = self.mainFrame.getActiveFit()
+ if getattr(mod2, "modPosition") is not None:
+ self.mainFrame.command.Submit(cmd.GuiModuleSwapOrCloneCommand(fitID, srcIdx, mod2.modPosition, clone))
else:
pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown")))
@@ -508,7 +498,7 @@ class FittingView(d.Display):
# second loop modifies self.mods, rewrites self.blanks to represent actual index of blanks
for i, (x, slot) in enumerate(self.blanks):
self.blanks[i] = x + i # modify blanks with actual index
- self.mods.insert(x + i, Rack.buildRack(slot))
+ self.mods.insert(x + i, Rack.buildRack(slot, sum(m.slot == slot for m in self.mods)))
if fit.mode:
# Modes are special snowflakes and need a little manual loving
@@ -516,7 +506,7 @@ class FittingView(d.Display):
# while also marking the mode header position in the Blanks list
if sFit.serviceFittingOptions["rackSlots"]:
self.blanks.append(len(self.mods))
- self.mods.append(Rack.buildRack(Slot.MODE))
+ self.mods.append(Rack.buildRack(Slot.MODE, None))
self.mods.append(fit.mode)
else:
@@ -527,8 +517,10 @@ class FittingView(d.Display):
self.populate(self.mods)
def fitChanged(self, event):
- print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
-
+ # print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID))
+ if not self:
+ event.Skip()
+ return
try:
if self.activeFitID is not None and self.activeFitID == event.fitID:
self.generateMods()
@@ -622,18 +614,18 @@ class FittingView(d.Display):
else:
mods = self.getSelectedMods()
- sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
ctrl = event.cmdDown or event.middleIsDown
click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left"
- sFit.toggleModulesState(fitID, self.mods[self.GetItemData(row)], mods, click)
+
+ self.mainFrame.command.Submit(cmd.GuiModuleStateChangeCommand(
+ fitID, self.mods[self.GetItemData(row)].modPosition, [mod.modPosition for mod in mods], click))
# update state tooltip
tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)])
if tooltip:
self.SetToolTip(tooltip)
- wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
else:
event.Skip()
@@ -665,14 +657,27 @@ class FittingView(d.Display):
slot = Slot.getValue(slotType)
slotMap[slot] = fit.getSlotsFree(slot) < 0
- font = wx.Font(self.GetClassDefaultAttributes().font)
-
for i, mod in enumerate(self.mods):
self.SetItemBackgroundColour(i, self.GetBackgroundColour())
# only consider changing color if we're dealing with a Module
if type(mod) is Module:
- if slotMap[mod.slot] or getattr(mod, 'restrictionOverridden', None): # Color too many modules as red
+ hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None)
+ # If module had broken fitting restrictions but now doesn't,
+ # ensure it is now valid, and remove restrictionOverridden
+ # variable. More in #1519
+ if not fit.ignoreRestrictions and hasRestrictionOverriden:
+ clean = False
+ if mod.fits(fit, False):
+ if not mod.hardpoint:
+ clean = True
+ elif fit.getHardpointsFree(mod.hardpoint) >= 0:
+ clean = True
+ if clean:
+ del mod.restrictionOverridden
+ hasRestrictionOverriden = not hasRestrictionOverriden
+
+ if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red
self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51))
elif sFit.serviceFittingOptions["colorFitBySlot"]: # Color by slot it enabled
self.SetItemBackgroundColour(i, self.slotColour(mod.slot))
@@ -681,11 +686,11 @@ class FittingView(d.Display):
if isinstance(mod, Rack) and \
sFit.serviceFittingOptions["rackSlots"] and \
sFit.serviceFittingOptions["rackLabels"]:
- font.SetWeight(wx.FONTWEIGHT_BOLD)
- self.SetItemFont(i, font)
+ self.font.SetWeight(wx.FONTWEIGHT_BOLD)
+ self.SetItemFont(i, self.font)
else:
- font.SetWeight(wx.FONTWEIGHT_NORMAL)
- self.SetItemFont(i, font)
+ self.font.SetWeight(wx.FONTWEIGHT_NORMAL)
+ self.SetItemFont(i, self.font)
self.Thaw()
self.itemCount = self.GetItemCount()
@@ -712,13 +717,12 @@ class FittingView(d.Display):
# noinspection PyPropertyAccess
def MakeSnapshot(self, maxColumns=1337):
if self.FVsnapshot:
- del self.FVsnapshot
+ self.FVsnapshot = None
tbmp = wx.Bitmap(16, 16)
tdc = wx.MemoryDC()
tdc.SelectObject(tbmp)
- font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
- tdc.SetFont(font)
+ tdc.SetFont(self.font)
columnsWidths = []
for i in range(len(self.DEFAULT_COLS)):
@@ -814,7 +818,7 @@ class FittingView(d.Display):
mdc.SetBackground(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)))
mdc.Clear()
- mdc.SetFont(font)
+ mdc.SetFont(self.font)
mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
cx = padding
diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py
index b391c1812..d33ff9779 100644
--- a/gui/builtinViews/implantEditor.py
+++ b/gui/builtinViews/implantEditor.py
@@ -156,7 +156,7 @@ class BaseImplantEditorView(wx.Panel):
currentMktGrp = sMkt.getMarketGroup(tree.GetItemData(parent))
items = sMkt.getItemsByMarketGroup(currentMktGrp)
for item in items:
- iconId = self.addMarketViewImage(item.icon.iconFile)
+ iconId = self.addMarketViewImage(item.iconID)
tree.AppendItem(parent, item.name, iconId, data=item)
tree.SortChildren(parent)
diff --git a/gui/characterEditor.py b/gui/characterEditor.py
index c7a2fef8c..2b4d59341 100644
--- a/gui/characterEditor.py
+++ b/gui/characterEditor.py
@@ -17,35 +17,30 @@
# along with pyfa. If not, see .
# =============================================================================
+import re
+
+import roman
# noinspection PyPackageRequirements
import wx
import wx.dataview
import wx.lib.agw.hyperlink
-
# noinspection PyPackageRequirements
import wx.lib.newevent
+from logbook import Logger
# noinspection PyPackageRequirements
from wx.dataview import TreeListCtrl
-from gui.bitmap_loader import BitmapLoader
-from gui.contextMenu import ContextMenu
-import gui.globalEvents as GE
-from gui.builtinViews.implantEditor import BaseImplantEditorView
-from gui.builtinViews.entityEditor import EntityEditor, BaseValidator, TextEntryValidatedDialog
-from service.fit import Fit
-from service.character import Character
-from service.esi import Esi
-from service.network import AuthenticationError, TimeoutError
-from service.market import Market
-from logbook import Logger
-
from wx.lib.agw.floatspin import FloatSpin
-
-from gui.utils.clipboard import toClipboard, fromClipboard
-
-import roman
-import re
-import webbrowser
+import gui.globalEvents as GE
+from gui.bitmap_loader import BitmapLoader
+from gui.builtinViews.entityEditor import BaseValidator, EntityEditor, TextEntryValidatedDialog
+from gui.builtinViews.implantEditor import BaseImplantEditorView
+from gui.contextMenu import ContextMenu
+from gui.utils.clipboard import fromClipboard, toClipboard
+from service.character import Character
+from service.esi import Esi
+from service.fit import Fit
+from service.market import Market
pyfalog = Logger(__name__)
@@ -134,7 +129,12 @@ class CharacterEntityEditor(EntityEditor):
def DoRename(self, entity, name):
sChar = Character.getInstance()
- sChar.rename(entity, name)
+
+ if entity.alphaCloneID:
+ trimmed_name = re.sub('[ \(\u03B1\)]+$', '', name)
+ sChar.rename(entity, trimmed_name)
+ else:
+ sChar.rename(entity, name)
def DoCopy(self, entity, name):
sChar = Character.getInstance()
@@ -734,17 +734,11 @@ class APIView(wx.Panel):
self.stDisabledTip.Wrap(-1)
hintSizer.Add(self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10)
- self.noCharactersTip = wx.StaticText(self, wx.ID_ANY,
- "You haven't logging into EVE SSO with any characters yet. Please use the "
- "button below to log into EVE.", style=wx.ALIGN_CENTER)
- self.noCharactersTip.Wrap(-1)
- hintSizer.Add(self.noCharactersTip, 0, wx.TOP | wx.BOTTOM, 10)
-
self.stDisabledTip.Hide()
hintSizer.AddStretchSpacer()
pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5)
- fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0)
+ fgSizerInput = wx.FlexGridSizer(1, 3, 0, 0)
fgSizerInput.AddGrowableCol(1)
fgSizerInput.SetFlexibleDirection(wx.BOTH)
fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
@@ -754,15 +748,28 @@ class APIView(wx.Panel):
fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
self.charChoice = wx.Choice(self, wx.ID_ANY, style=0)
- fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 10)
+ fgSizerInput.Add(self.charChoice, 1, wx.TOP | wx.BOTTOM | wx.EXPAND, 10)
+
+ self.fetchButton = wx.Button(self, wx.ID_ANY, "Get Skills", wx.DefaultPosition, wx.DefaultSize, 0)
+ self.fetchButton.Bind(wx.EVT_BUTTON, self.fetchSkills)
+ fgSizerInput.Add(self.fetchButton, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10)
pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5)
+ pmainSizer.AddStretchSpacer()
+
+ self.m_staticline1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
+ pmainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.ALL, 10)
+
+ self.noCharactersTip = wx.StaticText(self, wx.ID_ANY, "Don't see your EVE character in the list?", style=wx.ALIGN_CENTER)
+
+ self.noCharactersTip.Wrap(-1)
+ pmainSizer.Add(self.noCharactersTip, 0, wx.CENTER | wx.TOP | wx.BOTTOM, 0)
+
self.addButton = wx.Button(self, wx.ID_ANY, "Log In with EVE SSO", wx.DefaultPosition, wx.DefaultSize, 0)
self.addButton.Bind(wx.EVT_BUTTON, self.addCharacter)
- pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 5)
- self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString)
- pmainSizer.Add(self.stStatus, 0, wx.ALL, 5)
+ pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 10)
+
self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGOUT, self.ssoListChanged)
self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoListChanged)
self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
@@ -776,9 +783,18 @@ class APIView(wx.Panel):
def ssoCharChanged(self, event):
sChar = Character.getInstance()
activeChar = self.charEditor.entityEditor.getActiveEntity()
- sChar.setSsoCharacter(activeChar.ID, self.getActiveCharacter())
+ ssoChar = self.getActiveCharacter()
+ sChar.setSsoCharacter(activeChar.ID, ssoChar)
+
+ self.fetchButton.Enable(ssoChar is not None)
+
event.Skip()
+ def fetchSkills(self, evt):
+ sChar = Character.getInstance()
+ char = self.charEditor.entityEditor.getActiveEntity()
+ sChar.apiFetch(char.ID, self.__fetchCallback)
+
def addCharacter(self, event):
sEsi = Esi.getInstance()
sEsi.login()
@@ -788,17 +804,8 @@ class APIView(wx.Panel):
return self.charChoice.GetClientData(selection) if selection is not -1 else None
def ssoListChanged(self, event):
- sEsi = Esi.getInstance()
- ssoChars = sEsi.getSsoCharacters()
-
- if len(ssoChars) == 0:
- self.charChoice.Hide()
- self.m_staticCharText.Hide()
- self.noCharactersTip.Show()
- else:
- self.noCharactersTip.Hide()
- self.m_staticCharText.Show()
- self.charChoice.Show()
+ if not self: # todo: fix event not unbinding properly
+ return
self.charChanged(event)
@@ -814,6 +821,8 @@ class APIView(wx.Panel):
sso = sChar.getSsoCharacter(activeChar.ID)
+ self.fetchButton.Enable(sso is not None)
+
ssoChars = sEsi.getSsoCharacters()
self.charChoice.Clear()
@@ -825,9 +834,9 @@ class APIView(wx.Panel):
if sso is not None and char.ID == sso.ID:
self.charChoice.SetSelection(currId)
- if sso is None:
- self.charChoice.SetSelection(noneID)
+ if sso is None:
+ self.charChoice.SetSelection(noneID)
#
# if chars:
@@ -851,13 +860,17 @@ class APIView(wx.Panel):
event.Skip()
def __fetchCallback(self, e=None):
- charName = self.charChoice.GetString(self.charChoice.GetSelection())
- if e is None:
- self.stStatus.SetLabel("Successfully fetched {}\'s skills from EVE API.".format(charName))
- else:
+ if e:
exc_type, exc_obj, exc_trace = e
- pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}".format(charName, exc_obj))
- self.stStatus.SetLabel("Unable to retrieve {}\'s skills. Error message:\n{}".format(charName, exc_obj))
+ pyfalog.warn("Error fetching skill information for character")
+ pyfalog.warn(exc_obj)
+
+ wx.MessageBox(
+ "Error fetching skill information",
+ "Error", wx.ICON_ERROR | wx.STAY_ON_TOP)
+ else:
+ wx.MessageBox(
+ "Successfully fetched skills", "Success", wx.ICON_INFORMATION | wx.STAY_ON_TOP)
class SecStatusDialog(wx.Dialog):
diff --git a/gui/characterSelection.py b/gui/characterSelection.py
index cc63a1f82..051ce69b4 100644
--- a/gui/characterSelection.py
+++ b/gui/characterSelection.py
@@ -170,7 +170,6 @@ class CharacterSelection(wx.Panel):
def charChanged(self, event):
fitID = self.mainFrame.getActiveFit()
charID = self.getActiveCharacter()
- sChar = Character.getInstance()
if charID == -1:
# revert to previous character
diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py
index c03edacab..66c1c262e 100644
--- a/gui/chrome_tabs.py
+++ b/gui/chrome_tabs.py
@@ -1,4 +1,4 @@
-#===============================================================================
+# ===============================================================================
#
# ToDo: Bug - when selecting close on a tab, sometimes the tab to the right is
# selected, most likely due to determination of mouse position
@@ -11,7 +11,7 @@
# tab index?). This will also help with finding close buttons.
# ToDo: Fix page preview code (PFNotebookPagePreview)
#
-#= ==============================================================================
+# ===============================================================================
import wx
import wx.lib.newevent
@@ -20,6 +20,7 @@ from gui.bitmap_loader import BitmapLoader
from gui.utils import draw
from gui.utils import color as color_utils
from service.fit import Fit
+from gui.utils import fonts
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
@@ -29,7 +30,7 @@ PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent()
PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent()
-class VetoAble():
+class VetoAble:
def __init__(self):
self.__vetoed = False
@@ -40,7 +41,7 @@ class VetoAble():
return self.__vetoed
-class NotebookTabChangeEvent():
+class NotebookTabChangeEvent:
def __init__(self, old, new):
self.__old = old
self.__new = new
@@ -357,7 +358,7 @@ class _TabRenderer:
self.tab_bitmap = None
self.tab_back_bitmap = None
self.padding = 4
- self.font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
+ self.font = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self.tab_img = img
self.position = (0, 0) # Not used internally for rendering - helper for tab container
@@ -413,7 +414,7 @@ class _TabRenderer:
mdc.SelectObject(ebmp)
mdc.SetFont(self.font)
textSizeX, textSizeY = mdc.GetTextExtent(self.text)
- totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding* 2
+ totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding * 2
mdc.SelectObject(wx.NullBitmap)
return totalSize, self.tab_height
@@ -551,24 +552,6 @@ class _TabRenderer:
bmp,
self.left_width + self.padding - bmp.GetWidth() / 2,
(height - bmp.GetHeight()) / 2)
- text_start = self.left_width + self.padding + bmp.GetWidth() / 2
- else:
- text_start = self.left_width
-
- mdc.SetFont(self.font)
-
- maxsize = self.tab_width \
- - text_start \
- - self.right_width \
- - self.padding * 4
- color = self.selected_color if self.selected else self.inactive_color
-
- mdc.SetTextForeground(color_utils.GetSuitable(color, 1))
-
- # draw text (with no ellipses)
- text = draw.GetPartialText(mdc, self.text, maxsize, "")
- tx, ty = mdc.GetTextExtent(text)
- mdc.DrawText(text, text_start + self.padding, height / 2 - ty / 2)
# draw close button
if self.closeable:
@@ -595,6 +578,30 @@ class _TabRenderer:
bmp = wx.Bitmap(img)
self.tab_bitmap = bmp
+ # We draw the text separately in order to draw it directly on the native DC, rather than a memory one, because
+ # drawing text on a memory DC draws it blurry on HD/Retina screens
+ def DrawText(self, dc):
+ height = self.tab_height
+ dc.SetFont(self.font)
+
+ if self.tab_img:
+ text_start = self.left_width + self.padding + self.tab_img.GetWidth() / 2
+ else:
+ text_start = self.left_width
+
+ maxsize = self.tab_width \
+ - text_start \
+ - self.right_width \
+ - self.padding * 4
+ color = self.selected_color if self.selected else self.inactive_color
+
+ dc.SetTextForeground(color_utils.GetSuitable(color, 1))
+
+ # draw text (with no ellipses)
+ text = draw.GetPartialText(dc, self.text, maxsize, "")
+ tx, ty = dc.GetTextExtent(text)
+ dc.DrawText(text, text_start + self.padding, height / 2 - ty / 2)
+
def __repr__(self):
return "_TabRenderer(text={}, disabled={}) at {}".format(
self.text, self.disabled, hex(id(self))
@@ -729,6 +736,7 @@ class _TabsContainer(wx.Panel):
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
+ self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged)
@@ -769,6 +777,29 @@ class _TabsContainer(wx.Panel):
self.dragged_tab = tab
+ def OnMiddleUp(self, event):
+ mposx, mposy = event.GetPosition()
+
+ tab = self.FindTabAtPos(mposx, mposy)
+
+ if tab is None or not tab.closeable: # if not able to close, return False
+ return False
+
+ index = self.tabs.index(tab)
+ ev = PageClosing(index)
+ wx.PostEvent(self.Parent, ev)
+
+ if ev.isVetoed():
+ return False
+
+ index = self.GetTabIndex(tab)
+ self.Parent.DeletePage(index)
+ wx.PostEvent(self.Parent, PageClosed(index=index))
+
+ sel = self.GetSelected()
+ if sel is not None:
+ wx.PostEvent(self.Parent, PageChanged(-1, sel))
+
def OnMotion(self, event):
"""
Determines what happens when the mouse moves. This handles primarily
@@ -1144,6 +1175,10 @@ class _TabsContainer(wx.Panel):
img = img.AdjustChannels(1, 1, 1, 0.85)
bmp = wx.Bitmap(img)
mdc.DrawBitmap(bmp, posx, posy, True)
+
+ mdc.SetDeviceOrigin(posx, posy)
+ tab.DrawText(mdc)
+ mdc.SetDeviceOrigin(0, 0)
else:
selected = tab
@@ -1163,6 +1198,10 @@ class _TabsContainer(wx.Panel):
mdc.DrawBitmap(bmp, posx, posy, True)
+ mdc.SetDeviceOrigin(posx, posy)
+ selected.DrawText(mdc)
+ mdc.SetDeviceOrigin(0, 0)
+
def OnErase(self, event):
pass
@@ -1322,7 +1361,7 @@ class PFNotebookPagePreview(wx.Frame):
self.padding = 15
self.transp = 0
- hfont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
+ hfont = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self.SetFont(hfont)
tx, ty = self.GetTextExtent(self.title)
@@ -1384,7 +1423,7 @@ class PFNotebookPagePreview(wx.Frame):
mdc.SetBackground(wx.Brush(color))
mdc.Clear()
- font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
+ font = wx.Font(11, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
mdc.SetFont(font)
x, y = mdc.GetTextExtent(self.title)
@@ -1478,4 +1517,3 @@ if __name__ == "__main__":
top = Frame("Test Chrome Tabs")
top.Show()
app.MainLoop()
-
diff --git a/gui/contextMenu.py b/gui/contextMenu.py
index 11d574157..7c1caf085 100644
--- a/gui/contextMenu.py
+++ b/gui/contextMenu.py
@@ -186,8 +186,9 @@ from gui.builtinContextMenus import ( # noqa: E402,F401
itemStats,
damagePattern,
marketJump,
- droneSplit,
+ # droneSplit,
itemRemove,
+ fillWithModule,
droneRemoveStack,
ammoPattern,
project,
@@ -202,11 +203,12 @@ from gui.builtinContextMenus import ( # noqa: E402,F401
priceOptions,
amount,
cargoAmmo,
- droneStack,
+ # droneStack,
metaSwap,
implantSets,
fighterAbilities,
boosterSideEffects,
commandFits,
- tabbedFits
+ tabbedFits,
+ mutaplasmids,
)
diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py
index 5675c835e..91725e048 100644
--- a/gui/copySelectDialog.py
+++ b/gui/copySelectDialog.py
@@ -18,51 +18,104 @@
# =============================================================================
+from collections import OrderedDict
+
# noinspection PyPackageRequirements
import wx
+from service.port.eft import EFT_OPTIONS
+from service.port.multibuy import MULTIBUY_OPTIONS
+from service.settings import SettingsProvider
+
class CopySelectDialog(wx.Dialog):
copyFormatEft = 0
- copyFormatEftImps = 1
- copyFormatXml = 2
- copyFormatDna = 3
- copyFormatEsi = 4
- copyFormatMultiBuy = 5
+ copyFormatXml = 1
+ copyFormatDna = 2
+ copyFormatEsi = 3
+ copyFormatMultiBuy = 4
+ copyFormatEfs = 5
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Select a format", size=(-1, -1),
style=wx.DEFAULT_DIALOG_STYLE)
mainSizer = wx.BoxSizer(wx.VERTICAL)
- copyFormats = ["EFT", "EFT (Implants)", "XML", "DNA", "CREST", "MultiBuy"]
- copyFormatTooltips = {CopySelectDialog.copyFormatEft: "EFT text format",
- CopySelectDialog.copyFormatEftImps: "EFT text format",
- CopySelectDialog.copyFormatXml: "EVE native XML format",
- CopySelectDialog.copyFormatDna: "A one-line text format",
- CopySelectDialog.copyFormatEsi: "A JSON format used for EVE CREST",
- CopySelectDialog.copyFormatMultiBuy: "MultiBuy text format"}
- selector = wx.RadioBox(self, wx.ID_ANY, label="Copy to the clipboard using:", choices=copyFormats,
- style=wx.RA_SPECIFY_ROWS)
- selector.Bind(wx.EVT_RADIOBOX, self.Selected)
- for format, tooltip in copyFormatTooltips.items():
- selector.SetItemToolTip(format, tooltip)
+ self.copyFormats = OrderedDict((
+ ("EFT", (CopySelectDialog.copyFormatEft, EFT_OPTIONS)),
+ ("MultiBuy", (CopySelectDialog.copyFormatMultiBuy, MULTIBUY_OPTIONS)),
+ ("ESI", (CopySelectDialog.copyFormatEsi, None)),
+ ("EFS", (CopySelectDialog.copyFormatEfs, None)),
+ # ("XML", (CopySelectDialog.copyFormatXml, None)),
+ # ("DNA", (CopySelectDialog.copyFormatDna, None)),
+ ))
- self.copyFormat = CopySelectDialog.copyFormatEft
- selector.SetSelection(self.copyFormat)
+ defaultFormatOptions = {}
+ for formatId, formatOptions in self.copyFormats.values():
+ if formatOptions is None:
+ continue
+ defaultFormatOptions[formatId] = {opt[0]: opt[3] for opt in formatOptions}
- mainSizer.Add(selector, 0, wx.EXPAND | wx.ALL, 5)
+ self.settings = SettingsProvider.getInstance().getSettings("pyfaExport", {"format": 0, "options": defaultFormatOptions})
+ # Options used to be stored as int (EFT export options only),
+ # overwrite them with new format when needed
+ if isinstance(self.settings["options"], int):
+ self.settings["options"] = defaultFormatOptions
+
+ self.options = {}
+
+ initialized = False
+ for formatName, formatData in self.copyFormats.items():
+ formatId, formatOptions = formatData
+ if not initialized:
+ rdo = wx.RadioButton(self, wx.ID_ANY, formatName, style=wx.RB_GROUP)
+ initialized = True
+ else:
+ rdo = wx.RadioButton(self, wx.ID_ANY, formatName)
+ rdo.Bind(wx.EVT_RADIOBUTTON, self.Selected)
+ if self.settings['format'] == formatId:
+ rdo.SetValue(True)
+ self.copyFormat = formatId
+ mainSizer.Add(rdo, 0, wx.EXPAND | wx.ALL, 5)
+
+ if formatOptions:
+ bsizer = wx.BoxSizer(wx.VERTICAL)
+ self.options[formatId] = {}
+
+ for optId, optName, optDesc, _ in formatOptions:
+ checkbox = wx.CheckBox(self, -1, optName)
+ self.options[formatId][optId] = checkbox
+ if self.settings['options'].get(formatId, {}).get(optId, defaultFormatOptions.get(formatId, {}).get(optId)):
+ checkbox.SetValue(True)
+ bsizer.Add(checkbox, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, 3)
+ mainSizer.Add(bsizer, 1, wx.EXPAND | wx.LEFT, 20)
buttonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)
if buttonSizer:
mainSizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, 5)
+ self.toggleOptions()
self.SetSizer(mainSizer)
self.Fit()
self.Center()
def Selected(self, event):
- self.copyFormat = event.GetSelection()
+ obj = event.GetEventObject()
+ formatName = obj.GetLabel()
+ self.copyFormat = self.copyFormats[formatName][0]
+ self.toggleOptions()
+ self.Fit()
+
+ def toggleOptions(self):
+ for formatId in self.options:
+ for checkbox in self.options[formatId].values():
+ checkbox.Enable(self.GetSelected() == formatId)
def GetSelected(self):
return self.copyFormat
+
+ def GetOptions(self):
+ options = {}
+ for formatId in self.options:
+ options[formatId] = {optId: ch.IsChecked() for optId, ch in self.options[formatId].items()}
+ return options
diff --git a/gui/devTools.py b/gui/devTools.py
index 98a14662c..44012128c 100644
--- a/gui/devTools.py
+++ b/gui/devTools.py
@@ -35,7 +35,7 @@ class DevTools(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Damage Pattern Editor", size=wx.Size(400, 240))
-
+ self.mainFrame = parent
self.block = False
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)
@@ -56,13 +56,19 @@ class DevTools(wx.Dialog):
self.fitTest = wx.Button(self, wx.ID_ANY, "Test fits", wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.fitTest, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
- self.fitTest .Bind(wx.EVT_BUTTON, self.fit_test)
+ self.fitTest.Bind(wx.EVT_BUTTON, self.fit_test)
+
+ self.cmdPrint = wx.Button(self, wx.ID_ANY, "Command Print", wx.DefaultPosition, wx.DefaultSize, 0)
+ mainSizer.Add(self.cmdPrint, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
+
+ self.cmdPrint.Bind(wx.EVT_BUTTON, self.cmd_print)
self.SetSizer(mainSizer)
self.Layout()
self.CenterOnParent()
self.Show()
+ print(parent)
def objects_by_id(self, evt):
input = self.id_get.GetValue()
@@ -81,6 +87,11 @@ class DevTools(wx.Dialog):
else:
print(None)
+ def cmd_print(self, evt):
+ print("=" * 20)
+ for x in self.mainFrame.command.GetCommands():
+ print("{}{} {}".format("==> " if x == self.mainFrame.command.GetCurrentCommand() else "", x.GetName(), x))
+
def gc_collect(self, evt):
print(gc.collect())
print(gc.get_debug())
diff --git a/gui/errorDialog.py b/gui/errorDialog.py
index 706e3c5a3..d9112d4b8 100644
--- a/gui/errorDialog.py
+++ b/gui/errorDialog.py
@@ -26,6 +26,7 @@ import traceback
import config
from logbook import Logger
from service.prereqsCheck import version_block
+import datetime
pyfalog = Logger(__name__)
@@ -63,6 +64,11 @@ class ErrorFrame(wx.Frame):
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600),
style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP)
+ from eos.config import gamedata_version, gamedata_date
+
+ time = datetime.datetime.fromtimestamp(int(gamedata_date)).strftime('%Y-%m-%d %H:%M:%S')
+ version = "pyfa v" + config.getVersion() + '\nEVE Data Version: {} ({})\n\n'.format(gamedata_version, time) # gui.aboutData.versionString
+
desc = "pyfa has experienced an unexpected issue. Below is a message that contains crucial\n" \
"information about how this was triggered. Please contact the developers with the\n" \
"information provided through the EVE Online forums or file a GitHub issue."
@@ -97,7 +103,7 @@ class ErrorFrame(wx.Frame):
# mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5)
- self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version_block.strip(), wx.DefaultPosition,
+ self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version + version_block.strip(), wx.DefaultPosition,
(-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP)
self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5)
diff --git a/gui/esiFittings.py b/gui/esiFittings.py
index 92fd89b83..9d14ad8fe 100644
--- a/gui/esiFittings.py
+++ b/gui/esiFittings.py
@@ -1,5 +1,3 @@
-import time
-import webbrowser
import json
# noinspection PyPackageRequirements
import wx
@@ -15,10 +13,9 @@ from gui.display import Display
import gui.globalEvents as GE
from logbook import Logger
-import calendar
from service.esi import Esi
-from esipy.exceptions import APIException
-from service.port import ESIExportException
+from service.esiAccess import APIException
+from service.port.esi import ESIExportException
pyfalog = Logger(__name__)
@@ -32,7 +29,6 @@ class EveFittings(wx.Frame):
self.mainFrame = parent
mainSizer = wx.BoxSizer(wx.VERTICAL)
- sEsi = Esi.getInstance()
characterSelectSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -111,21 +107,23 @@ class EveFittings(wx.Frame):
waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self)
try:
- fittings = sEsi.getFittings(self.getActiveCharacter())
+ self.fittings = sEsi.getFittings(self.getActiveCharacter())
# self.cacheTime = fittings.get('cached_until')
# self.updateCacheStatus(None)
# self.cacheTimer.Start(1000)
- self.fitTree.populateSkillTree(fittings)
+ self.fitTree.populateSkillTree(self.fittings)
del waitDialog
except requests.exceptions.ConnectionError:
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
except APIException as ex:
- del waitDialog # Can't do this in a finally because then it obscures the message dialog
+ # Can't do this in a finally because then it obscures the message dialog
+ del waitDialog # noqa: F821
ESIExceptionHandler(self, ex)
except Exception as ex:
- del waitDialog
+ del waitDialog # noqa: F821
+ raise ex
def importFitting(self, event):
selection = self.fitView.fitSelection
@@ -133,7 +131,7 @@ class EveFittings(wx.Frame):
return
data = self.fitTree.fittingsTreeCtrl.GetItemData(selection)
sPort = Port.getInstance()
- fits = sPort.importFitFromBuffer(data)
+ import_type, fits = sPort.importFitFromBuffer(data)
self.mainFrame._openAfterImport(fits)
def deleteFitting(self, event):
@@ -150,15 +148,32 @@ class EveFittings(wx.Frame):
if dlg.ShowModal() == wx.ID_YES:
try:
sEsi.delFitting(self.getActiveCharacter(), data['fitting_id'])
+ # repopulate the fitting list
+ self.fitTree.populateSkillTree(self.fittings)
+ self.fitView.update([])
except requests.exceptions.ConnectionError:
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
-class ESIExceptionHandler(object):
+class ESIServerExceptionHandler(object):
def __init__(self, parentWindow, ex):
- if ex.response['error'] == "invalid_token":
+ dlg = wx.MessageDialog(parentWindow,
+ "There was an issue starting up the localized server, try setting "
+ "Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> "
+ "Login Authentication Method. If this doesn't fix the problem please file an "
+ "issue on Github.",
+ "Add Character Error",
+ wx.OK | wx.ICON_ERROR)
+ dlg.ShowModal()
+ pyfalog.error(ex)
+
+
+class ESIExceptionHandler(object):
+ # todo: make this a generate excetpion handler for all calls
+ def __init__(self, parentWindow, ex):
+ if ex.response['error'].startswith('Token is not valid') or ex.response['error'] == 'invalid_token': # todo: this seems messy, figure out a better response
dlg = wx.MessageDialog(parentWindow,
"There was an error validating characters' SSO token. Please try "
"logging into the character again to reset the token.", "Invalid Token",
@@ -178,7 +193,6 @@ class ExportToEve(wx.Frame):
self.mainFrame = parent
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
- sEsi = Esi.getInstance()
mainSizer = wx.BoxSizer(wx.VERTICAL)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -242,29 +256,30 @@ class ExportToEve(wx.Frame):
self.statusbar.SetStatusText("Sending request and awaiting response", 1)
sEsi = Esi.getInstance()
- try:
- sFit = Fit.getInstance()
- data = sPort.exportESI(sFit.getFit(fitID))
- res = sEsi.postFitting(self.getActiveCharacter(), data)
+ sFit = Fit.getInstance()
+ data = sPort.exportESI(sFit.getFit(fitID))
+ res = sEsi.postFitting(self.getActiveCharacter(), data)
+ try:
+ res.raise_for_status()
self.statusbar.SetStatusText("", 0)
- self.statusbar.SetStatusText("", 1)
- # try:
- # text = json.loads(res.text)
- # self.statusbar.SetStatusText(text['message'], 1)
- # except ValueError:
- # pyfalog.warning("Value error on loading JSON.")
- # self.statusbar.SetStatusText("", 1)
+ self.statusbar.SetStatusText(res.reason, 1)
except requests.exceptions.ConnectionError:
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
- self.statusbar.SetStatusText(msg)
+ self.statusbar.SetStatusText("ERROR", 0)
+ self.statusbar.SetStatusText(msg, 1)
except ESIExportException as ex:
pyfalog.error(ex)
self.statusbar.SetStatusText("ERROR", 0)
- self.statusbar.SetStatusText(ex.args[0], 1)
+ self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1)
except APIException as ex:
- ESIExceptionHandler(self, ex)
+ try:
+ ESIExceptionHandler(self, ex)
+ except Exception as ex:
+ self.statusbar.SetStatusText("ERROR", 0)
+ self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1)
+ pyfalog.error(ex)
class SsoCharacterMgmt(wx.Dialog):
@@ -304,8 +319,8 @@ class SsoCharacterMgmt(wx.Dialog):
self.Centre(wx.BOTH)
def ssoLogin(self, event):
- if (self):
- #todo: these events don't unbind properly when window is closed (?), hence the `if`. Figure out better way of doing this.
+ if self:
+ # todo: these events don't unbind properly when window is closed (?), hence the `if`. Figure out better way of doing this.
self.popCharList()
event.Skip()
@@ -323,10 +338,12 @@ class SsoCharacterMgmt(wx.Dialog):
self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE)
self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE)
- @staticmethod
- def addChar(event):
- sEsi = Esi.getInstance()
- sEsi.login()
+ def addChar(self, event):
+ try:
+ sEsi = Esi.getInstance()
+ sEsi.login()
+ except Exception as ex:
+ ESIServerExceptionHandler(self, ex)
def delChar(self, event):
item = self.lcCharacters.GetFirstSelected()
@@ -362,10 +379,17 @@ class FittingsTreeView(wx.Panel):
tree = self.fittingsTreeCtrl
tree.DeleteChildren(root)
+ sEsi = Esi.getInstance()
+
dict = {}
fits = data
for fit in fits:
+ if fit['fitting_id'] in sEsi.fittings_deleted:
+ continue
ship = getItem(fit['ship_type_id'])
+ if ship is None:
+ pyfalog.debug('Cannot find ship type id: {}'.format(fit['ship_type_id']))
+ continue
if ship.name not in dict:
dict[ship.name] = []
dict[ship.name].append(fit)
diff --git a/gui/fitCommands/__init__.py b/gui/fitCommands/__init__.py
new file mode 100644
index 000000000..42b718c5d
--- /dev/null
+++ b/gui/fitCommands/__init__.py
@@ -0,0 +1,37 @@
+from .guiToggleModuleState import GuiModuleStateChangeCommand
+from .guiAddModule import GuiModuleAddCommand
+from .guiRemoveModule import GuiModuleRemoveCommand
+from .guiAddCharge import GuiModuleAddChargeCommand
+from .guiFillWithModule import GuiFillWithModuleCommand
+from .guiSwapCloneModule import GuiModuleSwapOrCloneCommand
+from .guiRemoveCargo import GuiRemoveCargoCommand
+from .guiAddCargo import GuiAddCargoCommand
+from .guiRemoveImplant import GuiRemoveImplantCommand
+from .guiAddImplant import GuiAddImplantCommand
+from .guiAddBooster import GuiAddBoosterCommand
+from .guiRemoveBooster import GuiRemoveBoosterCommand
+from .guiAddCommand import GuiAddCommandCommand
+from .guiRemoveCommand import GuiRemoveCommandCommand
+from .guiSetMode import GuiSetModeCommand
+from .guiToggleCommand import GuiToggleCommandCommand
+from .guiAddProjected import GuiAddProjectedCommand
+from .guiRemoveProjected import GuiRemoveProjectedCommand
+from .guiCargoToModule import GuiCargoToModuleCommand
+from .guiModuleToCargo import GuiModuleToCargoCommand
+from .guiAddFighter import GuiAddFighterCommand
+from .guiRemoveFighter import GuiRemoveFighterCommand
+from .guiMetaSwap import GuiMetaSwapCommand
+from .guiToggleFighter import GuiToggleFighterCommand
+from .guiToggleImplant import GuiToggleImplantCommand
+from .guiToggleBooster import GuiToggleBoosterCommand
+from .guiAddDrone import GuiAddDroneCommand
+from .guiRemoveDrone import GuiRemoveDroneCommand
+from .guiChangeFighterQty import GuiChangeFighterQty
+from .guiChangeCargoQty import GuiChangeCargoQty
+from .guiChangeProjectedFitQty import GuiChangeProjectedFitQty
+from .guiChangeDroneQty import GuiChangeDroneQty
+from .guiChangeProjectedDroneQty import GuiChangeProjectedDroneQty
+from .guiToggleDrone import GuiToggleDroneCommand
+from .guiFitRename import GuiFitRenameCommand
+from .guiChangeImplantLocation import GuiChangeImplantLocation
+from .guiImportMutatedModule import GuiImportMutatedModuleCommand
diff --git a/gui/fitCommands/calc/__init__.py b/gui/fitCommands/calc/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gui/fitCommands/calc/fitAddBooster.py b/gui/fitCommands/calc/fitAddBooster.py
new file mode 100644
index 000000000..0470b556b
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddBooster.py
@@ -0,0 +1,49 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.booster import Booster
+pyfalog = Logger(__name__)
+
+
+class FitAddBoosterCommand(wx.Command):
+ """"
+ from sFit.addBooster
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Adding booster ({0}) to fit ID: {1}", self.itemID, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager="attributes")
+
+ if next((x for x in fit.boosters if x.itemID == self.itemID), None):
+ return False # already have item in list of boosters
+
+ try:
+ booster = Booster(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", self.itemID)
+ return False
+
+ self.old_item = fit.boosters.makeRoom(booster)
+ fit.boosters.append(booster)
+ self.new_index = fit.boosters.index(booster)
+ return True
+
+ def Undo(self):
+ if self.old_item:
+ # If we had an item in the slot previously, add it back.
+ cmd = FitAddBoosterCommand(self.fitID, self.old_item)
+ cmd.Do()
+ return True
+
+ from .fitRemoveBooster import FitRemoveBoosterCommand # Avoid circular import
+ cmd = FitRemoveBoosterCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddCargo.py b/gui/fitCommands/calc/fitAddCargo.py
new file mode 100644
index 000000000..fff3abd26
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddCargo.py
@@ -0,0 +1,43 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.cargo import Cargo
+pyfalog = Logger(__name__)
+
+
+class FitAddCargoCommand(wx.Command):
+ """"
+ from sFit.addCargo
+ """
+ def __init__(self, fitID, itemID, amount=1, replace=False):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.replace = replace # if this is false, we increment.
+
+ def Do(self):
+ pyfalog.debug("Adding cargo {0} (x{1}) for fit {2}", self.itemID, self.amount, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID)
+
+ cargo = next((x for x in fit.cargo if x.itemID == self.itemID), None)
+
+ if cargo is None:
+ cargo = Cargo(item)
+ fit.cargo.append(cargo)
+
+ if self.replace:
+ cargo.amount = self.amount
+ else:
+ cargo.amount += self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from .fitRemoveCargo import FitRemoveCargoCommand # Avoid circular import
+ cmd = FitRemoveCargoCommand(self.fitID, self.itemID, self.amount)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddCommand.py b/gui/fitCommands/calc/fitAddCommand.py
new file mode 100644
index 000000000..660d7b5a0
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddCommand.py
@@ -0,0 +1,47 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitAddCommandCommand(wx.Command): # well that's an unfrtunate name
+ """"
+ from sFit.addCommand
+ """
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ pyfalog.debug("Projecting command fit ({0}) onto: {1}", self.fitID, self.commandFitID)
+ fit = eos.db.getFit(self.fitID)
+ command = eos.db.getFit(self.commandFitID)
+
+ if not command:
+ # if redoing when the command fit has been deleted, simply fail this command
+ return False
+
+ if command in fit.commandFits:
+ return
+
+ fit.commandFitDict[command.ID] = command
+
+ # this bit is required -- see GH issue # 83
+ eos.db.saveddata_session.flush()
+ eos.db.saveddata_session.refresh(command)
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ command = eos.db.getFit(self.commandFitID)
+
+ if not command:
+ # can't find the command fit, it must have been deleted. Just skip this undo
+ return True
+
+ from .fitRemoveCommand import FitRemoveCommandCommand
+ cmd = FitRemoveCommandCommand(self.fitID, self.commandFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddDrone.py b/gui/fitCommands/calc/fitAddDrone.py
new file mode 100644
index 000000000..a7b8ed41c
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddDrone.py
@@ -0,0 +1,49 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.drone import Drone
+pyfalog = Logger(__name__)
+
+
+class FitAddDroneCommand(wx.Command):
+ """"
+ from sFit.addDrone
+ """
+ def __init__(self, fitID, itemID, amount=1, replace=False):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.replace = replace # if this is false, we increment.
+ self.index = None
+
+ def Do(self):
+ pyfalog.debug("Adding {0} drones ({1}) to fit ID: {2}", self.amount, self.itemID, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ for d in fit.drones.find(item):
+ if d is not None and d.amountActive == 0 and d.amount < max(5, fit.extraAttributes["maxActiveDrones"]):
+ drone = d
+ break
+ else:
+ try:
+ drone = Drone(item)
+ except ValueError:
+ pyfalog.warning("Invalid drone: {}", item)
+ return False
+
+ if not drone.fits(fit):
+ return False
+ fit.drones.append(drone)
+
+ drone.amount += self.amount
+ eos.db.commit()
+ self.index = fit.drones.index(drone)
+ return True
+
+ def Undo(self):
+ from .fitRemoveDrone import FitRemoveDroneCommand # Avoid circular import
+ cmd = FitRemoveDroneCommand(self.fitID, self.index, self.amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitAddFighter.py b/gui/fitCommands/calc/fitAddFighter.py
new file mode 100644
index 000000000..42ca59be1
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddFighter.py
@@ -0,0 +1,48 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.fighter import Fighter
+pyfalog = Logger(__name__)
+
+
+class FitAddFighterCommand(wx.Command):
+ """"
+ from sFit.addFighter
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+
+ def Do(self):
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ fighter = Fighter(item)
+ except ValueError:
+ pyfalog.warning("Invalid fighter: {}", item)
+ return False
+
+ if not fighter.fits(fit):
+ return False
+
+ used = fit.getSlotsUsed(fighter.slot)
+ total = fit.getNumSlots(fighter.slot)
+
+ if used >= total:
+ fighter.active = False
+
+ fit.fighters.append(fighter)
+ self.new_index = fit.fighters.index(fighter)
+
+ eos.db.commit()
+
+ return True
+
+ def Undo(self):
+ from .fitRemoveFighter import FitRemoveFighterCommand # Avoid circular import
+ cmd = FitRemoveFighterCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddImplant.py b/gui/fitCommands/calc/fitAddImplant.py
new file mode 100644
index 000000000..4e3fd372c
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddImplant.py
@@ -0,0 +1,48 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.implant import Implant
+pyfalog = Logger(__name__)
+
+
+class FitAddImplantCommand(wx.Command):
+ """"
+ from sFit.addImplant
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Adding implant to fit ({0}) for item ID: {1}", self.fitID, self.itemID)
+
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager="attributes")
+
+ if next((x for x in fit.implants if x.itemID == self.itemID), None):
+ return False # already have item in list of implants
+
+ try:
+ implant = Implant(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", self.itemID)
+ return False
+
+ self.old_item = fit.implants.makeRoom(implant)
+ fit.implants.append(implant)
+ self.new_index = fit.implants.index(implant)
+ return True
+
+ def Undo(self):
+ if self.old_item:
+ # If we had an item in the slot previously, add it back.
+ cmd = FitAddImplantCommand(self.fitID, self.old_item)
+ cmd.Do()
+ return True
+
+ from .fitRemoveImplant import FitRemoveImplantCommand # Avoid circular import
+ cmd = FitRemoveImplantCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddModule.py b/gui/fitCommands/calc/fitAddModule.py
new file mode 100644
index 000000000..cd96b6d4f
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddModule.py
@@ -0,0 +1,83 @@
+import wx
+from eos.saveddata.module import Module, State
+import eos.db
+from logbook import Logger
+from service.fit import Fit
+pyfalog = Logger(__name__)
+
+
+class FitAddModuleCommand(wx.Command):
+ """"
+ Fitting command that appends a module to a fit using the first available slot. In the case of a Subsystem, it checks
+ if there is already a subsystem with the same slot, and runs the replace command instead.
+
+ from sFit.appendModule
+ """
+ def __init__(self, fitID, itemID, mutaplasmidID=None, baseID=None):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.mutaplasmidID = mutaplasmidID
+ self.baseID = baseID
+ self.new_position = None
+ self.change = None
+ self.replace_cmd = None
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fitID = self.fitID
+ itemID = self.itemID
+ fit = eos.db.getFit(fitID)
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+
+ bItem = eos.db.getItem(self.baseID) if self.baseID else None
+ mItem = next((x for x in bItem.mutaplasmids if x.ID == self.mutaplasmidID)) if self.mutaplasmidID else None
+
+ try:
+ self.module = Module(item, bItem, mItem)
+ except ValueError:
+ pyfalog.warning("Invalid module: {}", item)
+ return False
+
+ # If subsystem and we need to replace, run the replace command instead and bypass the rest of this command
+ if self.module.item.category.name == "Subsystem":
+ for mod in fit.modules:
+ if mod.getModifiedItemAttr("subSystemSlot") == self.module.getModifiedItemAttr("subSystemSlot"):
+ from .fitReplaceModule import FitReplaceModuleCommand
+ self.replace_cmd = FitReplaceModuleCommand(self.fitID, mod.modPosition, itemID)
+ return self.replace_cmd.Do()
+
+ if self.module.fits(fit):
+ pyfalog.debug("Adding {} as module for fit {}", self.module, fit)
+ self.module.owner = fit
+ numSlots = len(fit.modules)
+ fit.modules.append(self.module)
+ if self.module.isValidState(State.ACTIVE):
+ self.module.state = State.ACTIVE
+
+ # todo: fix these
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ # self.recalc(fit)
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ sFit.checkStates(fit, self.module)
+
+ # fit.fill()
+ eos.db.commit()
+
+ self.change = numSlots != len(fit.modules)
+ self.new_position = self.module.modPosition
+ else:
+ return False
+
+ return True
+
+ def Undo(self):
+ # We added a subsystem module, which actually ran the replace command. Run the undo for that guy instead
+ if self.replace_cmd:
+ return self.replace_cmd.Undo()
+
+ from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import
+ if self.new_position:
+ cmd = FitRemoveModuleCommand(self.fitID, [self.new_position])
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedDrone.py b/gui/fitCommands/calc/fitAddProjectedDrone.py
new file mode 100644
index 000000000..6c2ddf270
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedDrone.py
@@ -0,0 +1,45 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.drone import Drone
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedDroneCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.index = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID)
+
+ drone = None
+ for d in fit.projectedDrones.find(item):
+ if d is None or d.amountActive == d.amount or d.amount >= 5:
+ drone = d
+ break
+
+ if drone is None:
+ drone = Drone(item)
+ if not drone.item.isType("projected"):
+ return False
+ fit.projectedDrones.append(drone)
+
+ self.index = fit.projectedDrones.index(drone)
+ drone.amount += 1
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedDrone import FitRemoveProjectedDroneCommand # avoids circular import
+ cmd = FitRemoveProjectedDroneCommand(self.fitID, self.index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedEnv.py b/gui/fitCommands/calc/fitAddProjectedEnv.py
new file mode 100644
index 000000000..dc1a0443a
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedEnv.py
@@ -0,0 +1,51 @@
+import wx
+from eos.saveddata.module import Module, State
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedEnvCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ module = Module(item)
+ except ValueError:
+ return False
+
+ # todo: thing to check for existing environmental effects
+
+ module.state = State.ONLINE
+ if module.isExclusiveSystemEffect:
+ # if this is an exclusive system effect, we need to cache the old one. We make room for the new one here, which returns the old one
+ self.old_item = fit.projectedModules.makeRoom(module)
+
+ fit.projectedModules.append(module)
+ eos.db.commit()
+ self.new_index = fit.projectedModules.index(module)
+ return True
+
+ def Undo(self):
+ if self.old_item:
+ # If we had an item in the slot previously, add it back.
+ cmd = FitAddProjectedEnvCommand(self.fitID, self.old_item)
+ cmd.Do()
+ return True
+ from gui.fitCommands.calc.fitRemoveProjectedEnv import FitRemoveProjectedEnvCommand # avoids circular import
+ cmd = FitRemoveProjectedEnvCommand(self.fitID, self.itemID)
+ cmd.Do()
+
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedFighter.py b/gui/fitCommands/calc/fitAddProjectedFighter.py
new file mode 100644
index 000000000..17d50e14b
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedFighter.py
@@ -0,0 +1,40 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.fighter import Fighter
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedFighterCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ fighter = Fighter(item)
+ except ValueError:
+ return False
+
+ fit.projectedFighters.append(fighter)
+ # sometimes fighters aren't added because they aren't valid projectionable ones. todo: move that logic into here
+ if fighter in fit.projectedFighters:
+ eos.db.commit()
+ self.new_index = fit.projectedFighters.index(fighter)
+ return True
+ return False
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedFighter import FitRemoveProjectedFighterCommand # avoids circular import
+ cmd = FitRemoveProjectedFighterCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedFit.py b/gui/fitCommands/calc/fitAddProjectedFit.py
new file mode 100644
index 000000000..cd1e1a7f9
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedFit.py
@@ -0,0 +1,39 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedFitCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, projectedFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.projectedFitID = projectedFitID
+ self.new_index = None
+ self.old_item = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.projectedFitID)
+ fit = eos.db.getFit(self.fitID)
+ projectedFit = eos.db.getFit(self.projectedFitID)
+
+ if projectedFit is None or projectedFit in fit.projectedFits:
+ return False
+
+ fit.projectedFitDict[projectedFit.ID] = projectedFit
+
+ # this bit is required -- see GH issue # 83
+ eos.db.saveddata_session.flush()
+ eos.db.saveddata_session.refresh(projectedFit)
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedFit import FitRemoveProjectedFitCommand # avoids circular import
+ cmd = FitRemoveProjectedFitCommand(self.fitID, self.projectedFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitAddProjectedModule.py b/gui/fitCommands/calc/fitAddProjectedModule.py
new file mode 100644
index 000000000..6d2ad3f4f
--- /dev/null
+++ b/gui/fitCommands/calc/fitAddProjectedModule.py
@@ -0,0 +1,43 @@
+import wx
+import eos.db
+from logbook import Logger
+from eos.saveddata.module import Module, State
+pyfalog = Logger(__name__)
+
+
+class FitAddProjectedModuleCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.new_index = None
+
+ def Do(self):
+ pyfalog.debug("Projecting fit ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+ item = eos.db.getItem(self.itemID, eager=("attributes", "group.category"))
+
+ try:
+ module = Module(item)
+ if not module.item.isType("projected"):
+ return False
+ except ValueError:
+ return False
+
+ module.state = State.ACTIVE
+ if not module.canHaveState(module.state, fit):
+ module.state = State.OFFLINE
+ fit.projectedModules.append(module)
+
+ eos.db.commit()
+ self.new_index = fit.projectedModules.index(module)
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitRemoveProjectedModule import FitRemoveProjectedModuleCommand # avoids circular import
+ cmd = FitRemoveProjectedModuleCommand(self.fitID, self.new_index)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitChangeCargoQty.py b/gui/fitCommands/calc/fitChangeCargoQty.py
new file mode 100644
index 000000000..2ba30e085
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeCargoQty.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeCargoQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing cargo ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ cargo = fit.cargo[self.position]
+ self.old_amount = cargo.amount
+ cargo.amount = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeCargoQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeDroneQty.py b/gui/fitCommands/calc/fitChangeDroneQty.py
new file mode 100644
index 000000000..8f7e8e801
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeDroneQty.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ drone = fit.drones[self.position]
+ self.old_amount = drone.amount
+ drone.amount = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeDroneQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeDroneVariation.py b/gui/fitCommands/calc/fitChangeDroneVariation.py
new file mode 100644
index 000000000..7f277895a
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeDroneVariation.py
@@ -0,0 +1,39 @@
+import wx
+from logbook import Logger
+
+import eos.db
+pyfalog = Logger(__name__)
+
+
+class FitChangeDroneVariationCommand(wx.Command):
+ """"
+ Fitting command that changes an existing drone into another variation.
+ """
+ def __init__(self, fitID, position, itemID):
+ wx.Command.__init__(self, True, "Change Module")
+
+ self.fitID = fitID
+ self.itemID = itemID
+ self.position = position
+ self.old_drone = None
+
+ def Do(self):
+ return self.change_drone(self.fitID, self.position, self.itemID)
+
+ def Undo(self):
+ self.change_drone(self.fitID, self.position, self.old_drone)
+ return True
+
+ def change_drone(self, fitID, position, itemID):
+ fit = eos.db.getFit(self.fitID)
+ drone = fit.drones[self.position]
+
+ if itemID == drone.itemID:
+ return False
+
+ self.old_drone = drone.itemID
+
+ drone.changeType(itemID)
+ eos.db.commit()
+ # todo: ensure that, whatever type we send in, is actually a variation of the original drone. If not, return False
+ return True
diff --git a/gui/fitCommands/calc/fitChangeFighterQty.py b/gui/fitCommands/calc/fitChangeFighterQty.py
new file mode 100644
index 000000000..4cef20034
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeFighterQty.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeFighterQty(wx.Command):
+ """"
+ from sFit.changeActiveFighters
+ """
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ fighter = fit.fighters[self.position]
+ self.old_amount = fighter.amountActive
+ fighter.amountActive = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeFighterQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeImplantLocation.py b/gui/fitCommands/calc/fitChangeImplantLocation.py
new file mode 100644
index 000000000..fc1dec890
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeImplantLocation.py
@@ -0,0 +1,25 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeImplantLocation(wx.Command):
+ def __init__(self, fitID, source):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.source = source
+ self.old_source = None
+
+ def Do(self):
+ pyfalog.debug("Toggling implant source for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ self.old_source = fit.implantSource
+ fit.implantSource = self.source
+ eos.db.commit()
+ return True
+
+
+ def Undo(self):
+ cmd = FitChangeImplantLocation(self.fitID, self.old_source)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeProjectedDroneQty.py b/gui/fitCommands/calc/fitChangeProjectedDroneQty.py
new file mode 100644
index 000000000..3ca926c17
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeProjectedDroneQty.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeProjectedDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Changing active fighters ({0}) for fit ({1}) to amount: {2}", self.position, self.fitID, self.amount)
+ fit = eos.db.getFit(self.fitID)
+ drone = fit.projectedDrones[self.position]
+ self.old_amount = drone.amount
+ drone.amount = self.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeProjectedDroneQty(self.fitID, self.position, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeProjectedFitQty.py b/gui/fitCommands/calc/fitChangeProjectedFitQty.py
new file mode 100644
index 000000000..aa75e8491
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeProjectedFitQty.py
@@ -0,0 +1,36 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitChangeProjectedFitQty(wx.Command):
+ """"
+ from sFit.changeAmount
+ """
+ def __init__(self, fitID, pfitID, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.pfitID = pfitID
+ self.amount = amount
+ self.old_amount = None
+
+ def Do(self):
+ pfit = eos.db.getFit(self.pfitID)
+
+ if not pfit: # fit was deleted
+ return False
+
+ amount = min(20, max(1, self.amount)) # 1 <= a <= 20
+
+ projectionInfo = pfit.getProjectionInfo(self.fitID)
+ if projectionInfo:
+ self.old_amount = projectionInfo.amount
+ projectionInfo.amount = amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitChangeProjectedFitQty(self.fitID, self.pfitID, self.old_amount)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitChangeState.py b/gui/fitCommands/calc/fitChangeState.py
new file mode 100644
index 000000000..0b901e640
--- /dev/null
+++ b/gui/fitCommands/calc/fitChangeState.py
@@ -0,0 +1,76 @@
+import wx
+from logbook import Logger
+
+import gui.mainFrame
+from eos.saveddata.module import Module
+from service.fit import Fit
+import eos.db
+
+pyfalog = Logger(__name__)
+
+
+class FitChangeStatesCommand(wx.Command):
+ """
+ Fitting command that trys to change the state of modules in [positions]. We use the base module to determine the
+ state that we will try to apply for all modules.
+
+
+ """
+ def __init__(self, fitID, baseModPos, positions, click):
+ # todo: instead of modules, needs to be positions. Dead objects are a thing
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.baseModPos = baseModPos
+ self.positions = positions
+ self.click = click
+ self.changed = None
+ self.old_states = {}
+
+ def Do(self):
+ fit = eos.db.getFit(self.fitID)
+ sFit = Fit.getInstance()
+ baseMod = fit.modules[self.baseModPos]
+
+ # make sure positions only include non-empty positions
+ self.positions = [x for x in self.positions if not fit.modules[x].isEmpty]
+
+ for x in self.positions:
+ self.old_states[x] = fit.modules[x].state
+
+ proposedState = Module.getProposedState(baseMod, self.click)
+ pyfalog.debug("Attempting to change modules to {}", proposedState)
+
+ if proposedState != baseMod.state:
+ pyfalog.debug("Toggle {} state: {} for fit ID: {}", baseMod, proposedState, self.fitID)
+
+ self.changed = True
+ baseMod.state = proposedState
+ for i in [x for x in self.positions if x != self.baseModPos]: # dont consider base module position
+ mod = fit.modules[i]
+ p = Module.getProposedState(mod, self.click, proposedState)
+ mod.state = p
+ if p != mod.state:
+ pyfalog.debug("Toggle {} state: {} for fit ID: {}", mod, p, self.fitID)
+ self.changed = True
+
+ # if we haven't change the state (eg, overheat -> overheat), simply fail the command
+ if self.changed:
+ eos.db.commit()
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ # self.recalc(fit)
+ # # Then, check states of all modules and change where needed. This will recalc if needed
+ sFit.checkStates(fit, baseMod)
+ # self.checkStates(fit, base)
+ return True
+ return False
+
+ def Undo(self):
+ # todo: some sanity checking to make sure that we are applying state back to the same modules?
+ fit = self.sFit.getFit(self.fitID)
+ for k, v in self.old_states.items():
+ mod = fit.modules[k]
+ pyfalog.debug("Reverting {} to state {} for fit ID", mod, v, self.fitID)
+ mod.state = v
+ return True
diff --git a/gui/fitCommands/calc/fitCloneModule.py b/gui/fitCommands/calc/fitCloneModule.py
new file mode 100644
index 000000000..519b653ef
--- /dev/null
+++ b/gui/fitCommands/calc/fitCloneModule.py
@@ -0,0 +1,44 @@
+import wx
+import eos.db
+from logbook import Logger
+import copy
+pyfalog = Logger(__name__)
+
+
+class FitCloneModuleCommand(wx.Command):
+ """
+ Clone a module from src to dst
+ This will overwrite dst! Checking for empty module must be
+ done at a higher level
+
+ from sFit.cloneModule
+ """
+ def __init__(self, fitID, src, dst):
+ wx.Command.__init__(self, True, "Module Clone")
+ self.fitID = fitID
+ self.src = src
+ self.dst = dst
+
+ def Do(self):
+ fit = eos.db.getFit(self.fitID)
+ # Gather modules
+ srcMod = fit.modules[self.src]
+ dstMod = fit.modules[self.dst] # should be a placeholder module
+
+ new = copy.deepcopy(srcMod)
+ new.owner = fit
+ if new.fits(fit):
+ pyfalog.debug("Cloning {} from source {} to destination {} for fit ID {}", srcMod, self.src, self.dst, self.fitID)
+ # insert copy if module meets hardpoint restrictions
+ fit.modules.remove(dstMod)
+ fit.modules.insert(self.dst, new)
+
+ eos.db.commit()
+ return True
+ return False
+
+ def Undo(self):
+ from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import
+ cmd = FitRemoveModuleCommand(self.fitID, [self.dst])
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitImportMutatedModule.py b/gui/fitCommands/calc/fitImportMutatedModule.py
new file mode 100644
index 000000000..3a28c68ea
--- /dev/null
+++ b/gui/fitCommands/calc/fitImportMutatedModule.py
@@ -0,0 +1,93 @@
+import wx
+from eos.saveddata.module import Module, State
+import eos.db
+from eos.db.gamedata.queries import getDynamicItem
+from logbook import Logger
+from service.fit import Fit
+pyfalog = Logger(__name__)
+
+
+class FitImportMutatedCommand(wx.Command):
+ """"
+ Fitting command that takes info about mutated module, composes it and adds it to a fit
+ """
+ def __init__(self, fitID, baseItem, mutaItem, attrMap):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.baseItem = baseItem
+ self.mutaItem = mutaItem
+ self.attrMap = attrMap
+ self.new_position = None
+ self.change = None
+ self.replace_cmd = None
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fitID = self.fitID
+ fit = eos.db.getFit(fitID)
+
+ if self.baseItem is None:
+ pyfalog.warning("Unable to build non-mutated module: no base item to build from")
+ return False
+
+ try:
+ mutaTypeID = self.mutaItem.ID
+ except AttributeError:
+ mutaplasmid = None
+ else:
+ mutaplasmid = getDynamicItem(mutaTypeID)
+ # Try to build simple item even though no mutaplasmid found
+ if mutaplasmid is None:
+ try:
+ module = Module(self.baseItem)
+ except ValueError:
+ pyfalog.warning("Unable to build non-mutated module: {}", self.baseItem)
+ return False
+ # Build mutated module otherwise
+ else:
+ try:
+ module = Module(mutaplasmid.resultingItem, self.baseItem, mutaplasmid)
+ except ValueError:
+ pyfalog.warning("Unable to build mutated module: {} {}", self.baseItem, self.mutaItem)
+ return False
+ else:
+ for attrID, mutator in module.mutators.items():
+ if attrID in self.attrMap:
+ mutator.value = self.attrMap[attrID]
+
+
+ # this is essentially the same as the FitAddModule command. possibly look into centralizing this functionality somewhere?
+ if module.fits(fit):
+ pyfalog.debug("Adding {} as module for fit {}", module, fit)
+ module.owner = fit
+ numSlots = len(fit.modules)
+ fit.modules.append(module)
+ if module.isValidState(State.ACTIVE):
+ module.state = State.ACTIVE
+
+ # todo: fix these
+ # As some items may affect state-limiting attributes of the ship, calculate new attributes first
+ # self.recalc(fit)
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ sFit.checkStates(fit, module)
+
+ # fit.fill()
+ eos.db.commit()
+
+ self.change = numSlots != len(fit.modules)
+ self.new_position = module.modPosition
+ else:
+ return False
+
+ return True
+
+ def Undo(self):
+ # We added a subsystem module, which actually ran the replace command. Run the undo for that guy instead
+ if self.replace_cmd:
+ return self.replace_cmd.Undo()
+
+ from .fitRemoveModule import FitRemoveModuleCommand # Avoid circular import
+ if self.new_position is not None:
+ cmd = FitRemoveModuleCommand(self.fitID, [self.new_position])
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveBooster.py b/gui/fitCommands/calc/fitRemoveBooster.py
new file mode 100644
index 000000000..14733c93b
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveBooster.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveBoosterCommand(wx.Command):
+ """"
+ from sFit.removeBooster
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Implant remove")
+ self.fitID = fitID
+ self.position = position
+ self.old = None
+
+ def Do(self):
+ pyfalog.debug("Removing booster from position ({0}) for fit ID: {1}", self.position, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ booster = fit.boosters[self.position]
+ self.old = booster.itemID
+ fit.boosters.remove(booster)
+ return True
+
+ def Undo(self):
+ from .fitAddBooster import FitAddBoosterCommand # Avoid circular import
+ cmd = FitAddBoosterCommand(self.fitID, self.old)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveCargo.py b/gui/fitCommands/calc/fitRemoveCargo.py
new file mode 100644
index 000000000..779c7c757
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveCargo.py
@@ -0,0 +1,47 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveCargoCommand(wx.Command):
+ """"
+ Fitting command that sets the amount for an item within the cargo.
+
+ from sFit.removeCargo
+ """
+ def __init__(self, fitID, itemID, amount=1, stack=False):
+ wx.Command.__init__(self, True, "Cargo remove")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.stack = stack # remove entire stack
+ self.amount = amount # remove x amount. If this goes over amount, removes stack
+ self.old_amount = None
+
+ def Do(self):
+ pyfalog.debug("Removing cargo {0} (x{1}) for fit {2}", self.itemID, self.amount, self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ cargo = next((x for x in fit.cargo if x.itemID == self.itemID), None)
+
+ if cargo is None:
+ return False
+
+ self.old_amount = cargo.amount
+
+ if self.amount >= cargo.amount:
+ self.stack = True # set to full stack, this allows easier logic in the Undo function
+
+ if self.stack or self.amount >= cargo.amount:
+ fit.cargo.remove(cargo)
+ eos.db.commit()
+ return True
+
+ cargo.amount -= self.amount
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddCargo import FitAddCargoCommand # Avoid circular import
+ cmd = FitAddCargoCommand(self.fitID, self.itemID, self.old_amount, True)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveCommand.py b/gui/fitCommands/calc/fitRemoveCommand.py
new file mode 100644
index 000000000..29e681b60
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveCommand.py
@@ -0,0 +1,37 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveCommandCommand(wx.Command): # well that's an unfortunate name
+ """"
+ from sFit.removeCommand
+ """
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ pyfalog.debug("Removing command projection from fit ({0}) for: {1}", self.fitID, self.commandFitID)
+ fit = eos.db.getFit(self.fitID)
+ command = eos.db.getFit(self.commandFitID)
+ if not command:
+ return False
+ del fit.commandFitDict[command.ID]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ command = eos.db.getFit(self.commandFitID)
+
+ if not command:
+ # can't find the command fit, it must have been deleted. Just skip this undo
+ return True
+
+ from .fitAddCommand import FitAddCommandCommand
+ cmd = FitAddCommandCommand(self.fitID, self.commandFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveDrone.py b/gui/fitCommands/calc/fitRemoveDrone.py
new file mode 100644
index 000000000..4e1fb0a6a
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveDrone.py
@@ -0,0 +1,43 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveDroneCommand(wx.Command):
+ """"
+ from sFit.addDrone
+ """
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Drone add")
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount # add x amount. If this goes over amount, removes stack
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing {0} drones for fit ID: {1}", self.amount, self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ d = fit.drones[self.position]
+ d.amount -= self.amount
+ if d.amountActive > 0:
+ d.amountActive -= self.amount
+
+ if d.amount == 0:
+ self.removed_item = d.itemID
+ del fit.drones[self.position]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ if self.removed_item:
+ from .fitAddDrone import FitAddDroneCommand # Avoid circular import
+ cmd = FitAddDroneCommand(self.fitID, self.removed_item, self.amount)
+ return cmd.Do()
+ else:
+ fit = eos.db.getFit(self.fitID)
+ d = fit.drones[self.position]
+ d.amount += self.amount
+ eos.db.commit()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveFighter.py b/gui/fitCommands/calc/fitRemoveFighter.py
new file mode 100644
index 000000000..da186bd48
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveFighter.py
@@ -0,0 +1,34 @@
+import wx
+from logbook import Logger
+
+import eos.db
+
+pyfalog = Logger(__name__)
+
+
+class FitRemoveFighterCommand(wx.Command):
+ """"
+ Fitting command that removes a module at a specified positions
+
+ from sFit.removeFighter
+ """
+ def __init__(self, fitID: int, position: int):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.change = None
+ self.removed_item = None
+
+ def Do(self):
+ fitID = self.fitID
+ fit = eos.db.getFit(fitID)
+ f = fit.fighters[self.position]
+ fit.fighters.remove(f)
+ self.removed_item = f.itemID
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddFighter import FitAddFighterCommand # avoids circular import
+ cmd = FitAddFighterCommand(self.fitID, self.removed_item)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitRemoveImplant.py b/gui/fitCommands/calc/fitRemoveImplant.py
new file mode 100644
index 000000000..d0f8101cd
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveImplant.py
@@ -0,0 +1,32 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveImplantCommand(wx.Command):
+ """"
+ Fitting command that sets the amount for an item within the cargo.
+
+ from sFit.removeImplant
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Implant remove")
+ self.fitID = fitID
+ self.position = position
+ self.old_implant = None
+
+ def Do(self):
+ pyfalog.debug("Removing implant from position ({0}) for fit ID: {1}", self.position, self.fitID)
+
+ fit = eos.db.getFit(self.fitID)
+ implant = fit.implants[self.position]
+ self.old_implant = implant.itemID
+ fit.implants.remove(implant)
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddImplant import FitAddImplantCommand # Avoid circular import
+ cmd = FitAddImplantCommand(self.fitID, self.old_implant)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveModule.py b/gui/fitCommands/calc/fitRemoveModule.py
new file mode 100644
index 000000000..5134f2f1b
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveModule.py
@@ -0,0 +1,59 @@
+import wx
+
+from gui.fitCommands.helpers import ModuleInfoCache
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveModuleCommand(wx.Command):
+ """"
+ Fitting command that removes a module at a specified positions
+
+ from sFit.removeModule
+ """
+ def __init__(self, fitID: int, positions: list = None):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.positions = positions
+ self.modCache = []
+ self.change = None
+
+ def Do(self):
+ fitID = self.fitID
+ fit = eos.db.getFit(fitID)
+
+ pyfalog.debug("Removing module from position ({0}) for fit ID: {1}", self.positions, fitID)
+
+ for x in self.positions:
+ mod = fit.modules[x]
+ if not mod.isEmpty:
+ pyfalog.debug(" -- Removing {}", mod)
+ self.modCache.append(ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID))
+ fit.modules.toDummy(x)
+
+ # if no modules have changes, skip command
+ if not len(self.modCache) > 0:
+ return False
+
+ numSlots = len(fit.modules)
+ # todo: determine if we need to do this still
+ # self.recalc(fit)
+ # self.checkStates(fit, None)
+ # fit.fill()
+ eos.db.commit()
+ self.slotsChanged = numSlots != len(fit.modules)
+ return True
+
+ def Undo(self):
+ pyfalog.debug("Reapplying {} removed module(s) for {}", len(self.modCache), self.fitID)
+
+ from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand # avoids circular import
+ for mod in self.modCache:
+ pyfalog.debug(" -- {}", mod)
+ # todo, send the state and charge?
+ cmd = FitReplaceModuleCommand(self.fitID, mod.modPosition, mod.itemID)
+ cmd.Do()
+ cmd.module.state = mod.state
+ cmd.module.charge = mod.charge
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedDrone.py b/gui/fitCommands/calc/fitRemoveProjectedDrone.py
new file mode 100644
index 000000000..9d70ade2e
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedDrone.py
@@ -0,0 +1,42 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular projected modules, besides the undo
+class FitRemoveProjectedDroneCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, position, stack=False):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.removed_item = None
+ self.stack = stack
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position)
+ fit = eos.db.getFit(self.fitID)
+
+ drone = fit.projectedDrones[self.position]
+ if self.stack:
+ fit.projectedDrones.remove(drone)
+ else:
+ if drone.amount > 1:
+ drone.amount -= 1
+ else:
+ fit.projectedDrones.remove(drone)
+
+ self.drone_item = drone.itemID
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedDrone import FitAddProjectedDroneCommand
+ cmd = FitAddProjectedDroneCommand(self.fitID, self.drone_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedEnv.py b/gui/fitCommands/calc/fitRemoveProjectedEnv.py
new file mode 100644
index 000000000..61b6ee939
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedEnv.py
@@ -0,0 +1,35 @@
+import wx
+import eos.db
+from logbook import Logger
+from .fitRemoveProjectedModule import FitRemoveProjectedModuleCommand
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular rpojected modules, besides the undo
+class FitRemoveProjectedEnvCommand(FitRemoveProjectedModuleCommand):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.itemID = itemID
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.itemID)
+ fit = eos.db.getFit(self.fitID)
+
+ item = next((x for x in fit.projectedModules if x.itemID == self.itemID), None)
+ self.removed_item = item.itemID
+ fit.projectedModules.remove(item)
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedEnv import FitAddProjectedEnvCommand
+ cmd = FitAddProjectedEnvCommand(self.fitID, self.removed_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedFighter.py b/gui/fitCommands/calc/fitRemoveProjectedFighter.py
new file mode 100644
index 000000000..d3550b43e
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedFighter.py
@@ -0,0 +1,34 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular rpojected modules, besides the undo
+class FitRemoveProjectedFighterCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position)
+ fit = eos.db.getFit(self.fitID)
+
+ fighter = fit.projectedFighters[self.position]
+ fit.projectedFighters.remove(fighter)
+ self.removed_item = fighter.itemID
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedFighter import FitAddProjectedFighterCommand
+ cmd = FitAddProjectedFighterCommand(self.fitID, self.removed_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedFit.py b/gui/fitCommands/calc/fitRemoveProjectedFit.py
new file mode 100644
index 000000000..66d62ab10
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedFit.py
@@ -0,0 +1,37 @@
+import wx
+import eos.db
+from logbook import Logger
+from .fitRemoveProjectedModule import FitRemoveProjectedModuleCommand
+pyfalog = Logger(__name__)
+
+
+# this has the same exact definition that regular rpojected modules, besides the undo
+class FitRemoveProjectedFitCommand(FitRemoveProjectedModuleCommand):
+ """"
+ from sFit.project
+ """
+
+ def __init__(self, fitID, projectedFitID):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.projectedFitID = projectedFitID
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.projectedFitID)
+ fit = eos.db.getFit(self.fitID)
+ projectedFit = eos.db.getFit(self.projectedFitID)
+
+ if projectedFit is None:
+ return False
+
+ del fit.projectedFitDict[projectedFit.ID]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ # todo: figure out if I need to return false here if the fit doesn't return true (means it was deleted)
+ from gui.fitCommands.calc.fitAddProjectedFit import FitAddProjectedFitCommand
+ cmd = FitAddProjectedFitCommand(self.fitID, self.projectedFitID)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRemoveProjectedModule.py b/gui/fitCommands/calc/fitRemoveProjectedModule.py
new file mode 100644
index 000000000..46f8dadba
--- /dev/null
+++ b/gui/fitCommands/calc/fitRemoveProjectedModule.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitRemoveProjectedModuleCommand(wx.Command):
+ """"
+ from sFit.project
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True)
+ self.fitID = fitID
+ self.position = position
+ self.removed_item = None
+
+ def Do(self):
+ pyfalog.debug("Removing ({0}) onto: {1}", self.fitID, self.position)
+ fit = eos.db.getFit(self.fitID)
+ self.removed_item = fit.projectedModules[self.position].itemID
+ del fit.projectedModules[self.position]
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ from gui.fitCommands.calc.fitAddProjectedModule import FitAddProjectedModuleCommand
+ cmd = FitAddProjectedModuleCommand(self.fitID, self.removed_item)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitRename.py b/gui/fitCommands/calc/fitRename.py
new file mode 100644
index 000000000..38a72c519
--- /dev/null
+++ b/gui/fitCommands/calc/fitRename.py
@@ -0,0 +1,26 @@
+import wx
+from logbook import Logger
+
+import eos.db
+
+pyfalog = Logger(__name__)
+
+
+class FitRenameCommand(wx.Command):
+ def __init__(self, fitID, newName):
+ wx.Command.__init__(self, True, "FitRename")
+ self.fitID = fitID
+ self.newName = newName
+ self.oldName = None
+
+ def Do(self):
+ pyfalog.debug("Renaming fit ({0}) to: {1}", self.fitID, self.newName)
+ fit = eos.db.getFit(self.fitID)
+ self.oldName = fit.name
+ fit.name = self.newName
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitRenameCommand(self.fitID, self.oldName)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitReplaceModule.py b/gui/fitCommands/calc/fitReplaceModule.py
new file mode 100644
index 000000000..286bd9451
--- /dev/null
+++ b/gui/fitCommands/calc/fitReplaceModule.py
@@ -0,0 +1,90 @@
+import wx
+from logbook import Logger
+
+import eos.db
+from eos.saveddata.module import Module, State
+from gui.fitCommands.helpers import ModuleInfoCache
+
+pyfalog = Logger(__name__)
+
+
+class FitReplaceModuleCommand(wx.Command):
+ """"
+ Fitting command that changes an existing module into another.
+
+ from sFit.changeModule
+ """
+ def __init__(self, fitID, position, itemID):
+ wx.Command.__init__(self, True, "Change Module")
+ self.fitID = fitID
+ self.itemID = itemID
+ self.position = position
+ self.module = None # the module version of itemID
+ self.old_module = None
+
+ def Do(self):
+ fit = eos.db.getFit(self.fitID)
+
+ mod = fit.modules[self.position]
+ if not mod.isEmpty:
+ self.old_module = ModuleInfoCache(mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID,
+ mod.mutaplasmidID)
+
+ return self.change_module(self.fitID, self.position, self.itemID)
+
+ def Undo(self):
+ if self.old_module is None:
+ fit = eos.db.getFit(self.fitID)
+ fit.modules.toDummy(self.position)
+ return True
+ self.change_module(self.fitID, self.position, self.old_module.itemID)
+ self.module.state = self.old_module.state
+ self.module.charge = self.old_module.charge
+ return True
+
+ def change_module(self, fitID, position, itemID):
+ fit = eos.db.getFit(fitID)
+
+ # We're trying to add a charge to a slot, which won't work. Instead, try to add the charge to the module in that slot.
+ # todo: evaluate if this is still a thing
+ # actually, this seems like it should be handled higher up...
+ #
+ # if self.isAmmo(itemID):
+ # module = fit.modules[self.position]
+ # if not module.isEmpty:
+ # self.setAmmo(fitID, itemID, [module])
+ # return True
+
+ pyfalog.debug("Changing position of module from position ({0}) for fit ID: {1}", self.position, fitID)
+
+ item = eos.db.getItem(itemID, eager=("attributes", "group.category"))
+ mod = fit.modules[self.position]
+
+ try:
+ self.module = Module(item)
+ except ValueError:
+ pyfalog.warning("Invalid item: {0}", itemID)
+ return False
+
+ if self.module.slot != mod.slot:
+ return False
+
+ # Dummy it out in case the next bit fails
+ fit.modules.toDummy(self.position)
+
+ if self.module.fits(fit):
+ self.module.owner = fit
+ fit.modules.toModule(self.position, self.module)
+ if self.module.isValidState(State.ACTIVE):
+ self.module.state = State.ACTIVE
+
+ if self.old_module and self.old_module.charge and self.module.isValidCharge(self.old_module.charge):
+ self.module.charge = self.old_module.charge
+
+ # Then, check states of all modules and change where needed. This will recalc if needed
+ # self.checkStates(fit, m)
+
+ # fit.fill()
+ eos.db.commit()
+ return True
+ return False
diff --git a/gui/fitCommands/calc/fitSetCharge.py b/gui/fitCommands/calc/fitSetCharge.py
new file mode 100644
index 000000000..91a4abe8d
--- /dev/null
+++ b/gui/fitCommands/calc/fitSetCharge.py
@@ -0,0 +1,48 @@
+import wx
+from logbook import Logger
+
+import eos.db
+import gui.mainFrame
+from service.fit import Fit
+
+pyfalog = Logger(__name__)
+
+
+class FitSetChargeCommand(wx.Command):
+ def __init__(self, fitID, positions, chargeID=None, projected=False):
+ # todo: determine if this command really should be used with a group of modules, or a simple per module basis
+ wx.Command.__init__(self, True, "Module Charge Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.chargeID = chargeID
+ self.positions = positions
+ self.projected = projected
+ self.cache = None
+
+ def Do(self):
+ return self.__setAmmo(self.positions, self.chargeID)
+
+ def Undo(self):
+ for position, chargeID in self.cache.items():
+ self.__setAmmo([position], chargeID)
+ return True
+
+ def __setAmmo(self, positions, chargeID):
+ fit = eos.db.getFit(self.fitID)
+ source = fit.modules if not self.projected else fit.projectedModules
+ self.cache = {source[i].modPosition: source[i].chargeID for i in positions}
+ ammo = eos.db.getItem(chargeID) if chargeID else None
+
+ if ammo is not None and not ammo.isCharge:
+ return False
+ result = False
+
+ for pos in positions:
+ mod = source[pos]
+ if not mod.isEmpty and mod.isValidCharge(ammo):
+ pyfalog.debug("Set ammo {} for {} on fit {}", ammo, mod, self.fitID)
+ result = True
+ mod.charge = ammo
+ eos.db.commit()
+ return result
diff --git a/gui/fitCommands/calc/fitSetMode.py b/gui/fitCommands/calc/fitSetMode.py
new file mode 100644
index 000000000..750be78dd
--- /dev/null
+++ b/gui/fitCommands/calc/fitSetMode.py
@@ -0,0 +1,28 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitSetModeCommand(wx.Command):
+ """"
+ from sFit.setMode
+ """
+ def __init__(self, fitID, mode):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.mode = mode
+ self.old_mode = None
+
+ def Do(self):
+ pyfalog.debug("Set mode for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ self.old_mode = fit.mode
+ fit.mode = self.mode
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitSetModeCommand(self.fitID, self.old_mode)
+ cmd.Do()
+ return True
diff --git a/gui/fitCommands/calc/fitSwapModule.py b/gui/fitCommands/calc/fitSwapModule.py
new file mode 100644
index 000000000..38ed8d4eb
--- /dev/null
+++ b/gui/fitCommands/calc/fitSwapModule.py
@@ -0,0 +1,38 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitSwapModuleCommand(wx.Command):
+ """"
+ from sFit.swapModules
+ """
+ def __init__(self, fitID, src, dst):
+ wx.Command.__init__(self, True, "Module Swap")
+ self.fitID = fitID
+ self.src = src
+ self.dst = dst
+
+ def Do(self):
+ self.__swap(self.fitID, self.src, self.dst)
+ return True
+
+ def Undo(self):
+ self.__swap(self.fitID, self.dst, self.src)
+ return True
+
+ def __swap(self, fitID, src, dst):
+ pyfalog.debug("Swapping modules from source ({0}) to destination ({1}) for fit ID: {1}", src, dst, fitID)
+ fit = eos.db.getFit(fitID)
+ # Gather modules
+ srcMod = fit.modules[src]
+ dstMod = fit.modules[dst]
+
+ # To swap, we simply remove mod and insert at destination.
+ fit.modules.remove(srcMod)
+ fit.modules.insert(dst, srcMod)
+ fit.modules.remove(dstMod)
+ fit.modules.insert(src, dstMod)
+
+ eos.db.commit()
diff --git a/gui/fitCommands/calc/fitToggleBooster.py b/gui/fitCommands/calc/fitToggleBooster.py
new file mode 100644
index 000000000..53b56487b
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleBooster.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleBoosterCommand(wx.Command):
+ """"
+ from sFit.toggleBooster
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling booster for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ booster = fit.boosters[self.position]
+ booster.active = not booster.active
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleBoosterCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleCommand.py b/gui/fitCommands/calc/fitToggleCommand.py
new file mode 100644
index 000000000..c7d5a43ad
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleCommand.py
@@ -0,0 +1,36 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleCommandCommand(wx.Command):
+ """"
+ from sFit.toggleCommandFit
+ """
+ def __init__(self, fitID, commandFitId):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.commandFitID = commandFitId
+
+ def Do(self):
+ pyfalog.debug("Toggle command fit ({0}) for: {1}", self.commandFitID, self.fitID)
+ commandFit = eos.db.getFit(self.commandFitID)
+
+ if not commandFit:
+ pyfalog.debug(" -- Command fit not found, deleted?")
+ return False
+
+ commandInfo = commandFit.getCommandInfo(self.fitID)
+
+ if not commandInfo:
+ pyfalog.debug(" -- Command fit info not found, deleted?")
+ return False
+
+ commandInfo.active = not commandInfo.active
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleCommandCommand(self.fitID, self.commandFitID)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleDrone.py b/gui/fitCommands/calc/fitToggleDrone.py
new file mode 100644
index 000000000..b281be700
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleDrone.py
@@ -0,0 +1,30 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleDroneCommand(wx.Command):
+ """"
+ from sFit.toggleDrone
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling drones for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ d = fit.drones[self.position]
+ if d.amount == d.amountActive:
+ d.amountActive = 0
+ else:
+ d.amountActive = d.amount
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleDroneCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleFighter.py b/gui/fitCommands/calc/fitToggleFighter.py
new file mode 100644
index 000000000..589b4bdd2
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleFighter.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleFighterCommand(wx.Command):
+ """"
+ from sFit.toggleFighter
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling fighters for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ f = fit.fighters[self.position]
+ f.active = not f.active
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleFighterCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/calc/fitToggleImplant.py b/gui/fitCommands/calc/fitToggleImplant.py
new file mode 100644
index 000000000..c004c297d
--- /dev/null
+++ b/gui/fitCommands/calc/fitToggleImplant.py
@@ -0,0 +1,27 @@
+import wx
+import eos.db
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class FitToggleImplantCommand(wx.Command):
+ """"
+ from sFit.toggleImplant
+ """
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Cargo add")
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ pyfalog.debug("Toggling implant for fit ID: {0}", self.fitID)
+ fit = eos.db.getFit(self.fitID)
+ implant = fit.implants[self.position]
+ implant.active = not implant.active
+
+ eos.db.commit()
+ return True
+
+ def Undo(self):
+ cmd = FitToggleImplantCommand(self.fitID, self.position)
+ return cmd.Do()
diff --git a/gui/fitCommands/guiAddBooster.py b/gui/fitCommands/guiAddBooster.py
new file mode 100644
index 000000000..fc732d857
--- /dev/null
+++ b/gui/fitCommands/guiAddBooster.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddBooster import FitAddBoosterCommand
+
+
+class GuiAddBoosterCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Booster Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddBoosterCommand(self.fitID, self.itemID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddCargo.py b/gui/fitCommands/guiAddCargo.py
new file mode 100644
index 000000000..4ff0f3447
--- /dev/null
+++ b/gui/fitCommands/guiAddCargo.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddCargo import FitAddCargoCommand
+
+
+class GuiAddCargoCommand(wx.Command):
+ def __init__(self, fitID, itemID, amount=1, replace=False):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.amount = amount
+ self.replace = replace
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddCargoCommand(self.fitID, self.itemID, self.amount, self.replace)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddCharge.py b/gui/fitCommands/guiAddCharge.py
new file mode 100644
index 000000000..ae2af59aa
--- /dev/null
+++ b/gui/fitCommands/guiAddCharge.py
@@ -0,0 +1,32 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitSetCharge import FitSetChargeCommand
+
+
+class GuiModuleAddChargeCommand(wx.Command):
+ def __init__(self, fitID, itemID, modules):
+ wx.Command.__init__(self, True, "Module Charge Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.positions = [mod.modPosition for mod in modules]
+ self.projected = modules[0].isProjected
+
+ def Do(self):
+ if self.internal_history.Submit(FitSetChargeCommand(self.fitID, self.positions, self.itemID, self.projected)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddCommand.py b/gui/fitCommands/guiAddCommand.py
new file mode 100644
index 000000000..06cfcbe98
--- /dev/null
+++ b/gui/fitCommands/guiAddCommand.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddCommand import FitAddCommandCommand
+
+
+class GuiAddCommandCommand(wx.Command):
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddCommandCommand(self.fitID, self.commandFitID)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ self.sFit.recalc(self.fitID)
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddDrone.py b/gui/fitCommands/guiAddDrone.py
new file mode 100644
index 000000000..6549d1e21
--- /dev/null
+++ b/gui/fitCommands/guiAddDrone.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddDrone import FitAddDroneCommand
+
+
+class GuiAddDroneCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ cmd = FitAddDroneCommand(self.fitID, self.itemID)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddFighter.py b/gui/fitCommands/guiAddFighter.py
new file mode 100644
index 000000000..01024e706
--- /dev/null
+++ b/gui/fitCommands/guiAddFighter.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddFighter import FitAddFighterCommand
+
+
+class GuiAddFighterCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddFighterCommand(self.fitID, self.itemID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddImplant.py b/gui/fitCommands/guiAddImplant.py
new file mode 100644
index 000000000..79b75f014
--- /dev/null
+++ b/gui/fitCommands/guiAddImplant.py
@@ -0,0 +1,32 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from eos.saveddata.fit import ImplantLocation
+from .calc.fitAddImplant import FitAddImplantCommand
+from .calc.fitChangeImplantLocation import FitChangeImplantLocation
+
+
+class GuiAddImplantCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Implant Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitAddImplantCommand(self.fitID, self.itemID)) and self.internal_history.Submit(FitChangeImplantLocation(self.fitID, ImplantLocation.FIT)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiAddModule.py b/gui/fitCommands/guiAddModule.py
new file mode 100644
index 000000000..f826566f4
--- /dev/null
+++ b/gui/fitCommands/guiAddModule.py
@@ -0,0 +1,68 @@
+import wx
+import eos.db
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddModule import FitAddModuleCommand
+from .calc.fitReplaceModule import FitReplaceModuleCommand
+from .calc.fitSetCharge import FitSetChargeCommand
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiModuleAddCommand(wx.Command):
+ def __init__(self, fitID, itemID, position=None):
+ """
+ Handles adding an item, usually a module, to the Fitting Window.
+
+ :param fitID: The fit ID that we are modifying
+ :param itemID: The item that is to be added to the Fitting View. If this turns out to be a charge, we attempt to
+ set the charge on the underlying module (requires position)
+ :param position: Optional. The position in fit.modules that we are attempting to set the item to
+ """
+ wx.Command.__init__(self, True, "Module Add: {}".format(itemID))
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.internal_history = wx.CommandProcessor()
+ self.position = position
+ self.old_mod = None
+
+ def Do(self):
+ pyfalog.debug("{} Do()".format(self))
+ success = False
+ item = eos.db.getItem(self.itemID)
+ if item.isCharge and self.position is not None:
+ pyfalog.debug("Trying to add a charge")
+ success = self.internal_history.Submit(FitSetChargeCommand(self.fitID, [self.position], self.itemID))
+ if not success:
+ pyfalog.debug(" Failed")
+ return False # if it's a charge item and this failed, nothing more we can try.
+ # if we have a position set, try to apply the module to that position
+ elif self.position is not None:
+ pyfalog.debug("Trying to add a module to a specific position")
+ success = self.internal_history.Submit(FitReplaceModuleCommand(self.fitID, self.position, self.itemID))
+ if not success:
+ pyfalog.debug(" Failed")
+ # something went wrong with trying to fit the module into specific location, attempt to append it
+ self.position = None
+
+ # if we're not trying to set module to a position, simply append
+ if self.position is None:
+ pyfalog.debug("Trying to append a module")
+ success = self.internal_history.Submit(FitAddModuleCommand(self.fitID, self.itemID))
+
+ if success:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=self.itemID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=self.itemID))
+ return True
diff --git a/gui/fitCommands/guiAddProjected.py b/gui/fitCommands/guiAddProjected.py
new file mode 100644
index 000000000..b2114e111
--- /dev/null
+++ b/gui/fitCommands/guiAddProjected.py
@@ -0,0 +1,56 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from eos.saveddata.module import Module
+from .calc.fitAddProjectedModule import FitAddProjectedModuleCommand
+from .calc.fitAddProjectedEnv import FitAddProjectedEnvCommand
+from .calc.fitAddProjectedFit import FitAddProjectedFitCommand
+from .calc.fitAddProjectedFighter import FitAddProjectedFighterCommand
+from .calc.fitAddProjectedDrone import FitAddProjectedDroneCommand
+from logbook import Logger
+import eos.db
+pyfalog = Logger(__name__)
+
+
+class GuiAddProjectedCommand(wx.Command):
+ def __init__(self, fitID, id, type='item'):
+ wx.Command.__init__(self, True, "Projected Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.id = id
+ self.type = type
+
+ def Do(self):
+ result = False
+ # since we can project various types, we need to switch of the fit command. We can't do this switch easily in
+ # the fit command since each type might have a different kind of undo, easier to split it out
+ if self.type == 'item':
+ item = eos.db.getItem(self.id, eager=("attributes", "group.category"))
+
+ if item.category.name == "Drone":
+ result = self.internal_history.Submit(FitAddProjectedDroneCommand(self.fitID, self.id))
+ elif item.category.name == "Fighter":
+ result = self.internal_history.Submit(FitAddProjectedFighterCommand(self.fitID, self.id))
+ elif item.group.name in Module.SYSTEM_GROUPS:
+ result = self.internal_history.Submit(FitAddProjectedEnvCommand(self.fitID, self.id))
+ else:
+ result = self.internal_history.Submit(FitAddProjectedModuleCommand(self.fitID, self.id))
+ elif self.type == 'fit':
+ result = self.internal_history.Submit(FitAddProjectedFitCommand(self.fitID, self.id))
+
+ if result:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiCargoToModule.py b/gui/fitCommands/guiCargoToModule.py
new file mode 100644
index 000000000..a2583e47c
--- /dev/null
+++ b/gui/fitCommands/guiCargoToModule.py
@@ -0,0 +1,77 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.fitSetCharge import FitSetChargeCommand
+from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand
+from gui.fitCommands.calc.fitRemoveCargo import FitRemoveCargoCommand
+from .calc.fitAddCargo import FitAddCargoCommand
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiCargoToModuleCommand(wx.Command):
+ """
+ Moves cargo to fitting window. Can either do a copy, move, or swap with current module
+ If we try to copy/move into a spot with a non-empty module, we swap instead.
+ To avoid redundancy in converting Cargo item, this function does the
+ sanity checks as opposed to the GUI View. This is different than how the
+ normal .swapModules() does things, which is mostly a blind swap.
+ """
+
+ def __init__(self, fitID, moduleIdx, cargoIdx, copy=False):
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.moduleIdx = moduleIdx
+ self.cargoIdx = cargoIdx
+ self.copy = copy
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fit = sFit.getFit(self.fitID)
+ module = fit.modules[self.moduleIdx]
+ cargo = fit.cargo[self.cargoIdx]
+ result = False
+
+ # We're trying to move a charge from cargo to a slot. Use SetCharge command (don't respect move vs copy)
+ if sFit.isAmmo(cargo.item.ID):
+ result = self.internal_history.Submit(FitSetChargeCommand(self.fitID, [module.modPosition], cargo.item.ID))
+ else:
+
+ pyfalog.debug("Moving cargo item to module for fit ID: {0}", self.fitID)
+
+ self.addCmd = FitReplaceModuleCommand(self.fitID, module.modPosition, cargo.itemID)
+
+ result = self.internal_history.Submit(self.addCmd)
+
+ if not result:
+ # creating module failed for whatever reason
+ return False
+
+ if self.addCmd.old_module is not None:
+ # we're swapping with an existing module, so remove cargo and add module
+ self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID)
+ result = self.internal_history.Submit(self.removeCmd)
+
+ self.addCargoCmd = FitAddCargoCommand(self.fitID, self.addCmd.old_module.itemID)
+ result = self.internal_history.Submit(self.addCargoCmd)
+ elif not self.copy:
+ # move, not copying, so remove cargo
+ self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID)
+ result = self.internal_history.Submit(self.removeCmd)
+
+ if result:
+ sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return result
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeCargoQty.py b/gui/fitCommands/guiChangeCargoQty.py
new file mode 100644
index 000000000..80a906fc3
--- /dev/null
+++ b/gui/fitCommands/guiChangeCargoQty.py
@@ -0,0 +1,33 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeCargoQty import FitChangeCargoQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeCargoQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeCargoQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeDroneQty.py b/gui/fitCommands/guiChangeDroneQty.py
new file mode 100644
index 000000000..5aa2ea12e
--- /dev/null
+++ b/gui/fitCommands/guiChangeDroneQty.py
@@ -0,0 +1,33 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeDroneQty import FitChangeDroneQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeDroneQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeFighterQty.py b/gui/fitCommands/guiChangeFighterQty.py
new file mode 100644
index 000000000..280bd1a16
--- /dev/null
+++ b/gui/fitCommands/guiChangeFighterQty.py
@@ -0,0 +1,34 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeFighterQty import FitChangeFighterQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeFighterQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeFighterQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeImplantLocation.py b/gui/fitCommands/guiChangeImplantLocation.py
new file mode 100644
index 000000000..5d6c57e9d
--- /dev/null
+++ b/gui/fitCommands/guiChangeImplantLocation.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeImplantLocation import FitChangeImplantLocation
+
+
+class GuiChangeImplantLocation(wx.Command):
+ def __init__(self, fitID, source):
+ wx.Command.__init__(self, True, "Implant Source Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.source = source
+
+ def Do(self):
+ if self.internal_history.Submit(FitChangeImplantLocation(self.fitID, self.source)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeProjectedDroneQty.py b/gui/fitCommands/guiChangeProjectedDroneQty.py
new file mode 100644
index 000000000..f7ceaa657
--- /dev/null
+++ b/gui/fitCommands/guiChangeProjectedDroneQty.py
@@ -0,0 +1,34 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeProjectedDroneQty import FitChangeProjectedDroneQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeProjectedDroneQty(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeProjectedDroneQty(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiChangeProjectedFitQty.py b/gui/fitCommands/guiChangeProjectedFitQty.py
new file mode 100644
index 000000000..7014a808b
--- /dev/null
+++ b/gui/fitCommands/guiChangeProjectedFitQty.py
@@ -0,0 +1,34 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeProjectedFitQty import FitChangeProjectedFitQty
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiChangeProjectedFitQty(wx.Command):
+ def __init__(self, fitID, pfitID, amount=1):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.pfitID = pfitID
+ self.amount = amount
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ cmd = FitChangeProjectedFitQty(self.fitID, self.pfitID, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiFillWithModule.py b/gui/fitCommands/guiFillWithModule.py
new file mode 100644
index 000000000..0fa962bef
--- /dev/null
+++ b/gui/fitCommands/guiFillWithModule.py
@@ -0,0 +1,50 @@
+import wx
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitAddModule import FitAddModuleCommand
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiFillWithModuleCommand(wx.Command):
+ def __init__(self, fitID, itemID, position=None):
+ """
+ Handles adding an item, usually a module, to the Fitting Window.
+
+ :param fitID: The fit ID that we are modifying
+ :param itemID: The item that is to be added to the Fitting View. If this turns out to be a charge, we attempt to
+ set the charge on the underlying module (requires position)
+ :param position: Optional. The position in fit.modules that we are attempting to set the item to
+ """
+ wx.Command.__init__(self, True, "Module Add: {}".format(itemID))
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.internal_history = wx.CommandProcessor()
+ self.position = position
+ self.old_mod = None
+
+ def Do(self):
+ pyfalog.debug("{} Do()".format(self))
+ pyfalog.debug("Trying to append a module")
+ added_modules = 0
+ success = self.internal_history.Submit(FitAddModuleCommand(self.fitID, self.itemID))
+ while (success):
+ added_modules += 1
+ success = self.internal_history.Submit(FitAddModuleCommand(self.fitID, self.itemID))
+
+ if added_modules > 0:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=self.itemID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=self.itemID))
+ return True
diff --git a/gui/fitCommands/guiFitRename.py b/gui/fitCommands/guiFitRename.py
new file mode 100644
index 000000000..b9a7a81d7
--- /dev/null
+++ b/gui/fitCommands/guiFitRename.py
@@ -0,0 +1,30 @@
+import wx
+import gui.mainFrame
+from .calc.fitRename import FitRenameCommand
+from service.fit import Fit
+from logbook import Logger
+from gui.builtinShipBrowser.events import FitRenamed
+pyfalog = Logger(__name__)
+
+
+class GuiFitRenameCommand(wx.Command):
+ def __init__(self, fitID, newName):
+ wx.Command.__init__(self, True)
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.newName = newName
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ if self.internal_history.Submit(FitRenameCommand(self.fitID, self.newName)):
+ wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiImportMutatedModule.py b/gui/fitCommands/guiImportMutatedModule.py
new file mode 100644
index 000000000..9187adf7e
--- /dev/null
+++ b/gui/fitCommands/guiImportMutatedModule.py
@@ -0,0 +1,38 @@
+import wx
+import eos.db
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitImportMutatedModule import FitImportMutatedCommand
+from service.fit import Fit
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiImportMutatedModuleCommand(wx.Command):
+
+ def __init__(self, fitID, baseItem, mutaItem, attrMap):
+ wx.Command.__init__(self, True, "Mutated Module Import: {} {} {}".format(baseItem, mutaItem, attrMap))
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.baseItem = baseItem
+ self.mutaItem = mutaItem
+ self.attrMap = attrMap
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ pyfalog.debug("{} Do()".format(self))
+
+ if self.internal_history.Submit(FitImportMutatedCommand(self.fitID, self.baseItem, self.mutaItem, self.attrMap)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd"))
+ return True
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel"))
+ return True
diff --git a/gui/fitCommands/guiMetaSwap.py b/gui/fitCommands/guiMetaSwap.py
new file mode 100644
index 000000000..d0c99b8bf
--- /dev/null
+++ b/gui/fitCommands/guiMetaSwap.py
@@ -0,0 +1,65 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveImplant import FitRemoveImplantCommand
+from .calc.fitAddImplant import FitAddImplantCommand
+from .calc.fitRemoveBooster import FitRemoveBoosterCommand
+from .calc.fitAddBooster import FitAddBoosterCommand
+from .calc.fitRemoveCargo import FitRemoveCargoCommand
+from .calc.fitAddCargo import FitAddCargoCommand
+from .calc.fitReplaceModule import FitReplaceModuleCommand
+from .calc.fitAddFighter import FitAddFighterCommand
+from .calc.fitRemoveFighter import FitRemoveFighterCommand
+from .calc.fitChangeDroneVariation import FitChangeDroneVariationCommand
+
+
+class GuiMetaSwapCommand(wx.Command):
+ def __init__(self, fitID, context, itemID, selection: list):
+ wx.Command.__init__(self, True, "Meta Swap")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+ self.context = context
+ self.data = []
+ fit = self.sFit.getFit(fitID)
+
+ if context == 'fittingModule':
+ for x in selection:
+ self.data.append(((FitReplaceModuleCommand, fitID, fit.modules.index(x), itemID),),)
+ elif context == 'implantItem':
+ for x in selection:
+ idx = fit.implants.index(x)
+ self.data.append(((FitRemoveImplantCommand, fitID, idx), (FitAddImplantCommand, fitID, itemID)))
+ elif context == 'boosterItem':
+ for x in selection:
+ idx = fit.boosters.index(x)
+ self.data.append(((FitRemoveBoosterCommand, fitID, idx), (FitAddBoosterCommand, fitID, itemID)))
+ elif context == 'cargoItem':
+ for x in selection:
+ self.data.append(((FitRemoveCargoCommand, fitID, x.itemID, 1, True), (FitAddCargoCommand, fitID, itemID, x.amount)))
+ elif context == 'fighterItem':
+ for x in selection:
+ self.data.append(((FitRemoveFighterCommand, fitID, fit.fighters.index(x)), (FitAddFighterCommand, fitID, itemID)))
+ elif context == 'droneItem':
+ for x in selection:
+ self.data.append(((FitChangeDroneVariationCommand, fitID, fit.drones.index(x), itemID),),)
+
+ def Do(self):
+ for cmds in self.data:
+ for cmd in cmds:
+ self.internal_history.Submit(cmd[0](*cmd[1:]))
+
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiModuleToCargo.py b/gui/fitCommands/guiModuleToCargo.py
new file mode 100644
index 000000000..66a9f9833
--- /dev/null
+++ b/gui/fitCommands/guiModuleToCargo.py
@@ -0,0 +1,73 @@
+import wx
+from logbook import Logger
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.fitRemoveCargo import FitRemoveCargoCommand
+from gui.fitCommands.calc.fitRemoveModule import FitRemoveModuleCommand
+from gui.fitCommands.calc.fitReplaceModule import FitReplaceModuleCommand
+from service.fit import Fit
+from .calc.fitAddCargo import FitAddCargoCommand
+
+pyfalog = Logger(__name__)
+
+
+class GuiModuleToCargoCommand(wx.Command):
+ def __init__(self, fitID, moduleIdx, cargoIdx, copy=False):
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.moduleIdx = moduleIdx
+ self.cargoIdx = cargoIdx
+ self.copy = copy
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ sFit = Fit.getInstance()
+ fit = sFit.getFit(self.fitID)
+ module = fit.modules[self.moduleIdx]
+ result = False
+
+ if self.cargoIdx: # we're swapping with cargo
+ if self.copy: # if copying, simply add item to cargo
+ result = self.internal_history.Submit(FitAddCargoCommand(
+ self.mainFrame.getActiveFit(), module.item.ID if not module.item.isAbyssal else module.baseItemID))
+ else: # otherwise, try to swap by replacing module with cargo item. If successful, remove old cargo and add new cargo
+
+ cargo = fit.cargo[self.cargoIdx]
+ self.modReplaceCmd = FitReplaceModuleCommand(self.fitID, module.modPosition, cargo.itemID)
+
+ result = self.internal_history.Submit(self.modReplaceCmd)
+
+ if not result:
+ # creating module failed for whatever reason
+ return False
+
+ if self.modReplaceCmd.old_module is not None:
+ # we're swapping with an existing module, so remove cargo and add module
+ self.removeCmd = FitRemoveCargoCommand(self.fitID, cargo.itemID)
+ result = self.internal_history.Submit(self.removeCmd)
+
+ self.addCargoCmd = FitAddCargoCommand(self.fitID, self.modReplaceCmd.old_module.itemID)
+ result = self.internal_history.Submit(self.addCargoCmd)
+
+ else: # dragging to blank spot, append
+ result = self.internal_history.Submit(FitAddCargoCommand(self.mainFrame.getActiveFit(),
+ module.item.ID if not module.item.isAbyssal else module.baseItemID))
+
+ if not self.copy: # if not copying, remove module
+ self.internal_history.Submit(FitRemoveModuleCommand(self.mainFrame.getActiveFit(), [self.moduleIdx]))
+
+ if result:
+ sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=module.item.ID))
+
+ return result
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveBooster.py b/gui/fitCommands/guiRemoveBooster.py
new file mode 100644
index 000000000..15b504575
--- /dev/null
+++ b/gui/fitCommands/guiRemoveBooster.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveBooster import FitRemoveBoosterCommand
+
+
+class GuiRemoveBoosterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveBoosterCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveCargo.py b/gui/fitCommands/guiRemoveCargo.py
new file mode 100644
index 000000000..f9aa5872a
--- /dev/null
+++ b/gui/fitCommands/guiRemoveCargo.py
@@ -0,0 +1,28 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveCargo import FitRemoveCargoCommand
+
+
+class GuiRemoveCargoCommand(wx.Command):
+ def __init__(self, fitID, itemID):
+ wx.Command.__init__(self, True, "Module Charge Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.itemID = itemID
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveCargoCommand(self.fitID, self.itemID, stack=True)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.GetCommands():
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveCommand.py b/gui/fitCommands/guiRemoveCommand.py
new file mode 100644
index 000000000..206be23c3
--- /dev/null
+++ b/gui/fitCommands/guiRemoveCommand.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveCommand import FitRemoveCommandCommand
+
+
+class GuiRemoveCommandCommand(wx.Command):
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveCommandCommand(self.fitID, self.commandFitID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveDrone.py b/gui/fitCommands/guiRemoveDrone.py
new file mode 100644
index 000000000..42e651e3d
--- /dev/null
+++ b/gui/fitCommands/guiRemoveDrone.py
@@ -0,0 +1,31 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveDrone import FitRemoveDroneCommand
+
+
+class GuiRemoveDroneCommand(wx.Command):
+ def __init__(self, fitID, position, amount=1):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+ self.amount = amount
+
+ def Do(self):
+ cmd = FitRemoveDroneCommand(self.fitID, self.position, self.amount)
+ if self.internal_history.Submit(cmd):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveFighter.py b/gui/fitCommands/guiRemoveFighter.py
new file mode 100644
index 000000000..f1b983ec5
--- /dev/null
+++ b/gui/fitCommands/guiRemoveFighter.py
@@ -0,0 +1,32 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveFighter import FitRemoveFighterCommand
+
+
+class GuiRemoveFighterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "Module Remove")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.position = position
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ success = self.internal_history.Submit(FitRemoveFighterCommand(self.fitID, self.position))
+
+ if success:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveImplant.py b/gui/fitCommands/guiRemoveImplant.py
new file mode 100644
index 000000000..876421e7a
--- /dev/null
+++ b/gui/fitCommands/guiRemoveImplant.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveImplant import FitRemoveImplantCommand
+
+
+class GuiRemoveImplantCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitRemoveImplantCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiRemoveModule.py b/gui/fitCommands/guiRemoveModule.py
new file mode 100644
index 000000000..944a63160
--- /dev/null
+++ b/gui/fitCommands/guiRemoveModule.py
@@ -0,0 +1,40 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .helpers import ModuleInfoCache
+from .calc.fitRemoveModule import FitRemoveModuleCommand
+
+
+class GuiModuleRemoveCommand(wx.Command):
+ def __init__(self, fitID, modules):
+ """
+ Handles removing modules from fit.modules,
+
+ :param fitID: The fit ID that we are modifying
+ :param modules: A list of Module objects that we are attempting to remove.
+ """
+ wx.Command.__init__(self, True, "Module Remove")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.modCache = [ModuleInfoCache(
+ mod.modPosition, mod.item.ID, mod.state, mod.charge, mod.baseItemID, mod.mutaplasmidID) for mod in modules if not mod.isEmpty]
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ success = self.internal_history.Submit(FitRemoveModuleCommand(self.fitID, [mod.modPosition for mod in self.modCache]))
+
+ if success:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="moddel", typeID=set([mod.itemID for mod in self.modCache])))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID, action="modadd", typeID=set([mod.itemID for mod in self.modCache])))
+ return True
diff --git a/gui/fitCommands/guiRemoveProjected.py b/gui/fitCommands/guiRemoveProjected.py
new file mode 100644
index 000000000..74d1ab308
--- /dev/null
+++ b/gui/fitCommands/guiRemoveProjected.py
@@ -0,0 +1,89 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitRemoveProjectedModule import FitRemoveProjectedModuleCommand
+from .calc.fitRemoveProjectedEnv import FitRemoveProjectedEnvCommand
+from .calc.fitRemoveProjectedFit import FitRemoveProjectedFitCommand
+from .calc.fitRemoveProjectedFighter import FitRemoveProjectedFighterCommand
+from logbook import Logger
+from .calc.fitRemoveProjectedDrone import FitRemoveProjectedDroneCommand
+
+from eos.saveddata.drone import Drone
+from eos.saveddata.module import Module
+from eos.saveddata.fighter import Fighter
+
+pyfalog = Logger(__name__)
+
+
+class GuiRemoveProjectedCommand(wx.Command):
+ mapping = {
+ 'fit': FitRemoveProjectedFitCommand,
+ 'module': FitRemoveProjectedModuleCommand,
+ 'fighter': FitRemoveProjectedFighterCommand,
+ 'env': FitRemoveProjectedEnvCommand,
+ 'drone': FitRemoveProjectedDroneCommand
+ }
+
+ def __init__(self, fitID, thing):
+ wx.Command.__init__(self, True, "Projected Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ fit = self.sFit.getFit(fitID)
+
+ if isinstance(thing, Drone):
+ self.data = fit.projectedDrones.index(thing)
+ self.type = 'drone'
+ elif isinstance(thing, Module):
+ # todo: projected stuff should be wrapped in a projected class wrapper for easier maintainence
+ if thing.item.group.name in Module.SYSTEM_GROUPS:
+ self.type = 'env'
+ self.data = thing.itemID
+ else:
+ self.type = 'module'
+ self.data = fit.projectedModules.index(thing)
+ elif isinstance(thing, Fighter):
+ self.data = fit.projectedFighters.index(thing)
+ self.type = 'fighter'
+ else:
+ # todo: fix!
+ self.data = thing.ID
+ self.type = 'fit'
+
+ def Do(self):
+ result = False
+ # since we can project various types, we need to switch of the fit command. We can't do this switch easily in
+ # the fit command since each type might have a different kind of undo, easier to split it out
+
+ cls = self.mapping.get(self.type, None)
+ if cls:
+ cmd = cls(self.fitID, self.data)
+ result = self.internal_history.Submit(cmd)
+
+ # if item.category.name == "Drone":
+ # pyfalog.warn("DRONE REMOVE PROJECTION NOT IMPLEMENTED")
+ # elif item.category.name == "Fighter":
+ # pyfalog.warn("FIGHTER REMOVE PROJECTION NOT IMPLEMENTED")
+ # elif item.group.name in Module.SYSTEM_GROUPS:
+ # result = self.internal_history.Submit(FitRemoveProjectedEnvCommand(self.fitID, self.id))
+ # else:
+ # # attempt a regular module projection
+ #
+ # elif self.type == 'fit':
+ # pyfalog.warn("FIT REMOVE PROJECTION NOT IMPLEMENTED")
+
+ if result:
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiSetMode.py b/gui/fitCommands/guiSetMode.py
new file mode 100644
index 000000000..9639028f9
--- /dev/null
+++ b/gui/fitCommands/guiSetMode.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitSetMode import FitSetModeCommand
+
+
+class GuiSetModeCommand(wx.Command):
+ def __init__(self, fitID, mode):
+ wx.Command.__init__(self, True, "Cargo Add")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.mode = mode
+
+ def Do(self):
+ if self.internal_history.Submit(FitSetModeCommand(self.fitID, self.mode)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiSwapCloneModule.py b/gui/fitCommands/guiSwapCloneModule.py
new file mode 100644
index 000000000..cbeaf0845
--- /dev/null
+++ b/gui/fitCommands/guiSwapCloneModule.py
@@ -0,0 +1,50 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from gui.fitCommands.calc.fitSwapModule import FitSwapModuleCommand
+from .calc.fitCloneModule import FitCloneModuleCommand
+from logbook import Logger
+pyfalog = Logger(__name__)
+
+
+class GuiModuleSwapOrCloneCommand(wx.Command):
+
+ def __init__(self, fitID, srcPosition, dstPosition, clone=False):
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.srcPosition = srcPosition
+ self.dstPosition = dstPosition
+ self.clone = clone
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ pyfalog.debug("{} Do()".format(self))
+
+ if self.clone:
+ pyfalog.debug("Trying to clone module")
+ if self.internal_history.Submit(FitCloneModuleCommand(self.fitID, self.srcPosition, self.dstPosition)):
+ self.sFit.recalc(self.fitID) # clone needs a recalc
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ else:
+ pyfalog.debug("Trying to Swap module")
+ if self.internal_history.Submit(FitSwapModuleCommand(self.fitID, self.srcPosition, self.dstPosition)):
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+
+ return False
+
+ def Undo(self):
+ pyfalog.debug("{} Undo()".format(self))
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+
+ if self.clone:
+ self.sFit.recalc(self.fitID)
+
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleBooster.py b/gui/fitCommands/guiToggleBooster.py
new file mode 100644
index 000000000..2a586a888
--- /dev/null
+++ b/gui/fitCommands/guiToggleBooster.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleBooster import FitToggleBoosterCommand
+
+
+class GuiToggleBoosterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleBoosterCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleCommand.py b/gui/fitCommands/guiToggleCommand.py
new file mode 100644
index 000000000..50785da26
--- /dev/null
+++ b/gui/fitCommands/guiToggleCommand.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleCommand import FitToggleCommandCommand
+
+
+class GuiToggleCommandCommand(wx.Command):
+ def __init__(self, fitID, commandFitID):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.commandFitID = commandFitID
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleCommandCommand(self.fitID, self.commandFitID)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleDrone.py b/gui/fitCommands/guiToggleDrone.py
new file mode 100644
index 000000000..7913df516
--- /dev/null
+++ b/gui/fitCommands/guiToggleDrone.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleDrone import FitToggleDroneCommand
+
+
+class GuiToggleDroneCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleDroneCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleFighter.py b/gui/fitCommands/guiToggleFighter.py
new file mode 100644
index 000000000..f5aee10d6
--- /dev/null
+++ b/gui/fitCommands/guiToggleFighter.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleFighter import FitToggleFighterCommand
+
+
+class GuiToggleFighterCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleFighterCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleImplant.py b/gui/fitCommands/guiToggleImplant.py
new file mode 100644
index 000000000..899eff0ee
--- /dev/null
+++ b/gui/fitCommands/guiToggleImplant.py
@@ -0,0 +1,30 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitToggleImplant import FitToggleImplantCommand
+
+
+class GuiToggleImplantCommand(wx.Command):
+ def __init__(self, fitID, position):
+ wx.Command.__init__(self, True, "")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.internal_history = wx.CommandProcessor()
+ self.fitID = fitID
+ self.position = position
+
+ def Do(self):
+ if self.internal_history.Submit(FitToggleImplantCommand(self.fitID, self.position)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/guiToggleModuleState.py b/gui/fitCommands/guiToggleModuleState.py
new file mode 100644
index 000000000..71e307f93
--- /dev/null
+++ b/gui/fitCommands/guiToggleModuleState.py
@@ -0,0 +1,33 @@
+import wx
+from service.fit import Fit
+
+import gui.mainFrame
+from gui import globalEvents as GE
+from .calc.fitChangeState import FitChangeStatesCommand
+
+
+class GuiModuleStateChangeCommand(wx.Command):
+ def __init__(self, fitID, baseMod, modules, click):
+ # todo: instead of modules, needs to be positions. Dead objects are a thing
+ wx.Command.__init__(self, True, "Module State Change")
+ self.mainFrame = gui.mainFrame.MainFrame.getInstance()
+ self.sFit = Fit.getInstance()
+ self.fitID = fitID
+ self.baseMod = baseMod
+ self.modules = modules
+ self.click = click
+ self.internal_history = wx.CommandProcessor()
+
+ def Do(self):
+ if self.internal_history.Submit(FitChangeStatesCommand(self.fitID, self.baseMod, self.modules, self.click)):
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
+ return False
+
+ def Undo(self):
+ for _ in self.internal_history.Commands:
+ self.internal_history.Undo()
+ self.sFit.recalc(self.fitID)
+ wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.fitID))
+ return True
diff --git a/gui/fitCommands/helpers.py b/gui/fitCommands/helpers.py
new file mode 100644
index 000000000..63aaf4b76
--- /dev/null
+++ b/gui/fitCommands/helpers.py
@@ -0,0 +1,3 @@
+from collections import namedtuple
+
+ModuleInfoCache = namedtuple('ModuleInfoCache', ['modPosition', 'itemID', 'state', 'charge', 'baseID', 'mutaplasmidID'])
diff --git a/gui/itemStats.py b/gui/itemStats.py
index 3440fb322..50a02f065 100644
--- a/gui/itemStats.py
+++ b/gui/itemStats.py
@@ -34,6 +34,9 @@ from gui.builtinItemStatsViews.itemDependants import ItemDependents
from gui.builtinItemStatsViews.itemEffects import ItemEffects
from gui.builtinItemStatsViews.itemAffectedBy import ItemAffectedBy
from gui.builtinItemStatsViews.itemProperties import ItemProperties
+from gui.builtinItemStatsViews.itemMutator import ItemMutator
+
+from eos.saveddata.module import Module
class ItemStatsDialog(wx.Dialog):
@@ -79,10 +82,8 @@ class ItemStatsDialog(wx.Dialog):
item = sMkt.getItem(victim.ID)
victim = None
self.context = itmContext
- if item.icon is not None:
- before, sep, after = item.icon.iconFile.rpartition("_")
- iconFile = "%s%s%s" % (before, sep, "0%s" % after if len(after) < 2 else after)
- itemImg = BitmapLoader.getBitmap(iconFile, "icons")
+ if item.iconID is not None:
+ itemImg = BitmapLoader.getBitmap(item.iconID, "icons")
if itemImg is not None:
self.SetIcon(wx.Icon(itemImg))
self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name,
@@ -90,7 +91,7 @@ class ItemStatsDialog(wx.Dialog):
self.SetMinSize((300, 200))
if "wxGTK" in wx.PlatformInfo: # GTK has huge tab widgets, give it a bit more room
- self.SetSize((580, 500))
+ self.SetSize((630, 550))
else:
self.SetSize((550, 500))
# self.SetMaxSize((500, -1))
@@ -101,7 +102,7 @@ class ItemStatsDialog(wx.Dialog):
if "wxGTK" in wx.PlatformInfo:
self.closeBtn = wx.Button(self, wx.ID_ANY, "Close", wx.DefaultPosition, wx.DefaultSize, 0)
self.mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5)
- self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent)
+ self.closeBtn.Bind(wx.EVT_BUTTON, (lambda e: self.Close()))
self.SetSizer(self.mainSizer)
@@ -146,7 +147,7 @@ class ItemStatsDialog(wx.Dialog):
ItemStatsDialog.counter -= 1
self.parentWnd.UnregisterStatsWindow(self)
- self.Destroy()
+ event.Skip()
class ItemStatsContainer(wx.Panel):
@@ -163,8 +164,13 @@ class ItemStatsContainer(wx.Panel):
self.traits = ItemTraits(self.nbContainer, stuff, item)
self.nbContainer.AddPage(self.traits, "Traits")
- self.desc = ItemDescription(self.nbContainer, stuff, item)
- self.nbContainer.AddPage(self.desc, "Description")
+ if isinstance(stuff, Module) and stuff.isMutated:
+ self.mutator = ItemMutator(self.nbContainer, stuff, item)
+ self.nbContainer.AddPage(self.mutator, "Mutations")
+
+ if item.description:
+ self.desc = ItemDescription(self.nbContainer, stuff, item)
+ self.nbContainer.AddPage(self.desc, "Description")
self.params = ItemParams(self.nbContainer, stuff, item, context)
self.nbContainer.AddPage(self.params, "Attributes")
diff --git a/gui/mainFrame.py b/gui/mainFrame.py
index c9e5fa09c..d5bc8f0e9 100644
--- a/gui/mainFrame.py
+++ b/gui/mainFrame.py
@@ -17,74 +17,63 @@
# along with pyfa. If not, see .
# =============================================================================
-import sys
+import datetime
import os.path
-from logbook import Logger
-
-import sqlalchemy
-# noinspection PyPackageRequirements
-import wx
-# noinspection PyPackageRequirements
-from wx.lib.wordwrap import wordwrap
-# noinspection PyPackageRequirements
-from wx.lib.inspection import InspectionTool
+import threading
import time
-
+import webbrowser
from codecs import open
-
-import config
-
-from eos.config import gamedata_version
-
-import gui.aboutData
-from gui.chrome_tabs import ChromeNotebook
-import gui.globalEvents as GE
-
-from gui.bitmap_loader import BitmapLoader
-from gui.mainMenuBar import MainMenuBar
-from gui.additionsPane import AdditionsPane
-from gui.marketBrowser import MarketBrowser
-from gui.builtinMarketBrowser.events import ItemSelected
-from gui.multiSwitch import MultiSwitch
-from gui.statsPane import StatsPane
-from gui.shipBrowser import ShipBrowser
-from gui.builtinShipBrowser.events import FitSelected, ImportSelected, Stage3Selected
-from gui.characterEditor import CharacterEditor
-from gui.characterSelection import CharacterSelection
-from gui.patternEditor import DmgPatternEditorDlg
-from gui.resistsEditor import ResistsEditorDlg
-from gui.setEditor import ImplantSetEditorDlg
-from gui.devTools import DevTools
-from gui.preferenceDialog import PreferenceDialog
-from gui.graphFrame import GraphFrame
-from gui.ssoLogin import SsoLogin
-from gui.copySelectDialog import CopySelectDialog
-from gui.utils.clipboard import toClipboard, fromClipboard
-from gui.updateDialog import UpdateDialog
-# noinspection PyUnresolvedReferences
-from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401
-from gui import graphFrame
-
-from service.settings import SettingsProvider
-from service.fit import Fit
-from service.character import Character
-from service.update import Update
-
-# import this to access override setting
-from eos.modifiedAttributeDict import ModifiedAttributeDict
-from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues
-from eos.db.saveddata.queries import getFit as db_getFit
-from service.port import Port, IPortUser
-from service.settings import HTMLExportSettings
-
from time import gmtime, strftime
-import threading
-import webbrowser
+# noinspection PyPackageRequirements
+import wx
import wx.adv
+from logbook import Logger
+# noinspection PyPackageRequirements
+# noinspection PyPackageRequirements
+from wx.lib.inspection import InspectionTool
-from service.esi import Esi, LoginMethod
+import config
+import gui.globalEvents as GE
+from eos.config import gamedata_date, gamedata_version
+from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues
+from eos.db.saveddata.queries import getFit as db_getFit
+# import this to access override setting
+from eos.modifiedAttributeDict import ModifiedAttributeDict
+from gui import graphFrame
+from gui.additionsPane import AdditionsPane
+from gui.bitmap_loader import BitmapLoader
+from gui.builtinMarketBrowser.events import ItemSelected
+from gui.builtinShipBrowser.events import FitSelected, ImportSelected, Stage3Selected
+# noinspection PyUnresolvedReferences
+from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401
+from gui.characterEditor import CharacterEditor
+from gui.characterSelection import CharacterSelection
+from gui.chrome_tabs import ChromeNotebook
+from gui.copySelectDialog import CopySelectDialog
+from gui.devTools import DevTools
from gui.esiFittings import EveFittings, ExportToEve, SsoCharacterMgmt
+from gui.graphFrame import GraphFrame
+from gui.mainMenuBar import MainMenuBar
+from gui.marketBrowser import MarketBrowser
+from gui.multiSwitch import MultiSwitch
+from gui.patternEditor import DmgPatternEditorDlg
+from gui.preferenceDialog import PreferenceDialog
+from gui.resistsEditor import ResistsEditorDlg
+from gui.setEditor import ImplantSetEditorDlg
+from gui.shipBrowser import ShipBrowser
+from gui.ssoLogin import SsoLogin
+from gui.statsPane import StatsPane
+from gui.updateDialog import UpdateDialog
+from gui.utils.clipboard import fromClipboard, toClipboard
+from service.character import Character
+from service.esi import Esi, LoginMethod
+from service.esiAccess import SsoMode
+from service.fit import Fit
+from service.port import EfsPort, IPortUser, Port
+from service.settings import HTMLExportSettings, SettingsProvider
+from service.update import Update
+import gui.fitCommands as cmd
disableOverrideEditor = False
@@ -97,6 +86,8 @@ except ImportError as e:
pyfalog = Logger(__name__)
+pyfalog.debug("Done loading mainframe imports")
+
# dummy panel(no paint no erasebk)
class PFPanel(wx.Panel):
@@ -150,6 +141,7 @@ class MainFrame(wx.Frame):
pyfalog.debug("Initialize MainFrame")
self.title = title
wx.Frame.__init__(self, None, wx.ID_ANY, self.title)
+ self.supress_left_up = False
MainFrame.__instance = self
@@ -240,13 +232,17 @@ class MainFrame(wx.Frame):
self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin)
self.Bind(GE.EVT_SSO_LOGGING_IN, self.ShowSsoLogin)
+ @property
+ def command(self) -> wx.CommandProcessor:
+ return Fit.getCommandProcessor(self.getActiveFit())
+
def ShowSsoLogin(self, event):
- if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL:
+ if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL and getattr(event, "sso_mode", SsoMode.AUTO) == SsoMode.AUTO:
dlg = SsoLogin(self)
if dlg.ShowModal() == wx.ID_OK:
sEsi = Esi.getInstance()
# todo: verify that this is a correct SSO Info block
- sEsi.handleLogin(dlg.ssoInfoCtrl.Value.strip())
+ sEsi.handleLogin({'SSOInfo': [dlg.ssoInfoCtrl.Value.strip()]})
def ShowUpdateBox(self, release, version):
dlg = UpdateDialog(self, release, version)
@@ -364,7 +360,8 @@ class MainFrame(wx.Frame):
def ShowAboutBox(self, evt):
info = wx.adv.AboutDialogInfo()
info.Name = "pyfa"
- info.Version = config.getVersion() # gui.aboutData.versionString
+ time = datetime.datetime.fromtimestamp(int(gamedata_date)).strftime('%Y-%m-%d %H:%M:%S')
+ info.Version = config.getVersion() + '\nEVE Data Version: {} ({})'.format(gamedata_version, time) # gui.aboutData.versionString
#
# try:
# import matplotlib
@@ -427,6 +424,7 @@ class MainFrame(wx.Frame):
style=wx.FD_SAVE,
defaultFile=defaultFile)
if dlg.ShowModal() == wx.ID_OK:
+ self.supress_left_up = True
format_ = dlg.GetFilterIndex()
path = dlg.GetPath()
if format_ == 0:
@@ -509,6 +507,10 @@ class MainFrame(wx.Frame):
self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES)
# User guide
self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId)
+
+ self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Undo(), id=wx.ID_UNDO)
+
+ self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Redo(), id=wx.ID_REDO)
# EVE Forums
self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId)
# Save current character
@@ -697,51 +699,63 @@ class MainFrame(wx.Frame):
else:
self.marketBrowser.search.Focus()
- def clipboardEft(self):
+ def clipboardEft(self, options):
fit = db_getFit(self.getActiveFit())
- toClipboard(Port.exportEft(fit))
+ toClipboard(Port.exportEft(fit, options))
- def clipboardEftImps(self):
- fit = db_getFit(self.getActiveFit())
- toClipboard(Port.exportEftImps(fit))
-
- def clipboardDna(self):
+ def clipboardDna(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportDna(fit))
- def clipboardEsi(self):
+ def clipboardEsi(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportESI(fit))
- def clipboardXml(self):
+ def clipboardXml(self, options):
fit = db_getFit(self.getActiveFit())
toClipboard(Port.exportXml(None, fit))
- def clipboardMultiBuy(self):
+ def clipboardMultiBuy(self, options):
fit = db_getFit(self.getActiveFit())
- toClipboard(Port.exportMultiBuy(fit))
+ toClipboard(Port.exportMultiBuy(fit, options))
+
+ def clipboardEfs(self, options):
+ fit = db_getFit(self.getActiveFit())
+ toClipboard(EfsPort.exportEfs(fit, 0))
def importFromClipboard(self, event):
clipboard = fromClipboard()
+ activeFit = self.getActiveFit()
try:
- fits = Port().importFitFromBuffer(clipboard, self.getActiveFit())
+ importType, importData = Port().importFitFromBuffer(clipboard, activeFit)
+ # If it's mutated item - make sure there's at least base item specified
+ if importType == "MutatedItem":
+ # we've imported an Abyssal module, need to fire off the command to add it to the fit
+ self.command.Submit(cmd.GuiImportMutatedModuleCommand(activeFit, *importData[0]))
+ return # no need to do anything else
except:
pyfalog.error("Attempt to import failed:\n{0}", clipboard)
else:
- self._openAfterImport(fits)
+ self._openAfterImport(importData)
def exportToClipboard(self, event):
CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft,
- CopySelectDialog.copyFormatEftImps: self.clipboardEftImps,
CopySelectDialog.copyFormatXml: self.clipboardXml,
CopySelectDialog.copyFormatDna: self.clipboardDna,
CopySelectDialog.copyFormatEsi: self.clipboardEsi,
- CopySelectDialog.copyFormatMultiBuy: self.clipboardMultiBuy}
+ CopySelectDialog.copyFormatMultiBuy: self.clipboardMultiBuy,
+ CopySelectDialog.copyFormatEfs: self.clipboardEfs}
dlg = CopySelectDialog(self)
- dlg.ShowModal()
- selected = dlg.GetSelected()
+ btnPressed = dlg.ShowModal()
- CopySelectDict[selected]()
+ if btnPressed == wx.ID_OK:
+ selected = dlg.GetSelected()
+ options = dlg.GetOptions()
+
+ settings = SettingsProvider.getInstance().getSettings("pyfaExport")
+ settings["format"] = selected
+ settings["options"] = options
+ CopySelectDict[selected](options.get(selected))
try:
dlg.Destroy()
@@ -966,6 +980,7 @@ class MainFrame(wx.Frame):
)
if dlg.ShowModal() == wx.ID_OK:
+ self.supress_left_up = True
self.waitDialog = wx.BusyInfo("Importing Character...")
sCharacter = Character.getInstance()
sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback)
diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py
index 9c421f779..c0a28f1dc 100644
--- a/gui/mainMenuBar.py
+++ b/gui/mainMenuBar.py
@@ -90,8 +90,8 @@ class MainMenuBar(wx.MenuBar):
editMenu = wx.Menu()
self.Append(editMenu, "&Edit")
- # editMenu.Append(wx.ID_UNDO)
- # editMenu.Append(wx.ID_REDO)
+ editMenu.Append(wx.ID_UNDO)
+ editMenu.Append(wx.ID_REDO)
editMenu.Append(wx.ID_COPY, "To Clipboard\tCTRL+C", "Export a fit to the clipboard")
editMenu.Append(wx.ID_PASTE, "From Clipboard\tCTRL+V", "Import a fit from the clipboard")
diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py
index 0144f651c..c3e3e1c16 100644
--- a/gui/pyfa_gauge.py
+++ b/gui/pyfa_gauge.py
@@ -14,11 +14,10 @@
# ===============================================================================
import copy
+
import wx
-from gui.utils import color as color_utils
-from gui.utils import draw, anim_effects
-from service.fit import Fit
+from gui.utils import anim_effects, color as color_utils, draw
class PyGauge(wx.Window):
@@ -130,8 +129,8 @@ class PyGauge(wx.Window):
return self._max_range
def Animate(self):
- sFit = Fit.getInstance()
- if sFit.serviceFittingOptions["enableGaugeAnimation"]:
+ # sFit = Fit.getInstance()
+ if True:
if not self._timer:
self._timer = wx.Timer(self, self._timer_id)
@@ -425,6 +424,13 @@ if __name__ == "__main__":
gauge.SetFractionDigits(1)
box.Add(gauge, 0, wx.ALL, 2)
+ gauge = PyGauge(self, font, size=(100, 5))
+ gauge.SetBackgroundColour(wx.Colour(52, 86, 98))
+ gauge.SetBarColour(wx.Colour(255, 128, 0))
+ gauge.SetValue(59)
+ gauge.SetFractionDigits(1)
+ box.Add(gauge, 0, wx.ALL, 2)
+
self.SetSizer(box)
self.Layout()
diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py
index 2b12edd8c..75152a1b4 100644
--- a/gui/shipBrowser.py
+++ b/gui/shipBrowser.py
@@ -189,7 +189,7 @@ class ShipBrowser(wx.Panel):
"amarr", "caldari", "gallente", "minmatar",
"sisters", "ore",
"serpentis", "angel", "blood", "sansha", "guristas", "mordu",
- "jove", "upwell", None
+ "jove", "upwell", "triglavian", None
]
def raceNameKey(self, ship):
@@ -236,10 +236,10 @@ class ShipBrowser(wx.Panel):
if self.filterShipsWithNoFits:
if fits > 0:
if filter_:
- self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race))
+ self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race, ship.graphicID))
else:
if filter_:
- self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race))
+ self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race, ship.graphicID))
self.raceselect.RebuildRaces(racesList)
@@ -335,8 +335,8 @@ class ShipBrowser(wx.Panel):
shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits
- for ID, name, booster, timestamp, notes in fitList:
- self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID))
+ for ID, name, booster, timestamp, notes, graphicID in fitList:
+ self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID, graphicID=graphicID))
self.lpane.RefreshList()
self.lpane.Thaw()
@@ -374,7 +374,7 @@ class ShipBrowser(wx.Panel):
self.lpane.AddWidget(
ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))),
- ship.race))
+ ship.race, ship.graphicID))
for ID, name, shipID, shipName, booster, timestamp, notes in fitList:
ship = sMkt.getItem(shipID)
@@ -384,7 +384,7 @@ class ShipBrowser(wx.Panel):
shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits
- self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID))
+ self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID, graphicID=ship.graphicID))
if len(ships) == 0 and len(fitList) == 0:
self.lpane.AddWidget(PFStaticText(self.lpane, label="No matching results."))
self.lpane.RefreshList(doFocus=False)
@@ -435,6 +435,7 @@ class ShipBrowser(wx.Panel):
fit[4]
),
shipItem.ID,
+ graphicID=shipItem.graphicID
))
self.lpane.RefreshList(doFocus=False)
self.lpane.Thaw()
diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py
index 2ff364752..4a8f1197b 100644
--- a/gui/ssoLogin.py
+++ b/gui/ssoLogin.py
@@ -1,5 +1,6 @@
import wx
+
class SsoLogin(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Login", size=wx.Size(400, 240))
diff --git a/gui/updateDialog.py b/gui/updateDialog.py
index c575eeaf3..c596d553e 100644
--- a/gui/updateDialog.py
+++ b/gui/updateDialog.py
@@ -62,7 +62,6 @@ class UpdateDialog(wx.Dialog):
self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow)
link_patterns = [
- (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"),
(re.compile("#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"),
(re.compile("@(\w+)", re.I), r"https://github.com/\1")
]
@@ -71,12 +70,27 @@ class UpdateDialog(wx.Dialog):
extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'],
link_patterns=link_patterns)
+ release_markup = markdowner.convert(self.releaseInfo['body'])
+
+ # run the text through markup again, this time with the hashing pattern. This is required due to bugs in markdown2:
+ # https://github.com/trentm/python-markdown2/issues/287
+ link_patterns = [
+ (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"),
+ ]
+
+ markdowner = markdown2.Markdown(
+ extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'],
+ link_patterns=link_patterns)
+
+ # The space here is required, again, due to bug. Again, see https://github.com/trentm/python-markdown2/issues/287
+ release_markup = markdowner.convert(' ' + release_markup)
+
self.browser.SetPage(html_tmpl.format(
self.releaseInfo['tag_name'],
releaseDate.strftime('%B %d, %Y'),
"This is a pre-release, be prepared for unstable features
" if version.is_prerelease else "",
- markdowner.convert(self.releaseInfo['body'])
- ),"")
+ release_markup
+ ), "")
notesSizer.Add(self.browser, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5)
mainSizer.Add(notesSizer, 1, wx.EXPAND, 5)
diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py
index 612df367e..d1c0af9d0 100644
--- a/gui/utils/exportHtml.py
+++ b/gui/utils/exportHtml.py
@@ -86,11 +86,11 @@ class exportHtmlThread(threading.Thread):
Pyfa Fittings
-
+