Add support for drag and drop between pyfa displays which explicitly support it (some other tweaks as well)

This commit is contained in:
blitzmann
2014-05-07 11:21:50 -04:00
parent d05b78a451
commit 294f51ae5d
10 changed files with 249 additions and 67 deletions

View File

@@ -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()

View File

@@ -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))

View File

@@ -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):

View File

@@ -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

View File

@@ -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()

View File

@@ -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):
'''

View File

@@ -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:

View File

@@ -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()

View File

@@ -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):

View File

@@ -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