Skip to content
Browse files

* cplay(1.47pre5):

	- '!' shell command with positional args
	- TAB completion
	- kill word/line with C-w/C-u
	- invert tags command 'i'
	- removed hide partial pathnames feature
	- renamed 'X' from [manual] to [stop]
	- bookmarks
	- fixed .. -> ../
	- actually chdir in filelist
	- fixed seek/stop/pause crash
	- minor code cleanup
  • Loading branch information...
1 parent d9745ec commit 77e22634ad19053a6095d9d8e9cba69553c47ab9 Ulf Betlehem committed with
Showing with 161 additions and 49 deletions.
  1. +15 −0 ChangeLog
  2. +146 −49 cplay
View
15 ChangeLog
@@ -1,3 +1,18 @@
+2003-04-07 Ulf Betlehem <flu@iki.fi>
+
+ * cplay(1.47pre5):
+ - '!' shell command with positional args
+ - TAB completion
+ - kill word/line with C-w/C-u
+ - invert tags command 'i'
+ - removed hide partial pathnames feature
+ - renamed 'X' from [manual] to [stop]
+ - bookmarks
+ - fixed .. -> ../
+ - actually chdir in filelist
+ - fixed seek/stop/pause crash
+ - minor code cleanup
+
2003-03-02 Ulf Betlehem <flu@iki.fi>
* cplay(1.47pre4):
View
195 cplay
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- python -*-
-__version__ = "cplay 1.47pre4"
+__version__ = "cplay 1.47pre5"
"""
cplay - A curses front-end for various audio players
@@ -319,11 +319,10 @@ class RootWindow(Window):
def command_quit(self, seriously=0):
if app.quit_silently or seriously: app.quit()
- app.stop_input_hook = None
app.do_input_hook = self.do_quit
app.start_input(_("Quit? (y/N)"))
- def do_quit(self, ch=None):
+ def do_quit(self, ch):
if chr(ch) == 'y': app.quit()
app.stop_input()
@@ -397,7 +396,7 @@ class ListWindow(Window):
self.buffer = []
self.bufptr = self.scrptr = 0
self.search_direction = 0
- self.last_match = ""
+ self.last_search = ""
self.keymap = Keymap()
self.keymap.bind(['k', curses.KEY_UP, 16], self.cursor_move, (-1,))
self.keymap.bind(['j', curses.KEY_DOWN, 14], self.cursor_move, (1,))
@@ -496,13 +495,13 @@ class ListWindow(Window):
self.do_search(advance = direction)
def stop_search(self):
- self.last_match = app.input_string
+ self.last_search = app.input_string
app.status(_("ok"), 1)
def do_search(self, ch = None, advance = 0):
if ch in [8, 127]: app.input_string = app.input_string[:-1]
elif ch: app.input_string = "%s%c" % (app.input_string, ch)
- else: app.input_string = app.input_string or self.last_match
+ else: app.input_string = app.input_string or self.last_search
index = self.bufptr + advance
while 1:
if not 0 <= index < len(self.buffer):
@@ -511,7 +510,7 @@ class ListWindow(Window):
break
line = string.lower(str(self.buffer[index]))
if string.find(line, string.lower(app.input_string)) != -1:
- app.status("%s: %s " % (app.input_prompt, app.input_string))
+ app.status("%s%s " % (app.input_prompt, app.input_string))
self.update_line(refresh = 0)
self.bufptr = index
self.update(force = 0)
@@ -531,17 +530,17 @@ class HelpWindow(ListWindow):
self.keymap.bind('q', self.parent.help, ())
self.buffer = string.split(_("""\
- Global Enter : chdir or play
- ------ Space : tag/untag current
- Up, C-p, k, Down, C-n, j, t, T : tag current/regex
- PgUp, K, PgDown, J u, U : untag current/regex
- Home, g, End, G
- : movement Filelist
+ Global Space : tag/untag current
+ ------ t, T : tag current/regex
+ Up/C-p/k, Down/C-n/j, u, U : untag current/regex
+ PgUp/K, PgDn/J, Home/g, End/G i : invert tags
+ : movement
+ Enter : chdir or play Filelist
Tab : filelist/playlist --------
n, p : next/prev track BkSpc : parent dir
z, x : toggle pause/stop o, s : goto, search recursively
Left, Right, a : add (tagged) to playlist
- C-b, C-f : seek backward/forward
+ C-b, C-f : seek backward/forward
c : toggle counter mode Playlist
C-s, C-r, / : isearch --------
C-g, Esc : cancel d, D : delete (tagged) tracks/playlist
@@ -552,12 +551,14 @@ class HelpWindow(ListWindow):
- Undocumented commands
- ---------------------
- X : toggle manual/automatic playlist advancement
- <, > : change number of dirs to hide for pathnames
+ Undocumented
+ ------------
+ b, ' : set/get filelist bookmark
+ X : stop playlist after each track
+ <, > : scroll horizontally (not implemented)
C-a, ^ : seek to beginning of current track (restart)
C-e, $ : seek to end of current track (useless?)
+ ! : shell command ($@ = tagged or current)
"""), "\n")
# ------------------------------------------
@@ -592,13 +593,10 @@ class ListEntry:
return self.filename or self.vp_pathname() # todo
def vp_pathname(self):
- if self.hide_dirs:
- return string.split(self.pathname, "/", self.hide_dirs+1)[-1]
return self.pathname
vps = [[_("filename"), vp_filename],
[_("pathname"), vp_pathname]]
- hide_dirs = 0
# ------------------------------------------
class PlaylistEntry(ListEntry):
@@ -620,24 +618,23 @@ class TagListWindow(ListWindow):
def __init__(self, parent):
ListWindow.__init__(self, parent)
self.keymap.bind(' ', self.command_tag_untag, ())
+ self.keymap.bind('i', self.command_invert_tags, ())
self.keymap.bind('t', self.command_tag, (1,))
self.keymap.bind('u', self.command_tag, (0,))
self.keymap.bind('T', self.command_tag_regexp, (1,))
self.keymap.bind('U', self.command_tag_regexp, (0,))
self.keymap.bind('l', self.command_change_viewpoint, ())
- self.keymap.bind('>', self.command_hide_dirs, (1,))
- self.keymap.bind('<', self.command_hide_dirs, (-1,))
-
- def command_hide_dirs(self, n):
- ListEntry.hide_dirs = max(0, ListEntry.hide_dirs+n)
- app.status(_("Hiding %s dirs") % ListEntry.hide_dirs, 1)
- self.update()
def command_change_viewpoint(self, klass=ListEntry):
klass.vps.append(klass.vps.pop(0))
app.status(_("Listing %s") % klass.vps[0][0], 1)
self.update()
+ def command_invert_tags(self):
+ for i in self.buffer:
+ i.set_tagged(not i.is_tagged())
+ self.update()
+
def command_tag_untag(self):
if not self.buffer: return
tmp = self.buffer[self.bufptr]
@@ -651,7 +648,6 @@ class TagListWindow(ListWindow):
def command_tag_regexp(self, value):
self.tag_value = value
- app.do_input_hook = None
app.stop_input_hook = self.stop_tag_regexp
app.start_input(value and _("Tag regexp") or _("Untag regexp"))
@@ -684,9 +680,92 @@ class FilelistWindow(TagListWindow):
self.keymap.bind('a', self.command_add_recursively, ())
self.keymap.bind('o', self.command_goto, ())
self.keymap.bind('s', self.command_search_recursively, ())
+ self.keymap.bind('b', self.command_set_bookmark, ())
+ self.keymap.bind("'", self.command_get_bookmark, ())
+ self.keymap.bind('!', self.command_shell, ())
+ self.bookmarks = { 39: [self.cwd, 0] }
+
+ def command_shell(self):
+ app.stop_input_hook = self.stop_shell
+ app.complete_input_hook = self.complete_shell
+ app.start_input(_("shell$ "), colon=0)
+
+ def stop_shell(self):
+ s = app.input_string
+ curses.endwin()
+ sys.stderr.write("\n")
+ argv = map(lambda x: x.pathname, self.get_tagged() or [self.current()])
+ argv = ["/bin/sh", "-c", s, "--"] + argv
+ pid = os.fork()
+ if pid == 0:
+ try: os.execv(argv[0], argv)
+ except: os._exit(1)
+ pid, r = os.waitpid(pid, 0)
+ sys.stderr.write("\nshell returned %s, press return!\n" % r)
+ sys.stdin.readline()
+ app.win_root.update()
+ app.restore_default_status()
+
+ def complete_shell(self, line):
+ return self.complete_generic(line, quote=1)
+
+ def complete_generic(self, line, quote=0):
+ import fnmatch
+ if quote:
+ s = re.sub('.*[^\\\\][ \'"()\[\]{}$`]', '', line)
+ s, part = re.sub('\\\\', '', s), line[:len(line)-len(s)]
+ else:
+ s, part = line, ""
+ s = os.path.expanduser(s)
+ head, tail = os.path.split(s)
+ results = fnmatch.filter(os.listdir(head or "."), tail+"*")
+ if len(results) == 0:
+ return line
+ if len(results) == 1:
+ s = os.path.join(head, results[0])
+ s = s + (os.path.isdir(s) and "/" or "")
+ else:
+ lm = results[0]
+ for result in results:
+ for i in range(min(len(result), len(lm))):
+ if result[i] != lm[i]:
+ lm = lm[:i]
+ break
+ s = os.path.join(head, lm)
+ if quote: s = re.sub('([ \'"()\[\]{}$`])', '\\\\\\1', s)
+ return part + s
+
+ def command_get_bookmark(self):
+ app.do_input_hook = self.do_get_bookmark
+ app.start_input(_("bookmark"))
+
+ def do_get_bookmark(self, ch):
+ app.input_string = ch
+ bookmark = self.bookmarks.get(ch)
+ if bookmark:
+ self.bookmarks[39] = [self.cwd, self.bufptr]
+ dir, pos = bookmark
+ self.chdir(dir)
+ self.listdir()
+ self.update()
+ # todo - goto pos in listdir?
+ self.bufptr = pos
+ self.update()
+ app.status(_("ok"), 1)
+ else:
+ app.status(_("Not found!"), 1)
+ app.stop_input()
+
+ def command_set_bookmark(self):
+ app.do_input_hook = self.do_set_bookmark
+ app.start_input(_("set bookmark"))
+
+ def do_set_bookmark(self, ch):
+ app.input_string = ch
+ self.bookmarks[ch] = [self.cwd, self.bufptr]
+ ch and app.status(_("ok"), 1) or app.stop_input()
def command_search_recursively(self):
- app.do_input_hook = None
app.stop_input_hook = self.stop_search_recursively
app.start_input(_("search"))
@@ -753,7 +832,7 @@ class FilelistWindow(TagListWindow):
elif VALID_SONG(filename): files.append(pathname)
elif VALID_PLAYLIST(filename): files.append(pathname)
except os.error: pass
- dots = ListEntry(os.path.join(self.cwd, ".."))
+ dots = ListEntry(os.path.join(self.cwd, ".."), 1)
self.buffer = [[dots], []][self.cwd == "/"]
for i in dirs: self.buffer.append(ListEntry(i, 1))
for i in files: self.buffer.append(ListEntry(i))
@@ -771,6 +850,8 @@ class FilelistWindow(TagListWindow):
def chdir(self, dir):
if hasattr(self, "cwd"): self.oldposition[self.cwd] = self.bufptr
self.cwd = os.path.normpath(dir)
+ try: os.chdir(self.cwd)
+ except: pass
def command_chdir_or_play(self):
if not self.buffer: return
@@ -788,8 +869,8 @@ class FilelistWindow(TagListWindow):
self.listdir(prevdir=dir)
def command_goto(self):
- app.do_input_hook = None
app.stop_input_hook = self.stop_goto
+ app.complete_input_hook = self.complete_generic
app.start_input(_("goto"))
def stop_goto(self):
@@ -823,7 +904,7 @@ class PlaylistWindow(TagListWindow):
self.random_prev = []
self.random_next = []
self.random_unplayed = []
- self.manual = 0
+ self.stop = 0
self.keymap.bind('\n', self.command_play, ())
self.keymap.bind('d', self.command_delete, ())
self.keymap.bind('D', self.command_delete_all, ())
@@ -833,9 +914,13 @@ class PlaylistWindow(TagListWindow):
self.keymap.bind('S', self.command_sort, ())
self.keymap.bind('r', self.command_toggle_repeat, ())
self.keymap.bind('R', self.command_toggle_random, ())
- self.keymap.bind('X', self.command_toggle_manual, ())
+ self.keymap.bind('X', self.command_toggle_stop, ())
self.keymap.bind('w', self.command_save_playlist, ())
self.keymap.bind('@', self.command_jump_to_active, ())
+ self.keymap.bind('f', self.command_filter, ())
+
+ def command_filter(self):
+ None
def command_change_viewpoint(self, klass=PlaylistEntry):
if not globals().get("ID3"):
@@ -851,7 +936,7 @@ class PlaylistWindow(TagListWindow):
self.name = _("Playlist %s %s %s") % (
space_out(self.repeat, _("[repeat]")),
space_out(self.random, _("[random]")),
- space_out(self.manual, _("[manual]")))
+ space_out(self.stop, _("[stop]")))
return ListWindow.get_title(self)
def append(self, item):
@@ -1040,16 +1125,16 @@ class PlaylistWindow(TagListWindow):
app.status(_("Sorted playlist"), 1)
def command_toggle_repeat(self):
- self.toggle("repeat", _("Repeat %s"))
+ self.toggle("repeat", _("Repeat: %s"))
def command_toggle_random(self):
- self.toggle("random", _("Random %s"))
+ self.toggle("random", _("Random: %s"))
self.random_prev = []
self.random_next = []
self.random_unplayed = self.buffer[:]
- def command_toggle_manual(self):
- self.toggle("manual", _("Manual %s"))
+ def command_toggle_stop(self):
+ self.toggle("stop", _("Stop playlist: %s"))
def toggle(self, attr, format):
setattr(self, attr, not getattr(self, attr))
@@ -1058,7 +1143,6 @@ class PlaylistWindow(TagListWindow):
def command_save_playlist(self):
default = self.pathname or "%s/" % app.win_filelist.cwd
- app.do_input_hook = None
app.stop_input_hook = self.stop_save_playlist
app.start_input(_("Save playlist"), default)
@@ -1126,7 +1210,6 @@ class Player:
self.stopped = 0
self.paused = 0
self.time_setup = None
- self.offset = 0
self.buf = None
self.tid = None
@@ -1322,9 +1405,11 @@ class Application:
self.input_string = ""
self.do_input_hook = None
self.stop_input_hook = None
+ self.complete_input_hook = None
self.input_keymap = Keymap()
self.input_keymap.bind(list(Window.chars), self.do_input)
self.input_keymap.bind(curses.KEY_BACKSPACE, self.do_input, (8,))
+ self.input_keymap.bind([21, 23], self.do_input)
self.input_keymap.bind(['\a', 27], self.cancel_input, ())
self.input_keymap.bind('\n', self.stop_input, ())
@@ -1368,6 +1453,7 @@ class Application:
curses.endwin()
XTERM and sys.stderr.write("\033]0;%s\a" % "xterm")
tty and tty.tcsetattr(sys.stdin.fileno(), tty.TCSADRAIN, self.tcattr)
+ print
def run(self):
while 1:
@@ -1378,7 +1464,7 @@ class Application:
timeout = 0.5
if self.kludge and self.player.poll():
self.player.stopped = 1 # end of playlist hack
- if not self.win_playlist.manual:
+ if not self.win_playlist.stop:
pathname = self.win_playlist.next_song()
pathname and self.play(pathname)
R = [sys.stdin, self.player.stdout_r, self.player.stderr_r]
@@ -1424,15 +1510,18 @@ class Application:
self.delayed_play(self.win_playlist.prev_song(), 0)
def seek(self, offset, relative):
+ if not self.player.pathname: return
self.player.seek(offset, relative)
self.delayed_play(self.player.pathname, self.player.offset)
def toggle_pause(self):
+ if not self.player.pathname: return
if not self.player.is_stopped():
if self.player.is_paused(): self.player.unpause()
else: self.player.pause()
def toggle_stop(self):
+ if not self.player.pathname: return
if not self.player.is_stopped(): self.player.stop()
else: self.play(self.player.pathname, self.player.offset)
@@ -1472,14 +1561,14 @@ class Application:
if self.get_volume():
app.status(_("%s volume %s%%") % (name, self.volume), 1)
- def start_input(self, prompt="", data=""):
+ def start_input(self, prompt="", data="", colon=1):
self.input_mode = 1
try: curses.curs_set(1)
except: pass
app.keymapstack.push(self.input_keymap)
- self.input_prompt = prompt
+ self.input_prompt = prompt + (colon and ": " or "")
self.input_string = data
- app.status("%s: %s " % (self.input_prompt, self.input_string))
+ app.status("%s%s " % (self.input_prompt, self.input_string))
def do_input(self, *args):
if self.do_input_hook:
@@ -1487,9 +1576,15 @@ class Application:
ch = args and args[0] or None
if ch in [8, 127]: # backspace
self.input_string = self.input_string[:-1]
+ elif ch == 9 and self.complete_input_hook:
+ self.input_string = self.complete_input_hook(self.input_string)
+ elif ch == 21: # C-u
+ self.input_string = ""
+ elif ch == 23: # C-w
+ self.input_string = re.sub("((.* )?)\w.*", "\\1", self.input_string)
elif ch:
self.input_string = "%s%c" % (self.input_string, ch)
- app.status("%s: %s " % (self.input_prompt, self.input_string))
+ app.status("%s%s " % (self.input_prompt, self.input_string))
def stop_input(self, *args):
self.input_mode = 0
@@ -1498,9 +1593,11 @@ class Application:
app.keymapstack.pop()
if not self.input_string:
app.status(_("cancel"), 1)
- return
- if self.stop_input_hook:
- return apply(self.stop_input_hook, args)
+ elif self.stop_input_hook:
+ apply(self.stop_input_hook, args)
+ self.do_input_hook = None
+ self.stop_input_hook = None
+ self.complete_input_hook = None
def cancel_input(self):
self.input_string = ""

0 comments on commit 77e2263

Please sign in to comment.
Something went wrong with that request. Please try again.