Example #1
0
    def figureOutHeight(self, _width=100, content=""):
        temp_label = Label(width=_width, markup=True, text=content)
        d = temp_label._font_properties
        dkw = dict(zip(d, [getattr(temp_label, x) for x in d]))
        from kivy.core.text.markup import MarkupLabel
        la = MarkupLabel(**dkw)

        w, h = la.render(real=False) # makes the _lines variables

        # Grab the heights dimensions of the input (guard against empty string)
        input_heights = []
        for entry in la._lines:
            heights = [item[1] for item in entry[4]]
            input_heights.extend(heights)
        input_height = max(input_heights) if len(input_heights) > 0 else 0

        return input_height*len(la._lines)
Example #2
0
    def _create_line_label(self, text, hint=False):
        # Create a label from a text, using line options
        ntext = text.replace(u'\n', u'').replace(u'\t', u' ' * self.tab_width)
        if self.password and not hint:  # Don't replace hint_text with *
            ntext = u'*' * len(ntext)
        ntext = self._get_bbcode(ntext)
        kw = self._get_line_options()
        cid = u'{}\0{}\0{}'.format(ntext, self.password, kw)
        texture = Cache_get('textinput.label', cid)

        if texture is None:
            # FIXME right now, we can't render very long line...
            # if we move on "VBO" version as fallback, we won't need to
            # do this.
            # try to find the maximum text we can handle
            label = Label(text=ntext, **kw)
            if text.find(u'\n') > 0:
                label.text = u''
            else:
                label.text = ntext
            label.refresh()

            # ok, we found it.
            texture = label.texture
            Cache_append('textinput.label', cid, texture)
            label.text = ''
        return texture
Example #3
0
    def _create_line_label(self, text):
        # Create a label from a text, using line options
        ntext = text.replace("\n", "").replace("\t", " " * self.tab_width)
        if self.password:
            ntext = "*" * len(ntext)
        ntext = self._get_bbcode(ntext)
        kw = self._get_line_options()
        cid = "%s\0%s" % (ntext, str(kw))
        texture = Cache_get("textinput.label", cid)

        if not texture:
            # FIXME right now, we can't render very long line...
            # if we move on "VBO" version as fallback, we won't need to do this.
            # try to found the maximum text we can handle
            label = Label(text=ntext, **kw)
            if text.find("\n") > 0:
                label.text = ""
            else:
                label.text = ntext
            try:
                label.refresh()
            except ValueError:
                return

            # ok, we found it.
            texture = label.texture
            Cache_append("textinput.label", cid, texture)
            label.text = ""
        return texture
Example #4
0
    def _create_line_label(self, text, hint=False):
        # Create a label from a text, using line options
        ntext = text.replace('\n', '').replace('\t', ' ' * self.tab_width)
        if self.password and not hint:  # Don't replace hint_text with *
            ntext = '*' * len(ntext)
        ntext = self._get_bbcode(ntext)
        kw = self._get_line_options()
        cid = '%s\0%s' % (ntext, str(kw))
        texture = Cache_get('textinput.label', cid)

        if not texture:
            # FIXME right now, we can't render very long line...
            # if we move on "VBO" version as fallback, we won't need to do this.
            # try to found the maximum text we can handle
            label = Label(text=ntext, **kw)
            if text.find('\n') > 0:
                label.text = ''
            else:
                label.text = ntext
            try:
                label.refresh()
            except ValueError:
                return

            # ok, we found it.
            texture = label.texture
            Cache_append('textinput.label', cid, texture)
            label.text = ''
        return texture
    def _create_line_label(self, text, hint=False):
        # Create a label from a text, using line options
        ntext = text.replace(u'\n', u'').replace(u'\t', u' ' * self.tab_width)
        if self.password and not hint:  # Don't replace hint_text with *
            ntext = u'*' * len(ntext)
        ntext = self._get_bbcode(ntext)
        kw = self._get_line_options()
        cid = u'{}\0{}\0{}'.format(ntext, self.password, kw)
        texture = Cache_get('textinput.label', cid)

        if texture is None:
            # FIXME right now, we can't render very long line...
            # if we move on "VBO" version as fallback, we won't need to
            # do this.
            # try to find the maximum text we can handle
            label = Label(text=ntext, **kw)
            if text.find(u'\n') > 0:
                label.text = u''
            else:
                label.text = ntext
            label.refresh()

            # ok, we found it.
            texture = label.texture
            Cache_append('textinput.label', cid, texture)
            label.text = ''
        return texture
Example #6
0
    def _create_line_label(self, text, hint=False):
        # Create a label from a text, using line options
        ntext = text.replace('\n', '').replace('\t', ' ' * self.tab_width)
        if self.password and not hint:  # Don't replace hint_text with *
            ntext = '*' * len(ntext)
        ntext = self._get_bbcode(ntext)
        kw = self._get_line_options()
        cid = '%s\0%s' % (ntext, str(kw))
        texture = Cache_get('textinput.label', cid)

        if not texture:
            # FIXME right now, we can't render very long line...
            # if we move on "VBO" version as fallback, we won't need to do this.
            # try to found the maximum text we can handle
            label = Label(text=ntext, **kw)
            if text.find('\n') > 0:
                label.text = ''
            else:
                label.text = ntext
            try:
                label.refresh()
            except ValueError:
                return

            # ok, we found it.
            texture = label.texture
            Cache_append('textinput.label', cid, texture)
            label.text = ''
        return texture
    def _create_label(self):
        # create the core label class according to markup value
        if self._label is not None:
            cls = self._label.__class__
        else:
            cls = None

        if cls is not CoreMarkupLabel:
            # markup have change, we need to change our rendering method.
            d = TerminalWidgetKivy._font_properties
            dkw = dict(list(zip(d, [getattr(self, x) for x in d])))
            self._label = CoreMarkupLabel(**dkw)

        self._update_line_options()
    def _create_label(self):
        # create the core label class according to markup value
        if self._label is not None:
            cls = self._label.__class__
        else:
            cls = None

        if cls is not CoreMarkupLabel:
            # markup have change, we need to change our rendering method.
            d = TerminalWidgetKivy._font_properties
            dkw = dict(list(zip(d, [getattr(self, x) for x in d])))
            self._label = CoreMarkupLabel(**dkw)

        self._update_line_options()
Example #9
0
 def checkCorrect(self, testWordStr, inputStr, decodedChar):
     if (self.gTimerState["mainGame"]):
         mainWord = MarkupLabel(testWordStr).markup[1]
         if (len(inputStr) > len(mainWord)):
             self.nextWord()  #Get new word, Reset text
         elif (len(inputStr) > 0):  #Highlight correct/wrong
             if (mainWord[len(inputStr) - 1] == decodedChar
                 ):  #Extract text from markup, then extract char at index
                 textHighlight = "#baed91"  #Correct, green
                 self.game_totalScore += 10
             else:
                 textHighlight = "#ff6961"  #Wrong, red
             self.lblTestWord.text = "[color={}]{}[/color]".format(
                 textHighlight, mainWord)
             self.lblScore.text = "Score: %05d" % self.game_totalScore
             if (len(inputStr) == len(mainWord)):  #Check if it's last word
                 self.cancelTimer(self.beforeNextWordClk)
                 self.beforeNextWordClk = Clock.schedule_once(
                     self.loadNextWord,
                     0.5)  #Wait for 0.5 second before showing next word
