示例#1
0
class JobStateContainer(Container):
    default_state = uiconst.UI_NORMAL

    def ApplyAttributes(self, attributes):
        Container.ApplyAttributes(self, attributes)
        self.jobData = attributes.jobData
        self.ConstructLayout()
        self.UpdateValue()

    def ConstructLayout(self):
        if not self.jobData:
            return
        if self.jobData.status == industry.STATUS_READY:
            self.deliverBtn = Button(name='deliverBtn', parent=self, align=uiconst.TOALL, label='<b>' + localization.GetByLabel('UI/Industry/Deliver'), func=self.OnDeliverBtn, padding=2)
            self.deliverBtn.width = self.deliverBtn.height = 0
            self.deliverBtn.Blink(time=3000)
            self.Enable()
        else:
            self.Disable()
            gaugeCont = ContainerAutoSize(name='gaugeCont', parent=self, align=uiconst.TOBOTTOM)
            mainCont = Container(name='mainCont', parent=self)
            self.deliverBtn = None
            self.valueLabel = EveLabelMedium(parent=mainCont, align=uiconst.CENTERLEFT, left=4)
        if self.jobData.status == industry.STATUS_INSTALLED:
            color = industryUIConst.COLOR_MANUFACTURING if self.jobData.activityID == industry.MANUFACTURING else industryUIConst.COLOR_SCIENCE
            self.gauge = Gauge(parent=gaugeCont, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, color=color, height=6, gaugeHeight=6, padTop=1, backgroundColor=(1.0, 1.0, 1.0, 0.05))

    def OnStatusChanged(self):
        self.Flush()
        self.ConstructLayout()
        if self.jobData.status == industry.STATUS_DELIVERED:
            uicore.animations.FadeTo(self.valueLabel, 0.0, 1.0)

    def UpdateValue(self, animate = False, num = 0):
        if not self.jobData or self.jobData.status in (industry.STATUS_READY, industry.STATUS_UNSUBMITTED):
            return
        if self.jobData.status == industry.STATUS_INSTALLED:
            progressRatio = self.jobData.GetJobProgressRatio()
            if progressRatio:
                self.gauge.SetValueInstantly(progressRatio)
                if animate and progressRatio != 1.0:
                    self.gauge.AnimFlash(1.0, duration=3.0, timeOffset=num * 0.1)
        self.valueLabel.text = self.jobData.GetJobStateLabel()

    def OnDeliverBtn(self, *args):
        sm.GetService('industrySvc').CompleteJob(self.jobData.jobID)
        sm.GetService('audio').SendUIEvent('ind_jobDelivered')

    def OnNewJobData(self, jobData):
        self.jobData = jobData
        self.OnStatusChanged()
        self.UpdateValue(animate=True)

    def StartUpdate(self):
        uthread.new(self._UpdateThread)

    def _UpdateThread(self):
        while not self.destroyed:
            self.UpdateValue()
            blue.synchro.SleepWallclock(500)
