2 """Yet another curses-based directory tree browser, in Python.
4 I thought I could use something like this for filename entry, kind of
5 like the old 4DOS 'select' command --- cd $(cursoutline.py). So you
6 navigate and hit Enter, and it exits and spits out the file you're on.
9 # There are several general approaches to the drawing-an-outline
10 # problem. This program supports the following operations:
11 # - move cursor to previous item (in preorder traversal)
12 # - move cursor to next item (likewise)
15 # And because it runs over the filesystem, it must be at least somewhat lazy
16 # about expanding children.
17 # And it doesn't really bother to worry about someone else changing the outline
19 # So the strategy is to store our current linear position in the
20 # inorder traversal, and defer operations on the current node until the next
21 # time we're traversing.
24 import curses
.wrapper
, time
, random
, cgitb
, os
, sys
25 cgitb
.enable(format
="text")
31 # XXX this won't work with UTF-8
32 return data
+ ' ' * (width
- len(data
))
35 def __init__(self
, name
):
37 def render(self
, depth
, width
):
38 return pad('%s%s %s' % (' ' * 4 * depth
, self
.icon(),
39 os
.path
.basename(self
.name
)), width
)
40 def icon(self
): return ' '
41 def traverse(self
): yield self
, 0
42 def expand(self
): pass
43 def collapse(self
): pass
46 def __init__(self
, name
):
47 File
.__init
__(self
, name
)
48 try: self
.kidnames
= os
.listdir(name
)
49 except: self
.kidnames
= None # probably permission denied
53 if self
.kidnames
is None: return []
55 self
.kids
= [factory(os
.path
.join(self
.name
, kid
))
56 for kid
in self
.kidnames
]
59 if self
.expanded
: return '[-]'
60 elif self
.kidnames
is None: return '[?]'
61 elif self
.children(): return '[+]'
63 def expand(self
): self
.expanded
= True
64 def collapse(self
): self
.expanded
= False
67 if not self
.expanded
: return
68 for child
in self
.children():
69 for kid
, depth
in child
.traverse():
73 if os
.path
.isdir(name
): return Dir(name
)
74 else: return File(name
)
77 cargo_cult_routine(stdscr
)
79 mydir
= factory(start
)
87 curses
.init_pair(1, curses
.COLOR_WHITE
, curses
.COLOR_BLUE
)
89 offset
= max(0, curidx
- curses
.LINES
+ 3)
90 for data
, depth
in mydir
.traverse():
92 stdscr
.attrset(curses
.color_pair(1) | curses
.A_BOLD
)
94 getattr(data
, pending_action
)()
101 stdscr
.attrset(curses
.color_pair(0))
102 if 0 <= line
- offset
< curses
.LINES
- 1:
103 stdscr
.addstr(line
- offset
, 0,
104 data
.render(depth
, curses
.COLS
))
108 if ch
== curses
.KEY_UP
: curidx
-= 1
109 elif ch
== curses
.KEY_DOWN
: curidx
+= 1
110 elif ch
== curses
.KEY_PPAGE
:
111 curidx
-= curses
.LINES
112 if curidx
< 0: curidx
= 0
113 elif ch
== curses
.KEY_NPAGE
:
114 curidx
+= curses
.LINES
115 if curidx
>= line
: curidx
= line
- 1
116 elif ch
== curses
.KEY_RIGHT
: pending_action
= 'expand'
117 elif ch
== curses
.KEY_LEFT
: pending_action
= 'collapse'
118 elif ch
== ESC
: return
119 elif ch
== ord('\n'): pending_save
= True
122 def cargo_cult_routine(win
):
130 saved_stdin
= os
.dup(0)
131 saved_stdout
= os
.dup(1)
134 stdin
= os
.open('/dev/tty', os
.O_RDONLY
)
135 stdout
= os
.open('/dev/tty', os
.O_RDWR
)
136 return saved_stdin
, saved_stdout
138 def restore_stdio((saved_stdin
, saved_stdout
)):
144 if __name__
== '__main__':
146 if len(sys
.argv
) > 1:
148 saved_fds
= open_tty()
149 try: curses
.wrapper(main
)
150 finally: restore_stdio(saved_fds
)
This page took 2.785151 seconds and 4 git commands to generate.