Ejemplo n.º 1
0
class Tasky(object):

    palette = [
        ('proj', '', '', '', 'dark green', ''),
        ('proj_focus', '', '', '', 'dark gray', 'dark green'),
        ('body','', '', '', 'dark blue', ''),
        ('body_focus', '', '', '', 'dark gray', 'dark cyan'),
        ('body_emph','', '', '', 'light red', ''),
        ('body_emph_focus', '', '', '', 'dark gray', 'dark magenta'),
        ('head', '', '', '',  'light red', 'black'),
        ('dim', '', '', '', 'g54', 'black')
    ]

    def __init__(self):

        self.warrior = TaskWarrior()

        self.limit = ''.join(sys.argv[1:])
        self.nvim = None
        self.note_folder = "~/.tasknote/"

        header = urwid.AttrMap(urwid.Text('tasky.α'), 'head')
        self.walker = urwid.SimpleListWalker([])
        self.list_box = ScrollingListBox(self.walker)
        self.view = urwid.Frame(urwid.AttrWrap(self.list_box, 'body'))
        self.required_status = ['pending']
        self.refresh()

        def update_header():
            limit = ' | ' + self.limit if self.limit else ''
            header_text = ['tasky.α', ('dim', limit)]
            self.view.set_header(urwid.AttrMap(urwid.Text(header_text), 'head'))

        update_header()

        loop = urwid.MainLoop(self.view, Tasky.palette, unhandled_input=self.keystroke)
        urwid.connect_signal(self.walker, 'modified', update_header)
        loop.screen.set_terminal_properties(colors=256)
        loop.run()

    def refresh(self):
        limit = self.limit or ''
        self.walker[:] = map(TaskWidget, self.warrior.retrieve_tasks(limit, status=self.required_status))


    def attach_nvim(self):
        Utility.run_command("tmux split-window -h 'NVIM_LISTEN_ADDRESS=/tmp/nvim nvim'")

        while True:
            try:
                time.sleep(1)
                nvim = attach('socket', path='/tmp/nvim')
            except:
                continue
            break

        self.nvim = nvim



    def keystroke(self, input):
        def exit():

            if self.nvim:
                self.nvim.quit("wq")

            raise urwid.ExitMainLoop()

        def undo():
            self.warrior.undo()
            self.refresh()

        view_action_map = {
            'q': exit,
            'Q': exit,
            'r': self.refresh,
            'u': undo,
            'i': self.new_task,
            ':': self.command_mode,
            '!': self.shell_mode,
            'l': self.change_limit,
            'A': self.show_all,
        }

        task_action_map = {
            'enter': (self.edit_task, False),
            'e': (self.edit_task, False),
            'n': (self.task_note, False),
            'c': (self.warrior.complete, True),
            'd': (self.warrior.delete, True),
            ' ': (self.warrior.toggle_active, True)
        }

        if input in view_action_map:
            view_action_map[input]()

        if input in task_action_map:
            (action, should_refresh) = task_action_map[input]
            action(self.selected_task())
            if should_refresh:
                self.refresh()

    def selected_task(self):
        return self.list_box.get_focus()[0].task


    def task_note(self, task):
        if not self.nvim:
            self.attach_nvim()

        if not os.path.isdir(expanduser(self.note_folder)):
            os.mkdir(expanduser(self.note_folder))

        note_file = expanduser(self.note_folder + task.uuid() + ".markdown")


        if not os.path.isfile(note_file):
            with open(note_file, 'w+') as fh:
                header = "# " + task.description()
                fh.write(header)
            
            # Annotate the task to show that it has notes now.
            Utility.run_command("task %i annotate Notes" % task.id())

        # either capture error or write note before changing buffer
        try:
            self.nvim.command("e " + note_file)
        except:
            self.nvim.command("w")
            self.nvim.command("e " + note_file)



    def present_editor(self, prompt, text, handler):
        self.foot = LineEditor(prompt, text)
        self.view.set_footer(self.foot)
        self.view.set_focus('footer')
        urwid.connect_signal(self.foot, 'done', handler)

    def command_mode(self):
        self.present_editor(': ', '', self.command_done)

    def shell_mode(self):
        self.present_editor('! ', '', self.shell_done)

    def change_limit(self):
        limit = self.limit or ''
        self.present_editor('Limit: ', limit, self.limit_done)

    def edit_task(self, task):
        self.edited_task = task
        self.present_editor(' >> ', task.description(), self.edit_done)

    def new_task(self):
        self.present_editor(' >> ', '', self.new_done)


    def dismiss_editor(action):
        def wrapped(self, content):
            self.view.set_focus('body')
            urwid.disconnect_signal(self, self.foot, 'done', action)
            if content is not None:
                action(self, content)
            self.view.set_footer(None)
        return wrapped

    @dismiss_editor
    def edit_done(self, content):
        self.warrior.mod(self.edited_task, content)
        self.edited_task = None
        self.refresh()

    @dismiss_editor
    def new_done(self, content):
        limit = self.limit or ''
        self.warrior.add(content + ' ' + limit)
        self.refresh()

    @dismiss_editor
    def command_done(self, content):
        Utility.run_command('task ' + content)
        self.refresh()

    @dismiss_editor
    def shell_done(self, content):
        Utility.run_command("tmux split-window '%s'" % content)

    @dismiss_editor
    def limit_done(self, content):
        self.limit = content
        self.refresh()

    def show_all(self):
        if 'completed' not in self.required_status:
            self.required_status += ['completed']
            self.refresh()
        else:
            self.required_status = ['pending']
            self.refresh()
