NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

userentry.py (41277B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\userentry.py
      2 import sys
      3 from carbon.common.script.util.commonutils import GetAttrs
      4 from carbon.common.script.util.timerstuff import AutoTimer
      5 from carbonui import TextColor, uiconst
      6 from carbonui.control.scrollentries import SE_BaseClassCore
      7 from carbonui.primitives.base import ReverseScaleDpi
      8 from carbonui.primitives.container import Container
      9 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     10 from carbonui.primitives.sprite import Sprite
     11 from carbonui.uicore import uicore
     12 from eve.client.script.ui.control import eveIcon
     13 from eve.client.script.ui.control.eveLabel import EveLabelMedium, EveLabelLarge
     14 from eve.client.script.ui.eveColor import SUCCESS_GREEN_HEX
     15 from eve.client.script.ui.shared.neocom.addressBook.buddyOnlineIndicator import BuddyOnlineIndicator
     16 from eve.client.script.ui.shared.stateFlag import GetStateFlagFromData, AddAndSetFlagIcon, GetFlagFromRelationShip, AddAndSetFlagIconFromData
     17 from eve.client.script.ui.station.agents.agentConversationIcon import AgentConversationIcon
     18 from eve.client.script.ui.station.agents.agentDialogueUtil import GetAgentNameAndLevel
     19 from eve.client.script.ui.station.agents.agentUtil import GetAgentLocationText
     20 from eve.client.script.ui.util.uix import GetTextHeight
     21 from eve.common.script.sys import idCheckers
     22 from eve.common.script.util.eveFormat import FmtISK
     23 from eveservices.menu import GetMenuService
     24 from eveservices.xmppchat import GetChatService
     25 import evetypes
     26 from gametime import GetWallclockTimeNow, HOUR, MIN
     27 import localization
     28 import telemetry
     29 import uthread
     30 from menu import MenuLabel
     31 from npcs.divisions import get_division_name
     32 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     33 HEIGHT = 40
     34 BUDDY_CONTAINER_PAD_LEFT = 4
     35 STATUS_ICON_SIZE = 12
     36 STATUS_ICON_PAD_LEFT = 2
     37 
     38 class User(SE_BaseClassCore):
     39     __guid__ = 'listentry.User'
     40     __params__ = ['charID']
     41     __notifyevents__ = ['OnContactLoggedOn',
     42      'OnContactLoggedOff',
     43      'OnClientContactChange',
     44      'OnPortraitCreated',
     45      'OnContactNoLongerContact',
     46      'OnStateSetupChange',
     47      'OnMyFleetInited',
     48      'OnFleetJoin_Local',
     49      'OnFleetLeave_Local',
     50      'ProcessOnUIAllianceRelationshipChanged',
     51      'OnContactChange',
     52      'OnBlockContacts',
     53      'OnUnblockContacts',
     54      'OnCrimewatchEngagementUpdated',
     55      'OnSuspectsAndCriminalsUpdate',
     56      'OnDisapprovalUpdate']
     57     isDragObject = True
     58     charid = None
     59     selected = 0
     60     big = 0
     61     id = None
     62     groupID = None
     63     picloaded = 0
     64     fleetCandidate = 0
     65     label = None
     66     slimuser = False
     67 
     68     def Startup(self, *args):
     69         self._CreatePicture()
     70         self._CreateName()
     71         self.sr.voiceIcon = None
     72         self.sr.eveGateIcon = None
     73         self.contactLabel = None
     74         self.rightCont = ContainerAutoSize(parent=self, align=uiconst.CENTERRIGHT, height=16, left=4)
     75         self.flagCont = None
     76         self._CreateOnlineStatusIcon()
     77         self._CreateStandingLabel()
     78         self._CreateCorpApplicationLabel()
     79         sm.RegisterNotify(self)
     80 
     81     def _CreatePicture(self):
     82         self.sr.picture = Container(parent=self, name='picture', state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT, pos=(2, 0, 32, 32))
     83         self.sr.extraIconCont = Container(name='extraIconCont', parent=self, idx=0, pos=(0, 0, 16, 16), align=uiconst.BOTTOMLEFT, state=uiconst.UI_HIDDEN)
     84 
     85     @telemetry.ZONE_METHOD
     86     def _CreateName(self):
     87         self.sr.namelabel = EveLabelLarge(name='nameLabel', parent=self, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, idx=0)
     88 
     89     def _CreateContactLabels(self):
     90         self.contactLabel = EveLabelMedium(text='', name='contactLabels', parent=self, state=uiconst.UI_DISABLED, idx=0, align=uiconst.BOTTOMLEFT, left=40, top=4, color=TextColor.SECONDARY)
     91 
     92     @telemetry.ZONE_METHOD
     93     def _CreateStandingLabel(self):
     94         self.sr.standingLabel = EveLabelMedium(text='', name='standingLabel', parent=self.rightCont, state=uiconst.UI_DISABLED, align=uiconst.TORIGHT)
     95 
     96     def _CreateOnlineStatusIcon(self):
     97         self.onlineStatusIcon = BuddyOnlineIndicator(parent=self.rightCont, align=uiconst.TORIGHT, width=16)
     98 
     99     @telemetry.ZONE_METHOD
    100     def _CreateCorpApplicationLabel(self):
    101         self.sr.corpApplicationLabel = EveLabelMedium(text='corpApplicationLabel', name='corpApplicationLabel', parent=self, state=uiconst.UI_DISABLED, idx=0, align=uiconst.CENTERRIGHT)
    102         self.sr.corpApplicationLabel.left = 16
    103 
    104     def PreLoad(node):
    105         data = node
    106         charinfo = data.Get('info', None) or cfg.eveowners.Get(data.charID)
    107         data.info = charinfo
    108         if data.GetLabel:
    109             data.label = data.GetLabel(data)
    110         elif not data.Get('label', None):
    111             label = charinfo.name
    112             if data.bounty:
    113                 label += '<br>'
    114                 label += localization.GetByLabel('UI/Common/BountyAmount', bountyAmount=FmtISK(data.bounty.bounty, 0))
    115             elif data.killTime:
    116                 label += '<br>' + localization.GetByLabel('UI/PeopleAndPlaces/ExpiresTime', expires=data.killTime)
    117             data.label = label
    118         groupID = evetypes.GetGroupID(data.info.typeID)
    119         data.invtype = data.info.typeID
    120         data.IsCharacter = groupID == const.groupCharacter
    121         data.IsCorporation = groupID == const.groupCorporation
    122         data.IsFaction = groupID == const.groupFaction
    123         data.IsAlliance = groupID == const.groupAlliance
    124         if data.IsCorporation and not idCheckers.IsNPC(data.charID):
    125             logoData = cfg.corptickernames.Get(data.charID)
    126 
    127     def Load(self, node):
    128         self.sr.node = node
    129         data = node
    130         self.name = data.info.name
    131         self.sr.namelabel.text = data.label
    132         self.charid = self.id = data.itemID = data.charID
    133         self.picloaded = 0
    134         self.sr.parwnd = data.Get('dad', None)
    135         self.fleetCandidate = data.Get('fleetster', 1) and not data.info.IsNPC()
    136         self.confirmOnDblClick = data.Get('dblconfirm', 1)
    137         self.leaveDadAlone = data.Get('leavedad', 0)
    138         self.slimuser = data.Get('slimuser', False)
    139         self.data = {'Name': self.name,
    140          'charid': data.charID}
    141         self.inWatchlist = sm.GetService('addressbook').IsInWatchlist(data.charID)
    142         self.isContactList = data.Get('contactType', None)
    143         self.applicationDate = data.Get('applicationDate', None)
    144         self.contactLevel = None
    145         if self.isContactList:
    146             self.contactLevel = data.contactLevel
    147             self.SetLabelText()
    148         self.isCorpOrAllianceContact = data.contactType and data.contactType != 'contact'
    149         data.listvalue = [data.info.name, data.charID]
    150         level = self.sr.node.Get('sublevel', 0)
    151         subLevelOffset = 16
    152         self.sr.picture.left = 2 + max(0, subLevelOffset * level)
    153         self.LoadPortrait()
    154         if data.IsCharacter:
    155             uthread.new(self.SetRelationship, data)
    156             self.onlineStatusIcon.Hide()
    157             if data.charID != session.charid:
    158                 if not idCheckers.IsNPC(data.charID) and not self.isCorpOrAllianceContact and self.inWatchlist:
    159                     try:
    160                         self.SetOnline(sm.GetService('onlineStatus').GetOnlineStatus(data.charID, fetch=False))
    161                     except IndexError:
    162                         sys.exc_clear()
    163 
    164         else:
    165             self.onlineStatusIcon.Hide()
    166             if data.charID != session.charid:
    167                 uthread.new(self.SetRelationship, data)
    168         self.sr.namelabel.left = 40 + max(0, subLevelOffset * level)
    169         if self.sr.node.Get('selected', 0):
    170             self.Select()
    171         else:
    172             self.Deselect()
    173         if not self.isCorpOrAllianceContact:
    174             self.contactLevel = sm.GetService('addressbook').GetStandingsLevel(self.charid, 'contact')
    175             self.SetBlocked(1)
    176         else:
    177             self.SetBlocked(0)
    178         if self.isCorpOrAllianceContact:
    179             self.SetStandingText(self.contactLevel)
    180         if self.applicationDate:
    181             self.sr.corpApplicationLabel.SetText(localization.GetByLabel('UI/Corporations/Applied', applydate=self.applicationDate))
    182             self.sr.corpApplicationLabel.Show()
    183             data.Set('sort_' + localization.GetByLabel('UI/Common/Date'), self.applicationDate)
    184             data.Set('sort_' + localization.GetByLabel('UI/Common/Name'), data.info.name)
    185         else:
    186             self.sr.corpApplicationLabel.SetText('')
    187             self.sr.corpApplicationLabel.Hide()
    188 
    189     def GetValue(self):
    190         return [self.name, self.id]
    191 
    192     def GetDynamicHeight(node, width):
    193         if '<br>' in node.label:
    194             node.height = max(HEIGHT, GetTextHeight(node.label, linespace=11))
    195         else:
    196             node.height = HEIGHT
    197         return node.height
    198 
    199     def OnContactLoggedOn(self, charID):
    200         if self and not self.destroyed and charID == self.charid and charID != session.charid and not idCheckers.IsNPC(charID):
    201             self.SetOnline(1)
    202 
    203     def OnContactLoggedOff(self, charID):
    204         if self and not self.destroyed and charID == self.charid and charID != session.charid and not idCheckers.IsNPC(charID):
    205             self.SetOnline(0)
    206 
    207     def OnClientContactChange(self, charID, online):
    208         if online:
    209             self.OnContactLoggedOn(charID)
    210         else:
    211             self.OnContactLoggedOff(charID)
    212 
    213     def OnContactNoLongerContact(self, charID):
    214         if self and not self.destroyed and charID == self.charid and charID != session.charid:
    215             self.SetOnline(None)
    216 
    217     def OnPortraitCreated(self, charID, _size):
    218         if self.destroyed:
    219             return
    220         if self.sr.node and charID == self.sr.node.charID and not self.picloaded:
    221             self.LoadPortrait(orderIfMissing=False)
    222 
    223     def OnContactChange(self, contactIDs, contactType = None):
    224         if self.destroyed:
    225             return
    226         self.SetRelationship(self.sr.node)
    227         if self.charid in contactIDs:
    228             if sm.GetService('addressbook').IsInAddressBook(self.charid, contactType):
    229                 if not self.isContactList:
    230                     self.isContactList = contactType
    231             else:
    232                 self.isContactList = None
    233             self.inWatchlist = sm.GetService('addressbook').IsInWatchlist(self.charid)
    234             if not self.inWatchlist:
    235                 isBlocked = sm.GetService('addressbook').IsBlocked(self.charid)
    236                 if isBlocked:
    237                     self.onlineStatusIcon.SetBlocked()
    238                 else:
    239                     self.onlineStatusIcon.Hide()
    240 
    241     def OnBlockContacts(self, contactIDs):
    242         if not self or self.destroyed:
    243             return
    244         if self.charid in contactIDs:
    245             self.SetBlocked(1)
    246         self.UpdateComponentSizes()
    247 
    248     def OnUnblockContacts(self, contactIDs):
    249         if not self or self.destroyed:
    250             return
    251         if self.charid in contactIDs:
    252             self.SetBlocked(0)
    253         self.UpdateComponentSizes()
    254 
    255     def OnStateSetupChange(self, what):
    256         if self.destroyed:
    257             return
    258         self.SetRelationship(self.sr.node)
    259 
    260     def ProcessOnUIAllianceRelationshipChanged(self, *args):
    261         if self.destroyed:
    262             return
    263         self.SetRelationship(self.sr.node)
    264 
    265     def OnFleetJoin_Local(self, memberInfo, state = 'Active'):
    266         if self.destroyed:
    267             return
    268         myID = GetAttrs(self, 'sr', 'node', 'charID')
    269         charID = memberInfo.charID
    270         if myID is not None and charID == myID:
    271             uthread.new(self.SetRelationship, self.sr.node)
    272 
    273     def OnFleetLeave_Local(self, memberInfo):
    274         if self.destroyed:
    275             return
    276         myID = GetAttrs(self, 'sr', 'node', 'charID')
    277         charID = memberInfo.charID
    278         if myID is not None and charID == myID or charID == session.charid:
    279             uthread.new(self.SetRelationship, self.sr.node)
    280 
    281     def OnMyFleetInited(self):
    282         if self.destroyed:
    283             return
    284         uthread.new(self.SetRelationship, self.sr.node)
    285 
    286     def SetOnline(self, online):
    287         if self.destroyed:
    288             return
    289         if self.slimuser:
    290             return
    291         if online is None or not self.inWatchlist or self.isCorpOrAllianceContact:
    292             self.onlineStatusIcon.Hide()
    293         elif online:
    294             self.onlineStatusIcon.SetOnline()
    295         else:
    296             self.onlineStatusIcon.SetOffline()
    297 
    298     def SetBlocked(self, blocked):
    299         isBlocked = sm.GetService('addressbook').IsBlocked(self.charid)
    300         if blocked and isBlocked and not self.isCorpOrAllianceContact:
    301             self.onlineStatusIcon.SetBlocked()
    302         elif self.inWatchlist:
    303             self.onlineStatusIcon.SetNotBlocked()
    304         else:
    305             self.onlineStatusIcon.Hide()
    306 
    307     @telemetry.ZONE_METHOD
    308     def SetLabelText(self):
    309         labelMask = sm.GetService('addressbook').GetLabelMask(self.charid)
    310         self.sr.node.labelMask = labelMask
    311         labeltext = sm.GetService('addressbook').GetLabelText(labelMask, self.isContactList)
    312         if labeltext:
    313             self.SetContactLabelText(labeltext)
    314 
    315     def SetContactLabelText(self, labeltext):
    316         if not self.contactLabel:
    317             self._CreateContactLabels()
    318         self.sr.namelabel.SetAlign(uiconst.TOPLEFT)
    319         self.sr.namelabel.top = 3
    320         self.contactLabel.text = labeltext
    321 
    322     def SetStandingText(self, standing):
    323         self.sr.standingLabel.text = standing
    324 
    325     def SetRelationship(self, data, debugFlag = None):
    326         if self.destroyed:
    327             return
    328         if self.slimuser:
    329             return
    330         if not data:
    331             return
    332         flag = None
    333         if data.Get('contactType', None):
    334             if self.contactLevel is None:
    335                 return
    336             flag = GetFlagFromRelationShip(self.contactLevel)
    337         else:
    338             flag = GetStateFlagFromData(data)
    339         if flag:
    340             self._ConstructFlagCont(flag)
    341 
    342     def _ConstructFlagCont(self, flag):
    343         if self.flagCont is None:
    344             self.flagCont = Container(name='flagCont', parent=self.rightCont, pos=(0, 0, 16, 16), align=uiconst.TORIGHT, idx=0)
    345         AddAndSetFlagIcon(self.flagCont, flag=flag, top=0, left=0, width=12, height=12, align=uiconst.CENTER)
    346 
    347     def LoadPortrait(self, orderIfMissing = True):
    348         self.sr.picture.Flush()
    349         if self.sr.node is None:
    350             return
    351         if eveIcon.GetOwnerLogo(self.sr.picture, self.id, size=32, callback=True, orderIfMissing=orderIfMissing):
    352             self.picloaded = 1
    353 
    354     def RemoveFromListGroup(self, listGroupIDs, charIDs, listname):
    355         if self.destroyed:
    356             return
    357         if listGroupIDs:
    358             for listGroupID, charID in listGroupIDs:
    359                 uicore.registry.RemoveFromListGroup(listGroupID, charID)
    360 
    361             sm.GetService('addressbook').RefreshWindow()
    362         if charIDs and listname:
    363             name = [localization.GetByLabel('UI/AddressBook/RemoveAddressBook1'), cfg.eveowners.Get(charIDs[0]).name][len(charIDs) == 1]
    364             if prompt_player_fsd_dialog('WarnDeleteFromAddressbook', {'name': name,
    365              'type': listname}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES:
    366                 return
    367             sm.GetService('addressbook').DeleteEntryMulti(charIDs, None)
    368 
    369     def GetMenu(self):
    370         if self.destroyed:
    371             return
    372         m = []
    373         selected = self.sr.node.scroll.GetSelectedNodes(self.sr.node)
    374         multi = len(selected) > 1
    375         if multi:
    376             return self._GetMultiMenu(selected)
    377         channelID = self.sr.node.Get('channelID', None)
    378         m = GetMenuService().GetMenuFromItemIDTypeID(self.id, self.sr.node.invtype, channelID=channelID)
    379         if self.sr.node.Get('GetMenu', None) is not None:
    380             m += self.sr.node.GetMenu(self.sr.node)
    381         listGroupID = self.sr.node.Get('listGroupID', None)
    382         if listGroupID is not None:
    383             group = uicore.registry.GetListGroup(listGroupID)
    384             if group:
    385                 if listGroupID not in [('buddygroups', 'all'), ('buddygroups', 'allcorps')]:
    386                     m.append(None)
    387                     m.append((MenuLabel('UI/Common/RemoveFromGroup', {'groupname': group['label']}), self.RemoveFromListGroup, ([(listGroupID, self.charid)], [], '')))
    388         if self.sr.node.Get('MenuFunction', None):
    389             cm = [None]
    390             cm += self.sr.node.MenuFunction([self.sr.node])
    391             m += cm
    392         if self.isContactList is not None:
    393             if sm.GetService('addressbook').ShowLabelMenuAndManageBtn(self.isContactList):
    394                 m.append(None)
    395                 assignLabelMenu = sm.StartService('addressbook').GetAssignLabelMenu(selected, [self.charid], self.isContactList)
    396                 if len(assignLabelMenu) > 0:
    397                     m.append((MenuLabel('UI/Mail/AssignLabel'), assignLabelMenu))
    398                 removeLabelMenu = sm.StartService('addressbook').GetRemoveLabelMenu(selected, [self.charid], self.isContactList)
    399                 if len(removeLabelMenu) > 0:
    400                     m.append((MenuLabel('UI/Mail/LabelRemove'), removeLabelMenu))
    401         return m
    402 
    403     def _GetMultiMenu(self, selected):
    404         m = []
    405         charIDs = []
    406         multiCharIDs = []
    407         multiEveCharIDs = []
    408         listGroupIDs = {}
    409         listGroupID_charIDs = []
    410         onlyCharacters = True
    411         for entry in selected:
    412             listGroupID = entry.listGroupID
    413             if listGroupID:
    414                 listGroupIDs[listGroupID] = 0
    415                 listGroupID_charIDs.append((listGroupID, entry.charID))
    416             if entry.charID:
    417                 charIDs.append(entry.charID)
    418                 multiCharIDs.append(entry.charID)
    419                 if not idCheckers.IsCharacter(entry.charID):
    420                     onlyCharacters = False
    421 
    422         if self.isContactList is None:
    423             if onlyCharacters:
    424                 m += GetMenuService().CharacterMenu(charIDs)
    425             if listGroupIDs:
    426                 listname = ''
    427                 delCharIDs = []
    428                 rem = []
    429                 for listGroupID, charID in listGroupID_charIDs:
    430                     if listGroupID in [('buddygroups', 'all'), ('buddygroups', 'allcorps')]:
    431                         if onlyCharacters:
    432                             return m
    433                         uicore.registry.GetListGroup(listGroupID)
    434                         listname = [localization.GetByLabel('UI/Generic/BuddyList')][listGroupID == ('buddygroups', 'all')]
    435                         delCharIDs.append(charID)
    436                         rem.append((listGroupID, charID))
    437 
    438                 for each in rem:
    439                     listGroupID_charIDs.remove(each)
    440 
    441                 foldername = 'folders'
    442                 if len(listGroupIDs) == 1:
    443                     group = uicore.registry.GetListGroup(listGroupIDs.keys()[0])
    444                     if group:
    445                         foldername = group['label']
    446                 label = ''
    447                 if delCharIDs and listname:
    448                     label = localization.GetByLabel('UI/PeopleAndPlaces/RemoveMultipleFromAddressbook', removecount=len(delCharIDs))
    449                     if listGroupID_charIDs:
    450                         label += [', ']
    451                 if listGroupID_charIDs:
    452                     label += localization.GetByLabel('UI/PeopleAndPlaces/RemoveFromFolder', foldername=foldername, removecount=len(listGroupID_charIDs))
    453                 m.append((label, self.RemoveFromListGroup, (listGroupID_charIDs, delCharIDs, listname)))
    454         else:
    455             addressBookSvc = sm.GetService('addressbook')
    456             counter = len(selected)
    457             blocked = 0
    458             if self.isContactList == 'contact':
    459                 editLabel = localization.GetByLabel('UI/PeopleAndPlaces/EditContacts', contactcount=counter)
    460                 m.append((editLabel, addressBookSvc.EditContacts, [multiCharIDs, 'contact']))
    461                 deleteLabel = localization.GetByLabel('UI/PeopleAndPlaces/RemoveContacts', contactcount=counter)
    462                 m.append((deleteLabel, addressBookSvc.DeleteEntryMulti, [multiCharIDs, 'contact']))
    463                 for charid in multiCharIDs:
    464                     if sm.GetService('addressbook').IsBlocked(charid):
    465                         blocked += 1
    466 
    467                 if blocked == counter:
    468                     unblockLabel = localization.GetByLabel('UI/PeopleAndPlaces/UnblockContacts', contactcount=blocked)
    469                     m.append((unblockLabel, addressBookSvc.UnblockOwner, [multiCharIDs]))
    470             elif self.isContactList == 'corpcontact':
    471                 editLabel = localization.GetByLabel('UI/PeopleAndPlaces/EditCorpContacts', contactcount=counter)
    472                 m.append((editLabel, addressBookSvc.EditContacts, [multiCharIDs, 'corpcontact']))
    473                 deleteLabel = localization.GetByLabel('UI/PeopleAndPlaces/RemoveCorpContacts', contactcount=counter)
    474                 m.append((deleteLabel, addressBookSvc.DeleteEntryMulti, [multiCharIDs, 'corpcontact']))
    475             elif self.isContactList == 'alliancecontact':
    476                 editLabel = localization.GetByLabel('UI/PeopleAndPlaces/EditAllianceContacts', contactcount=counter)
    477                 m.append((editLabel, addressBookSvc.EditContacts, [multiCharIDs, 'alliancecontact']))
    478                 deleteLabel = localization.GetByLabel('UI/PeopleAndPlaces/RemoveAllianceContacts', contactcount=counter)
    479                 m.append((deleteLabel, addressBookSvc.DeleteEntryMulti, [multiCharIDs, 'alliancecontact']))
    480             m.append(None)
    481             assignLabelMenu = sm.StartService('addressbook').GetAssignLabelMenu(selected, multiCharIDs, self.isContactList)
    482             if len(assignLabelMenu) > 0:
    483                 m.append((MenuLabel('UI/PeopleAndPlaces/AddContactLabel'), assignLabelMenu))
    484             removeLabelMenu = sm.StartService('addressbook').GetRemoveLabelMenu(selected, multiCharIDs, self.isContactList)
    485             if len(removeLabelMenu) > 0:
    486                 m.append((MenuLabel('UI/PeopleAndPlaces/RemoveContactLabel'), removeLabelMenu))
    487             m.append(None)
    488             m.append((MenuLabel('UI/Commands/CapturePortrait'), sm.StartService('photo').SavePortraits, [multiEveCharIDs]))
    489         if self.sr.node.Get('MenuFunction', None):
    490             cm = [None]
    491             cm += self.sr.node.MenuFunction(selected)
    492             m += cm
    493         return m
    494 
    495     def ShowInfo(self, *args):
    496         if self.destroyed:
    497             return
    498         sm.GetService('info').ShowInfo(cfg.eveowners.Get(self.charid).typeID, self.charid)
    499 
    500     def OnClick(self, *args):
    501         if self.destroyed:
    502             return
    503         prompt_player_fsd_dialog('ListEntryClick')
    504         self.sr.node.scroll.SelectNode(self.sr.node)
    505         if self.sr.node.Get('OnClick', None):
    506             self.sr.node.OnClick(self)
    507 
    508     def OnDblClick(self, *args):
    509         if self.destroyed:
    510             return
    511         if self.sr.node.Get('OnDblClick', None):
    512             self.sr.node.OnDblClick(self)
    513             return
    514         if self.sr.parwnd and hasattr(self.sr.parwnd, 'Select') and self.confirmOnDblClick:
    515             self.sr.parwnd.Select(self)
    516             self.sr.parwnd.Confirm()
    517             return
    518         if not self.leaveDadAlone and self.sr.parwnd and uicore.registry.GetModalWindow() == self.sr.parwnd:
    519             self.sr.parwnd.SetModalResult(uiconst.ID_OK)
    520             return
    521         onDblClick = settings.user.ui.Get('dblClickUser', 0)
    522         if onDblClick == 0:
    523             sm.GetService('info').ShowInfo(cfg.eveowners.Get(self.charid).typeID, self.charid)
    524         elif onDblClick == 1:
    525             GetChatService().Invite(self.charid)
    526 
    527     def GetDragData(self, *args):
    528         if self and not self.destroyed and not self.slimuser:
    529             return self.sr.node.scroll.GetSelectedNodes(self.sr.node)
    530         else:
    531             return []
    532 
    533     @classmethod
    534     def GetCopyData(cls, node):
    535         return node.label
    536 
    537     def OnSuspectsAndCriminalsUpdate(self, criminalizedCharIDs, decriminalizedCharIDs):
    538         return self._UpdateOnSuspectCriminalAndDisapprovalChanges(criminalizedCharIDs, decriminalizedCharIDs)
    539 
    540     def OnDisapprovalUpdate(self, newCharIDs, removedCharIDs):
    541         return self._UpdateOnSuspectCriminalAndDisapprovalChanges(newCharIDs, removedCharIDs)
    542 
    543     def _UpdateOnSuspectCriminalAndDisapprovalChanges(self, newCharIDs, removedCharIDs):
    544         if self.destroyed:
    545             return
    546         charID = GetAttrs(self, 'sr', 'node', 'charID')
    547         if charID is not None and (charID in newCharIDs or charID in removedCharIDs):
    548             uthread.new(self.SetRelationship, self.sr.node)
    549 
    550     def OnCrimewatchEngagementUpdated(self, otherCharId, timeout):
    551         if self.destroyed:
    552             return
    553         charID = GetAttrs(self, 'sr', 'node', 'charID')
    554         if charID is not None and charID == otherCharId:
    555             uthread.new(self.SetRelationship, self.sr.node)
    556 
    557     def OnDropData(self, dragObj, nodes):
    558         nonCharNodes = []
    559         for eachNode in nodes:
    560             itemID = getattr(eachNode, 'itemID', None)
    561             if idCheckers.IsCharacter(itemID):
    562                 continue
    563             nonCharNodes.append(eachNode)
    564 
    565         if not nonCharNodes or not self.charid or not idCheckers.IsCharacter(self.charid):
    566             return
    567         from eve.client.script.ui.station.pvptrade.tradeUtil import TryInitiateTrade
    568         TryInitiateTrade(self.charid, nonCharNodes)
    569 
    570     def _IsDisplayed(self, container):
    571         return container is not None and not container.destroyed and container.display
    572 
    573     def _OnResize(self, *args):
    574         super(User, self)._OnResize(args)
    575         self.UpdateComponentSizes()
    576 
    577     def UpdateComponentSizes(self):
    578         occupiedWidth = BUDDY_CONTAINER_PAD_LEFT
    579         if self._IsDisplayed(self.onlineStatusIcon):
    580             occupiedWidth += STATUS_ICON_SIZE + STATUS_ICON_PAD_LEFT
    581         if self._IsDisplayed(self.flagCont):
    582             occupiedWidth += self.flagCont.width + 1
    583         spaceAvailableForNameLabel = self.width - self.sr.namelabel.left - occupiedWidth
    584         self.sr.namelabel.SetRightAlphaFade(fadeEnd=spaceAvailableForNameLabel, maxFadeWidth=3)
    585 
    586     def LoadTooltipPanel(self, panel, *args):
    587         char_id = GetAttrs(self, 'sr', 'node', 'charID')
    588         if char_id is None:
    589             return
    590         from character.client.identifier import CharacterIdentifier
    591         from cosmetics.client.character.title.api.repository import get_repository
    592         equipped_state = get_repository().get_equipped_title(CharacterIdentifier(char_id))
    593         if equipped_state is None or equipped_state.equipped_title is None:
    594             return
    595         from achievement_system.client.ui.chip.character_title import CharacterTitleChip
    596         title_chip = CharacterTitleChip(name='character_title_chip', character_id=char_id)
    597         panel.LoadStandardSpacing()
    598         panel.AddCell(title_chip, cellPadding=(-4, -4, -4, -4))
    599 
    600 
    601 class UserSimple(User):
    602     __guid__ = 'listentry.UserSimple'
    603     ENTRYHEIGHT = 18
    604 
    605     def Startup(self, *args):
    606         User.Startup(self, *args)
    607         self.iconCont = Container(parent=self, align=uiconst.TOLEFT, width=16)
    608 
    609     def Load(self, node, *args):
    610         User.Load(self, node, *args)
    611         self.sr.namelabel.left = 16
    612         self.sr.label = self.sr.namelabel
    613         self.sr.label.maxLines = 1
    614 
    615     def SetRelationship(self, data, *args, **kwargs):
    616         if self.destroyed:
    617             return
    618         if not data:
    619             return
    620         AddAndSetFlagIconFromData(parentCont=self.iconCont, data=data, top=4, left=4)
    621 
    622     def LoadPortrait(self, orderIfMissing = True):
    623         pass
    624 
    625     def GetDynamicHeight(node, width):
    626         node.height = UserSimple.ENTRYHEIGHT
    627         return node.height
    628 
    629 
    630 class AgentEntry(SE_BaseClassCore):
    631     __guid__ = 'listentry.AgentEntry'
    632     default_name = 'AgentEntry'
    633     isDragObject = True
    634 
    635     def Startup(self, *args):
    636         self.divisionName = ''
    637         self.agentName = ''
    638         self.levelName = ''
    639         self.agentType = ''
    640         self.agentLocation = ''
    641         self.missionState = ''
    642         self.locationLabel = None
    643         self.agentChatBtn = None
    644         self.timer = None
    645         self._timerUpdatesThread = None
    646         picCont = Container(parent=self, pos=(1, 0, 50, 0), name='pictureContainer', state=uiconst.UI_PICKCHILDREN, align=uiconst.TOLEFT)
    647         self.sr.pic = Sprite(parent=picCont, name='picture', align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN)
    648         textCont = Container(parent=self, name='textCont', state=uiconst.UI_PICKCHILDREN, align=uiconst.TOALL, padLeft=6, padTop=2, padRight=6)
    649         self.textCont = Container(parent=textCont, name='text', state=uiconst.UI_PICKCHILDREN, align=uiconst.TOALL, clipChildren=True, padTop=2)
    650         self.sr.namelabel = EveLabelMedium(name='nameLabel', text='', parent=self.textCont)
    651         self.sr.timerLabel = EveLabelMedium(name='timerLabel', text='', align=uiconst.TOPLEFT, parent=self.textCont, top=14)
    652         self.sr.levelLabel = EveLabelMedium(name='levelLabel', text='', align=uiconst.TOPLEFT, parent=self.textCont, top=14)
    653         self.sr.missionLabel = EveLabelMedium(name='missionLabel', text='', parent=self.textCont, align=uiconst.TOPRIGHT)
    654         self.agentChatBtn = AgentConversationIcon(name='agentChatBtn', parent=self, align=uiconst.BOTTOMRIGHT, left=0, top=0)
    655         buttonIconOnMouseEnter = self.agentChatBtn.OnMouseEnter
    656         self.agentChatBtn.OnMouseEnter = (self.OnChatButtonMouseEnter, self.agentChatBtn, buttonIconOnMouseEnter)
    657 
    658     def PreLoad(node):
    659         data = node
    660         charinfo = data.Get('info', None) or cfg.eveowners.Get(data.charID)
    661         data.info = charinfo
    662         data.itemID = data.charID
    663         data.invtype = data.info.typeID
    664 
    665     def GetAgentInfo(self, data):
    666         charID = data.charID
    667         self.UpdateMissionStateText(charID)
    668         agentInfo = sm.GetService('agents').GetAgentByID(charID)
    669         self.agentName = GetAgentNameAndLevel(charID, agentInfo.level)
    670         if agentInfo:
    671             self.agentChatBtn.SetAgentID(charID)
    672             agentDivision = get_division_name(agentInfo.divisionID).replace('&', '&amp;')
    673             if charID in const.rookieAgentList:
    674                 self.agentType = localization.GetByLabel('UI/AgentFinder/TutorialAgentDivision', divisionName=agentDivision)
    675             elif agentInfo.agentTypeID == const.agentTypeEpicArcAgent:
    676                 self.agentType = localization.GetByLabel('UI/AgentFinder/EpicArcAgentDivision', divisionName=agentDivision)
    677             elif agentInfo.agentTypeID in (const.agentTypeGenericStorylineMissionAgent, const.agentTypeStorylineMissionAgent):
    678                 self.agentType = localization.GetByLabel('UI/AgentFinder/StorylineAgentDivision', divisionName=agentDivision)
    679             elif agentInfo.agentTypeID == const.agentTypeEventMissionAgent:
    680                 self.agentType = localization.GetByLabel('UI/AgentFinder/EventAgentDivision', divisionName=agentDivision)
    681             elif agentInfo.agentTypeID == const.agentTypeCareerAgent:
    682                 self.agentType = localization.GetByLabel('UI/AgentFinder/CareerAgentDivision', divisionName=agentDivision)
    683             elif agentInfo.agentTypeID == const.agentTypeHeraldry:
    684                 self.agentType = localization.GetByLabel('UI/AgentFinder/HeraldryAgent')
    685             elif agentInfo.agentTypeID == const.agentTypeAura:
    686                 self.agentType = ''
    687             else:
    688                 self.agentType = agentDivision
    689             if agentInfo.stationID and session.stationid != agentInfo.stationID:
    690                 agentInfo = sm.GetService('agents').GetAgentByID(data.charID)
    691                 solarSystemID = sm.GetService('contracts').GetSolarSystemIDForStationOrStructure(agentInfo.stationID)
    692                 pathfinder = sm.GetService('clientPathfinderService')
    693                 numJumps = pathfinder.GetAutopilotJumpCount(session.solarsystemid2, solarSystemID)
    694                 self.agentLocation = GetAgentLocationText(solarSystemID)
    695         else:
    696             self.agentChatBtn.display = False
    697 
    698     def UpdateMissionStateText(self, charID):
    699         activeMission = sm.GetService('journal').GetActiveMissionForAgent(charID)
    700         if activeMission:
    701             self.missionState = activeMission.GetMissionStateTextColored()
    702 
    703     def Load(self, node):
    704         self.sr.node = node
    705         data = node
    706         if self.sr.node.Get('selected', 0):
    707             self.Select()
    708         else:
    709             self.Deselect()
    710         self.timer = getattr(data, 'timer', None)
    711         self.GetAgentInfo(data)
    712         extraTop = 0
    713         if self.agentLocation != '':
    714             if not self.locationLabel:
    715                 self.locationLabel = EveLabelMedium(name='locationLabel', parent=self.textCont, text=self.agentLocation, top=14)
    716             extraTop = 14
    717         self.name = data.info.name
    718         self.sr.namelabel.text = self.agentName
    719         self.sr.missionLabel.text = self.missionState
    720         if self.timer:
    721             extraTop += 14
    722             self._StartTimerUpdates()
    723             self.sr.timerLabel.top = extraTop
    724         else:
    725             self._StopTimerUpdates()
    726             self.sr.timerLabel.top = extraTop
    727         extraTop += 14
    728         if self.levelName == '':
    729             levelText = self.agentType
    730         else:
    731             levelText = self.levelName
    732         self.sr.levelLabel.text = levelText
    733         self.sr.levelLabel.top = extraTop
    734         self.charID = data.charID
    735         sm.GetService('photo').GetPortrait(self.charID, 64, self.sr.pic)
    736 
    737     def _StartTimerUpdates(self):
    738         self._UpdateTimer()
    739         if not self._timerUpdatesThread:
    740             self._timerUpdatesThread = AutoTimer(interval=500, method=self._UpdateTimer)
    741 
    742     def _GetTimerText(self, timeLeft):
    743         if timeLeft >= 12 * HOUR:
    744             showTo = 'hour'
    745         elif timeLeft >= HOUR:
    746             showTo = 'minute'
    747         elif timeLeft >= 10 * MIN:
    748             showTo = 'minute'
    749         else:
    750             showTo = 'second'
    751         formatter = localization.formatters.FormatTimeIntervalShortWritten
    752         formattedTime = formatter(timeLeft, showFrom='hour', showTo=showTo)
    753         text = localization.GetByLabel('UI/Agents/AgentEntry/Timer', timeLeft=formattedTime, color=SUCCESS_GREEN_HEX)
    754         return text
    755 
    756     def _UpdateTimer(self):
    757         if self.destroyed or not self.sr.timerLabel or not self.timer:
    758             self._StopTimerUpdates()
    759             return
    760         now = GetWallclockTimeNow()
    761         timeLeft = max(self.timer - now, 0)
    762         text = self._GetTimerText(timeLeft)
    763         self.sr.timerLabel.SetText(text)
    764         if timeLeft == 0:
    765             self._StopTimerUpdates()
    766 
    767     def _StopTimerUpdates(self):
    768         if self._timerUpdatesThread:
    769             self._timerUpdatesThread.KillTimer()
    770             if not self.destroyed and self.sr.timerLabel:
    771                 self.sr.timerLabel.text = ''
    772                 self.sr.levelLabel.top = self.sr.timerLabel.top
    773 
    774     def GetMenu(self):
    775         if self.destroyed:
    776             return
    777         m = []
    778         selected = self.sr.node.scroll.GetSelectedNodes(self.sr.node)
    779         multi = len(selected) > 1
    780         if multi:
    781             return self._GetMultiMenu(selected)
    782         m = GetMenuService().GetMenuFromItemIDTypeID(self.charID, self.sr.node.invtype)
    783         if self.sr.node.Get('GetMenu', None) is not None:
    784             m += self.sr.node.GetMenu(self.sr.node)
    785         listGroupID = self.sr.node.Get('listGroupID', None)
    786         if listGroupID is not None:
    787             group = uicore.registry.GetListGroup(listGroupID)
    788             if group:
    789                 if not listGroupID == ('agentgroups', 'all'):
    790                     m.append(None)
    791                     m.append((MenuLabel('UI/Common/RemoveFromGroup', {'groupname': group['label']}), self.RemoveFromListGroup, ([(listGroupID, self.charID)], [], '')))
    792         if self.sr.node.Get('MenuFunction', None):
    793             cm = [None]
    794             cm += self.sr.node.MenuFunction([self.sr.node])
    795             m += cm
    796         return m
    797 
    798     def _GetMultiMenu(self, selected):
    799         m = []
    800         charIDs = []
    801         multiCharIDs = []
    802         listGroupIDs = {}
    803         listGroupID_charIDs = []
    804         onlyCharacters = True
    805         for entry in selected:
    806             listGroupID = entry.listGroupID
    807             if listGroupID:
    808                 listGroupIDs[listGroupID] = 0
    809                 listGroupID_charIDs.append((listGroupID, entry.charID))
    810             if entry.charID:
    811                 charIDs.append(entry.charID)
    812                 multiCharIDs.append(entry.charID)
    813                 if not idCheckers.IsCharacter(entry.charID):
    814                     onlyCharacters = False
    815 
    816         if onlyCharacters:
    817             m += GetMenuService().CharacterMenu(charIDs)
    818         if listGroupIDs:
    819             listname = ''
    820             delCharIDs = []
    821             rem = []
    822             for listGroupID, charID in listGroupID_charIDs:
    823                 if listGroupID == ('agentgroups', 'all'):
    824                     if onlyCharacters:
    825                         return m
    826                     group = uicore.registry.GetListGroup(listGroupID)
    827                     listname = [localization.GetByLabel('UI/Agents/AgentList'), localization.GetByLabel('UI/Generic/BuddyList')][listGroupID == ('buddygroups', 'all')]
    828                     delCharIDs.append(charID)
    829                     rem.append((listGroupID, charID))
    830 
    831             for each in rem:
    832                 listGroupID_charIDs.remove(each)
    833 
    834             foldername = 'folders'
    835             if len(listGroupIDs) == 1:
    836                 group = uicore.registry.GetListGroup(listGroupIDs.keys()[0])
    837                 if group:
    838                     foldername = group['label']
    839             label = ''
    840             if delCharIDs and listname:
    841                 label = localization.GetByLabel('UI/PeopleAndPlaces/RemoveMultipleFromAddressbook', removecount=len(delCharIDs))
    842                 if listGroupID_charIDs:
    843                     label += [', ']
    844             if listGroupID_charIDs:
    845                 label += localization.GetByLabel('UI/PeopleAndPlaces/RemoveFromFolder', foldername=foldername, removecount=len(listGroupID_charIDs))
    846             m.append((label, self.RemoveFromListGroup, (listGroupID_charIDs, delCharIDs, listname)))
    847         if self.sr.node.Get('MenuFunction', None):
    848             cm = [None]
    849             cm += self.sr.node.MenuFunction(selected)
    850             m += cm
    851         return m
    852 
    853     def ShowInfo(self, *args):
    854         if self.destroyed:
    855             return
    856         sm.GetService('info').ShowInfo(cfg.eveowners.Get(self.charID).typeID, self.charID)
    857 
    858     def GetDragData(self, *args):
    859         if self and not self.destroyed:
    860             return self.sr.node.scroll.GetSelectedNodes(self.sr.node)
    861         else:
    862             return []
    863 
    864     def OnClick(self, *args):
    865         if self.destroyed:
    866             return
    867         prompt_player_fsd_dialog('ListEntryClick')
    868         self.sr.node.scroll.SelectNode(self.sr.node)
    869         if self.sr.node.Get('OnClick', None):
    870             self.sr.node.OnClick(self)
    871 
    872     @telemetry.ZONE_METHOD
    873     def OnDblClick(self, *args):
    874         if self.destroyed:
    875             return
    876         if self.sr.node.Get('OnDblClick', None):
    877             self.sr.node.OnDblClick(self)
    878             return
    879         agentInfo = sm.GetService('agents').GetAgentByID(self.charID)
    880         if session.stationid and agentInfo:
    881             sm.GetService('agents').OpenDialogueWindow(self.charID)
    882             return
    883         self.ShowInfo()
    884 
    885     def GetHeight(self, *args):
    886         node, width = args
    887         node.height = 51
    888         return node.height
    889 
    890     def RemoveFromListGroup(self, listGroupIDs, charIDs, listname):
    891         if self.destroyed:
    892             return
    893         if listGroupIDs:
    894             for listGroupID, charID in listGroupIDs:
    895                 uicore.registry.RemoveFromListGroup(listGroupID, charID)
    896 
    897             sm.GetService('addressbook').RefreshWindow()
    898         if charIDs and listname:
    899             name = [localization.GetByLabel('UI/AddressBook/RemoveAddressBook1'), cfg.eveowners.Get(charIDs[0]).name][len(charIDs) == 1]
    900             if prompt_player_fsd_dialog('WarnDeleteFromAddressbook', {'name': name,
    901              'type': listname}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES:
    902                 return
    903             sm.GetService('addressbook').DeleteEntryMulti(charIDs, None)
    904 
    905     def OnChatButtonMouseEnter(self, btn, buttonIconOnMouseEnter, *args):
    906         self.OnMouseEnter()
    907         buttonIconOnMouseEnter()
    908 
    909     def UpdateAlignment(self, *args, **kwargs):
    910         alignment = super(AgentEntry, self).UpdateAlignment(*args, **kwargs)
    911         self.UpdateLabelRightFade()
    912         return alignment
    913 
    914     def UpdateLabelRightFade(self):
    915         availableWidth = ReverseScaleDpi(self.displayWidth) - 64 - ReverseScaleDpi(self.sr.missionLabel.displayWidth)
    916         self.sr.namelabel.SetRightAlphaFade(fadeEnd=availableWidth, maxFadeWidth=20)
    917         if self.agentChatBtn and self.agentChatBtn.display:
    918             availableWidth = ReverseScaleDpi(self.displayWidth) - 64 - self.agentChatBtn.width
    919             self.sr.levelLabel.SetRightAlphaFade(fadeEnd=availableWidth, maxFadeWidth=20)
    920         else:
    921             self.sr.levelLabel.SetRightAlphaFade(fadeEnd=0, maxFadeWidth=0)