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

redeemItemsContainer.py (29180B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\redeem\redeemItemsContainer.py
      2 import math
      3 import eveformat
      4 import evelink.client
      5 import eveui
      6 import homestation.client
      7 import localization
      8 import redeem.client
      9 import signals
     10 import threadutils
     11 import uthread2
     12 from carbon.client.script.environment.AudioUtil import PlaySound
     13 from carbonui import ButtonVariant, Color, uiconst
     14 from carbonui.control.button import Button
     15 from carbonui.control.combo import Combo
     16 from carbonui.control.scroll import Scroll
     17 from carbonui.primitives.fill import Fill
     18 from carbonui.primitives.frame import Frame
     19 from carbonui.uianimations import animations
     20 from carbonui.uicore import uicore
     21 from carbonui.primitives.container import Container
     22 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     23 from carbonui.primitives.sprite import Sprite
     24 from eve.client.script.ui import eveColor, eveThemeColor
     25 from eve.client.script.ui.control import eveLabel, itemIcon
     26 from carbonui.control.buttonIcon import ButtonIcon
     27 from carbonui.control.message import QuickMessage
     28 from eve.client.script.ui.quickFilter import QuickFilterEdit
     29 from eve.client.script.ui.shared.inventory.invContainers import RedeemItems, GetRedeemSortOptions
     30 from eve.client.script.ui.shared.redeem.redeemItem import RedeemItem, DraggableRedeemItem, REDEEM_ITEM_WIDTH, GetRedeemItemHeight, GetRedeemItemHeightWithoutDescription
     31 from eve.client.script.ui.shared.redeem.redeemUiConst import CREATION_DATE_SORT_KEY
     32 from uihider import CommandBlockerService
     33 
     34 def alert_user(text):
     35     message = QuickMessage(parent=uicore.layer.abovemain, name='RedeemActionAlert')
     36     message.minTop = uicore.desktop.height / 2 - 56
     37     msg = u'\n{}\n'.format(text)
     38     message.ShowMsg(msg)
     39     uthread2.call_after_wallclocktime_delay(message.Close, 3)
     40 
     41 
     42 PADDING_NORMAL = 8
     43 PADDING_WIDE = 2 * PADDING_NORMAL
     44 FOOTER_HEIGHT = 50
     45 BUTTON_CONTAINER_HEIGHT = 35
     46 ITEM_ROW_PADDING = 12
     47 INFO_CONTAINER_COLOR = (0.0, 0.0, 0.0, 0.5)
     48 INV_NAME = 'redeemItems'
     49 
     50 class RedeemItemsContainer(Container):
     51     default_name = 'RedeemItemsContainer'
     52     default_showHeaderCount = True
     53     default_showDeliveryInfo = True
     54     default_showFooter = True
     55     default_areItemsDraggable = False
     56     default_sidePadding = 50
     57     default_topPadding = 4
     58     default_bottomPadding = 6
     59 
     60     def ApplyAttributes(self, attributes):
     61         super(RedeemItemsContainer, self).ApplyAttributes(attributes)
     62         self.isReady = False
     63         self._is_updating_scroll_selection = False
     64         self.controller = attributes.controller
     65         if not self.controller:
     66             self.controller = Controller(redeem_data=attributes.redeemData, redeem_service=sm.GetService('redeem'), home_station_service=homestation.Service.instance())
     67         self.onContentUpdateCallback = attributes.get('onContentUpdateCallback', None)
     68         self.areItemsDraggable = attributes.get('areItemsDraggable', self.default_areItemsDraggable)
     69         self.showHeaderCount = attributes.get('showHeaderCount', self.default_showHeaderCount)
     70         self.showDeliveryInfo = attributes.get('showDeliveryInfo', self.default_showDeliveryInfo)
     71         self.showFooter = attributes.get('showFooter', self.default_showFooter)
     72         self.sidePadding = attributes.get('sidePadding', self.default_sidePadding)
     73         self.topPadding = attributes.get('topPadding', self.default_topPadding)
     74         self.bottomPadding = attributes.get('bottomPadding', self.default_bottomPadding)
     75         self.redeemItems = None
     76         self.redeemItemsContainer = None
     77         self.dragArea = Container(name='dragArea', parent=self, align=uiconst.TOALL)
     78         if self.showHeaderCount:
     79             self.ConstructHeader()
     80         if self.showFooter:
     81             self.ConstructFooter()
     82         if self.showDeliveryInfo:
     83             self.ConstructDeliveryInfo()
     84         self.redeemItemsContainer = Container(name='redeemItemsContainer', parent=self, align=uiconst.TOALL, padding=(self.sidePadding,
     85          self.topPadding,
     86          self.sidePadding,
     87          self.bottomPadding))
     88         self.ConstructRedeemItemsContainer()
     89         self.controller.on_selection_changed.connect(self._OnSelectionChanged)
     90         self.controller.on_load.connect(self._OnTokensUpdated)
     91         self.controller.on_redeem.connect(self._OnRedeem)
     92         self.controller.on_trash.connect(self._OnTrash)
     93         self.controller.on_refresh_sort.connect(self._RefreshSort)
     94         self.controller.on_text_filter_changed.connect(self._OnTextFilterChanged)
     95         self.controller.select_all_triggered.connect(self._SelectAllTriggered)
     96         uthread2.start_tasklet(self.controller.load)
     97 
     98     def ConstructHeader(self):
     99         Header(parent=self, align=uiconst.TOTOP, padding=(self.sidePadding,
    100          PADDING_WIDE,
    101          self.sidePadding,
    102          0), controller=self.controller)
    103         ControlContainer(parent=self, align=uiconst.TOTOP, controller=self.controller, padding=(self.sidePadding,
    104          10,
    105          self.sidePadding,
    106          16))
    107 
    108     def ConstructDeliveryInfo(self):
    109         if not CommandBlockerService.instance().is_blocked(['redeem_to_home_station']):
    110             HomeStationSection(parent=self, align=uiconst.TOBOTTOM, padding=(self.sidePadding,
    111              0,
    112              self.sidePadding,
    113              PADDING_NORMAL), controller=self.controller)
    114         CurrentStationSection(parent=self, align=uiconst.TOBOTTOM, padding=(self.sidePadding,
    115          0,
    116          self.sidePadding,
    117          PADDING_NORMAL), controller=self.controller)
    118         AutoInjectNotice(parent=self, align=uiconst.TOBOTTOM, padding=(self.sidePadding,
    119          0,
    120          self.sidePadding,
    121          PADDING_NORMAL), controller=self.controller)
    122 
    123     def ConstructFooter(self):
    124         Footer(parent=self, align=uiconst.TOBOTTOM, controller=self.controller)
    125 
    126     @eveui.skip_if_destroyed
    127     def ConstructRedeemItemsContainer(self):
    128         if self.redeemItems and not self.redeemItems.destroyed:
    129             self.redeemItems.Close()
    130         self.redeemItems = RedeemItems(name=INV_NAME, parent=self.redeemItemsContainer, showControls=False, state=uiconst.UI_NORMAL, itemID='redeemItems', hasCapacity=False, oneWay=True, viewOnly=True, hasScrollBackground=False, rowPadding=ITEM_ROW_PADDING, shouldAddFinalRowPadding=False, getItemClass=self.GetItemClass, scrollClass=Scroll, shouldShowHint=self.ShouldShowHint(), selectAllOnFirstLoad=True, onSelectionChange=self._OnScrollSelectionChanged)
    131         self.redeemItems.GetRubberbandParentContainer = self.GetDragArea
    132         self.OnMouseDown = self.redeemItems.scroll.OnMouseDown
    133         self.OnMouseUp = self.redeemItems.scroll.OnMouseUp
    134 
    135     def GetDragArea(self):
    136         return self.dragArea
    137 
    138     def _OnTokensUpdated(self):
    139         if self.redeemItems and not self.redeemItems.destroyed:
    140             self.redeemItems.Refresh()
    141         else:
    142             self.ConstructRedeemItemsContainer()
    143         self.isReady = True
    144         if self.onContentUpdateCallback:
    145             self.onContentUpdateCallback()
    146 
    147     def _OnScrollSelectionChanged(self, selected_nodes):
    148         if self._is_updating_scroll_selection:
    149             return
    150         self.controller.on_selection_changed.disconnect(self._OnSelectionChanged)
    151         try:
    152             selected_tokens = [ node.item for node in selected_nodes ]
    153             self.controller.select_exclusive(selected_tokens)
    154         finally:
    155             self.controller.on_selection_changed.connect(self._OnSelectionChanged)
    156 
    157     def _OnSelectionChanged(self):
    158         self._is_updating_scroll_selection = True
    159         try:
    160             selected_nodes = [ node for node in self.redeemItems.scroll.GetNodes() if token_key(node.item) in self.controller.selected_keys ]
    161             self.redeemItems.scroll.SelectNodes(selected_nodes)
    162         finally:
    163             self._is_updating_scroll_selection = False
    164 
    165     def _SelectAllTriggered(self):
    166         self.redeemItems.scroll.SelectAll()
    167 
    168     def IsReady(self):
    169         return self.isReady
    170 
    171     def HasTokens(self):
    172         return self.controller.tokens_count > 0
    173 
    174     def RedeemAll(self):
    175         self.controller.redeem_all(destination=Destination.current_station)
    176 
    177     def GetItemClass(self):
    178         if self.areItemsDraggable:
    179             return DraggableRedeemItem
    180         return RedeemItem
    181 
    182     def ShouldShowHint(self):
    183         return not self.areItemsDraggable
    184 
    185     def _OnRedeem(self, delivery_location_id, redeem_result):
    186         PlaySound(uiconst.SOUND_ADD_OR_USE)
    187         alert_message = None
    188         if redeem_result['itemsCreated']:
    189             alert_message = localization.GetByLabel('UI/Redeem/RedeemToStationModalConfirmation', station=delivery_location_id)
    190         elif any((redeem_result[x] for x in ['activated_licenses', 'itemsCreated', 'isk_rewarded'])):
    191             alert_message = localization.GetByLabel('UI/Redeem/RedeemModalConfirmation')
    192         if alert_message:
    193             alert_user(alert_message)
    194 
    195     def _OnTrash(self):
    196         alert_user(localization.GetByLabel('UI/Redeem/TrashModalConfirmation'))
    197         PlaySound(uiconst.SOUND_REMOVE)
    198 
    199     def _RefreshSort(self, sortby, direction):
    200         self.redeemItems.RefreshSort(sortby, direction)
    201 
    202     def _OnTextFilterChanged(self, text):
    203         self.redeemItems.SetQuickFilterInput(text)
    204 
    205 
    206 class Station(object):
    207 
    208     def __init__(self, station_id, type_id):
    209         self.id = station_id
    210         self.type_id = type_id
    211 
    212     @staticmethod
    213     def from_session(session):
    214         docked_id = get_docked_id(session)
    215         if docked_id:
    216             return Station(station_id=docked_id, type_id=sm.GetService('map').GetItem(docked_id).typeID)
    217 
    218     @property
    219     def name(self):
    220         return cfg.evelocations.Get(self.id).name
    221 
    222     @property
    223     def link(self):
    224         return evelink.location_link(self.id)
    225 
    226 
    227 def get_docked_id(session):
    228     station_id = getattr(session, 'stationid', None)
    229     structure_id = getattr(session, 'structureid', None)
    230     ship_id = getattr(session, 'shipid', None)
    231     if station_id is not None:
    232         return station_id
    233     if structure_id is not None and structure_id != ship_id:
    234         return structure_id
    235 
    236 
    237 class Destination(object):
    238     current_station = 'current_station'
    239     home_station = 'home_station'
    240 
    241 
    242 class Controller(object):
    243     __notifyevents__ = ('OnRedeemingTokensUpdated', 'OnSessionChanged')
    244 
    245     def __init__(self, redeem_data, redeem_service, home_station_service):
    246         self._redeem_data = redeem_data
    247         self._redeem_service = redeem_service
    248         self._home_station_service = home_station_service
    249         self._raw_tokens = []
    250         self._tokens = {}
    251         self._selected = set()
    252         self.invName = INV_NAME
    253         self.on_selection_changed = signals.Signal('on_selection_changed')
    254         self.on_load = signals.Signal('on_load')
    255         self.on_station_changed = signals.Signal('on_station_changed')
    256         self.on_redeem = signals.Signal('on_redeem')
    257         self.on_trash = signals.Signal('on_trash')
    258         self.on_refresh_sort = signals.Signal('on_refresh_sort')
    259         self.on_text_filter_changed = signals.Signal('on_text_filter_changed')
    260         self.select_all_triggered = signals.Signal('select_all_triggered')
    261         sm.RegisterNotify(self)
    262 
    263     @property
    264     def tokens(self):
    265         return self._tokens.values()
    266 
    267     @property
    268     def tokens_count(self):
    269         return len(self._tokens)
    270 
    271     @property
    272     def selected(self):
    273         return [ self._tokens[key] for key in self._selected ]
    274 
    275     @property
    276     def selected_keys(self):
    277         return [ key for key in self._selected ]
    278 
    279     @property
    280     def selected_count(self):
    281         return len(self._selected)
    282 
    283     @property
    284     def current_station(self):
    285         return Station.from_session(session)
    286 
    287     @property
    288     def auto_injected_selected_count(self):
    289         return len(filter(lambda token: self._redeem_data.is_auto_injected(token), self.selected))
    290 
    291     def load(self):
    292         self.deselect_all()
    293         self._raw_tokens = []
    294         self._tokens = []
    295         self._raw_tokens = self._redeem_service.GetRedeemTokens()
    296         self._tokens = {token_key(token):token for token in self._raw_tokens}
    297         self.on_load()
    298 
    299     def get_by_key(self, key):
    300         return self._tokens[key]
    301 
    302     def select(self, tokens):
    303         self._selected = self._selected.union(set((token_key(token) for token in tokens)))
    304         self.on_selection_changed()
    305 
    306     def select_all(self):
    307         self.select(self.tokens)
    308 
    309     def select_exclusive(self, tokens):
    310         self._selected = set((token_key(token) for token in tokens))
    311         self.on_selection_changed()
    312 
    313     def deselect(self, tokens):
    314         if any((token_key(token) in self._selected for token in tokens)):
    315             self._selected = self._selected.difference(set((token_key(token) for token in tokens)))
    316             self.on_selection_changed()
    317 
    318     def deselect_all(self):
    319         self.deselect(self.selected)
    320 
    321     def redeem(self, tokens, destination):
    322         if destination == Destination.current_station:
    323             delivery_location_id = self.current_station.id
    324         elif destination == Destination.home_station:
    325             delivery_location_id = self._home_station_service.get_home_station().id
    326         else:
    327             raise RuntimeError('Unknown destination {!r}'.format(destination))
    328         confirmed = redeem.confirm_redeem(character_id=session.charid, tokens=tokens, delivery_location_id=delivery_location_id, redeem_data=self._redeem_data)
    329         if not confirmed:
    330             return
    331         tokens = [ {'tokenID': token.tokenID,
    332          'massTokenID': token.massTokenID,
    333          'typeID': token.typeID} for token in tokens ]
    334         redeem_result = self._redeem_service.ClaimRedeemTokens(tokens=tokens, charID=session.charid, useHomeStation=destination == Destination.home_station)
    335         self.on_redeem(delivery_location_id, redeem_result)
    336 
    337     def redeem_all(self, destination):
    338         self.redeem(self.tokens, destination)
    339 
    340     def redeem_selected(self, destination):
    341         self.redeem(self.selected, destination)
    342 
    343     def trash(self, tokens):
    344         if not tokens:
    345             return
    346         if not redeem.confirm_trash(tokens):
    347             return
    348         token_keys = [ token_key(token) for token in tokens ]
    349         self._redeem_service.TrashRedeemTokens(token_keys)
    350         self.on_trash()
    351 
    352     def trash_selected(self):
    353         self.trash(self.selected)
    354 
    355     def change_sort_order(self, sortby, direction):
    356         self.on_refresh_sort(sortby, direction)
    357 
    358     def change_text_filter(self, text):
    359         self.deselect_all()
    360         self.on_text_filter_changed(text)
    361 
    362     def OnRedeemingTokensUpdated(self):
    363         uthread2.start_tasklet(self.load)
    364 
    365     def OnSessionChanged(self, is_remote, session, changes):
    366         docked_or_undocked = any((name in changes for name in ('stationid', 'structureid')))
    367         took_or_released_structure_control = 'shipid' in changes and session.structureid and session.structureid in changes['shipid']
    368         if docked_or_undocked or took_or_released_structure_control:
    369             uthread2.start_tasklet(self.on_station_changed)
    370 
    371 
    372 def token_key(token):
    373     return (token.tokenID, token.massTokenID)
    374 
    375 
    376 class Header(ContainerAutoSize):
    377 
    378     def __init__(self, controller, **kwargs):
    379         super(Header, self).__init__(alignMode=uiconst.TOTOP, **kwargs)
    380         self._controller = controller
    381         self._label = None
    382         self.layout()
    383         self.update()
    384         self._controller.on_load.connect(self.update)
    385 
    386     def layout(self):
    387         self._label = eveLabel.EveCaptionLarge(parent=self, state=uiconst.UI_DISABLED, align=uiconst.TOTOP)
    388 
    389     @threadutils.threaded
    390     def update(self):
    391         text = localization.GetByLabel('UI/RedeemWindow/ReedemNumItems', num=self._controller.tokens_count)
    392         self._label.SetText(text)
    393 
    394 
    395 class Footer(Container):
    396 
    397     def __init__(self, controller, **kwargs):
    398         super(Footer, self).__init__(bgColor=eveColor.BLACK, height=FOOTER_HEIGHT, **kwargs)
    399         self._controller = controller
    400         self._label = None
    401         self.layout()
    402         self.update()
    403         self._controller.on_selection_changed.connect(self.update)
    404         self._controller.on_load.connect(self.update)
    405 
    406     def layout(self):
    407         ButtonIcon(parent=self, align=uiconst.CENTERLEFT, left=PADDING_WIDE, width=32, height=32, texturePath='res:/UI/Texture/Classes/RedeemPanel/trashcan_icon.png', iconSize=24, func=self._controller.trash_selected, args=(), hint=localization.GetByLabel('UI/RedeemWindow/TrashSelectedItems'))
    408         self._label = eveLabel.EveLabelMedium(parent=self, align=uiconst.CENTERRIGHT, left=PADDING_WIDE)
    409 
    410     @threadutils.threaded
    411     def update(self):
    412         self._label.SetText(localization.GetByLabel('UI/Redeem/RedeemItemsSelected', selected=self._controller.selected_count, total=self._controller.tokens_count))
    413 
    414 
    415 class AutoInjectNotice(ContainerAutoSize):
    416     icon_size = 24
    417     color = Color.from_hex('#369ebd')
    418 
    419     def __init__(self, controller, **kwargs):
    420         super(AutoInjectNotice, self).__init__(alignMode=uiconst.TOTOP, minHeight=(self.icon_size + 2 * PADDING_NORMAL), clipChildren=True, **kwargs)
    421         self.display = False
    422         self._controller = controller
    423         self._frame = None
    424         self._corner = None
    425         self._icon = None
    426         self._label = None
    427         self._padding_before = (self.padTop, self.padBottom)
    428         self._is_revealed = False
    429         self.layout()
    430         self.update(animate=False)
    431         self._controller.on_selection_changed.connect(self.update)
    432 
    433     def layout(self):
    434         color = eveThemeColor.THEME_ACCENT
    435         self._frame = Frame(bgParent=self, texturePath='res:/UI/Texture/Shared/DarkStyle/panel1Corner_Solid.png', cornerSize=9, rotation=math.pi, color=color, opacity=0.1)
    436         self._corner = Sprite(name='AUTOINJECT_SPRITE_A', parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/Shared/DarkStyle/cornerTriSmall.png', width=5, height=5, color=color)
    437         self._icon = Sprite(name='AUTOINJECT_SPRITE_B', parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, top=PADDING_NORMAL, left=PADDING_NORMAL, width=self.icon_size, height=self.icon_size, texturePath='res:/UI/Texture/classes/RedeemPanel/autoInjectFlat.png', color=color)
    438         self._label = eveLabel.EveLabelMedium(name='AUTOINJECT_LABEL_A', parent=self, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, padding=(PADDING_NORMAL + self.icon_size + PADDING_NORMAL,
    439          PADDING_NORMAL,
    440          PADDING_NORMAL,
    441          PADDING_NORMAL), color=color)
    442 
    443     def reveal(self):
    444         self.padTop, self.padBottom = self._padding_before
    445         self.display = True
    446         self.EnableAutoSize()
    447 
    448     def anim_reveal(self):
    449         if self.display:
    450             return
    451         self.display = True
    452         _, height = self.GetAutoSize()
    453         self.DisableAutoSize()
    454         duration = 0.2
    455         animations.FadeIn(self, duration=duration)
    456         animations.MorphScalar(self, 'height', startVal=0, endVal=height, duration=duration / 2.0, callback=self.EnableAutoSize)
    457         animations.MorphScalar(self, 'padTop', startVal=0, endVal=self._padding_before[0], duration=duration)
    458         animations.MorphScalar(self, 'padBottom', startVal=0, endVal=self._padding_before[1], duration=duration)
    459 
    460     def hide(self):
    461         self.display = False
    462 
    463     def anim_hide(self):
    464         if not self.display:
    465             return
    466         self.DisableAutoSize()
    467         duration = 0.2
    468         animations.FadeOut(self, duration=duration)
    469         animations.MorphScalar(self, 'height', startVal=self.height, endVal=0, duration=duration / 2.0, callback=self.Hide)
    470         animations.MorphScalar(self, 'padTop', startVal=self._padding_before[0], endVal=0, duration=duration)
    471         animations.MorphScalar(self, 'padBottom', startVal=self._padding_before[1], endVal=0, duration=duration)
    472 
    473     @threadutils.threaded
    474     def update(self, animate = True):
    475         self._label.SetText(localization.GetByLabel('UI/RedeemWindow/AutoInjectNotification', count=self._controller.auto_injected_selected_count))
    476         if self._controller.auto_injected_selected_count > 0:
    477             self.anim_reveal() if animate else self.reveal()
    478         else:
    479             self.anim_hide() if animate else self.hide()
    480 
    481     def OnColorThemeChanged(self):
    482         super(AutoInjectNotice, self).OnColorThemeChanged()
    483         newColor = eveThemeColor.THEME_ACCENT.rgb
    484         self._frame.SetRGB(*newColor)
    485         self._corner.SetRGB(*newColor)
    486         self._icon.SetRGB(*newColor)
    487         self._label.SetRGB(*newColor)
    488 
    489 
    490 class StationSection(ContainerAutoSize):
    491     icon_size = 48
    492     background_color = INFO_CONTAINER_COLOR
    493 
    494     def __init__(self, button_text, primary = False, **kwargs):
    495         super(StationSection, self).__init__(bgColor=self.background_color, **kwargs)
    496         self._button_text = button_text
    497         self._primary = primary
    498         self._icon = None
    499         self._label = None
    500         self._button = None
    501         self._feedback = None
    502         self.layout()
    503         self.update()
    504 
    505     def redeem(self):
    506         raise NotImplementedError()
    507 
    508     def get_text(self):
    509         raise NotImplementedError()
    510 
    511     def update_icon(self, icon):
    512         raise NotImplementedError()
    513 
    514     def is_enabled(self):
    515         raise NotImplementedError()
    516 
    517     def layout(self):
    518         inner = ContainerAutoSize(parent=self, align=uiconst.TOTOP, alignMode=uiconst.TOTOP, minHeight=self.icon_size + 2 * PADDING_NORMAL)
    519         self._feedback = Fill(parent=inner, align=uiconst.TOALL, color=(1.0, 1.0, 1.0), opacity=0.0)
    520         icon_cont = ContainerAutoSize(parent=inner, align=uiconst.TOLEFT, padding=8)
    521         self._icon = itemIcon.ItemIcon(parent=icon_cont, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, width=self.icon_size, height=self.icon_size, showOmegaOverlay=False)
    522         button_cont = ContainerAutoSize(parent=inner, align=uiconst.TORIGHT, padding=(PADDING_NORMAL,
    523          0,
    524          PADDING_NORMAL,
    525          0))
    526         if self._primary:
    527             button_variant = ButtonVariant.PRIMARY
    528         else:
    529             button_variant = ButtonVariant.NORMAL
    530         self._button = Button(parent=button_cont, align=uiconst.CENTER, label=self._button_text, func=self.redeem, args=(), variant=button_variant)
    531         self._button.Disable()
    532         label_cont = ContainerAutoSize(parent=inner, align=uiconst.TOTOP, minHeight=self.icon_size, padding=(0,
    533          PADDING_NORMAL,
    534          0,
    535          PADDING_NORMAL))
    536         self._label = eveLabel.EveLabelMedium(parent=label_cont, align=uiconst.CENTERLEFT, state=uiconst.UI_NORMAL, autoFadeSides=25)
    537 
    538     @threadutils.threaded
    539     def update(self, animate = False, feedback = False):
    540         if animate:
    541             animations.FadeOut(self._icon, duration=0.25)
    542             animations.FadeOut(self._label, duration=0.25, sleep=True)
    543         self.update_icon(self._icon)
    544         self._label.SetText(self.get_text())
    545         if self.is_enabled():
    546             self._button.Enable()
    547         else:
    548             self._button.Disable()
    549         if animate:
    550             animations.FadeIn(self._icon, duration=0.25)
    551             animations.FadeIn(self._label, endVal=self._label.default_color[3], duration=0.25)
    552             if feedback:
    553                 animations.FadeTo(self._feedback, endVal=0.5, duration=0.35, timeOffset=0.1, curveType=uiconst.ANIM_WAVE)
    554 
    555 
    556 class CurrentStationSection(StationSection):
    557 
    558     def __init__(self, controller, **kwargs):
    559         self._controller = controller
    560         super(CurrentStationSection, self).__init__(button_text=localization.GetByLabel('UI/RedeemWindow/RedeemToCurrentStation'), primary=True, **kwargs)
    561         self._controller.on_station_changed.connect(self.handle_station_changed)
    562         self._controller.on_selection_changed.connect(self.update)
    563 
    564     def is_enabled(self):
    565         return self._controller.current_station is not None and self._controller.selected_count > 0
    566 
    567     def get_text(self):
    568         current_station = self._controller.current_station
    569         if current_station is None:
    570             return u'{}<br>{}'.format(localization.GetByLabel('UI/RedeemWindow/CurrentStationHeader'), eveformat.color(localization.GetByLabel('UI/RedeemWindow/NotDocked'), eveColor.WARNING_ORANGE))
    571         else:
    572             return u'{}<br>{}'.format(localization.GetByLabel('UI/RedeemWindow/CurrentStationHeader'), current_station.link)
    573 
    574     def update_icon(self, icon):
    575         current_station = self._controller.current_station
    576         if current_station is None:
    577             icon.SetTexturePath('res:/UI/Texture/Icons/5_64_11.png')
    578             icon.opacity = 0.5
    579         else:
    580             icon.SetTypeIDandItemID(typeID=current_station.type_id, itemID=current_station.id)
    581             icon.opacity = 1.0
    582 
    583     def redeem(self):
    584         self._controller.redeem_selected(Destination.current_station)
    585 
    586     def handle_station_changed(self):
    587         self.update(animate=True, feedback=True)
    588 
    589 
    590 class HomeStationSection(StationSection):
    591 
    592     def __init__(self, controller, **kwargs):
    593         super(HomeStationSection, self).__init__(button_text=localization.GetByLabel('UI/RedeemWindow/RedeemToHomeStation'), **kwargs)
    594         self._controller = controller
    595         homestation.Service.instance().on_home_station_changed.connect(self.handle_home_station_changed)
    596         self._controller.on_selection_changed.connect(self.update)
    597 
    598     @property
    599     def home_station(self):
    600         return homestation.Service.instance().get_home_station()
    601 
    602     def is_enabled(self):
    603         return self._controller.selected_count > 0
    604 
    605     def get_text(self):
    606         try:
    607             return u'{}<br>{}'.format(localization.GetByLabel('UI/RedeemWindow/HomeStationHeader'), self.home_station.link)
    608         except homestation.NotLoggedInError:
    609             return ''
    610 
    611     def update_icon(self, icon):
    612         try:
    613             icon.SetTypeIDandItemID(itemID=self.home_station.id, typeID=self.home_station.type_id)
    614         except homestation.NotLoggedInError:
    615             pass
    616 
    617     def redeem(self):
    618         self._controller.redeem_selected(destination=Destination.home_station)
    619 
    620     def handle_home_station_changed(self):
    621         self.update(animate=True, feedback=True)
    622 
    623 
    624 class ControlContainer(ContainerAutoSize):
    625     __notifyevents__ = ('OnInvContRefreshed',)
    626     default_height = 30
    627     default_availableSpace = None
    628 
    629     def __init__(self, controller, **kwargs):
    630         super(ControlContainer, self).__init__(alignMode=uiconst.TOLEFT, **kwargs)
    631         self._controller = controller
    632         self.availableSpace = kwargs.get('availableSpace', None)
    633         self.sortCombo = None
    634         self.layout()
    635         sm.RegisterNotify(self)
    636 
    637     def layout(self):
    638         options = []
    639         for eachOption in GetRedeemSortOptions():
    640             if eachOption is None:
    641                 continue
    642             labelPath, value = eachOption
    643             options.append((localization.GetByLabel(labelPath), value))
    644 
    645         selectedValue = self.GetSelectedOptionFromSettings()
    646         labelCont = ContainerAutoSize(name='labelCont', parent=self, align=uiconst.TOLEFT)
    647         text = localization.GetByLabel('UI/Common/SortBy')
    648         label = eveLabel.EveLabelMedium(parent=labelCont, align=uiconst.CENTERRIGHT, left=uiconst.CENTERLEFT, text=text)
    649         comboCont = ContainerAutoSize(name='comboCont', parent=self, align=uiconst.TOLEFT)
    650         self.sortCombo = Combo(name='myCombo', parent=comboCont, label='', options=options, align=uiconst.CENTER, callback=self.OnSortOrderChanged, select=selectedValue, width=100)
    651         buttonCont = ContainerAutoSize(name='buttonCont', parent=self, align=uiconst.TOLEFT, left=10)
    652         btn = Button(parent=buttonCont, align=uiconst.CENTERRIGHT, label=localization.GetByLabel('UI/RedeemWindow/SelectAll'), func=self._controller.select_all_triggered, args=())
    653         filterAlign = uiconst.TORIGHT if self.align == uiconst.TOTOP else uiconst.TOLEFT
    654         filtCont = ContainerAutoSize(name='filtCont', parent=self, align=filterAlign, left=10)
    655         self.quickFilter = QuickFilterEdit(parent=filtCont, align=uiconst.CENTERRIGHT, width=190)
    656         self.quickFilter.ReloadFunction = self.OnQuickFilter
    657         self.height = max(label.height, self.sortCombo.height, btn.height)
    658         if self.availableSpace is not None:
    659             self.AdjustWidths()
    660 
    661     def AdjustWidths(self):
    662         if self.availableSpace is None:
    663             return
    664         if self.availableSpace < self.width + 50:
    665             self.quickFilter.width = 100
    666 
    667     def GetSelectedOptionFromSettings(self):
    668         return settings.char.ui.Get('containerSortIconsBy_%s' % self._controller.invName, (CREATION_DATE_SORT_KEY, 1))
    669 
    670     def OnInvContRefreshed(self, invCont):
    671         if invCont.name == self._controller.invName:
    672             if self.sortCombo and not self.sortCombo.destroyed:
    673                 selectedValue = self.GetSelectedOptionFromSettings()
    674                 self.sortCombo.SelectItemByValue(selectedValue)
    675 
    676     def OnSortOrderChanged(self, cb, key, value):
    677         self._controller.on_refresh_sort(*value)
    678 
    679     def OnQuickFilter(self, *args):
    680         text = self.quickFilter.GetValue()
    681         self._controller.change_text_filter(text)