Ejemplo n.º 2
0
Archivo: tasky.py Proyecto: s992/Tasky
class Tasky(object):
    palette = [('proj', '', '', '', 'dark green', ''),
               ('proj_focus', '', '', '', 'black', 'dark green'),
               ('body', '', '', '', 'dark blue', ''),
               ('body_focus', '', '', '', 'black', 'dark cyan'),
               ('body_emph', '', '', '', 'light red', ''),
               ('body_emph_focus', '', '', '', 'black', 'dark magenta'),
               ('head', '', '', '', 'light red', 'black'),
               ('dim', '', '', '', 'g54', 'black')]

    def __init__(self):

        self.warrior = TaskWarrior()
        self.config = Config()

        self.default_limit = self.config.get_default_filter()
        self.limit = self.default_limit
        self.show_annotations = []

        self.walker = urwid.SimpleListWalker([])
        self.list_box = ScrollingListBox(self.walker)
        self.view = urwid.Frame(urwid.AttrWrap(self.list_box, 'body'))
        self.refresh()

        def update_header():
            limit = ' | ' + self.limit if self.limit else ''
            header_text = ['tasky.α', ('dim', limit)]
            self.view.set_header(urwid.AttrMap(urwid.Text(header_text),
                                               'head'))

        update_header()

        loop = urwid.MainLoop(self.view,
                              Tasky.palette,
                              unhandled_input=self.keystroke)
        self.loop = loop
        urwid.connect_signal(self.walker, 'modified', update_header)
        loop.screen.set_terminal_properties(colors=256)
        loop.run()

    def refresh(self):
        limit = self.limit or ''
        self.walker[:] = [
            TaskWidget(task,
                       task.uuid() in self.show_annotations)
            for task in self.warrior.pending_tasks(limit)
        ]

    def sync(self):
        self.warrior.sync()
        self.refresh()

    def keystroke(self, input):
        def exit():
            raise urwid.ExitMainLoop()

        def undo():
            self.warrior.undo()
            self.refresh()

        view_action_map = {
            'q': exit,
            'Q': exit,
            'r': self.sync,
            'u': undo,
            'i': self.new_inbox,
            't': self.new_task,
            ':': self.command_mode,
            '!': self.shell_mode,
            '/': self.change_limit,
            '-': self.all,
            'b': self.clear_limit,
        }

        task_action_map = {
            'enter': (self.edit_task, False),
            'e': (self.edit_task_detail, True),
            'n': (self.task_annotate, True),
            'N': (self.task_note, True),
            'c': (self.warrior.complete, True),
            'd': (self.warrior.delete, True),
            ' ': (self.warrior.toggle_active, True),
            'y': (self.copy_description, False),
            'Y': (self.copy_notes, False),
            'o': (self.open_browser, False),
            'a': (self.toggle_annotations, True),
            'A': (self.toggle_all_annotations, True),
            'T': (self.new_task_with_defaults, True),
            's': (self.sleep_task, True),
        }

        config_bind = self.config.get_bind(input)

        if config_bind and len(config_bind):
            self.limit = config_bind
            self.refresh()
            return

        if input in view_action_map:
            view_action_map[input]()
            return

        if input in task_action_map:
            (action, should_refresh) = task_action_map[input]
            action(self.selected_task())
            if should_refresh:
                self.refresh()

    def selected_task(self):
        return self.list_box.get_focus()[0].task

    def task_annotate(self, task):
        self.edited_task = task
        self.present_editor(' >> ', '', self.annotate_done)

    def task_note(self, task):
        self.loop.stop()
        subprocess.check_call(['tasknote %s' % str(task.id())], shell=True)
        self.loop.start()

    def present_editor(self, prompt, text, handler):
        self.foot = LineEditor(prompt, text)
        self.view.set_footer(self.foot)
        self.view.set_focus('footer')
        urwid.connect_signal(self.foot, 'done', handler)

    def command_mode(self):
        self.present_editor(': ', '', self.command_done)

    def shell_mode(self):
        self.present_editor('! ', '', self.shell_done)

    def change_limit(self):
        self.present_editor('Filter: ', '', self.limit_done)

    def all(self):
        self.limit = ''
        self.refresh()

    def clear_limit(self):
        self.limit = self.default_limit
        self.refresh()

    def copy_description(self, task):
        Utility.write_to_clipboard(task.description())

    def copy_notes(self, task):
        notes = [note[u'description'] for note in task.annotations()]
        Utility.write_to_clipboard("\n".join(notes))

    def open_browser(self, task):
        urls = []

        if Utility.is_url(task.description()):
            urls.append(task.description())

        possibles = [
            v[u'description'] for v in task.annotations()
            if Utility.is_url(v[u'description'])
        ]
        urls.extend(possibles)

        for url in urls:
            Utility.run_command('open -a "Google Chrome" %s' % url)

    def toggle_annotations(self, task):
        if not len(task.annotations()):
            return

        if task.uuid() in self.show_annotations:
            self.show_annotations.remove(task.uuid())
        else:
            self.show_annotations.append(task.uuid())

    def toggle_all_annotations(self, task):
        if len(self.show_annotations):
            self.show_annotations = []
        else:
            limit = self.limit or ''
            self.show_annotations = [
                t.uuid() for t in self.warrior.pending_tasks(limit)
            ]

    def edit_task(self, task):
        self.edited_task = task
        self.present_editor(' >> ', task.description(), self.edit_done)

    def sleep_task(self, task):
        self.edited_task = task
        self.present_editor(' >> ', 'wait:', self.edit_done)

    def edit_task_detail(self, task):
        self.loop.stop()
        subprocess.check_call(['task %s edit' % str(task.id())], shell=True)
        self.loop.start()

    def new_task(self, prefilled=''):
        self.present_editor(' >> ', prefilled, self.new_done)

    def new_inbox(self):
        self.new_task('+in ')

    def new_task_with_defaults(self, task):
        project = task.project()
        tags = task.tags_string()
        prefilled = ''

        if project:
            prefilled += 'pro:{} '.format(project)

        prefilled += '{} '.format(tags)

        self.new_task(prefilled)

    def dismiss_editor(action):
        def wrapped(self, content):
            self.view.set_focus('body')
            urwid.disconnect_signal(self, self.foot, 'done', action)
            if content is not None:
                action(self, content)
            self.view.set_footer(None)

        return wrapped

    @dismiss_editor
    def edit_done(self, content):
        self.warrior.mod(self.edited_task, content)
        self.edited_task = None
        self.refresh()

    @dismiss_editor
    def new_done(self, content):
        self.warrior.add(content)
        self.refresh()

    @dismiss_editor
    def command_done(self, content):
        Utility.run_command('task ' + content)
        self.refresh()

    @dismiss_editor
    def shell_done(self, content):
        self.loop.stop()
        subprocess.check_call(['/bin/zsh', '-i', '-c', content])
        self.loop.start()

    @dismiss_editor
    def limit_done(self, content):
        self.limit = content
        self.refresh()

    @dismiss_editor
    def annotate_done(self, content):
        self.warrior.annotate(self.edited_task, content)
        self.edited_task = None
        self.refresh()