Example #10
0
class TerminalWidgetKivy(FocusBehavior, Widget, TerminalWidget):
    _font_properties = ('lines', 'font_size', 'font_name', 'bold', 'italic',
                        'underline', 'strikethrough',
                        'halign', 'valign', 'padding_left', 'padding_top',
                        'padding_right', 'padding_bottom',
                        'shorten', 'mipmap',
                        'line_height', 'max_lines', 'strip',
                        'split_str', 'unicode_errors',
                        'font_hinting', 'font_kerning', 'font_blended')

    def __init__(self, session, **kwargs):
        self._trigger_texture = Clock.create_trigger(self._texture_update, -1)

        super(TerminalWidgetKivy, self).__init__(**kwargs)

        self.session = session
        self.tab_width = session.get_tab_width()
        self.refresh_font()
        
        # bind all the property for recreating the texture
        d = TerminalWidgetKivy._font_properties
        fbind = self.fbind
        update = self._trigger_texture_update
        fbind('disabled', update, 'disabled')
        for x in d:
            fbind(x, update, x)

        self._label = None
        self._create_label()

        self.line_rects = {}
        self._touch_count = 0
        self.cancel_selection()
        self.font_kerning = True

        # force the texture creation
        self._trigger_texture()

    def _create_label(self):
        # create the core label class according to markup value
        if self._label is not None:
            cls = self._label.__class__
        else:
            cls = None

        if cls is not CoreMarkupLabel:
            # markup have change, we need to change our rendering method.
            d = TerminalWidgetKivy._font_properties
            dkw = dict(list(zip(d, [getattr(self, x) for x in d])))
            self._label = CoreMarkupLabel(**dkw)

        self._update_line_options()

    def _create_line_label(self):
        d = TerminalWidgetKivy._font_properties
        dkw = dict(list(zip(d, [getattr(self, x) for x in d])))
        return CoreMarkupLabel(**dkw)

    def _update_line_options(self):
        min_line_ht = self._label.get_extents('_')[1]
        self.line_height = min_line_ht
        self._label.options['color'] = [1,1,1,1]

    def _trigger_texture_update(self, name=None, source=None, value=None):
        # check if the label core class need to be switch to a new one
        if source:
            if name == 'font_size':
                self._label.options[name] = value
            else:
                self._label.options[name] = value

        if name != 'lines':
            self._trigger_texture()

    def _texture_update(self, *largs):
        self._update_line_options()

        logging.getLogger('term_widget').debug('texture update, cursor visible:{}'.format(self.cursor_visible))
        lines = [line[:] for line in self.lines]
        line_options = [line_option[:] for line_option in self.line_options]
        c_col, c_row = self.term_cursor

        self.canvas.clear()

        dy = self.line_height + self.line_spacing
        y = self.height
        x = 0

        last_f_color = self.session.cfg.default_foreground_color
        last_b_color = self.session.cfg.default_background_color
        last_mode = 0

        for i in range(len(lines)):
            x = 0
            b_x = 0

            line = lines[i]
            line_option = line_options[i] if i < len(line_options) else []

            col = 0
            last_col = 0
            text = ''
            text_parts = []

            def render_text(t, xxxx):
                cur_f_color, cur_b_color = last_f_color, last_b_color

                if last_mode & TextMode.REVERSE:
                    cur_f_color, cur_b_color = last_b_color, last_f_color

                text = ''.join(['[color=',
                                self._get_color_hex(cur_f_color),
                                ']',
                                escape_markup(t),
                                '[/color]'])

                text_parts.append(text)

                return self._add_background(t,
                                        cur_b_color, xxxx, y - (i + 1) * dy)

            last_option = None
            for col in range(len(line_option)):
                if line_option[col] is None:
                    continue

                if last_option == line_option[col]:
                    continue

                f_color, b_color, mode = line_option[col]

                n_f_color, n_b_color, n_mode = last_f_color, last_b_color, last_mode

                # foreground
                if f_color and len(f_color) > 0:
                    n_f_color = f_color
                elif f_color is None:
                    n_f_color = self.session.cfg.default_foreground_color

                # background
                if b_color and len(b_color) > 0:
                    n_b_color = b_color
                elif b_color is None:
                    n_b_color = self.session.cfg.default_background_color

                #mode
                if mode is not None:
                    n_mode = mode

                if (n_f_color, n_b_color, n_mode) == (last_f_color, last_b_color, last_mode):
                    continue

                if last_col < col:
                    if self.cursor_visible and i == c_row and last_col <= c_col and c_col < col:
                        b_x = render_text(''.join(line[last_col: c_col]), b_x)

                        tmp_l_f, last_f_color, tmp_l_b, last_b_color = \
                          last_f_color, last_b_color, last_b_color, self.session.cfg.default_cursor_color
                        b_x = render_text(''.join(line[c_col: c_col + 1]), b_x)
                        last_f_color, last_b_color = tmp_l_f, tmp_l_b

                        b_x = render_text(''.join(line[c_col + 1: col]), b_x)
                    else:
                        b_x = render_text(''.join(line[last_col: col]), b_x)

                last_col = col
                last_option = line_option[col]
                last_f_color, last_b_color, last_mode = n_f_color, n_b_color, n_mode

            if last_col < len(line):
                if self.cursor_visible and i == c_row and last_col <= c_col and c_col < len(line):
                    b_x = render_text(''.join(line[last_col: c_col]), b_x)

                    tmp_l_f, last_f_color, tmp_l_b, last_b_color = \
                          last_f_color, last_b_color, last_b_color, self.session.cfg.default_cursor_color
                    b_x = render_text(''.join(line[c_col: c_col + 1]), b_x)
                    last_f_color, last_b_color = tmp_l_f, tmp_l_b

                    b_x = render_text(''.join(line[c_col + 1:]), b_x)
                else:
                    b_x = render_text(''.join(line[last_col:]), b_x)

            if self.cursor_visible and i == c_row and c_col >= len(line):
                tmp_l_f, last_f_color, tmp_l_b, last_b_color = \
                          last_f_color, last_b_color, last_b_color, self.session.cfg.default_cursor_color
                b_x = render_text(' ', b_x)
                last_f_color, last_b_color = tmp_l_f, tmp_l_b

            #add background to fill empty cols
            if b_x < self.width:
                tmp_b_c, last_b_color = last_b_color, self.session.cfg.default_background_color
                render_text(' ' * (self.visible_cols + 1), b_x)
                last_b_color = tmp_b_c

            try:
                self._add_text(i, ''.join(text_parts), x, y - (i + 1) * dy)
            except:
                logging.exception('show text:{},x={},y={}'.format(''.join(text_parts), x, y - (i + 1) * dy))

    def _get_color_hex(self, l_color):
        return '#%02x%02x%02x%02x' % (l_color[0], l_color[1], l_color[2], l_color[3])

    def _add_background(self, text, color, x, y):
        if not text or len(text) == 0:
            return x

        cid = '%s\0%02x%02x%02x%02x' % (text, color[0], color[1], color[2], color[3])

        t = Cache_get('termwidget.b', cid)

        if t is not None:
            if self.session.cfg.debug_more:
                logging.getLogger('term_widget').debug('reuse the background texture, pos={}, {}, size={}'.format(x, y, t.size))
            self.canvas.add(Rectangle(texture=t, pos=(x , y), size=t.size))
            return x + t.size[0]

        from kivy.graphics import Color
        from kivy.graphics.instructions import InstructionGroup
        from kivy.graphics.texture import Texture

        size = Cache_get('termwidget.b_size', text)

        if size is None:
            text = self.norm_text(text)
            size = self._label.get_extents(text)
            size = (size[0], size[1] + 1)
            Cache_append('termwidget.b_size', text, size)

        t = Texture.create(size=size)

        buf = color * size[0] * size[1]
        buf = b''.join(map(chr, buf))
        t.blit_buffer(buf, colorfmt='rgba', bufferfmt='ubyte')

        Cache_append('termwidget.b', cid, t)

        self.canvas.add(Rectangle(texture=t, pos=(x , y), size=size, group='background'))

        return x + size[0]

    def _add_text(self, line_num, text, x, y):
        self.line_rects[line_num] = Rectangle(size=(0,0), pos=(x, y))

        if not text or len(text) == 0:
            return

        label = Cache_get('termwidget.label', text)

        texture = None

        if label is None:
            label = self._create_line_label()
            text = self.norm_text(text)
            label.text = text#.decode('utf_8', errors='ignore')
            label.refresh()

            if label.texture:
                label.texture.bind()

            texture = label.texture

            Cache_append('termwidget.label', text, label)
            if self.session.cfg.debug_more:
                logging.getLogger('term_widget').debug('cache the foreground texture, pos={}, {}, size={}'.format(x, y, texture.size))
        else:
            texture = label.texture
            if self.session.cfg.debug_more:
                logging.getLogger('term_widget').debug('reuse the foreground texture, pos={}, {}, size={}'.format(x, y, texture.size))

        self.line_rects[line_num] = Rectangle(texture=texture, size=texture.size, pos=(x, y), group='foreground')
        self.canvas.add(self.line_rects[line_num])

    def _get_text_width(self, text):
        width = Cache_get('termwidget.width', text)

        if width is not None:
            return width

        txt = self.norm_text(text)

        width = self._label.get_extents(txt)[0]

        Cache_append('termwidget.width', text, width)

        return width

    def refresh(self):
        self._trigger_texture()

    def on_touch_down(self, touch):
        touch_pos = touch.pos

        if not self.collide_point(*touch_pos):
            return super(TerminalWidgetKivy, self).on_touch_down(touch)

        touch.grab(self)
        self._touch_count += 1

        cursor = self._get_cursor_from_xy(*touch_pos)
        if not self._selection_touch:
            self.cancel_selection()
            self._selection_touch = touch
            self._selection_from = self._selection_to = cursor
            self._update_selection()

        self.focus = True
        return super(TerminalWidgetKivy, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if touch.grab_current is not self:
            return super(TerminalWidgetKivy, self).on_touch_move(touch)

        if self._selection_touch is touch:
            self._selection_to = self._get_cursor_from_xy(touch.x, touch.y)
            self._update_selection()

        return super(TerminalWidgetKivy, self).on_touch_move(touch)

    def on_touch_up(self, touch):
        if touch.grab_current is not self:
            return super(TerminalWidgetKivy, self).on_touch_up(touch)

        touch.ungrab(self)
        self._touch_count -= 1

        if self._selection_touch is touch:
            self._selection_to = self._get_cursor_from_xy(touch.x, touch.y)
            self._update_selection(True)

        self.focus = True
        return super(TerminalWidgetKivy, self).on_touch_up(touch)

    def _get_cursor_from_xy(self, x, y):
        '''Return the (row, col) of the cursor from an (x, y) position.
        '''
        padding_left = self.padding[0]
        padding_top = self.padding[1]
        l = self.lines
        dy = self.line_height + self.line_spacing
        cx = x - self.x
        cy = (self.top - padding_top) - y
        cy = int(boundary(round(cy / dy - 0.5), 0, len(l) - 1))
        _get_text_width = self._get_text_width
        for i in range(0, len(l[cy])):
            if _get_text_width(''.join(l[cy][:i])) + \
                  _get_text_width(l[cy][i]) * 0.6 + \
                  padding_left > cx:
                return i, cy

        return len(l[cy]), cy

    #
    # Selection control
    #
    def cancel_selection(self):
        super(TerminalWidgetKivy, self).cancel_selection()
        self._selection_touch = None

    def _update_selection(self, finished=False):
        self._selection_finished = finished

        if not finished:
            self._selection = True
        else:
            self._selection = True
            self._selection_touch = None

        self._update_graphics_selection()

    def _update_graphics_selection(self):
        if not self._selection:
            return
        self.canvas.remove_group('selection')
        dy = self.line_height + self.line_spacing

        padding_top = self.padding[1]
        padding_bottom = self.padding[3]
        _top = self.top
        y = _top - padding_top
        miny = self.y + padding_bottom
        maxy = _top - padding_top
        draw_selection = self._draw_selection

        a, b = self.get_selection()
        s1c, s1r = a
        s2c, s2r = b
        s2r += 1
        # pass only the selection lines[]
        # passing all the lines can get slow when dealing with a lot of text
        y -= s1r * dy
        _lines = self.lines
        _get_text_width = self._get_text_width
        width = self.width
        padding_left = self.padding[0]
        padding_right = self.padding[2]
        x = self.x
        canvas_add = self.canvas.add
        selection_color = self.selection_color
        for line_num, value in enumerate(_lines[s1r:s2r], start=s1r):
            r = self.line_rects[line_num]
            if miny <= y <= maxy + dy:
                draw_selection(r.pos, r.size, line_num, (s1c, s1r),
                               (s2c, s2r - 1), _lines, _get_text_width,
                               width,
                               padding_left, padding_right, x,
                               canvas_add, selection_color)
            y -= dy

    def _draw_selection(self, *largs):
        pos, size, line_num, (s1c, s1r), (s2c, s2r),\
            _lines, _get_text_width, width,\
            padding_left, padding_right, x, canvas_add, selection_color = largs
        # Draw the current selection on the widget.
        if line_num < s1r or line_num > s2r or line_num >= len(_lines):
            return
        x, y = pos
        w, h = size
        x1 = x
        x2 = x + w
        if line_num == s1r:
            lines = _lines[line_num]
            if not lines:
                return
            s1c = s1c if s1c <= len(lines) else len(lines)
            x1 += _get_text_width(''.join(lines[:s1c]))
        if line_num == s2r:
            lines = _lines[line_num]
            if not lines:
                return
            s2c = s2c if s2c <= len(lines) else len(lines)
            x2 = x + _get_text_width(''.join(lines[:s2c]))
        width_minus_padding = width - (padding_right + padding_left)
        maxx = x + width_minus_padding
        if x1 > maxx:
            return
        x1 = max(x1, x)
        x2 = min(x2, x + width_minus_padding)
        canvas_add(Color(*selection_color, group='selection'))
        canvas_add(Rectangle(
            pos=(x1, pos[1]), size=(x2 - x1, size[1] + 1), group='selection'))

    def copy_to_clipboard(self, data):
        from kivy.core.clipboard import Clipboard
        Clipboard.copy(data)

    def paste_from_clipboard(self):
        from kivy.core.clipboard import Clipboard

        return Clipboard.paste()

    def refresh_font(self):
        if self.session and self.session.cfg:
            config = self.session.cfg.config
            cfg = self.session.cfg

            self.font_name, nouse_font_name, self.font_size = cfg.get_font_info()
    #
    # Properties
    #

    selection_color = ListProperty([0.1843, 0.6549, 0.8313, .5])
    font_name = StringProperty('WenQuanYi')
    font_size = NumericProperty('17.5sp')
    line_height = NumericProperty(1.0)
    line_spacing = NumericProperty(1.0)
    bold = BooleanProperty(False)
    italic = BooleanProperty(False)
    underline = BooleanProperty(False)
    strikethrough = BooleanProperty(False)
    padding_left = NumericProperty(0)
    padding_top = NumericProperty(0)
    padding_right = NumericProperty(0)
    padding_bottom = NumericProperty(0)
    padding = ReferenceListProperty(padding_left, padding_top, padding_right, padding_bottom)
    halign = OptionProperty('left', options=['left', 'center', 'right',
                            'justify'])
    valign = OptionProperty('top',
                            options=['bottom', 'middle', 'center', 'top'])
    texture = ObjectProperty(None, allownone=True)
    texture_size = ListProperty([0, 0])
    mipmap = BooleanProperty(False)
    shorten = BooleanProperty(False)
    split_str = StringProperty('')
    unicode_errors = OptionProperty(
        'replace', options=('strict', 'replace', 'ignore'))
    max_lines = NumericProperty(0)
    strip = BooleanProperty(False)
    font_hinting = OptionProperty(
        'mono', options=[None, 'normal', 'light', 'mono'], allownone=True)
    font_kerning = BooleanProperty(True)
    font_blended = BooleanProperty(True)
Example #11
0
    def insert_text(self, substring, from_undo=False):
        self.page = self.parent.parent.parent

        if substring == "AC":
            self.text = ""
            return
        if substring == "Del":
            if self.last_press in "\r\n=":
                self.insert_text("AC")
            else:
                self.do_backspace()
            return

        if substring == "*":
            substring = "x"

        if substring == "**":
            substring = "^"

        if substring == "/":
            substring = "÷"

        if substring == "a\u00b2":
            substring = "^2"

        if substring == "ceil":
            if self.selection_text:
                self.text = self.text.replace(self.selection_text,
                                              f"ceil({self.selection_text})")
            else:
                self.text = f"ceil({self.text})"
            return
        if substring == "|a|":
            if self.selection_text:
                self.text = self.text.replace(self.selection_text,
                                              f"abs({self.selection_text})")
            else:
                self.text = f"abs({self.text})"
            return
        if substring == "floor":
            if self.selection_text:
                self.text = self.text.replace(self.selection_text,
                                              f"floor({self.selection_text})")
            else:
                self.text = f"floor({self.text})"
            return
        if substring == "\u221aa":
            if self.selection_text:
                self.text = self.text.replace(self.selection_text,
                                              f"sqrt({self.selection_text})")
            else:
                self.text = f"sqrt({self.text})"
            return

        if substring == "a!":
            substring = "!"

        if substring == str(config_data['base']) + '\u00aa':
            substring = str(config_data['base']) + '^'

        if self.last_press in "+-÷x^%\u00b1":
            if self.last_press == substring:
                return
            else:
                if self.text and substring in "+-÷x^%":
                    self.do_backspace()

        if substring in "÷x^" and (self.text == "0" or not self.text):
            return

        if ((current_page[0] == "standard"
             and substring not in string.digits + "%()+-x÷^.!=\r\n" + string.ascii_letters
             and not substring.isdecimal()
             and substring != "^2") or
            (current_page[0] == "scientific"
             and substring not in string.digits + "%()e+-x÷^.!sincotae=\r\n"
             and substring != "^2" and substring
             not in ['sin', 'cos', 'tan', 'cosec', 'cot', 'sec', 'log']
             and substring not in [
                 'sin\u00af\u00b9', 'cos\u00af\u00b9', 'tan\u00af\u00b9',
                 'cosec\u00af\u00b9', 'cot\u00af\u00b9', 'sec\u00af\u00b9'
            ] and substring != str(config_data['base'] + '^')
            and substring != '\u03c0')
                or (current_page[0] == "convert"
                    and substring not in string.digits + ".=\r\n")
                or (current_page[0] == "days"
                    and substring not in string.digits + "=\r\n")):

            return

        self.last_press = substring
        if substring in ["\r", "\n", "="]:
            if self.text == 'pass':
                self.text = ''
                self.page.parent.parent.parent.parent.options_open(
                    self.page.layout.buttons[-1][-1])
                self.modal_pass = ModalView(size_hint=(0.8, 0.8))
                self.modal_pass.add_widget(Pass())
                self.modal_pass.open()
                self.modal_pass.bind(
                    on_dismiss=lambda *args: self.page.parent.parent.parent.parent.options_close())
                return
            if self.text[-1] in "+-÷x^(%":
                self.page.preview.text = "Complete the equation first!"
                return
            self.last_press = substring
            for opr in "+-÷x^()!%":
                if self.text.count(opr):
                    break
            else:
                if current_page[0] not in ["scientific", "convert", "days"]:
                    link = False
                    if re.findall("[0-9]", self.text):
                        return
                    if self.text.count('.') == 0 and self.text.isalpha:
                        self.text = "www."+self.text+".com"
                        link = True
                    elif self.text.count('.') == 1:
                        if 'www' in self.text:
                            self.text += ".com"
                        else:
                            self.text = "www."+self.text
                        link = True

                    if self.text.count('.') == 2 or link:
                        webbrowser.get().open_new_tab(self.text)
                        self.page.preview.text = "Opened in web browser!"
                        Clock.schedule_once(lambda dt: setattr(
                            self.page.preview, 'text', ''), 1)
                        self.text = ''
                    return
            self.page.old_text = self.text
            self.page.preview.text = "[ref=self.old_text]" + \
                self.text + "[/ref]"

            if current_page[0] == "standard":
                substring = self.text
                substring = self.replace(substring)
                if "!" in self.text:
                    substring = self.fac_solve(self.text)
                try:
                    substring = Basic(exp=substring).solution
                except:
                    self.page.preview.text = (
                        "There's some error!")
                    return
            elif current_page[0] == "scientific":
                substring = self.text
                substring = self.replace(substring)
                if "!" in substring:
                    substring = self.fac_solve(substring)

                rad = 1 if self.page.layout.buttons[0][2].text == "RAD" else 0
                for r in ["sin", "tan", "cos", "cot", "cosec", "sec"]:
                    substring = substring.replace(r, f"Basic(rad={rad}).{r}")
                for r in [
                        "sin\u00af\u00b9", "tan\u00af\u00b9",
                        "cos\u00af\u00b9", "cot\u00af\u00b9",
                        "cosec\u00af\u00b9", "sec\u00af\u00b9"
                ]:
                    r1 = r.replace("\u00af\u00b9", "")
                    substring = substring.replace(r, f"a{r1}")
                substring = substring.replace(
                    "log", f"Basic(base={config_data['base']}).log")
                from math import factorial

                try:
                    substring = Basic(exp=substring).solution
                except:
                    self.page.preview.text = "Something went wrong!"
                    return
            elif current_page[0] == "convert":
                if not self.quantity:
                    self.quantity = self.page.layout.buttons[0][1].text
                if not self.from_unit:
                    self.from_unit = self.page.layout.buttons[1][1].text
                if not self.to_unit:
                    self.to_unit = self.page.layout.buttons[1][3].text
                try:
                    substring = (str(
                        eval("Convert." + self.quantity)
                        (self.text.split()[0], self.from_unit, self.to_unit)) +
                        " " + self.to_unit)
                    self.page.preview.text = ("[ref=self.old_text]" +
                                              self.text + " " +
                                              self.from_unit + "[/ref]")
                except:
                    self.page.preview.text = "There's some error!"
                    return
            elif current_page[0] == "days":
                try:
                    substring = days_number(
                        self.page.layout.buttons[1][0].text,
                        self.page.layout.buttons[1][2].text,
                    )
                    if self.page.layout.buttons[2][-1].state == "down":
                        substring += 1
                    if self.page.layout.buttons[3][-1].state == "down":
                        substring -= 1
                    substring = str(substring)
                    self.page.preview.text = f"{self.page.layout.buttons[1][0].text} to {self.page.layout.buttons[1][2].text}"
                except:
                    self.page.preview.text = "Oops! Couldn't find that!"
                    return
            self.solved = True
            write_history(
                self.page.preview.text if current_page[0] == "days" else
                MarkupLabel(self.page.preview.text).markup[1],
                substring,
                current_page[0],
            )
            self.text = ""

        if self.text == "0" and substring != ".":
            self.do_backspace()
        for sub_ in substring:
            return super(Text, self).insert_text(substring,
                                                 from_undo=from_undo)
Example #12
0
def modal_update_old(self):
    print("modal_update_old()")
    #def modal_update(columnHeadings, rows):

    # How do I get this data from kivy; inside this function? drill down to the .text
    # Where are the columnHeading fields in kivy data? in cells with .id == "Header_Label".
    # grid object is present here.
    # - - - - - - - - - -
    print("From DataGrid.remove_row(self, n_cols({}), instance, **kwargs)".
          format(n_cols))
    childs = grid.children
    print(str(childs))
    #childs = self.parent.children
    selected = 0  # No cells selected, yet.
    doUpdate = True
    theData = {}
    for ch in childs:
        print("ch: " + str(ch))
        for c in reversed(ch.children):
            print("c: " + str(c))
            if c.id != "Header_Label":
                print("c.id: " + str(c.id))
                if c.state == "down":
                    #print(str(c.state))
                    #print("self.remove_widget(c) was here.")
                    #self.remove_widget(c)
                    nml2 = MarkupLabel(c.text).markup
                    print(" Value: " + nml2[1])
                    print(
                        str(c.id) + '   -   ' + str(c.state) + ' - ' + nml2[1])
                    theData[selected] = nml2[1]  # Keep this data for update.
                    print("theData: " + theData)
                    selected += 1  # Another cell selected.
    if selected > 4:
        print("Only a single row may be selected for update.")
        doUpdate = False
    if selected == 0:  # If not selected, above; then pick the first row in memory which is the last row on the screen.
        print("You must select a row to be updated.")
        doUpdate = False
        #print(str(selected))
        #for ch in childs:
        #    print(str(ch))
        #    count_01 = n_cols
        #    count_02 = 0
        #    count = -1
        #    #count = 0
        #    # TODO; next 3 lines loop forever. grid.children must be the wrong object to set this to?
        #    while True:
        #        count += 1
        #        if count >= n_cols:
        #            break
        #        #while (count + + < n_cols):
        #        print(count)
        #        if n_cols != len(ch.children):
        #            for c in ch.children:
        #                print(c)
        #                if c.id != "Header_Label":
        #                    print("Length: " + str(len(ch.children)), end='')
        #                    print(" N_cols: " + str(n_cols + 1), end='')
        #
        #                    print(" self.remove_widget(c) was here.")
        #                    #self.remove_widget(c)
        #                    #count += 1
        #                    break
        #                else:
        #                    break
        #        else:
        #            break
    if not doUpdate:
        # TODO; Display the error message in kivy so folks can see it.
        print("Can not doUpdate because of some previous error.")
        exit()

    print("doUpdate")
    print("Data: " + theData)
    # - - - - - - - - - -
    columnHeadings = ['ID', 'Nome', 'Preco', 'IVA']
    # TODO; where are the product fields inside kivy data?
    rows = {
        1: ['000', 'Product Name 1', '123.45', '23'],
        2: ['001', 'Product Name 2', '234.56', '34'],
        3: ['002', 'Product Name 3', '345.67', '45']
    }
    #def modal_update(self, columnHeadings, rows):
    print("modal_update {}".format(columnHeadings))
    print("{}".format(rows))
    # TODO; copied modal_insert to modal_update; now make modal_update work!
    # data should contain Column headings (labels) and Data.
    # columnHeadings = ['ID', 'Nome', 'Preco', 'IVA']
    # rows = {1:['000', 'Product Name 1', '123.45', '23'],
    #		  2:['001', 'Product Name 2', '234.56', '34']
    #		  3:['002', 'Product Name 3', '345.67', '45']
    # 			}
    # modal_update(self, columnHeadings, rows)

    # TODO; Make this variable according to the count of items in columnHeadings and rows['1']!
    elementsCH = len(columnHeadings)
    elementsR = len(
        rows[1])  # TODO; shouldn't we iterate through the selected rows?
    if elementsCH != elementsR:
        # TODO; logg or msgbox this!
        # error() inform somebody
        print(
            "Something is wrong, the number of columnHeadings ({}) != the number of rows (())!"
            .format(elementsCH, elementsR))
        exit()

    insertion_grid = GridLayout(cols=2)
    # TODO; Apply iteration here!
    # for elementsR:
    #	lbl? = Label(text=columnHeadings[?], id="lbl")
    #	txt? = TextInput(text=rows['?'][0], id="txtinp")
    lbl1 = Label(text=columnHeadings[0], id="lbl")
    txt1 = TextInput(text=rows[1][0], id="txtinp")
    insertion_grid.add_widget(lbl1)
    insertion_grid.add_widget(txt1)
    lbl2 = Label(text=columnHeadings[1], id="lbl")
    txt2 = TextInput(text=rows[1][1], id="txtinp")
    insertion_grid.add_widget(lbl2)
    insertion_grid.add_widget(txt2)
    lbl3 = Label(text=columnHeadings[2], id="lbl")
    txt3 = TextInput(text=rows[1][2], id="txtinp")
    insertion_grid.add_widget(lbl3)
    insertion_grid.add_widget(txt3)
    lbl4 = Label(text=columnHeadings[3], id="lbl")
    txt4 = TextInput(text=rows[1][3], id="txtinp")
    insertion_grid.add_widget(lbl4)
    insertion_grid.add_widget(txt4)
    #lbl1 = Label(text='ID', id="lbl")
    #lbl2 = Label(text='Nome', id="lbl")
    #lbl3 = Label(text='Preco', id="lbl")
    #lbl4 = Label(text='IVA', id="lbl")
    #txt1 = TextInput(text='000', id="txtinp")
    #txt2 = TextInput(text='Product Name', id="txtinp")
    #txt3 = TextInput(text='123.45', id="txtinp")
    #txt4 = TextInput(text='23', id="txtinp")

    # create content and assign to the view

    content = Button(text='Close me!')

    modal_layout = BoxLayout(orientation="vertical")
    modal_layout.add_widget(insertion_grid)

    def update_def(self):
        input_list = []
        for text_inputs in reversed(self.parent.children[2].children):
            if text_inputs.id == "txtinp":
                input_list.append(text_inputs.text)
        print(input_list)
        # TODO; how do I make this an UPDATE?
        print(input_list)
        grid.update_row(input_list, body_alignment, col_size, self)
        # def update_row(self, n_cols, instance, **kwargs):
        #grid.add_row(input_list, body_alignment, col_size, self)
        # def add_row(self, row_data, row_align, cols_size, instance, **kwargs):

    # print view
    # view.dismiss

    update_btn = Button(text="Update", on_press=update_def)
    modal_layout.add_widget(update_btn)
    modal_layout.add_widget(content)

    view = ModalView(auto_dismiss=False)

    view.add_widget(modal_layout)
    # bind the on_press event of the button to the dismiss function
    content.bind(on_press=view.dismiss)
    update_btn.bind(on_release=view.dismiss)

    view.open()
Example #13
0
    def update_row(self, row_data, row_align, cols_size, instance, **kwargs):
        #def add_row(self, row_data, row_align, cols_size, instance, **kwargs):
        #def update_row(self, n_cols, instance, **kwargs):

        print("update_row()")
        doUpdate = True
        theData = {}
        childs = self.parent.children  # DataGrid.children
        selected = 0
        for ch in childs:  # DataGrid.children[n] n = all children
            print("ch: " + str(ch))
            for c in reversed(ch.children):  # DataGrid.children[n].children
                print("c: " + str(c))
                if c.id != "Header_Label":
                    print("c.id: " + str(c.id))
                    # HINT; google kivy datagrid state and find source that references kivy objects.
                    if c.state == "down":  # state can be ('normal', 'down', ...)
                        print(str(c.id) + '   -   ' + str(c.state), end='')
                        nml2 = MarkupLabel(c.text).markup
                        print("A cell or column item on the selected row.",
                              end='')
                        print(" N_cols: " + str(n_cols), end='')
                        print(" Id: " + str(c.id), end='')
                        print(" Length: " + str(len(ch.children)), end='')
                        print(" Value: " + nml2[1])
                        #self.remove_widget(c)
                        #print (str(c.id) + '   -   ' + str(c.state))
                        theData[selected] = nml2[
                            1]  # Keep this data for update.
                        print("theData: " + str(theData))
                        selected += 1
        if selected == 0:  # None were found to be state='down' so delete something - the bottom row.
            # But, interestingly, the bottom row on the screen is the first row in memory.
            # Except that items that have .id == 'Header_Label' are the first row(s) in memory.
            for ch in childs:
                count_01 = n_cols
                count_02 = 0
                selected = 0
                while (
                        selected < n_cols
                ):  # Number of columns in a row; must delete all columns in the row.
                    if n_cols != len(ch.children):
                        for c in ch.children:
                            if c.id != "Header_Label":
                                print(
                                    "A cell or column item on the selected row.",
                                    end='')
                                #print("Data: " + str(c.text))
                                nml2 = MarkupLabel(c.text).markup
                                #print("~m: {}".format(nml2))
                                print("N_cols: " + str(n_cols), end='')
                                print(" Count: " + str(selected), end='')
                                print(" Id: " + str(c.id), end='')
                                print(" Length: " + str(len(ch.children)),
                                      end='')
                                print(" Value: " + nml2[1])

                                theData[selected] = nml2[
                                    1]  # Keep this data for update.
                                print("theData: " + str(theData))
                                #self.remove_widget(c) # there goes one of the columns in the row.
                                selected += 1
                                break
                            else:
                                break
                    else:
                        break
        if not doUpdate:
            # TODO; Display the error message in kivy so folks can see it.
            print("Can not doUpdate because of some previous error.")
            exit()
        print("If the data is OK then do the ENTRY and UPDATE here.")

        print("doUpdate")
        print("Data: " + str(theData))
        print("TODO; ENTRY screen")
        print("TODO; rob entry screen from add_row?")
        print("TODO; UPDATE DataGrid")
        print("TODO; UPDATE database")
        print("Done updating items.")
class TerminalWidgetKivy(FocusBehavior, Widget, TerminalWidget):
    _font_properties = ('lines', 'font_size', 'font_name', 'bold', 'italic',
                        'underline', 'strikethrough', 'halign', 'valign',
                        'padding_left', 'padding_top', 'padding_right',
                        'padding_bottom', 'shorten', 'mipmap', 'line_height',
                        'max_lines', 'strip', 'split_str', 'unicode_errors',
                        'font_hinting', 'font_kerning', 'font_blended')

    def __init__(self, session, **kwargs):
        self._trigger_texture = Clock.create_trigger(self._texture_update, -1)

        super(TerminalWidgetKivy, self).__init__(**kwargs)

        self.session = session
        self.tab_width = session.get_tab_width()
        self.refresh_font()

        # bind all the property for recreating the texture
        d = TerminalWidgetKivy._font_properties
        fbind = self.fbind
        update = self._trigger_texture_update
        fbind('disabled', update, 'disabled')
        for x in d:
            fbind(x, update, x)

        self._label = None
        self._create_label()

        self.line_rects = {}
        self._touch_count = 0
        self.cancel_selection()
        self.font_kerning = True

        # force the texture creation
        self._trigger_texture()

    def _create_label(self):
        # create the core label class according to markup value
        if self._label is not None:
            cls = self._label.__class__
        else:
            cls = None

        if cls is not CoreMarkupLabel:
            # markup have change, we need to change our rendering method.
            d = TerminalWidgetKivy._font_properties
            dkw = dict(list(zip(d, [getattr(self, x) for x in d])))
            self._label = CoreMarkupLabel(**dkw)

        self._update_line_options()

    def _create_line_label(self):
        d = TerminalWidgetKivy._font_properties
        dkw = dict(list(zip(d, [getattr(self, x) for x in d])))
        return CoreMarkupLabel(**dkw)

    def _update_line_options(self):
        min_line_ht = self._label.get_extents('_')[1]
        self.line_height = min_line_ht
        self._label.options['color'] = [1, 1, 1, 1]

    def _trigger_texture_update(self, name=None, source=None, value=None):
        # check if the label core class need to be switch to a new one
        if source:
            if name == 'font_size':
                self._label.options[name] = value
            else:
                self._label.options[name] = value

        if name != 'lines':
            self._trigger_texture()

    def _texture_update(self, *largs):
        self._update_line_options()

        logging.getLogger('term_widget').debug(
            'texture update, cursor visible:{}'.format(self.cursor_visible))
        lines = [line[:] for line in self.lines]
        line_options = [line_option[:] for line_option in self.line_options]
        c_col, c_row = self.term_cursor

        self.canvas.clear()

        dy = self.line_height + self.line_spacing
        y = self.height
        x = 0

        last_f_color = self.session.cfg.default_foreground_color
        last_b_color = self.session.cfg.default_background_color
        last_mode = 0

        for i in range(len(lines)):
            x = 0
            b_x = 0

            line = lines[i]
            line_option = line_options[i] if i < len(line_options) else []

            col = 0
            last_col = 0
            text = ''
            text_parts = []

            def render_text(t, xxxx):
                cur_f_color, cur_b_color = last_f_color, last_b_color

                if last_mode & TextMode.REVERSE:
                    cur_f_color, cur_b_color = last_b_color, last_f_color

                text = ''.join([
                    '[color=',
                    self._get_color_hex(cur_f_color), ']',
                    escape_markup(t), '[/color]'
                ])

                text_parts.append(text)

                return self._add_background(t, cur_b_color, xxxx,
                                            y - (i + 1) * dy)

            last_option = None
            for col in range(len(line_option)):
                if line_option[col] is None:
                    continue

                if last_option == line_option[col]:
                    continue

                f_color, b_color, mode = line_option[col]

                n_f_color, n_b_color, n_mode = last_f_color, last_b_color, last_mode

                # foreground
                if f_color and len(f_color) > 0:
                    n_f_color = f_color
                elif f_color is None:
                    n_f_color = self.session.cfg.default_foreground_color

                # background
                if b_color and len(b_color) > 0:
                    n_b_color = b_color
                elif b_color is None:
                    n_b_color = self.session.cfg.default_background_color

                #mode
                if mode is not None:
                    n_mode = mode

                if (n_f_color, n_b_color, n_mode) == (last_f_color,
                                                      last_b_color, last_mode):
                    continue

                if last_col < col:
                    if self.cursor_visible and i == c_row and last_col <= c_col and c_col < col:
                        b_x = render_text(''.join(line[last_col:c_col]), b_x)

                        tmp_l_f, last_f_color, tmp_l_b, last_b_color = \
                          last_f_color, last_b_color, last_b_color, self.session.cfg.default_cursor_color
                        b_x = render_text(''.join(line[c_col:c_col + 1]), b_x)
                        last_f_color, last_b_color = tmp_l_f, tmp_l_b

                        b_x = render_text(''.join(line[c_col + 1:col]), b_x)
                    else:
                        b_x = render_text(''.join(line[last_col:col]), b_x)

                last_col = col
                last_option = line_option[col]
                last_f_color, last_b_color, last_mode = n_f_color, n_b_color, n_mode

            if last_col < len(line):
                if self.cursor_visible and i == c_row and last_col <= c_col and c_col < len(
                        line):
                    b_x = render_text(''.join(line[last_col:c_col]), b_x)

                    tmp_l_f, last_f_color, tmp_l_b, last_b_color = \
                          last_f_color, last_b_color, last_b_color, self.session.cfg.default_cursor_color
                    b_x = render_text(''.join(line[c_col:c_col + 1]), b_x)
                    last_f_color, last_b_color = tmp_l_f, tmp_l_b

                    b_x = render_text(''.join(line[c_col + 1:]), b_x)
                else:
                    b_x = render_text(''.join(line[last_col:]), b_x)

            if self.cursor_visible and i == c_row and c_col >= len(line):
                tmp_l_f, last_f_color, tmp_l_b, last_b_color = \
                          last_f_color, last_b_color, last_b_color, self.session.cfg.default_cursor_color
                b_x = render_text(' ', b_x)
                last_f_color, last_b_color = tmp_l_f, tmp_l_b

            #add background to fill empty cols
            if b_x < self.width:
                tmp_b_c, last_b_color = last_b_color, self.session.cfg.default_background_color
                render_text(' ' * (self.visible_cols + 1), b_x)
                last_b_color = tmp_b_c

            try:
                self._add_text(i, ''.join(text_parts), x, y - (i + 1) * dy)
            except:
                logging.exception('show text:{},x={},y={}'.format(
                    ''.join(text_parts), x, y - (i + 1) * dy))

    def _get_color_hex(self, l_color):
        return '#%02x%02x%02x%02x' % (l_color[0], l_color[1], l_color[2],
                                      l_color[3])

    def _add_background(self, text, color, x, y):
        if not text or len(text) == 0:
            return x

        cid = '%s\0%02x%02x%02x%02x' % (text, color[0], color[1], color[2],
                                        color[3])

        t = Cache_get('termwidget.b', cid)

        if t is not None:
            if self.session.cfg.debug_more:
                logging.getLogger('term_widget').debug(
                    'reuse the background texture, pos={}, {}, size={}'.format(
                        x, y, t.size))
            self.canvas.add(Rectangle(texture=t, pos=(x, y), size=t.size))
            return x + t.size[0]

        from kivy.graphics import Color
        from kivy.graphics.instructions import InstructionGroup
        from kivy.graphics.texture import Texture

        size = Cache_get('termwidget.b_size', text)

        if size is None:
            text = self.norm_text(text)
            size = self._label.get_extents(text)
            size = (size[0], size[1] + 1)
            Cache_append('termwidget.b_size', text, size)

        t = Texture.create(size=size)

        buf = color * size[0] * size[1]
        buf = b''.join(map(chr, buf))
        t.blit_buffer(buf, colorfmt='rgba', bufferfmt='ubyte')

        Cache_append('termwidget.b', cid, t)

        self.canvas.add(
            Rectangle(texture=t, pos=(x, y), size=size, group='background'))

        return x + size[0]

    def _add_text(self, line_num, text, x, y):
        self.line_rects[line_num] = Rectangle(size=(0, 0), pos=(x, y))

        if not text or len(text) == 0:
            return

        label = Cache_get('termwidget.label', text)

        texture = None

        if label is None:
            label = self._create_line_label()
            text = self.norm_text(text)
            label.text = text  #.decode('utf_8', errors='ignore')
            label.refresh()

            if label.texture:
                label.texture.bind()

            texture = label.texture

            Cache_append('termwidget.label', text, label)
            if self.session.cfg.debug_more:
                logging.getLogger('term_widget').debug(
                    'cache the foreground texture, pos={}, {}, size={}'.format(
                        x, y, texture.size))
        else:
            texture = label.texture
            if self.session.cfg.debug_more:
                logging.getLogger('term_widget').debug(
                    'reuse the foreground texture, pos={}, {}, size={}'.format(
                        x, y, texture.size))

        self.line_rects[line_num] = Rectangle(texture=texture,
                                              size=texture.size,
                                              pos=(x, y),
                                              group='foreground')
        self.canvas.add(self.line_rects[line_num])

    def _get_text_width(self, text):
        width = Cache_get('termwidget.width', text)

        if width is not None:
            return width

        txt = self.norm_text(text)

        width = self._label.get_extents(txt)[0]

        Cache_append('termwidget.width', text, width)

        return width

    def refresh(self):
        self._trigger_texture()

    def on_touch_down(self, touch):
        touch_pos = touch.pos

        if not self.collide_point(*touch_pos):
            return super(TerminalWidgetKivy, self).on_touch_down(touch)

        touch.grab(self)
        self._touch_count += 1

        cursor = self._get_cursor_from_xy(*touch_pos)
        if not self._selection_touch:
            self.cancel_selection()
            self._selection_touch = touch
            self._selection_from = self._selection_to = cursor
            self._update_selection()

        self.focus = True
        return super(TerminalWidgetKivy, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if touch.grab_current is not self:
            return super(TerminalWidgetKivy, self).on_touch_move(touch)

        if self._selection_touch is touch:
            self._selection_to = self._get_cursor_from_xy(touch.x, touch.y)
            self._update_selection()

        return super(TerminalWidgetKivy, self).on_touch_move(touch)

    def on_touch_up(self, touch):
        if touch.grab_current is not self:
            return super(TerminalWidgetKivy, self).on_touch_up(touch)

        touch.ungrab(self)
        self._touch_count -= 1

        if self._selection_touch is touch:
            self._selection_to = self._get_cursor_from_xy(touch.x, touch.y)
            self._update_selection(True)

        self.focus = True
        return super(TerminalWidgetKivy, self).on_touch_up(touch)

    def _get_cursor_from_xy(self, x, y):
        '''Return the (row, col) of the cursor from an (x, y) position.
        '''
        padding_left = self.padding[0]
        padding_top = self.padding[1]
        l = self.lines
        dy = self.line_height + self.line_spacing
        cx = x - self.x
        cy = (self.top - padding_top) - y
        cy = int(boundary(round(cy / dy - 0.5), 0, len(l) - 1))
        _get_text_width = self._get_text_width
        for i in range(0, len(l[cy])):
            if _get_text_width(''.join(l[cy][:i])) + \
                  _get_text_width(l[cy][i]) * 0.6 + \
                  padding_left > cx:
                return i, cy

        return len(l[cy]), cy

    #
    # Selection control
    #
    def cancel_selection(self):
        super(TerminalWidgetKivy, self).cancel_selection()
        self._selection_touch = None

    def _update_selection(self, finished=False):
        self._selection_finished = finished

        if not finished:
            self._selection = True
        else:
            self._selection = True
            self._selection_touch = None

        self._update_graphics_selection()

    def _update_graphics_selection(self):
        if not self._selection:
            return
        self.canvas.remove_group('selection')
        dy = self.line_height + self.line_spacing

        padding_top = self.padding[1]
        padding_bottom = self.padding[3]
        _top = self.top
        y = _top - padding_top
        miny = self.y + padding_bottom
        maxy = _top - padding_top
        draw_selection = self._draw_selection

        a, b = self.get_selection()
        s1c, s1r = a
        s2c, s2r = b
        s2r += 1
        # pass only the selection lines[]
        # passing all the lines can get slow when dealing with a lot of text
        y -= s1r * dy
        _lines = self.lines
        _get_text_width = self._get_text_width
        width = self.width
        padding_left = self.padding[0]
        padding_right = self.padding[2]
        x = self.x
        canvas_add = self.canvas.add
        selection_color = self.selection_color
        for line_num, value in enumerate(_lines[s1r:s2r], start=s1r):
            r = self.line_rects[line_num]
            if miny <= y <= maxy + dy:
                draw_selection(r.pos, r.size, line_num, (s1c, s1r),
                               (s2c, s2r - 1), _lines, _get_text_width, width,
                               padding_left, padding_right, x, canvas_add,
                               selection_color)
            y -= dy

    def _draw_selection(self, *largs):
        pos, size, line_num, (s1c, s1r), (s2c, s2r),\
            _lines, _get_text_width, width,\
            padding_left, padding_right, x, canvas_add, selection_color = largs
        # Draw the current selection on the widget.
        if line_num < s1r or line_num > s2r or line_num >= len(_lines):
            return
        x, y = pos
        w, h = size
        x1 = x
        x2 = x + w
        if line_num == s1r:
            lines = _lines[line_num]
            if not lines:
                return
            s1c = s1c if s1c <= len(lines) else len(lines)
            x1 += _get_text_width(''.join(lines[:s1c]))
        if line_num == s2r:
            lines = _lines[line_num]
            if not lines:
                return
            s2c = s2c if s2c <= len(lines) else len(lines)
            x2 = x + _get_text_width(''.join(lines[:s2c]))
        width_minus_padding = width - (padding_right + padding_left)
        maxx = x + width_minus_padding
        if x1 > maxx:
            return
        x1 = max(x1, x)
        x2 = min(x2, x + width_minus_padding)
        canvas_add(Color(*selection_color, group='selection'))
        canvas_add(
            Rectangle(pos=(x1, pos[1]),
                      size=(x2 - x1, size[1] + 1),
                      group='selection'))

    def copy_to_clipboard(self, data):
        from kivy.core.clipboard import Clipboard
        Clipboard.copy(data)

    def paste_from_clipboard(self):
        from kivy.core.clipboard import Clipboard

        return Clipboard.paste()

    def refresh_font(self):
        if self.session and self.session.cfg:
            config = self.session.cfg.config
            cfg = self.session.cfg

            self.font_name, nouse_font_name, self.font_size = cfg.get_font_info(
            )

    #
    # Properties
    #

    selection_color = ListProperty([0.1843, 0.6549, 0.8313, .5])
    font_name = StringProperty('WenQuanYi')
    font_size = NumericProperty('17.5sp')
    line_height = NumericProperty(1.0)
    line_spacing = NumericProperty(1.0)
    bold = BooleanProperty(False)
    italic = BooleanProperty(False)
    underline = BooleanProperty(False)
    strikethrough = BooleanProperty(False)
    padding_left = NumericProperty(0)
    padding_top = NumericProperty(0)
    padding_right = NumericProperty(0)
    padding_bottom = NumericProperty(0)
    padding = ReferenceListProperty(padding_left, padding_top, padding_right,
                                    padding_bottom)
    halign = OptionProperty('left',
                            options=['left', 'center', 'right', 'justify'])
    valign = OptionProperty('top',
                            options=['bottom', 'middle', 'center', 'top'])
    texture = ObjectProperty(None, allownone=True)
    texture_size = ListProperty([0, 0])
    mipmap = BooleanProperty(False)
    shorten = BooleanProperty(False)
    split_str = StringProperty('')
    unicode_errors = OptionProperty('replace',
                                    options=('strict', 'replace', 'ignore'))
    max_lines = NumericProperty(0)
    strip = BooleanProperty(False)
    font_hinting = OptionProperty('mono',
                                  options=[None, 'normal', 'light', 'mono'],
                                  allownone=True)
    font_kerning = BooleanProperty(True)
    font_blended = BooleanProperty(True)