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

message_list_entry.py (12478B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\chat\client\message_list_entry.py
      2 import logging
      3 import re
      4 import sys
      5 import types
      6 import blue
      7 import evelink.client
      8 import evetypes
      9 import eveicon
     10 from carbonui import Align, TextColor
     11 import carbonui.const as uiconst
     12 from carbonui.primitives.fill import Fill
     13 from chatutil.filter import CleanText
     14 from eve.client.script.ui import eveColor
     15 from menu import MenuLabel
     16 from carbon.common.script.util.commonutils import StripTags
     17 from carbon.common.script.util.format import GetTimeParts
     18 from carbonui.control.scrollentries import SE_BaseClassCore
     19 from carbonui.control.link import Link
     20 from carbonui.primitives.container import Container
     21 from carbonui.primitives.gradientSprite import Gradient2DSprite
     22 from carbonui.control.buttonIcon import ButtonIcon
     23 from chatutil import LinkURLs
     24 from eve.client.script.ui.control.eveIcon import Icon
     25 from eve.client.script.ui.control.eveLabel import Label
     26 from eve.common.script.sys.idCheckers import IsCharacter
     27 from eve.common.lib import appConst as const
     28 from eveservices.menu import StartMenuService, GetMenuService
     29 from chat.client.const import SLASH_EMOTE_STRING, ChatMessageMode
     30 logger = logging.getLogger(__name__)
     31 PICTURE_SIZE = {ChatMessageMode.TEXT_ONLY: 0,
     32  ChatMessageMode.SMALL_PORTRAIT: 32,
     33  ChatMessageMode.LARGE_PORTRAIT: 64}
     34 
     35 class ChatMessageListEntry(SE_BaseClassCore):
     36     default_clipChildren = True
     37     defaultTextProps = {'autoDetectCharset': True,
     38      'linkStyle': uiconst.LINKSTYLE_REGULAR,
     39      'state': uiconst.UI_NORMAL,
     40      'align': uiconst.TOTOP,
     41      'padRight': 4,
     42      'padLeft': 4}
     43 
     44     def ApplyAttributes(self, attributes):
     45         self._highlight_background = None
     46         super(ChatMessageListEntry, self).ApplyAttributes(attributes)
     47 
     48     def Startup(self, *args):
     49         self.text = None
     50         self.backgroundGradient = None
     51         self._expander = None
     52         self.picParent = Container(parent=self, align=uiconst.TOPLEFT, width=32, height=32)
     53         self.pic = Icon(parent=self.picParent, align=uiconst.TOALL)
     54 
     55     def Load(self, node):
     56         self.picloaded = 0
     57         textProps = self.GetTextProperties(node)
     58         if not self.text:
     59             mouseOverWordCallback = node.mouseOverWordCallback
     60             collectWordsInStack = node.collectWordsInStack
     61             self.text = Label(parent=self, idx=0, mouseOverWordCallback=mouseOverWordCallback, collectWordsInStack=collectWordsInStack, **textProps)
     62             self.text.GetMenu = self.GetMenu
     63         charname, text = ChatMessageListEntry.GetCharacterAndTextForNode(node)
     64         self.text.text = text
     65         if node.mode and charname:
     66             self.picParent.width = self.picParent.height = PICTURE_SIZE[node.mode]
     67             self.picParent.state = uiconst.UI_NORMAL
     68             self.LoadPortrait()
     69         else:
     70             self.picParent.state = uiconst.UI_HIDDEN
     71         self.AddEntryBackground(node.charid)
     72 
     73     @staticmethod
     74     def GetCharacterAndTextForNode(node):
     75         if node.sender is None or type(node.sender) in types.StringTypes:
     76             return (None, node.text)
     77         nodetext = LinkURLs(node.text)
     78         try:
     79             charname = cfg.eveowners.Get(node.sender).name
     80         except ValueError:
     81             charname = None
     82 
     83         if nodetext.startswith('/slash'):
     84             color = '0xb200dddd'
     85             nodetext = nodetext[1:]
     86             showCharname = False
     87         else:
     88             color = node.color
     89             showCharname = True
     90         isEmote = False
     91         if nodetext.startswith(SLASH_EMOTE_STRING):
     92             nodetext = nodetext.replace(SLASH_EMOTE_STRING, '', 1)
     93             isEmote = True
     94         timestr = ''
     95         if settings.user.ui.Get('timestampchat', 0):
     96             year, month, wd, day, hour, min, sec, ms = GetTimeParts(node.timestamp)
     97             timestr = '<color=%s>[%02d:%02d:%02d]</color> ' % (color,
     98              hour,
     99              min,
    100              sec)
    101         if charname:
    102             if node.sender == const.ownerSystem:
    103                 info = 'showinfo:5//%s' % (session.solarsystemid or session.solarsystemid2)
    104             else:
    105                 info = 'showinfo:1373//%d' % node.charid
    106             if showCharname:
    107                 linkStyle = uiconst.LINKSTYLE_UNBOLD
    108                 if isEmote:
    109                     prefix = '<url:%s linkStyle=%s><color=%s>* %s</color></url>' % (info,
    110                      linkStyle,
    111                      color,
    112                      charname)
    113                 else:
    114                     prefix = '<url:%s linkStyle=%s><color=%s>%s</color></url> &gt; ' % (info,
    115                      linkStyle,
    116                      color,
    117                      charname)
    118             else:
    119                 prefix = '<url:%s/></url>' % info
    120         else:
    121             prefix = '<color=%s>%s</color> &gt; ' % (color, node.sender)
    122         if node.undelivered:
    123             color = '0xb2505050'
    124             nodetext += ' - undelivered'
    125         text = '%s<color=%s>%s%s</color>' % (timestr,
    126          color,
    127          prefix,
    128          nodetext)
    129         return (charname, text)
    130 
    131     def LoadHighlightedText(self, text, *args):
    132         if self.sr.node.text.lower().find(text.lower()) >= 0:
    133             self.isHighlighted = True
    134             strippedText = StripTags(self.text.text)
    135             pattern = re.compile(text, re.IGNORECASE)
    136             textWithEffect = '<b><color=red>' + text + '</color></b>'
    137             newText = pattern.sub(textWithEffect, strippedText)
    138             self.text.text = newText
    139 
    140     def RemoveHighlightedText(self, *args):
    141         if getattr(self, 'isHighlighted', False):
    142             self.Load(self.sr.node)
    143 
    144     def LoadPortrait(self, orderIfMissing = True):
    145         if self is None or self.destroyed:
    146             return
    147         if self.sr.node.charid == const.ownerSystem:
    148             self.pic.LoadIcon('ui_6_64_7')
    149             return
    150         size = PICTURE_SIZE[self.sr.node.mode]
    151         if sm.GetService('photo').GetPortrait(self.sr.node.charid, size, self.pic, orderIfMissing, callback=True):
    152             self.picloaded = 1
    153 
    154     @staticmethod
    155     def GetDynamicHeight(node, width):
    156         props = ChatMessageListEntry.GetTextProperties(node)
    157         width = width - props['padLeft'] - props['padRight']
    158         maxLines = getattr(node, 'maxLines', None)
    159         _, text = ChatMessageListEntry.GetCharacterAndTextForNode(node)
    160         _, textHeight = Label.MeasureTextSize(text, width=width, **props)
    161         if maxLines:
    162             maxText = '\n'.join((str(x) for x in range(maxLines)))
    163             _, maxTextHeight = Label.MeasureTextSize(maxText, width=width, **props)
    164             if textHeight > maxTextHeight:
    165                 if maxLines == 2:
    166                     textHeight = maxTextHeight
    167                 else:
    168                     textHeight = Label.MeasureTextSize('\n.', width=width, **props)[1]
    169                 node.showExpander = True
    170             else:
    171                 node.showExpander = False
    172         if node.mode == ChatMessageMode.TEXT_ONLY:
    173             return textHeight
    174         elif node.mode == ChatMessageMode.SMALL_PORTRAIT:
    175             return max(PICTURE_SIZE[node.mode], textHeight + props['padTop'] * 2)
    176         else:
    177             return max(PICTURE_SIZE[node.mode], textHeight + props['padTop'] * 2)
    178 
    179     def GetMenu(self):
    180         m = []
    181         mouseOverUrl = self.text.GetMouseOverUrl()
    182         characterMenuAdded = False
    183         if mouseOverUrl:
    184             try:
    185                 parsed = evelink.parse_show_info_url(mouseOverUrl)
    186             except Exception:
    187                 logger.debug('failed to parse show info link %s', mouseOverUrl, exc_info=True)
    188                 sys.exc_clear()
    189                 m = Link().GetLinkMenu(self.text, mouseOverUrl.replace('&amp;', '&'))
    190             else:
    191                 if parsed.item_id:
    192                     m = StartMenuService().GetMenuFromItemIDTypeID(parsed.item_id, parsed.type_id, includeMarketDetails=True)
    193                     if evetypes.GetGroupID(parsed.type_id) == const.groupCharacter:
    194                         characterMenuAdded = True
    195                         if not parsed.item_id != session.charid:
    196                             m += [None, (MenuLabel('UI/Chat/ReportIskSpammer'), lambda *args: sm.GetService('chat').report_isk_spammer(parsed.item_id, self.sr.node.channelid))]
    197                 else:
    198                     m = Link().GetLinkMenu(self.text, mouseOverUrl.replace('&amp;', '&'))
    199         m += [None, (MenuLabel('UI/Common/Copy'), self.CopyText)]
    200         if self.sr.node.channelMenu:
    201             m += self.sr.node.channelMenu()
    202         senderID = self.sr.node.sender
    203         if not characterMenuAdded and senderID and IsCharacter(senderID):
    204             m += [None, (MenuLabel('UI/Common/Pilot', {'character': senderID}), ('isDynamic', GetMenuService().CharacterMenu, (senderID,)))]
    205         return m
    206 
    207     def CopyText(self):
    208         node = self.sr.node
    209         timestr = ''
    210         if settings.user.ui.Get('timestampchat', 0):
    211             year, month, wd, day, hour, min, sec, ms = GetTimeParts(node.timestamp)
    212             timestr = '[%02d:%02d:%02d] ' % (hour, min, sec)
    213         who = node.sender
    214         try:
    215             who = cfg.eveowners.Get(who).name
    216         except ValueError:
    217             pass
    218 
    219         text = node.text.replace('&gt;', '>').replace('&amp;', '&')
    220         text = CleanText(text)
    221         t = '%s%s > %s\r\n' % (timestr, who, text)
    222         blue.pyos.SetClipboardData(t)
    223 
    224     def AddEntryBackground(self, charid):
    225         if charid == session.charid and settings.user.ui.Get('myMsgHighlighted', 0):
    226             if self._highlight_background is None:
    227                 self._highlight_background = Fill(bgParent=self, color=eveColor.CRYO_BLUE[:3] + (0.1,), idx=0)
    228 
    229     def AddBackgroundGradient(self, color):
    230         self.backgroundGradient = Gradient2DSprite(bgParent=self, idx=0, name='gradient2d', rgbHorizontal=[0, 1], rgbVertical=[0, 1], rgbDataHorizontal=[color, color], rgbDataVertical=[color, color], rgbInterp='bezier', alphaHorizontal=[0, 1], alphaDataHorizontal=[1.0, 0.2], alphaVertical=[0, 0.5, 1], alphaDataVertical=[0.35, 0.05, 0.0], textureSize=16)
    231         return self.backgroundGradient
    232 
    233     def AddFrameGradient(self, color):
    234         frameGradient = Gradient2DSprite(parent=None, idx=0, name='frameGradient2d', rgbHorizontal=[0, 1], rgbVertical=[0, 1], rgbDataHorizontal=[color, color], rgbDataVertical=[color, color], rgbInterp='linear', alphaHorizontal=[0, 0.5, 1], alphaDataHorizontal=[1.0, 0.8, 0.15], alphaVertical=[0, 0.5, 1], alphaDataVertical=[1.0, 0.8, 0.15], textureSize=16)
    235         self.picParent.background.insert(0, frameGradient)
    236 
    237     def _OnSizeChange_NoBlock(self, width, height):
    238         super(ChatMessageListEntry, self)._OnSizeChange_NoBlock(width, height)
    239         self._UpdateExpander()
    240 
    241     def _UpdateExpander(self):
    242         if self.sr.node.showExpander:
    243             if self._expander is None:
    244                 self._expander = ButtonIcon(parent=self, align=Align.TORIGHT, idx=0, texturePath=eveicon.expand, height=32, width=32, iconSize=16, iconColor=TextColor.SECONDARY, func=self._ExpandEntry)
    245         elif self._expander is not None:
    246             self._expander.Close()
    247             self._expander = None
    248 
    249     def _ExpandEntry(self, *args, **kwargs):
    250         if self.sr.node.maxLines:
    251             self.sr.node.maxLines = None
    252             self.sr.node.showExpander = False
    253             if self._expander is not None:
    254                 self._expander.Close()
    255                 self._expander = None
    256         self.sr.node.scroll.UpdateNodesWidthAndPosition(forceUpdate=True)
    257         self.sr.node.scroll.UpdatePosition()
    258 
    259     @classmethod
    260     def GetTextProperties(cls, node):
    261         textProps = cls.defaultTextProps
    262         textProps['fontsize'] = node.fontsize
    263         textProps['letterspace'] = node.letterspace
    264         if node.mode == ChatMessageMode.TEXT_ONLY:
    265             textProps['padTop'] = 0
    266             textProps['padLeft'] = 0
    267             textProps['specialIndent'] = 8
    268         else:
    269             textProps['padTop'] = 4
    270             textProps['specialIndent'] = 0
    271             portrait_size = PICTURE_SIZE[node.mode]
    272             if node.mode == ChatMessageMode.SMALL_PORTRAIT:
    273                 textProps['padLeft'] = portrait_size + 4
    274             else:
    275                 textProps['padLeft'] = portrait_size + 4
    276         return textProps
    277 
    278     @classmethod
    279     def MarkMessageUndelivered(cls, node):
    280         node.undelivered = True
    281         if node.panel:
    282             node.panel.Load(node)