class ActivateMultiTrainingWindow(Window):
    __guid__ = 'form.ActivateMultiTrainingWindow'
    default_width = 420
    default_heigt = 100
    default_windowID = 'ActivateMultiTrainingWindow'
    default_topParentHeight = 0
    default_clipChildren = True
    default_isPinable = False
    GRAY_COLOR = util.Color.GRAY5
    GREEN_COLOR = (0.0, 1.0, 0.0, 0.8)
    LINE_COLOR = (1, 1, 1, 0.2)
    WHITE_COLOR = util.Color.WHITE
    CONFIRM_DELAY = 3000

    def ApplyAttributes(self, attributes):
        Window.ApplyAttributes(self, attributes)
        self.itemID = attributes['itemID']
        self.expireDate1 = None
        self.expireDate2 = None
        self.reloading = False
        self.confirmed = False
        self.highlight = None
        self.Layout()
        self.Reload()
        uthread.new(self.UpdateTimersThread)

    def Layout(self):
        self.HideHeader()
        self.MakeUnResizeable()
        self.container = ContainerAutoSize(parent=self.GetMainArea(),
                                           align=uiconst.TOTOP,
                                           alignMode=uiconst.TOTOP,
                                           state=uiconst.UI_PICKCHILDREN,
                                           padding=(15, 15, 15, 0),
                                           callback=self.OnContainerResized)
        EveLabelLargeBold(parent=self.container,
                          align=uiconst.TOTOP,
                          text=localization.GetByLabel(
                              'UI/ActivateMultiTraining/ActivateHeading'))
        EveLabelMedium(parent=self.container,
                       align=uiconst.TOTOP,
                       text=localization.GetByLabel(
                           'UI/ActivateMultiTraining/ActivateDescription'),
                       color=self.GRAY_COLOR,
                       padding=(0, 5, 0, 10))
        Line(parent=self.container, align=uiconst.TOTOP, color=self.LINE_COLOR)
        slot1 = ContainerAutoSize(parent=self.container,
                                  align=uiconst.TOTOP,
                                  alignMode=uiconst.TOTOP,
                                  state=uiconst.UI_PICKCHILDREN,
                                  bgColor=(0, 0, 0, 0.3))
        self.slot1Background = Fill(parent=slot1,
                                    color=self.GREEN_COLOR,
                                    opacity=0.0)
        self.slot1Title = EveLabelMediumBold(parent=slot1,
                                             align=uiconst.TOTOP,
                                             text='',
                                             padding=(60, 12, 140, 0),
                                             color=self.WHITE_COLOR)
        self.slot1Expiry = EveLabelMediumBold(parent=slot1,
                                              align=uiconst.TOTOP,
                                              text='',
                                              padding=(60, 0, 140, 10),
                                              color=self.GRAY_COLOR)
        self.slot1Button = Button(parent=slot1,
                                  label='',
                                  align=uiconst.CENTERRIGHT,
                                  fontsize=13,
                                  fixedwidth=120,
                                  fixedheight=30,
                                  pos=(10, 0, 0, 0))
        self.slot1Button.confirmHilite = GradientSprite(
            bgParent=self.slot1Button,
            rotation=-math.pi / 2,
            rgbData=[(0, self.GREEN_COLOR[:3])],
            alphaData=[(0, 0.5), (0.3, 0.2), (0.6, 0.14)],
            opacity=0.0)
        self.slot1Icon = Sprite(
            parent=slot1,
            texturePath='res:/UI/Texture/Icons/add_training_queue.png',
            align=uiconst.CENTERLEFT,
            pos=(15, 0, 32, 32))
        Line(parent=self.container, align=uiconst.TOTOP, color=self.LINE_COLOR)
        slot2 = ContainerAutoSize(parent=self.container,
                                  align=uiconst.TOTOP,
                                  alignMode=uiconst.TOTOP,
                                  state=uiconst.UI_PICKCHILDREN,
                                  bgColor=(0, 0, 0, 0.3))
        self.slot2Background = Fill(parent=slot2,
                                    color=self.GREEN_COLOR,
                                    opacity=0.0)
        self.slot2Title = EveLabelMediumBold(parent=slot2,
                                             align=uiconst.TOTOP,
                                             text='',
                                             padding=(60, 12, 140, 0),
                                             color=self.WHITE_COLOR)
        self.slot2Expiry = EveLabelMediumBold(parent=slot2,
                                              align=uiconst.TOTOP,
                                              text='',
                                              padding=(60, 0, 140, 10),
                                              color=self.GRAY_COLOR)
        self.slot2Button = Button(parent=slot2,
                                  label='',
                                  align=uiconst.CENTERRIGHT,
                                  fontsize=13,
                                  fixedwidth=120,
                                  fixedheight=30,
                                  pos=(10, 0, 0, 0))
        self.slot2Button.confirmHilite = GradientSprite(
            bgParent=self.slot2Button,
            rotation=-math.pi / 2,
            rgbData=[(0, self.GREEN_COLOR[:3])],
            alphaData=[(0, 0.5), (0.3, 0.2), (0.6, 0.14)],
            opacity=0.0)
        self.slot2Icon = Sprite(
            parent=slot2,
            texturePath='res:/UI/Texture/Icons/add_training_queue.png',
            align=uiconst.CENTERLEFT,
            pos=(15, 0, 32, 32))
        Line(parent=self.container, align=uiconst.TOTOP, color=self.LINE_COLOR)
        self.closeButton = Button(
            parent=self.container,
            label=localization.GetByLabel('UI/Generic/Cancel'),
            func=self.Close,
            align=uiconst.TOTOP,
            fontsize=13,
            padding=(120, 10, 120, 30))

    def Reload(self, delay=0, force=False):
        try:
            self.reloading = True
            blue.pyos.synchro.SleepWallclock(delay)
            if self.destroyed:
                return
            queues = sm.GetService('skillqueue').GetMultipleCharacterTraining(
                force).items()
            if len(queues) < 1:
                self.expireDate1 = None
                self.slot1Title.text = localization.GetByLabel(
                    'UI/ActivateMultiTraining/AdditionalQueueNotActive')
                self.slot1Title.color = self.GRAY_COLOR
                self.slot1Icon.opacity = 0.3
                self.slot1Button.SetLabel(
                    localization.GetByLabel(
                        'UI/ActivateMultiTraining/Activate'))
                self.slot1Button.SetHint(
                    localization.GetByLabel(
                        'UI/ActivateMultiTraining/ActivateHint',
                        expiryDate=blue.os.GetWallclockTime() +
                        30 * const.DAY))
                self.slot1Button.func = functools.partial(
                    self.OnConfirmButton, self.slot1Button, self.OnAddQueue, 2,
                    None)
            if len(queues) < 2:
                self.expireDate2 = None
                self.slot2Title.text = localization.GetByLabel(
                    'UI/ActivateMultiTraining/AdditionalQueueNotActive')
                self.slot2Title.color = self.GRAY_COLOR
                self.slot2Icon.opacity = 0.3
                self.slot2Button.SetLabel(
                    localization.GetByLabel(
                        'UI/ActivateMultiTraining/Activate'))
                self.slot2Button.SetHint(
                    localization.GetByLabel(
                        'UI/ActivateMultiTraining/ActivateHint',
                        expiryDate=blue.os.GetWallclockTime() +
                        30 * const.DAY))
                self.slot2Button.func = functools.partial(
                    self.OnConfirmButton, self.slot2Button, self.OnAddQueue, 3,
                    None)
            for index, (trainingID,
                        trainingExpiry) in enumerate(sorted(queues)):
                if index == 0:
                    self.expireDate1 = trainingExpiry
                    self.slot1Title.text = localization.GetByLabel(
                        'UI/ActivateMultiTraining/AdditionalQueueActive')
                    self.slot1Title.color = self.GREEN_COLOR
                    self.slot1Icon.opacity = 1.0
                    self.slot1Button.SetLabel(
                        localization.GetByLabel(
                            'UI/ActivateMultiTraining/Extend'))
                    self.slot1Button.SetHint(
                        localization.GetByLabel(
                            'UI/ActivateMultiTraining/ExtendHint',
                            expiryDate=trainingExpiry + 30 * const.DAY))
                    self.slot1Button.func = functools.partial(
                        self.OnConfirmButton, self.slot1Button,
                        self.OnAddQueue, 1, trainingID)
                elif index == 1:
                    self.expireDate2 = trainingExpiry
                    self.slot2Title.text = localization.GetByLabel(
                        'UI/ActivateMultiTraining/AdditionalQueueActive')
                    self.slot2Title.color = self.GREEN_COLOR
                    self.slot2Icon.opacity = 1.0
                    self.slot2Button.SetLabel(
                        localization.GetByLabel(
                            'UI/ActivateMultiTraining/Extend'))
                    self.slot2Button.SetHint(
                        localization.GetByLabel(
                            'UI/ActivateMultiTraining/ExtendHint',
                            expiryDate=trainingExpiry + 30 * const.DAY))
                    self.slot2Button.func = functools.partial(
                        self.OnConfirmButton, self.slot2Button,
                        self.OnAddQueue, 2, trainingID)

            uicore.animations.FadeTo(self.slot1Button.confirmHilite,
                                     self.slot1Button.confirmHilite.opacity,
                                     0.0,
                                     duration=0.3)
            uicore.animations.FadeTo(self.slot2Button.confirmHilite,
                                     self.slot2Button.confirmHilite.opacity,
                                     0.0,
                                     duration=0.3)
            self.EnableButtons()
            if len(queues) == 0:
                self.slot2Button.Disable()
            if self.confirmed:
                self.slot1Button.state = uiconst.UI_HIDDEN
                self.slot2Button.state = uiconst.UI_HIDDEN
                self.closeButton.SetLabel(
                    localization.GetByLabel('UI/Generic/OK'))
            else:
                self.slot1Button.state = uiconst.UI_NORMAL
                self.slot2Button.state = uiconst.UI_NORMAL
                self.closeButton.SetLabel(
                    localization.GetByLabel('UI/Generic/Cancel'))
            if self.highlight == 1:
                uicore.animations.FadeTo(self.slot1Background,
                                         self.slot1Background.opacity,
                                         0.1,
                                         duration=0.3)
                self.closeButton.Blink(blinks=1)
                self.highlight = None
            elif self.highlight == 2:
                uicore.animations.FadeTo(self.slot2Background,
                                         self.slot2Background.opacity,
                                         0.1,
                                         duration=0.3)
                self.closeButton.Blink(blinks=1)
                self.highlight = None
            self.UpdateTimers()
        finally:
            self.reloading = False

    def UpdateTimersThread(self):
        while not self.destroyed:
            blue.pyos.synchro.SleepWallclock(1000)
            self.UpdateTimers()

    def OnContainerResized(self):
        self.height = self.container.height

    def OnConfirmButton(self, button, *args):
        if not self.reloading:
            self.DisableButtons()
            button.Enable()
            button.func = functools.partial(*args[:-1])
            button.SetLabel(
                localization.GetByLabel('UI/ActivateMultiTraining/Confirm'))
            uicore.animations.FadeTo(button.confirmHilite,
                                     button.confirmHilite.opacity,
                                     1.0,
                                     duration=0.3)
            self.Reload(self.CONFIRM_DELAY)

    def OnAddQueue(self, slot, trainingID, button):
        if not self.confirmed:
            self.confirmed = True
            self.highlight = slot
            self.Reload()
            try:
                sm.RemoteSvc('userSvc').ActivateMultiTraining(
                    self.itemID, trainingID=trainingID)
                self.Reload(force=True)
            except UserError:
                self.Close()
                raise

    def UpdateTimers(self):
        if self.expireDate1:
            timeRemaining = localization.formatters.FormatTimeIntervalShortWritten(
                self.expireDate1 - blue.os.GetWallclockTime(),
                showFrom='day',
                showTo='second')
            self.slot1Expiry.text = localization.GetByLabel(
                'UI/ActivateMultiTraining/Ends', timeRemaining=timeRemaining)
        else:
            self.slot1Expiry.text = ''
        if self.expireDate2:
            timeRemaining = localization.formatters.FormatTimeIntervalShortWritten(
                self.expireDate2 - blue.os.GetWallclockTime(),
                showFrom='day',
                showTo='second')
            self.slot2Expiry.text = localization.GetByLabel(
                'UI/ActivateMultiTraining/Ends', timeRemaining=timeRemaining)
        else:
            self.slot2Expiry.text = ''

    def DisableButtons(self):
        self.slot1Button.Disable()
        self.slot2Button.Disable()

    def EnableButtons(self):
        self.slot1Button.Enable()
        self.slot2Button.Enable()
