# This file is part of ranger, the console file manager.
# License: GNU GPL version 3, see the file "AUTHORS" for details.
"""The base GUI element for views on the directory"""
from __future__ import (absolute_import, division, print_function)
import curses
from ranger.ext.keybinding_parser import key_to_string
from . import Widget
from ..displayable import DisplayableContainer
class ViewBase(Widget, DisplayableContainer): # pylint: disable=too-many-instance-attributes
draw_bookmarks = False
need_clear = False
draw_hints = False
draw_info = False
def __init__(self, win): # pylint: disable=super-init-not-called
DisplayableContainer.__init__(self, win)
self.fm.signal_bind('move', self.request_clear)
self.old_draw_borders = self.settings.draw_borders
self.columns = None
self.main_column = None
self.pager = None
def request_clear(self):
self.need_clear = True
def draw(self):
if self.need_clear:
self.win.erase()
self.need_redraw = True
self.need_clear = False
for tab in self.fm.tabs.values():
directory = tab.thisdir
if directory:
directory.load_content_if_outdated()
directory.use()
DisplayableContainer.draw(self)
if self.draw_bookmarks:
self._draw_bookmarks()
elif self.draw_hints:
self._draw_hints()
elif self.draw_info:
self._draw_info(self.draw_info)
def finalize(self):
if self.pager is not None and self.pager.visible:
try:
self.fm.ui.win.move(self.main_column.y, self.main_column.x)
except curses.error:
pass
else:
col_x = self.main_column.x
col_y = self.main_column.y + self.main_column.target.pointer \
- self.main_column.scroll_begin
try:
self.fm.ui.win.move(col_y, col_x)
except curses.error:
pass
def _draw_bookmarks(self):
self.columns[-1].clear_image(force=True)
self.fm.bookmarks.update_if_outdated()
self.color_reset()
self.need_clear = True
sorted_bookmarks = sorted(
(
item for item in self.fm.bookmarks
if self.fm.settings.show_hidden_bookmarks
or '/.' not in item[1].path
),
key=lambda t: t[0].lower(),
)
hei = min(self.hei - 1, len(sorted_bookmarks))
ystart = self.hei - hei
maxlen = self.wid
self.addnstr(ystart - 1, 0, "mark path".ljust(self.wid), self.wid)
whitespace = " " * maxlen
for line, items in zip(range(self.hei - 1), sorted_bookmarks):
key, mark = items
string = " " + key + " " + mark.path
self.addstr(ystart + line, 0, whitespace)
self.addnstr(ystart + line, 0, string, self.wid)
self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
def _draw_info(self, lines):
self.columns[-1].clear_image(force=True)
self.need_clear = True
hei = min(self.hei - 1, len(lines))
ystart = self.hei - hei
i = ystart
whitespace = " " * self.wid
for line in lines:
if i >= self.hei:
break
self.addstr(i, 0, whitespace)
self.addnstr(i, 0, line, self.wid)
i += 1
def _draw_hints(self):
self.columns[-1].clear_image(force=True)
self.color_reset()
self.need_clear = True
hints = []
def populate_hints(keymap, prefix=""):
for key, value in keymap.items():
key = prefix + key_to_string(key)
if isinstance(value, dict):
populate_hints(value, key)
else:
text = value
if text.startswith('hint') or text.startswith('chain hint'):
continue
hints.append((key, text))
populate_hints(self.fm.ui.keybuffer.pointer)
def sort_hints(hints):
"""Sort the hints by the action string but first group them by the
first key.
"""
from itertools import groupby
# groupby needs the list to be sorted.
hints.sort(key=lambda t: t[0])
def group_hints(hints):
def first_key(hint):
return hint[0][0]
def action_string(hint):
return hint[1]
return (sorted(group, key=action_string)
for _, group
in groupby(
hints,
key=first_key))
grouped_hints = group_hints(hints)
# If there are too many hints, collapse the sublists.
if len(hints) > self.fm.settings.hint_collapse_threshold:
def first_key_in_group(group):
return group[0][0][0]
grouped_hints = (
[(first_key_in_group(hint_group), "...")]
if len(hint_group) > 1
else hint_group
for hint_group in grouped_hints
)
# Sort by the first action in group.
grouped_hints = sorted(grouped_hints, key=lambda g: g[0][1])
def flatten(nested_list):
return [item for inner_list in nested_list for item in inner_list]
return flatten(grouped_hints)
hints = sort_hints(hints)
hei = min(self.hei - 1, len(hints))
ystart = self.hei - hei
self.addnstr(ystart - 1, 0, "key command".ljust(self.wid), self.wid)
try:
self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
except curses.error:
pass
whitespace = " " * self.wid
i = ystart
for key, cmd in hints:
string = " " + key.ljust(11) + " " + cmd
self.addstr(i, 0, whitespace)
self.addnstr(i, 0, string, self.wid)
i += 1
def click(self, event):
if DisplayableContainer.click(self, event):
return True
direction = event.mouse_wheel_direction()
if direction:
self.main_column.scroll(direction)
return False
def resize(self, y, x, hei=None, wid=None):
DisplayableContainer.resize(self, y, x, hei, wid)
def poke(self):
DisplayableContainer.poke(self)