6 from pysize
.ui
.utils
import human_unit
, short_string
, update_progress
7 from pysize
.ui
.utils
import sanitize_string
8 from pysize
.ui
.char_matrix
import CharMatrix
, MASK
, SELECTED
9 from pysize
.core
.pysize_fs_tree
import pysize_tree
10 from pysize
.core
.compute_size
import size_observable
12 class _CursesApp(object):
14 def __init__(self
, win
, options
, args
):
16 self
.max_depth
= options
.max_depth
17 if options
.min_size
!= 'auto':
18 raise Exception, 'curses UI supports only --min-size=auto'
22 self
.last_star_time
= time
.time()
23 size_observable
.add_observer(self
.draw_star
)
26 progress
= update_progress()
28 self
.status_win
.insch(0, self
.width
- 1, progress
, curses
.A_REVERSE
)
29 self
.status_win
.refresh()
31 def _init_curses(self
):
32 curses
.use_default_colors()
37 return sum(map(lambda n
: 1<<n
, positions
))
39 self
.MATRIX_TO_CURSES
= {
41 dots(3, 4, 5): curses
.ACS_HLINE
,
42 dots(1, 4, 7): curses
.ACS_VLINE
,
43 dots(1, 3, 4, 5, 7): curses
.ACS_PLUS
,
44 dots(4, 5, 7): curses
.ACS_ULCORNER
,
45 dots(3, 4, 7): curses
.ACS_URCORNER
,
46 dots(1, 4, 5): curses
.ACS_LLCORNER
,
47 dots(1, 3, 4): curses
.ACS_LRCORNER
,
48 dots(3, 4, 5, 7): curses
.ACS_TTEE
,
49 dots(1, 3, 4, 5): curses
.ACS_BTEE
,
50 dots(1, 4, 5, 7): curses
.ACS_LTEE
,
51 dots(1, 3, 4, 7): curses
.ACS_RTEE
55 self
.status_win
= curses
.newwin(0, 0)
56 self
.panel
= curses
.newwin(0, 0)
59 print 'Cannot create windows'
63 """Called when the terminal size changes."""
64 self
.height
, self
.width
= self
.window
.getmaxyx()
65 self
.height
-= 1 # Status bar
67 self
.status_win
.resize(1, self
.width
)
68 self
.status_win
.mvwin(self
.height
, 0)
69 self
.panel
.resize(self
.height
, self
.width
)
70 self
._set
_paths
(self
.paths
)
76 self
.tree
= pysize_tree(self
.paths
, self
.max_depth
,
78 self
.need_tree
= False
79 if not self
.tree
.root
:
83 self
._draw
_matrix
(CharMatrix(self
.width
, self
.height
, self
.tree
,
87 self
.status_win
.erase()
88 self
.status_win
.hline(0, 0, ord(' ') | curses
.A_REVERSE
, self
.width
)
89 status_line
= sanitize_string(self
.tree
.root
.get_name()) + ' '
90 status_line
+= human_unit(self
.tree
.root
.size
)
92 if len(status_line
) + len(END
) >= self
.width
:
93 status_line
= short_string(status_line
, self
.width
- 2)
96 self
.status_win
.addstr(status_line
, curses
.A_REVERSE
)
97 self
.status_win
.refresh()
104 except curses
.error
, e
:
108 def _set_paths(self
, paths
):
109 self
.selected_node
= None
110 self
.selection
= None
112 self
.need_tree
= True
114 def _next_step(self
):
115 self
._set
_paths
([self
.selected_node
.get_dirname()])
117 def _select(self
, function
):
118 if not self
.selected_node
:
119 if self
.tree
.root
.children
:
120 self
.selected_node
= self
.tree
.root
.children
[0]
122 # Forcing browsing to the left
123 self
._set
_paths
([self
.tree
.root
.get_dirname()])
125 selected
= function(self
.selected_node
)
127 if function
== self
.tree
.get_first_child
and \
128 self
.selected_node
.is_dir():
129 # Browsing to the right
132 self
.selected_node
= selected
133 if self
.selected_node
== self
.tree
.root
:
134 # Browsing to the left
135 self
._set
_paths
([self
.tree
.root
.get_dirname()])
138 if self
.selected_node
:
139 self
._set
_paths
(self
.selected_node
.get_fullpaths())
142 """The main dispatcher."""
147 curses
.KEY_RESIZE
: lambda:
150 curses
.KEY_DOWN
: lambda:
151 self
._select
(self
.tree
.get_next_sibling
),
153 curses
.KEY_UP
: lambda:
154 self
._select
(self
.tree
.get_previous_sibling
),
156 curses
.KEY_LEFT
: lambda:
157 self
._select
(self
.tree
.get_parent
),
159 curses
.KEY_RIGHT
: lambda:
160 self
._select
(self
.tree
.get_first_child
),
168 c
= self
.window
.getch()
169 action
= key_bindings
.get(c
, lambda: None)
172 def _draw_matrix(self
, matrix
):
173 for (y
, line
) in enumerate(matrix
.matrix
):
174 for (x
, char
) in enumerate(line
):
175 if isinstance(char
, int):
176 selected
= (char
& SELECTED
) != 0
178 char
= self
.MATRIX_TO_CURSES
[char
]
180 char |
= curses
.A_REVERSE
181 self
.panel
.insch(y
, x
, char
)
184 char
= char
.encode('UTF-8')
185 except UnicodeDecodeError:
187 self
.panel
.insstr(y
, x
, char
)
189 def _run_curses(win
, options
, args
):
190 app
= _CursesApp(win
, options
, args
)
193 def run(options
, args
):
194 curses
.wrapper(_run_curses
, options
, args
)
197 return sys
.stdin
.isatty() and sys
.stdout
.isatty()