def get_ui(position=None): """ Returns user interface (lightbar, pager). Optional argument position is tuple position of prior lightbar instance. """ from x84.bbs import getterminal, Lightbar, Pager term = getterminal() assert term.height > 10 and term.width >= 40 # +-+ +----+ # |lb |pager # +-+ +----+ height = term.height - 7 lb_width = int(term.width * .3) pg_width = term.width - (lb_width) lb_xloc = (term.width / 2) - (term.width / 2) pg_xloc = lb_xloc + lb_width lightbar = Lightbar(height, lb_width, (term.height - height - 1), lb_xloc) pager = Pager(height, pg_width, (term.height - height - 1), pg_xloc) pager.ypadding = 2 pager.xpadding = 2 lightbar.update(get_bbslist(max_len=lightbar.visible_width)) ## pressing Return is same as 't'elnet lightbar.keyset['enter'].extend((u't', u'T')) ## re-select previous selection if position is not None: lightbar.position = position return (pager, lightbar)
def get_pager(): """ Returns pager window for oneliners content. """ from x84.bbs import getterminal, Pager term = getterminal() xloc = max(0, (term.width / 2) - 50) width = min(term.width - 0, 100) pager = Pager(yloc=7, xloc=xloc, height=term.height - 10, width=width) pager.colors['border'] = term.blue return pager
def get_pager(pager=None): """ Return Pager for use as chat window. """ from x84.bbs import getterminal, Pager term = getterminal() height = (term.height - 4) width = int(term.width * .9) yloc = term.height - height - 1 xloc = int(term.width / 2) - (width / 2) new_pager = Pager(height, width, yloc, xloc) if pager is not None: content = pager.content # little hack to keep empty lines from re-importing for row in range(len(content)): ucs = content[row] if ucs.startswith(u'\x1b(B'): ucs = ucs[len(u'\x1b(B'):] if ucs.endswith(u'\x1b[m'): ucs = ucs[len(u'\x1b[m'):] content[row] = ucs new_pager.update('\r\n'.join(content)) new_pager.enable_scrolling = True new_pager.colors['border'] = term.cyan new_pager.glyphs['right-vert'] = u'|' new_pager.glyphs['left-vert'] = u'|' new_pager.glyphs['bot-horiz'] = u'' return new_pager
def get_pager(pager=None): """ Return Pager for use as chat window. """ from x84.bbs import getterminal, Pager term = getterminal() height = (term.height - 4) width = int(term.width * .9) yloc = term.height - height - 1 xloc = int(term.width / 2) - (width / 2) new_pager = Pager(height, width, yloc, xloc) if pager is not None: content = pager.content # little hack to keep empty lines from re-importing for row in range(len(content)): ucs = content[row] if ucs.startswith(u'\x1b(B'): ucs = ucs[len(u'\x1b(B'):] if ucs.endswith(u'\x1b[m'): ucs = ucs[len(u'\x1b[m'):] content[row] = ucs new_pager.update('\r\n'.join(content)) new_pager.enable_scrolling = True new_pager.colors['border'] = term.blue new_pager.glyphs['right-vert'] = u'|' new_pager.glyphs['left-vert'] = u'|' new_pager.glyphs['bot-horiz'] = u'' return new_pager
def get_reader(): """ Provide Pager UI element for message reading. """ reader_height = (term.height - (term.height / 3) - 2) reader_indent = 2 reader_width = min(term.width - 1, min(term.width - reader_indent, 80)) reader_ypos = ((term.height - 1 ) - reader_height if (term.width - reader_width) < len_preview else 2) reader_height = term.height - reader_ypos - 1 msg_reader = Pager( height=reader_height, width=reader_width, yloc=reader_ypos, xloc=min(len_preview + 2, term.width - reader_width)) msg_reader.glyphs['top-horiz'] = u'' msg_reader.glyphs['right-vert'] = u'' return msg_reader
def prompt_resize_term(session, term, name): want_cols = get_ini('sesame', '{0}_cols'.format(name), getter='getint') want_rows = get_ini('sesame', '{0}_rows'.format(name), getter='getint') if want_cols and want_rows and not term.kind.startswith('ansi'): while not (term.width == want_cols and term.height == want_rows): resize_msg = (u'Please resize your window to {0} x {1} ' u'current size is {term.width} x {term.height} ' u'or press return. Press escape to cancel.'.format( want_cols, want_rows, term=term)) echo(term.normal + term.home + term.clear_eos + term.home) pager = Pager(yloc=0, xloc=0, width=want_cols, height=want_rows, colors={'border': term.bold_red}) echo(term.move(term.height - 1, term.width)) echo(pager.refresh() + pager.border()) width = min(70, term.width - 5) for yoff, line in enumerate(term.wrap(resize_msg, width)): echo(u''.join((term.move(yoff + 1, 5), line.rstrip()))) event, data = session.read_events(('input', 'refresh')) if event == 'refresh': continue if event == 'input': session.buffer_input(data, pushback=True) inp = term.inkey(0) while inp: if inp.code == term.KEY_ENTER: echo(term.normal + term.home + term.clear_eos) return True if inp.code == term.KEY_ESCAPE: return False inp = term.inkey(0) return True # if terminal type is 'ansi', just pass-through return True
def prompt_resize_term(session, term, name): want_cols = get_ini('sesame', '{0}_cols'.format(name), getter='getint') want_rows = get_ini('sesame', '{0}_rows'.format(name), getter='getint') if want_cols and want_rows and not term.kind.startswith('ansi'): while not (term.width == want_cols and term.height == want_rows): resize_msg = (u'Please resize your window to {0} x {1} ' u'current size is {term.width} x {term.height} ' u'or press return. Press escape to cancel.' .format(want_cols, want_rows, term=term)) echo(term.normal + term.home + term.clear_eos + term.home) pager = Pager(yloc=0, xloc=0, width=want_cols, height=want_rows, colors={'border': term.bold_red}) echo(term.move(term.height - 1, term.width)) echo(pager.refresh() + pager.border()) width = min(70, term.width - 5) for yoff, line in enumerate(term.wrap(resize_msg, width)): echo(u''.join((term.move(yoff + 1, 5), line.rstrip()))) event, data = session.read_events(('input', 'refresh')) if event == 'refresh': continue if event == 'input': session.buffer_input(data, pushback=True) inp = term.inkey(0) while inp: if inp.code == term.KEY_ENTER: echo(term.normal + term.home + term.clear_eos) return True if inp.code == term.KEY_ESCAPE: return False inp = term.inkey(0) return True # if terminal type is 'ansi', just pass-through return True
def get_pager(news_txt, position=None): """ Return Pager instance with content ``news_txt``. """ from x84.bbs import getterminal, Pager term = getterminal() width = min(130, (term.width - 2)) height = term.height - len(redraw(None).splitlines()) yloc = term.height - height xloc = (term.width / 2) - (width / 2) pager = Pager(height, width, yloc, xloc) pager.colors['border'] = term.blue pager.glyphs['left-vert'] = u'' pager.glyphs['right-vert'] = u'' pager.update('\n'.join(news_txt)) if position is not None: pager.position = position return pager
def show_scores(): from x84.bbs import DBProxy, Pager, getterminal from x84.bbs import echo, getsession, ini session, term = getsession(), getterminal() allscores = DBProxy('tetris').items() if 0 == len(allscores): return # line up over tetris game, but logo & 'made by jojo' in view # -- since we have so much screen width, columize the scores, # the math brings it out to 2 columns, but fmt is adjustable pager_title = term.blue_reverse_underline('- hiGh SCOREs -') len_handle = ini.CFG.getint('nua', 'max_user') score_fmt = u'%s %s %s %s' len_pos = 2 len_score = 10 len_level = 3 height, width = 11, 73 yloc, xloc = 10, 3 pager = Pager(height=height, width=width, yloc=yloc, xloc=xloc) pager.xpadding = 1 pager.glyphs['left-vert'] = u'' pager.glyphs['right-vert'] = u'' pager.colors['border'] = term.blue_reverse pager.alignment = 'center' # pre-fesh pager border before fetch echo(pager.border() + pager.title(pager_title) + pager.clear()) highscores = sorted( [(_score, _level, _handle.decode('utf8')) for (_handle, (_score, _level, _)) in allscores], reverse=True) pager.append(score_fmt % ( term.bold_blue_underline('No'.ljust(len_pos)), term.bold_blue_underline('SCORE'.ljust(len_score)), term.bold_blue_underline('lVl'.ljust(len_level)), term.bold_blue_underline('hANdlE'.rjust(len_handle),))) pos = 0 for pos, (_score, _level, _handle) in enumerate(highscores): if _handle == session.user.handle: pager.append(score_fmt % ( term.blue_reverse(str(pos + 1).ljust(len_pos)), term.blue_reverse(str(_score).ljust(len_score)), term.blue_reverse(str(_level).ljust(len_level)), term.blue_reverse(_handle.rjust(len_handle)),)) else: pager.append(score_fmt % ( term.bold_blue(str(pos + 1).ljust(len_pos)), term.blue(str(_score).ljust(len_score)), term.blue(str(_level).ljust(len_level)), _handle.rjust(len_handle),)) # append additional empty slot rows while len(pager.content) < pager.visible_height: pos += 1 pager.append(score_fmt % ( term.bold_blue(str(pos + 1).ljust(len_pos)), term.bold_black(u'.'.ljust(len_score)), term.bold_black(u'.'.ljust(len_level)), term.bold_black(u'.'.rjust(len_handle)),)) dirty = 2 # 2=do not refresh border & title pager.move_home() while not pager.quit: # 1=full refresh dirty = 1 if session.poll_event('refresh') else dirty if dirty: echo(u''.join(( term.normal, ((pager.border() + pager.title(pager_title)) if dirty != 2 else u''), pager.refresh(), pager.footer(u''.join(( term.underline_blue('q'), term.bold_blue('uit')))), ))) dirty = 0 echo(pager.process_keystroke(term.inkey(1)))
def show_scores(): from x84.bbs import DBProxy, Pager, getterminal from x84.bbs import getch, echo, getsession, ini session, term = getsession(), getterminal() allscores = DBProxy('tetris').items() if 0 == len(allscores): return # line up over tetris game, but logo & 'made by jojo' in view # -- since we have so much screen width, columize the scores, # the math brings it out to 2 columns, but fmt is adjustable pager_title = term.blue_reverse_underline('- hiGh SCOREs -') len_handle = ini.CFG.getint('nua', 'max_user') score_fmt = u'%s %s %s %s' len_pos = 2 len_score = 10 len_level = 3 height, width = 11, 73 yloc, xloc = 10, 3 pager = Pager(height=height, width=width, yloc=yloc, xloc=xloc) pager.xpadding = 1 pager.glyphs['left-vert'] = u'' pager.glyphs['right-vert'] = u'' pager.colors['border'] = term.blue_reverse pager.alignment = 'center' # pre-fesh pager border before fetch echo(pager.border() + pager.title(pager_title) + pager.clear()) highscores = sorted( [(_score, _level, _handle.decode('utf8')) for (_handle, (_score, _level, _)) in allscores], reverse=True) pager.append(score_fmt % ( term.bold_blue_underline('No'.ljust(len_pos)), term.bold_blue_underline('SCORE'.ljust(len_score)), term.bold_blue_underline('lVl'.ljust(len_level)), term.bold_blue_underline('hANdlE'.rjust(len_handle),))) pos = 0 for pos, (_score, _level, _handle) in enumerate(highscores): if _handle == session.user.handle: pager.append(score_fmt % ( term.blue_reverse(str(pos + 1).ljust(len_pos)), term.blue_reverse(str(_score).ljust(len_score)), term.blue_reverse(str(_level).ljust(len_level)), term.blue_reverse(_handle.rjust(len_handle)),)) else: pager.append(score_fmt % ( term.bold_blue(str(pos + 1).ljust(len_pos)), term.blue(str(_score).ljust(len_score)), term.blue(str(_level).ljust(len_level)), _handle.rjust(len_handle),)) # append additional empty slot rows while len(pager.content) < pager.visible_height: pos += 1 pager.append(score_fmt % ( term.bold_blue(str(pos + 1).ljust(len_pos)), term.bold_black(u'.'.ljust(len_score)), term.bold_black(u'.'.ljust(len_level)), term.bold_black(u'.'.rjust(len_handle)),)) dirty = 2 # 2=do not refresh border & title pager.move_home() while not pager.quit: # 1=full refresh dirty = 1 if session.poll_event('refresh') else dirty if dirty: echo(u''.join(( term.normal, ((pager.border() + pager.title(pager_title)) if dirty != 2 else u''), pager.refresh(), pager.footer(u''.join(( term.underline_blue('q'), term.bold_blue('uit')))), ))) dirty = 0 echo(pager.process_keystroke(getch(1)))
def main(save_key=u'draft'): """ Main procedure. """ # pylint: disable=R0914,R0912,R0915 # Too many local variables # Too many branches # Too many statements from x84.bbs import getsession, getterminal, echo, getch, Ansi, Pager session, term = getsession(), getterminal() movement = (term.KEY_UP, term.KEY_DOWN, term.KEY_NPAGE, term.KEY_PPAGE, term.KEY_HOME, term.KEY_END, u'\r', term.KEY_ENTER) keyset = {'edit': (term.KEY_ENTER,), 'command': (unichr(27), term.KEY_ESCAPE), 'kill': (u'K',), 'undo': (u'u', 'U',), 'goto': (u'G',), 'insert': (u'I',), 'insert-before': (u'O',), 'insert-after': (u'o',), 'join': (u'J',), 'rubout': (unichr(8), unichr(127), unichr(23), term.KEY_BACKSPACE,), } def merge(): """ Merges line editor content as a replacement for the currently selected lightbar row. Returns True if text inserted caused additional rows to be appended, which is meaningful in a window refresh context. """ lightbar.content[lightbar.index] = [ lightbar.selection[0], softwrap_join(wrap_rstrip(lneditor.content)) + HARDWRAP] prior_length = len(lightbar.content) prior_position = lightbar.position set_lbcontent(lightbar, get_lbcontent(lightbar)) if len(lightbar.content) - prior_length == 0: echo(lightbar.refresh_row(prior_position[0])) return False while len(lightbar.content) - prior_length > 0: # hidden move-down for each appended line lightbar.move_down() prior_length += 1 return True def statusline(lightbar): """ Display status line and command help on ``lightbar`` borders """ lightbar.colors['border'] = term.red if edit else term.yellow keyset_cmd = u'' if not edit: keyset_cmd = u''.join(( term.yellow(u'-( '), term.yellow_underline(u'S'), u':', term.bold(u'ave'), u' ', term.yellow_underline(u'A'), u':', term.bold(u'bort'), u' ', term.yellow_underline(u'?'), u':', term.bold(u'help'), term.yellow(u' )-'),)) keyset_cmd = lightbar.pos(lightbar.height - 1, max(0, lightbar.width - (len(Ansi(keyset_cmd)) + 3)) ) + keyset_cmd return u''.join(( lightbar.border(), keyset_cmd, lightbar.pos(lightbar.height, lightbar.xpadding), u''.join(( term.red(u'-[ '), u'EditiNG liNE ', term.reverse_red('%d' % (lightbar.index + 1,)), term.red(u' ]-'),)) if edit else u''.join(( term.yellow(u'-( '), u'liNE ', term.yellow('%d/%d ' % ( lightbar.index + 1, len(lightbar.content),)), '%d%% ' % ( int((float(lightbar.index + 1) / max(1, len(lightbar.content))) * 100)), term.yellow(u' )-'),)), lightbar.title(u''.join(( term.red('-] '), term.bold(u'Escape'), u':', term.bold_red(u'command mode'), term.red(' [-'),) ) if edit else u''.join(( term.yellow('-( '), term.bold(u'Enter'), u':', term.bold_yellow(u'edit mode'), term.yellow(' )-'),))),)) def redraw_lneditor(lightbar, lneditor): """ Return ucs suitable for refreshing line editor. """ return ''.join(( term.normal, statusline(lightbar), lneditor.border(), lneditor.refresh())) def get_ui(ucs, lightbar=None): """ Returns Lightbar and ScrollingEditor instance. """ lbr = get_lightbar(ucs) lbr.position = (lightbar.position if lightbar is not None else (0, 0)) lne = get_lneditor(lbr) return lbr, lne def banner(): """ Returns string suitable clearing screen """ return u''.join(( term.move(0, 0), term.normal, term.clear)) def redraw(lightbar, lneditor): """ Returns ucs suitable for redrawing Lightbar and ScrollingEditor UI elements. """ return u''.join(( term.normal, redraw_lightbar(lightbar), redraw_lneditor(lightbar, lneditor) if edit else u'', )) def redraw_lightbar(lightbar): """ Returns ucs suitable for redrawing Lightbar. """ return u''.join(( statusline(lightbar), lightbar.refresh(),)) def resize(lightbar): """ Resize Lightbar. """ if edit: # always re-merge current line on resize, merge() lbr, lne = get_ui(get_lbcontent(lightbar), lightbar) echo(redraw(lbr, lne)) return lbr, lne ucs = session.user.get(save_key, u'') lightbar, lneditor = get_ui(ucs, None) echo(banner()) dirty = True edit = False digbuf, num_repeat = u'', -1 count_repeat = lambda: range(max(num_repeat, 1)) while True: # poll for refresh if session.poll_event('refresh'): echo(banner()) lightbar, lneditor = resize(lightbar) dirty = True if dirty: session.activity = 'editing %s' % (save_key,) echo(redraw(lightbar, lneditor)) dirty = False # poll for input inp = getch(1) # buffer keystrokes for repeat if (not edit and inp is not None and type(inp) is not int and inp.isdigit()): digbuf += inp if len(digbuf) > 10: # overflow, echo(u'\a') digbuf = inp try: num_repeat = int(digbuf) except ValueError: try: num_repeat = int(inp) except ValueError: pass continue else: digbuf = u'' # toggle edit mode, if inp in keyset['command'] or not edit and inp in keyset['edit']: edit = not edit # toggle if not edit: # switched to command mode, merge our lines echo(term.normal + lneditor.erase_border()) merge() lightbar.colors['highlight'] = term.yellow_reverse else: # switched to edit mode, save draft, # instantiate new line editor save_draft(save_key, get_lbcontent(lightbar)) lneditor = get_lneditor(lightbar) lightbar.colors['highlight'] = term.red_reverse dirty = True # command mode, kill line elif not edit and inp in keyset['kill']: # when 'killing' a line, make accomidations to clear # bottom-most row, otherwise a ghosting effect occurs for _ in count_repeat(): del lightbar.content[lightbar.index] set_lbcontent(lightbar, get_lbcontent(lightbar)) if lightbar.visible_bottom > len(lightbar.content): echo(lightbar.refresh_row(lightbar.visible_bottom + 1)) else: dirty = True save_draft(save_key, get_lbcontent(lightbar)) # command mode, insert line elif not edit and inp in keyset['insert']: for _ in count_repeat(): lightbar.content.insert(lightbar.index, (lightbar.index, HARDWRAP,)) set_lbcontent(lightbar, get_lbcontent(lightbar)) save_draft(save_key, get_lbcontent(lightbar)) dirty = True # command mode; goto line elif not edit and inp in keyset['goto']: if num_repeat == -1: # 'G' alone goes to end of file, num_repeat = len(lightbar.content) echo(lightbar.goto((num_repeat or 1) - 1)) echo(statusline(lightbar)) # command mode; insert-before (switch to edit mode) elif not edit and inp in keyset['insert-before']: lightbar.content.insert(lightbar.index, (lightbar.index, HARDWRAP,)) set_lbcontent(lightbar, get_lbcontent(lightbar)) edit = dirty = True # switched to edit mode, save draft, # instantiate new line editor lightbar.colors['highlight'] = term.red_reverse lneditor = get_lneditor(lightbar) save_draft(save_key, get_lbcontent(lightbar)) # command mode; insert-after (switch to edit mode) elif not edit and inp in keyset['insert-after']: lightbar.content.insert(lightbar.index + 1, (lightbar.index + 1, HARDWRAP,)) set_lbcontent(lightbar, get_lbcontent(lightbar)) edit = dirty = True # switched to edit mode, save draft, # instantiate new line editor lightbar.colors['highlight'] = term.red_reverse lightbar.move_down() lneditor = get_lneditor(lightbar) save_draft(save_key, get_lbcontent(lightbar)) # command mode, undo elif not edit and inp in keyset['undo']: for _ in count_repeat(): if len(UNDO): set_lbcontent(lightbar, UNDO.pop()) dirty = True else: echo(u'\a') break # command mode, join line elif not edit and inp in keyset['join']: for _ in count_repeat(): if lightbar.index + 1 < len(lightbar.content): idx = lightbar.index lightbar.content[idx] = (idx, WHITESPACE.join(( lightbar.content[idx][1].rstrip(), lightbar.content[idx + 1][1].lstrip(),))) del lightbar.content[idx + 1] prior_length = len(lightbar.content) set_lbcontent(lightbar, get_lbcontent(lightbar)) if len(lightbar.content) - prior_length > 0: lightbar.move_down() dirty = True else: echo(u'\a') break if dirty: save_draft(save_key, get_lbcontent(lightbar)) # command mode, basic cmds & movement elif not edit and inp is not None: if inp in (u'a', u'A',): if yes_no(lightbar, term.yellow(u'- ') + term.bold_red(u'AbORt') + term.yellow(u' -')): return False dirty = True elif inp in (u's', u'S',): if yes_no(lightbar, term.yellow(u'- ') + term.bold_green(u'SAVE') + term.yellow(u' -'), term.reverse_green): save(save_key, get_lbcontent(lightbar)) return True dirty = True elif inp in (u'?',): pager = Pager(lightbar.height, lightbar.width, lightbar.yloc, lightbar.xloc) pager.update(get_help()) pager.colors['border'] = term.bold_blue echo(pager.border() + pager.title(u''.join(( term.bold_blue(u'-( '), term.white_on_blue(u'r'), u':', term.bold(u'eturn'), u' ', term.bold_blue(u' )-'),)))) pager.keyset['exit'].extend([u'r', u'R']) pager.read() echo(pager.erase_border()) dirty = True else: moved = False for _ in count_repeat(): echo(lightbar.process_keystroke(inp)) moved = lightbar.moved or moved if moved: echo(statusline(lightbar)) # edit mode; movement elif edit and inp in movement: dirty = merge() if inp in (u'\r', term.KEY_ENTER,): lightbar.content.insert(lightbar.index + 1, [lightbar.selection[0] + 1, u'']) inp = term.KEY_DOWN dirty = True ucs = lightbar.process_keystroke(inp) if lightbar.moved: echo(term.normal + lneditor.erase_border()) echo(ucs) lneditor = get_lneditor(lightbar) save_draft(save_key, get_lbcontent(lightbar)) echo(lneditor.refresh()) else: dirty = True # edit mode -- append character / backspace elif edit and inp is not None: if (inp in keyset['rubout'] and len(lneditor.content) == 0 and lightbar.index > 0): # erase past margin, echo(term.normal + lneditor.erase_border()) del lightbar.content[lightbar.index] lightbar.move_up() set_lbcontent(lightbar, get_lbcontent(lightbar)) lneditor = get_lneditor(lightbar) dirty = True else: # edit mode, add/delete ch echo(lneditor.process_keystroke(inp)) if lneditor.moved: echo(statusline(lightbar)) if inp is not None and type(inp) is not int and not inp.isdigit(): # commands were processed, reset num_repeat to 1 num_repeat = -1
def main(save_key=u'draft'): """ Main procedure. """ # pylint: disable=R0914,R0912,R0915 # Too many local variables # Too many branches # Too many statements from x84.bbs import getsession, getterminal, echo, getch, Ansi, Pager session, term = getsession(), getterminal() movement = (term.KEY_UP, term.KEY_DOWN, term.KEY_NPAGE, term.KEY_PPAGE, term.KEY_HOME, term.KEY_END, u'\r', term.KEY_ENTER) keyset = { 'edit': (term.KEY_ENTER, ), 'command': (unichr(27), term.KEY_ESCAPE), 'kill': (u'K', ), 'undo': ( u'u', 'U', ), 'goto': (u'G', ), 'insert': (u'I', ), 'insert-before': (u'O', ), 'insert-after': (u'o', ), 'join': (u'J', ), 'rubout': ( unichr(8), unichr(127), unichr(23), term.KEY_BACKSPACE, ), } def merge(): """ Merges line editor content as a replacement for the currently selected lightbar row. Returns True if text inserted caused additional rows to be appended, which is meaningful in a window refresh context. """ lightbar.content[lightbar.index] = [ lightbar.selection[0], softwrap_join(wrap_rstrip(lneditor.content)) + HARDWRAP ] prior_length = len(lightbar.content) prior_position = lightbar.position set_lbcontent(lightbar, get_lbcontent(lightbar)) if len(lightbar.content) - prior_length == 0: echo(lightbar.refresh_row(prior_position[0])) return False while len(lightbar.content) - prior_length > 0: # hidden move-down for each appended line lightbar.move_down() prior_length += 1 return True def statusline(lightbar): """ Display status line and command help on ``lightbar`` borders """ lightbar.colors['border'] = term.red if edit else term.yellow keyset_cmd = u'' if not edit: keyset_cmd = u''.join(( term.yellow(u'-( '), term.yellow_underline(u'S'), u':', term.bold(u'ave'), u' ', term.yellow_underline(u'A'), u':', term.bold(u'bort'), u' ', term.yellow_underline(u'?'), u':', term.bold(u'help'), term.yellow(u' )-'), )) keyset_cmd = lightbar.pos( lightbar.height - 1, max(0, lightbar.width - (len(Ansi(keyset_cmd)) + 3))) + keyset_cmd return u''.join(( lightbar.border(), keyset_cmd, lightbar.pos(lightbar.height, lightbar.xpadding), u''.join(( term.red(u'-[ '), u'EditiNG liNE ', term.reverse_red('%d' % (lightbar.index + 1, )), term.red(u' ]-'), )) if edit else u''.join(( term.yellow(u'-( '), u'liNE ', term.yellow('%d/%d ' % ( lightbar.index + 1, len(lightbar.content), )), '%d%% ' % (int((float(lightbar.index + 1) / max(1, len(lightbar.content))) * 100)), term.yellow(u' )-'), )), lightbar.title(u''.join(( term.red('-] '), term.bold(u'Escape'), u':', term.bold_red(u'command mode'), term.red(' [-'), )) if edit else u''.join(( term.yellow('-( '), term.bold(u'Enter'), u':', term.bold_yellow(u'edit mode'), term.yellow(' )-'), ))), )) def redraw_lneditor(lightbar, lneditor): """ Return ucs suitable for refreshing line editor. """ return ''.join((term.normal, statusline(lightbar), lneditor.border(), lneditor.refresh())) def get_ui(ucs, lightbar=None): """ Returns Lightbar and ScrollingEditor instance. """ lbr = get_lightbar(ucs) lbr.position = (lightbar.position if lightbar is not None else (0, 0)) lne = get_lneditor(lbr) return lbr, lne def banner(): """ Returns string suitable clearing screen """ return u''.join((term.move(0, 0), term.normal, term.clear)) def redraw(lightbar, lneditor): """ Returns ucs suitable for redrawing Lightbar and ScrollingEditor UI elements. """ return u''.join(( term.normal, redraw_lightbar(lightbar), redraw_lneditor(lightbar, lneditor) if edit else u'', )) def redraw_lightbar(lightbar): """ Returns ucs suitable for redrawing Lightbar. """ return u''.join(( statusline(lightbar), lightbar.refresh(), )) def resize(lightbar): """ Resize Lightbar. """ if edit: # always re-merge current line on resize, merge() lbr, lne = get_ui(get_lbcontent(lightbar), lightbar) echo(redraw(lbr, lne)) return lbr, lne ucs = session.user.get(save_key, u'') lightbar, lneditor = get_ui(ucs, None) echo(banner()) dirty = True edit = False digbuf, num_repeat = u'', -1 count_repeat = lambda: range(num_repeat if num_repeat != -1 else 1) while True: # poll for refresh if session.poll_event('refresh'): echo(banner()) lightbar, lneditor = resize(lightbar) dirty = True if dirty: session.activity = 'editing %s' % (save_key, ) echo(redraw(lightbar, lneditor)) dirty = False # poll for input inp = getch(1) # buffer keystrokes for repeat if (not edit and inp is not None and type(inp) is not int and inp.isdigit()): digbuf += inp if len(digbuf) > 10: # overflow, echo(u'\a') digbuf = inp try: num_repeat = int(digbuf) except ValueError: try: num_repeat = int(inp) except ValueError: pass continue else: digbuf = u'' # toggle edit mode, if inp in keyset['command'] or not edit and inp in keyset['edit']: edit = not edit # toggle if not edit: # switched to command mode, merge our lines echo(term.normal + lneditor.erase_border()) merge() lightbar.colors['highlight'] = term.yellow_reverse else: # switched to edit mode, save draft, # instantiate new line editor save_draft(save_key, get_lbcontent(lightbar)) lneditor = get_lneditor(lightbar) lightbar.colors['highlight'] = term.red_reverse dirty = True # command mode, kill line elif not edit and inp in keyset['kill']: # when 'killing' a line, make accomidations to clear # bottom-most row, otherwise a ghosting effect occurs for _count in count_repeat(): del lightbar.content[lightbar.index] set_lbcontent(lightbar, get_lbcontent(lightbar)) if lightbar.visible_bottom > len(lightbar.content): echo(lightbar.refresh_row(lightbar.visible_bottom + 1)) else: dirty = True save_draft(save_key, get_lbcontent(lightbar)) # command mode, insert line elif not edit and inp in keyset['insert']: for _count in count_repeat(): lightbar.content.insert(lightbar.index, ( lightbar.index, HARDWRAP, )) set_lbcontent(lightbar, get_lbcontent(lightbar)) save_draft(save_key, get_lbcontent(lightbar)) dirty = True # command mode; goto line elif not edit and inp in keyset['goto']: if num_repeat == -1: # 'G' alone goes to end of file, num_repeat = len(lightbar.content) echo(lightbar.goto((num_repeat or 1) - 1)) echo(statusline(lightbar)) # command mode; insert-before (switch to edit mode) elif not edit and inp in keyset['insert-before']: lightbar.content.insert(lightbar.index, ( lightbar.index, HARDWRAP, )) set_lbcontent(lightbar, get_lbcontent(lightbar)) edit = dirty = True # switched to edit mode, save draft, # instantiate new line editor lightbar.colors['highlight'] = term.red_reverse lneditor = get_lneditor(lightbar) save_draft(save_key, get_lbcontent(lightbar)) # command mode; insert-after (switch to edit mode) elif not edit and inp in keyset['insert-after']: lightbar.content.insert(lightbar.index + 1, ( lightbar.index + 1, HARDWRAP, )) set_lbcontent(lightbar, get_lbcontent(lightbar)) edit = dirty = True # switched to edit mode, save draft, # instantiate new line editor lightbar.colors['highlight'] = term.red_reverse lightbar.move_down() lneditor = get_lneditor(lightbar) save_draft(save_key, get_lbcontent(lightbar)) # command mode, undo elif not edit and inp in keyset['undo']: for _count in count_repeat(): if len(UNDO): set_lbcontent(lightbar, UNDO.pop()) dirty = True else: echo(u'\a') break # command mode, join line elif not edit and inp in keyset['join']: for _count in count_repeat(): if lightbar.index + 1 < len(lightbar.content): idx = lightbar.index lightbar.content[idx] = ( idx, WHITESPACE.join(( lightbar.content[idx][1].rstrip(), lightbar.content[idx + 1][1].lstrip(), ))) del lightbar.content[idx + 1] prior_length = len(lightbar.content) set_lbcontent(lightbar, get_lbcontent(lightbar)) if len(lightbar.content) - prior_length > 0: lightbar.move_down() dirty = True else: echo(u'\a') break if dirty: save_draft(save_key, get_lbcontent(lightbar)) # command mode, basic cmds & movement elif not edit and inp is not None: if inp in ( u'a', u'A', ): if yes_no( lightbar, term.yellow(u'- ') + term.bold_red(u'AbORt') + term.yellow(u' -')): return False dirty = True elif inp in ( u's', u'S', ): if yes_no( lightbar, term.yellow(u'- ') + term.bold_green(u'SAVE') + term.yellow(u' -'), term.reverse_green): save(save_key, get_lbcontent(lightbar)) return True dirty = True elif inp in (u'?', ): pager = Pager(lightbar.height, lightbar.width, lightbar.yloc, lightbar.xloc) pager.update(get_help()) pager.colors['border'] = term.bold_blue echo(pager.border() + pager.title(u''.join(( term.bold_blue(u'-( '), term.white_on_blue(u'r'), u':', term.bold(u'eturn'), u' ', term.bold_blue(u' )-'), )))) pager.keyset['exit'].extend([u'r', u'R']) pager.read() echo(pager.erase_border()) dirty = True else: moved = False for _count in count_repeat(): echo(lightbar.process_keystroke(inp)) moved = lightbar.moved or moved if moved: echo(statusline(lightbar)) # edit mode; movement elif edit and inp in movement: dirty = merge() if inp in ( u'\r', term.KEY_ENTER, ): lightbar.content.insert(lightbar.index + 1, [lightbar.selection[0] + 1, u'']) inp = term.KEY_DOWN dirty = True ucs = lightbar.process_keystroke(inp) if lightbar.moved: echo(term.normal + lneditor.erase_border()) echo(ucs) lneditor = get_lneditor(lightbar) save_draft(save_key, get_lbcontent(lightbar)) echo(lneditor.refresh()) else: dirty = True # edit mode -- append character / backspace elif edit and inp is not None: if (inp in keyset['rubout'] and len(lneditor.content) == 0 and lightbar.index > 0): # erase past margin, echo(term.normal + lneditor.erase_border()) del lightbar.content[lightbar.index] lightbar.move_up() set_lbcontent(lightbar, get_lbcontent(lightbar)) lneditor = get_lneditor(lightbar) dirty = True else: # edit mode, add/delete ch echo(lneditor.process_keystroke(inp)) if lneditor.moved: echo(statusline(lightbar)) if inp is not None and type(inp) is not int and not inp.isdigit(): # commands were processed, reset num_repeat to 1 num_repeat = -1