Esempio n. 1
0
class SinglelineEditCore(Container):
    __guid__ = 'uicontrols.SinglelineEditCore'
    default_name = 'edit_singleline'
    default_align = uiconst.TOTOP
    default_width = 100
    default_height = 20
    default_state = uiconst.UI_NORMAL
    default_maxLength = None
    default_label = ''
    default_setvalue = ''
    default_hinttext = ''
    default_passwordCharacter = None
    default_autoselect = False
    default_adjustWidth = False
    default_dynamicHistoryWidth = False
    default_readonly = False
    default_OnChange = None
    default_OnSetFocus = None
    default_OnFocusLost = None
    default_OnReturn = None
    default_OnAnyChar = None
    default_OnInsert = None
    default_fontsize = None
    default_fontStyle = None
    default_fontFamily = None
    default_fontPath = None
    TEXTLEFTMARGIN = 4
    TEXTRIGHTMARGIN = 4
    registerHistory = True

    def ApplyAttributes(self, attributes):
        if self.default_fontsize is None:
            self.default_fontsize = fontConst.DEFAULT_FONTSIZE
        self.DECIMAL = '.'
        Container.ApplyAttributes(self, attributes)
        self.rightAlignedButtons = weakref.WeakSet()
        self._clearButton = None
        self.hinttext = ''
        self.isTabStop = 1
        self.integermode = None
        self.floatmode = None
        self.passwordchar = None
        self.caretIndex = (0, 0)
        self.selFrom = None
        self.selTo = None
        self.value = None
        self.text = ''
        self.suffix = ''
        self.maxletters = None
        self.historyMenu = None
        self.historySaveLast = None
        self.displayHistory = False
        self.allowHistoryInnerMatches = False
        self.maxHistoryShown = 5
        self.numericControlsCont = None
        self.updateNumericInputThread = None
        self.draggedValue = None
        self.OnChange = None
        self.OnFocusLost = None
        self.OnReturn = None
        self.OnInsert = None
        self.readonly = attributes.get('readonly', self.default_readonly)
        self.fontStyle = attributes.get('fontStyle', self.default_fontStyle)
        self.fontFamily = attributes.get('fontFamily', self.default_fontFamily)
        self.fontPath = attributes.get('fontPath', self.default_fontPath)
        self.fontsize = attributes.get('fontsize', self.default_fontsize)
        self._textClipper = Container(name='_textClipper', parent=self, clipChildren=True, padding=(1, 0, 1, 0))
        self._textClipper._OnSizeChange_NoBlock = self.OnClipperSizeChange
        self.Prepare_()
        self.autoselect = attributes.get('autoselect', self.default_autoselect)
        self.adjustWidth = attributes.get('adjustWidth', self.default_adjustWidth)
        self.dynamicHistoryWidth = attributes.get('dynamicHistoryWidth', self.default_dynamicHistoryWidth)
        self.sr.text.shadow = self.sr.hinttext.shadow = attributes.get('shadow', None)
        fontcolor = attributes.get('fontcolor', (1.0, 1.0, 1.0, 1.0))
        if fontcolor is not None:
            self.SetTextColor(fontcolor)
        if attributes.get('ints', None):
            self.IntMode(*attributes.ints)
        elif attributes.get('floats', None):
            self.FloatMode(*attributes.floats)
        self.SetPasswordChar(attributes.get('passwordCharacter', self.default_passwordCharacter))
        self.SetMaxLength(attributes.get('maxLength', self.default_maxLength))
        self.SetLabel(attributes.get('label', self.default_label))
        self.SetHintText(attributes.get('hinttext', self.default_hinttext))
        self.SetValue(attributes.get('setvalue', self.default_setvalue))
        self.height = 20
        self.OnChange = attributes.get('OnChange', self.default_OnChange)
        self.__OnSetFocus = attributes.get('OnSetFocus', self.default_OnSetFocus)
        self.OnFocusLost = attributes.get('OnFocusLost', self.default_OnFocusLost)
        self.OnReturn = attributes.get('OnReturn', self.default_OnReturn)
        self.OnInsert = attributes.get('OnInsert', self.default_OnInsert)
        OnAnyChar = attributes.get('OnAnyChar', self.default_OnAnyChar)
        if OnAnyChar:
            self.OnAnyChar = OnAnyChar
        uicore.event.RegisterForTriuiEvents(uiconst.UI_MOUSEDOWN, self.OnGlobalMouseDownCallback)

    def OnGlobalMouseDownCallback(self, uiObject, *args, **kwds):
        if self.destroyed:
            return False
        if uiObject is not self and self.historyMenu:
            historyMenu = self.historyMenu()
            if historyMenu and not historyMenu.destroyed:
                if not uiObject.IsUnder(historyMenu):
                    self.CloseHistoryMenu()
        return True

    def Prepare_(self):
        self.Prepare_Background_()
        self.sr.text = Label(text='', parent=self._textClipper, name='value', align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, maxLines=1, left=self.TEXTLEFTMARGIN, fontStyle=self.fontStyle, fontFamily=self.fontFamily, fontPath=self.fontPath, fontsize=self.fontsize)
        self.sr.hinttext = Label(text='', parent=self._textClipper, name='hinttext', align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, maxLines=1, left=self.TEXTLEFTMARGIN, fontStyle=self.fontStyle, fontFamily=self.fontFamily, fontPath=self.fontPath, fontsize=self.fontsize)

    def SetHintText(self, hint):
        self.hinttext = hint
        self.CheckHintText()

    def AutoFitToText(self, text = None, minWidth = None):
        if self.align in (uiconst.TOTOP, uiconst.TOBOTTOM, uiconst.TOALL):
            raise RuntimeError('Incorrect alignment for SingleLine.AutoFitToText')
        if text is not None:
            textwidth, textheight = self.sr.text.MeasureTextSize(text)
            autoWidth = textwidth + self.TEXTLEFTMARGIN * 2 + 2
        else:
            autoWidth = self.sr.text.textwidth + self.TEXTLEFTMARGIN * 2 + 2
        if minWidth:
            autoWidth = max(minWidth, autoWidth)
        self.width = autoWidth
        self.sr.text.left = self.TEXTLEFTMARGIN

    def CheckHintText(self):
        if self.GetText():
            self.sr.hinttext.display = False
        else:
            self.sr.hinttext.display = True
        self.sr.hinttext.text = self.hinttext

    def SetTextColor(self, color):
        self.sr.text.SetRGB(*color)
        self.sr.hinttext.SetRGB(*color)
        self.sr.hinttext.SetAlpha(self.sr.hinttext.GetAlpha() * 0.5)

    def Prepare_Background_(self):
        if not self.sr.underlay:
            self.sr.underlay = Frame(name='__underlay', frameConst=('ui_1_16_161', 7, -2), bgParent=self)

    def Prepare_Caret_(self):
        self.sr.caret = Fill(parent=self._textClipper, name='caret', align=uiconst.TOPLEFT, color=(1.0, 1.0, 1.0, 0.75), pos=(self.TEXTLEFTMARGIN,
         1,
         1,
         1), idx=0, state=uiconst.UI_HIDDEN)

    def ShowClearButton(self, icon = None, hint = None, showOnLetterCount = 1):
        if self._clearButton:
            self._clearButton.Close()
        icon = icon or 'res:/UI/Texture/Icons/73_16_210.png'
        clearButton = self.AddIconButton(icon, hint)
        clearButton.OnClick = self.OnClearButtonClick
        clearButton.Hide()
        clearButton._showOnLetterCount = showOnLetterCount
        self._clearButton = clearButton
        return clearButton

    def OnClearButtonClick(self):
        self.SetValue(u'')

    def AddIconButton(self, texturePath, hint = None):
        from eve.client.script.ui.control.buttons import ButtonIcon
        rightAlignedButton = ButtonIcon(texturePath=texturePath, pos=(0, 0, 16, 16), align=uiconst.CENTERRIGHT, parent=self, hint=hint, idx=0)
        self.rightAlignedButtons.add(rightAlignedButton)
        return rightAlignedButton

    def RefreshTextClipper(self):
        if self._clearButton:
            if len(self.text) >= self._clearButton._showOnLetterCount:
                self._clearButton.Show()
            else:
                self._clearButton.Hide()
        padRight = 1
        iconLeft = 1
        for each in self.rightAlignedButtons:
            if not each.destroyed and each.display:
                padRight += each.width
                each.left = iconLeft
                iconLeft += each.width

        self._textClipper.padRight = padRight
        w, h = self._textClipper.GetAbsoluteSize()
        self.sr.text.SetRightAlphaFade(-self.sr.text.left + w - 3, 8)

    def OnClipperSizeChange(self, newWidth, newHeight):
        if newWidth:
            self.RefreshCaretPosition()
            self.RefreshSelectionDisplay()
            self.RefreshTextClipper()

    def LoadCombo(self, id, options, callback = None, setvalue = None, comboIsTabStop = 1):
        for each in self.children[:]:
            if each.name == 'combo':
                each.Close()

        combo = Combo(parent=self, label='', options=options, name=id, select=setvalue, callback=self.OnComboChange, pos=(0, 0, 16, 16), align=uiconst.BOTTOMRIGHT)
        combo.sr.inputCallback = callback
        combo.isTabStop = comboIsTabStop
        combo.name = 'combo'
        combo.Confirm = self.ComboConfirm
        combo.Hide()
        self.sr.combo = combo
        comboButton = self.AddIconButton('res:/UI/Texture/Icons/38_16_229.png')
        comboButton.name = 'combo'
        comboButton.OnMouseDown = (self.ExpandCombo, combo)

    def ExpandCombo(self, combo, *args, **kwds):
        if not combo._Expanded():
            uthread.new(combo.Expand, self.GetAbsolute())

    def ComboConfirm(self, *args):
        if self.sr.combo and not self.sr.combo.destroyed:
            self.OnComboChange(self.sr.combo, self.sr.combo.GetKey(), self.sr.combo.GetValue())
        self.sr.combo.Cleanup(setfocus=0)

    def OnUp(self, *args):
        if self.sr.combo:
            if not self.sr.combo._Expanded():
                uthread.new(self.sr.combo.Expand, self.GetAbsolute())
            else:
                self.sr.combo.OnUp()

    def OnDown(self, *args):
        if self.sr.combo:
            if not self.sr.combo._Expanded():
                uthread.new(self.sr.combo.Expand, self.GetAbsolute())
            else:
                self.sr.combo.OnDown()

    def GetComboValue(self):
        if self.sr.combo:
            return self.sr.combo.GetValue()

    def OnComboChange(self, combo, label, value, *args):
        self.SetValue(label, updateIndex=0)
        if combo.sr.inputCallback:
            combo.sr.inputCallback(combo, label, value)

    def ClearHistory(self, *args):
        id, mine, all = self.GetHistory(getAll=1)
        if id in all:
            del all[id]
            settings.user.ui.Set('editHistory', all)

    def RegisterHistory(self, value = None):
        if self.integermode or self.floatmode or self.passwordchar is not None or not self.registerHistory:
            return
        id, mine, all = self.GetHistory(getAll=1)
        current = (value or self.GetValue(registerHistory=0)).rstrip()
        if current not in mine:
            mine.append(current)
        all[id] = mine
        settings.user.ui.Set('editHistory', all)

    def CheckHistory(self):
        if self.integermode or self.floatmode or self.passwordchar is not None or self.displayHistory == 0:
            return
        if self.readonly:
            return
        valid = self.GetValid()
        if valid:
            self.ShowHistoryMenu(valid[:5])
            return 1
        self.CloseHistoryMenu()
        return 0

    def GetValid(self):
        current = self.GetValue(registerHistory=0)
        id, mine = self.GetHistory()
        valid = [ each for each in mine if each.lower().startswith(current.lower()) and each != current ]
        valid.sort(key=lambda x: len(x))
        return valid

    def ShowHistoryMenu(self, history):
        hadMenu = 0
        if self.historyMenu and self.historyMenu():
            hadMenu = 1
        self.CloseHistoryMenu()
        if not history:
            return
        l, t, w, h = self.GetAbsolute()
        mp = Container(name='historyMenuParent', parent=uicore.layer.menu, pos=(l,
         t + h + 2,
         w,
         0), align=uiconst.TOPLEFT)
        if not hadMenu:
            mp.opacity = 0.0
        Frame(parent=mp, frameConst=uiconst.FRAME_BORDER1_CORNER0, color=(1.0, 1.0, 1.0, 0.2))
        Frame(parent=mp, frameConst=uiconst.FRAME_FILLED_CORNER0, color=(0.0, 0.0, 0.0, 0.75))
        mps = Container(name='historyMenuSub', parent=mp, idx=0)
        self.PopulateHistoryMenu(mps, mp, history)
        mp.sr.entries = mps
        self.historyMenu = weakref.ref(mp)
        if not hadMenu:
            uicore.effect.MorphUI(mp, 'opacity', 1.0, 250.0, float=1)

    def PopulateHistoryMenu(self, mps, mp, history):
        for entry in history:
            displayText, editText = entry if isinstance(entry, tuple) else (entry, entry)
            self.GetHistoryMenuEntry(displayText, editText, mps, mp)

    def GetHistoryMenuEntry(self, displayText, text, menuSub, mp, info = None):
        ep = Container(name='entryParent', parent=menuSub, clipChildren=1, pos=(0, 0, 0, 16), align=uiconst.TOTOP, state=uiconst.UI_NORMAL)
        ep.OnMouseEnter = (self.HEMouseEnter, ep)
        ep.OnMouseDown = (self.HEMouseDown, ep)
        ep.OnMouseUp = (self.HEMouseUp, ep)
        Line(parent=ep, align=uiconst.TOBOTTOM)
        t = Label(text=displayText, parent=ep, left=6, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED)
        ep.height = t.textheight + 4
        ep.sr.hilite = Fill(parent=ep, color=(1.0, 1.0, 1.0, 0.25), pos=(1, 1, 1, 1), state=uiconst.UI_HIDDEN)
        ep.selected = 0
        ep.sr.menu = mp
        ep.string = text
        mp.height += ep.height
        if self.dynamicHistoryWidth:
            mp.width = max(mp.width, t.width + 12)
        ep.info = info

    def HEMouseDown(self, entry, mouseButton, *args):
        if mouseButton == uiconst.MOUSELEFT:
            self.SetValue(entry.string, updateIndex=1)
            self.OnHistoryClick(entry.string)

    def HEMouseUp(self, entry, mouseButton, *args):
        if mouseButton == uiconst.MOUSELEFT:
            self.CloseHistoryMenu()

    def HEMouseEnter(self, entry, *args):
        if not (self.historyMenu and self.historyMenu()):
            return
        hm = self.historyMenu()
        for _entry in hm.sr.entries.children:
            _entry.sr.hilite.state = uiconst.UI_HIDDEN
            _entry.selected = 0

        entry.sr.hilite.state = uiconst.UI_DISABLED
        entry.selected = 1

    def GetHistory(self, getAll = 0):
        id = self.GetHistoryID()
        all = settings.user.ui.Get('editHistory', {})
        if type(all) == list:
            log.LogError('Singlelineedit error: all:', all)
            log.LogTraceback('Singlelineedit error: all: %s' % all, severity=log.LGERR)
            settings.user.ui.Delete('editHistory')
            all = {}
        if getAll:
            return (id, all.get(id, []), all)
        return (id, all.get(id, []))

    def OnHistoryClick(self, clickedString, *args):
        pass

    def CloseHistoryMenu(self):
        if self.historyMenu:
            historyMenu = self.historyMenu()
            if historyMenu and not historyMenu.destroyed:
                self.active = None
                historyMenu.Close()
                self.historyMenu = None

    def BrowseHistory(self, down):
        justopened = 0
        if not (self.historyMenu and self.historyMenu()):
            if not self.CheckHistory():
                return
            justopened = 1
        hm = self.historyMenu()
        currentIdx = None
        i = 0
        for entry in hm.sr.entries.children:
            if entry.selected:
                currentIdx = i
            entry.sr.hilite.state = uiconst.UI_HIDDEN
            entry.selected = 0
            i += 1

        if justopened:
            return
        if currentIdx is None:
            if down:
                currentIdx = 0
            else:
                currentIdx = len(hm.sr.entries.children) - 1
        elif down:
            currentIdx += 1
            if currentIdx >= len(hm.sr.entries.children):
                currentIdx = 0
        else:
            currentIdx -= 1
            if currentIdx < 0:
                currentIdx = len(hm.sr.entries.children) - 1
        self.active = active = hm.sr.entries.children[currentIdx]
        active.sr.hilite.state = uiconst.UI_DISABLED
        active.selected = 1
        if not getattr(self, 'blockSetValue', 0):
            self.SetValue(active.string, updateIndex=1)

    def GetHistoryID(self):
        id = ''
        item = self
        while item.parent:
            id = '/' + item.name + id
            if isinstance(item, Window):
                break
            item = item.parent

        return id

    def SetReadOnly(self, state):
        self.readonly = state

    def SetMaxLength(self, maxLength):
        self.maxletters = maxLength

    def SetHistoryVisibility(self, status):
        self.displayHistory = status

    def SetPasswordChar(self, char):
        self.passwordchar = char

    def OnSetFocus(self, *args):
        if self.pickState != uiconst.TR2_SPS_ON:
            return
        if not self.readonly and uicore.imeHandler:
            uicore.imeHandler.SetFocus(self)
        if self and not self.destroyed and self.parent and self.parent.name == 'inlines':
            if self.parent.parent and getattr(self.parent.parent.sr, 'node', None):
                browser = GetBrowser(self)
                if browser:
                    uthread.new(browser.ShowObject, self)
        self.sr.background.AnimEntry()
        if self.integermode or self.floatmode:
            self.SetText(self.text)
            self.caretIndex = self.GetCursorFromIndex(-1)
        self.ShowCaret()
        if self.autoselect:
            self.SelectAll()
        else:
            self.RefreshSelectionDisplay()
        if self.__OnSetFocus:
            self.__OnSetFocus(*args)

    def OnKillFocus(self, *args):
        if not self.readonly and uicore.imeHandler:
            uicore.imeHandler.KillFocus(self)
        if self.autoselect:
            self.SelectNone()
        self.sr.background.AnimExit()
        if self.integermode or self.floatmode:
            ret = self.CheckBounds(self.text, 1, allowEmpty=bool(self.hinttext), returnNoneIfOK=1)
            if ret is not None:
                text = ret
            else:
                text = self.text
            self.SetText(text, 1)
        self.HideCaret()
        self.CloseHistoryMenu()
        if self.OnFocusLost:
            uthread.new(self.OnFocusLost, self)

    def SetValue(self, text, add = 0, keepSelection = 0, updateIndex = 1, docallback = 1):
        self.draggedValue = None
        text = text or ''
        isString = isinstance(text, basestring)
        if isString:
            text = StripTags(text, stripOnly=['localized'])
        if self.floatmode:
            if isString:
                text = self.PrepareFloatString(text)
            text = self.CheckBounds(text, 0, bool(self.hinttext))
        elif self.integermode:
            text = self.CheckBounds(text, 0, bool(self.hinttext))
        else:
            text = text.replace('&lt;', '<').replace('&gt;', '>')
            if self.maxletters:
                text = text[:self.maxletters]
        if updateIndex:
            self.SetText(text, 0)
            self.caretIndex = self.GetCursorFromIndex(-1)
        self.SetText(text, 1)
        self.selFrom = self.selTo = None
        self.RefreshSelectionDisplay()
        self.OnTextChange(docallback)

    def GetValue(self, refreshDigits = 1, raw = 0, registerHistory = 1):
        ret = self.text
        if refreshDigits and (self.integermode or self.floatmode):
            ret = self.CheckBounds(ret, 0)
        if self.integermode:
            ret = ret or 0
            try:
                ret = int(ret)
            except:
                ret = 0
                sys.exc_clear()

        elif self.floatmode:
            ret = ret or 0
            floatdigits = self.floatmode[2]
            try:
                ret = round(float(ret), floatdigits)
            except:
                ret = 0.0
                sys.exc_clear()

        elif not raw:
            ret = ret.replace('<', '&lt;').replace('>', '&gt;')
        if registerHistory:
            self.RegisterHistory()
        return ret

    def IntMode(self, minint = None, maxint = None):
        if maxint is None:
            maxint = sys.maxint
        self.integermode = (minint, min(sys.maxint, maxint))
        self.floatmode = None
        self.OnMouseWheel = self.MouseWheel
        if minint and not self.text:
            self.SetValue(minint)
        self.ShowNumericControls()

    def FloatMode(self, minfloat = None, maxfloat = None, digits = 1):
        self.floatmode = (minfloat, maxfloat, int(digits))
        self.integermode = None
        self.OnMouseWheel = self.MouseWheel
        if minfloat and not self.text:
            self.SetValue(minfloat)
        self.ShowNumericControls()

    def ShowNumericControls(self):
        if self.numericControlsCont:
            return
        self.numericControlsCont = Container(name='numericControlsCont', parent=self, align=uiconst.TORIGHT, idx=0, width=10, padding=(0, 1, 1, 1), opacity=0.75)
        self.upButton = ButtonIcon(name='upButton', parent=self.numericControlsCont, align=uiconst.CENTER, pos=(0, -4, 9, 9), iconSize=7, texturePath='res:/UI/Texture/Shared/up.png')
        self.upButton.OnMouseDown = self.OnNumericUpButtonMouseDown
        self.upButton.OnMouseUp = self.OnNumericUpButtonMouseUp
        self.downButton = ButtonIcon(name='downButton', parent=self.numericControlsCont, align=uiconst.CENTER, pos=(0, 4, 9, 9), iconSize=7, texturePath='res:/UI/Texture/Shared/down.png')
        self.downButton.OnMouseDown = self.OnNumericDownButtonMouseDown
        self.downButton.OnMouseUp = self.OnNumericDownButtonMouseUp

    def OnNumericUpButtonMouseDown(self, *args):
        ButtonIcon.OnMouseDown(self.upButton, *args)
        self.updateNumericInputThread = uthread.new(self.UpdateNumericInputThread, 1)

    def OnNumericDownButtonMouseDown(self, *args):
        ButtonIcon.OnMouseDown(self.downButton, *args)
        self.updateNumericInputThread = uthread.new(self.UpdateNumericInputThread, -1)

    def KillNumericInputThread(self):
        if self.updateNumericInputThread:
            self.updateNumericInputThread.kill()
            self.updateNumericInputThread = None

    def OnNumericUpButtonMouseUp(self, *args):
        ButtonIcon.OnMouseUp(self.upButton, *args)
        self.KillNumericInputThread()

    def OnNumericDownButtonMouseUp(self, *args):
        ButtonIcon.OnMouseUp(self.downButton, *args)
        self.KillNumericInputThread()

    def UpdateNumericInputThread(self, diff):
        sleepTime = 500
        while uicore.uilib.leftbtn:
            self.ChangeNumericValue(diff)
            blue.synchro.SleepWallclock(sleepTime)
            sleepTime -= 0.5 * sleepTime
            sleepTime = max(10, sleepTime)

    def MouseWheel(self, *args):
        if self.readonly:
            return
        self.ChangeNumericValue((uicore.uilib.dz / 120) ** 3)

    def ChangeNumericValue(self, val):
        if uicore.uilib.Key(uiconst.VK_CONTROL):
            val *= 10
        if self.integermode:
            if val > 0:
                val = max(1, long(val))
            else:
                val = min(-1, long(val))
            errorValue = self.integermode[0] or 0
        elif self.floatmode:
            val *= 1 / float(10 ** self.floatmode[2])
            errorValue = self.floatmode[0] or 0
        else:
            return
        if val > 0:
            self.upButton.Blink(0.2)
        else:
            self.downButton.Blink(0.2)
        self.ClampMinMaxValue(val)

    def ClampMinMaxValue(self, change = 0):
        if not (self.integermode or self.floatmode):
            return
        try:
            current = self.GetValue(registerHistory=0)
        except ValueError:
            current = errorValue
            sys.exc_clear()

        text = self.CheckBounds(repr(current + change), 0)
        if self.floatmode:
            floatdigits = self.floatmode[2]
            text = '%%.%df' % floatdigits % float(text)
        if uicore.registry.GetFocus() is self:
            self.SetText(text)
        else:
            self.SetText(text, format=True)
        self.caretIndex = self.GetCursorFromIndex(-1)
        self.selFrom = None
        self.selTo = None
        self.RefreshSelectionDisplay()
        self.OnTextChange()

    def OnDblClick(self, *args):
        self.caretIndex = self.GetIndexUnderCursor()
        self.selFrom = self.GetCursorFromIndex(0)
        self.selTo = self.caretIndex = self.GetCursorFromIndex(-1)
        self.RefreshCaretPosition()
        self.RefreshSelectionDisplay()
        self.RefreshTextClipper()

    def OnMouseDown(self, button, *etc):
        if uicore.uilib.mouseTravel > 10:
            return
        if hasattr(self, 'RegisterFocus'):
            self.RegisterFocus(self)
        gettingFocus = uicore.registry.GetFocus() != self
        if gettingFocus:
            uicore.registry.SetFocus(self)
        leftClick = button == uiconst.MOUSELEFT
        if uicore.uilib.Key(uiconst.VK_SHIFT):
            if self.selFrom is None:
                self.selFrom = self.caretIndex
            self.selTo = self.caretIndex = self.GetIndexUnderCursor()
            self.RefreshCaretPosition()
            self.RefreshSelectionDisplay()
            self.RefreshTextClipper()
        elif leftClick:
            self.caretIndex = self.mouseDownCaretIndex = self.GetIndexUnderCursor()
            self.selFrom = None
            self.selTo = None
            self.RefreshCaretPosition()
            self.RefreshSelectionDisplay()
            self.RefreshTextClipper()
            if self.autoselect and gettingFocus:
                self.SelectAll()
            else:
                self.sr.selectionTimer = AutoTimer(50, self.UpdateSelection)

    def SetSelection(self, start, end):
        if start < 0:
            start = len(self.text)
        self.selFrom = self.GetCursorFromIndex(start)
        if end < 0:
            end = -1
        self.selTo = self.caretIndex = self.GetCursorFromIndex(end)
        self.RefreshCaretPosition()
        self.RefreshSelectionDisplay()
        self.RefreshTextClipper()

    def UpdateSelection(self):
        oldCaretIndex = self.mouseDownCaretIndex
        newCaretIndex = self.GetIndexUnderCursor()
        self.selFrom = oldCaretIndex
        self.selTo = newCaretIndex
        self.caretIndex = newCaretIndex
        self.RefreshCaretPosition()
        self.RefreshSelectionDisplay()
        self.RefreshTextClipper()

    def SelectNone(self):
        self.selFrom = (None, None)
        self.selTo = (None, None)
        self.RefreshSelectionDisplay()

    def OnMouseUp(self, *args):
        self.mouseDownCaretIndex = None
        self.sr.selectionTimer = None

    def GetIndexUnderCursor(self):
        l, t = self.sr.text.GetAbsolutePosition()
        cursorXpos = uicore.uilib.x - l
        return self.sr.text.GetIndexUnderPos(cursorXpos)

    def GetCursorFromIndex(self, index):
        return self.sr.text.GetWidthToIndex(index)

    def RefreshCaretPosition(self):
        if self.destroyed:
            return
        self.GetCaret()
        self.sr.caret.left = self.sr.text.left + self.caretIndex[1] - 1
        if not (self.integermode or self.floatmode):
            w, h = self._textClipper.GetAbsoluteSize()
            if self.sr.text.textwidth < w - self.TEXTLEFTMARGIN - self.TEXTRIGHTMARGIN:
                self.sr.text.left = self.TEXTLEFTMARGIN
            else:
                if self.sr.text.left + self.sr.text.textwidth < w - self.TEXTLEFTMARGIN - self.TEXTRIGHTMARGIN:
                    self.sr.text.left = w - self.TEXTLEFTMARGIN - self.TEXTRIGHTMARGIN - self.sr.text.textwidth
                if self.sr.caret.left > w - self.TEXTRIGHTMARGIN:
                    diff = self.sr.caret.left - w + self.TEXTRIGHTMARGIN
                    self.sr.text.left -= diff
                elif self.sr.caret.left < self.TEXTLEFTMARGIN:
                    diff = -self.sr.caret.left + self.TEXTLEFTMARGIN
                    self.sr.text.left += diff
            self.sr.caret.left = self.sr.text.left + self.caretIndex[1] - 1

    def ShowCaret(self):
        self.GetCaret()
        self.RefreshCaretPosition()
        w, h = self.GetAbsoluteSize()
        self.sr.caret.height = h - 4
        self.sr.caret.top = 2
        self.sr.caret.state = uiconst.UI_DISABLED
        self.sr.caretTimer = AutoTimer(400, self.BlinkCaret)

    ShowCursor = ShowCaret

    def HideCaret(self):
        self.sr.caretTimer = None
        if self.sr.get('caret', None):
            self.sr.caret.state = uiconst.UI_HIDDEN

    HideCursor = HideCaret

    def GetCaret(self):
        if not self.sr.get('caret', None) and not self.destroyed:
            self.Prepare_Caret_()

    def BlinkCaret(self):
        if self.destroyed:
            self.sr.caretTimer = None
            return
        if self.sr.get('caret', None):
            if not trinity.app.IsActive():
                self.sr.caret.state = uiconst.UI_HIDDEN
                return
            self.sr.caret.state = [uiconst.UI_HIDDEN, uiconst.UI_DISABLED][self.sr.caret.state == uiconst.UI_HIDDEN]

    def OnChar(self, char, flag):
        if self.floatmode:
            if unichr(char) in ',.':
                return False
        if self.OnAnyChar(char):
            isLatinBased = uicore.font.IsLatinBased(unichr(char))
            if isLatinBased or not uicore.imeHandler:
                keyboardLanguageID = languageConst.LANG_ENGLISH
            else:
                keyboardLanguageID = uicore.imeHandler.GetKeyboardLanguageID()
            fontFamily = uicore.font.GetFontFamilyBasedOnWindowsLanguageID(keyboardLanguageID)
            if fontFamily != self.sr.text.fontFamily:
                self.sr.text.fontFamily = fontFamily
                self.sr.hinttext.fontFamily = fontFamily
            if char in [127, uiconst.VK_BACK]:
                if self.GetSelectionBounds() != (None, None):
                    self.DeleteSelected()
                else:
                    self.Delete(0)
                self.CheckHistory()
                if self.OnInsert:
                    self.OnInsert(char, flag)
                return True
            if char != uiconst.VK_RETURN:
                self.Insert(char)
                self.CheckHistory()
                if self.OnInsert:
                    self.OnInsert(char, flag)
                return True
        return False

    def OnAnyChar(self, char, *args):
        return True

    def Confirm(self, *args):
        if self.OnReturn:
            self.CloseHistoryMenu()
            return uthread.new(self.OnReturn)
        searchFrom = GetWindowAbove(self)
        if searchFrom:
            wnds = [ w for w in searchFrom.Find('trinity.Tr2Sprite2dContainer') + searchFrom.Find('trinity.Tr2Sprite2d') if getattr(w, 'btn_default', 0) == 1 ]
            if len(wnds):
                for wnd in wnds:
                    if self == wnd:
                        continue
                    if wnd.IsVisible():
                        if hasattr(wnd, 'OnClick'):
                            uthread.new(wnd.OnClick, wnd)
                        return True

        return False

    def OnKeyDown(self, vkey, flag):
        if self.floatmode:
            if vkey in (uiconst.VK_DECIMAL, uiconst.VK_OEM_PERIOD, uiconst.VK_OEM_COMMA):
                self.Insert(self.DECIMAL)
                return
        HOME = uiconst.VK_HOME
        END = uiconst.VK_END
        CTRL = uicore.uilib.Key(uiconst.VK_CONTROL)
        SHIFT = uicore.uilib.Key(uiconst.VK_SHIFT)
        if self.destroyed:
            return
        oldCaretIndex = self.caretIndex
        selection = self.GetSelectionBounds()
        index = self.caretIndex[0]
        if vkey == uiconst.VK_LEFT:
            if CTRL:
                index = self.text.rfind(' ', 0, max(index - 1, 0)) + 1 or 0
            else:
                index = max(index - 1, 0)
        elif vkey == uiconst.VK_RIGHT:
            if CTRL:
                index = self.text.find(' ', index) + 1 or len(self.text)
            else:
                index = index + 1
            index = min(index, len(self.text))
        elif vkey == HOME:
            index = 0
        elif vkey == END:
            index = len(self.text)
        elif vkey in (uiconst.VK_DELETE,):
            if self.GetSelectionBounds() != (None, None):
                self.DeleteSelected()
                return
            self.Delete(1)
        else:
            if vkey in (uiconst.VK_UP, uiconst.VK_DOWN):
                self.BrowseHistory(vkey == uiconst.VK_DOWN)
                if vkey == uiconst.VK_UP:
                    self.ChangeNumericValue(1)
                elif vkey == uiconst.VK_DOWN:
                    self.ChangeNumericValue(-1)
            else:
                self.OnUnusedKeyDown(self, vkey, flag)
            return
        self.caretIndex = self.GetCursorFromIndex(index)
        if vkey in (uiconst.VK_LEFT,
         uiconst.VK_RIGHT,
         HOME,
         END):
            if SHIFT:
                if self.selTo is not None:
                    self.selTo = self.caretIndex
                elif self.selTo is None:
                    self.selFrom = oldCaretIndex
                    self.selTo = self.caretIndex
            elif selection != (None, None):
                if vkey == uiconst.VK_LEFT:
                    index = selection[0][0]
                elif vkey == uiconst.VK_RIGHT:
                    index = selection[1][0]
                self.caretIndex = self.GetCursorFromIndex(index)
            if not SHIFT or self.selFrom == self.selTo:
                self.selFrom = self.selTo = None
            self.CloseHistoryMenu()
        self.RefreshCaretPosition()
        self.RefreshSelectionDisplay()
        self.RefreshTextClipper()

    def OnUnusedKeyDown(self, *args):
        pass

    def StripNumberString(self, numberString):
        if self.integermode:
            return filter(lambda x: x in '-0123456789', numberString)
        if self.floatmode:
            return filter(lambda x: x in '-0123456789e.', numberString)
        return numberString

    def PrepareFloatString(self, numberString):
        commasInString = numberString.count(',')
        periodsInString = numberString.count('.')
        if commasInString and periodsInString:
            haveDecimal = False
            stripped = u''
            legalFloats = '-0123456789e,.'
            for each in reversed(unicode(numberString)):
                if each in legalFloats:
                    if each in u',.':
                        if haveDecimal:
                            continue
                        haveDecimal = True
                        stripped = '.' + stripped
                    else:
                        stripped = each + stripped

            return stripped
        if commasInString >= 2:
            numberString = filter(lambda x: x in '-0123456789e.', numberString)
            return numberString
        if periodsInString >= 2:
            numberString = filter(lambda x: x in '-0123456789e,', numberString)
            numberString = numberString.replace(',', self.DECIMAL)
            return numberString
        numberString = filter(lambda x: x in '-0123456789e,.', numberString)
        numberString = numberString.replace(',', self.DECIMAL)
        return numberString

    def Insert(self, ins):
        if self.readonly:
            return None
        if not isinstance(ins, basestring):
            text = unichr(ins)
        else:
            text = ins
        text = text.replace(u'\r', u' ').replace(u'\n', u'')
        current = self.GetText()
        if self.GetSelectionBounds() != (None, None):
            self.DeleteSelected()
        if (self.integermode or self.floatmode) and text:
            if self.floatmode:
                if self.DECIMAL in text and self.DECIMAL in self.text:
                    uicore.Message('uiwarning03')
                    return None
            if text == u'-':
                newvalue = self.text[:self.caretIndex[0]] + text + self.text[self.caretIndex[0]:]
                if newvalue != u'-':
                    newvalue = self.StripNumberString(newvalue)
                    try:
                        if self.integermode:
                            long(newvalue)
                        else:
                            float(newvalue)
                    except ValueError as e:
                        uicore.Message('uiwarning03')
                        sys.exc_clear()
                        return None

            elif text != self.DECIMAL:
                text = self.StripNumberString(text)
                try:
                    if self.integermode:
                        long(text)
                    else:
                        float(text)
                except ValueError as e:
                    uicore.Message('uiwarning03')
                    sys.exc_clear()
                    return None

            elif text not in '0123456789' and self.integermode:
                uicore.Message('uiwarning03')
                return None
        before = self.text[:self.caretIndex[0]]
        after = self.text[self.caretIndex[0]:]
        become = before + text + after
        if self.maxletters and len(become) > self.maxletters:
            become = become[:self.maxletters]
            uicore.Message('uiwarning03')
        self.autoselect = False
        if (self.integermode or self.floatmode) and become and become[-1] not in (self.DECIMAL, '-'):
            become = self.StripNumberString(become)
        self.SetText(become)
        index = self.caretIndex[0] + len(text)
        self.caretIndex = self.GetCursorFromIndex(index)
        self.OnTextChange()

    def GetMenu(self):
        m = []
        start, end = self.GetSelectionBounds()
        if start is not None:
            start = start[0]
        if end is not None:
            end = end[0]
        m += [(MenuLabel('/Carbon/UI/Controls/Common/Copy'), self.Copy, (start, end))]
        if not self.readonly:
            if uicore.imeHandler:
                uicore.imeHandler.GetMenuDelegate(self, None, m)
            paste = GetClipboardData()
            if paste:
                m += [(MenuLabel('/Carbon/UI/Controls/Common/Paste'), self.Paste, (paste,
                   start,
                   end,
                   True))]
            if self.displayHistory and self.passwordchar is None:
                m += [(MenuLabel('/Carbon/UI/Controls/Common/ClearHistory'), self.ClearHistory, (None,))]
        return m

    def OnTextChange(self, docallback = 1):
        self.CheckHintText()
        self.RefreshCaretPosition()
        self.RefreshTextClipper()
        if docallback and self.OnChange:
            self.OnChange(self.text)

    def CheckBounds(self, qty, warnsnd = 0, allowEmpty = 1, returnNoneIfOK = 0):
        if allowEmpty and not qty:
            return ''
        if qty == '-' or qty is None:
            qty = 0
        isInt = self.integermode is not None
        isFloat = self.floatmode is not None
        if isFloat:
            minbound, maxbound = self.floatmode[:2]
        elif isInt:
            minbound, maxbound = self.integermode
        else:
            return str(qty)
        pQty = self.StripNumberString(repr(qty))
        minusIndex = pQty.find('-')
        if minusIndex > 0 and pQty[minusIndex - 1] != 'e':
            uicore.Message('uiwarning03')
            if minbound is not None:
                return minbound
            return ''
        if isFloat:
            if pQty == self.DECIMAL:
                uicore.Message('uiwarning03')
                if minbound is not None:
                    return minbound
                return ''
            qty = float(pQty or 0)
        else:
            qty = long(pQty or 0)
        warn = 0
        ret = qty
        if maxbound is not None and qty > maxbound:
            warn = 1
            ret = maxbound
        elif minbound is not None and qty < minbound:
            warn = 1
            ret = minbound
        elif returnNoneIfOK:
            return
        if warn and warnsnd:
            uicore.Message('uiwarning03')
        return ret

    def RefreshSelectionDisplay(self):
        selection = self.GetSelectionBounds()
        if selection != (None, None):
            self.GetSelectionLayer()
            f, t = selection
            self.sr.selection.left = self.sr.text.left + f[1]
            self.sr.selection.width = t[1] - f[1]
            self.sr.selection.state = uiconst.UI_DISABLED
        elif self.sr.selection:
            self.sr.selection.state = uiconst.UI_HIDDEN

    def GetSelectionBounds(self):
        if self.selFrom and self.selTo and self.selFrom[0] != self.selTo[0]:
            return (min(self.selFrom, self.selTo), max(self.selFrom, self.selTo))
        return (None, None)

    def GetSelectionLayer(self):
        w, h = self.GetAbsoluteSize()
        if not self.sr.selection:
            self.sr.selection = Fill(parent=self._textClipper, name='selection', align=uiconst.TOPLEFT, pos=(0,
             1,
             0,
             h - 2), idx=1)

    def DeleteSelected(self):
        if self.readonly:
            return
        start, end = self.GetSelectionBounds()
        self.selFrom = self.selTo = None
        self.RefreshSelectionDisplay()
        text = self.GetText()
        self.SetText(text[:start[0]] + text[end[0]:])
        self.caretIndex = start
        self.OnTextChange()

    def SelectAll(self):
        self.selFrom = self.GetCursorFromIndex(0)
        self.selTo = self.GetCursorFromIndex(-1)
        self.RefreshSelectionDisplay()

    def Cut(self, *args):
        if self.GetSelectionBounds() != (None, None):
            self.Copy()
            self.DeleteSelected()

    def Copy(self, selectStart = None, selectEnd = None):
        if self.passwordchar is None:
            text = self.GetText()
            if self.floatmode:
                text = text.replace(self.DECIMAL, self.GetLocalizedDecimal())
        else:
            text = self.passwordchar * len(self.GetText())
        if selectStart is not None and selectEnd is not None:
            blue.pyos.SetClipboardData(text[selectStart:selectEnd])
        else:
            start, end = self.GetSelectionBounds()
            if not start and not end:
                blue.pyos.SetClipboardData(text)
            else:
                blue.pyos.SetClipboardData(text[start[0]:end[0]])

    def GetLocalizedDecimal(self):
        if session:
            localizedDecimal = eveLocalization.GetDecimalSeparator(localization.SYSTEM_LANGUAGE)
        else:
            localizedDecimal = prefs.GetValue('decimal', '.')
        return localizedDecimal

    def Paste(self, paste, deleteStart = None, deleteEnd = None, forceFocus = False):
        if self.floatmode:
            haveIllegalChar = False
            legalChars = '-0123456789e,.'
            for char in paste:
                if char in legalChars:
                    if haveIllegalChar:
                        uicore.Message('uiwarning03')
                        return
                else:
                    haveIllegalChar = True

            if haveIllegalChar:
                uicore.Message('uiwarning03')
            paste = self.PrepareFloatString(paste)
        hadFocus = uicore.registry.GetFocus() is self
        if deleteStart is None or deleteEnd is None:
            start, end = self.GetSelectionBounds()
            if start is not None and end is not None:
                self.DeleteSelected()
        else:
            text = self.GetText()
            self.SetText(text[:deleteStart] + text[deleteEnd:])
            self.caretIndex = self.GetCursorFromIndex(deleteStart)
            self.OnTextChange()
        self.Insert(paste)
        if (hadFocus or forceFocus) and not uicore.registry.GetFocus() == self:
            uicore.registry.SetFocus(self)

    def EncodeOutput(self, otext):
        if not otext:
            return ''
        if self.integermode or self.floatmode:
            elem = [ each for each in otext if each not in ('-', '.') ]
            if not len(elem):
                return ''
        if self.integermode:
            return localization.formatters.FormatNumeric(long(float(otext)), useGrouping=True)
        if self.floatmode:
            decimalPlaces = self.floatmode[2]
            return localization.formatters.FormatNumeric(float(otext), useGrouping=True, decimalPlaces=decimalPlaces)
        if not isinstance(otext, basestring):
            otext = str(otext)
        return otext

    def GetText(self):
        return self.text

    def SetText(self, text, format = 0):
        if not isinstance(text, basestring):
            if self.integermode:
                text = repr(int(text))
            elif self.floatmode:
                text = '%.*f' % (self.floatmode[2], float(text))
            else:
                text = str(text)
        text = StripTags(text, stripOnly=['localized'])
        if self.passwordchar is not None:
            displayText = self.passwordchar * len(text.replace('<br>', ''))
        elif format:
            displayText = self.EncodeOutput(text) + self.suffix
        elif self.floatmode:
            displayText = text.replace(self.DECIMAL, self.GetLocalizedDecimal())
        else:
            displayText = text
        displayText = StripTags(displayText, stripOnly=['localized'])
        self.sr.text.text = displayText.replace('<', '&lt;').replace('>', '&gt;')
        self.text = text

    def Delete(self, direction = 1):
        if self.readonly:
            return
        if direction:
            begin = self.caretIndex[0]
            newCaretIndex = self.caretIndex[0]
            end = min(self.caretIndex[0] + 1, len(self.text))
        else:
            end = self.caretIndex[0]
            begin = max(self.caretIndex[0] - 1, 0)
            newCaretIndex = begin
        become = self.text[:begin] + self.text[end:]
        if not become and (self.floatmode or self.integermode):
            if self.floatmode:
                minbound, maxbound = self.floatmode[:2]
            if self.integermode:
                minbound, maxbound = self.integermode
            if minbound <= 0 <= maxbound:
                become = ''
        self.SetText(become)
        newCaretIndex = min(newCaretIndex, len(self.text))
        self.caretIndex = self.GetCursorFromIndex(newCaretIndex)
        self.OnTextChange()

    def Disable(self):
        Container.Disable(self)
        self.opacity = 0.3

    def Enable(self):
        Container.Enable(self)
        self.opacity = 1.0
