def drawLeftStatus(vd, scr, vs): 'Draw left side of status bar.' cattr = colors.get_color('color_status') active = vs is vd.sheets[0] # active sheet if active: cattr = update_attr(cattr, colors.color_active_status, 0) else: cattr = update_attr(cattr, colors.color_inactive_status, 0) if scr is vd.winTop: cattr = update_attr(cattr, colors.color_top_status, 1) attr = cattr.attr error_attr = update_attr(cattr, colors.color_error, 1).attr warn_attr = update_attr(cattr, colors.color_warning, 2).attr sep = options.disp_status_sep x = 0 y = vs.windowHeight - 1 # status for each window try: lstatus = vs.leftStatus() maxwidth = options.disp_lstatus_max if maxwidth > 0: lstatus = middleTruncate(lstatus, maxwidth // 2) x = clipdraw(scr, y, 0, lstatus, attr, w=vs.windowWidth - 1) vd.onMouse(scr, y, 0, 1, x, BUTTON1_PRESSED='sheets', BUTTON3_PRESSED='rename-sheet', BUTTON3_CLICKED='rename-sheet') except Exception as e: vd.exceptionCaught(e) if not active: return one = False for (pri, msgparts), n in sorted(vd.statuses.items(), key=lambda k: -k[0][0]): try: if x > vs.windowWidth: break if one: # any messages already: x += clipdraw(scr, y, x, sep, attr, w=vs.windowWidth - x) one = True msg = composeStatus(msgparts, n) if pri == 3: msgattr = error_attr elif pri == 2: msgattr = warn_attr elif pri == 1: msgattr = warn_attr else: msgattr = attr x += clipdraw(scr, y, x, msg, msgattr, w=vs.windowWidth - x) except Exception as e: vd.exceptionCaught(e)
def drawColHeader(self, scr, y, h, vcolidx): 'Compose and draw column header for given vcolidx.' col = self.visibleCols[vcolidx] # hdrattr highlights whole column header # sepattr is for header separators and indicators sepcattr = colors.get_color('color_column_sep') hdrcattr = self._colorize(col, None) if vcolidx == self.cursorVisibleColIndex: hdrcattr = update_attr(hdrcattr, colors.color_current_hdr, 2) C = options.disp_column_sep if (self.keyCols and col is self.keyCols[-1]) or vcolidx == self.rightVisibleColIndex: C = options.disp_keycol_sep x, colwidth = self._visibleColLayout[vcolidx] # AnameTC T = getType(col.type).icon if T is None: # still allow icon to be explicitly non-displayed '' T = '?' hdrs = col.name.split('\n') for i in range(h): name = ' ' # save room at front for LeftMore if h-i-1 < len(hdrs): name += hdrs[::-1][h-i-1] if len(name) > colwidth-1: name = name[:colwidth-len(options.disp_truncator)] + options.disp_truncator if i == h-1: hdrcattr = update_attr(hdrcattr, colors.color_bottom_hdr, 5) clipdraw(scr, y+i, x, name, hdrcattr.attr, colwidth) vd.onMouse(scr, y+i, x, 1, colwidth, BUTTON3_RELEASED='rename-col') if C and x+colwidth+len(C) < self.windowWidth: scr.addstr(y+i, x+colwidth, C, sepcattr.attr) clipdraw(scr, y+h-1, x+colwidth-len(T), T, hdrcattr.attr) try: if vcolidx == self.leftVisibleColIndex and col not in self.keyCols and self.nonKeyVisibleCols.index(col) > 0: A = options.disp_more_left scr.addstr(y, x, A, sepcattr.attr) except ValueError: # from .index pass
def input(scr, prompt, **kwargs): ymax, xmax = scr.getmaxyx() promptlen = visidata.clipdraw(scr, ymax - 1, 0, prompt, 0, w=xmax - 1) r = visidata.vd.editline(scr, ymax - 1, promptlen, xmax - promptlen - 2, **kwargs) curses.flushinp() return r
def drawRightStatus(vd, scr, vs): 'Draw right side of status bar. Return length displayed.' rightx = vs.windowWidth ret = 0 statcolors = [ (vd.rightStatus(vs), 'color_status'), ] active = vs is vd.sheets[0] # active sheet if active: statcolors.append((vd.keystrokes, 'color_keystrokes')) if vs.currentThreads: statcolors.insert(0, vd.checkMemoryUsage()) if vs.progresses: gerund = vs.progresses[0].gerund else: gerund = 'processing' statcolors.insert(1, (' %s %s…' % (vs.progressPct, gerund), 'color_status')) if active and vd.currentReplay: statcolors.insert(0, (vd.replayStatus, 'color_status_replay')) for rstatcolor in statcolors: if rstatcolor: try: rstatus, coloropt = rstatcolor rstatus = ' ' + rstatus cattr = colors.get_color(coloropt) if scr is vd.winTop: cattr = update_attr(cattr, colors.color_top_status, 0) if active: cattr = update_attr(cattr, colors.color_active_status, 0) else: cattr = update_attr(cattr, colors.color_inactive_status, 0) statuslen = clipdraw(scr, vs.windowHeight - 1, rightx, rstatus, cattr.attr, w=vs.windowWidth - 1, rtl=True) rightx -= statuslen ret += statuslen except Exception as e: vd.exceptionCaught(e) if scr: curses.doupdate() return ret
def input(self, prompt, type=None, defaultLast=False, history=[], **kwargs): '''Display prompt and return line of user input. type: list of previous items, or a string indicating the type of input. defaultLast: on empty input, if True, return last history item ''' if type: if isinstance(type, str): history = self.lastInputs[type] else: history = type sheet = self.sheets[0] rstatuslen = self.drawRightStatus(sheet._scr, sheet) attr = 0 promptlen = clipdraw(sheet._scr, sheet.windowHeight - 1, 0, prompt, attr, w=sheet.windowWidth - rstatuslen - 1) ret = self.editText(sheet.windowHeight - 1, promptlen, sheet.windowWidth - promptlen - rstatuslen - 2, attr=colors.color_edit_cell, unprintablechar=options.disp_unprintable, truncchar=options.disp_truncator, history=history, **kwargs) if ret: if isinstance(type, str): self.lastInputs[type].append(ret) elif defaultLast: history or fail("no previous input") ret = history[-1] return ret
def editline(vd, scr, y, x, w, i=0, attr=curses.A_NORMAL, value='', fillchar=' ', truncchar='-', unprintablechar='.', completer=lambda text, idx: None, history=[], display=True, updater=lambda val: None): 'A better curses line editing widget.' with EnableCursor(): ESC = '^[' ENTER = '^J' TAB = '^I' history_state = HistoryState(history) complete_state = CompleteState(completer) insert_mode = True first_action = True v = str(value) # value under edit # i = 0 # index into v, initial value can be passed in as argument as of 1.2 if i != 0: first_action = False left_truncchar = right_truncchar = truncchar def rfind_nonword(s, a, b): if not s: return 0 while not s[b].isalnum() and b >= a: # first skip non-word chars b -= 1 while s[b].isalnum() and b >= a: b -= 1 return b while True: updater(v) if display: dispval = clean_printable(v) else: dispval = '*' * len(v) dispi = i # the onscreen offset within the field where v[i] is displayed if len(dispval) < w: # entire value fits dispval += fillchar * (w - len(dispval) - 1) elif i == len(dispval): # cursor after value (will append) dispi = w - 1 dispval = left_truncchar + dispval[len(dispval) - w + 2:] + fillchar elif i >= len(dispval) - w // 2: # cursor within halfwidth of end dispi = w - (len(dispval) - i) dispval = left_truncchar + dispval[len(dispval) - w + 1:] elif i <= w // 2: # cursor within halfwidth of beginning dispval = dispval[:w - 1] + right_truncchar else: dispi = w // 2 # visual cursor stays right in the middle k = 1 if w % 2 == 0 else 0 # odd widths have one character more dispval = left_truncchar + dispval[i - w // 2 + 1:i + w // 2 - k] + right_truncchar prew = clipdraw(scr, y, x, dispval[:dispi], attr, w) clipdraw(scr, y, x + prew, dispval[dispi:], attr, w - prew + 1) scr.move(y, x + prew) ch = vd.getkeystroke(scr) if ch == '': continue elif ch == 'KEY_IC': insert_mode = not insert_mode elif ch == '^A' or ch == 'KEY_HOME': i = 0 elif ch == '^B' or ch == 'KEY_LEFT': i -= 1 elif ch in ('^C', '^Q', ESC): raise EscapeException(ch) elif ch == '^D' or ch == 'KEY_DC': v = delchar(v, i) elif ch == '^E' or ch == 'KEY_END': i = len(v) elif ch == '^F' or ch == 'KEY_RIGHT': i += 1 elif ch in ('^H', 'KEY_BACKSPACE', '^?'): i -= 1 v = delchar(v, i) elif ch == TAB: v, i = complete_state.complete(v, i, +1) elif ch == 'KEY_BTAB': v, i = complete_state.complete(v, i, -1) elif ch == ENTER: break elif ch == '^K': v = v[:i] # ^Kill to end-of-line elif ch == '^O': v = launchExternalEditor(v) elif ch == '^R': v = str(value) # ^Reload initial value elif ch == '^T': v = delchar(splice(v, i - 2, v[i - 1]), i) # swap chars elif ch == '^U': v = v[i:] i = 0 # clear to beginning elif ch == '^V': v = splice(v, i, until_get_wch(scr)) i += 1 # literal character elif ch == '^W': j = rfind_nonword(v, 0, i - 1) v = v[:j + 1] + v[i:] i = j + 1 # erase word elif ch == '^Z': suspend() elif history and ch == 'KEY_UP': v, i = history_state.up(v, i) elif history and ch == 'KEY_DOWN': v, i = history_state.down(v, i) elif ch.startswith('KEY_'): pass else: if first_action: v = '' if insert_mode: v = splice(v, i, ch) else: v = v[:i] + ch + v[i + 1:] i += 1 if i < 0: i = 0 if i > len(v): i = len(v) first_action = False complete_state.reset() return v
def drawRow(self, scr, row, rowidx, ybase, rowcattr: ColorAttr, maxheight, isNull='', topsep='', midsep='', botsep='', endsep='', keytopsep='', keymidsep='', keybotsep='', endtopsep='', endmidsep='', endbotsep='', colsep='', keysep='', selectednote='' ): # sepattr is the attr between cell/columns sepcattr = update_attr(rowcattr, colors.color_column_sep, 1) # apply current row here instead of in a colorizer, because it needs to know dispRowIndex if rowidx == self.cursorRowIndex: color_current_row = colors.get_color('color_current_row', 5) basecellcattr = sepcattr = update_attr(rowcattr, color_current_row) else: basecellcattr = rowcattr displines = {} # [vcolidx] -> list of lines in that cell for vcolidx, (x, colwidth) in sorted(self._visibleColLayout.items()): if x < self.windowWidth: # only draw inside window vcols = self.visibleCols if vcolidx >= len(vcols): continue col = vcols[vcolidx] cellval = col.getCell(row) if colwidth > 1 and isNumeric(col): cellval.display = cellval.display.rjust(colwidth-2) try: if isNull(cellval.value): cellval.note = options.disp_note_none cellval.notecolor = 'color_note_type' except (TypeError, ValueError): pass if col.height > 1: lines = splitcell(cellval.display, width=colwidth-2) else: lines = [cellval.display] displines[vcolidx] = (col, cellval, lines) heights = [0] for col, cellval, lines in displines.values(): h = len(lines) # of this cell heights.append(min(col.height, h)) height = min(max(heights), maxheight) or 1 # display even empty rows self._rowLayout[rowidx] = (ybase, height) for vcolidx, (col, cellval, lines) in displines.items(): if vcolidx not in self._visibleColLayout: continue x, colwidth = self._visibleColLayout[vcolidx] cattr = self._colorize(col, row, cellval) cattr = update_attr(cattr, basecellcattr) note = getattr(cellval, 'note', None) if note: notecattr = update_attr(cattr, colors.get_color(cellval.notecolor), 10) clipdraw(scr, ybase, x+colwidth-len(note), note, notecattr.attr) if len(lines) > height: firstn = sum(len(i)+1 for i in lines[:height-1]) lines[height-1] = cellval.display[firstn:] del lines[height:] elif len(lines) < height: lines.extend(['']*(height-len(lines))) for i, line in enumerate(lines): y = ybase+i if vcolidx == self.rightVisibleColIndex: # right edge of sheet if len(lines) == 1: sepchars = endsep else: if i == 0: sepchars = endtopsep elif i == len(lines)-1: sepchars = endbotsep else: sepchars = endmidsep elif (self.keyCols and col is self.keyCols[-1]): # last keycol if len(lines) == 1: sepchars = keysep else: if i == 0: sepchars = keytopsep elif i == len(lines)-1: sepchars = keybotsep else: sepchars = keymidsep else: if len(lines) == 1: sepchars = colsep else: if i == 0: sepchars = topsep elif i == len(lines)-1: sepchars = botsep else: sepchars = midsep clipdraw(scr, y, x, disp_column_fill+line, cattr.attr, w=colwidth-(1 if note else 0)) vd.onMouse(scr, y, x, 1, colwidth, BUTTON3_RELEASED='edit-cell') if x+colwidth+len(sepchars) <= self.windowWidth: scr.addstr(y, x+colwidth, sepchars, basecellcattr.attr) if self.isSelected(row): clipdraw(scr, ybase, 0, selectednote, basecellcattr.attr) return height