Source

berlios / tll / tll

#!/usr/bin/env python
'''Simple launcher'''

__author__ = "Miki Tebeka <miki.tebeka@gmail.com>"
__version__ = "0.3.0"

import Tkinter as tk
from tkFont import Font
from tkMessageBox import showerror
from threading import Timer

from os.path import exists, expanduser, join, isfile
from os import environ, popen, stat, system
from stat import S_IEXEC
from string import strip

def unique(items):
    seen = set()
    for item in items:
        if item in seen:
            continue
        seen.add(item)
        yield item

def config_file(base_file, env_key):
    filename = ""
    if env_key in environ:
        return environ[env_key]
    elif "HOME" in environ:
        return join(environ["HOME"], base_file)

    return ""

class History:
    histnames = (".tll_history", "TLL_HISTORY")

    def __init__(self, history=None):
        self.history = list(history) if history else []
        self.index = 0

    def with_config_file(self, func, mode):
        histfile = config_file(*self.histnames)
        if (not histfile) or ((mode[0] == "r") and (not isfile(histfile))):
            return

        try:
            fo = open(histfile, mode)
            return func(fo)
        finally:
            fo.close()

    def load(self):
        def _load(fo):
            history = filter(None, map(strip, fo))
            history = list(unique(history))

            self.history = history

        self.with_config_file(_load, "r")

    def save(self):
        def _save(fo):
            for item in unique(self.history):
                print >> fo, item
            fo.close()

        self.with_config_file(_save, "wt")

    def update(self, newline):
        old = filter(lambda line: line != newline, self.history)
        self.history = [newline] + old

    def next(self, step):
        self.index = (self.index + step) % len(self.history)
        return self.history[self.index]

def load_aliases():
    rcfile = config_file(".tllrc", "TLLRC")

    if (not rcfile) or (not isfile(rcfile)):
        return {}

    aliases = {}
    for line in open(rcfile):
        line = line.strip()
        if (not line) or (line[0] == "#"):
            continue
        alias, target = map(strip, line.split("=", 1))

        aliases[alias] = target

    return aliases

def is_executable(path):
    if not isfile(path):
        return 0

    return S_IEXEC & stat(path).st_mode

def launch(name, args, aliases):
    fullname = aliases.get(name, name)

    if " " in fullname:
        fullname, xargs = fullname.split(" ", 1)
        args = "%s %s" % (xargs, args)

    fullname = expanduser(fullname)
    if not exists(fullname):
        fullname = popen("which %s 2>/dev/null" % fullname).read().strip()
        if not fullname:
            raise ValueError("can't find %s" % name)

    if is_executable(fullname):
        system("\"%s\" %s&" % (fullname, args))
    else:
        system("start \"%s\"" % fullname)

class TLL:
    def __init__(self, history):
        self.user_cancel = 0
        self.history = history
        self.root = root = tk.Tk()
        root.title("TLL")
        root.bind("<Escape>", self.quit)
        font = Font(family="Courier", size=40, weight="bold")
        self.command = cmd = tk.Entry(root, width=20, font=font)
        cmd.pack(fill=tk.BOTH)
        cmd.bind("<Return>", lambda e: root.quit())
        cmd.bind("<Up>", lambda e: self.move(-1))
        cmd.bind("<Down>", lambda e: self.move(1))

    def run(self):
        self.command.focus()
        self.root.mainloop()

    def quit(self, event):
        self.user_cancel = 1
        self.root.quit()

    def move(self, step):
        line = self.history.next(step)
        self.command.delete(0, tk.END)
        self.command.insert(0, line)

    def get(self):
        return self.command.get().strip()

def set_user_path():
    if "HOME" not in environ:
        return

    new_path = "%s:%s" % (join(environ["HOME"], "bin"), environ["PATH"])
    environ["PATH"] = new_path

def main(argv=None):
    if argv is None:
        import sys
        argv = sys.argv

    from optparse import OptionParser

    parser = OptionParser(usage="usage: %prog", 
            version="tll %s" % __version__)

    opts, args = parser.parse_args(argv[1:])
    if len(args) != 0:
        parser.error("wrong number of arguments") # Will exit

    set_user_path()

    aliases = load_aliases()
    history = History()
    history.load()

    while 1:
        try:
            history.index = 0
            ui = TLL(history)
            # Quit after 30 seconds
            timer = Timer(30, ui.quit, (None, ))
            timer.start()
            ui.run()
            timer.cancel()

            if ui.user_cancel:
                raise SystemExit

            command = ui.get()
            if not command:
                showerror("TLL Error", "Please enter *something*")
                continue

            history.update(command)
            history.save()

            if " " in command:
                command, args = command.split(" ", 1)
            else:
                args = ""
            launch(command, args, aliases)
            break
        except ValueError:
            showerror("TLL Error", "Can't launch %s" % command)
            raise SystemExit

if __name__ == "__main__":
    main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.