Ejemplo n.º 3
0
class Tasky(object):

    palette = [('proj', '', '', '', 'dark green', ''),
               ('proj_focus', '', '', '', 'dark gray', 'dark green'),
               ('body', '', '', '', 'dark blue', ''),
               ('body_focus', '', '', '', 'dark gray', 'dark cyan'),
               ('body_emph', '', '', '', 'light red', ''),
               ('body_emph_focus', '', '', '', 'dark gray', 'dark magenta'),
               ('head', '', '', '', 'light red', 'black'),
               ('dim', '', '', '', 'g54', 'black')]

    def __init__(self):

        self.warrior = TaskWarrior()

        self.limit = ''.join(sys.argv[1:])

        header = urwid.AttrMap(urwid.Text('tasky.α'), 'head')
        self.walker = urwid.SimpleListWalker([])
        self.list_box = ScrollingListBox(self.walker)
        self.view = urwid.Frame(urwid.AttrWrap(self.list_box, 'body'))
        self.refresh()

        def update_header():
            limit = ' | ' + self.limit if self.limit else ''
            header_text = ['tasky.α', ('dim', limit)]
            self.view.set_header(urwid.AttrMap(urwid.Text(header_text),
                                               'head'))

        update_header()

        loop = urwid.MainLoop(self.view,
                              Tasky.palette,
                              unhandled_input=self.keystroke)
        urwid.connect_signal(self.walker, 'modified', update_header)
        loop.screen.set_terminal_properties(colors=256)
        loop.run()

    def refresh(self):
        limit = self.limit or ''
        self.walker[:] = map(TaskWidget, self.warrior.pending_tasks(limit))

    def keystroke(self, input):
        def exit():
            raise urwid.ExitMainLoop()

        def undo():
            self.warrior.undo()
            self.refresh()

        view_action_map = {
            'q': exit,
            'Q': exit,
            'r': self.refresh,
            'u': undo,
            'i': self.new_task,
            ':': self.command_mode,
            '!': self.shell_mode,
            'l': self.change_limit
        }

        task_action_map = {
            'enter': (self.edit_task, False),
            'e': (self.edit_task, False),
            'n': (self.task_note, False),
            'c': (self.warrior.complete, True),
            'd': (self.warrior.delete, True),
            ' ': (self.warrior.toggle_active, True)
        }

        if input in view_action_map:
            view_action_map[input]()

        if input in task_action_map:
            (action, should_refresh) = task_action_map[input]
            action(self.selected_task())
            if should_refresh:
                self.refresh()

    def selected_task(self):
        return self.list_box.get_focus()[0].task

    def task_note(self, task):
        Utility.run_command("tmux split-window 'tasknote %i'" % task.id())

    def present_editor(self, prompt, text, handler):
        self.foot = LineEditor(prompt, text)
        self.view.set_footer(self.foot)
        self.view.set_focus('footer')
        urwid.connect_signal(self.foot, 'done', handler)

    def command_mode(self):
        self.present_editor(': ', '', self.command_done)

    def shell_mode(self):
        self.present_editor('! ', '', self.shell_done)

    def change_limit(self):
        limit = self.limit or ''
        self.present_editor('Limit: ', limit, self.limit_done)

    def edit_task(self, task):
        self.edited_task = task
        self.present_editor(' >> ', task.description(), self.edit_done)

    def new_task(self):
        self.present_editor(' >> ', '', self.new_done)

    def dismiss_editor(action):
        def wrapped(self, content):
            self.view.set_focus('body')
            urwid.disconnect_signal(self, self.foot, 'done', action)
            if content is not None:
                action(self, content)
            self.view.set_footer(None)

        return wrapped

    @dismiss_editor
    def edit_done(self, content):
        self.warrior.mod(self.edited_task, content)
        self.edited_task = None
        self.refresh()

    @dismiss_editor
    def new_done(self, content):
        limit = self.limit or ''
        self.warrior.add(content + ' ' + limit)
        self.refresh()

    @dismiss_editor
    def command_done(self, content):
        Utility.run_command('task ' + content)
        self.refresh()

    @dismiss_editor
    def shell_done(self, content):
        Utility.run_command("tmux split-window '%s'" % content)

    @dismiss_editor
    def limit_done(self, content):
        self.limit = content
        self.refresh()
