diff --git a/files/foldermenu.py b/files/foldermenu.py index 67a3bae..f394b3a 100755 --- a/files/foldermenu.py +++ b/files/foldermenu.py @@ -44,27 +44,28 @@ def setup_options(): parser.add_argument("--version",action='version', version=VERSION) parser.add_argument("args",type=str,action="store",default="",nargs="?", help="Arguments for the command, if -c used. The string will be re-parsed with shutils. Use '--' to skip local parsing.") - options=parser.parse_args() + + options = parser.parse_args() if not os.path.exists(DEFAULTFILE): - options.defaults=False + options.defaults = False return options def termsize(): rows, columns = os.popen('stty size', 'r').read().split() - return (int(rows),int(columns)) + return (int(rows), int(columns)) def ichr(i): - ''' convert integer to 1-9, a-z, A-Z, omitting q,x ''' + ''' convert integer to 1-9, a-z, A-Z, omitting q,x ''' if i < 10: return str(i) - i=i + 87 - if i>112: - i=i+1 - if i>119: - i=i+1 - if i>122: - i=i-122+64 + i += 87 + if i > 112: + i += 1 + if i > 119: + i += 1 + if i > 122: + i += 64 - 122 return chr(i) class bc: @@ -87,19 +88,20 @@ class bc: self.CYA = '' self.WHI = '' self.END = '' - - def pos(self,y,x): - return "\033["+str(y)+";"+str(x)+"H" - def posprint(self, y,x,s): - sys.stdout.write( self.pos(y,x) + str(s) ) + def pos(self,y,x): + return "\033[%s;%sH"%( y, x ) + + def posprint(self, y, x, s): + sys.stdout.write( self.pos(y, x) + str(s) ) + class getch: def __init__(self): import sys, tty, termios - + def get(self): - + fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: @@ -109,283 +111,351 @@ class getch: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch + class launch_item: ''' Class for launchable items ''' - description='' - command='' - launcher='' - def __init__(self,command,description,launcher): - self.command=command - self.description=description - self.launcher=launcher - + def __init__(self, command, description, launcher): + self.command = command + self.description = description + self.launcher = launcher + class entry_collection: ''' Object containing the list items, and the printing methods ''' - options=[] - entries=[] - dirs=[] - banner=[] - menu_keys=[ichr(i+1) for i in range(60)] - args='' - dir_mode=False - co=bc() - max_length=0 - def __init__(self, options): - self.options=options + self.options = options + self.menu_keys=[ichr(i+1) for i in range(60)] + self.args = '' + self.co = bc() + self.dir_mode = False + self.max_length = 0 self.initialize() if not self.options.colors: self.co.disable() def initialize(self): - self.entries=[] - self.dirs=[] - self.banner=[] + self.entries = [] + self.dirs = [] + self.banner = [] if self.options.defaults: self.read_menu(DEFAULTFILE) self.read_menu() self.read_folder() - self.entries=self.entries[0:60] - self.dirs=self.dirs[0:60] - if len(self.entries)>0: - self.max_length=max([len(e.description) for e in self.entries])+1 + self.entries = self.entries[0:60] + self.dirs = self.dirs[0:60] + if len(self.entries) > 0: + self.max_length = max([len(e.description) for e in self.entries]) + 1 - def set_args(self,args): - self.args=args; - - def read_menu(self,menu_file=MENUFILE): + def set_args(self, args): + self.args = args + + def read_menu(self, menu_file = MENUFILE): ''' Read the menu file ''' if os.path.exists(menu_file): - f=file(menu_file,'r') - for row in f: - if row.strip()=='': - continue - if row[0:2]=='##': - self.banner.append(row[2:].replace("\n","").replace("\r","")) - continue - if row[0]=='#': - continue - row=row.strip().split(':',1) - if len(row)==1: - row=["$"+row[0].strip(), row[0]] - else: - row=[row[0].strip(),row[1]] - self.entries.append(launch_item(command=row[1], - description=row[0], - launcher="menu")) + with open(menu_file, 'rt') as f: + for row in f: + if row.strip() == '': + continue + if row[0:2] == '##': + self.banner.append( + row[2:].replace("\n","").replace("\r","") + ) + continue + if row[0] == '#': + continue + row = row.strip().split(':',1) + if len(row) == 1: + row = [ "$" + row[0].strip(), row[0] ] + else: + row = [ row[0].strip(), row[1] ] + self.entries.append( + launch_item( + command = row[1], + description = row[0], + launcher = "menu" + ) + ) + def read_folder(self): ''' Read folder contents, return executable files and dirs ''' - - self.dirs.append(launch_item(command='..', - description='..', - launcher="dir")) - dirs=[] - executables=[] + + self.dirs.append( + launch_item( + command = '..', + description = '..', + launcher = "dir" + ) + ) + dirs = [] + executables = [] for f in os.listdir('.'): if os.path.isfile(f): - if os.access(f,os.X_OK): + if os.access(f, os.X_OK): executables.append(f) - if os.path.isdir(f) and f[0]!='.': + if os.path.isdir(f) and f[0] != '.': dirs.append(f) dirs.sort() for d in dirs: - self.dirs.append(launch_item( - command=d, - description=d, - launcher="dir")) + self.dirs.append( + launch_item( + command = d, + description = d, + launcher = "dir" + ) + ) if self.options.executables: executables.sort() for e in executables: - self.entries.append(launch_item( - command=e, - description=e, - launcher="exec")) + self.entries.append( + launch_item( + command = e, + description = e, + launcher = "exec" + ) + ) - def entry_color(self,launcher): - if launcher=="dir": + def entry_color(self, launcher): + if launcher == "dir": return self.co.WHI - if launcher=="exec": + if launcher == "exec": return self.co.GRE - if launcher=="menu": + if launcher == "menu": return self.co.CYA def menu(self): """ draws the menu at the top of the screen """ if self.dir_mode: - helptext=".:executables" - my_entries=self.dirs + helptext = "[.]executables" + my_entries = self.dirs else: - helptext='-:args ('+self.co.END+self.args+self.co.YEL+') .:folders' - my_entries=self.entries + helptext = '[.]folders [-]args (%s%s%s)'%( + self.co.END, + self.args, + self.co.YEL + ) + my_entries = self.entries - maxrows,maxcolumns = termsize() - rows=maxrows - 5 - maxcolumns-=10 - if self.options.columns==0: - pars=1 - if len(my_entries)>9: - pars=2 - if len(my_entries)>30: - pars=3 - pars=float(pars) + maxrows, maxcolumns = termsize() + rows = maxrows - 5 + maxcolumns -= 10 + if self.options.columns == 0: + pars = 1 + if len(my_entries) > 9: + pars = 2 + if len(my_entries) > 30: + pars = 3 + pars = float(pars) else: - pars=float(self.options.columns) + pars = float(self.options.columns) if self.options.banner: - b=self.banner + banner = self.banner else: - b=[] - blen=len(b) - cwd=os.path.basename(os.getcwd())[0:15] - self.co.posprint(1,3,self.co.END+self.co.CLR+self.co.WHI+cwd+self.co.YEL+' Menu q/x:exit '+helptext+self.co.END) - for i,e in enumerate(b): - self.co.posprint(i+2,0,e) - rows=int(math.ceil(len(my_entries)/pars)) + banner = [] + blen = len(banner) + cwd = os.path.basename( os.getcwd() )[0:15] + + self.co.posprint( + 1, + 1, + self.co.END + self.co.CLR + ) + self.co.posprint( + 1, + 3, + "%s%s%s [q/x]exit %s%s"%( + self.co.WHI, + cwd, + self.co.YEL, + helptext, + self.co.END + ) + ) + for i, e in enumerate(banner): + self.co.posprint( + i+2, + 0, + e + ) + rows = int( math.ceil( len(my_entries) / pars ) ) while rows > maxrows: - pars+=1 - rows=int(math.ceil(len(my_entries)/pars)) - maxcolumns=int(math.ceil(maxcolumns/pars)) - r=1+blen - par=1 - for e,i in zip(my_entries,self.menu_keys): - if r-blen>rows: - par+=1 - r=1+blen - printline=e.description - if len(printline)>maxcolumns: - printline=printline[:maxcolumns]+"..." - if par==1: - self.co.posprint(r+1,2,self.co.WHI+i+self.co.END+' '+self.entry_color(e.launcher)+printline+self.co.END) + pars += 1 + rows = int( math.ceil( len(my_entries) / pars ) ) + maxcolumns = int(math.ceil(maxcolumns / pars)) + r = 1 + blen + par = 1 + for entry, key in zip(my_entries, self.menu_keys): + if r - blen > rows: + par += 1 + r = 1 + blen + printline = entry.description + if len(printline) > maxcolumns: + printline = printline[:maxcolumns] + "..." + if par == 1: + column = 2 + border = "" else: - self.co.posprint(r+1,maxcolumns*(par-1),'| '+self.co.WHI+i+self.co.END+' '+self.entry_color(e.launcher)+printline+self.co.END) - r+=1 - self.co.posprint(rows+2+blen,0,"") + column = maxcolumns * ( par - 1 ) + border = '| ' + self.co.posprint( + r+1, + column, + "%s%s%s%s %s%s%s"%( + border, + self.co.WHI, + key, + self.co.END, + self.entry_color(entry.launcher), + printline, + self.co.END + ) + ) + r += 1 + self.co.posprint( + rows + 2 + blen, + 0, + "#" + ) def list(self): """ draws the list at cursor """ - maxrows,maxcolumns = termsize() - rows=maxrows-5 - maxcolumns-=10 + maxrows, maxcolumns = termsize() + rows = maxrows - 5 + maxcolumns -= 10 # heuristics for guessing column count - if self.options.columns==0: - pars=float(1) - if len(self.entries)>9: - pars=math.floor(maxcolumns/float(self.max_length)) - pars=max(1,pars) - if len(self.entries)/pars < pars: - while len(self.entries)/pars < pars: - pars-=1 + if self.options.columns == 0: + pars = 1.0 + if len(self.entries) > 9: + pars = max( + 1.0, + math.floor( + maxcolumns / float(self.max_length) + ) + ) + while len(self.entries) / pars < pars: + pars -= 1 else: - pars=float(self.options.columns) - rows=int(math.ceil(len(self.entries)/float(pars))) - maxcolumns=int(math.floor(maxcolumns/pars))-2 - # If names won't fit the columns, make sure at least 3 characters are visible - if maxcolumns<6: - origmaxrows,origmaxcolumns = termsize() - origmaxrows-=5 - origmaxcolumns-=10 - while maxcolumns<6: - pars=pars-1 - rows=int(math.ceil(len(self.entries)/float(pars))) - maxcolumns=int(math.floor(origmaxcolumns/pars))-2 + pars = float(self.options.columns) - self.max_length=min(maxcolumns,self.max_length) + rows = int(math.ceil( len(self.entries) / float(pars) )) + maxcolumns = int(math.floor(maxcolumns / pars)) - 2 + # If names won't fit the columns, make sure at least 3 characters are visible + if maxcolumns < 6: + origmaxrows, origmaxcolumns = termsize() + origmaxrows -= 5 + origmaxcolumns -= 10 + while maxcolumns < 6: + pars -= 1 + rows = int(math.ceil( len(self.entries) / float(pars) )) + maxcolumns = int(math.floor( origmaxcolumns / pars )) - 2 + + self.max_length = min(maxcolumns, self.max_length) if self.options.horizontal: - foo=pars - pars=rows - rows=foo - formatted=[] + pars, rows = rows, pars + formatted = [] for r in range(int(rows)): formatted.append([]) for p in range(int(pars)): formatted[r].append(' '*(self.max_length)) if self.options.horizontal: - formatted[r][p]+=' ' - r=0 - par=0 - for e,i in zip(self.entries,self.menu_keys): - if r>=rows: - par=1+par - r=0 - printline=e.description[:(maxcolumns-3)] - printline=printline+' '*(self.max_length-len(printline)-1) - formatted[r][par]=self.co.WHI+i+self.co.END+' '+self.entry_color(e.launcher)+printline+self.co.END - r=1+r + formatted[r][p] += ' ' + r = 0 + par = 0 + for entry,key in zip(self.entries, self.menu_keys): + if r >= rows: + par += 1 + r = 0 + printline = entry.description[:(maxcolumns-3)] + printline += ' '*(self.max_length-len(printline)-1) + formatted[r][par]="%s%s%s %s%s%s"%( + self.co.WHI, + key, + self.co.END, + self.entry_color(entry.launcher), + printline, + self.co.END + ) + r += 1 if self.options.horizontal: # let's shuffle the deck, and print values in horizontal order: - formatted=zip(*formatted) + formatted = zip(*formatted) if self.options.banner: if self.banner: print("\n".join(self.banner)) for row in formatted: print '|'.join(row) - + def launch(self,key): ''' launch the given entry ''' - bg=False - wait=True - #Run the program in background - idx=self.menu_keys.index(key) + bg = False + idx = self.menu_keys.index(key) # note, no error checking here if self.dir_mode: - command_str=self.dirs[idx].command + command_str = self.dirs[idx].command os.chdir(command_str) return # continue here if not changing folders - command_str=self.entries[idx].command + command_str = self.entries[idx].command - if command_str[-1]=='&': - command_str=command_str[:-1] - bg=True - if len(self.args)>0: - command_str=command_str+" "+self.args - if self.entries[idx].launcher=='exec': - command_str='./'+command_str + if command_str[-1] == '&': + #Run the program in background + command_str = command_str[:-1] + bg = True + if len(self.args) > 0: + command_str += ' ' + self.args + if self.entries[idx].launcher == 'exec': + command_str = './' + command_str if not self.options.command: - print('#$ '+command_str) + print(command_str) try: if bg: - subprocess.Popen(command_str, stderr=subprocess.PIPE, shell=True,executable="/bin/bash") + subprocess.Popen( + command_str, + stderr = subprocess.PIPE, + shell=True, + executable="/bin/bash" + ) else: - subprocess.call(command_str, stderr=subprocess.STDOUT, shell=True,executable="/bin/bash") + subprocess.call( + command_str, + stderr = subprocess.STDOUT, + shell = True, + executable = "/bin/bash" + ) except: - print('Non-zero exit code: "'+command_str+'"') - - if (self.options.command or self.options.once or bg): - wait=False - - if wait: + print('Non-zero exit code: "' + command_str + '"') + + if not (self.options.command or self.options.once or bg): print('Press any key...') ch=getch() inkey=ord(ch.get()) def flip_mode(self): - self.dir_mode=not self.dir_mode - - def is_key(self,key): + self.dir_mode = not self.dir_mode + + def is_key(self, key): if self.dir_mode: - my_len=len(self.dirs) + my_len = len(self.dirs) else: - my_len=len(self.entries) + my_len = len(self.entries) try: - idx=self.menu_keys.index(key) + idx = self.menu_keys.index(key) except ValueError: - return (False,'Not a possible key') - if idx+1>my_len: - return (False,'No such entry') - return (True,'') + return (False, 'Not a possible key') + if idx + 1 > my_len: + return (False, 'No such entry') + return (True, '') def start_engines(): - options=setup_options() - entries=entry_collection(options) + options = setup_options() + entries = entry_collection(options) if options.list: entries.list() if not options.command: sys.exit(0) if options.command: - found,message=entries.is_key(options.command) + found, message = entries.is_key(options.command) if not found: print(message) sys.exit(1) @@ -393,28 +463,30 @@ def start_engines(): entries.launch(options.command) sys.exit(0) - ch=getch() + ch = getch() while True: entries.menu() - inkey=ord(ch.get()) + inkey = ord(ch.get()) #print('-'+str((inkey))+'-') - if inkey in [113,120,27,3,24,4]: - print('Exited in: '+os.getcwd()) + if inkey in (113, 120, 27, 3, 24, 4): # esc, q, x + print('Exited in: ' + os.getcwd()) sys.exit(0) - if inkey==45: # - + if inkey == 45: # - + print('') readline.set_startup_hook(lambda: readline.insert_text(entries.args)) - args=raw_input('args: ') + args = raw_input('args: ') entries.set_args(args) readline.set_startup_hook(None) - if inkey==46: # . + if inkey == 46: # . entries.flip_mode() - found,message=entries.is_key(chr(inkey)) + found, message = entries.is_key(chr(inkey)) if found: entries.launch(chr(inkey)) if options.once and not entries.dir_mode: sys.exit(0) entries.initialize() -start_engines() +if __name__ == "__main__": + start_engines()