示例#3
0
class StructureProfileSettingCont(Container):
    TAB_ID = 'PROFILE_SETTINGS'

    def ApplyAttributes(self, attributes):
        Container.ApplyAttributes(self, attributes)
        self.structureBrowserController = attributes.structureBrowserController
        self.allStructuresProfileController = attributes.allStructuresProfileController
        self.ChangeSignalConnection(connect=True)
        self.structureProfileController = None
        self.SetStructureProfileController()

    def ChangeSignalConnection(self, connect=True):
        signalAndCallback = [
            (self.structureBrowserController.on_profile_selected,
             self.OnProfileSelected)
        ]
        ChangeSignalConnect(signalAndCallback, connect)

    def SetStructureProfileController(self):
        profileID = self.structureBrowserController.GetSelectedProfileID()
        if profileID != ALL_PROFILES:
            slimController = self.allStructuresProfileController.GetSlimProfileController(
                profileID)
            if slimController is None:
                return
            self.ChangeSignalConnectionForProfileController(connect=False)
            self.structureProfileController = slimController.GetFullProfileController(
            )
            self.ChangeSignalConnectionForProfileController(connect=True)

    def ChangeSignalConnectionForProfileController(self, connect=True):
        if self.structureProfileController:
            signalAndCallback = [
                (self.structureProfileController.on_profile_value_changed,
                 self.OnProfileChanged)
            ]
            ChangeSignalConnect(signalAndCallback, connect)

    def OnTabSelect(self):
        profileID = self.structureBrowserController.GetSelectedProfileID()
        if self.structureProfileController is None:
            self.SetStructureProfileController()
        self.structureBrowserController.SetTabSelected(self.TAB_ID)
        self.ConstructUI()

    def ConstructUI(self, flush=True):
        self.Flush()
        profileID = self.structureBrowserController.GetSelectedProfileID()
        if profileID == ALL_PROFILES:
            return
        btnCont = Container(parent=self,
                            idx=0,
                            height=32,
                            align=uiconst.TOBOTTOM)
        LineThemeColored(parent=btnCont,
                         colorType=uiconst.COLORTYPE_UIHILIGHT,
                         align=uiconst.TOTOP)
        self.saveBtn = Button(parent=btnCont,
                              label=GetByLabel('UI/Common/Buttons/Save'),
                              func=self.SaveProfile,
                              align=uiconst.CENTERRIGHT)
        self.mainCont = Container(name='mainCont',
                                  parent=self,
                                  padding=(0, 4, 4, 4))
        self.categoryListCont = ServiceListCont(
            name='groupListCont',
            parent=self.mainCont,
            structureBrowserController=self.structureBrowserController)
        profileName = self.structureProfileController.GetProfileName()
        EveLabelLarge(text=profileName,
                      parent=self.mainCont,
                      state=uiconst.UI_DISABLED,
                      align=uiconst.TOTOP,
                      top=4,
                      padBottom=5)
        self.categoryCont = SettingsCategoryCont(
            name='categoryCont',
            parent=self.mainCont,
            padding=(0, 2, 0, 2),
            structureBrowserController=self.structureBrowserController,
            structureProfileController=self.structureProfileController)

    def SaveProfile(self, *args):
        settingsToSave = []
        scBySettingID = self.structureProfileController.GetAllGroups()
        groupIDsInSettings = set()
        for settingID, scSet in scBySettingID.iteritems():
            for eachSg in scSet:
                if eachSg.GetSettingType() == structures.SETTINGS_TYPE_BOOL:
                    groupID = NO_GROUP
                else:
                    groupID = eachSg.GetGroupID()
                    groupIDsInSettings.add(groupID)
                settingsToSave.append((settingID, eachSg.GetValue(), groupID))

        accessGroupsController = sm.GetService(
            'structureControllers').GetAccessGroupController()
        accessGroupsController.PopulatePublicGroupInfo(
            groupIDsInSettings, forceFetchInfoForPublicGroup=True)
        warning = False
        toSave = []
        for settingID, value, groupID in settingsToSave:
            if accessGroupsController.GetGroupInfoFromID(
                    groupID, fetchToServer=False) is None:
                warning = True
                continue
            toSave.append((settingID, value, groupID))

        if warning:
            eve.Message('CustomNotify', {
                'notify':
                GetByLabel('UI/StructureSettingWnd/AccessListNotValid')
            })
        oldHadChangedValue = self.structureBrowserController.HasProfileChanged(
        )
        try:
            self.structureBrowserController.SetProfileChangedValue(False)
            self.structureProfileController.SaveProfile(toSave)
        except Exception:
            self.structureBrowserController.SetProfileChangedValue(
                oldHadChangedValue)
            raise

    def OnProfileSelected(self, profileID):
        self.SetStructureProfileController()
        self.ConstructUI()

    def OnProfileChanged(self):
        self.saveBtn.Blink()
        self.structureBrowserController.SetProfileChangedValue(True)

    def Close(self):
        try:
            self.ChangeSignalConnection(connect=False)
            self.ChangeSignalConnectionForProfileController(connect=False)
        except Exception as e:
            log.LogError('Failed at closing structure setting wnd, e = ', e)
        finally:
            Container.Close(self)