Ejemplo n.º 4
0
class Tasky(object):

    palette = [
        ('proj', '', '', '', 'dark green', ''),
        ('proj_focus', '', '', '', 'dark gray', 'dark green'),
        ('body','', '', '', 'dark blue', ''),
        ('body_focus', '', '', '', 'dark gray', 'dark cyan'),
        ('body_emph','', '', '', 'light red', ''),
        ('body_emph_focus', '', '', '', 'dark gray', 'dark magenta'),
        ('head', '', '', '',  'light red', 'black'),
        ('dim', '', '', '', 'g54', 'black')
    ]

    def __init__(self):

        self.warrior = TaskWarrior()

        self.limit = ''.join(sys.argv[1:])

        header = urwid.AttrMap(urwid.Text('tasky.α'), 'head')
        self.walker = urwid.SimpleListWalker([])
        self.list_box = ScrollingListBox(self.walker)
        self.view = urwid.Frame(urwid.AttrWrap(self.list_box, 'body'))
        self.refresh()

        def update_header():
            limit = ' | ' + self.limit if self.limit else ''
            header_text = ['tasky.α', ('dim', limit)]
            self.view.set_header(urwid.AttrMap(urwid.Text(header_text), 'head'))

        update_header()

        loop = urwid.MainLoop(self.view, Tasky.palette, unhandled_input=self.keystroke)
        urwid.connect_signal(self.walker, 'modified', update_header)
        loop.screen.set_terminal_properties(colors=256)
        loop.run()

    def refresh(self):
        limit = self.limit or ''
        self.walker[:] = map(TaskWidget, self.warrior.pending_tasks(limit))

    def keystroke(self, input):
        def exit():
            raise urwid.ExitMainLoop()

        def undo():
            self.warrior.undo()
            self.refresh()

        view_action_map = {
            'q': exit,
            'Q': exit,
            'r': self.refresh,
            'u': undo,
            'i': self.new_task,
            ':': self.command_mode,
            '!': self.shell_mode,
            'l': self.change_limit
        }

        task_action_map = {
            'enter': (self.edit_task, False),
            'e': (self.edit_task, False),
            'n': (self.task_note, False),
            'c': (self.warrior.complete, True),
            'd': (self.warrior.delete, True),
            ' ': (self.warrior.toggle_active, True)
        }

        if input in view_action_map:
            view_action_map[input]()

        if input in task_action_map:
            (action, should_refresh) = task_action_map[input]
            action(self.selected_task())
            if should_refresh:
                self.refresh()

    def selected_task(self):
        return self.list_box.get_focus()[0].task


    def task_note(self, task):
        Utility.run_command("tmux split-window 'tasknote %i'" % task.id())

    def present_editor(self, prompt, text, handler):
        self.foot = LineEditor(prompt, text)
        self.view.set_footer(self.foot)
        self.view.set_focus('footer')
        urwid.connect_signal(self.foot, 'done', handler)

    def command_mode(self):
        self.present_editor(': ', '', self.command_done)

    def shell_mode(self):
        self.present_editor('! ', '', self.shell_done)

    def change_limit(self):
        limit = self.limit or ''
        self.present_editor('Limit: ', limit, self.limit_done)

    def edit_task(self, task):
        self.edited_task = task
        self.present_editor(' >> ', task.description(), self.edit_done)

    def new_task(self):
        self.present_editor(' >> ', '', self.new_done)


    def dismiss_editor(action):
        def wrapped(self, content):
            self.view.set_focus('body')
            urwid.disconnect_signal(self, self.foot, 'done', action)
            if content is not None:
                action(self, content)
            self.view.set_footer(None)
        return wrapped

    @dismiss_editor
    def edit_done(self, content):
        self.warrior.mod(self.edited_task, content)
        self.edited_task = None
        self.refresh()

    @dismiss_editor
    def new_done(self, content):
        limit = self.limit or ''
        self.warrior.add(content + ' ' + limit)
        self.refresh()

    @dismiss_editor
    def command_done(self, content):
        Utility.run_command('task ' + content)
        self.refresh()

    @dismiss_editor
    def shell_done(self, content):
        Utility.run_command("tmux split-window '%s'" % content)

    @dismiss_editor
    def limit_done(self, content):
        self.limit = content
        self.refresh()