1 # Copyright (C) 2009, 2010 Roman Zimbelmann <romanz@lavabit.com>
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 """The BrowserView manages a set of BrowserColumns."""
18 from ranger
.ext
.signals
import Signal
19 from ranger
.ext
.keybinding_parser
import key_to_string
21 from .browsercolumn
import BrowserColumn
22 from .pager
import Pager
23 from ..displayable
import DisplayableContainer
25 class BrowserView(Widget
, DisplayableContainer
):
29 draw_bookmarks
= False
35 def __init__(self
, win
, ratios
, preview
= True):
36 DisplayableContainer
.__init
__(self
, win
)
37 self
.preview
= preview
40 self
.pager
= Pager(self
.win
, embedded
=True)
41 self
.pager
.visible
= False
42 self
.add_child(self
.pager
)
44 self
.change_ratios(ratios
, resize
=False)
46 for option
in ('preview_directories', 'preview_files'):
47 self
.settings
.signal_bind('setopt.' + option
,
48 self
._request
_clear
_if
_has
_borders
, weak
=True)
50 self
.fm
.env
.signal_bind('move', self
.request_clear
)
51 self
.settings
.signal_bind('setopt.column_ratios', self
.request_clear
)
53 def change_ratios(self
, ratios
, resize
=True):
54 if isinstance(ratios
, Signal
):
57 for column
in self
.columns
:
59 self
.remove_child(column
)
62 ratio_sum
= float(sum(ratios
))
63 self
.ratios
= tuple(x
/ ratio_sum
for x
in ratios
)
65 last
= 0.1 if self
.settings
.padding_right
else 0
66 if len(self
.ratios
) >= 2:
67 self
.stretch_ratios
= self
.ratios
[:-2] + \
68 ((self
.ratios
[-2] + self
.ratios
[-1] * 1.0 - last
),
69 (self
.ratios
[-1] * last
))
71 offset
= 1 - len(ratios
)
72 if self
.preview
: offset
+= 1
74 for level
in range(len(ratios
)):
75 fl
= BrowserColumn(self
.win
, level
+ offset
)
77 self
.columns
.append(fl
)
80 self
.main_column
= self
.columns
[self
.preview
and -2 or -1]
82 self
.main_column
= None
84 self
.main_column
.display_infostring
= True
85 self
.main_column
.main_column
= True
87 self
.resize(self
.y
, self
.x
, self
.hei
, self
.wid
)
89 def _request_clear_if_has_borders(self
):
90 if self
.settings
.draw_borders
:
93 def request_clear(self
):
94 self
.need_clear
= True
99 self
.need_redraw
= True
100 self
.need_clear
= False
101 for path
in self
.fm
.tabs
.values():
103 directory
= self
.env
.get_directory(path
)
104 directory
.load_content_if_outdated()
106 DisplayableContainer
.draw(self
)
107 if self
.settings
.draw_borders
:
109 if self
.draw_bookmarks
:
110 self
._draw
_bookmarks
()
111 elif self
.draw_hints
:
115 if self
.pager
.visible
:
117 self
.fm
.ui
.win
.move(self
.main_column
.y
, self
.main_column
.x
)
122 x
= self
.main_column
.x
123 y
= self
.main_column
.y
+ self
.main_column
.target
.pointer\
124 - self
.main_column
.scroll_begin
125 self
.fm
.ui
.win
.move(y
, x
)
129 def _draw_borders(self
):
131 self
.color('in_browser', 'border')
134 right_end
= self
.wid
- 1
136 for child
in self
.columns
:
137 if not child
.has_preview():
138 left_start
= child
.x
+ child
.wid
141 if not self
.pager
.visible
:
142 for child
in reversed(self
.columns
):
143 if not child
.has_preview():
144 right_end
= child
.x
- 1
147 if right_end
< left_start
:
148 right_end
= self
.wid
- 1
150 win
.hline(0, left_start
, curses
.ACS_HLINE
, right_end
- left_start
)
151 win
.hline(self
.hei
- 1, left_start
, curses
.ACS_HLINE
,
152 right_end
- left_start
)
153 win
.vline(1, left_start
, curses
.ACS_VLINE
, self
.hei
- 2)
155 for child
in self
.columns
:
156 if not child
.has_preview():
158 if child
.main_column
and self
.pager
.visible
:
159 win
.vline(1, right_end
, curses
.ACS_VLINE
, self
.hei
- 2)
161 x
= child
.x
+ child
.wid
164 win
.vline(1, x
, curses
.ACS_VLINE
, y
- 1)
165 win
.addch(0, x
, curses
.ACS_TTEE
, 0)
166 win
.addch(y
, x
, curses
.ACS_BTEE
, 0)
168 # in case it's off the boundaries
171 self
.addch(0, left_start
, curses
.ACS_ULCORNER
)
172 self
.addch(self
.hei
- 1, left_start
, curses
.ACS_LLCORNER
)
173 self
.addch(0, right_end
, curses
.ACS_URCORNER
)
174 self
.addch(self
.hei
- 1, right_end
, curses
.ACS_LRCORNER
)
176 def _draw_bookmarks(self
):
177 self
.fm
.bookmarks
.update_if_outdated()
179 self
.need_clear
= True
181 sorted_bookmarks
= sorted((item
for item
in self
.fm
.bookmarks \
182 if self
.fm
.settings
.show_hidden_bookmarks
or \
183 '/.' not in item
[1].path
), key
=lambda t
: t
[0].lower())
185 hei
= min(self
.hei
- 1, len(sorted_bookmarks
))
186 ystart
= self
.hei
- hei
189 self
.addnstr(ystart
- 1, 0, "mark path".ljust(self
.wid
), self
.wid
)
191 whitespace
= " " * maxlen
192 for line
, items
in zip(range(self
.hei
-1), sorted_bookmarks
):
194 string
= " " + key
+ " " + mark
.path
195 self
.addstr(ystart
+ line
, 0, whitespace
)
196 self
.addnstr(ystart
+ line
, 0, string
, self
.wid
)
198 self
.win
.chgat(ystart
- 1, 0, curses
.A_UNDERLINE
)
200 def _draw_hints(self
):
201 self
.need_clear
= True
203 for k
, v
in self
.fm
.env
.keybuffer
.pointer
.items():
205 if isinstance(v
, dict):
209 if text
.startswith('hint') or text
.startswith('chain hint'):
211 hints
.append((k
, text
))
212 hints
.sort(key
=lambda t
: t
[1])
214 hei
= min(self
.hei
- 1, len(hints
))
215 ystart
= self
.hei
- hei
216 self
.addnstr(ystart
- 1, 0, "key command".ljust(self
.wid
),
218 self
.win
.chgat(ystart
- 1, 0, curses
.A_UNDERLINE
)
219 whitespace
= " " * self
.wid
221 for key
, cmd
in hints
:
222 string
= " " + key
.ljust(11) + " " + cmd
223 self
.addstr(i
, 0, whitespace
)
224 self
.addnstr(i
, 0, string
, self
.wid
)
228 # Should the last column be cut off? (Because there is no preview)
229 if not self
.settings
.collapse_preview
or not self
.preview \
230 or not self
.stretch_ratios
:
232 result
= not self
.columns
[-1].has_preview()
233 target
= self
.columns
[-1].target
234 if not result
and target
and target
.is_file
and \
235 self
.fm
.settings
.preview_script
and \
236 self
.fm
.settings
.use_preview_script
:
238 result
= not self
.fm
.previews
[target
.realpath
]['foundpreview']
240 return self
.old_collapse
241 self
.old_collapse
= result
244 def resize(self
, y
, x
, hei
, wid
):
245 """Resize all the columns according to the given ratio"""
246 DisplayableContainer
.resize(self
, y
, x
, hei
, wid
)
247 borders
= self
.settings
.draw_borders
248 pad
= 1 if borders
else 0
251 self
.is_collapsed
= self
._collapse
()
252 if self
.is_collapsed
:
253 generator
= enumerate(self
.stretch_ratios
)
255 generator
= enumerate(self
.ratios
)
257 last_i
= len(self
.ratios
) - 1
259 for i
, ratio
in generator
:
260 wid
= int(ratio
* self
.wid
)
262 cut_off
= self
.is_collapsed
and not self
.settings
.padding_right
265 wid
= int(self
.wid
- left
+ 1 - pad
)
267 self
.columns
[i
].resize(pad
, left
- 1, hei
- pad
* 2, 1)
268 self
.columns
[i
].visible
= False
272 self
.pager
.resize(pad
, left
, hei
- pad
* 2, \
273 max(1, self
.wid
- left
- pad
))
276 self
.columns
[i
].resize(pad
, left
, hei
- pad
* 2, \
277 max(1, self
.wid
- left
- pad
))
281 self
.columns
[i
].resize(pad
, left
, hei
- pad
* 2, \
288 def click(self
, event
):
289 if DisplayableContainer
.click(self
, event
):
291 direction
= event
.mouse_wheel_direction()
293 self
.main_column
.scroll(direction
)
296 def open_pager(self
):
297 self
.pager
.visible
= True
298 self
.pager
.focused
= True
299 self
.need_clear
= True
302 self
.columns
[-1].visible
= False
303 self
.columns
[-2].visible
= False
307 def close_pager(self
):
308 self
.pager
.visible
= False
309 self
.pager
.focused
= False
310 self
.need_clear
= True
313 self
.columns
[-1].visible
= True
314 self
.columns
[-2].visible
= True
319 DisplayableContainer
.poke(self
)
321 # Show the preview column when it has a preview but has
322 # been hidden (e.g. because of padding_right = False)
323 if not self
.pager
.visible
and not self
.columns
[-1].visible
and \
324 self
.columns
[-1].target
and self
.columns
[-1].target
.is_directory \
325 or self
.columns
[-1].has_preview() and not self
.pager
.visible
:
326 self
.columns
[-1].visible
= True
328 if self
.preview
and self
.is_collapsed
!= self
._collapse
():
329 self
.resize(self
.y
, self
.x
, self
.hei
, self
.wid
)