selectedItemWnd.py (40361B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\selectedItemWnd.py 2 import blue 3 import eveformat.client 4 import eveicon 5 import evetypes 6 import localization 7 import telemetry 8 import uthread 9 import uthread2 10 import utillib 11 from brennivin import itertoolsext 12 from carbon.client.script.environment.audioService import GetAudioService 13 from carbon.common.script.util.format import FmtDist 14 from carbon.common.script.util.timerstuff import AutoTimer 15 from carbonui import TextColor, uiconst, Align 16 from carbonui.control.buttonIcon import ButtonIcon 17 from carbonui.control.contextMenu import menuDataFactory 18 from carbonui.control.contextMenu.menuConst import DISABLED_ENTRY0 19 from carbonui.control.contextMenu.menuData import MenuData 20 from carbonui.control.contextMenu.menuEntryData import MenuEntryData 21 from carbonui.control.dragdrop.dragdata import ShipDragData 22 from carbonui.control.scrollContainer import ScrollContainer 23 from carbonui.primitives.container import Container 24 from carbonui.primitives.containerAutoSize import ContainerAutoSize 25 from carbonui.uiconst import PickState 26 from carbonui.window.header.small import SmallWindowHeader 27 from carbonui.window.segment.underlay import WindowSegmentUnderlay 28 from carbonui.window.widget import WidgetWindow 29 from collections import OrderedDict 30 from eve.client.script.parklife import states as state 31 from eve.client.script.ui.control import eveIcon, eveLabel 32 from eve.client.script.ui.inflight import actionPanelDefaults, selectedItemConst, actionConst 33 from eve.client.script.ui.inflight.actionConst import CMD_BY_ACTIONID 34 from eve.client.script.ui.inflight.selectedItemButton import SelectedItemButton 35 from eve.client.script.ui.inflight.selectedItemConst import ACTIONS_ALWAYS_VISIBLE, ACTIONS_BY_CATEGORYID, ACTIONS_BY_GROUPID, ACTIONS_BY_TYPEID, SUB_MENU_ACTIONS, TOGGLE_ACTIONS 36 from eve.client.script.ui.inflight.selectedItemTimers import HasSkyhookTimer, HasStructureTimer, GetTimerText 37 from eve.client.script.ui.shared import killRights 38 from eve.client.script.ui.shared.pointerTool import pointerToolConst 39 from eve.client.script.ui.util import uix 40 from eve.common.lib import appConst as const 41 from eve.common.script.sys import idCheckers 42 from eve.common.script.util import eveFormat 43 from eveservices.menu import GetMenuService, StartMenuService 44 from eveservices.xmppchat import GetChatService 45 from localization import GetByLabel 46 from menu import MenuLabel 47 from npcs.client.components.entity_bounty import get_entity_bounty 48 from shipcaster.client.shipcasterUtil import GetShipcasterText 49 from spacecomponents.client.components import cargobay 50 from spacecomponents.common.helper import HasCargoBayComponent, HasShipcasterComponent, HasStarterShipcasterComponent, HasUnderConstructionComponent, HasTowGameObjectiveComponent, HasFilamentSpoolupComponent 51 from spacecomponents.common.helper import HasItemTrader 52 from uihider import UiHiderMixin 53 ITEM_ICON_SIZE_LARGE = 64 54 ITEM_ICON_SIZE_SMALL = 32 55 BUTTON_CONTAINER_MIN_WIDTH = 270 56 BUTTON_ICON_LARGE = 32 57 BUTTON_ICON_SMALL = 16 58 BUTTON_SIZE_LARGE = BUTTON_ICON_LARGE 59 BUTTON_SIZE_SMALL = BUTTON_ICON_SMALL + 4 60 BUTTON_PADDING = 4 61 TITLE_CHIP_PADDING = 8 62 HEIGHTSTATE_SMALL = 1 63 HEIGHTSTATE_MEDIUM = 2 64 HEIGHTSTATE_LARGE = 3 65 66 class CharIcon(Container): 67 isDragObject = True 68 69 def ApplyAttributes(self, attributes): 70 super(CharIcon, self).ApplyAttributes(attributes) 71 self.typeID = None 72 self.itemID = None 73 self.chatIcon = ButtonIcon(name='chatIcon', parent=self, align=Align.BOTTOMRIGHT, texturePath=eveicon.chat_bubble, pos=(0, 0, 16, 16), iconSize=16, func=self.OnChatButton, hint=GetByLabel('UI/Chat/StartConversation')) 74 self.icon = eveIcon.Icon(name='iconSprite', align=Align.TOALL, pickState=PickState.OFF, parent=self) 75 76 def OnChatButton(self, *args): 77 GetChatService().Invite(self.itemID) 78 79 def SetID(self, typeID, itemID): 80 self.typeID = typeID 81 self.itemID = itemID 82 self.icon.LoadIconByTypeID(typeID, itemID=itemID, ignoreSize=True) 83 84 def GetMenu(self): 85 return sm.GetService('menu').GetMenuForOwner(self.itemID) 86 87 def GetDragData(self, *args): 88 charID = self.itemID 89 if not charID: 90 return [] 91 fakeNode = utillib.KeyVal() 92 fakeNode.charID = charID 93 fakeNode.typeID = self.typeID 94 fakeNode.itemID = charID 95 fakeNode.info = cfg.eveowners.Get(charID) 96 fakeNode.__guid__ = 'listentry.User' 97 return [fakeNode] 98 99 def OnClick(self, *args): 100 sm.GetService('info').ShowInfo(self.typeID, self.itemID) 101 102 103 class SelectedItemWnd(UiHiderMixin, WidgetWindow): 104 uniqueUiName = pointerToolConst.UNIQUE_NAME_SELECTED_ITEM_VIEW 105 __notifyevents__ = ['ProcessSessionChange', 106 'OnStateChange', 107 'DoBallRemove', 108 'OnDistSettingsChange', 109 'OnPlanetViewChanged', 110 'DoBallsRemove', 111 'OnActiveTrackingChange'] 112 default_minSize = (256, 0) 113 default_windowID = 'selecteditemview' 114 default_open = True 115 default_isKillable = False 116 default_captionLabelPath = 'UI/Inflight/ActiveItem/SelectedItem' 117 default_scope = uiconst.SCOPE_INFLIGHT 118 default_apply_content_padding = False 119 120 @staticmethod 121 def default_left(*args): 122 return actionPanelDefaults.GetActiveItemLeft() 123 124 @staticmethod 125 def default_top(*args): 126 return actionPanelDefaults.GetActiveItemTop() 127 128 @staticmethod 129 def default_width(): 130 return actionPanelDefaults.GetActiveItemWidth() 131 132 @staticmethod 133 def default_height(): 134 return actionPanelDefaults.GetActiveItemHeight() 135 136 def ApplyAttributes(self, attributes): 137 super(SelectedItemWnd, self).ApplyAttributes(attributes) 138 panelname = attributes.panelname 139 self.inited = False 140 self.canAnchorToOthers = 0 141 self.lastActionSerial = None 142 self.lastSessionChange = None 143 self.lastIcon = None 144 self.lastBountyCheck = None 145 self.bountyText = None 146 self.buttonCont = None 147 self.updateAutoTimer = None 148 self.itemID = None 149 self.panelname = panelname 150 self.lastActionDist = None 151 self.lastCloakingStatus = False 152 self.noContentHint = None 153 self.bountySvc = sm.GetService('bountySvc') 154 self._last_button_size = None 155 self.titleCont = None 156 self.character_title_chip = None 157 self._update_extend_content_into_header() 158 self.ConstructLayout() 159 stateSvc = sm.GetServiceIfRunning('stateSvc') 160 if stateSvc: 161 self.itemID = stateSvc.GetExclState(state.selected) 162 self.topCont._OnSizeChange_NoBlock = self._on_top_cont_size_changed 163 self.on_compact_mode_changed.connect(self._on_compact_mode_changed) 164 self.on_content_padding_changed.connect(self._on_content_padding_changed) 165 self.on_header_inset_changed.connect(self._on_window_header_inset_changed) 166 self.on_end_scale.connect(self._on_end_scale) 167 self.inited = True 168 uthread2.start_tasklet(self.UpdateAll) 169 170 def _UpdateMinSize(self): 171 top_height = self.nameRowCont.height + self.topCont.padTop + self.topCont.padBottom 172 _, bottom_height = self.bottomCont.GetAbsoluteSize() 173 content_height = top_height + bottom_height 174 _, height_min = self.GetWindowSizeForContentSize(height=content_height) 175 if self.compact: 176 width_min = 200 177 else: 178 width_min = self.default_minSize[0] 179 self.SetMinSize((width_min, height_min)) 180 181 def ConstructLayout(self): 182 self._ConstructButtonCont() 183 self.topCont = Container(name='topCont', parent=self.content, align=uiconst.TOALL, clipChildren=True) 184 self._ConstructIconsAndLabel() 185 self._update_padding_of_content() 186 self.on_name_row_size_changed(*self.nameRowCont.GetAbsoluteSize()) 187 188 def _on_top_cont_size_changed(self, *args, **kwargs): 189 self.ReconstructItemIcons() 190 self.UpdateAll() 191 192 def _on_end_scale(self, window): 193 self._UpdateMinSize() 194 195 def _ConstructIconsAndLabel(self): 196 self.killRightsUtilMenuCont = Container(name='utilMenuCont', align=uiconst.TORIGHT, parent=self.topCont, width=16, state=uiconst.UI_HIDDEN, padLeft=4) 197 self.killRightsUtilMenu = killRights.KillRightsUtilMenu(menuAlign=uiconst.TOPRIGHT, parent=self.killRightsUtilMenuCont, width=16, height=16, align=uiconst.TOPRIGHT) 198 self.iconCont = ContainerAutoSize(name='iconCont', parent=self.topCont, align=uiconst.TOLEFT, padTop=2, padRight=8) 199 self.ReconstructItemIcons() 200 self.labelCont = ScrollContainer(name='labelCont', parent=self.topCont, state=uiconst.UI_PICKCHILDREN) 201 self.labelCont.mainCont.state = uiconst.UI_PICKCHILDREN 202 self.nameRowCont = NameRowContainer(name='nameRowCont', parent=self.labelCont, align=Align.TOTOP, clipChildren=True, height=24) 203 self.nameLabel = eveLabel.EveLabelMedium(name='nameLabel', parent=self.nameRowCont, align=Align.TOLEFT, color=TextColor.HIGHLIGHT, default_maxLines=1, autoFadeSides=16, padTop=2) 204 self.titleCont = ContainerAutoSize(name='titleCont', parent=self.nameRowCont, align=Align.TOLEFT, padLeft=TITLE_CHIP_PADDING) 205 self.titleCont.on_size_changed.connect(self.on_title_size_changed) 206 self.nameRowCont.on_size_changed.connect(self.on_name_row_size_changed) 207 self.distanceLabel = eveLabel.EveLabelMedium(name='distanceLabel', parent=self.labelCont, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, color=TextColor.HIGHLIGHT) 208 self.distanceLabel.display = False 209 self.securityLabel = eveLabel.EveLabelMedium(name='securityLabel', parent=self.labelCont, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, color=TextColor.HIGHLIGHT) 210 self.securityLabel.display = False 211 self.timerLabel = eveLabel.EveLabelMedium(name='timerLabel', parent=self.labelCont, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, color=TextColor.HIGHLIGHT) 212 self.timerLabel.display = False 213 self.bountyLabel = eveLabel.EveLabelMedium(name='bountyLabel', parent=self.labelCont, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, color=TextColor.HIGHLIGHT) 214 self.bountyLabel.display = False 215 216 def on_title_size_changed(self, width, height): 217 self.on_name_row_size_changed(*self.nameRowCont.GetAbsoluteSize()) 218 219 def on_name_row_size_changed(self, width, height): 220 if self.character_title_chip is None: 221 title_width = self.titleCont.width 222 else: 223 title_width = self.character_title_chip.measured_width 224 if width < self.nameLabel.width + title_width + TITLE_CHIP_PADDING: 225 self.titleCont.display = False 226 self.nameRowCont.state = uiconst.UI_NORMAL 227 else: 228 self.titleCont.display = True 229 self.nameRowCont.state = uiconst.UI_PICKCHILDREN 230 231 def _OnResize(self, *args, **kw): 232 self._UpdateButtonSize() 233 super(SelectedItemWnd, self)._OnResize(*args, **kw) 234 235 def _UpdateIconContVisibility(self): 236 self.iconCont.SetSizeAutomatically() 237 heightState = self.GetHeightState() 238 if heightState == HEIGHTSTATE_SMALL: 239 self.iconCont.Hide() 240 else: 241 self.iconCont.Show() 242 iconSize = ITEM_ICON_SIZE_LARGE if heightState == HEIGHTSTATE_LARGE else ITEM_ICON_SIZE_SMALL 243 self.iconSprite.SetSize(iconSize, iconSize) 244 self.charIcon.SetSize(iconSize, iconSize) 245 self.charIcon.left = iconSize + 4 246 247 def GetHeightState(self): 248 _, h = self.topCont.GetAbsoluteSize() 249 if h > ITEM_ICON_SIZE_LARGE: 250 return HEIGHTSTATE_LARGE 251 elif h > ITEM_ICON_SIZE_SMALL: 252 return HEIGHTSTATE_MEDIUM 253 else: 254 return HEIGHTSTATE_SMALL 255 256 def _UpdateButtonSize(self): 257 button_size = self.button_size 258 icon_size = self.get_button_icon_size(button_size) 259 self.buttonCont.height = button_size 260 for button in self.buttonCont.children: 261 self._ApplyButtonSize(button, icon_size, button_size) 262 263 if self._last_button_size is None or button_size != self._last_button_size: 264 self._last_button_size = button_size 265 self._UpdateMinSize() 266 267 def _ApplyButtonSize(self, button, iconSize, buttonSize): 268 button.width = buttonSize 269 button.SetIconSize(iconSize) 270 button.SetTexturePath(actionConst.ICON_BY_ACTIONID[button.GetActionID()].resolve(iconSize)) 271 272 @property 273 def button_size(self): 274 total_width, _ = self.GetCurrentAbsoluteSize() 275 if total_width >= self.get_min_button_container_width(BUTTON_SIZE_LARGE): 276 return BUTTON_SIZE_LARGE 277 else: 278 return BUTTON_SIZE_SMALL 279 280 def get_button_icon_size(self, button_size): 281 if button_size == BUTTON_SIZE_LARGE: 282 return BUTTON_ICON_LARGE 283 else: 284 return BUTTON_ICON_SMALL 285 286 def get_min_button_container_width(self, button_size): 287 container_padding = self.buttonCont.padLeft + self.buttonCont.padRight 288 total_width = container_padding + len(self.buttonCont.children) * (button_size + BUTTON_PADDING) 289 return max(total_width, BUTTON_CONTAINER_MIN_WIDTH) 290 291 def _ConstructButtonCont(self): 292 self.bottomCont = ContainerAutoSize(name='bottomCont', parent=self.content, align=uiconst.TOBOTTOM) 293 self.buttonCont = Container(name='buttonCont', parent=self.bottomCont, align=uiconst.TOTOP, clipChildren=True) 294 self.buttonCont.isTabStop = 1 295 self.bottomContUnderlay = WindowSegmentUnderlay(bgParent=self.bottomCont) 296 297 def ReconstructItemIcons(self): 298 self.lastIcon = None 299 self.iconCont.Flush() 300 self.iconSprite = eveIcon.Icon(name='iconSprite', align=uiconst.TOPLEFT, parent=self.iconCont, state=uiconst.UI_HIDDEN) 301 self.iconSprite.OnClick = (self.ShowInfo, self.iconSprite) 302 self.iconSprite.GetMenu = (self.GetIconMenu, self.iconSprite) 303 self.iconSprite.GetDragData = self.GetShipDragData 304 self.charIcon = CharIcon(name='charIconSprite', align=uiconst.TOPLEFT, parent=self.iconCont, state=uiconst.UI_HIDDEN) 305 self._UpdateIconContVisibility() 306 307 def _on_compact_mode_changed(self, window): 308 self.header.show_caption = not self.compact 309 self._update_extend_content_into_header() 310 self._UpdateMinSize() 311 312 def _on_window_header_inset_changed(self, window): 313 self._update_padding_of_content() 314 315 def _on_content_padding_changed(self, window): 316 self._update_padding_of_content() 317 318 def _update_extend_content_into_header(self): 319 self.extend_content_into_header = self.compact 320 321 def _update_padding_of_content(self): 322 pad_left, pad_top, pad_right, pad_bottom = self.content_padding 323 _, inset_right = self.header_inset 324 self.topCont.padding = (pad_left, 325 pad_top, 326 inset_right if self.compact else pad_right, 327 0) 328 self.buttonCont.padding = (pad_left, 329 pad_bottom, 330 pad_right, 331 pad_bottom) 332 333 def Prepare_Header_(self): 334 self._SetHeader(SmallWindowHeader(show_caption=not self.compact)) 335 336 def OnActiveTrackingChange(self, isActivelyTracking): 337 self.AdjustUIToActiveTrackingStatus(isActivelyTracking) 338 339 def AdjustUIToActiveTrackingStatus(self, isTracking): 340 caption = localization.GetByLabel('UI/Inflight/ActiveItem/SelectedItem') 341 if isTracking: 342 caption += ' - ' + localization.GetByLabel('UI/Inflight/ActiveItem/Tracking') 343 self.SetCaption(caption) 344 345 def _OnClose(self, *args): 346 self.updateAutoTimer = None 347 348 def ProcessSessionChange(self, isRemote, session, change): 349 self.lastActionSerial = None 350 self.lastActionDist = None 351 self.lastCloakingStatus = None 352 self.lastSessionChange = blue.os.GetWallclockTime() 353 354 def OnItemIDSelected(self, itemID): 355 self.itemID = itemID 356 self.lastActionSerial = None 357 self.lastActionDist = None 358 self.lastBountyCheck = None 359 self.bountyText = None 360 uthread2.start_tasklet(self.UpdateAll, True) 361 362 def OnStateChange(self, itemID, flag, true, *args): 363 if itemID != session.shipid and not self.destroyed: 364 uthread.new(self._OnStateChange, itemID, flag, true) 365 366 @telemetry.ZONE_METHOD 367 def _OnStateChange(self, itemID, flag, true): 368 if flag == state.selected and true: 369 self.OnItemIDSelected(itemID) 370 if itemID == self.itemID and flag not in (state.selected, state.mouseOver): 371 self.lastActionSerial = None 372 self.lastActionDist = None 373 self.UpdateAll() 374 375 def OnDistSettingsChange(self): 376 uthread.new(self._OnDistSettingsChange) 377 378 @telemetry.ZONE_METHOD 379 def _OnDistSettingsChange(self): 380 self.ResetLastActionAndCloakingAndUpdateAll() 381 382 def ResetLastActionAndCloakingAndUpdateAll(self): 383 self.lastActionSerial = None 384 self.lastCloakingStatus = None 385 self.UpdateAll(True) 386 387 def TryGetInvItem(self, itemID): 388 if session.shipid is None: 389 return 390 ship = sm.GetService('invCache').GetInventoryFromId(session.shipid) 391 if ship: 392 for invItem in ship.List(): 393 if invItem.itemID == itemID: 394 return invItem 395 396 def OnExpanded(self, *args): 397 if not self.inited: 398 return 399 super(SelectedItemWnd, self).OnExpanded(*args) 400 self.ResetLastActionAndCloakingAndUpdateAll() 401 402 def OnEndMaximize_(self, *args): 403 if not self.inited: 404 return 405 self.ResetLastActionAndCloakingAndUpdateAll() 406 407 def FadeInSelf(self): 408 self.opacity = 0.001 409 super(SelectedItemWnd, self).FadeInSelf() 410 if not self.inited: 411 return 412 self.ResetLastActionAndCloakingAndUpdateAll() 413 414 def SetNoContentHint(self, hint = None): 415 if self.noContentHint is None and hint: 416 self.noContentHint = eveLabel.EveCaptionMedium(text=hint, parent=self.sr.main, align=uiconst.RELATIVE, left=16, top=18, width=256, color=TextColor.SECONDARY) 417 elif self.noContentHint: 418 if hint: 419 self.noContentHint.text = hint 420 self.noContentHint.state = uiconst.UI_DISABLED 421 else: 422 self.noContentHint.state = uiconst.UI_HIDDEN 423 424 def FlushContent(self): 425 self.nameLabel.text = '' 426 self._clear_achievement_info() 427 self.distanceLabel.display = False 428 self.securityLabel.display = False 429 self.timerLabel.display = False 430 self.bountyLabel.display = False 431 self.SetNoContentHint(localization.GetByLabel('UI/Inflight/ActiveItem/NoObjectSelected')) 432 self.buttonCont.Flush() 433 self.iconSprite.state = uiconst.UI_HIDDEN 434 self.charIcon.state = uiconst.UI_HIDDEN 435 436 @telemetry.ZONE_METHOD 437 def UpdateAll(self, reconstructButtons = False): 438 if self.destroyed: 439 return 440 if self.itemID == session.shipid: 441 self.itemID = None 442 bp = sm.GetService('michelle').GetBallpark() 443 if self.IsWindowVisible() and bp and self.itemID: 444 slimItem = uix.GetBallparkRecord(self.itemID) 445 if slimItem: 446 ball = bp.GetBall(self.itemID) 447 if ball: 448 self._UpdateSlimItem(slimItem, reconstructButtons) 449 return 450 invItem = self.TryGetInvItem(self.itemID) 451 if invItem: 452 self._UpdateInvItem(invItem, reconstructButtons) 453 return 454 self.lastActionSerial = None 455 self.lastActionDist = None 456 self.FlushContent() 457 458 def _UpdateSlimItem(self, slimItem, reconstructButtons): 459 self.SetNoContentHint(None) 460 name_text = uix.GetSlimItemName(slimItem) 461 jumpExtraText = self._GetJumpExtraText(slimItem) 462 if jumpExtraText: 463 name_text += jumpExtraText 464 self.nameLabel.text = name_text 465 self.nameRowCont.char_id = slimItem.charID 466 self.nameRowCont.char_name = name_text 467 self._update_achievement_info(slimItem) 468 distanceText = self._GetDistanceText() 469 self.distanceLabel.text = distanceText or u'' 470 self.distanceLabel.display = bool(distanceText) 471 securityText = self._GetSecurityStatusTex(slimItem) 472 self.securityLabel.text = securityText or u'' 473 self.securityLabel.display = bool(securityText) 474 timerText = GetTimerText(slimItem) 475 if timerText.startswith('<br>'): 476 timerText = timerText[4:] 477 self.timerLabel.text = timerText 478 self.timerLabel.display = bool(timerText) 479 bountyText = self.GetBountyText(slimItem) 480 self.bountyLabel.text = bountyText or u'' 481 self.bountyLabel.display = bool(bountyText) 482 self._CheckUpdateIcons(slimItem, slimItem.typeID) 483 self.UpdateKillRightsIconVisibility(slimItem) 484 if reconstructButtons or self._CheckDistanceChanged() or self._RequiresRegularUpdates(slimItem): 485 actions = GetMenuService().UnmergedCelestialMenu(slimItem) 486 self.ReconstructButtons(slimItem, None, actions) 487 if not self.updateAutoTimer: 488 self.updateAutoTimer = AutoTimer(500, self.UpdateAll) 489 490 def _update_achievement_info(self, slim_item): 491 char_id = slim_item.charID 492 if char_id is None: 493 self._clear_achievement_info() 494 return 495 category_id = slim_item.categoryID 496 if category_id and category_id != const.categoryShip: 497 self._clear_achievement_info() 498 return 499 if self.character_title_chip is not None: 500 if self.character_title_chip.character_id == char_id: 501 return 502 self._clear_achievement_info() 503 from achievement_system.client.ui.chip.character_title import CharacterTitleChip 504 self.character_title_chip = CharacterTitleChip(name='character_title_chip', parent=self.titleCont, align=uiconst.TOLEFT, character_id=char_id) 505 506 def _clear_achievement_info(self): 507 self.titleCont.Flush() 508 self.character_title_chip = None 509 510 def _GetSlimItemText(self, slimItem, density): 511 text = uix.GetSlimItemName(slimItem) 512 jumpExtraText = self._GetJumpExtraText(slimItem) 513 if jumpExtraText: 514 text += jumpExtraText 515 distanceText = self._GetDistanceText() 516 securityText = self._GetSecurityStatusTex(slimItem) 517 if density == HEIGHTSTATE_LARGE: 518 if distanceText: 519 text += '<br>%s' % distanceText 520 if securityText: 521 text += '<br>%s' % securityText 522 else: 523 if distanceText: 524 text = '%s - %s' % (text, distanceText) 525 if securityText: 526 text += ' (%s)' % securityText 527 timerText = GetTimerText(slimItem) 528 if timerText: 529 text += timerText 530 bountyText = self.GetBountyText(slimItem) 531 if bountyText: 532 if density == HEIGHTSTATE_SMALL: 533 text += ' (%s)' % bountyText 534 else: 535 text += '<br>' + bountyText 536 return text 537 538 def _UpdateInvItem(self, invItem, reconstructButtons): 539 self._CheckUpdateIcons(None, invItem.typeID) 540 self.UpdateKillRightsIconVisibility() 541 self.nameLabel.text = uix.GetItemName(invItem) 542 self.distanceLabel.display = False 543 self.securityLabel.display = False 544 self.timerLabel.display = False 545 bountyText = self.GetBountyText() 546 self.bountyLabel.text = bountyText or u'' 547 self.bountyLabel.display = bool(bountyText) 548 self.SetNoContentHint(None) 549 self.UpdateKillRightsIconVisibility() 550 if reconstructButtons: 551 data = [(invItem, 0, None)] 552 actions = StartMenuService().InvItemMenu(data) 553 self.ReconstructButtons(None, invItem, actions) 554 555 def _CheckDistanceChanged(self): 556 bp = sm.GetService('michelle').GetBallpark() 557 ball = bp.GetBall(self.itemID) 558 distanceToItem = getattr(ball, 'surfaceDist', None) 559 reconstructButtons = False 560 if distanceToItem is not None: 561 myBallMode = None 562 myball = bp.GetBall(session.shipid) 563 if myball: 564 myBallMode = myball.mode 565 if self.lastCloakingStatus is None or self.lastCloakingStatus != myball.isCloaked: 566 reconstructButtons = True 567 self.lastCloakingStatus = myball.isCloaked 568 if not self.lastActionDist or myBallMode != self.lastActionDist[1] or self.CheckDistanceUpdate(self.lastActionDist[0], distanceToItem): 569 self.lastActionDist = (distanceToItem, myBallMode) 570 reconstructButtons = True 571 return reconstructButtons 572 573 def _GetDistanceText(self): 574 bp = sm.GetService('michelle').GetBallpark() 575 ball = bp.GetBall(self.itemID) 576 distanceToItem = getattr(ball, 'surfaceDist', None) 577 if distanceToItem is not None: 578 return FmtDist(distanceToItem, maxdemicals=1) 579 580 def _GetSecurityStatusTex(self, slimItem): 581 securityStatusText = '' 582 sec = slimItem.securityStatus 583 if sec: 584 securityStatusText = localization.GetByLabel('UI/Inflight/ActiveItem/SelectedItemSecurity', secStatus=sec) 585 return securityStatusText 586 587 def _GetJumpExtraText(self, slimItem): 588 if slimItem.groupID == const.groupStargate: 589 return self._GetStargateSecurityText(slimItem) 590 if HasShipcasterComponent(slimItem.typeID) or HasStarterShipcasterComponent(slimItem.typeID): 591 shipcasterText = GetShipcasterText(slimItem) 592 if shipcasterText: 593 return '<br>%s' % shipcasterText 594 return '' 595 return '' 596 597 def _GetStargateSecurityText(self, slimItem): 598 return u' ({security_status})'.format(security_status=eveformat.solar_system_security_status(slimItem.jumps[0].locationID)) 599 600 def GetBountyText(self, slimItem = None): 601 bountyItemID = None 602 bountyTypeID = None 603 bountySlim = None 604 if slimItem: 605 if slimItem.categoryID == const.categoryEntity: 606 bountyTypeID = slimItem.typeID 607 bountySlim = slimItem 608 elif slimItem.charID: 609 bountyItemID = slimItem.charID 610 bountySlim = slimItem 611 if (bountyItemID, bountyTypeID) != self.lastBountyCheck: 612 bounty = self._GetBountyAmount(bountyTypeID, bountySlim) 613 if bounty: 614 self.bountyText = localization.GetByLabel('UI/Common/BountyAmount', bountyAmount=eveFormat.FmtISK(bounty, 0)) 615 else: 616 self.bountyText = None 617 self.lastBountyCheck = (bountyItemID, bountyTypeID) 618 bountyText = '' 619 if self.bountyText: 620 bountyText += self.bountyText 621 return bountyText 622 623 def UpdateKillRightsIconVisibility(self, slimItem = None): 624 displayUtilMenu = False 625 if slimItem: 626 killRightID, price = self.bountySvc.GetBestKillRight(slimItem.charID) 627 self.killRightsUtilMenu.UpdateKillRightInfo(killRightID, price, slimItem.charID, slimItem.itemID) 628 stateSvc = sm.GetService('stateSvc') 629 if killRightID is not None and not (stateSvc.CheckSuspect(slimItem) or stateSvc.CheckCriminal(slimItem)) and slimItem.categoryID != const.categoryStructure: 630 displayUtilMenu = True 631 self.killRightsUtilMenuCont.display = displayUtilMenu 632 633 def _CheckUpdateIcons(self, slimItem, typeID): 634 charID = None 635 categoryID = None 636 if slimItem: 637 if slimItem.categoryID == const.categoryShip: 638 if idCheckers.IsCharacter(slimItem.charID): 639 charID = slimItem.charID 640 categoryID = slimItem.categoryID 641 if self.lastIcon != (typeID, self.itemID, charID): 642 uthread2.start_tasklet(self.UpdateIcons, typeID, charID) 643 self.lastIcon = (typeID, self.itemID, charID) 644 else: 645 self.iconSprite.state = uiconst.UI_NORMAL 646 if categoryID == const.categoryShip and charID: 647 self.charIcon.state = uiconst.UI_NORMAL 648 649 def _RequiresRegularUpdates(self, slimItem): 650 if HasStructureTimer(slimItem) or HasSkyhookTimer(slimItem): 651 return True 652 if HasShipcasterComponent(slimItem.typeID) or HasStarterShipcasterComponent(slimItem.typeID): 653 return True 654 if HasTowGameObjectiveComponent(slimItem.typeID): 655 return True 656 if HasFilamentSpoolupComponent(slimItem.typeID): 657 return True 658 return False 659 660 @telemetry.ZONE_METHOD 661 def DoBallsRemove(self, pythonBalls, isRelease): 662 for ball, slimItem, terminal in pythonBalls: 663 self.DoBallRemove(ball, slimItem, terminal) 664 665 def DoBallRemove(self, ball, slimItem, terminal): 666 if self.IsWindowVisible() and ball and ball.id == self.itemID: 667 uthread.pool('ActiveItem::UpdateAll', self.UpdateAll, True) 668 669 def UpdateIcons(self, typeID, charID): 670 self.iconSprite.LoadIconByTypeID(typeID, itemID=self.itemID, ignoreSize=True) 671 self.iconSprite.typeID = typeID 672 self.iconSprite.itemID = self.itemID 673 categoryID = evetypes.GetCategoryID(typeID) 674 self.iconSprite.categoryID = categoryID 675 self.iconSprite.state = uiconst.UI_NORMAL 676 if categoryID == const.categoryShip and charID: 677 typeID = cfg.eveowners.Get(charID).typeID 678 self.charIcon.SetID(typeID, charID) 679 self.charIcon.state = uiconst.UI_NORMAL 680 else: 681 self.charIcon.state = uiconst.UI_HIDDEN 682 683 def GetShipDragData(self): 684 categoryID = self.iconSprite.categoryID 685 if categoryID != const.categoryShip: 686 return [] 687 return ShipDragData(self.iconSprite.typeID, self.iconSprite.itemID, self.charIcon.itemID) 688 689 def _GetBountyAmount(self, bountyTypeID, slimItem): 690 bounty = None 691 if bountyTypeID: 692 bounty = get_entity_bounty(bountyTypeID, slimItem.itemID) 693 elif slimItem: 694 if self.bountySvc.CanHaveBounty(slimItem): 695 bounty = self.bountySvc.GetBounty(slimItem.charID, slimItem.corpID, slimItem.allianceID) 696 return bounty 697 698 def GetNpcBounty(self, bountyAmount): 699 return (bountyAmount, None, None) 700 701 def ShowInfo(self, btn, *args): 702 if btn and btn.typeID: 703 sm.GetService('info').ShowInfo(btn.typeID, btn.itemID) 704 705 def GetIconMenu(self, btn, *args): 706 if btn and btn.typeID: 707 return GetMenuService().GetMenuFromItemIDTypeID(getattr(btn, 'itemID', None), btn.typeID) 708 709 def CheckDistanceUpdate(self, lastdist, dist): 710 diff = abs(lastdist - dist) 711 if not diff: 712 return False 713 elif dist: 714 return bool(diff / dist > 0.01) 715 else: 716 return bool(lastdist != dist) 717 718 def IsWindowVisible(self): 719 return bool(not self.IsCollapsed() and not self.IsMinimized() and self.opacity > 0 and self.display) 720 721 @telemetry.ZONE_METHOD 722 def ReconstructButtons(self, slimItem, invItem, actions): 723 if self.destroyed: 724 return 725 if not self.IsWindowVisible(): 726 self.updateAutoTimer = None 727 return 728 menuData = menuDataFactory.CreateMenuDataFromRawTuples(actions) 729 if menuData is None: 730 menuData = MenuData() 731 actionNamesSerial, validActionsByName = self._GetValidActionsByName(menuData) 732 if actionNamesSerial == self.lastActionSerial: 733 return 734 self.buttonCont.Flush() 735 visibleActionNames = self._GetVisibleActionNamesOrdered(invItem, slimItem, validActionsByName) 736 reasonsWhyNotAvailable = getattr(actions, 'reasonsWhyNotAvailable', {}) 737 for actionName in visibleActionNames: 738 if actionName is None: 739 continue 740 menuEntryData = self._GetMenuEntryData(actionName, validActionsByName) 741 disabledHint = None 742 if not menuEntryData: 743 menuEntryData = MenuEntryData(text=self._GetDisabledMenuLabel(actionName)) 744 disabledHint = reasonsWhyNotAvailable.get(actionName, localization.GetByLabel('UI/Menusvc/MenuHints/NoReasonGiven')) 745 self._ConstructButton(menuEntryData, disabledHint) 746 747 self._UpdateButtonSize() 748 self.lastActionSerial = actionNamesSerial 749 750 def _GetDisabledMenuLabel(self, actionName): 751 if actionName in ('UI/Inflight/WarpToWithinDistance', 'UI/Inflight/WarpToMoonMiningPoint'): 752 return MenuLabel(actionName, {'distance': 0}) 753 elif actionName == 'UI/Inflight/JumpByShipcaster': 754 return MenuLabel(actionName, {'text': localization.GetByLabel('UI/Inflight/Jump')}) 755 else: 756 return MenuLabel(actionName) 757 758 def _GetMenuEntryData(self, actionName, validActionsByName): 759 menuEntryData = validActionsByName.get(actionName, None) 760 if not menuEntryData and actionName in TOGGLE_ACTIONS: 761 menuEntryData = validActionsByName.get(TOGGLE_ACTIONS[actionName], None) 762 return menuEntryData 763 764 def _GetNotAvailableHint(self, actionName, reasonsWhyNotAvailable, validActionsByName): 765 if actionName not in validActionsByName: 766 return reasonsWhyNotAvailable.get(actionName, localization.GetByLabel('UI/Menusvc/MenuHints/NoReasonGiven')) 767 768 def _GetVisibleActionNamesOrdered(self, invItem = None, slimItem = None, validActionsByName = None): 769 if slimItem: 770 typeID = slimItem.typeID 771 groupID = slimItem.groupID 772 categoryID = slimItem.categoryID 773 elif invItem: 774 typeID = invItem.typeID 775 groupID = invItem.groupID 776 categoryID = invItem.categoryID 777 else: 778 typeID = None 779 groupID = None 780 categoryID = None 781 actions = [] 782 if 'UI/Inflight/AlignTo' not in validActionsByName: 783 approachLabelPath = 'UI/Inflight/ApproachObject' 784 else: 785 approachLabelPath = 'UI/Inflight/AlignTo' 786 actions += [approachLabelPath, 'UI/Inflight/Submenus/WarpToWithin'] 787 if typeID and typeID in ACTIONS_BY_TYPEID: 788 actions += ACTIONS_BY_TYPEID[typeID] 789 elif groupID and groupID in ACTIONS_BY_GROUPID: 790 actions += ACTIONS_BY_GROUPID[groupID] 791 elif categoryID and categoryID in ACTIONS_BY_CATEGORYID: 792 actions += ACTIONS_BY_CATEGORYID[categoryID] 793 item = slimItem or invItem 794 if item: 795 if HasCargoBayComponent(typeID) and not HasItemTrader(typeID): 796 if HasUnderConstructionComponent(typeID): 797 actions.append('UI/Inflight/SpaceComponents/UnderConstruction/Access') 798 elif cargobay.IsAccessibleByCharacter(item, session.charid): 799 actions.append('UI/Commands/OpenCargo') 800 if HasItemTrader(typeID): 801 actions.append('UI/Inflight/SpaceComponents/ItemTrader/Access') 802 actions = itertoolsext.remove_duplicates_and_preserve_order(actions) 803 actions += ACTIONS_ALWAYS_VISIBLE 804 return actions 805 806 def _GetValidActionsByName(self, menuData): 807 menuEntries = self.GetAllActionsFromMenus(menuData) 808 actionNamesSerial = '' 809 validActionsByName = {} 810 for menuEntryData in menuEntries: 811 labelPath = menuEntryData.GetLabelPath() 812 if labelPath in selectedItemConst.ICON_ID_AND_CMD_BY_ACTIONID: 813 validActionsByName[labelPath] = menuEntryData 814 if labelPath: 815 actionNamesSerial += '%s_' % labelPath 816 817 return (actionNamesSerial, validActionsByName) 818 819 def _ConstructButton(self, menuEntryData, disabledHint): 820 actionObject = selectedItemConst.ICON_ID_AND_CMD_BY_ACTIONID[menuEntryData.GetLabelPath()] 821 SelectedItemButton(name=actionObject.uniqueUiName or menuEntryData.GetLabelPath(), parent=self.buttonCont, align=uiconst.TOLEFT, uniqueUiName=actionObject.uniqueUiName, menuEntryData=menuEntryData, func=lambda *args: self.on_selected_item_button(menuEntryData.func), cmdName=CMD_BY_ACTIONID.get(menuEntryData.GetLabelPath(), ''), disabledHint=disabledHint, enabled=not bool(disabledHint), padRight=BUTTON_PADDING) 822 823 def on_selected_item_button(self, func): 824 GetAudioService().SendUIEvent('ui_radial_select_play') 825 func() 826 827 def GetAllActionsFromMenus(self, menuData): 828 allActionsDict = OrderedDict() 829 subMenuActions = [] 830 for menuEntryData in menuData.GetEntries(): 831 if not menuEntryData or not menuEntryData.IsEnabled(): 832 continue 833 allActionsDict[menuEntryData.GetLabelPath()] = menuEntryData 834 if menuEntryData.GetLabelPath() in SUB_MENU_ACTIONS: 835 for eachSubOption in menuEntryData.GetSubMenuData() or []: 836 if eachSubOption is not None: 837 subMenuActions.append(eachSubOption) 838 839 for menuEntryData in subMenuActions: 840 if menuEntryData.GetLabelPath() not in allActionsDict: 841 allActionsDict[menuEntryData.GetLabelPath()] = menuEntryData 842 843 return allActionsDict.values() 844 845 def _IsDisabledEntry(self, each): 846 if isinstance(each, MenuEntryData): 847 return not each.IsEnabled() 848 else: 849 return each[1] == DISABLED_ENTRY0 850 851 def OnPlanetViewChanged(self, newPlanetID, oldPlanetID): 852 for planetID in (newPlanetID, oldPlanetID): 853 if planetID == self.itemID: 854 self.OnItemIDSelected(self.itemID) 855 856 def GetMenuMoreOptions(self): 857 menu = super(SelectedItemWnd, self).GetMenuMoreOptions() 858 menu += self.GetMenu() 859 return menu 860 861 def GetMenu(self, *args): 862 menu = super(SelectedItemWnd, self).GetMenu(*args) 863 menu.AddEntry(MenuLabel('UI/Generic/Copy'), self._CopyText) 864 if self.charIcon.display: 865 menu.AddSeparator() 866 menu.AddEntry(MenuLabel('UI/Common/Pilot', {'character': self.charIcon.itemID}), subMenuData=lambda : self.charIcon.GetMenu(), texturePath=eveicon.person) 867 return menu 868 869 def _CopyText(self): 870 text = self._GetTextCopyContent() 871 text = eveformat.strip_tags(text, tags=['localized']) 872 if text is not None: 873 blue.pyos.SetClipboardData(text) 874 875 def _GetTextCopyContent(self): 876 ballpark = sm.GetService('michelle').GetBallpark() 877 if ballpark is None or self.itemID is None: 878 return 879 slim_item = uix.GetBallparkRecord(self.itemID) 880 if slim_item is None: 881 return 882 ball = ballpark.GetBall(self.itemID) 883 if ball: 884 text = self._GetSlimItemText(slim_item, density=HEIGHTSTATE_LARGE) 885 return text.replace(u'<br>', '\n') 886 item = self.TryGetInvItem(self.itemID) 887 if item: 888 return u'{name}\n{bounty}'.format(name=uix.GetItemName(item), bounty=self.GetBountyText()) 889 890 891 class NameRowContainer(Container): 892 893 def __init__(self, *args, **kwargs): 894 self.char_id = None 895 self.char_name = None 896 super(NameRowContainer, self).__init__(*args, **kwargs) 897 898 def LoadTooltipPanel(self, panel, *args): 899 if self.char_id is None or self.char_name is None: 900 return 901 panel.LoadStandardSpacing() 902 panel.columns = 1 903 from character.client.identifier import CharacterIdentifier 904 from cosmetics.client.character.title.api.repository import get_repository 905 equipped_state = get_repository().get_equipped_title(CharacterIdentifier(self.char_id)) 906 has_title = equipped_state is not None and equipped_state.equipped_title is not None 907 if has_title: 908 from achievement_system.client.ui.chip.character_title import CharacterTitleChip 909 title_chip = CharacterTitleChip(name='character_title_chip', character_id=self.char_id) 910 panel.AddTextBodyLabel(text=self.char_name) 911 panel.AddCell(title_chip) 912 else: 913 panel.AddTextBodyLabel(text=self.char_name)