class TutorialWindow(uicontrols.Window):
    __guid__ = 'form.TutorialWindow'
    default_windowID = 'aura9'
    default_width = 350
    default_height = 240
    defaultClipperHeight = 132
    default_iconNum = 'res:/UI/Texture/WindowIcons/tutorial.png'

    def ApplyAttributes(self, attributes):
        uicontrols.Window.ApplyAttributes(self, attributes)
        self.scope = 'all'
        self.sr.browser = uicontrols.Edit(parent=self.sr.main, padding=const.defaultPadding, readonly=1)
        self.sr.browser.HideBackground(True)
        self.sr.browser.AllowResizeUpdates(0)
        self.sr.browser.sr.window = self
        m = uicls.UtilMenu(menuAlign=uiconst.TOPRIGHT, parent=self.sr.topParent, align=uiconst.TOPRIGHT, left=const.defaultPadding, top=18, GetUtilMenu=self.SettingMenu, texturePath='res:/UI/Texture/Icons/73_16_50.png')
        self.nextFunc = attributes.nextFunc
        self.backFunc = attributes.backFunc
        self.onStartScalingWidth = None
        self.onStartScalingHeight = None
        self.constrainScreen = True
        if self.sr.stack is not None:
            self.sr.stack.RemoveWnd(self, 0, 0)
        if sm.GetService('tutorial').IsTutorialWindowUnkillable():
            self.MakeUnKillable()
            repairSysSkill = sm.GetService('skills').HasSkill(const.typeRepairSystems)
            shieldOpsSkill = sm.GetService('skills').HasSkill(const.typeShieldOperations)
            if repairSysSkill or shieldOpsSkill:
                self.MakeKillable()
        Sprite(parent=self.sr.topParent, name='mainicon', pos=(0, 2, 64, 64), texturePath='res:/UI/Texture/Icons/74_64_13.png')
        self.HideHeader()
        self.MakeUnstackable()
        self.SetMinSize([350, 220])
        self.imgpar = uiprimitives.Container(name='imgpar', parent=self.sr.main, align=uiconst.TOLEFT, width=64, idx=4, state=uiconst.UI_HIDDEN, clipChildren=1)
        imgparclipper = uiprimitives.Container(name='imgparclipper', parent=self.imgpar, align=uiconst.TOALL, left=5, top=5, width=5, height=5, clipChildren=1)
        self.img = uiprimitives.Sprite(parent=imgparclipper, align=uiconst.RELATIVE, left=1, top=1)
        self.bottomCont = uiprimitives.Container(name='bottom', parent=self.sr.maincontainer, align=uiconst.TOBOTTOM, height=32, idx=0)
        self.backBtn = Button(parent=self.bottomCont, label=localization.GetByLabel('UI/Commands/Back'), name='tutorialBackBtn', func=self.backFunc, align=uiconst.TOLEFT, padding=(8, 0, 0, 6))
        self.nextBtn = Button(parent=self.bottomCont, label=localization.GetByLabel('UI/Commands/Next'), name='tutorialNextBtn', func=self.nextFunc, align=uiconst.TORIGHT, padding=(0, 0, 8, 6), btn_default=1)
        self.Confirm = self.nextFunc
        self.sr.text = uicontrols.EveLabelMedium(text='', parent=self.bottomCont, state=uiconst.UI_DISABLED, align=uiconst.CENTER)
        top = self.tTop = uiprimitives.Container(name='tTop', parent=self.sr.topParent, align=uiconst.TOALL, padding=(64, 0, 24, 0), idx=0)
        self.captionText = uicontrols.EveLabelLarge(text='', parent=top, align=uiconst.TOTOP, top=10, state=uiconst.UI_DISABLED)
        self.captionText.OnSizeChanged = self.CheckTopHeight
        self.subcaption = uicontrols.Label(text='', parent=top, align=uiconst.TOTOP, state=uiconst.UI_DISABLED, fontsize=18, color=TutorialColor.HINT_FRAME)
        self.sr.browser.AllowResizeUpdates(1)
        self.SetParent(uicore.layer.abovemain)
        uicore.animations.SpSwoopBlink(self.blinkFill, rotation=math.pi - 0.5, duration=3.0, loops=TutorialConstants.NUM_BLINKS)
        uicore.animations.SpSwoopBlink(self.blinkBorder, rotation=math.pi - 0.5, duration=3.0, loops=TutorialConstants.NUM_BLINKS)

    def Prepare_Background_(self):
        self.sr.underlay = WindowUnderlay(parent=self, name='underlay', state=uiconst.UI_DISABLED)
        self.blinkFill = uiprimitives.Sprite(bgParent=self.sr.underlay, name='blinkFill', texturePath='res:/UI/Texture/classes/Tutorial/fill_no_border.png', state=uiconst.UI_DISABLED, color=(1, 1, 1, 0.5))
        self.blinkBorder = uiprimitives.Sprite(bgParent=self.sr.underlay, name='blinkBorder', texturePath='res:/UI/Texture/classes/Tutorial/border.png', state=uiconst.UI_DISABLED, color=TutorialColor.HINT_FRAME)
        uicontrols.Frame(name='frame', bgParent=self.sr.underlay, color=TutorialColor.WINDOW_FRAME, frameConst=uiconst.FRAME_BORDER1_CORNER0)
        BlurredSceneUnderlay(bgParent=self.sr.underlay)

    def RegisterPositionAndSize(self, key = None, windowID = None):
        uicontrols.Window.RegisterPositionAndSize(self, key, windowID)
        self.currentBottom = self.top + self.height

    def SettingMenu(self, menuParent):
        shouldAutoReszie = settings.char.windows.Get('tutorialShouldAutoReszie', 1)
        menuParent.AddCheckBox(text=localization.GetByLabel('UI/Tutorial/AutoResizeTutorialWindow'), checked=shouldAutoReszie, callback=(self.ChangeAutoResize, not shouldAutoReszie))
        menuParent.AddDivider()
        menuParent.AddIconEntry(icon='res:/UI/Texture/Icons/38_16_190.png', text=localization.GetByLabel('UI/Tutorial/OpenTutorialsMenu'), callback=self.OpenTutorialList)

    def OpenTutorialList(self):
        wnd = HelpWindow.GetIfOpen()
        if wnd:
            wnd.Maximize()
            wnd.sr.mainTabs.ShowPanelByName(localization.GetByLabel('UI/Help/Tutorials'))
        else:
            wnd = HelpWindow.Open(showPanel=localization.GetByLabel('UI/Help/Tutorials'))

    def ChangeAutoResize(self, shouldAutoReszie):
        settings.char.windows.Set('tutorialShouldAutoReszie', shouldAutoReszie)
        self.CheckHeight()

    def ToggleMinimize(self):
        """
        The method is overridden for the tutorial window since it lives in a different layer and can't stack
        If window is minimized, maximize it
        If window is not minimized and in front, minimize it
        """
        if not self.IsMinimizable():
            return
        if self.IsMinimized():
            self.Maximize()
        else:
            self.Minimize()

    @staticmethod
    def default_top(*args):
        return uicore.desktop.height - 270

    @staticmethod
    def default_left(*args):
        leftpush, rightpush = uicontrols.Window.GetSideOffset()
        return uicore.desktop.width - 360 - rightpush

    def _OnClose(self, *args):
        uthread.new(sm.GetService('tutorial').Cleanup)

    def OnStartScale_(self, wnd, *args):
        self.onStartScalingWidth = self.width
        self.onStartScalingHeight = self.height

    def OnEndScale_(self, wnd, *args):
        uicontrols.Window.OnEndScale_(self, wnd, *args)
        if abs(self.onStartScalingHeight - self.height) > 5 or abs(self.onStartScalingWidth - self.width) > 5:
            settings.char.windows.Set('tutorialShouldAutoReszie', 0)

    def CloseByUser(self, *args):
        tutorialSvc = sm.GetService('tutorial')
        tut = tutorialSvc.GetCurrentTutorial()
        if tut is not None:
            if hasattr(self, 'startTime'):
                totaltime = (blue.os.GetWallclockTime() - self.startTime) / const.SEC
            else:
                totaltime = 0
            timeSpentInPage = (blue.os.GetWallclockTime() - tutorialSvc.pageTime) / const.SEC
            try:
                numClicks = uicore.uilib.GetGlobalClickCount() - tutorialSvc.numMouseClicks
                numKeys = uicore.uilib.GetGlobalKeyDownCount() - tutorialSvc.numKeyboardClicks
            except:
                numClicks = numKeys = 0

            if not getattr(self, 'done', 0):
                tutorialSvc.SetSequenceStatus(tut.sequenceID, tut.tutorialID, tut.pageNo, 'aborted')
                with util.ExceptionEater('eventLog'):
                    tutorialSvc.LogTutorialEvent(['tutorialID',
                     'pageNo',
                     'sequenceID',
                     'timeInTutorial',
                     'numMouseClicks',
                     'numKeyboardClicks',
                     'reason',
                     'timeInPage'], 'Closed', tut.tutorialID, tut.pageNo, tut.sequenceID, totaltime, numClicks, numKeys, 'aborted', timeSpentInPage)
            else:
                tutorialSvc.SetSequenceStatus(tut.sequenceID, tut.tutorialID, tut.pageNo, 'done')
                with util.ExceptionEater('eventLog'):
                    tutorialSvc.LogTutorialEvent(['tutorialID',
                     'pageNo',
                     'sequenceID',
                     'timeInTutorial',
                     'numMouseClicks',
                     'numKeyboardClicks',
                     'reason',
                     'timeInPage'], 'Closed', tut.tutorialID, tut.pageNo, tut.sequenceID, totaltime, numClicks, numKeys, 'done', timeSpentInPage)
        tutorialSvc.Cleanup()
        if settings.char.windows.Get('tutorialShouldAutoReszie', 1):
            self.display = False
            cw, currentClipperHeight = self.sr.browser.sr.clipper.GetAbsoluteSize()
            self.ChangeWindowHeight(currentClipperHeight, self.defaultClipperHeight)
            self.RegisterPositionAndSize()
        self.Close()
        if getattr(self, 'showTutorialReminder', True):
            uthread.new(self._TutorialReminder)

    def _TutorialReminder(self):
        blue.pyos.synchro.SleepWallclock(1000)
        tutorialSvc = sm.GetService('tutorial')
        tutorialSvc.uipointerSvc.PointTo('neocom.tutorial', localization.GetByLabel('UI/Tutorial/ResumeTutorialPointer'))
        blue.pyos.synchro.SleepWallclock(RESUME_TUTORIAL_HINT_DURATION_SEC * 1000)
        browser = tutorialSvc.GetTutorialBrowser(create=False)
        if browser is None:
            tutorialSvc.uipointerSvc.ClearPointers()

    def CheckTopHeight(self):
        h = 0
        for each in self.tTop.children:
            if each.state != uiconst.UI_HIDDEN:
                h += each.height + each.top

        self.SetTopparentHeight(max(74, h))

    def CheckHeight(self, *args):
        browser = self.sr.browser
        shouldAutoReszie = settings.char.windows.Get('tutorialShouldAutoReszie', 1)
        if shouldAutoReszie:
            cw, currentClipperHeight = browser.sr.clipper.GetCurrentAbsoluteSize()
            if not currentClipperHeight:
                cw, currentClipperHeight = browser.sr.clipper.GetAbsoluteSize()
            contentHeight = browser.GetContentHeight()
            self.ChangeWindowHeight(currentClipperHeight, contentHeight + 10)
            browser.scrollEnabled = 1
        else:
            browser.scrollEnabled = 1
            uthread.new(self.ShowScrollControlIfNeeded)

    def ChangeWindowHeight(self, currentClipperHeight, contentHeight):
        if self.defaultClipperHeight is None:
            return
        if self.defaultClipperHeight > contentHeight and self.defaultClipperHeight <= currentClipperHeight:
            diff = currentClipperHeight - self.defaultClipperHeight
            uicore.animations.MorphScalar(self, 'height', startVal=self.height, endVal=self.height - diff, duration=0.2, loops=1, curveType=2, callback=None, sleep=False)
            uicore.animations.MorphScalar(self, 'top', startVal=self.top, endVal=max(self.top + diff, 0), duration=0.2, loops=1, curveType=2, callback=None, sleep=True)
        else:
            diff = currentClipperHeight - max(contentHeight, self.defaultClipperHeight)
            uicore.animations.MorphScalar(self, 'height', startVal=self.height, endVal=self.height - diff, duration=0.2, loops=1, curveType=2, callback=None, sleep=False)
            uicore.animations.MorphScalar(self, 'top', startVal=self.top, endVal=max(self.top + diff, 0), duration=0.2, loops=1, curveType=2, callback=None, sleep=True)

    def ShowScrollControlIfNeeded(self, *args):
        if self.sr.browser.scrollingRange:
            self.sr.browser.sr.scrollcontrols.state = uiconst.UI_NORMAL

    def LoadImage(self, imagePath):
        if not blue.ResFile().Open(imagePath):
            log.LogError('Image not found in res:', imagePath)
            return
        texture, tWidth, tHeight, bw, bh = sm.GetService('photo').GetTextureFromURL(imagePath, sizeonly=1, dontcache=1)
        self.img.state = uiconst.UI_NORMAL
        self.img.SetTexturePath(imagePath)
        self.img.width = tWidth
        self.img.height = tHeight
        self.imgpar.width = min(128, tWidth) + self.img.left + 5
        if self.imgpar.state != uiconst.UI_DISABLED:
            self.imgpar.state = uiconst.UI_DISABLED

    def LoadAndGiveGoodies(self, goodies, tutorialID, pageID, pageNo):
        goodieHtml = ''
        if len(goodies) != 0:
            if goodies[0] == -1:
                goodieHtml += '\n                        <br>\n                        <font size=12>%s</font>\n                        <br><br>\n                ' % localization.GetByLabel('UI/Tutorial/TutorialGoodie/AlreadyReceived')
                return goodieHtml
            for goodie in goodies:
                invtype = cfg.invtypes.Get(goodie.invTypeID)
                goodieHtml += '\n                        <hr>\n                        <p>\n                        <img style=margin-right:0;margin-bottom:0 src="typeicon:typeID=%s&bumped=1&showFitting=0" align=left>\n                        <font size=20 margin-left=20>%s</font>\n                        <a href=showinfo:%s><img style:vertical-align:bottom src="icon:38_208" size=16 alt="%s"></a>\n                        <br><br>\n                        </p>\n                    ' % (goodie.invTypeID,
                 invtype.typeName,
                 goodie.invTypeID,
                 localization.GetByLabel('UI/Commands/ShowInfo'))

            sm.GetService('tutorial').GiveGoodies(tutorialID, pageID, pageNo)
            return goodieHtml

    def LoadTutorial(self, tutorialID = None, pageNo = None, pageID = None, sequenceID = None, force = 0, VID = None, skipCriteria = False, checkBack = 0, diffMouseClicks = 0, diffKeyboardClicks = 0):
        self.sr.browser.scrollEnabled = 0
        self.backBtn.state = uiconst.UI_HIDDEN
        self.nextBtn.state = uiconst.UI_HIDDEN
        self.backBtn.Blink(0)
        self.nextBtn.Blink(0)
        self.sr.text.text = ''
        self.done = 0
        self.reverseBack = 0
        imagePath = None
        pageCount = None
        body = '\n            <html>\n            <head>\n            <LINK REL="stylesheet" TYPE="text/css" HREF="res:/ui/css/tutorial.css">\n            </head>\n            <body>'
        tutData = None
        if VID:
            tutData = sm.RemoteSvc('tutorialSvc').GetTutorialInfo(VID)
        elif tutorialID:
            tutData = sm.GetService('tutorial').GetTutorialInfo(tutorialID)
        if tutData:
            fadeOut = uicore.animations.FadeOut(self.sr.browser.sr.clipper, duration=0.05, loops=1, curveType=2, callback=None, sleep=False)
            if self and self.destroyed:
                return
            pageCount = len(tutData.pages)
            if pageNo == -1:
                pageNo = pageCount
            else:
                pageNo = pageNo or 1
            if pageNo > pageCount:
                log.LogWarn('Open Tutorial Page Failed:, have page %s but max %s pages. falling back to page 1 :: tutorialID: %s, sequenceID: %s, VID: %s' % (pageNo,
                 pageCount,
                 tutorialID,
                 sequenceID,
                 VID))
                pageNo = 1
            with util.ExceptionEater('eventLog'):
                sm.GetService('tutorial').LogTutorialEvent(['tutorialID', 'pageNo', 'openedByUser'], 'OpenTutorial', tutorialID, pageNo, force)
            dispPageNo, dispPageCount = pageNo, pageCount
            pageData = tutData.pages[pageNo - 1]
            caption = self.captionText
            loop = 1
            while 1:
                captionTextParts = localization.GetByMessageID(tutData.tutorial[0].tutorialNameID).split(':')
                if len(captionTextParts) > 1:
                    tutorialNumber = captionTextParts[0]
                    rest = ':'.join(captionTextParts[1:]).strip()
                    captionText = '%s: %s' % (tutorialNumber, rest)
                else:
                    captionText = localization.GetByMessageID(tutData.tutorial[0].tutorialNameID)
                caption.text = captionText
                if pageData and pageData.pageNameID:
                    self.subcaption.text = localization.GetByMessageID(pageData.pageNameID)
                    self.subcaption.state = uiconst.UI_DISABLED
                else:
                    self.subcaption.state = uiconst.UI_HIDDEN
                if caption.textheight < 52 or not loop:
                    break
                caption.fontsize = 13
                caption.letterspace = 0
                caption.last = (0, 0)
                loop = 0

            if sequenceID:
                check = []
                seqTutData = sm.GetService('tutorial').GetTutorialInfo(tutorialID)
                for criteria in seqTutData.criterias:
                    cd = sm.GetService('tutorial').GetCriteria(criteria.criteriaID)
                    if cd is None:
                        continue
                    check.append(criteria)

                closeToEnd = 0
                for criteria in seqTutData.pagecriterias:
                    if criteria.pageID == pageData.pageID:
                        check.append(criteria)
                        closeToEnd = 1
                    elif not closeToEnd:
                        cd = sm.GetService('tutorial').GetCriteria(criteria.criteriaID)
                        if cd is None:
                            continue
                        if not cd.criteriaName.startswith('rookieState'):
                            continue
                        check.append(criteria)

                actionData = seqTutData.actions
                pageActionData = seqTutData.pageactions
            else:
                check = [ c for c in tutData.criterias ]
                for criteria in tutData.pagecriterias:
                    if criteria.pageID == pageData.pageID:
                        check.append(criteria)

                actionData = tutData.actions
                pageActionData = tutData.pageactions
            actions = [ sm.GetService('tutorial').GetAction(action.actionID) for action in actionData ]
            actions += [ sm.GetService('tutorial').GetAction(action.actionID) for action in pageActionData if action.pageID == pageData.pageID ]
            preRookieState = eve.rookieState
            if skipCriteria:
                criteriaCheck = None
            else:
                criteriaCheck = sm.GetService('tutorial').ParseCriterias(check, 'tut', self, tutorialID)
            if not self or getattr(self, 'sr', None) is None:
                return
            if criteriaCheck:
                if preRookieState:
                    eve.SetRookieState(preRookieState)
                body += '<br>' + localization.GetByMessageID(criteriaCheck.messageTextID)
                with util.ExceptionEater('eventLog'):
                    sm.GetService('tutorial').LogTutorialEvent(['tutorialID',
                     'pageNo',
                     'sequenceID',
                     'clickedByUser',
                     'errorMessageID',
                     'numMouseClicks',
                     'numKeyboardClicks'], 'CriteriaNotMet', tutorialID, pageNo, sequenceID, force, criteriaCheck.messageTextID, diffMouseClicks, diffKeyboardClicks)
                if pageNo > 1 or sequenceID and sm.GetService('tutorial').GetNextInSequence(tutorialID, sequenceID, -1):
                    self.backBtn.state = uiconst.UI_NORMAL
                if sm.GetService('tutorial').waitingForWarpConfirm == False:
                    self.nextBtn.state = uiconst.UI_NORMAL
                    self.nextBtn.OnClick = sm.GetService('tutorial').Reload
                    self.Confirm = sm.GetService('tutorial').Reload
                    self.nextBtn.SetLabel(localization.GetByLabel('UI/Commands/Next'))
                    self.sr.text.text = ''
                self.backBtn.OnClick = self.backFunc
                if checkBack:
                    self.reverseBack = 1
            else:
                sm.GetService('tutorial').ParseActions(actions)
                self.sr.text.text = localization.GetByLabel('UI/Tutorial/PageOf', num=dispPageNo, total=dispPageCount)
                if pageNo > 1 or sequenceID and sm.GetService('tutorial').GetNextInSequence(tutorialID, sequenceID, -1):
                    self.backBtn.state = uiconst.UI_NORMAL
                sm.GetService('tutorial').SetCriterias(check)
                if pageData:
                    page = pageData
                    body += '%s' % localization.GetByMessageID(page.textID)
                    self.nextBtn.state = uiconst.UI_NORMAL
                    self.nextBtn.OnClick = self.nextFunc
                    self.Confirm = self.nextFunc
                    self.backBtn.OnClick = self.backFunc
                    if pageNo < pageCount or sequenceID and sm.GetService('tutorial').GetNextInSequence(tutorialID, sequenceID):
                        self.nextBtn.SetLabel(localization.GetByLabel('UI/Commands/Next'))
                    else:
                        self.nextBtn.SetLabel(localization.GetByLabel('UI/Commands/Done'))
                        self.done = 1
                    imagePath = page.imagePath
                else:
                    body += '\n                        Page %s was not found in this tutorial.\n                        ' % pageNo
        else:
            self.captionText.text = localization.GetByLabel('UI/Tutorial/EveTutorials')
            body = '%s %s' % (localization.GetByLabel('UI/Tutorial/UnknownTutorial'), tutorialID)
        body += '</body></html>'
        blue.pyos.synchro.Yield()
        self.CheckTopHeight()
        self.LoadHTML('', newThread=0)
        if self.state == uiconst.UI_HIDDEN:
            self.Maximize()
        if imagePath:
            self.LoadImage(imagePath)
            self.sr.browser.left = self.img.width
        else:
            if self.imgpar.state != uiconst.UI_HIDDEN:
                self.imgpar.state = uiconst.UI_HIDDEN
            self.sr.browser.left = const.defaultPadding
        blue.pyos.synchro.Yield()
        goodies = sm.RemoteSvc('tutorialLocationSvc').GetTutorialGoodies(tutorialID, pageID, pageNo)
        goodieHtml = self.LoadAndGiveGoodies(goodies, tutorialID, pageID, pageNo)
        if goodieHtml:
            body += '<br>%s' % goodieHtml
        self.LoadHTML(body, newThread=0)
        self.SetCaption(localization.GetByLabel('UI/Tutorial/EveTutorials'))
        if not hasattr(self, 'startTime') or not hasattr(self, 'current') or self.current.sequenceID != sequenceID:
            self.startTime = blue.os.GetWallclockTime()
        tutorialPageState = TutorialPageState(tutorialID, pageNo, pageID, pageCount, sequenceID, VID, pageData.pageActionID)
        settings.char.generic.Set('tutorialPageState', tuple(tutorialPageState))
        settings.char.generic.Delete('tutorialCompleted')
        self.current = tutorialPageState
        if sequenceID:
            if self.done:
                sm.GetService('tutorial').SetSequenceStatus(sequenceID, tutorialID, pageNo, 'done')
            else:
                sm.GetService('tutorial').SetSequenceStatus(sequenceID, tutorialID, pageNo)
            if not sm.GetService('tutorial').CheckTutorialDone(sequenceID, tutorialID):
                sm.GetService('tutorial').SetSequenceDoneStatus(sequenceID, tutorialID, pageNo)
        for page in tutData.pages:
            if page.pageID == pageID or page.pageNumber == pageNo:
                if not criteriaCheck:
                    translatedText = localization.GetByMessageID(page.uiPointerTextID)
                    sm.GetService('uipointerSvc').PointTo(page.uiPointerID, translatedText)
                    break

        fadeOut.Stop()
        uicore.animations.FadeIn(self.sr.browser.sr.clipper, endVal=1.0, duration=0.3, loops=1, curveType=2, callback=None, sleep=False)
        self.CheckHeight()

    def LoadHTML(self, html, newThread = 1):
        self.ShowLoad()
        self.sr.browser.LoadHTML(html, newThread=newThread)

    def LoadEnd(self):
        self.HideLoad()

    def Reload(self, forced = 1, *args):
        if not self.sr.browser:
            return
        uthread.new(self.sr.browser.LoadHTML, None, scrollTo=self.sr.browser.GetScrollProportion())