member_list_entry.py (16022B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\chat\client\member_list_entry.py 2 import sys 3 import carbonui.const as uiconst 4 from carbon.common.script.util.commonutils import GetAttrs 5 from carbonui.control.scrollentries import SE_BaseClassCore 6 from carbonui.primitives.container import Container 7 from carbonui.primitives.sprite import Sprite 8 from eve.client.script.ui.control import eveIcon 9 from eve.client.script.ui.control.eveLabel import EveLabelMedium 10 from eve.client.script.ui.control.eveWindowUnderlay import ListEntryUnderlay 11 from eve.client.script.ui.shared.neocom.addressBook.contactsConst import COLOR_ONLINE, COLOR_OFFLINE 12 from eve.client.script.ui.shared.stateFlag import GetStateFlagFromData, AddAndSetFlagIcon, AddAndSetFlagIconFromData 13 from eve.client.script.ui.util.linkUtil import GetCharIDFromTextLink 14 from eve.common.script.sys import idCheckers 15 from eveservices.menu import GetMenuService 16 import evetypes 17 from menu import MenuLabel 18 import localization 19 import telemetry 20 import uthread 21 import log 22 from carbonui.uicore import uicore 23 import threadutils 24 from player_messaging.client.ui_message import prompt_player_fsd_dialog 25 ICON_ONLINESTATUS = 'res:/UI/Texture/Classes/Contacts/onlineIcon.png' 26 ICON_BLOCKED = 'res:/UI/Texture/Classes/Contacts/blockedWhite.png' 27 BUDDY_CONTAINER_PAD_LEFT = 3 28 BUDDY_CONTAINER_PAD_TOP = 1 29 STATUS_ICON_SIZE = 12 30 STATUS_ICON_PAD_LEFT = 2 31 32 class ChatMemberListEntryUnderlay(ListEntryUnderlay): 33 default_padBottom = 0 34 35 36 class ChatMemberListEntry(SE_BaseClassCore): 37 __guid__ = 'listentry.ChatMemberListEntry' 38 __notifyevents__ = ['OnContactLoggedOn', 39 'OnContactLoggedOff', 40 'OnClientContactChange', 41 'OnContactNoLongerContact', 42 'OnStateSetupChange', 43 'OnMyFleetInited', 44 'OnFleetJoin_Local', 45 'OnFleetLeave_Local', 46 'ProcessOnUIAllianceRelationshipChanged', 47 'OnContactChange', 48 'OnBlockContacts', 49 'OnUnblockContacts', 50 'OnCrimewatchEngagementUpdated', 51 'OnSuspectsAndCriminalsUpdate', 52 'OnDisapprovalUpdate'] 53 default_highlightClass = ChatMemberListEntryUnderlay 54 isDragObject = True 55 charid = None 56 selected = 0 57 id = None 58 groupID = None 59 picloaded = 0 60 label = None 61 62 def Startup(self, scroll, *args, **kwargs): 63 self._CreatePicture() 64 self._CreateName() 65 self.flagCont = None 66 self._CreateBuddyContainer() 67 self._CreateBottomRightContainer() 68 sm.RegisterNotify(self) 69 70 def _CreatePicture(self): 71 self.sr.picture = Container(name='picture', parent=self, pos=(0, 0, 32, 32), state=uiconst.UI_DISABLED, align=uiconst.RELATIVE) 72 73 @telemetry.ZONE_METHOD 74 def _CreateName(self): 75 self.sr.namelabel = EveLabelMedium(name='nameLabel', parent=self, state=uiconst.UI_DISABLED, text='', idx=0) 76 77 def _CreateBuddyContainer(self): 78 self.sr.statusIconContainer = Container(name='statusIconContainer', parent=self, align=uiconst.TOPRIGHT, width=16, height=16, idx=0) 79 self.sr.statusIcon = Sprite(name='statusIcon', parent=self.sr.statusIconContainer, align=uiconst.CENTER, width=STATUS_ICON_SIZE, height=STATUS_ICON_SIZE, texturePath=ICON_ONLINESTATUS) 80 81 def _CreateBottomRightContainer(self): 82 self.bottomRightCont = Container(name='bottomRightCont', parent=self, align=uiconst.BOTTOMRIGHT, width=16, height=16, idx=0) 83 84 def PreLoad(node): 85 data = node 86 charinfo = data.Get('info', None) or cfg.eveowners.Get(data.charID) 87 data.info = charinfo 88 if data.GetLabel: 89 data.label = data.GetLabel(data) 90 elif not data.Get('label', None): 91 label = charinfo.name 92 data.label = label 93 groupID = evetypes.GetGroupID(data.info.typeID) 94 data.invtype = data.info.typeID 95 data.IsCharacter = groupID == const.groupCharacter 96 97 def Load(self, node): 98 self.sr.node = node 99 data = node 100 self.channelID = data.Get('channelID', None) 101 self.name = data.info.name 102 self.sr.namelabel.text = data.label 103 self.charid = self.id = data.itemID = data.charID 104 self.picloaded = 0 105 self.confirmOnDblClick = data.Get('dblconfirm', 1) 106 self.data = {'Name': self.name, 107 'charid': data.charID} 108 self.inWatchlist = sm.GetService('addressbook').IsInWatchlist(data.charID) 109 self.sr.namelabel.SetAlign(uiconst.CENTERLEFT) 110 self.sr.namelabel.left = 40 111 self.LoadPortrait() 112 uthread.new(self.SetRelationship, data) 113 self.sr.statusIconContainer.display = False 114 if data.charID != session.charid and self.inWatchlist: 115 try: 116 self.SetOnline(sm.GetService('onlineStatus').GetOnlineStatus(data.charID, fetch=False)) 117 except IndexError: 118 sys.exc_clear() 119 120 if self.sr.node.Get('selected', 0): 121 self.Select() 122 else: 123 self.Deselect() 124 self.SetBlocked(1) 125 if uicore.uilib.mouseOver is self: 126 self.Select() 127 128 def GetDynamicHeight(node, width): 129 node.height = 32 130 return node.height 131 132 def OnContactLoggedOn(self, charID): 133 self._OnContactsOnlineStateChanged(charID, isOnline=1) 134 135 def OnContactLoggedOff(self, charID): 136 self._OnContactsOnlineStateChanged(charID, isOnline=0) 137 138 def _OnContactsOnlineStateChanged(self, charID, isOnline): 139 if self._IsMeOrDestroyedEntry(charID): 140 return 141 if self.IsThisCharacter(charID): 142 self.SetOnline(isOnline) 143 144 def OnClientContactChange(self, charID, online): 145 self._OnContactsOnlineStateChanged(charID, isOnline=bool(online)) 146 147 def OnContactNoLongerContact(self, charID): 148 self._OnContactsOnlineStateChanged(charID, isOnline=None) 149 150 def IsThisCharacter(self, charID): 151 return charID == self.charid 152 153 def _IsMeOrDestroyedEntry(self, charID): 154 if not self: 155 return True 156 if self.destroyed: 157 return True 158 if charID == session.charid: 159 return True 160 if idCheckers.IsNPC(charID): 161 return True 162 return False 163 164 def OnPortraitCreated(self, charID, _size): 165 if self.destroyed: 166 return 167 if self.sr.node and charID == self.sr.node.charID and not self.picloaded: 168 self.LoadPortrait(orderIfMissing=False) 169 170 def OnContactChange(self, contactIDs, contactType = None): 171 if self.destroyed: 172 return 173 self.SetRelationship(self.sr.node) 174 if self.charid in contactIDs: 175 addressbookSvc = sm.GetService('addressbook') 176 self.inWatchlist = addressbookSvc.IsInWatchlist(self.charid) 177 if self.inWatchlist: 178 self.sr.statusIconContainer.display = True 179 else: 180 isBlocked = addressbookSvc.IsBlocked(self.charid) 181 if isBlocked: 182 self.sr.statusIconContainer.display = True 183 self.sr.statusIcon.SetRGB(1.0, 1.0, 1.0) 184 else: 185 self.sr.statusIconContainer.display = False 186 187 def OnBlockContacts(self, contactIDs): 188 if not self or self.destroyed: 189 return 190 if self.charid in contactIDs: 191 self.SetBlocked(1) 192 193 def OnUnblockContacts(self, contactIDs): 194 if not self or self.destroyed: 195 return 196 if self.charid in contactIDs: 197 self.SetBlocked(0) 198 199 def OnStateSetupChange(self, what): 200 if self.destroyed: 201 return 202 self.SetRelationship(self.sr.node) 203 204 def ProcessOnUIAllianceRelationshipChanged(self, *args): 205 if self.destroyed: 206 return 207 self.SetRelationship(self.sr.node) 208 209 def OnFleetJoin_Local(self, memberInfo): 210 thisCharID = self.GetCharIdFromNode() 211 if thisCharID is None: 212 return 213 charID = memberInfo.charID 214 if charID == thisCharID: 215 uthread.new(self.SetRelationship, self.sr.node) 216 217 def OnFleetLeave_Local(self, memberInfo): 218 thisCharID = self.GetCharIdFromNode() 219 if thisCharID is None: 220 return 221 charID = memberInfo.charID 222 if charID == thisCharID or charID == session.charid: 223 uthread.new(self.SetRelationship, self.sr.node) 224 225 def OnMyFleetInited(self): 226 if self.destroyed: 227 return 228 uthread.new(self.SetRelationship, self.sr.node) 229 230 def SetOnline(self, online): 231 if self.destroyed: 232 return 233 if online is None or not self.inWatchlist: 234 if not sm.GetService('addressbook').IsBlocked(self.charid): 235 self.sr.statusIconContainer.display = False 236 else: 237 color = COLOR_ONLINE if online else COLOR_OFFLINE 238 self.sr.statusIcon.SetRGBA(*color) 239 if online: 240 hint = localization.GetByLabel('UI/Common/Online') 241 else: 242 hint = localization.GetByLabel('UI/Common/Offline') 243 self.sr.statusIcon.hint = hint 244 self.sr.statusIconContainer.display = True 245 246 def SetBlocked(self, blocked): 247 isBlocked = sm.GetService('addressbook').IsBlocked(self.charid) 248 if blocked and isBlocked: 249 self.sr.statusIcon.SetTexturePath(ICON_BLOCKED) 250 self.sr.statusIconContainer.display = True 251 elif self.inWatchlist: 252 self.sr.statusIcon.SetTexturePath(ICON_ONLINESTATUS) 253 else: 254 self.sr.statusIconContainer.display = False 255 256 def SetRelationship(self, data): 257 if self.destroyed or not data: 258 return 259 flag = GetStateFlagFromData(data) 260 if self.flagCont is None: 261 self.flagCont = self.bottomRightCont 262 AddAndSetFlagIcon(self.flagCont, align=uiconst.CENTER, flag=flag, width=12, height=12) 263 self.flagCont.display = bool(flag is not None) 264 265 def LoadPortrait(self, orderIfMissing = True): 266 self.sr.picture.Flush() 267 if self.sr.node is None: 268 return 269 if eveIcon.GetOwnerLogo(self.sr.picture, self.id, size=32, callback=True, orderIfMissing=orderIfMissing): 270 self.picloaded = 1 271 272 def GetMenu(self): 273 if self.destroyed: 274 return 275 else: 276 selected = self.sr.node.scroll.GetSelectedNodes(self.sr.node) 277 if len(selected) < 2: 278 m = GetMenuService().GetMenuFromItemIDTypeID(self.id, self.sr.node.invtype, channelID=self.channelID) 279 if self.charid != session.charid: 280 m += [None, (MenuLabel('UI/Chat/ReportIskSpammer'), self._ReportISKSpammer)] 281 return m 282 charIDs = [] 283 for entry in selected: 284 if entry.charID: 285 charIDs.append(entry.charID) 286 287 return GetMenuService().CharacterMenu(charIDs) 288 289 def _ReportISKSpammer(self, *args): 290 sm.GetService('chat').report_isk_spammer(self.charid, self.channelID) 291 292 def ShowInfo(self, *args): 293 if self.destroyed: 294 return 295 sm.GetService('info').ShowInfo(cfg.eveowners.Get(self.charid).typeID, self.charid) 296 297 def OnClick(self, *args): 298 if self.destroyed: 299 return 300 prompt_player_fsd_dialog('ListEntryClick') 301 self.sr.node.scroll.SelectNode(self.sr.node) 302 if self.sr.node.Get('OnClick', None): 303 self.sr.node.OnClick(self) 304 305 def OnDblClick(self, *args): 306 if self.destroyed: 307 return 308 if self.sr.node.Get('OnDblClick', None): 309 self.sr.node.OnDblClick(self) 310 return 311 onDblClick = settings.user.ui.Get('dblClickUser', 0) 312 if onDblClick == 0: 313 sm.GetService('info').ShowInfo(cfg.eveowners.Get(self.charid).typeID, self.charid) 314 elif onDblClick == 1: 315 sm.GetService('chat').invite_to_private_chat(self.charid) 316 317 def GetDragData(self, *args): 318 if self and not self.destroyed: 319 return self.sr.node.scroll.GetSelectedNodes(self.sr.node) 320 else: 321 return [] 322 323 def OnSuspectsAndCriminalsUpdate(self, criminalizedCharIDs, decriminalizedCharIDs): 324 return self._UpdateOnSuspectCriminalAndDisapprovalChanges(criminalizedCharIDs, decriminalizedCharIDs) 325 326 def OnDisapprovalUpdate(self, newCharIDs, removedCharIDs): 327 return self._UpdateOnSuspectCriminalAndDisapprovalChanges(newCharIDs, removedCharIDs) 328 329 def _UpdateOnSuspectCriminalAndDisapprovalChanges(self, newCharIDs, removedCharIDs): 330 charID = self.GetCharIdFromNode() 331 if charID is None: 332 return 333 if charID in newCharIDs or charID in removedCharIDs: 334 uthread.new(self.SetRelationship, self.sr.node) 335 336 def OnCrimewatchEngagementUpdated(self, otherCharId, timeout): 337 charID = self.GetCharIdFromNode() 338 if charID is None: 339 return 340 if charID == otherCharId: 341 uthread.new(self.SetRelationship, self.sr.node) 342 343 @threadutils.throttled(0.2) 344 def UpdateRelationship(self, characterID): 345 if self.destroyed: 346 return 347 charID = self.GetCharIdFromNode() 348 if charID is None or charID != characterID: 349 return 350 currentData = self.sr.node 351 self.SetRelationship(currentData) 352 353 def GetCharIdFromNode(self): 354 if self.destroyed: 355 return None 356 charID = GetAttrs(self, 'sr', 'node', 'charID') 357 return charID 358 359 def OnDropData(self, dragObj, nodes): 360 nonCharNodes = [] 361 for eachNode in nodes: 362 itemID = getattr(eachNode, 'itemID', None) 363 if not itemID: 364 itemID = GetCharIDFromTextLink(eachNode) 365 if idCheckers.IsCharacter(itemID): 366 continue 367 nonCharNodes.append(eachNode) 368 369 if nonCharNodes and self.charid: 370 from eve.client.script.ui.station.pvptrade.tradeUtil import TryInitiateTrade 371 TryInitiateTrade(self.charid, nonCharNodes) 372 self.sr.node.scroll.GetContentContainer().OnDropData(dragObj, nodes) 373 374 def _IsDisplayed(self, container): 375 return container is not None and not container.destroyed and container.display 376 377 def _OnResize(self, *args): 378 self.UpdateComponentSizes() 379 380 def UpdateComponentSizes(self): 381 occupiedWidth = 4 382 if self._IsDisplayed(self.sr.statusIconContainer): 383 occupiedWidth += STATUS_ICON_SIZE + STATUS_ICON_PAD_LEFT 384 if self._IsDisplayed(self.flagCont): 385 occupiedWidth = max(occupiedWidth, self.flagCont.width + 5) 386 spaceAvailableForNameLabel = self.width - self.sr.namelabel.left - occupiedWidth 387 self.sr.namelabel.SetRightAlphaFade(fadeEnd=spaceAvailableForNameLabel, maxFadeWidth=3) 388 389 @classmethod 390 def GetCopyData(cls, node): 391 return node.label 392 393 394 class ChatMemberCompactListEntry(ChatMemberListEntry): 395 __guid__ = 'listentry.ChatMemberCompactListEntry' 396 ENTRYHEIGHT = 16 397 398 def Startup(self, *args): 399 ChatMemberListEntry.Startup(self, *args) 400 self.iconCont = Container(name='iconCont', parent=self, align=uiconst.TOLEFT, width=12) 401 402 def _CreatePicture(self): 403 pass 404 405 def _CreateBottomRightContainer(self): 406 pass 407 408 def Load(self, node): 409 ChatMemberListEntry.Load(self, node) 410 self.sr.namelabel.left = 16 411 412 def SetRelationship(self, data): 413 if self.destroyed or not data: 414 return 415 AddAndSetFlagIconFromData(parentCont=self.iconCont, data=data, align=uiconst.CENTER, top=-1, left=0, width=12, height=12) 416 417 def LoadPortrait(self, orderIfMissing = True): 418 pass 419 420 def _CreateBuddyContainer(self): 421 self.sr.statusIconContainer = Container(name='statusIconContainer', parent=self, align=uiconst.TORIGHT, width=16, height=16) 422 self.sr.statusIcon = Sprite(name='statusIcon', parent=self.sr.statusIconContainer, align=uiconst.CENTERRIGHT, width=STATUS_ICON_SIZE, height=STATUS_ICON_SIZE, texturePath=ICON_ONLINESTATUS)