diff --git a/deluge/ui/console/colors.py b/deluge/ui/console/colors.py index 3df92294e..2dd5039fe 100644 --- a/deluge/ui/console/colors.py +++ b/deluge/ui/console/colors.py @@ -33,6 +33,8 @@ # # +from deluge.ui.console.modes import format_utils + try: import curses except ImportError: @@ -150,6 +152,24 @@ def get_line_length(line, encoding="UTF-8"): line = replace_tabs(line) return len(line) +def get_line_width(line, encoding="UTF-8"): + """ + Get width of string considering double width characters + + """ + if line.count("{!") != line.count("!}"): + raise BadColorString("Number of {! is not equal to number of !}") + + if isinstance(line, unicode): + line = line.encode(encoding, "replace") + + # Remove all the color tags + line = strip_colors(line) + + # Replace tabs with the appropriate amount of spaces + line = replace_tabs(line) + return format_utils.strwidth(line) + def parse_color_string(s, encoding="UTF-8"): """ Parses a string and returns a list of 2-tuples (color, string). diff --git a/deluge/ui/console/modes/format_utils.py b/deluge/ui/console/modes/format_utils.py index 8ca7b33a2..87c5a7e33 100644 --- a/deluge/ui/console/modes/format_utils.py +++ b/deluge/ui/console/modes/format_utils.py @@ -201,3 +201,33 @@ def wrap_string(string,width,min_lines=0,strip_colors=True): ret.append(" ") return ret + +from unicodedata import east_asian_width + +def strwidth(string): + """ + Measure width of a string considering asian double width characters + """ + if not isinstance(string, unicode): + string = unicode(string, 'utf-8') + eaw = east_asian_width + length = sum( [1 + (eaw(c) in ['W','F']) for c in string] ) + #Using list comprehenstion for improved performance + #The code above is equal to: + #length = 0 + #for char in string: + #length += 1 + #if east_asian_width(char) in ['W','F']: + #length += 1 + return length + +def pad_string(string, length, character=" ", side="right"): + """ + Pad string with specified character to desired length, considering double width characters. + """ + w = strwidth(string) + diff = length - w + if side == "left": + return "%s%s" % (character * diff, string) + elif side == "right": + return "%s%s" % (string, character * diff) \ No newline at end of file diff --git a/deluge/ui/console/modes/legacy.py b/deluge/ui/console/modes/legacy.py index 2b5f4a0a1..618ecb8bc 100644 --- a/deluge/ui/console/modes/legacy.py +++ b/deluge/ui/console/modes/legacy.py @@ -47,6 +47,7 @@ from deluge.ui.client import client import deluge.component as component from deluge.ui.console.modes import format_utils +strwidth = format_utils.strwidth import logging,os log = logging.getLogger(__name__) @@ -353,7 +354,7 @@ class Legacy(BaseMode): for line in text.splitlines(): # We need to check for line lengths here and split as necessary try: - line_length = colors.get_line_length(line) + line_length = colors.get_line_width(line) except colors.BadColorString: log.error("Passed a bad colored line: %s", line) continue @@ -364,11 +365,11 @@ class Legacy(BaseMode): s_len = 0 # We need to split this over multiple lines for chunk in get_line_chunks(line): - if (len(chunk[1]) + s_len) < (self.cols - 1): + if (strwidth(chunk[1]) + s_len) < (self.cols - 1): # This chunk plus the current string in 's' isn't over # the maximum width, so just append the color tag and text s += chunk[0] + chunk[1] - s_len += len(chunk[1]) + s_len += strwidth(chunk[1]) else: # The chunk plus the current string in 's' is too long. # We need to take as much of the chunk and put it into 's' @@ -379,7 +380,7 @@ class Legacy(BaseMode): self.lines.append(s) # Start a new 's' with the remainder chunk s = chunk[0] + chunk[1][remain:] - s_len = len(chunk[1][remain:]) + s_len = strwidth(chunk[1][remain:]) # Append the final string which may or may not be the full width if s: self.lines.append(s) @@ -411,13 +412,13 @@ class Legacy(BaseMode): for index, (color, s) in enumerate(parsed): if index + 1 == len(parsed): # This is the last string so lets append some " " to it - s += " " * (self.cols - (col + len(s)) - 1) + s += " " * (self.cols - (col + strwidth(s)) - 1) try: self.stdscr.addstr(row, col, s, color) except curses.error: pass - col += len(s) + col += strwidth(s) def do_command(self, cmd):