class Industry(Window):
    __guid__ = 'form.Industry'
    __notifyevents__ = [
        'OnIndustryLeftOrRightKey', 'OnIndustryDropData',
        'OnIndustryRemoveBlueprint', 'OnIndustryJob', 'OnBlueprintReload',
        'OnFacilityReload', 'OnBlueprintEntryDblClicked'
    ]
    default_captionLabelPath = 'UI/Industry/Industry'
    default_descriptionLabelPath = 'UI/Industry/IndustryTooltip'
    default_caption = localization.GetByLabel('UI/Industry/Industry')
    default_windowID = 'industryWnd'
    default_iconNum = 'res:/UI/Texture/WindowIcons/Industry.png'
    default_height = 800
    default_topParentHeight = 0
    default_isStackable = False
    default_minSize = (1004, 650)

    def ApplyAttributes(self, attributes):
        Window.ApplyAttributes(self, attributes)
        blueprintID = attributes.Get('blueprintID', None)
        blueprintTypeID = attributes.Get('blueprintTypeID', None)
        bpData = attributes.Get('bpData', None)
        self.history = HistoryBuffer()
        self.jobData = None
        self.pendingBlueprint = None
        self.loadBlueprintThread = None
        self.topCont = Container(name='topCont',
                                 parent=self.sr.main,
                                 align=uiconst.TOTOP,
                                 height=TOP_HEIGHT,
                                 clipChildren=True)
        self.currView = BaseView(parent=self.topCont,
                                 align=uiconst.TOTOP,
                                 height=VIEW_HEIGHT)
        self.jobsStrip = JobsStrip(parent=self.topCont,
                                   align=uiconst.TOTOP,
                                   padding=(5, -1, 7, 2),
                                   callback=self.OnBlueprintsSelected)
        self.bottomCont = Container(name='bottomCont',
                                    parent=self.sr.main,
                                    controller=self,
                                    height=0.4,
                                    padding=(4, 0, 4, 0),
                                    callback=self.OnBlueprintsSelected)
        cont = ContainerAutoSize(name='historyArrowCont',
                                 parent=self.sr.main,
                                 align=uiconst.TOPRIGHT,
                                 height=16,
                                 left=3,
                                 top=1)
        self.expandViewBtn = ButtonIcon(
            name='expandViewBtn',
            parent=cont,
            align=uiconst.TORIGHT,
            width=16,
            iconSize=7,
            texturePath='res:/UI/Texture/classes/Neocom/arrowDown.png',
            func=self.OnExpandViewBtn)
        self.goForwardBtn = ButtonIcon(
            name='goForwardBtn',
            parent=cont,
            align=uiconst.TORIGHT,
            width=16,
            iconSize=16,
            padRight=5,
            texturePath='res:/UI/Texture/icons/38_16_224.png',
            func=self.OnForward,
            hint=localization.GetByLabel('UI/Control/EveWindow/Next'))
        self.goBackBtn = ButtonIcon(
            name='goBackBtn',
            parent=cont,
            align=uiconst.TORIGHT,
            width=16,
            iconSize=16,
            texturePath='res:/UI/Texture/icons/38_16_223.png',
            func=self.OnBack,
            hint=localization.GetByLabel('UI/Control/EveWindow/Previous'))
        self.browserCont = Container(name='browserCont',
                                     parent=self.bottomCont,
                                     padding=(0, 2, 0, 2))
        self.browserBlueprints = BrowserBlueprints(
            parent=self.browserCont, callback=self.OnBlueprintsSelected)
        self.browserFacilities = BrowserFacilities(
            parent=self.browserCont, callback=self.OnFacilitySelected)
        self.browserJobs = BrowserJobs(parent=self.browserCont,
                                       callback=self.OnJobSelected)
        tabs = ((localization.GetByLabel('UI/Industry/Blueprints'),
                 self.browserBlueprints, None, 'blueprints', None,
                 GetByLabel('UI/Industry/TabBlueprints')),
                (localization.GetByLabel('UI/Industry/Facilities'),
                 self.browserFacilities, None, 'facilities', None,
                 GetByLabel('UI/Industry/TabFacilities')),
                (localization.GetByLabel('UI/Industry/Jobs'), self.browserJobs,
                 None, 'jobs', None, GetByLabel('UI/Industry/TabJobs')))
        self.tabs = TabGroup(parent=self.browserCont,
                             tabs=tabs,
                             height=26,
                             labelPadding=12,
                             idx=0,
                             padLeft=0,
                             groupID='IndustryWindowBrowsers',
                             autoselecttab=not self.IsBrowserCollapsed())
        self.expandBottomBtn = ButtonIcon(
            name='expandBottomBtn',
            parent=self.bottomCont,
            align=uiconst.TOPRIGHT,
            pos=(2, -3, 16, 16),
            iconSize=7,
            texturePath='res:/UI/Texture/classes/Neocom/arrowDown.png',
            func=self.OnExpandBottomBtn)
        if blueprintID or blueprintTypeID:
            self.ShowBlueprint(blueprintID, blueprintTypeID, bpData=bpData)
        if self.IsViewCollapsed():
            self.CollapseView(animate=False)
        else:
            self.expandViewBtn.SetRotation(-pi)
        if self.IsBrowserCollapsed():
            self.CollapseBrowser(animate=False)
        else:
            self.expandBottomBtn.SetRotation(-pi)
        sm.GetService('audio').SendUIEvent('ind_windowOpened')

    def Close(self, *args, **kwargs):
        Window.Close(self, *args, **kwargs)
        if self.jobData:
            sm.GetService('industrySvc').DisconnectJob(self.jobData)

    @telemetry.ZONE_METHOD
    def OnNewJobData(self, branchHistory=True):
        if self.jobData:
            settings.user.ui.Set('IndustryCurrentActivityID',
                                 self.jobData.activityID)
        self.jobsStrip.OnNewJobData(self.jobData)
        self.currView.OnNewJobData(self.jobData)
        self.browserBlueprints.OnNewJobData(self.jobData)
        if branchHistory and self.jobData:
            self.history.Append((self.jobData.blueprintID,
                                 self.jobData.blueprint.blueprintTypeID,
                                 self.jobData.activityID))
            self.UpdateHistoryButtons()

    @telemetry.ZONE_METHOD
    def OnBlueprintsSelected(self,
                             bpData,
                             activityID=None,
                             branchHistory=True):
        self.pendingBlueprint = (bpData, activityID, branchHistory)
        if not self.loadBlueprintThread:
            self.loadBlueprintThread = uthread.new(self._OnBlueprintSelected)

    def _OnBlueprintSelected(self):
        try:
            while self.pendingBlueprint:
                while uicore.uilib.Key(uiconst.VK_UP) or uicore.uilib.Key(
                        uiconst.VK_DOWN):
                    blue.synchro.Yield()

                bpData, activityID, branchHistory = self.pendingBlueprint
                self.pendingBlueprint = None
                if activityID is None:
                    activityID = self._GetDefaultActivityID(bpData)
                if self.jobData and self.jobData.blueprint.IsSameBlueprint(
                        bpData):
                    if activityID == self.jobData.activityID:
                        break
                self.jobData = None
                if bpData.jobID is not None:
                    jobData = sm.GetService('industrySvc').GetJobByID(
                        bpData.jobID)
                    if jobData.status < industry.STATUS_COMPLETED:
                        self.jobData = sm.GetService(
                            'industrySvc').JobDataWithBlueprint(jobData)
                if not self.jobData:
                    self.jobData = self.CreateJob(bpData, activityID)
                if not self.pendingBlueprint:
                    self.browserBlueprints.OnActivitySelected(
                        self.jobData.blueprintID, activityID)
                    self.OnNewJobData(branchHistory)
                blue.synchro.SleepWallclock(500)

        finally:
            self.loadBlueprintThread = None

    @telemetry.ZONE_METHOD
    def OnFacilitySelected(self, facilityData):
        if self.jobData:
            self.jobData.facility = facilityData

    @telemetry.ZONE_METHOD
    def OnJobSelected(self, jobData):
        if jobData.status > industry.STATUS_COMPLETED:
            jobData = sm.GetService('industrySvc').RecreateJob(jobData)
        else:
            jobData = sm.GetService('industrySvc').JobDataWithBlueprint(
                jobData)
        if not jobData:
            return
        self.jobData = jobData
        self.OnNewJobData(jobData)
        self.UpdateBlueprintBrowserActivitySelected()

    @telemetry.ZONE_METHOD
    def UpdateBlueprintBrowserActivitySelected(self):
        if self.jobData:
            self.browserBlueprints.OnActivitySelected(self.jobData.blueprintID,
                                                      self.jobData.activityID)

    def OnBlueprintEntryDblClicked(self):
        if self.IsViewCollapsed():
            self.ExpandView()

    def _GetDefaultActivityID(self, bpData):
        currActivityID = settings.user.ui.Get('IndustryCurrentActivityID',
                                              None)
        if currActivityID in bpData.activities:
            return currActivityID
        for activityID in industry.ACTIVITIES:
            if activityID in bpData.activities:
                return activityID

    @telemetry.ZONE_METHOD
    def CreateJob(self, bpData, activityID):
        return sm.GetService('industrySvc').CreateJob(bpData, activityID,
                                                      bpData.facilityID)

    def ShowBlueprint(self,
                      blueprintID=None,
                      blueprintTypeID=None,
                      activityID=None,
                      branchHistory=True,
                      bpData=None):
        if not bpData:
            if blueprintID:
                bpData = sm.GetService('blueprintSvc').GetBlueprintItem(
                    blueprintID)
            else:
                bpData = sm.GetService('blueprintSvc').GetBlueprintTypeCopy(
                    blueprintTypeID)
        self.OnBlueprintsSelected(bpData,
                                  activityID=activityID,
                                  branchHistory=branchHistory)

    def ShowJob(self, jobID):
        if jobID:
            self.OnJobSelected(sm.GetService('industrySvc').GetJobByID(jobID))

    @classmethod
    def OpenOrShowBlueprint(cls,
                            blueprintID=None,
                            blueprintTypeID=None,
                            bpData=None):
        wnd = cls.GetIfOpen()
        if wnd:
            wnd.Maximize()
            wnd.ShowBlueprint(blueprintID, blueprintTypeID, bpData=bpData)
            if wnd.IsViewCollapsed():
                wnd.ExpandView()
            uicore.registry.SetFocus(wnd)
        else:
            wnd = cls.Open(blueprintID=blueprintID,
                           blueprintTypeID=blueprintTypeID,
                           bpData=bpData)
            wnd.Maximize()

    def OnExpandBottomBtn(self, *args):
        if self.IsBrowserCollapsed():
            self.ExpandBrowser()
        else:
            self.CollapseBrowser()

    def OnExpandViewBtn(self, *args):
        if self.IsViewCollapsed():
            self.ExpandView()
        else:
            self.CollapseView()

    def ExpandView(self, animate=True):
        settings.user.ui.Set('industryWndIsViewCollapsed', False)
        sm.ScatterEvent('OnIndustryViewExpandCollapse')
        self.expandViewBtn.SetRotation(-pi)
        self.expandViewBtn.Disable()
        self.topCont.Show()
        if animate:
            uicore.animations.MorphScalar(self.topCont,
                                          'height',
                                          self.topCont.height,
                                          TOP_HEIGHT,
                                          duration=0.3)
            uicore.animations.FadeIn(self.topCont, duration=0.3, sleep=True)
        else:
            self.topCont.height = TOP_HEIGHT
            self.topCont.opacity = 1.0
        self.expandBottomBtn.Show()
        self.expandViewBtn.Enable()

    def CollapseView(self, animate=True):
        settings.user.ui.Set('industryWndIsViewCollapsed', True)
        sm.ScatterEvent('OnIndustryViewExpandCollapse')
        self.expandViewBtn.Disable()
        self.expandViewBtn.SetRotation(0)
        self.expandBottomBtn.Hide()
        if animate:
            uicore.animations.MorphScalar(self.topCont,
                                          'height',
                                          self.topCont.height,
                                          0,
                                          duration=0.3)
            uicore.animations.FadeOut(self.topCont, duration=0.3, sleep=True)
        else:
            self.topCont.height = 0
            self.topCont.opacity = 0.0
        self.topCont.Hide()
        self.expandViewBtn.Enable()

    def IsViewCollapsed(self):
        return settings.user.ui.Get('industryWndIsViewCollapsed', False)

    def ExpandBrowser(self, animate=True):
        if self.tabs.GetSelectedIdx() is None:
            self.tabs.AutoSelect()
        settings.user.ui.Set('industryWndIsBrowserCollapsed', False)
        self.expandBottomBtn.SetRotation(-pi)
        self.expandBottomBtn.Disable()
        height = settings.user.ui.Get('industryWndExpandedHeight',
                                      self.default_height)
        self.browserCont.Show()
        if animate:
            uicore.animations.MorphScalar(self,
                                          'height',
                                          self.height,
                                          height,
                                          duration=0.3)
            uicore.animations.FadeIn(self.browserCont,
                                     duration=0.3,
                                     sleep=True)
        else:
            self.height = height
            self.browserCont.opacity = 1.0
        self.UnlockHeight()
        self.expandViewBtn.Show()
        self.expandBottomBtn.Enable()

    def CollapseBrowser(self, animate=True):
        if not self.IsBrowserCollapsed():
            settings.user.ui.Set('industryWndExpandedHeight', self.height)
        settings.user.ui.Set('industryWndIsBrowserCollapsed', True)
        self.expandBottomBtn.Disable()
        self.expandBottomBtn.SetRotation(0)
        self.expandViewBtn.Hide()
        if animate:
            uicore.animations.MorphScalar(self,
                                          'height',
                                          self.height,
                                          FIXED_HEIGHT,
                                          duration=0.3)
            uicore.animations.FadeOut(self.browserCont,
                                      duration=0.3,
                                      sleep=True)
        else:
            self.height = FIXED_HEIGHT
            self.browserCont.opacity = 0.0
        self.LockHeight(FIXED_HEIGHT)
        self.browserCont.Hide()
        self.expandBottomBtn.Enable()

    def IsBrowserCollapsed(self):
        return settings.user.ui.Get('industryWndIsBrowserCollapsed', False)

    def OnBack(self):
        historyID = self.history.GoBack()
        if historyID:
            if uicore.uilib.mouseOver != self.goBackBtn:
                self.goBackBtn.Blink()
            self.ShowBlueprint(branchHistory=False, *historyID)
            self.UpdateHistoryButtons()

    def OnForward(self):
        historyID = self.history.GoForward()
        if historyID:
            if uicore.uilib.mouseOver != self.goForwardBtn:
                self.goForwardBtn.Blink()
            self.ShowBlueprint(branchHistory=False, *historyID)
            self.UpdateHistoryButtons()

    def UpdateHistoryButtons(self):
        if self.history.IsBackEnabled():
            self.goBackBtn.Enable()
        else:
            self.goBackBtn.Disable()
        if self.history.IsForwardEnabled():
            self.goForwardBtn.Enable()
        else:
            self.goForwardBtn.Disable()

    def OnMouseWheel(self, *args):
        sm.ScatterEvent('OnIndustryWndMouseWheel')

    def OnClick(self, *args):
        sm.ScatterEvent('OnIndustryWndClick')

    def CloseByUser(self, *args):
        sm.GetService('audio').SendUIEvent('ind_windowClosed')
        Window.CloseByUser(self, *args)

    def _GetCurrentActivities(self):
        """
        Returns available activities for current blueprint correctly sorted
        """
        currActivities = self.jobData.blueprint.activities.keys()
        currActivities = sorted(currActivities,
                                key=lambda x: ACTIVITIES.index(x))
        return currActivities

    def SelectPreviousActivity(self):
        if not self.jobData or self.jobData.IsInstalled():
            return
        currActivities = self._GetCurrentActivities()
        idx = currActivities.index(self.jobData.activityID)
        if idx == 0:
            return
        activityID = currActivities[idx - 1]
        self._SelectActivity(activityID)

    def SelectNextActivity(self):
        if not self.jobData or self.jobData.IsInstalled():
            return
        currActivities = self._GetCurrentActivities()
        idx = currActivities.index(self.jobData.activityID)
        if idx == len(currActivities) - 1:
            return
        activityID = currActivities[idx + 1]
        self._SelectActivity(activityID)

    def _SelectActivity(self, activityID):
        self.browserBlueprints.OnActivitySelected(self.jobData.blueprintID,
                                                  activityID)
        self.OnBlueprintsSelected(self.jobData.blueprint, activityID)

    def OnIndustryLeftOrRightKey(self, key):
        if key == uiconst.VK_LEFT:
            self.SelectPreviousActivity()
        elif key == uiconst.VK_RIGHT:
            self.SelectNextActivity()

    def OnDropData(self, dragSource, dragData):
        if not dragData:
            return
        typeID = itemID = None
        data = dragData[0]
        bpData = getattr(data, 'bpData', None)
        if getattr(data, 'item', None):
            itemID = getattr(data.item, 'itemID', None)
            typeID = data.item.typeID
        else:
            typeID = getattr(data, 'typeID', None)
            itemID = getattr(data, 'itemID', None)
        if itemID or typeID:
            categoryID = cfg.invtypes.Get(typeID).categoryID
            if industryCommon.IsBlueprintCategory(categoryID):
                Industry.OpenOrShowBlueprint(itemID, typeID, bpData)
            else:
                raise UserError('ItemNotBlueprint', {'itemname': typeID})

    def OnIndustryDropData(self, dragSource, dragData):
        self.OnDropData(dragSource, dragData)

    def OnIndustryRemoveBlueprint(self):
        """
        Active blueprint removed by user
        """
        self.jobData = None
        self.OnNewJobData()

    def OnIndustryJob(self, jobID, ownerID, blueprintID, installerID, status,
                      successfulRuns):
        """
        Notification if a job is modified in anyway.
        """
        if self.destroyed:
            return
        if self.jobData and self.jobData.jobID == jobID:
            if status == industry.STATUS_CANCELLED:
                self.jobData = None
            else:
                self.jobData.status = status
                self.jobData.successfulRuns = successfulRuns
            self.OnNewJobData()
        elif self.jobData and self.jobData.blueprintID == blueprintID:
            self.jobData = sm.GetService('industrySvc').GetJobByID(jobID)
            self.OnNewJobData()
        if status in (industry.STATUS_INSTALLED, industry.STATUS_READY):
            if self.tabs.GetSelectedIdx() != TAB_JOBS:
                self.tabs.BlinkPanelByName(
                    localization.GetByLabel('UI/Industry/Jobs'))
        if status == industry.STATUS_INSTALLED:
            if self.tabs.GetSelectedIdx() == TAB_BLUEPRINTS:
                self.browserBlueprints.SetFocus()

    def OnBlueprintReload(self, ownerID):
        """
        If a blueprint changes relating to our current job, just reload.
        """
        if self.jobData and self.jobData.ownerID == ownerID:
            self.Reload()

    def OnFacilityReload(self, facilityID):
        """
        If a facility changes relating to our current job, just reload.
        """
        if self.jobData and self.jobData.facilityID == facilityID:
            self.Reload(force=True)

    def Reload(self, force=False):
        """
        Whatever the industry window is currently displaying at the top, reload all the data and redraw.
        """
        try:
            jobData = self.jobData
            if jobData:
                if force:
                    self.jobData = None
                if jobData.jobID:
                    self.ShowJob(jobData.jobID)
                else:
                    self.ShowBlueprint(
                        blueprintID=jobData.blueprint.blueprintID,
                        blueprintTypeID=jobData.blueprint.blueprintTypeID,
                        activityID=jobData.activityID,
                        bpData=jobData.blueprint)
        except UserError:
            self.OnIndustryRemoveBlueprint()