def write_nack(self): color = get_theme().COLOR_CHAR_NACK self._win.attron(to_curses_attr(color)) self.addstr(get_theme().CHAR_NACK) self._win.attroff(to_curses_attr(color)) self.addstr(' ') return poopt.wcswidth(get_theme().CHAR_NACK) + 1
def write_ack(self) -> int: color = get_theme().COLOR_CHAR_ACK self._win.attron(to_curses_attr(color)) self.addstr(get_theme().CHAR_ACK_RECEIVED) self._win.attroff(to_curses_attr(color)) self.addstr(' ') return poopt.wcswidth(get_theme().CHAR_ACK_RECEIVED) + 1
def write_participants_number(self, room): theme = get_theme() color = to_curses_attr(theme.COLOR_INFORMATION_BAR) self.addstr('{', color) self.addstr(str(len(room.users)), to_curses_attr(theme.COLOR_GROUPCHAT_NAME)) self.addstr('} ', color)
def write_room_name(self, room): theme = get_theme() color = to_curses_attr(theme.COLOR_INFORMATION_BAR) self.addstr('[', color) self.addstr(room.name, to_curses_attr(theme.COLOR_GROUPCHAT_NAME)) self.addstr(']', color)
def write_contact_jid(self, jid): """ Just write the jid that we are talking to """ self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self.addstr(jid.full, to_curses_attr(get_theme().COLOR_CONVERSATION_NAME)) self.addstr('] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def rewrite_text(self) -> None: """ Refresh the line onscreen, but first, always adjust the view_pos. Also, each FORMAT_CHAR+attr_char count only take one screen column (this is done in _addstr_colored_lite), we have to do some special calculations to find the correct length of text to display, and the position of the cursor. """ self.adjust_view_pos() text = self.text self._win.erase() if self.color: self._win.attron(to_curses_attr(self.color)) displayed_text = text[self.view_pos:self.view_pos + self.width - 1].replace('\t', '\x18') self._win.attrset(0) self._addstr_colored_lite(displayed_text) # Fill the rest of the line with the input color if self.color: (_, x) = self._win.getyx() size = self.width - x self.addnstr(' ' * size, size, to_curses_attr(self.color)) self.addstr(0, poopt.wcswidth( displayed_text[:self.pos - self.view_pos]), '') if self.color: self._win.attroff(to_curses_attr(self.color)) curses.curs_set(1) self._refresh()
def rewrite_text(self): """ Refresh the line onscreen, but first, always adjust the view_pos. Also, each FORMAT_CHAR+attr_char count only take one screen column (this is done in _addstr_colored_lite), we have to do some special calculations to find the correct length of text to display, and the position of the cursor. """ self.adjust_view_pos() text = self.text self._win.erase() if self.color: self._win.attron(to_curses_attr(self.color)) displayed_text = text[self.view_pos: self.view_pos + self.width - 1].replace( '\t', '\x18') self._win.attrset(0) self._addstr_colored_lite(displayed_text) # Fill the rest of the line with the input color if self.color: (_, x) = self._win.getyx() size = self.width - x self.addnstr(' ' * size, size, to_curses_attr(self.color)) self.addstr(0, poopt.wcswidth( displayed_text[:self.pos - self.view_pos]), '') if self.color: self._win.attroff(to_curses_attr(self.color)) curses.curs_set(1) self._refresh()
def write_room_name(self, name): jid = safeJID(name) room_name, nick = jid.bare, jid.resource theme = get_theme() self.addstr(nick, to_curses_attr(theme.COLOR_PRIVATE_NAME)) txt = ' from room %s' % room_name self.addstr(txt, to_curses_attr(theme.COLOR_INFORMATION_BAR))
def refresh(self) -> None: self._win.erase() # draw QR code code = qrcode.QRCode() code.add_data(self.qr) out = io.StringIO() code.print_ascii(out, invert=self.inverted) self.addstr(" " + self.qr + "\n") self.addstr(out.getvalue(), to_curses_attr((15, 0))) self.addstr(" ") col = to_curses_attr(get_theme().COLOR_TAB_NORMAL) if self.invert: self.addstr(self.str_invert, col) else: self.addstr(self.str_invert) self.addstr(" ") if self.invert: self.addstr(self.str_close) else: self.addstr(self.str_close, col) self._refresh()
def addstr_colored(self, text: str, y: Optional[int] = None, x: Optional[int] = None) -> None: """ Write a string on the window, setting the attributes as they are in the string. For example: \x19bhello → hello in bold \x191}Bonj\x192}our → 'Bonj' in red and 'our' in green next_attr_char is the \x19 delimiter attr_char is the char following it, it can be one of 'u', 'b', 'i', 'c[0-9]' """ if y is not None and x is not None: self.move(y, x) next_attr_char = text.find(FORMAT_CHAR) attr_italic = curses.A_ITALIC if hasattr( curses, 'A_ITALIC') else curses.A_REVERSE while next_attr_char != -1 and text: if next_attr_char + 1 < len(text): attr_char = text[next_attr_char + 1].lower() else: attr_char = str() if next_attr_char != 0: self.addstr(text[:next_attr_char]) if attr_char == 'o': self._win.attrset(0) elif attr_char == 'u': self._win.attron(curses.A_UNDERLINE) elif attr_char == 'b': self._win.attron(curses.A_BOLD) elif attr_char == 'i': self._win.attron(attr_italic) if (attr_char in string.digits or attr_char == '-') and attr_char != '': color_str = text[next_attr_char + 1:text.find('}', next_attr_char)] if ',' in color_str: tup, char = read_tuple(color_str) self._win.attron(to_curses_attr(tup)) if char: if char == 'o': self._win.attrset(0) elif char == 'u': self._win.attron(curses.A_UNDERLINE) elif char == 'b': self._win.attron(curses.A_BOLD) elif char == 'i': self._win.attron(attr_italic) else: # this will reset previous bold/uderline sequences if any was used self._win.attroff(curses.A_UNDERLINE) self._win.attroff(curses.A_BOLD) elif color_str: self._win.attron(to_curses_attr((int(color_str), -1))) text = text[next_attr_char + len(color_str) + 2:] else: text = text[next_attr_char + 2:] next_attr_char = text.find(FORMAT_CHAR) self.addstr(text)
def write_contact_informations(self, contact): """ Write the informations about the contact """ if not contact: self.addstr("(contact not in roster)", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) return display_name = contact.name if display_name: self.addstr('%s '%(display_name), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def draw_resource_line(self, y, resource, colored): """ Draw a specific resource line """ color = get_theme().color_show(resource.presence) self.addstr(y, 4, get_theme().CHAR_STATUS, to_curses_attr(color)) if colored: self.addstr(y, 8, self.truncate_name(str(resource.jid), 6), to_curses_attr(get_theme().COLOR_SELECTED_ROW)) else: self.addstr(y, 8, self.truncate_name(str(resource.jid), 6)) self.finish_line()
def refresh(self, name=None, window=None): log.debug('Refresh: %s', self.__class__.__name__) self._win.erase() if name: self.addstr(name, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) else: self.addstr(self.message, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) if window: self.print_scroll_position(window) self.finish_line(get_theme().COLOR_INFORMATION_BAR) self._refresh()
def write_contact_jid(self, jid): """ Just displays the resource in an other color """ log.debug("write_contact_jid DynamicConversationInfoWin, jid: %s", jid.resource) self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self.addstr(jid.bare, to_curses_attr(get_theme().COLOR_CONVERSATION_NAME)) if jid.resource: self.addstr("/%s" % (jid.resource,), to_curses_attr(get_theme().COLOR_CONVERSATION_RESOURCE)) self.addstr('] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def refresh(self) -> None: self._win.erase() self._win.attron(to_curses_attr(self.color)) format_string = '←{:^%s}→' % 7 inp = format_string.format(repr(self.value)) self.addstr(0, 0, inp) if self.last_key == 'KEY_RIGHT': self.move(0, 8) else: self.move(0, 0) self._win.attroff(to_curses_attr(self.color)) self._refresh()
def write_resource_information(self, resource): """ Write the information about the resource """ if not resource: presence = "unavailable" else: presence = resource.presence color = get_theme().color_show(presence) self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self.addstr(get_theme().CHAR_STATUS, to_curses_attr(color)) self.addstr(']', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_contact_information(self, contact): """ Write the information about the contact """ if not contact: self.addstr("(contact not in roster)", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) return display_name = contact.name if display_name: self.addstr('%s ' % (display_name), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_resource_information(self, resource): """ Write the informations about the resource """ if not resource: presence = "unavailable" else: presence = resource.presence color = get_theme().color_show(presence) self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self.addstr(get_theme().CHAR_STATUS, to_curses_attr(color)) self.addstr(']', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def refresh(self): self._win.erase() self._win.attron(to_curses_attr(self.color)) self.addnstr(0, 0, ' ' * (8), self.width) self.addstr(0, 2, "%s" % self.value) self.addstr(0, 8, '→') self.addstr(0, 0, '←') if self.last_key == 'KEY_RIGHT': self.addstr(0, 8, '') else: self.addstr(0, 0, '') self._win.attroff(to_curses_attr(self.color)) self._refresh()
def rewrite_text(self): self._win.erase() if self.color: self._win.attron(to_curses_attr(self.color)) self.addstr('*'*len(self.text[self.view_pos:self.view_pos+self.width-1])) if self.color: (y, x) = self._win.getyx() size = self.width-x self.addnstr(' '*size, size, to_curses_attr(self.color)) self.addstr(0, self.pos, '') if self.color: self._win.attroff(to_curses_attr(self.color)) self._refresh()
def refresh(self): self._win.erase() self._win.attron(to_curses_attr(self.color)) self.addnstr(0, 0, ' ' * self.width, self.width) if self.val_pos > 0: self.addstr(0, 0, '←') if self.val_pos < len(self.options) - 1: self.addstr(0, self.width - 1, '→') if self.options: option = self.options[self.val_pos]['label'] self.addstr(0, self.width // 2 - len(option) // 2, option) self._win.attroff(to_curses_attr(self.color)) self._refresh()
def refresh(self) -> None: self._win.erase() self._win.attron(to_curses_attr(self.color)) self.addnstr(0, 0, ' ' * self.width, self.width) if self.val_pos > 0: self.addstr(0, 0, '←') if self.val_pos < len(self.options) - 1: self.addstr(0, self.width - 1, '→') if self.options: option = self.options[self.val_pos] self.addstr(0, self.width // 2 - len(option) // 2, option) self._win.attroff(to_curses_attr(self.color)) self._refresh()
def draw_resource_line(self, y: int, resource: Resource, colored: bool) -> None: """ Draw a specific resource line """ theme = get_theme() color = theme.color_show(resource.presence) self.addstr(y, 4, theme.CHAR_STATUS, to_curses_attr(color)) if colored: self.addstr(y, 8, self.truncate_name(str(resource.jid), 6), to_curses_attr(theme.COLOR_SELECTED_ROW)) else: self.addstr(y, 8, self.truncate_name(str(resource.jid), 6)) self.finish_line()
def refresh(self, topic: Optional[str] = None) -> None: log.debug('Refresh: %s', self.__class__.__name__) self._win.erase() if topic is not None: msg = topic[:self.width - 1] else: msg = self._message[:self.width - 1] self.addstr(0, 0, msg, to_curses_attr(get_theme().COLOR_TOPIC_BAR)) _, x = self._win.getyx() remaining_size = self.width - x if remaining_size: self.addnstr(' ' * remaining_size, remaining_size, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self._refresh()
def write_contact_jid(self, jid): """ Just displays the resource in an other color """ log.debug("write_contact_jid DynamicConversationInfoWin, jid: %s", jid.resource) self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self.addstr(jid.bare, to_curses_attr(get_theme().COLOR_CONVERSATION_NAME)) if jid.resource: self.addstr( "/%s" % (jid.resource, ), to_curses_attr(get_theme().COLOR_CONVERSATION_RESOURCE)) self.addstr('] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def refresh(self): self._win.erase() self._win.attron(to_curses_attr(self.color)) self.addnstr(0, 0, ' '*self.width, self.width) if self.val_pos > 0: self.addstr(0, 0, '←') if self.val_pos < len(self.options)-1: self.addstr(0, self.width-1, '→') if self.options: option = self.options[self.val_pos] self.addstr(0, self.width//2-len(option)//2, option[0]['label']) self.addstr(0, 2, '✔' if option[1] else '☐') self._win.attroff(to_curses_attr(self.color)) self._refresh()
def refresh(self): if not self.edition_input: self._win.erase() self._win.attron(to_curses_attr(self.color)) self.addnstr(0, 0, ' ' * self.width, self.width) option = self.options[self.val_pos] self.addstr(0, self.width // 2 - len(option) // 2, option) if self.val_pos > 0: self.addstr(0, 0, '←') if self.val_pos < len(self.options) - 1: self.addstr(0, self.width - 1, '→') self._win.attroff(to_curses_attr(self.color)) self._refresh() else: self.edition_input.refresh()
def draw_group(self, y, group, colored): """ Draw a groupname on a line """ if colored: self._win.attron(to_curses_attr(get_theme().COLOR_SELECTED_ROW)) if group.folded: self.addstr(y, 0, '[+] ') else: self.addstr(y, 0, '[-] ') contacts = " (%s/%s)" % (group.get_nb_connected_contacts(), len(group)) self.addstr(y, 4, self.truncate_name(group.name, len(contacts)+4) + contacts) if colored: self._win.attroff(to_curses_attr(get_theme().COLOR_SELECTED_ROW)) self.finish_line()
def draw_plus(self, y: int) -> None: """ Draw the indicator that shows that the list is longer than what is displayed """ self.addstr(y, self.width - 5, '++++', to_curses_attr(get_theme().COLOR_MORE_INDICATOR))
def draw_group_info(self, group: RosterGroup) -> None: """ draw the group information """ self.addstr(0, 0, group.name, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self.finish_line(get_theme().COLOR_INFORMATION_BAR)
def write_additional_informations(self, informations, jid): """ Write all informations added by plugins by getting the value returned by the callbacks. """ for key in informations: self.addstr(informations[key](jid), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def _addstr_colored_lite(self, text, y=None, x=None): """ Just like addstr_colored, with the single-char attributes (\x0E to \x19 instead of \x19 + attr). We do not use any } char in this version """ chars = format_chars + '\n' if y is not None and x is not None: self.move(y, x) format_char = find_first_format_char(text, chars) attr_italic = curses.A_ITALIC if hasattr( curses, 'A_ITALIC') else curses.A_REVERSE while format_char != -1: if text[format_char] == '\n': attr_char = '|' else: attr_char = self.text_attributes[format_chars.index( text[format_char])] self.addstr(text[:format_char]) self.addstr(attr_char, curses.A_REVERSE) text = text[format_char + 1:] if attr_char == 'o': self._win.attrset(0) elif attr_char == 'u': self._win.attron(curses.A_UNDERLINE) elif attr_char == 'b': self._win.attron(curses.A_BOLD) elif attr_char == 'i': self._win.attron(attr_italic) elif attr_char in string.digits and attr_char != '': self._win.attron(to_curses_attr((int(attr_char), -1))) format_char = find_first_format_char(text, chars) self.addstr(text)
def write_disconnected(self, room): """ Shows a message if the room is not joined """ if not room.joined: self.addstr(' -!- Not connected ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_pre_msg(self, msg, with_timestamps, nick_size) -> int: offset = 0 if with_timestamps: offset += self.write_time(msg.str_time) if not msg.nickname: # not a message, nothing to do afterwards return offset nick = truncate_nick(msg.nickname, nick_size) offset += poopt.wcswidth(nick) if msg.nick_color: color = msg.nick_color elif msg.user: color = msg.user.color else: color = None if msg.ack: if msg.ack > 0: offset += self.write_ack() else: offset += self.write_nack() if msg.me: self._win.attron(to_curses_attr(get_theme().COLOR_ME_MESSAGE)) self.addstr('* ') self.write_nickname(nick, color, msg.highlight) offset += self.write_revisions(msg) self.addstr(' ') offset += 3 else: self.write_nickname(nick, color, msg.highlight) offset += self.write_revisions(msg) self.addstr('> ') offset += 2 return offset
def _addstr_colored_lite(self, text: str, y: Optional[int] = None, x: Optional[int] = None) -> None: """ Just like addstr_colored, with the single-char attributes (\x0E to \x19 instead of \x19 + attr). We do not use any } char in this version """ chars = format_chars + '\n' if y is not None and x is not None: self.move(y, x) format_char = find_first_format_char(text, chars) attr_italic = curses.A_ITALIC if hasattr( curses, 'A_ITALIC') else curses.A_REVERSE while format_char != -1: if text[format_char] == '\n': attr_char = '|' else: attr_char = self.text_attributes[format_chars.index( text[format_char])] self.addstr(text[:format_char]) self.addstr(attr_char, curses.A_REVERSE) text = text[format_char + 1:] if attr_char == 'o': self._win.attrset(0) elif attr_char == 'u': self._win.attron(curses.A_UNDERLINE) elif attr_char == 'b': self._win.attron(curses.A_BOLD) elif attr_char == 'i': self._win.attron(attr_italic) elif attr_char in string.digits and attr_char != '': self._win.attron(to_curses_attr((int(attr_char), -1))) format_char = find_first_format_char(text, chars) self.addstr(text)
def write_pre_msg(self, msg, with_timestamps, nick_size): offset = 0 if with_timestamps: offset += self.write_time(msg.str_time) if not msg.nickname: # not a message, nothing to do afterwards return offset nick = truncate_nick(msg.nickname, nick_size) offset += poopt.wcswidth(nick) if msg.nick_color: color = msg.nick_color elif msg.user: color = msg.user.color else: color = None if msg.ack: if msg.ack > 0: offset += self.write_ack() else: offset += self.write_nack() if msg.me: self._win.attron(to_curses_attr(get_theme().COLOR_ME_MESSAGE)) self.addstr('* ') self.write_nickname(nick, color, msg.highlight) offset += self.write_revisions(msg) self.addstr(' ') offset += 3 else: self.write_nickname(nick, color, msg.highlight) offset += self.write_revisions(msg) self.addstr('> ') offset += 2 return offset
def write_revisions(self, msg): if msg.revisions: self._win.attron(to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE)) self.addstr('%d' % msg.revisions) self._win.attrset(0) return ceil(log10(msg.revisions + 1)) return 0
def write_additional_information(self, information, jid): """ Write all information added by plugins by getting the value returned by the callbacks. """ for key in information: self.addstr(information[key](jid), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def refresh(self) -> None: height, width = self._win.getmaxyx() self._win.erase() sorted_tabs = [tab for tab in self.core.tabs if tab] theme = get_theme() if not config.get('show_inactive_tabs'): sorted_tabs = [ tab for tab in sorted_tabs if tab.vertical_color != theme.COLOR_VERTICAL_TAB_NORMAL ] nb_tabs = len(sorted_tabs) use_nicks = config.get('use_tab_nicks') if nb_tabs >= height: # TODO: As sorted_tabs filters out gap tabs this ensures pos is # always set, preventing UnboundLocalError. Now is this how this # should be fixed. pos = 0 for y, tab in enumerate(sorted_tabs): if tab.vertical_color == theme.COLOR_VERTICAL_TAB_CURRENT: pos = y break # center the current tab as much as possible if pos < height // 2: sorted_tabs = sorted_tabs[:height] elif nb_tabs - pos <= height // 2: sorted_tabs = sorted_tabs[-height:] else: sorted_tabs = sorted_tabs[pos - height // 2:pos + height // 2] asc_sort = (config.get('vertical_tab_list_sort') == 'asc') for y, tab in enumerate(sorted_tabs): color = tab.vertical_color if asc_sort: y = height - y - 1 self.addstr(y, 0, "%2d" % tab.nb, to_curses_attr(theme.COLOR_VERTICAL_TAB_NUMBER)) self.addstr('.') if use_nicks: self.addnstr("%s" % tab.get_nick(), width - 4, to_curses_attr(color)) else: self.addnstr("%s" % tab.name, width - 4, to_curses_attr(color)) separator = to_curses_attr(theme.COLOR_VERTICAL_SEPARATOR) self._win.attron(separator) self._win.vline(0, width - 1, curses.ACS_VLINE, height) self._win.attroff(separator) self._refresh()
def write_own_nick(self, room): """ Write our own nick in the info bar """ nick = room.own_nick if not nick: return self.addstr(truncate_nick(nick, 13), to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
def write_additional_information(self, information, jid): """ Write all information added by plugins by getting the value returned by the callbacks. """ color = to_curses_attr(get_theme().COLOR_INFORMATION_BAR) for plugin in information.values(): self.addstr(plugin(jid), color)
def write_revisions(self, msg) -> int: if msg.revisions: self._win.attron( to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE)) self.addstr('%d' % msg.revisions) self._win.attrset(0) return ceil(log10(msg.revisions + 1)) return 0
def refresh(self, txt=None): log.debug('Refresh: %s', self.__class__.__name__) if txt: self.txt = txt self._win.erase() self.addstr(0, 0, self.txt[:self.width-1], to_curses_attr(get_theme().COLOR_WARNING_PROMPT)) self.finish_line(get_theme().COLOR_WARNING_PROMPT) self._refresh()
def draw_roster_information(self, roster: Roster) -> None: """ The header at the top """ self.addstr( 'Roster: %s/%s contacts' % (roster.get_nb_connected_contacts(), len(roster)), to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self.finish_line(get_theme().COLOR_INFORMATION_BAR)
def _display_border(self) -> None: self._image = None attribute = to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR) self._win.attron(attribute) self._win.border(curses.ACS_VLINE, curses.ACS_VLINE, curses.ACS_HLINE, curses.ACS_HLINE, curses.ACS_ULCORNER, curses.ACS_URCORNER, curses.ACS_LLCORNER, curses.ACS_LRCORNER) self._win.attroff(attribute)