container.py (60902B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\container.py 2 from math import pi, fabs 3 import eveicon 4 import evetypes 5 import eveui 6 import gametime 7 import mathext 8 from carbon.common.script.sys.serviceConst import ROLE_GML, ROLE_PROGRAMMER, ROLE_WORLDMOD 9 from carbon.common.script.util import timerstuff 10 from carbon.common.script.util.commonutils import StripTags 11 from carbonui import Density 12 from carbonui.control.contextMenu.menuData import MenuData 13 from carbonui.control.scrollentries import SE_BaseClassCore 14 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText 15 from carbonui.primitives.container import Container 16 from carbonui.primitives.containerAutoSize import ContainerAutoSize 17 from carbonui.primitives.fill import Fill 18 from clientsettings.setting import UserSettingEnum 19 from carbonui.util.various_unsorted import NiceFilter 20 from eve.client.script.ui import eveColor, eveThemeColor 21 from eve.client.script.ui.control import eveLabel, eveScroll 22 from carbonui.control.buttonIcon import ButtonIcon 23 from eve.client.script.ui.control.entries.util import GetFromClass 24 from eve.client.script.ui.control.themeColored import FillThemeColored, FrameThemeColored 25 from eve.client.script.ui.shared.inventory import invSettings 26 from eve.client.script.ui.shared.item import InvItem 27 from eve.common.script.sys import eveCfg 28 from eve.common.script.util.eveFormat import GetAveragePrice 29 from eveexceptions import UserError 30 from inventorycommon.const import VIEW_MODES, SCROLL_VIEW_MODES, VIEWMODE_DETAILS, VIEWMODE_ICONS, VIEWMODE_LIST, VIEWMODE_CARDS, flagFrigateEscapeBay, flagStructureDeed 31 from inventorycommon.util import GetItemVolume 32 from eve.client.script.ui.util import uix 33 import blue 34 import carbon.client.script.util.lg as lg 35 import uthread 36 import carbonui.const as uiconst 37 import log 38 import trinity 39 import eve.client.script.environment.invControllers as invCtrl 40 import localization 41 import telemetry 42 import functools 43 from carbonui.uicore import uicore 44 from menu import MenuLabel 45 from bannedwords.client import bannedwords 46 from eve.common.lib import appConst as const 47 VIEW_MODE_ROW_PADDING = {VIEWMODE_LIST: 0, 48 VIEWMODE_DETAILS: 0, 49 VIEWMODE_ICONS: 12, 50 VIEWMODE_CARDS: 6} 51 52 class InvContViewBtns(ContainerAutoSize): 53 default_name = 'InvContViewBtns' 54 default_align = uiconst.TOPLEFT 55 default_height = 24 56 57 def ApplyAttributes(self, attributes): 58 ContainerAutoSize.ApplyAttributes(self, attributes) 59 self.controller = attributes.controller 60 self.viewModeButtons = {} 61 for viewMode in self.controller.availableViewModes: 62 self.viewModeButtons[viewMode] = ButtonIcon(texturePath=self.controller.GetViewModeIcon(viewMode), parent=self, width=self.height, align=uiconst.TOLEFT, func=functools.partial(self._SetViewMode, viewMode), hint=self.controller.GetViewModeName(viewMode)) 63 64 def _SetViewMode(self, viewMode): 65 self.UpdateButtons(viewMode) 66 self.controller.SetInvContViewMode(viewMode) 67 68 def UpdateButtons(self, activeViewMode): 69 for viewMode, button in self.viewModeButtons.iteritems(): 70 if viewMode == activeViewMode: 71 button.SetSelected() 72 else: 73 button.SetDeselected() 74 75 76 class InvContQuickFilter(Container): 77 default_align = uiconst.TOPLEFT 78 default_height = 24 79 default_width = 100 80 81 def ApplyAttributes(self, attributes): 82 Container.ApplyAttributes(self, attributes) 83 self.invCont = attributes.invCont 84 self.inputThread = None 85 self.quickFilterInputBox = SingleLineEditText(name='quickFilterInputBox', parent=self, align=uiconst.CENTER, width=self.width, height=24, top=0, density=Density.COMPACT, OnChange=self.SetQuickFilterInput, isTypeField=True, hintText=localization.GetByLabel('UI/Common/Search')) 86 self.quickFilterInputBox.ShowClearButton(hint=localization.GetByLabel('UI/Inventory/Clear')) 87 self.quickFilterInputBox.SetHistoryVisibility(0) 88 if self.invCont: 89 self.SetInvCont(self.invCont) 90 91 def SetInvCont(self, invCont): 92 self.invCont = invCont 93 if not invSettings.keep_inv_quick_filter_setting.is_enabled(): 94 self.quickFilterInputBox.SetText(u'') 95 self.quickFilterInputBox.ClearSelection() 96 97 def GetQuickFilterInput(self): 98 return self.quickFilterInputBox.GetText() 99 100 def SetQuickFilterInput(self, txt): 101 if self.inputThread: 102 return 103 self.inputThread = uthread.new(self._SetQuickFilterInput, txt) 104 105 def ClearFilter(self): 106 self.quickFilterInputBox.SetValue('') 107 108 def _SetQuickFilterInput(self, txt): 109 if txt: 110 blue.synchro.SleepWallclock(300) 111 if self.invCont: 112 self.invCont.SetQuickFilterInput(self.quickFilterInputBox.GetValue()) 113 self.inputThread = None 114 115 def UpdateInputBoxAlign(self, newAlign): 116 self.quickFilterInputBox.SetAlign(newAlign) 117 118 119 class InvContCapacityGauge(Container): 120 __notifyevents__ = ['OnItemChange', 'OnSessionChanged', 'OnSessionMutated'] 121 default_align = uiconst.TOPLEFT 122 default_state = uiconst.UI_NORMAL 123 default_clipChildren = True 124 125 def ApplyAttributes(self, attributes): 126 Container.ApplyAttributes(self, attributes) 127 sm.RegisterNotify(self) 128 invCont = attributes.invCont 129 self.secondaryVolume = 0.0 130 self.additionalVolume = 0.0 131 self.refreshGaugesThread = None 132 self.resetPending = False 133 self.refreshCapacityPending = False 134 self.queuedItemChanges = [] 135 self.capacityText = eveLabel.EveLabelSmall(name='capacityText', parent=self, align=uiconst.CENTERLEFT, top=1, left=8) 136 self.bg = Fill(bgParent=self, color=(0.0, 0.0, 0.0), opacity=0.4) 137 self.capacityGaugeParentSec = Container(name='capacityGaugeParent', parent=self, align=uiconst.TOALL, state=uiconst.UI_DISABLED) 138 self.capacityGaugeSec = Fill(parent=self.capacityGaugeParentSec, align=uiconst.TOLEFT_PROP, color=self._get_gauge_highlight_color()) 139 self.capacityGaugeParent = Container(name='capacityGaugeParent', parent=self, align=uiconst.TOALL, state=uiconst.UI_DISABLED) 140 self.capacityGauge = Fill(parent=self.capacityGaugeParent, align=uiconst.TOLEFT_PROP, color=self._get_gauge_color()) 141 self.capacityGaugeAdd = Fill(parent=self.capacityGaugeParent, align=uiconst.TOLEFT_PROP, color=self._get_gauge_highlight_color()) 142 if invCont: 143 self.SetInvCont(invCont) 144 else: 145 self.invCont = None 146 147 @staticmethod 148 def _get_gauge_color(): 149 return tuple(eveThemeColor.THEME_FOCUSDARK[:3]) + (0.6,) 150 151 @staticmethod 152 def _get_gauge_highlight_color(): 153 return tuple(eveThemeColor.THEME_FOCUS[:3]) + (0.6,) 154 155 def SetInvCont(self, invCont): 156 self.invCont = invCont 157 self.SetSecondaryVolume() 158 self.SetAdditionalVolume() 159 self.RefreshCapacity() 160 161 @telemetry.ZONE_METHOD 162 def RefreshCapacity(self): 163 if not session.IsItSafe(): 164 self.refreshCapacityPending = True 165 return 166 self.refreshCapacityPending = False 167 if not self.invCont: 168 return 169 self.UpdateLabel() 170 proportion = 0.0 171 if self.invCont.invController.hasCapacity: 172 cap = self.invCont.invController.GetCapacity() 173 if cap.capacity: 174 proportion = min(1.0, max(0.0, cap.used / float(cap.capacity))) 175 currWidth = self.capacityGauge.width 176 duration = 0.5 * fabs(currWidth - proportion) ** 0.3 177 uicore.animations.MorphScalar(self.capacityGauge, 'width', currWidth, proportion, duration=duration) 178 179 def UpdateLabel(self): 180 currentInvCont = self.invCont 181 if currentInvCont is None or currentInvCont.destroyed: 182 return 183 if currentInvCont.invController.hasCapacity: 184 cap = currentInvCont.invController.GetCapacity() 185 volume = cap.used + self.additionalVolume 186 text = localization.GetByLabel('UI/Inventory/ContainerQuantityAndCapacity', quantity=volume, capacity=cap.capacity) 187 else: 188 volume = 0.0 189 for item in currentInvCont.invController.GetItems(): 190 volume += GetItemVolume(item) 191 192 text = localization.GetByLabel('UI/Inventory/ContainerCapacity', capacity=volume) 193 if currentInvCont is None or currentInvCont.destroyed: 194 return 195 if self.secondaryVolume: 196 if text: 197 text = '(%s) ' % localization.formatters.FormatNumeric(self.secondaryVolume, useGrouping=True, decimalPlaces=1) + text 198 if currentInvCont.invController.locationFlag == flagFrigateEscapeBay: 199 text = localization.GetByLabel('UI/Inventory/FrigateEscapeBayQuantityAndCapacity', quantity=volume, capacity=cap.capacity) 200 elif currentInvCont.invController.locationFlag == flagStructureDeed: 201 text = localization.GetByLabel('UI/Inventory/DeedBayQuantityAndCapacity', quantity=1 if volume else 0, capacity=1) 202 self.capacityText.text = text 203 204 def HideLabel(self): 205 self.capacityText.Hide() 206 207 def ShowLabel(self): 208 self.capacityText.Show() 209 210 def GetHint(self): 211 return self.capacityText.text 212 213 @telemetry.ZONE_METHOD 214 def SetAdditionalVolume(self, items = []): 215 if not self.invCont: 216 return 217 volume = 0 218 for item in items: 219 volume += GetItemVolume(item) 220 221 self.additionalVolume = volume 222 value = self.GetVolumeProportion(volume) 223 animValue = min(value, 1.0 - self.capacityGauge.width) 224 currWidth = self.capacityGaugeAdd.width 225 duration = 0.5 * fabs(currWidth - animValue) ** 0.3 226 uicore.animations.MorphScalar(self.capacityGaugeAdd, 'width', currWidth, animValue, duration=duration) 227 color = self._get_gauge_highlight_color() 228 if self.invCont.invController.hasCapacity: 229 cap = self.invCont.invController.GetCapacity() 230 if cap.capacity and volume + cap.used > cap.capacity: 231 color = eveColor.WARNING_ORANGE[:3] 232 self.capacityGaugeAdd.SetRGBA(*color) 233 self.UpdateLabel() 234 235 @telemetry.ZONE_METHOD 236 def SetSecondaryVolume(self, items = []): 237 self.secondaryVolume = volume = sum([ GetItemVolume(i) for i in items ]) 238 value = self.GetVolumeProportion(volume) 239 currWidth = self.capacityGaugeSec.width 240 duration = 0.5 * fabs(currWidth - value) ** 0.3 241 uicore.animations.MorphScalar(self.capacityGaugeSec, 'width', currWidth, value, duration=duration) 242 self.UpdateLabel() 243 244 def GetVolumeProportion(self, volume): 245 if self.invCont and self.invCont.invController.hasCapacity: 246 cap = self.invCont.invController.GetCapacity() 247 if cap.capacity: 248 return min(1.0, volume / cap.capacity) 249 return 0 250 251 def OnItemChange(self, item, change, location): 252 itemID = item.itemID 253 sourceLocationID = item.locationID 254 destinationLocationID = change[const.ixLocationID] if const.ixLocationID in change else item.locationID 255 if not session.IsItSafe(): 256 self.queuedItemChanges.append((itemID, sourceLocationID, destinationLocationID)) 257 return 258 if self._ShouldResetGauges(itemID, sourceLocationID, destinationLocationID): 259 self.ResetGauges() 260 261 def _ShouldResetGauges(self, itemID, sourceLocationID, destinationLocationID): 262 if not self.invCont or itemID == eveCfg.GetActiveShip(): 263 return False 264 return self.invCont.invController.itemID in (sourceLocationID, destinationLocationID) 265 266 def _ApplyItemChangesReceivedDuringSessionChange(self): 267 if not session.IsItSafe(): 268 return 269 try: 270 for itemID, sourceLocationID, destinationLocationID in self.queuedItemChanges: 271 if self._ShouldResetGauges(itemID, sourceLocationID, destinationLocationID): 272 self.ResetGauges() 273 break 274 275 if self.refreshCapacityPending: 276 self.RefreshCapacity() 277 finally: 278 self.queuedItemChanges = [] 279 280 def OnSessionChanged(self, *args, **kwargs): 281 self._ApplyItemChangesReceivedDuringSessionChange() 282 283 def OnSessionMutated(self, *args, **kwargs): 284 self._ApplyItemChangesReceivedDuringSessionChange() 285 286 def ResetGauges(self): 287 self.resetPending = True 288 if self.refreshGaugesThread: 289 return 290 self.refreshGaugesThread = uthread.new(self._ResetGauges) 291 292 def _ResetGauges(self): 293 try: 294 while self.resetPending: 295 self.RefreshCapacity() 296 nodes = self.invCont.scroll.GetSelected() 297 self.SetSecondaryVolume([ node.item for node in nodes ]) 298 self.SetAdditionalVolume() 299 self.resetPending = False 300 blue.synchro.Sleep(500) 301 302 finally: 303 self.refreshGaugesThread = None 304 305 def OnColorThemeChanged(self): 306 if self.capacityGauge: 307 self.capacityGauge.color = self._get_gauge_color() 308 if self.capacityGaugeSec: 309 self.capacityGaugeSec.color = self._get_gauge_highlight_color() 310 if self.capacityGaugeAdd: 311 self.capacityGaugeAdd.color = self._get_gauge_highlight_color() 312 313 314 class ItemSortData(object): 315 316 def __init__(self, rec): 317 self.rec = rec 318 self.name = StripTags(uix.GetItemName(rec).lower()) 319 self.typeName = StripTags(uix.GetCategoryGroupTypeStringForItem(rec).lower()) 320 self.itemID = rec.itemID 321 self.quantity = 0 322 if not (rec.singleton or rec.typeID in (const.typeBookmark,)): 323 self.quantity = rec.stacksize 324 325 def GetSortKey(self, sortby, direction): 326 if sortby == 'name': 327 return (self.name, 328 self.typeName, 329 self.quantity, 330 self.itemID, 331 self.rec) 332 if sortby == 'qty': 333 return (self.quantity, 334 self.typeName, 335 self.name, 336 self.itemID, 337 self.rec) 338 if sortby == 'type': 339 return (self.typeName, 340 self.name, 341 self.quantity, 342 self.itemID, 343 self.rec) 344 if sortby == 'estPrice': 345 estimatedPrice = self.GetEstimatedPrice() 346 return (estimatedPrice, 347 self.name, 348 self.typeName, 349 self.quantity, 350 self.itemID, 351 self.rec) 352 353 def GetEstimatedPrice(self): 354 if evetypes.GetCategoryID(self.rec.typeID) == const.categoryBlueprint: 355 return None 356 averagePrice = GetAveragePrice(self.rec) 357 if not averagePrice: 358 return None 359 return self.rec.stacksize * averagePrice 360 361 362 class _InvContBase(Container): 363 __guid__ = 'invCont._InvContBase' 364 default_name = 'InventoryContainer' 365 default_showControls = False 366 default_containerViewMode = VIEWMODE_ICONS 367 default_availableViewModes = (VIEWMODE_ICONS, VIEWMODE_DETAILS, VIEWMODE_LIST) 368 default_hasScrollBackground = False 369 default_shouldAddFinalRowPadding = True 370 default_scrollClass = eveScroll.Scroll 371 default_selectAllOnFirstLoad = False 372 __notifyevents__ = ['OnPostCfgDataChanged', 'OnInvTempFilterChanged'] 373 __invControllerClass__ = None 374 375 def ApplyAttributes(self, attributes): 376 Container.ApplyAttributes(self, attributes) 377 sm.RegisterNotify(self) 378 self.itemID = attributes.itemID 379 self.displayName = attributes.displayName 380 self.activeFilters = attributes.get('activeFilters', []) 381 showControls = attributes.get('showControls', self.default_showControls) 382 self.quickFilterInput = attributes.Get('quickFilterInput', None) 383 self.hasScrollBackground = attributes.get('hasScrollBackground', self.default_hasScrollBackground) 384 self.shouldAddFinalRowPadding = attributes.get('shouldAddFinalRowPadding', self.default_shouldAddFinalRowPadding) 385 self.scrollClass = attributes.get('scrollClass', self.default_scrollClass) 386 self.selectAllOnFirstLoad = attributes.get('selectAllOnFirstLoad', self.default_selectAllOnFirstLoad) 387 self.availableViewModes = attributes.get('availableViewModes', self.default_availableViewModes) 388 self.getItemClass = attributes.get('getItemClass', self.GetItemClass) 389 self.invController = self._GetInvController(attributes) 390 self.scroll = None 391 self.items = [] 392 self.cols = None 393 self.droppedIconData = (None, None) 394 self.iconWidth = 64 395 self.sr.resizeTimer = None 396 self.tmpDropIdx = {} 397 self.refreshingView = False 398 self.reRefreshView = False 399 self.changeViewModeThread = None 400 self.hintText = None 401 self.dragStart = None 402 self.previouslyHighlighted = None 403 self.dragContainer = None 404 self.itemChangeThread = None 405 self.initialized = False 406 self.numFilteredItems = 0 407 self.hasLoaded = False 408 self.view_mode_setting = UserSettingEnum(settings_key=self.GetViewModeSettingKey(), options=VIEW_MODES, default_value=self.default_containerViewMode) 409 self.rowPadding = 0 410 self.colMargin = 16 411 self._SetViewModeValue(self.view_mode_setting.get()) 412 if showControls: 413 self.topCont = Container(parent=self, align=uiconst.TOTOP, height=24, padBottom=8) 414 InvContViewBtns(parent=self.topCont, align=uiconst.CENTERLEFT, controller=self) 415 if self.IsQuickFilterEnabled(): 416 InvContQuickFilter(parent=self.topCont, align=uiconst.CENTERRIGHT, invCont=self) 417 self.ConstructUI() 418 419 def ConstructUI(self): 420 self.scroll = self.scrollClass(parent=self, state=uiconst.UI_PICKCHILDREN, hasUnderlay=self.hasScrollBackground, rowPadding=self.rowPadding, shouldAddFinalRowPadding=self.shouldAddFinalRowPadding) 421 self.scroll.sr.id = self._GetIDForScroll() 422 self.scroll.OnNewHeaders = self.OnNewHeadersSet 423 self.scroll.allowFilterColumns = 1 424 self.scroll.SetColumnsHiddenByDefault(uix.GetInvItemDefaultHiddenHeaders()) 425 self.scroll.dad = self 426 self.scroll.Copy = self.Copy 427 self.scroll.Cut = self.Cut 428 self.scroll.Paste = self.Paste 429 content = self.content = self.scroll.GetContentContainer() 430 content.OnDragEnter = self.OnDragEnter 431 content.OnDragExit = self.OnDragExit 432 content.OnDropData = self.OnScrollContentDropData 433 content.GetMenu = lambda : self.GetContainerMenu() 434 content.OnMouseUp = self.scroll.OnMouseUp = self.OnScrollMouseUp 435 content.OnMouseDown = self.scroll.OnMouseDown = self.OnScrollMouseDown 436 self.invSvcCookie = sm.GetService('inv').Register(self) 437 self.InitializeSortBy() 438 uthread.new(self._ChangeViewMode) 439 self.view_mode_setting.on_change.connect(self._on_view_mode_setting_changed) 440 441 def _GetIDForScroll(self): 442 return 'containerWnd__%s' % self.invController.GetName() 443 444 def _on_view_mode_setting_changed(self, view_mode): 445 if self.viewMode != view_mode: 446 self._SetViewModeValue(view_mode) 447 self._ChangeViewMode() 448 449 def InitializeSortBy(self): 450 self.sortIconsBy, self.sortIconsDir = settings.char.ui.Get('containerSortIconsBy_%s' % self.name, ('type', 0)) 451 452 def _GetInvController(self, attributes): 453 return self.__invControllerClass__(itemID=attributes.itemID) 454 455 def GetItemClass(self): 456 return InvItem 457 458 def GetViewModeSettingKey(self): 459 return u'containerViewMode_{}'.format(self.name) 460 461 def SetInvContViewMode(self, value): 462 self._SetViewModeValue(value) 463 self.view_mode_setting.set(self.viewMode) 464 self._ChangeViewMode() 465 466 def _SetViewModeValue(self, value): 467 if value in self.availableViewModes: 468 self.viewMode = value 469 else: 470 self.viewMode = self.availableViewModes[0] if self.availableViewModes else None 471 self.itemClass = self.getItemClass() 472 self.itemHeight = self.itemClass.GetEntryHeight() 473 self.itemWidth = self.itemClass.GetEntryWidth() 474 self.colMargin = self.itemClass.GetEntryColMargin() 475 self.rowPadding = VIEW_MODE_ROW_PADDING.get(self.viewMode, 6) 476 if self.scroll: 477 self.scroll.rowPadding = self.rowPadding 478 self.cols = None 479 480 def IsViewModesEnabled(self): 481 return bool(self.availableViewModes) 482 483 def IsQuickFilterEnabled(self): 484 return True 485 486 def IsCapacityEnabled(self): 487 return True 488 489 def IsEstimatedValueEnabled(self): 490 return True 491 492 def SetQuickFilterInput(self, filterTxt = ''): 493 if len(filterTxt) > 0: 494 self.quickFilterInput = filterTxt.lower() 495 self.Refresh() 496 else: 497 prefilter = self.quickFilterInput 498 self.quickFilterInput = None 499 if prefilter != None: 500 self.Refresh() 501 self.hintText = None 502 503 def QuickFilter(self, item): 504 name = uix.GetItemName(item).lower() 505 typename = evetypes.GetName(item.typeID).lower() 506 input = self.quickFilterInput.lower() 507 return name.find(input) + 1 or typename.find(input) + 1 508 509 def OnInvTempFilterChanged(self): 510 self.Refresh() 511 512 def SetFilters(self, filters): 513 self.activeFilters = filters 514 self.Refresh() 515 516 def FilterOptionsChange(self, combo, label, value, *args): 517 if combo and combo.name == 'filterCateg': 518 if value is None: 519 ops = [(localization.GetByLabel('UI/Inventory/Filters/All'), None)] 520 else: 521 ops = sm.GetService('marketutils').GetFilterops(value) 522 self.sr.filterGroup.LoadOptions(ops) 523 settings.user.ui.Set('containerCategoryFilter%s' % self.name.title(), value) 524 elif combo and combo.name == 'filterGroup': 525 settings.user.ui.Set('containerGroupFilter%s' % self.name.title(), value) 526 self.Refresh() 527 528 def OnPostCfgDataChanged(self, what, data): 529 if what == 'evelocations': 530 itemID = data[0] 531 for each in self.scroll.GetNodes(): 532 if each and getattr(each, 'item', None) and each.item.itemID == itemID: 533 each.name = None 534 uix.GetItemLabel(each.item, each, 1) 535 if each.panel: 536 each.panel.UpdateLabel() 537 return 538 539 def _OnClose(self, *args): 540 if self.itemChangeThread: 541 self.itemChangeThread.kill() 542 543 def _ChangeViewMode(self): 544 if self.changeViewModeThread: 545 self.changeViewModeThread.kill() 546 self.changeViewModeThread = uthread.new(self.__ChangeViewMode) 547 548 def __ChangeViewMode(self): 549 if self.destroyed: 550 return 551 self.items = self.invController.GetItems() 552 self.invController.OnItemsViewed() 553 self.PrimeItems([ item for item in self.items if item is not None ]) 554 if self.items: 555 self.items = self.FilterItems(self.items) 556 self.UpdateHint() 557 try: 558 if self.ShouldShowSortBy(): 559 self.RefreshSort(self.sortIconsBy, self.sortIconsDir) 560 else: 561 self.RefreshView() 562 except UserError: 563 self.Close() 564 if self.sr.Get('cookie', None): 565 sm.GetService('inv').Unregister(self.invSvcCookie) 566 raise 567 568 if self.selectAllOnFirstLoad and not self.hasLoaded: 569 self.SelectAll() 570 self.hasLoaded = True 571 572 def IsInItems(self, itemID): 573 for rec in self.items: 574 if rec and rec.itemID == itemID: 575 return True 576 577 return False 578 579 def FilterItems(self, items): 580 oldNum = len(items) 581 if self.quickFilterInput: 582 bannedwords.check_search_words_allowed(self.quickFilterInput) 583 items = NiceFilter(self.QuickFilter, items) 584 for filter in self.activeFilters: 585 items = sm.GetService('itemFilter').FilterItems(items, filter) 586 587 if self.invController.filtersEnabled: 588 items = sm.GetService('itemFilter').ApplyTempFilter(items) 589 self.numFilteredItems = oldNum - len(items) 590 return items 591 592 def UpdateHint(self): 593 if len(self.items) == 0: 594 if self.numFilteredItems: 595 self.hintText = localization.GetByLabel('UI/Inventory/NothingFoundFiltered', numFilters=self.numFilteredItems) 596 elif not self.invController.CheckCanQuery(): 597 self.hintText = localization.GetByLabel('UI/Inventory/NoAccessHint') 598 elif self.invController.isCompact and self.invController.GetName(): 599 self.hintText = localization.GetByLabel('UI/Common/NothingFoundInContainer', invContainerName=self.invController.GetName()) 600 else: 601 self.hintText = localization.GetByLabel('UI/Common/NothingFound') 602 else: 603 self.hintText = '' 604 self.scroll.ShowHint(self.hintText) 605 606 def Refresh(self): 607 self._ChangeViewMode() 608 609 def GetSortData(self, sortby, direction): 610 sortData = [] 611 for rec in self.items: 612 if rec is None: 613 continue 614 sortKey = ItemSortData(rec).GetSortKey(sortby, direction) 615 if not sortKey: 616 log.LogError('Unknown sortkey used in container sorting', sortby, direction) 617 continue 618 sortData.append((sortKey, rec)) 619 620 return sortData 621 622 def RefreshSort(self, sortby, direction): 623 self.sortIconsBy = sortby 624 self.sortIconsDir = direction 625 settings.char.ui.Set('containerSortIconsBy_%s' % self.name, (sortby, direction)) 626 sortData = self.GetSortData(sortby, direction) 627 locCmpFunc = localization.util.GetSortFunc(localization.util.GetLanguageID()) 628 629 def _sort(left, right): 630 if len(left) == 0: 631 return 0 632 if isinstance(left[0], basestring): 633 res = locCmpFunc(left[0], right[0]) 634 else: 635 res = cmp(left[0], right[0]) 636 if res == 0: 637 return _sort(left[1:], right[1:]) 638 return res 639 640 sortedList = sorted(sortData, cmp=_sort, key=lambda x: x[0]) 641 sortedList = [ x[1] for x in sortedList ] 642 if direction: 643 sortedList.reverse() 644 self.SetSortedItems(sortedList) 645 646 def SetSortedItems(self, sortedList): 647 self.items = sortedList 648 self.RefreshView() 649 650 def SortIconsBy(self, sortby, direction): 651 self.RefreshSort(sortby, direction) 652 653 def _OnSizeChange_NoBlock(self, *args): 654 self.sr.resizeTimer = timerstuff.AutoTimer(250, self.OnEndScale_) 655 656 def OnEndScale_(self, *etc): 657 if self.destroyed: 658 self.sr.resizeTimer = None 659 return 660 if not self.initialized: 661 return 662 self.sr.resizeTimer = None 663 oldcols = self.cols 664 self.RefreshCols() 665 if self.viewMode not in SCROLL_VIEW_MODES: 666 if self.refreshingView: 667 self.reRefreshView = True 668 return 669 if oldcols != self.cols or not self.itemClass.IsFixedWidth(): 670 uthread.new(self.RefreshView) 671 672 @telemetry.ZONE_METHOD 673 def AddItem(self, rec, index = None, fromWhere = None): 674 if self.quickFilterInput: 675 if not self.QuickFilter(rec): 676 return 677 if not self.FilterItems([rec]): 678 return 679 lg.Info('vcont', 'AddItem', fromWhere, rec.stacksize, evetypes.GetName(rec.typeID)) 680 for node in self.scroll.sr.nodes: 681 if self.viewMode in SCROLL_VIEW_MODES: 682 if node is not None and node.Get('item', None) and node.item.itemID == rec.itemID: 683 lg.Warn('vcont', 'Tried to add an item that is already there??') 684 self.UpdateItem(node.item) 685 return 686 else: 687 for internalNode in node.internalNodes: 688 if internalNode is not None and internalNode.item.itemID == rec.itemID: 689 lg.Warn('vcont', 'Tried to add an item that is already there??') 690 self.UpdateItem(internalNode.item) 691 return 692 693 if self.viewMode in SCROLL_VIEW_MODES: 694 from eve.client.script.ui.shared.item import Item 695 self.items.append(rec) 696 data = self.GetItemData(rec, self.scroll.sr.id) 697 self.scroll.AddEntries(-1, [GetFromClass(Item, data)]) 698 self.UpdateHint() 699 else: 700 if index is not None: 701 while index < len(self.items): 702 if self.items[index] is None: 703 return self.SetItem(index, rec) 704 index += 1 705 706 while index >= len(self.scroll.sr.nodes) * self.cols: 707 self.AddRow() 708 709 return self.SetItem(index, rec) 710 if len(self.items) and None in self.items: 711 idx = self.tmpDropIdx.get(rec.itemID, None) 712 if idx is None: 713 idx = self.items.index(None) 714 return self.SetItem(idx, rec) 715 if not self.cols: 716 self.RefreshCols() 717 if index >= len(self.scroll.sr.nodes) * self.cols: 718 self.AddRow() 719 return self.SetItem(0, rec) 720 721 @telemetry.ZONE_METHOD 722 def UpdateItem(self, rec, *etc): 723 lg.Info('vcont', 'UpdateItem', rec and '[%s %s]' % (rec.stacksize, evetypes.GetName(rec.typeID))) 724 if self.viewMode in SCROLL_VIEW_MODES: 725 idx = 0 726 for each in self.items: 727 if each.itemID == rec.itemID: 728 self.items[idx] = rec 729 break 730 idx += 1 731 732 for entry in self.scroll.sr.nodes: 733 if entry.item.itemID == rec.itemID: 734 newentry = self.GetItemData(rec, self.scroll.sr.id) 735 for key, value in newentry.iteritems(): 736 entry[key] = value 737 738 if entry.panel: 739 entry.panel.Load(entry) 740 self.UpdateHint() 741 return 742 743 else: 744 i = 0 745 for rowNode in self.scroll.sr.nodes: 746 for entry in rowNode.internalNodes: 747 if entry is not None and entry.item and entry.item.itemID == rec.itemID: 748 self.SetItem(i, rec) 749 return 750 i += 1 751 752 def GetItemData(self, rec, scrollID = None): 753 return uix.GetItemData(rec, self.viewMode, self.invController.viewOnly, container=self, scrollID=scrollID) 754 755 @telemetry.ZONE_METHOD 756 def RemoveItem(self, rec): 757 lg.Info('vcont', 'RemoveItem', rec and '[%s %s]' % (rec.stacksize, evetypes.GetName(rec.typeID))) 758 if self.viewMode in SCROLL_VIEW_MODES: 759 for entry in self.scroll.sr.nodes: 760 if entry.item.itemID == rec.itemID: 761 self.scroll.RemoveEntries([entry]) 762 break 763 764 for item in self.items: 765 if item.itemID == rec.itemID: 766 self.items.remove(item) 767 768 else: 769 i = 0 770 for rowNode in self.scroll.sr.nodes: 771 si = 0 772 for entry in rowNode.internalNodes: 773 if entry and entry.item and entry.item.itemID == rec.itemID: 774 self.SetItem(i, None) 775 rowNode.internalNodes[si] = None 776 break 777 si += 1 778 i += 1 779 780 i = 0 781 for item in self.items: 782 if item and item.itemID == rec.itemID: 783 self.items[i] = None 784 i += 1 785 786 self.CleanupRows() 787 788 @telemetry.ZONE_METHOD 789 def CleanupRows(self): 790 rm = [] 791 for rowNode in self.scroll.sr.nodes: 792 internalNodes = rowNode.Get('internalNodes', []) 793 if internalNodes == [None] * len(internalNodes): 794 rm.append(rowNode) 795 else: 796 rm = [] 797 798 if rm: 799 self.scroll.RemoveEntries(rm) 800 801 @telemetry.ZONE_METHOD 802 def AddRow(self): 803 self.items += [None] * self.cols 804 self.scroll.AddEntries(fromIdx=-1, nodesData=[GetFromClass(Row, {'lenitems': len(self.scroll.sr.nodes) * self.cols, 805 'rec': [None] * self.cols, 806 'internalNodes': [None] * self.cols, 807 'parentWindow': self, 808 'hilightable': False, 809 'container': self, 810 'itemClass': self.itemClass, 811 'itemWidth': self.itemWidth, 812 'itemHeight': self.itemHeight})]) 813 self.scroll.UpdatePosition() 814 815 @telemetry.ZONE_METHOD 816 def StackAll(self): 817 self.invController.StackAll() 818 uthread.new(self.Refresh) 819 820 def OnScrollContentDropData(self, dragObj, nodes): 821 idx = None 822 if self.viewMode not in SCROLL_VIEW_MODES and self.cols: 823 l, t = self.scroll.GetAbsolutePosition() 824 idx = self.cols * len(self.scroll.sr.nodes) + (uicore.uilib.x - l) // (64 + self.colMargin) 825 sm.ScatterEvent('OnInvContDragExit', self.invController.GetInvID(), []) 826 self.OnDropDataWithIdx(nodes, idx) 827 828 def OnDragEnter(self, dragObj, nodes): 829 sm.ScatterEvent('OnInvContDragEnter', self.invController.GetInvID(), nodes) 830 831 def OnDragExit(self, dragObj, nodes): 832 sm.ScatterEvent('OnInvContDragExit', self.invController.GetInvID(), nodes) 833 834 def OnDropData(self, dragObj, nodes): 835 return self.OnDropDataWithIdx(nodes) 836 837 def Cut(self): 838 items = self.scroll.GetSelected() 839 sm.GetService('inv').SetItemClipboard(items) 840 841 def Copy(self): 842 if bool(session.role & ROLE_PROGRAMMER): 843 items = self.scroll.GetSelected() 844 sm.GetService('inv').SetItemClipboard(items, copy=True) 845 return eveScroll.Scroll.Copy(self.scroll) 846 847 def Paste(self, value): 848 items, copy = sm.GetService('inv').PopItemClipboard() 849 if copy and bool(session.role & ROLE_PROGRAMMER): 850 from eve.devtools.script import param 851 for item in items: 852 itemID = sm.GetService('slash').cmd_createitem(param.ParamObject('%s %s' % (item.rec.typeID, item.rec.stacksize))) 853 if itemID: 854 invController = invCtrl.StationItems() if session.stationid else invCtrl.ShipCargo() 855 blue.synchro.SleepWallclock(100) 856 newItem = invController.GetItem(itemID) 857 if newItem: 858 self.invController.AddItems([newItem]) 859 860 else: 861 self.invController.OnDropData(items) 862 863 @telemetry.ZONE_METHOD 864 def OnDropDataWithIdx(self, nodes, idx = None): 865 self.scroll.ClearSelection() 866 if len(nodes) and getattr(nodes[0], 'scroll', None) and not nodes[0].scroll.destroyed: 867 nodes[0].scroll.ClearSelection() 868 if nodes and getattr(nodes[0], 'item', None) in self.invController.GetItems() and not uicore.uilib.Key(uiconst.VK_SHIFT): 869 if getattr(nodes[0], 'scroll', None) != self.scroll: 870 return 871 if idx is not None: 872 for i, node in enumerate(nodes): 873 self.tmpDropIdx[node.itemID] = idx + i 874 875 for node in nodes: 876 idx = self.tmpDropIdx.get(node.itemID, None) 877 if idx is None: 878 if None in self.items: 879 idx = self.items.index(None) 880 else: 881 idx = len(self.items) 882 self.OnItemDrop(idx, node) 883 884 return 885 return self.invController.OnDropData(nodes) 886 887 @telemetry.ZONE_METHOD 888 def OnItemDrop(self, index, node): 889 if self.viewMode == VIEWMODE_ICONS: 890 self.RemoveItem(node.item) 891 self.AddItem(node.item, index) 892 893 @telemetry.ZONE_METHOD 894 def SetItem(self, index, rec): 895 lg.Info('vcont', 'SetItem', index, rec and '[%s %s]' % (rec.stacksize, evetypes.GetName(rec.typeID))) 896 if not self or self.destroyed: 897 return 898 if index < len(self.items) and rec and self.items[index] is not None and self.items[index].itemID != rec.itemID: 899 while index < len(self.items) and self.items[index] is not None: 900 index += 1 901 902 if self.cols: 903 rowIndex = index // self.cols 904 else: 905 rowIndex = 0 906 while rowIndex >= len(self.scroll.sr.nodes): 907 self.AddRow() 908 909 while index >= len(self.items): 910 self.items += [None] 911 912 self.items[index] = rec 913 try: 914 self.scroll.sr.nodes[rowIndex].rec[index % self.cols] = rec 915 self.UpdateHint() 916 node = None 917 if rec: 918 node = self.GetItemData(rec, self.scroll.sr.id) 919 if not self or self.destroyed: 920 return 921 node.scroll = self.scroll 922 node.panel = None 923 node.idx = index 924 node.__guid__ = self.itemClass.__guid__ 925 node.decoClass = self.itemClass 926 node.width = self.itemWidth 927 node.height = self.itemHeight 928 self.scroll.sr.nodes[index // self.cols].internalNodes[index % self.cols] = node 929 except IndexError: 930 return 931 932 icon = self.GetIcon(index) 933 if icon: 934 if rec is None: 935 icon.display = False 936 icon.sr.node = None 937 else: 938 icon.display = True 939 node.panel = icon 940 node.viewOnly = self.invController.viewOnly 941 icon.Load(node) 942 943 @telemetry.ZONE_METHOD 944 def RefreshCols(self): 945 w = self.scroll.GetContentWidth() 946 columns = w // (self.itemWidth + self.colMargin) 947 if w % (self.itemWidth + self.colMargin) >= self.itemWidth: 948 columns += 1 949 self.cols = max(1, columns) 950 951 def PrimeItems(self, itemlist): 952 locations = [] 953 for rec in itemlist: 954 if rec.categoryID == const.categoryStation and rec.groupID == const.groupStation: 955 locations.append(rec.itemID) 956 locations.append(rec.locationID) 957 if rec.singleton and (rec.categoryID == const.categoryShip or rec.groupID in (const.groupWreck, 958 const.groupCargoContainer, 959 const.groupSecureCargoContainer, 960 const.groupAuditLogSecureContainer, 961 const.groupFreightContainer, 962 const.groupBiomass)): 963 locations.append(rec.itemID) 964 965 if locations: 966 cfg.evelocations.Prime(locations) 967 968 def OnNewHeadersSet(self, *args): 969 self.RefreshView() 970 971 @telemetry.ZONE_METHOD 972 def RefreshView(self, *args): 973 from eve.client.script.ui.shared.item import Item 974 if self.refreshingView: 975 return 976 self.refreshingView = 1 977 try: 978 if self.viewMode in SCROLL_VIEW_MODES: 979 self.scroll.sr.id = self._GetIDForScroll() 980 self.scroll.hiliteSorted = 1 981 scrolllist = [] 982 for rec in self.items: 983 if rec: 984 theData = self.GetItemData(rec, self.scroll.sr.id) 985 list = GetFromClass(Item, theData) 986 scrolllist.append(list) 987 988 hdr = uix.GetInvItemDefaultHeaders() 989 scrll = self.scroll.GetScrollProportion() 990 self.scroll.LoadContent(contentList=scrolllist, headers=hdr, scrollTo=scrll) 991 else: 992 if not self.cols: 993 self.RefreshCols() 994 while self.items and self.items[-1] is None: 995 self.items = self.items[:-1] 996 997 content = [] 998 selectedItems = [ node.item for node in self.scroll.GetSelected() ] 999 for i in xrange(len(self.items)): 1000 blue.pyos.BeNice() 1001 row_index = i % self.cols 1002 if row_index == 0: 1003 entry = [None] * self.cols 1004 nodes = [None] * self.cols 1005 content.append(GetFromClass(Row, {'lenitems': i, 1006 'rec': entry, 1007 'internalNodes': nodes, 1008 'parentWindow': self, 1009 'hilightable': False, 1010 'container': self, 1011 'itemClass': self.itemClass, 1012 'itemWidth': self.itemWidth, 1013 'itemHeight': self.itemHeight})) 1014 if self.items[i]: 1015 node = self.GetItemData(self.items[i]) 1016 node.scroll = self.scroll 1017 node.panel = None 1018 node.__guid__ = self.itemClass.__guid__ 1019 node.decoClass = self.itemClass 1020 node.idx = i 1021 node.selected = node.item in selectedItems 1022 nodes[row_index] = node 1023 entry[row_index] = self.items[i] 1024 1025 self.scroll.sr.sortBy = None 1026 self.scroll.sr.id = None 1027 self.scroll.LoadContent(fixedEntryHeight=self.itemHeight, contentList=content, scrollTo=self.scroll.GetScrollProportion()) 1028 self.CleanupRows() 1029 self.UpdateHint() 1030 self.initialized = True 1031 sm.ScatterEvent('OnInvContRefreshed', self) 1032 finally: 1033 if not self.destroyed: 1034 if self.viewMode == VIEWMODE_DETAILS: 1035 self.scroll.sr.minColumnWidth = {localization.GetByLabel('UI/Common/Name'): 44} 1036 self.scroll.UpdateTabStops() 1037 else: 1038 self.scroll.sr.minColumnWidth = {} 1039 self.refreshingView = 0 1040 if self.reRefreshView: 1041 self.reRefreshView = False 1042 self.RefreshCols() 1043 uthread.new(self.RefreshView) 1044 self._UpdateScrollPadding() 1045 1046 def _UpdateScrollPadding(self): 1047 if self.viewMode not in SCROLL_VIEW_MODES: 1048 self.scroll.innerPadding = (8, 4, 0, 0) 1049 self.scroll.padding = (-8, 0, 0, 0) 1050 self.scroll.sr.maincontainer.padTop = -4 1051 self.scroll.sr.clipper.clipChildren = False 1052 else: 1053 self.scroll.innerPadding = 0 1054 self.scroll.padding = 0 1055 self.scroll.sr.maincontainer.padTop = 0 1056 self.scroll.sr.clipper.clipChildren = True 1057 1058 def SelectAll(self): 1059 if not self.destroyed: 1060 self.scroll.SelectAll() 1061 1062 def InvertSelection(self): 1063 if not self.destroyed: 1064 self.scroll.ToggleSelected() 1065 1066 def GetIcons(self): 1067 return [ icon for row in self.scroll.GetContentContainer().children for icon in row.sr.icons if icon.display ] 1068 1069 def OnScrollMouseDown(self, *args): 1070 if args[0] == uiconst.MOUSELEFT: 1071 self.dragStart = self.GetDragPosition() 1072 if uicore.uilib.Key(uiconst.VK_CONTROL) or uicore.uilib.Key(uiconst.VK_SHIFT): 1073 self.previouslyHighlighted = [ x.panel for x in self.scroll.GetSelected() ] 1074 else: 1075 self.previouslyHighlighted = [] 1076 dragContainer = getattr(self, 'dragContainer', None) 1077 if dragContainer is None or dragContainer.destroyed: 1078 self.dragContainer = Container(name='dragContainer', parent=self.GetRubberbandParentContainer(), align=uiconst.TOPLEFT, idx=0) 1079 FrameThemeColored(parent=self.dragContainer, frameConst=uiconst.FRAME_BORDER1_CORNER3, colorType=uiconst.COLORTYPE_UIHILIGHT, opacity=0.5) 1080 FillThemeColored(parent=self.dragContainer, frameConst=uiconst.FRAME_FILLED_CORNER3, opacity=0.3) 1081 self.dragContainer.Hide() 1082 uthread.new(self.RubberbandSelection_thread) 1083 1084 def IsRowInDragArea(self, row_top, row_height, startY, currentY): 1085 isYBeforeRow = lambda y: y < row_top 1086 isYAfterRow = lambda y: row_top + row_height < y 1087 isSelectionBeforeRow = lambda startY, currentY: isYBeforeRow(startY) and isYBeforeRow(currentY) 1088 isSelectionAfterRow = lambda startY, currentY: isYAfterRow(startY) and isYAfterRow(currentY) 1089 return not (isSelectionBeforeRow(startY, currentY) or isSelectionAfterRow(startY, currentY)) 1090 1091 def IsIconInDragArea(self, icon, startX, currentX): 1092 iconX = self._CorrectXInScroll(icon.absoluteLeft) 1093 isXBeforeIcon = lambda x: x < iconX 1094 isXAfterIcon = lambda x: iconX + icon.width < x 1095 isSelectionBeforeIcon = lambda startX, currentX: isXBeforeIcon(startX) and isXBeforeIcon(currentX) 1096 isSelectionAfterIcon = lambda startX, currentX: isXAfterIcon(startX) and isXAfterIcon(currentX) 1097 return not (isSelectionBeforeIcon(startX, currentX) or isSelectionAfterIcon(startX, currentX)) 1098 1099 def _CorrectXInScroll(self, x): 1100 return x - self.scroll.GetContentContainer().absoluteLeft 1101 1102 def _CorrectYInScroll(self, y): 1103 return y - self.scroll.GetContentContainer().absoluteTop 1104 1105 def GetDragPosition(self): 1106 x = self._CorrectXInScroll(uicore.uilib.x) 1107 y = self._CorrectYInScroll(uicore.uilib.y) 1108 return (x, y) 1109 1110 def GetRubberbandParentContainer(self): 1111 return self.scroll.GetContentContainer() 1112 1113 def UpdateDragContainerPosition(self, startX, startY, currentX, currentY): 1114 xCorrection = 0 1115 yCorrection = 0 1116 scrollContent = self.scroll.GetContentContainer() 1117 dragArea = self.GetRubberbandParentContainer() 1118 if scrollContent != dragArea: 1119 xCorrection = scrollContent.absoluteLeft - dragArea.absoluteLeft 1120 yCorrection = scrollContent.absoluteTop - dragArea.absoluteTop 1121 left = max(startX + xCorrection, 0) 1122 width = min(currentX + xCorrection, self.GetRubberbandParentContainer().displayWidth) - left 1123 top = max(startY + yCorrection, 0) 1124 height = min(currentY + yCorrection, self.GetRubberbandParentContainer().displayHeight) - top 1125 self.dragContainer.left = left 1126 self.dragContainer.top = top 1127 self.dragContainer.width = width 1128 self.dragContainer.height = height 1129 1130 def RubberbandSelection_thread(self, *args): 1131 from eve.client.script.ui.shared.item import Item 1132 timer = IntervalTimer() 1133 scroll_remainder = 0.0 1134 block_scrolling = not is_mouse_inside_vertically(self.scroll) 1135 while self.dragStart and uicore.uilib.leftbtn and trinity.app.IsActive(): 1136 startX, startY = self.dragStart 1137 currentX, currentY = self.GetDragPosition() 1138 if startX > currentX: 1139 temp = currentX 1140 currentX = startX 1141 startX = temp 1142 if startY > currentY: 1143 temp = currentY 1144 currentY = startY 1145 startY = temp 1146 self.UpdateDragContainerPosition(startX, startY, currentX, currentY) 1147 self.dragContainer.Show() 1148 for each in self.scroll.GetContentContainer().children: 1149 if isinstance(each, Row): 1150 row_top = each.top 1151 row_height = self.itemClass.GetEntryHeight() 1152 if self.IsRowInDragArea(row_top, row_height, startY, currentY): 1153 for icon in each.sr.icons: 1154 if self.IsIconInDragArea(icon, startX, currentX): 1155 if icon in self.previouslyHighlighted: 1156 if uicore.uilib.Key(uiconst.VK_SHIFT): 1157 icon.Select() 1158 elif uicore.uilib.Key(uiconst.VK_CONTROL): 1159 icon.Deselect() 1160 else: 1161 icon.Select() 1162 elif icon not in self.previouslyHighlighted: 1163 icon.Deselect() 1164 else: 1165 icon.Select() 1166 1167 else: 1168 for icon in each.sr.icons: 1169 if icon not in self.previouslyHighlighted: 1170 icon.Deselect() 1171 else: 1172 icon.Select() 1173 1174 elif isinstance(each, Item): 1175 if each.top >= startY and each.top + each.height <= currentY or each.top + each.height >= startY and each.top <= currentY: 1176 each.Select() 1177 if each in self.previouslyHighlighted: 1178 if uicore.uilib.Key(uiconst.VK_SHIFT): 1179 each.Select() 1180 elif uicore.uilib.Key(uiconst.VK_CONTROL): 1181 each.Deselect() 1182 else: 1183 each.Select() 1184 elif each not in self.previouslyHighlighted: 1185 each.Deselect() 1186 else: 1187 each.Select() 1188 1189 dt = timer.tick().total_seconds() 1190 if block_scrolling: 1191 block_scrolling = not is_mouse_inside_vertically(self.scroll) 1192 if not block_scrolling: 1193 scroll_speed = calculate_drag_scroll_speed_vertical(self.scroll) 1194 if not mathext.is_almost_zero(scroll_speed): 1195 scroll_delta = scroll_speed * dt 1196 scroll_remainder += scroll_delta % 1.0 1197 while scroll_remainder >= 1.0: 1198 scroll_delta += 1.0 1199 scroll_remainder -= 1.0 1200 1201 self.scroll.ScrollPixels(mathext.floor(scroll_delta)) 1202 eveui.wait_for_next_frame() 1203 1204 self.dragStart = None 1205 self.previouslyHighlighted = None 1206 self.dragContainer.Hide() 1207 1208 def OnScrollMouseUp(self, *args): 1209 from eve.client.script.ui.shared.item import Item 1210 if self.dragStart and args[0] == uiconst.MOUSELEFT: 1211 startX, startY = self.dragStart 1212 endX, endY = self.GetDragPosition() 1213 if startX > endX: 1214 temp = endX 1215 endX = startX 1216 startX = temp 1217 if startY > endY: 1218 temp = endY 1219 endY = startY 1220 startY = temp 1221 preSelectedNodes = self.scroll.GetSelected() if uicore.uilib.Key(uiconst.VK_CONTROL) or uicore.uilib.Key(uiconst.VK_SHIFT) else [] 1222 selectedNodes = [] 1223 for each in self.scroll.GetContentContainer().children: 1224 if isinstance(each, Row): 1225 row_top = each.top 1226 row_height = self.itemClass.GetEntryHeight() 1227 if self.IsRowInDragArea(row_top, row_height, startY, endY): 1228 for icon in each.sr.icons: 1229 if self.IsIconInDragArea(icon, startX, endX) and icon.sr.node: 1230 selectedNodes.append(icon.sr.node) 1231 1232 elif isinstance(each, Item): 1233 if each.top >= startY and each.top + each.height <= endY or each.top + each.height >= startY and each.top <= endY: 1234 selectedNodes.append(each.sr.node) 1235 1236 if uicore.uilib.Key(uiconst.VK_SHIFT): 1237 selectedNodes.extend(preSelectedNodes) 1238 elif uicore.uilib.Key(uiconst.VK_CONTROL): 1239 newSelectedNodes = [ item for item in selectedNodes if item not in preSelectedNodes ] 1240 newSelectedNodes.extend([ item for item in preSelectedNodes if item not in selectedNodes ]) 1241 selectedNodes = newSelectedNodes 1242 uthread.new(self.scroll.SelectNodes, selectedNodes) 1243 self.dragStart = None 1244 self.previouslyHighlighted = None 1245 self.dragContainer.Hide() 1246 1247 def GetIcon(self, index): 1248 for each in self.scroll.GetContentContainer().children: 1249 if getattr(each, 'index', -1) == index // self.cols * self.cols: 1250 lg.Info('vcont', 'GetIcon(', index, ') returns', each.sr.icons[index % self.cols].name) 1251 return each.sr.icons[index % self.cols] 1252 1253 lg.Info('vcont', 'GetIcon(', index, ') found nothing') 1254 1255 def RegisterSpecialActionButton(self, button): 1256 pass 1257 1258 def GetContainerMenu(self): 1259 return GetContainerMenu(self) 1260 1261 def IsItemPresent(self, itemID): 1262 return itemID in [ item.itemID for item in self.items if item is not None ] 1263 1264 def GetViewModeName(self, viewMode): 1265 if viewMode == VIEWMODE_ICONS: 1266 return localization.GetByLabel('UI/Inventory/Icons') 1267 if viewMode == VIEWMODE_DETAILS: 1268 return localization.GetByLabel('UI/Inventory/Details') 1269 if viewMode == VIEWMODE_LIST: 1270 return localization.GetByLabel('UI/Inventory/List') 1271 if viewMode == VIEWMODE_CARDS: 1272 return localization.GetByLabel('UI/Inventory/CardsViewMode') 1273 return localization.GetByLabel('UI/Generic/Unknown') 1274 1275 def GetActiveViewModeName(self): 1276 return self.GetViewModeName(self.viewMode) 1277 1278 def GetViewModeIcon(self, viewMode): 1279 if viewMode == VIEWMODE_ICONS: 1280 return eveicon.grid_view 1281 if viewMode == VIEWMODE_DETAILS: 1282 return eveicon.details_view 1283 if viewMode == VIEWMODE_LIST: 1284 return eveicon.list_view 1285 if viewMode == VIEWMODE_CARDS: 1286 return eveicon.card_view 1287 1288 def GetActiveViewModeIcon(self): 1289 return self.GetViewModeIcon(self.viewMode) 1290 1291 def ShouldShowSortBy(self): 1292 return self.viewMode not in SCROLL_VIEW_MODES 1293 1294 1295 def is_mouse_inside_vertically(element): 1296 _, element_top, _, element_height = element.GetAbsolute() 1297 return element_top <= uicore.uilib.y <= element_top + element_height 1298 1299 1300 class IntervalTimer(object): 1301 1302 def __init__(self, now_func = None): 1303 if now_func is None: 1304 now_func = gametime.now 1305 self._now_func = now_func 1306 self._last_update = None 1307 1308 def start(self): 1309 self._last_update = self._now_func() 1310 1311 def tick(self): 1312 if self._last_update is None: 1313 self.start() 1314 now = self._now_func() 1315 dt = now - self._last_update 1316 self._last_update = now 1317 return dt 1318 1319 1320 def calculate_drag_scroll_speed_vertical(scroll): 1321 MIN_SCROLL_SPEED = 100 1322 MAX_SCROLL_SPEED = 4000 1323 SCROLL_SPEED_DISTANCE_FACTOR = 1.6 1324 _, scroll_top, _, scroll_height = scroll.GetAbsolute() 1325 distance_from_top = scroll_top - uicore.uilib.y 1326 distance_from_bottom = uicore.uilib.y - (scroll_top + scroll_height) 1327 if distance_from_top > 0: 1328 return mathext.clamp(distance_from_top ** SCROLL_SPEED_DISTANCE_FACTOR, MIN_SCROLL_SPEED, MAX_SCROLL_SPEED) 1329 elif distance_from_bottom > 0: 1330 return -mathext.clamp(distance_from_bottom ** SCROLL_SPEED_DISTANCE_FACTOR, MIN_SCROLL_SPEED, MAX_SCROLL_SPEED) 1331 else: 1332 return 0 1333 1334 1335 class Row(SE_BaseClassCore): 1336 __guid__ = 'listentry.VirtualContainerRow' 1337 default_showHilite = False 1338 1339 def Startup(self, dad): 1340 self.dad = dad.dad 1341 self.initialized = False 1342 self.sr.icons = [] 1343 1344 @telemetry.ZONE_METHOD 1345 def Load(self, node): 1346 if self.initialized: 1347 return 1348 self.initialized = True 1349 self.sr.node = node 1350 self.index = node.lenitems 1351 for i in range(len(self.sr.icons), len(node.internalNodes)): 1352 icon = node.itemClass(parent=self, state=uiconst.UI_NORMAL, align=uiconst.TOPLEFT) 1353 icon.width = self.GetItemWidth(node) 1354 icon.height = node.itemHeight 1355 icon.top = 0 1356 icon.left = (icon.width + self.dad.colMargin) * i 1357 self.sr.icons.append(icon) 1358 1359 for icon in self.sr.icons[self.dad.cols:]: 1360 icon.sr.node = None 1361 icon.subnodeIdx = None 1362 1363 i = 0 1364 for subnode in node.internalNodes: 1365 icon = self.sr.icons[i] 1366 if not node.itemClass.IsFixedWidth(): 1367 icon.width = self.GetItemWidth(node) 1368 icon.left = (icon.width + self.dad.colMargin) * i 1369 if subnode is None: 1370 icon.display = False 1371 icon.sr.node = None 1372 icon.subnodeIdx = None 1373 else: 1374 subnode.panel = icon 1375 icon.Load(subnode) 1376 icon.display = True 1377 icon.subnodeIdx = subnode.idx = self.index + i 1378 i += 1 1379 1380 def GetItemWidth(self, node): 1381 if node.itemClass.IsFixedWidth(): 1382 return node.itemWidth 1383 return max(node.itemWidth, (self.width + self.dad.colMargin) / len(self.sr.node.internalNodes) - self.dad.colMargin) 1384 1385 def GetMenu(self): 1386 return self.dad.GetContainerMenu() 1387 1388 def OnDropData(self, dragObj, nodes): 1389 l, t, w, h = self.GetAbsolute() 1390 index = self.index + (uicore.uilib.x - l) // (64 + self.dad.colMargin) 1391 self.dad.OnDropDataWithIdx(nodes, index) 1392 1393 def OnDragEnter(self, dragObj, nodes): 1394 self.sr.node.container.OnDragEnter(dragObj, nodes) 1395 1396 def OnDragExit(self, dragObj, nodes): 1397 self.sr.node.container.OnDragExit(dragObj, nodes) 1398 1399 def OnMouseDown(self, *etc): 1400 if self.dad.scroll: 1401 self.dad.scroll.OnMouseDown(*etc) 1402 1403 def OnMouseUp(self, *etc): 1404 if self.dad.scroll: 1405 self.dad.scroll.OnMouseUp(*etc) 1406 1407 def OnMouseMove(self, *etc): 1408 if self.dad.scroll: 1409 self.dad.scroll.OnMouseMove(*etc) 1410 1411 def ShowSelected(self, *args): 1412 pass 1413 1414 1415 def GetContainerMenu(containerWindow): 1416 menu = MenuData() 1417 if session.role & (ROLE_GML | ROLE_WORLDMOD): 1418 menu.AddEntry(text=MenuLabel('UI/Commands/Refresh'), func=containerWindow.Refresh) 1419 menu.AddSeparator() 1420 menu.AddEntry(text=MenuLabel('UI/Common/SelectAll'), func=containerWindow.SelectAll) 1421 menu.AddEntry(text=MenuLabel('UI/Inventory/InvertSelection'), func=containerWindow.InvertSelection) 1422 if containerWindow.ShouldShowSortBy(): 1423 menu.AddSeparator() 1424 sort_options = GetBasicSortOptions() 1425 sort_options += [None, ('UI/Inventory/EstPrice', ('estPrice', 0)), ('UI/Inventory/EstPriceReversed', ('estPrice', 1))] 1426 menu.AddEntry(text=MenuLabel('UI/Common/SortBy'), texturePath=eveicon.bars_sort_ascending, subMenuData=GetSortMenuForOptionsOptions(containerWindow, sort_options)) 1427 if not containerWindow.invController.viewOnly: 1428 container_item = containerWindow.invController.GetInventoryItem() 1429 container_slim = sm.GetService('michelle').GetItem(container_item.itemID) 1430 offer_stack_all = should_offer_stack_all(session, container_group_id=container_item.groupID, container_category_id=container_item.categoryID, container_owner_id=container_item.ownerID, container_corporation_id=getattr(container_slim, 'corpID', None), container_alliance_id=getattr(container_slim, 'allianceID', None)) 1431 if offer_stack_all: 1432 menu.AddSeparator() 1433 menu.AddEntry(text=MenuLabel('UI/Inventory/StackAll'), texturePath=eveicon.stack, func=containerWindow.StackAll) 1434 return menu 1435 1436 1437 def should_offer_stack_all(session, container_group_id, container_category_id, container_owner_id, container_corporation_id, container_alliance_id): 1438 return container_group_id in (const.groupStation, const.groupPlanetaryCustomsOffices) or container_category_id == const.categoryStructure or container_owner_id in (session.charid, session.corpid, session.allianceid) or session.corpid == container_corporation_id or session.allianceid and session.allianceid == container_alliance_id 1439 1440 1441 def GetSortMenuForOptionsOptions(containerWindow, options): 1442 m = [] 1443 for eachOption in options: 1444 if eachOption is None: 1445 m.append(None) 1446 else: 1447 labelPath, args = eachOption 1448 m.append((MenuLabel(labelPath), containerWindow.SortIconsBy, args)) 1449 1450 return m 1451 1452 1453 def GetBasicSortOptions(): 1454 return [('UI/Common/Name', ('name', 0)), 1455 ('UI/Inventory/NameReversed', ('name', 1)), 1456 None, 1457 ('UI/Inventory/ItemQuantity', ('qty', 0)), 1458 ('UI/Inventory/QuantityReversed', ('qty', 1)), 1459 None, 1460 ('UI/Common/Type', ('type', 0)), 1461 ('UI/Inventory/TypeReversed', ('type', 1))]