diff --git a/eos/db/saveddata/cargo.py b/eos/db/saveddata/cargo.py new file mode 100644 index 000000000..deabd3b2e --- /dev/null +++ b/eos/db/saveddata/cargo.py @@ -0,0 +1,32 @@ +#=============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +#=============================================================================== + +from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean +from sqlalchemy.orm import mapper + +from eos.db import saveddata_meta +from eos.types import Cargo + +cargo_table = Table("cargo", saveddata_meta, + Column("ID", Integer, primary_key=True), + Column("fitID", Integer, ForeignKey("fits.ID"), nullable = False, index = True), + Column("itemID", Integer, nullable = False), + Column("amount", Integer, nullable = False)) + +mapper(Cargo, cargo_table) \ No newline at end of file diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index 25516b7b6..ccb8bbb71 100755 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -24,11 +24,12 @@ from sqlalchemy.sql import and_ from eos.db import saveddata_meta from eos.db.saveddata.module import modules_table from eos.db.saveddata.drone import drones_table +from eos.db.saveddata.cargo import cargo_table from eos.db.saveddata.implant import fitImplants_table -from eos.types import Fit, Module, User, Booster, Drone, Implant, Character, DamagePattern +from eos.types import Fit, Module, User, Booster, Drone, Cargo, Implant, Character, DamagePattern from eos.effectHandlerHelpers import HandledModuleList, HandledDroneList, \ HandledImplantBoosterList, HandledProjectedModList, HandledProjectedDroneList, \ -HandledProjectedFitList +HandledProjectedFitList, HandledCargoList fits_table = Table("fits", saveddata_meta, Column("ID", Integer, primary_key = True), Column("ownerID", ForeignKey("users.ID"), nullable = True, index = True), @@ -53,6 +54,8 @@ mapper(Fit, fits_table, "_Fit__boosters" : relation(Booster, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', single_parent=True), "_Fit__drones" : relation(Drone, collection_class = HandledDroneList, cascade='all, delete, delete-orphan', single_parent=True, primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), + "_Fit__cargo" : relation(Cargo, collection_class = HandledCargoList, cascade='all, delete, delete-orphan', single_parent=True, + primaryjoin = and_(cargo_table.c.fitID == fits_table.c.ID)), "_Fit__projectedDrones" : relation(Drone, collection_class = HandledProjectedDroneList, cascade='all, delete, delete-orphan', single_parent=True, primaryjoin = and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), "_Fit__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all, delete, delete-orphan', single_parent=True, diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index c4b66fb94..b3b4c75ee 100755 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -187,6 +187,46 @@ class HandledDroneList(HandledList): return d +class HandledCargoList(HandledList): + # shameless copy of HandledDroneList + # I have no idea what this does, but I needed it + # @todo: investigate this + def find(self, item): + for d in self: + if d.item == item: + yield d + + def findFirst(self, item): + for d in self.find(item): + return d + + def append(self, cargo): + list.append(self, cargo) + + def remove(self, cargo): + HandledList.remove(self, cargo) + + def appendItem(self, item, qty = 1): + if qty < 1: ValueError("Amount of cargo to add should be >= 1") + d = self.findFirst(item) + + if d is None: + d = eos.types.Cargo(item) + self.append(d) + + d.qty += qty + return d + + def removeItem(self, item, qty): + if qty < 1: ValueError("Amount of cargo to remove should be >= 1") + d = self.findFirst(item) + if d is None: return + d.qty -= qty + if d.qty <= 0: + self.remove(d) + return None + + return d class HandledImplantBoosterList(HandledList): def __init__(self): diff --git a/eos/saveddata/cargo.py b/eos/saveddata/cargo.py new file mode 100644 index 000000000..54ca21d0e --- /dev/null +++ b/eos/saveddata/cargo.py @@ -0,0 +1,75 @@ +#=============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +#=============================================================================== + +from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut +from eos.effectHandlerHelpers import HandledItem, HandledCharge +from sqlalchemy.orm import validates, reconstructor + +# Cargo class copied from Implant class and hacked to make work. \o/ +# @todo: clean me up, Scotty +class Cargo(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): + + def __init__(self, item): + self.__item = item + self.itemID = item.ID + self.active = True + self.amount = 0 + self.__itemModifiedAttributes = ModifiedAttributeDict() + self.__itemModifiedAttributes.original = self.item.attributes + + @reconstructor + def init(self): + self.__item = None + + def __fetchItemInfo(self): + import eos.db + self.__item = eos.db.getItem(self.itemID) + self.__itemModifiedAttributes = ModifiedAttributeDict() + self.__itemModifiedAttributes.original = self.__item.attributes + + @property + def itemModifiedAttributes(self): + if self.__item is None: + self.__fetchItemInfo() + + return self.__itemModifiedAttributes + + @property + def item(self): + if self.__item is None: + self.__fetchItemInfo() + + return self.__item + + def clear(self): + self.itemModifiedAttributes.clear() + + @validates("fitID", "itemID", "active") + def validator(self, key, val): + map = {"fitID": lambda val: isinstance(val, int), + "itemID" : lambda val: isinstance(val, int), + "active": lambda val: isinstance(val, bool)} + + if map[key](val) == False: raise ValueError(str(val) + " is not a valid value for " + key) + else: return val + + def __deepcopy__(self, memo): + copy = Cargo(self.item) + copy.active = self.active + return copy \ No newline at end of file diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index b27b1b1f6..de5750a04 100755 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -18,14 +18,14 @@ #=============================================================================== from eos.effectHandlerHelpers import HandledList, HandledModuleList, HandledDroneList, HandledImplantBoosterList, \ -HandledProjectedFitList, HandledProjectedModList, HandledProjectedDroneList +HandledProjectedFitList, HandledProjectedModList, HandledProjectedDroneList, HandledCargoList from eos.modifiedAttributeDict import ModifiedAttributeDict from sqlalchemy.orm import validates, reconstructor from itertools import chain from eos import capSim from copy import deepcopy from math import sqrt, log, asinh -from eos.types import Drone, Ship, Character, State, Slot, Module, Implant, Booster, Skill +from eos.types import Drone, Cargo, Ship, Character, State, Slot, Module, Implant, Booster, Skill from eos.saveddata.module import State import re import xml.dom @@ -52,6 +52,7 @@ class Fit(object): def __init__(self): self.__modules = HandledModuleList() self.__drones = HandledDroneList() + self.__cargo = HandledCargoList() self.__implants = HandledImplantBoosterList() self.__boosters = HandledImplantBoosterList() self.__projectedFits = HandledProjectedFitList() @@ -174,11 +175,9 @@ class Fit(object): d.amount = int(amount) f.drones.append(d) elif item.category.name == "Charge": - for i in xrange(int(amount)): - for mod in f.modules: - if (mod.isValidCharge(item) and mod.charge == None): - mod.charge = item - break; + c = Cargo(item) + c.amount = int(amount) + f.cargo.append(c) else: for i in xrange(int(amount)): try: @@ -216,9 +215,10 @@ class Fit(object): # maintain map of drones and their quantities droneMap = {} + cargoMap = {} for i in range(1, len(lines)): ammoName = None - droneAmount = None + extraAmount = None line = lines[i].strip() if not line: @@ -239,7 +239,7 @@ class Fit(object): modName = modAmmo[0].strip() elif len(modExtra) == 2: # line with drone/cargo and qty - droneAmount = modExtra[1].strip() + extraAmount = modExtra[1].strip() modName = modExtra[0].strip() else: # line with just module @@ -248,17 +248,20 @@ class Fit(object): try: # get item information. If we are on a Drone/Cargo line, throw out cargo item = db.getItem(modName, eager="group.category") - if len(modExtra) == 2 and item.category.name != "Drone": - continue except: # if no data can be found (old names) continue if item.category.name == "Drone": - droneAmount = int(droneAmount) if droneAmount is not None else 1 + extraAmount = int(extraAmount) if extraAmount is not None else 1 if not modName in droneMap: droneMap[modName] = 0 - droneMap[modName] += droneAmount + droneMap[modName] += extraAmount + if len(modExtra) == 2 and item.category.name != "Drone": + extraAmount = int(extraAmount) if extraAmount is not None else 1 + if not modName in cargoMap: + cargoMap[modName] = 0 + cargoMap[modName] += extraAmount elif item.category.name == "Implant": fit.implants.append(Implant(item)) else: @@ -286,6 +289,11 @@ class Fit(object): d.amount = droneMap[droneName] fit.drones.append(d) + for cargoName in cargoMap: + c = Cargo(db.getItem(cargoName)) + c.amount = cargoMap[cargoName] + fit.cargo.append(c) + return fit @classmethod @@ -327,12 +335,14 @@ class Fit(object): f.name = fitLines[0][1:-1] # Assign ship to fitting f.ship = Ship(db.getItem(shipname)) + for i in range(1, len(fitLines)): line = fitLines[i] if not line: continue # Parse line into some data we will need misc = re.match("(Drones|Implant|Booster)_(Active|Inactive)=(.+)",line) + cargo = re.match("Cargohold=(.+)",line) if misc: entityType = misc.group(1) entityState = misc.group(2) @@ -389,6 +399,19 @@ class Fit(object): b.active = False f.boosters.append(b) # If we don't have any prefixes, then it's a module + elif cargo: + cargoData = re.match("(.+),([0-9]+)", cargo.group(1)) + cargoName = cargoData.group(1) if cargoData else cargo.group(1) + cargoAmount = int(cargoData.group(2)) if cargoData else 1 + # Bail if we can't get item + try: + item = db.getItem(cargoName) + except: + continue + # Add Cargo to the fitting + c = Cargo(item) + c.amount = cargoAmount + f.cargo.append(c) else: withCharge = re.match("(.+),(.+)", line) modName = withCharge.group(1) if withCharge else line @@ -398,6 +421,7 @@ class Fit(object): modItem = db.getItem(modName) except: continue + # Create module and activate it if it's activable m = Module(modItem) if m.isValidState(State.ACTIVE): @@ -449,6 +473,13 @@ class Fit(object): d = Drone(item) d.amount = int(hardware.getAttribute("qty")) f.drones.append(d) + elif hardware.getAttribute("slot").lower() == "cargo": + # although the eve client only support charges in cargo, third-party programs + # may support items or "refits" in cargo. Support these by blindly adding all + # cargo, not just charges + c = Cargo(item) + c.amount = int(hardware.getAttribute("qty")) + f.cargo.append(c) else: try: m = Module(item) @@ -492,6 +523,9 @@ class Fit(object): export += "\n\n" for drone in self.drones: export += "%s x%s\n" % (drone.item.name, drone.amount) + if len(self.cargo) > 0: + for cargo in self.cargo: + export += "%s x%s\n" % (cargo.item.name, cargo.amount) if export[-1] == "\n": export = export[:-1] @@ -532,6 +566,17 @@ class Fit(object): for drone in self.drones: dna += ":{0};{1}".format(drone.itemID, drone.amount) + for cargo in self.cargo: + # DNA format is a simple/dumb format. As CCP uses the slot information of the item itself + # without designating slots in the DNA standard, we need to make sure we only include + # charges in the DNA export. If modules were included, the EVE Client will interpret these + # as being "Fitted" to whatever slot they are for, and it causes an corruption error in the + # client when trying to save the fit + if cargo.item.category.name == "Charge": + if not cargo.item.ID in charges: + charges[cargo.item.ID] = 0 + charges[cargo.item.ID] += cargo.amount + for charge in charges: dna += ":{0};{1}".format(charge, charges[charge]) @@ -583,6 +628,11 @@ class Fit(object): hardware.setAttribute("type", drone.item.name) fitting.appendChild(hardware) + for cargo in fit.cargo: + if not cargo.item.name in charges: + charges[cargo.item.name] = 0 + charges[cargo.item.name] += cargo.amount + for name, qty in charges.items(): hardware = doc.createElement("hardware") hardware.setAttribute("qty", "%d" % qty) @@ -651,6 +701,10 @@ class Fit(object): def drones(self): return self.__drones + @property + def cargo(self): + return self.__cargo + @property def modules(self): return self.__modules diff --git a/eos/types.py b/eos/types.py index 358e0828c..9896e526a 100755 --- a/eos/types.py +++ b/eos/types.py @@ -25,6 +25,7 @@ from eos.saveddata.damagePattern import DamagePattern from eos.saveddata.character import Character, Skill from eos.saveddata.module import Module, State, Slot, Hardpoint, Rack from eos.saveddata.drone import Drone +from eos.saveddata.cargo import Cargo from eos.saveddata.implant import Implant from eos.saveddata.booster import SideEffect from eos.saveddata.booster import Booster diff --git a/gui/additionsPane.py b/gui/additionsPane.py index a17357654..449d3935b 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -21,6 +21,7 @@ import wx import gui.mainFrame from gui.boosterView import BoosterView from gui.droneView import DroneView +from gui.cargoView import CargoView from gui.implantView import ImplantView from gui.projectedView import ProjectedView from gui.pyfatogglepanel import TogglePanel @@ -55,8 +56,10 @@ class AdditionsPane(TogglePanel): boosterImg = bitmapLoader.getImage("booster_small", "icons") projectedImg = bitmapLoader.getImage("projected_small", "icons") gangImg = bitmapLoader.getImage("fleet_fc_small", "icons") + cargoImg = bitmapLoader.getImage("cargo_small", "icons") self.notebook.AddPage(DroneView(self.notebook), "Drones", tabImage = droneImg, showClose = False) + self.notebook.AddPage(CargoView(self.notebook), "Cargo", tabImage = cargoImg, showClose = False) self.notebook.AddPage(ImplantView(self.notebook), "Implants", tabImage = implantImg, showClose = False) self.notebook.AddPage(BoosterView(self.notebook), "Boosters", tabImage = boosterImg, showClose = False) @@ -68,6 +71,6 @@ class AdditionsPane(TogglePanel): self.notebook.SetSelection(0) - PANES = ["Drones", "Implants", "Boosters", "Projected", "Fleet"] + PANES = ["Drones", "Cargo", "Implants", "Boosters", "Projected", "Fleet"] def select(self, name): self.notebook.SetSelection(self.PANES.index(name)) diff --git a/gui/builtinContextMenus/__init__.py b/gui/builtinContextMenus/__init__.py index e50949d3f..edec6a9fc 100644 --- a/gui/builtinContextMenus/__init__.py +++ b/gui/builtinContextMenus/__init__.py @@ -1,2 +1,2 @@ __all__ = ["moduleAmmoPicker", "itemStats", "damagePattern", "marketJump", "droneSplit", "itemRemove", - "droneRemoveStack", "ammoPattern", "project", "factorReload", "whProjector"] + "droneRemoveStack", "ammoPattern", "project", "factorReload", "whProjector", "cargo"] diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py new file mode 100644 index 000000000..2284c11af --- /dev/null +++ b/gui/builtinContextMenus/cargo.py @@ -0,0 +1,95 @@ +from gui.contextMenu import ContextMenu +from gui.itemStats import ItemStatsDialog +import eos.types +import gui.mainFrame +import service +import gui.globalEvents as GE +import wx + +class Cargo(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + # Make sure context menu registers in the correct view + if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: + return False + return True + + def getText(self, itmContext, selection): + return "Add {0} to Cargo".format(itmContext) + + def activate(self, fullContext, selection, i): + sFit = service.Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + + cargo = eos.types.Cargo(selection[0]) + sFit.addChangeCargo(fitID,cargo, 1) + self.mainFrame.additionsPane.notebook.SetSelection(1) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + +Cargo.register() + +class CargoAmount(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + return srcContext in ("cargoItem",) and selection[0].amount >= 0 + + def getText(self, itmContext, selection): + return "Change {0} Quantity".format(itmContext) + + def activate(self, fullContext, selection, i): + srcContext = fullContext[0] + dlg = CargoChanger(self.mainFrame, selection[0], srcContext) + dlg.ShowModal() + dlg.Destroy() + +CargoAmount.register() + +class CargoChanger(wx.Dialog): + + def __init__(self, parent, cargo, context): + wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) + self.cargo = cargo + self.context = context + + bSizer1 = wx.BoxSizer(wx.HORIZONTAL) + + self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) + + bSizer1.Add(self.input, 1, wx.ALL, 5) + self.input.Bind(wx.EVT_CHAR, self.onChar) + self.input.Bind(wx.EVT_TEXT_ENTER, self.change) + self.button = wx.Button(self, wx.ID_OK, u"Done") + bSizer1.Add(self.button, 0, wx.ALL, 5) + + self.SetSizer(bSizer1) + self.Layout() + self.Centre(wx.BOTH) + self.button.Bind(wx.EVT_BUTTON, self.change) + + def change(self, event): + sFit = service.Fit.getInstance() + mainFrame = gui.mainFrame.MainFrame.getInstance() + fitID = mainFrame.getActiveFit() + + sFit.addChangeCargo(fitID, self.cargo, int(self.input.GetLineText(0))) + + wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) + + event.Skip() + self.Destroy() + ## checks to make sure it's valid number + def onChar(self, event): + key = event.GetKeyCode() + + acceptable_characters = "1234567890" + acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste + if key in acceptable_keycode or key >= 255 or (key < 255 and chr(key) in acceptable_characters): + event.Skip() + return + else: + return False + diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index 0b44d209d..801948a3a 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -102,6 +102,9 @@ class PriceViewFull(StatsView): for drone in fit.drones: for _ in xrange(drone.amount): typeIDs.append(drone.itemID) + for cargo in fit.cargo: + for _ in xrange(cargo.amount): + typeIDs.append(cargo.itemID) if self._timer: if self._timer.IsRunning(): self._timer.Stop() diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index 60944db08..ec8133590 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -95,7 +95,7 @@ class TargetingMiscViewFull(StatsView): def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + #If we did anything interesting, we'd update our labels to reflect the new fit's stats here stats = (("labelTargets", lambda: fit.maxTargets, 3, 0, 0, ""), ("labelRange", lambda: fit.maxTargetRange / 1000, 3, 0, 0, "km"), @@ -147,6 +147,20 @@ class TargetingMiscViewFull(StatsView): label.SetToolTip(wx.ToolTip("Max Warp Distance: %.1f AU" % fit.maxWarpDistance)) else: label.SetToolTip(wx.ToolTip("")) + elif labelName == "labelFullCargo": + if fit: + # if you add stuff to cargo, the capacity doesn't change and thus it is still cached + # This assures us that we force refresh of cargo tooltip + capacity = fit.ship.getModifiedItemAttr("capacity") + cargoSize = 0 + for mod in fit.cargo: + cargoSize += mod.getModifiedItemAttr("volume") * mod.amount + a = capacity-cargoSize + tip = u"Capacity: %sm\u00B3\n"% capacity + tip += u"Available: %.1fm\u00B3" %a + label.SetToolTip(wx.ToolTip(tip)) + else: + label.SetToolTip(wx.ToolTip("")) counter += 1 diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index d33342410..4a61644e7 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -75,10 +75,16 @@ class AttributeDisplay(ViewColumn): else: attr = mod.getAttribute(self.info.name) + if self.info.name == "volume": + str = (formatAmount(attr, 3, 0, 3)) + if hasattr(mod, "amount"): + str = str + u"m\u00B3 (%s m\u00B3)"%(formatAmount(attr*mod.amount, 3, 0, 3)) + attr = str + if isinstance(attr, (float, int)): - return (formatAmount(attr, 3, 0, 3)) - else: - return attr if attr is not None else "" + attr = (formatAmount(attr, 3, 0, 3)) + + return attr if attr is not None else "" def getImageId(self, mod): return -1 diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 5e40a75cf..5b2a65d58 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -22,7 +22,7 @@ from gui import builtinViewColumns from gui.viewColumn import ViewColumn from gui import bitmapLoader import wx -from eos.types import Drone, Fit, Module, Slot, Rack +from eos.types import Drone, Cargo, Fit, Module, Slot, Rack import service class BaseName(ViewColumn): @@ -36,6 +36,8 @@ class BaseName(ViewColumn): def getText(self, stuff): if isinstance(stuff, Drone): return "%dx %s" % (stuff.amount, stuff.item.name) + elif isinstance(stuff, Cargo): + return "%dx %s" % (stuff.amount, stuff.item.name) elif isinstance(stuff, Fit): return "%s (%s)" % (stuff.name, stuff.ship.item.name) elif isinstance(stuff, Rack): diff --git a/gui/cargoView.py b/gui/cargoView.py new file mode 100644 index 000000000..765b495c8 --- /dev/null +++ b/gui/cargoView.py @@ -0,0 +1,117 @@ +#=============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +#=============================================================================== + +import wx +import service +import gui.display as d +import gui.marketBrowser as mb +from gui.builtinViewColumns.state import State +from gui.contextMenu import ContextMenu +import globalEvents as GE + +# @todo: Was copied form another class and modified. Look through entire file, refine +class CargoView(d.Display): + DEFAULT_COLS = ["Base Icon", + "Base Name", + "attr:volume"] + + def __init__(self, parent): + d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) + + self.lastFitId = None + + self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) + #self.mainFrame.Bind(mb.ITEM_SELECTED, self.addItem) + self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) + self.Bind(wx.EVT_KEY_UP, self.kbEvent) + + if "__WXGTK__" in wx.PlatformInfo: + self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) + else: + self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) + + def kbEvent(self,event): + keycode = event.GetKeyCode() + if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: + fitID = self.mainFrame.getActiveFit() + cFit = service.Fit.getInstance() + row = self.GetFirstSelected() + if row != -1: + cFit.removeCargo(fitID, self.GetItemData(row)) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + event.Skip() + + def fitChanged(self, event): + #Clear list and get out if current fitId is None + if event.fitID is None and self.lastFitId is not None: + self.DeleteAllItems() + self.lastFitId = None + event.Skip() + return + + cFit = service.Fit.getInstance() + fit = cFit.getFit(event.fitID) + + self.original = fit.cargo if fit is not None else None + self.cargo = stuff = fit.cargo if fit is not None else None + if stuff is not None: stuff.sort(key=lambda cargo: cargo.itemID) + + if event.fitID != self.lastFitId: + self.lastFitId = event.fitID + + item = self.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_DONTCARE) + + if item != -1: + self.EnsureVisible(item) + + self.deselectItems() + + self.populate(stuff) + self.refresh(stuff) + event.Skip() + + def removeItem(self, event): + row, _ = self.HitTest(event.Position) + if row != -1: + col = self.getColumn(event.Position) + if col != self.getColIndex(State): + fitID = self.mainFrame.getActiveFit() + cFit = service.Fit.getInstance() + cargo = self.cargo[self.GetItemData(row)] + cFit.removeCargo(fitID, self.original.index(cargo)) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + def scheduleMenu(self, event): + event.Skip() + if self.getColumn(event.Position) != self.getColIndex(State): + wx.CallAfter(self.spawnMenu) + + def spawnMenu(self): + sel = self.GetFirstSelected() + if sel != -1: + cFit = service.Fit.getInstance() + fit = cFit.getFit(self.mainFrame.getActiveFit()) + cargo = fit.cargo[sel] + + sMkt = service.Market.getInstance() + sourceContext = "cargoItem" + itemContext = sMkt.getCategoryByItem(cargo.item).name + + menu = ContextMenu.getMenu((cargo,), (sourceContext, itemContext)) + self.PopupMenu(menu) diff --git a/icons/cargo_small.png b/icons/cargo_small.png new file mode 100644 index 000000000..2906b9214 Binary files /dev/null and b/icons/cargo_small.png differ diff --git a/service/fit.py b/service/fit.py index 7f849cb37..5e6bdacd5 100644 --- a/service/fit.py +++ b/service/fit.py @@ -426,6 +426,47 @@ class Fit(object): fit.modules.insert(dst, new) eos.db.commit() + def addChangeCargo(self, fitID, c, amount): + if fitID == None: + return False + + fit = eos.db.getFit(fitID) + cargo = None + + if c not in fit.cargo: + # adding from market + for x in fit.cargo.find(c.item): + if x is not None: + # found item already in cargo, use previous value and remove old + cargo = x + fit.cargo.remove(x) + break + + if cargo is None: + # if we don't have the item already in cargo, use default values + cargo = c + else: + # changing existing + cargo = eos.types.Cargo(c.item) + fit.cargo.remove(c) # remove old + + fit.cargo.append(cargo) + cargo.amount += amount + + self.recalc(fit) + eos.db.commit() + + return True + + def removeCargo(self, fitID, position): + if fitID is None: + return False + + fit = eos.db.getFit(fitID) + charge = fit.cargo[position] + fit.cargo.remove(charge) + self.recalc(fit) + return True def addDrone(self, fitID, itemID): if fitID == None: