diff --git a/gui/boosterView.py b/gui/boosterView.py index 410247bbe..804268819 100644 --- a/gui/boosterView.py +++ b/gui/boosterView.py @@ -25,6 +25,20 @@ import gui.marketBrowser as mb from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu +class BoosterViewDrop(wx.PyDropTarget): + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class BoosterView(d.Display): DEFAULT_COLS = ["State", "attr:boosterness", @@ -42,11 +56,25 @@ class BoosterView(d.Display): self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_KEY_UP, self.kbEvent) + self.SetDropTarget(BoosterViewDrop(self.handleListDrag)) + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) + def handleListDrag(self, x, y, data): + ''' + Handles dragging of items from various pyfa displays which support it + + data is list with two indices: + data[0] is hard-coded str of originating source + data[1] is typeID or index of data we want to manipulate + ''' + + if data[0] == "market": + wx.PostEvent(self.mainFrame, mb.ItemSelected(itemID=int(data[1]))) + def kbEvent(self,event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: @@ -57,7 +85,6 @@ class BoosterView(d.Display): 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() @@ -101,7 +128,7 @@ class BoosterView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): self.removeBooster(self.boosters[self.GetItemData(row)]) - + def removeBooster(self, booster): fitID = self.mainFrame.getActiveFit() cFit = service.Fit.getInstance() diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py index 2284c11af..3afedf9c9 100644 --- a/gui/builtinContextMenus/cargo.py +++ b/gui/builtinContextMenus/cargo.py @@ -23,9 +23,9 @@ class Cargo(ContextMenu): 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) + typeID = int(selection[0].ID) + sFit.addCargo(fitID, typeID, 1) + self.mainFrame.additionsPane.select("Cargo") wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) Cargo.register() @@ -75,7 +75,7 @@ class CargoChanger(wx.Dialog): mainFrame = gui.mainFrame.MainFrame.getInstance() fitID = mainFrame.getActiveFit() - sFit.addChangeCargo(fitID, self.cargo, int(self.input.GetLineText(0))) + sFit.addCargo(fitID, self.cargo, int(self.input.GetLineText(0))) wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index ccd9a3f17..ee7d74b7e 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -9,7 +9,7 @@ class ItemStats(ContextMenu): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def display(self, srcContext, selection): - return srcContext in ("marketItemGroup", "marketItemMisc", "fittingModule", "fittingCharge", "fittingShip", "baseShip", + return srcContext in ("marketItemGroup", "marketItemMisc", "fittingModule", "fittingCharge", "fittingShip", "baseShip", "cargoItem", "droneItem", "implantItem", "boosterItem", "skillItem", "projectedModule", "projectedDrone", "projectedCharge") def getText(self, itmContext, selection): diff --git a/gui/builtinContextMenus/marketJump.py b/gui/builtinContextMenus/marketJump.py index e9cb7e48f..11b0651cf 100644 --- a/gui/builtinContextMenus/marketJump.py +++ b/gui/builtinContextMenus/marketJump.py @@ -9,7 +9,7 @@ class MarketJump(ContextMenu): def display(self, srcContext, selection): validContexts = ("marketItemMisc", "fittingModule", "fittingCharge", "droneItem", "implantItem", - "boosterItem", "projectedModule", "projectedDrone", "projectedCharge") + "boosterItem", "projectedModule", "projectedDrone", "projectedCharge", "cargoItem") if not srcContext in validContexts: return False sMkt = service.Market.getInstance() @@ -28,7 +28,7 @@ class MarketJump(ContextMenu): def activate(self, fullContext, selection, i): srcContext = fullContext[0] - if srcContext in ("fittingModule", "droneItem", "implantItem", "boosterItem", "projectedModule", "projectedDrone"): + if srcContext in ("fittingModule", "droneItem", "implantItem", "boosterItem", "projectedModule", "projectedDrone", "cargoItem"): item = selection[0].item elif srcContext in ("fittingCharge", "projectedCharge"): item = selection[0].charge diff --git a/gui/builtinContextMenus/project.py b/gui/builtinContextMenus/project.py index 535be8063..4b614756b 100644 --- a/gui/builtinContextMenus/project.py +++ b/gui/builtinContextMenus/project.py @@ -21,7 +21,9 @@ class Project(ContextMenu): def activate(self, fullContext, selection, i): sFit = service.Fit.getInstance() fitID = self.mainFrame.getActiveFit() - sFit.project(fitID, selection[0]) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + trigger = sFit.project(fitID, selection[0]) + if trigger: + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.additionsPane.select("Projected") Project.register() diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index bc407da4e..4aae999df 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -96,10 +96,10 @@ class FittingViewDrop(wx.PyDropTarget): def OnData(self, x, y, t): if self.GetData(): - self.dropFn(x, y, int(self.dropData.GetText())) + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) return t - class FittingView(d.Display): DEFAULT_COLS = ["State", "Ammo Icon", @@ -130,7 +130,7 @@ class FittingView(d.Display): else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - self.SetDropTarget(FittingViewDrop(self.swapItems)) + self.SetDropTarget(FittingViewDrop(self.handleListDrag)) self.activeFitID = None self.FVsnapshot = None self.itemCount = 0 @@ -175,7 +175,24 @@ class FittingView(d.Display): self.SetToolTip(None) event.Skip() + def handleListDrag(self, x, y, data): + ''' + Handles dragging of items from various pyfa displays which support it + + data is list with two items: + data[0] is hard-coded str of originating source + data[1] is typeID or index of data we want to manipulate + ''' + + if data[0] == "fitting": + self.swapItems(x, y, int(data[1])) + elif data[0] == "cargo": + self.swapCargo(x, y, int(data[1])) + elif data[0] == "market": + wx.PostEvent(self.mainFrame, gui.marketBrowser.ItemSelected(itemID=int(data[1]))) + def handleDrag(self, type, fitID): + #Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": wx.PostEvent(self.mainFrame, gui.shipBrowser.FitSelected(fitID=fitID)) @@ -202,9 +219,10 @@ class FittingView(d.Display): def startDrag(self, event): row = event.GetIndex() + if row != -1 and row not in self.blanks: data = wx.PyTextDataObject() - data.SetText(str(self.GetItemData(row))) + data.SetText("fitting:"+str(self.mods[row].position)) dropSource = wx.DropSource(self) dropSource.SetData(data) @@ -233,15 +251,16 @@ class FittingView(d.Display): event.Skip() def fitRemoved(self, event): - '''If fit is removed and active, the page is deleted. - We also refresh the fit of the new current page in case + ''' + If fit is removed and active, the page is deleted. + We also refresh the fit of the new current page in case delete fit caused change in stats (projected) ''' fitID = event.fitID if fitID == self.getActiveFit(): self.parent.DeletePage(self.parent.GetPageIndex(self)) - + try: # Sometimes there is no active page after deletion, hence the try block cFit = service.Fit.getInstance() @@ -249,7 +268,7 @@ class FittingView(d.Display): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) except wx._core.PyDeadObjectError: pass - + event.Skip() def fitRenamed(self, event): @@ -325,36 +344,46 @@ class FittingView(d.Display): self.slotsChanged() wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) - def swapItems(self, x, y, itemID): + def swapCargo(self, x, y, srcIdx): + '''Swap a module from cargo to fitting window''' + + dstRow, _ = self.HitTest((x, y)) + if dstRow != -1 and dstRow not in self.blanks: + module = self.mods[dstRow] + + cFit = service.Fit.getInstance() + cFit.swapModuleWithCargo(self.mainFrame.getActiveFit(), module.position, srcIdx) + + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + + def swapItems(self, x, y, srcIdx): + '''Swap two modules in fitting window''' mstate = wx.GetMouseState() + cFit = service.Fit.getInstance() + fit = cFit.getFit(self.activeFitID) if mstate.CmdDown(): clone = True else: clone = False - srcRow = self.FindItemData(-1,itemID) dstRow, _ = self.HitTest((x, y)) - if srcRow != -1 and dstRow != -1 and dstRow not in self.blanks: - self._swap(srcRow, dstRow, clone) - def _swap(self, srcRow, dstRow, clone = False): - mod1 = self.mods[self.GetItemData(srcRow)] - mod2 = self.mods[self.GetItemData(dstRow)] + if dstRow != -1 and dstRow not in self.blanks: + mod1 = fit.modules[srcIdx] + mod2 = self.mods[dstRow] - # can't swap modules to different racks - if mod1.slot != mod2.slot: - return + # can't swap modules to different racks + if mod1.slot != mod2.slot: + return - cFit = service.Fit.getInstance() + if clone and mod2.isEmpty: + cFit.cloneModule(self.mainFrame.getActiveFit(), mod1.position, mod2.position) + else: + cFit.swapModules(self.mainFrame.getActiveFit(), mod1.position, mod2.position) - if clone and mod2.isEmpty: - cFit.cloneModule(self.mainFrame.getActiveFit(), mod1.position, mod2.position) - else: - cFit.swapModules(self.mainFrame.getActiveFit(), mod1.position, mod2.position) - - self.generateMods() - self.refresh(self.mods) + self.generateMods() + self.refresh(self.mods) def generateMods(self): ''' diff --git a/gui/cargoView.py b/gui/cargoView.py index 765b495c8..d0aaa5610 100644 --- a/gui/cargoView.py +++ b/gui/cargoView.py @@ -25,6 +25,20 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE +class CargoViewDrop(wx.PyDropTarget): + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + # @todo: Was copied form another class and modified. Look through entire file, refine class CargoView(d.Display): DEFAULT_COLS = ["Base Icon", @@ -37,15 +51,44 @@ class CargoView(d.Display): 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) + self.SetDropTarget(CargoViewDrop(self.handleListDrag)) + self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) + def handleListDrag(self, x, y, data): + ''' + Handles dragging of items from various pyfa displays which support it + + data is list with two indices: + data[0] is hard-coded str of originating source + data[1] is typeID or index of data we want to manipulate + ''' + + if data[0] == "fitting": + self.swapModule(x, y, int(data[1])) + elif data[0] == "market": + sFit = service.Fit.getInstance() + sFit.addCargo(self.mainFrame.getActiveFit(), int(data[1]), 1) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + + def startDrag(self, event): + row = event.GetIndex() + + if row != -1: + data = wx.PyTextDataObject() + data.SetText("cargo:"+str(row)) + + dropSource = wx.DropSource(self) + dropSource.SetData(data) + res = dropSource.DoDragDrop() + def kbEvent(self,event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: @@ -57,6 +100,20 @@ class CargoView(d.Display): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) event.Skip() + def swapModule(self, x, y, modIdx): + '''Swap a module from fitting window with cargo''' + + dstRow, _ = self.HitTest((x, y)) + if dstRow != -1: + # Gather module information to get position + sFit = service.Fit.getInstance() + fit = sFit.getFit(self.mainFrame.getActiveFit()) + module = fit.modules[modIdx] + + sFit.swapModuleWithCargo(self.mainFrame.getActiveFit(), module.position, dstRow) + + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + 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: diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index ec830b462..852e01452 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -227,6 +227,7 @@ class ItemView(d.Display): # Make sure WE do interesting stuff too self.Bind(wx.EVT_CONTEXT_MENU, self.contextMenu) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated) + self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) # Make reverse map, used by sorter self.metaMap = self.makeReverseMetaMap() @@ -235,6 +236,18 @@ class ItemView(d.Display): for itemID in self.sMarket.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: self.recentlyUsedModules.add(self.sMarket.getItem(itemID)) + def startDrag(self, event): + row = self.GetFirstSelected() + + if row != -1: + data = wx.PyTextDataObject() + data.SetText("market:"+str(self.active[row].ID)) + + dropSource = wx.DropSource(self) + dropSource.SetData(data) + res = dropSource.DoDragDrop() + + def itemActivated(self, event=None): # Check if something is selected, if so, spawn the menu for it sel = self.GetFirstSelected() diff --git a/gui/projectedView.py b/gui/projectedView.py index d8a4b9483..dc97b6d06 100644 --- a/gui/projectedView.py +++ b/gui/projectedView.py @@ -26,7 +26,6 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import eos.types - class ProjectedViewDrop(wx.PyDropTarget): def __init__(self, dropFn): wx.PyDropTarget.__init__(self) @@ -37,7 +36,8 @@ class ProjectedViewDrop(wx.PyDropTarget): def OnData(self, x, y, t): if self.GetData(): - self.dropFn(x, y, int(self.dropData.GetText())) + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) return t class ProjectedView(d.Display): @@ -59,14 +59,32 @@ class ProjectedView(d.Display): self.Bind(wx.EVT_KEY_UP, self.kbEvent) self.droneView = gui.droneView.DroneView - + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) - self.SetDropTarget(ProjectedViewDrop(self.mergeDrones)) + self.SetDropTarget(ProjectedViewDrop(self.handleListDrag)) + + def handleListDrag(self, x, y, data): + ''' + Handles dragging of items from various pyfa displays which support it + + data is list with two indices: + data[0] is hard-coded str of originating source + data[1] is typeID or index of data we want to manipulate + ''' + + if data[0] == "projected": + # if source is coming from projected, we are trying to combine drones. + self.mergeDrones(x, y, int(data[1])) + elif data[0] == "market": + sFit = service.Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + sFit.project(fitID, int(data[1])) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) def kbEvent(self,event): keycode = event.GetKeyCode() @@ -87,14 +105,14 @@ class ProjectedView(d.Display): if activeFit: sFit = service.Fit.getInstance() draggedFit = sFit.getFit(fitID) - sFit.project(activeFit,draggedFit) + sFit.project(activeFit, draggedFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) def startDrag(self, event): row = event.GetIndex() if row != -1 and isinstance(self.get(row), eos.types.Drone): data = wx.PyTextDataObject() - data.SetText(str(self.GetItemData(row))) + data.SetText("projected:"+str(self.GetItemData(row))) dropSource = wx.DropSource(self) dropSource.SetData(data) @@ -187,7 +205,7 @@ class ProjectedView(d.Display): sFit = service.Fit.getInstance() sFit.toggleProjected(fitID, item, "right" if event.Button == 3 else "left") wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - + def scheduleMenu(self, event): event.Skip() if self.getColumn(event.Position) != self.getColIndex(State): diff --git a/service/fit.py b/service/fit.py index 5e6bdacd5..46167a88e 100644 --- a/service/fit.py +++ b/service/fit.py @@ -27,7 +27,7 @@ from codecs import open import eos.db import eos.types -from eos.types import State, Slot +from eos.types import State, Slot, Module, Cargo from service.market import Market from service.damagePattern import DamagePattern @@ -162,8 +162,8 @@ class Fit(object): fit = eos.db.getFit(fitID) sFlt = Fleet.getInstance() sFlt.removeAssociatedFleetData(fit) - self.removeProjectedData(fitID) - + self.removeProjectedData(fitID) + eos.db.remove(fit) def copyFit(self, fitID): @@ -179,15 +179,15 @@ class Fit(object): fit = eos.db.getFit(fitID) fit.clear() return fit - + def removeProjectedData(self, fitID): '''Removes projection relation from ships that have fitID as projection. See GitHub issue #90''' fit = eos.db.getFit(fitID) fits = eos.db.getProjectedFits(fitID) - + for projectee in fits: projectee.projectedFits.remove(fit) - + def toggleFactorReload(self, fitID): if fitID is None: return None @@ -294,6 +294,10 @@ class Fit(object): def project(self, fitID, thing): fit = eos.db.getFit(fitID) + + if isinstance(thing, int): + thing = eos.db.getItem(thing, eager=("attributes", "group.category")) + if isinstance(thing, eos.types.Fit): if thing.ID == fitID: return @@ -323,6 +327,7 @@ class Fit(object): eos.db.commit() self.recalc(fit) + return True def toggleProjected(self, fitID, thing, click): fit = eos.db.getFit(fitID) @@ -391,6 +396,40 @@ class Fit(object): eos.db.commit() return numSlots != len(fit.modules) + def swapModuleWithCargo(self, fitID, moduleIdx, cargoIdx): + ''' + This swaps a module from the cargo with a module from fit. + + 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. + ''' + fit = eos.db.getFit(fitID) + + # Gather modules and convert Cargo item to Module, silently return if not a module + try: + cargoP = Module(fit.cargo[cargoIdx].item) + if cargoP.isValidState(State.ACTIVE): + cargoP.state = State.ACTIVE + module = fit.modules[moduleIdx] + moduleP = Cargo(module.item) + moduleP.amount = 1 + except Exception, e: + return + + # can't swap modules to different racks + if cargoP.slot != module.slot: + return + + # To swap, we simply remove mod and insert at destination. + fit.modules.remove(module) + fit.modules.insert(moduleIdx, cargoP) + fit.cargo.remove(fit.cargo[cargoIdx]) + fit.cargo.insert(cargoIdx, moduleP) + + eos.db.commit() + self.recalc(fit) + def swapModules(self, fitID, src, dst): fit = eos.db.getFit(fitID) # Gather modules @@ -426,29 +465,26 @@ class Fit(object): fit.modules.insert(dst, new) eos.db.commit() - def addChangeCargo(self, fitID, c, amount): + + def addCargo(self, fitID, itemID, amount): if fitID == None: return False fit = eos.db.getFit(fitID) + item = eos.db.getItem(itemID) 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 + # adding from market + for x in fit.cargo.find(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 + if cargo is None: + # if we don't have the item already in cargo, use default values + cargo = eos.types.Cargo(item) fit.cargo.append(cargo) cargo.amount += amount