Commit 94ce313e authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge pull request #98 from mruwek/column-ui-multi

Execute commands on multiple items + repeat command
parents 8b1ff41a 06563a35
...@@ -79,6 +79,7 @@ x = cmd do {} ...@@ -79,6 +79,7 @@ x = cmd do {}
pp = postpone pp = postpone
ps = postpone_s ps = postpone_s
pr = pri pr = pri
m = mark
0 = first_column 0 = first_column
$ = last_column $ = last_column
h = prev_column h = prev_column
...@@ -92,3 +93,4 @@ L = swap_left ...@@ -92,3 +93,4 @@ L = swap_left
R = swap_right R = swap_right
<Left> = prev_column <Left> = prev_column
<Right> = next_column <Right> = next_column
<Esc> = reset
...@@ -125,6 +125,8 @@ class _Config: ...@@ -125,6 +125,8 @@ class _Config:
'e': 'cmd edit {}', 'e': 'cmd edit {}',
'u': 'cmd revert', 'u': 'cmd revert',
'x': 'cmd do {}', 'x': 'cmd do {}',
'm': 'mark',
'.': 'repeat',
'pp': 'postpone', 'pp': 'postpone',
'ps': 'postpone_s', 'ps': 'postpone_s',
'pr': 'pri', 'pr': 'pri',
...@@ -141,6 +143,7 @@ class _Config: ...@@ -141,6 +143,7 @@ class _Config:
'R': 'swap_right', 'R': 'swap_right',
'<Left>': 'prev_column', '<Left>': 'prev_column',
'<Right>': 'next_column', '<Right>': 'next_column',
'<Esc>': 'reset',
}, },
} }
......
...@@ -96,6 +96,8 @@ class UIApplication(CLIApplicationBase): ...@@ -96,6 +96,8 @@ class UIApplication(CLIApplicationBase):
self.todofile = TodoFile.TodoFile(config().todotxt()) self.todofile = TodoFile.TodoFile(config().todotxt())
self.todolist = TodoList.TodoList(self.todofile.read()) self.todolist = TodoList.TodoList(self.todofile.read())
self.marked_todos = []
self.columns = urwid.Columns([], dividechars=0, min_width=COLUMN_WIDTH) self.columns = urwid.Columns([], dividechars=0, min_width=COLUMN_WIDTH)
self.commandline = CommandLineWidget('topydo> ') self.commandline = CommandLineWidget('topydo> ')
self.keystate_widget = KeystateWidget() self.keystate_widget = KeystateWidget()
...@@ -106,6 +108,8 @@ class UIApplication(CLIApplicationBase): ...@@ -106,6 +108,8 @@ class UIApplication(CLIApplicationBase):
self.keymap = config().column_keymap() self.keymap = config().column_keymap()
self._alarm = None self._alarm = None
self._last_cmd = None
# console widget # console widget
self.console = ConsoleWidget() self.console = ConsoleWidget()
get_terminal_size(self._console_width) get_terminal_size(self._console_width)
...@@ -165,11 +169,19 @@ class UIApplication(CLIApplicationBase): ...@@ -165,11 +169,19 @@ class UIApplication(CLIApplicationBase):
def _output(self, p_text): def _output(self, p_text):
self._print_to_console(p_text + "\n") self._print_to_console(p_text + "\n")
def _execute_handler(self, p_command, p_output=None): def _execute_handler(self, p_command, p_todo_id=None, p_output=None):
""" """
Executes a command, given as a string. Executes a command, given as a string.
""" """
p_output = p_output or self._output p_output = p_output or self._output
self._last_cmd = (p_command, p_output == self._output)
if '{}' in p_command:
if self._has_marked_todos():
p_todo_id = ' '.join(self.marked_todos)
p_command = p_command.format(p_todo_id)
p_command = shlex.split(p_command) p_command = shlex.split(p_command)
try: try:
(subcommand, args) = get_subcommand(p_command) (subcommand, args) = get_subcommand(p_command)
...@@ -199,14 +211,28 @@ class UIApplication(CLIApplicationBase): ...@@ -199,14 +211,28 @@ class UIApplication(CLIApplicationBase):
def _update_all_columns(self): def _update_all_columns(self):
for column, _ in self.columns.contents: for column, _ in self.columns.contents:
column.update() column.update()
column.keystate = None
def _post_execute(self): def _post_execute(self):
# store dirty flag because base _post_execute will reset it after flush # store dirty flag because base _post_execute will reset it after flush
dirty = self.todolist.dirty dirty = self.todolist.dirty
super()._post_execute() super()._post_execute()
if dirty: if dirty or self.marked_todos:
self._update_all_columns() self._reset_state()
def _repeat_last_cmd(self, p_todo_id=None):
try:
cmd, verbosity = self._last_cmd
except TypeError:
return
self._execute_handler(cmd, p_todo_id,
self._output if verbosity else lambda _: None)
def _reset_state(self):
self.marked_todos = []
self._update_all_columns()
def _blur_commandline(self): def _blur_commandline(self):
self.mainwindow.focus_item = 0 self.mainwindow.focus_item = 0
...@@ -276,6 +302,7 @@ class UIApplication(CLIApplicationBase): ...@@ -276,6 +302,7 @@ class UIApplication(CLIApplicationBase):
'copy_column': self._copy_column, 'copy_column': self._copy_column,
'swap_left': self._swap_column_left, 'swap_left': self._swap_column_left,
'swap_right': self._swap_column_right, 'swap_right': self._swap_column_right,
'reset': self._reset_state,
} }
dispatch[p_action]() dispatch[p_action]()
...@@ -328,17 +355,21 @@ class UIApplication(CLIApplicationBase): ...@@ -328,17 +355,21 @@ class UIApplication(CLIApplicationBase):
When no position is given, it is added to the end, otherwise inserted When no position is given, it is added to the end, otherwise inserted
before that position. before that position.
""" """
def execute_silent(p_cmd, p_todo_id=None):
self._execute_handler(p_cmd, p_todo_id, lambda _: None)
todolist = TodoListWidget(p_view, p_view.data['title'], self.keymap) todolist = TodoListWidget(p_view, p_view.data['title'], self.keymap)
no_output = lambda _: None
urwid.connect_signal(todolist, 'execute_command_silent', urwid.connect_signal(todolist, 'execute_command_silent',
lambda cmd: self._execute_handler(cmd, no_output)) execute_silent)
urwid.connect_signal(todolist, 'execute_command', self._execute_handler) urwid.connect_signal(todolist, 'execute_command', self._execute_handler)
urwid.connect_signal(todolist, 'repeat_cmd', self._repeat_last_cmd)
urwid.connect_signal(todolist, 'refresh', self.mainloop.screen.clear) urwid.connect_signal(todolist, 'refresh', self.mainloop.screen.clear)
urwid.connect_signal(todolist, 'add_pending_action', self._set_alarm) urwid.connect_signal(todolist, 'add_pending_action', self._set_alarm)
urwid.connect_signal(todolist, 'remove_pending_action', self._remove_alarm) urwid.connect_signal(todolist, 'remove_pending_action', self._remove_alarm)
urwid.connect_signal(todolist, 'column_action', self._column_action_handler) urwid.connect_signal(todolist, 'column_action', self._column_action_handler)
urwid.connect_signal(todolist, 'show_keystate', self._print_keystate) urwid.connect_signal(todolist, 'show_keystate', self._print_keystate)
urwid.connect_signal(todolist, 'toggle_mark',
self._process_mark_toggle)
options = self.columns.options( options = self.columns.options(
width_type='given', width_type='given',
...@@ -455,6 +486,22 @@ class UIApplication(CLIApplicationBase): ...@@ -455,6 +486,22 @@ class UIApplication(CLIApplicationBase):
return sz return sz
def _has_marked_todos(self):
return len(self.marked_todos) > 0
def _process_mark_toggle(self, p_todo_id):
"""
Adds p_todo_id to marked_todos attribute and returns True if p_todo_id
is not already present. Removes p_todo_id from marked_todos and returns
False otherwise.
"""
if p_todo_id not in self.marked_todos:
self.marked_todos.append(p_todo_id)
return True
else:
self.marked_todos.remove(p_todo_id)
return False
def run(self): def run(self):
layout = columns() layout = columns()
if len(layout) > 0: if len(layout) > 0:
......
...@@ -60,8 +60,10 @@ class TodoListWidget(urwid.LineBox): ...@@ -60,8 +60,10 @@ class TodoListWidget(urwid.LineBox):
'refresh', 'refresh',
'add_pending_action', 'add_pending_action',
'remove_pending_action', 'remove_pending_action',
'repeat_cmd',
'column_action', 'column_action',
'show_keystate', 'show_keystate',
'toggle_mark',
]) ])
@property @property
...@@ -199,6 +201,18 @@ class TodoListWidget(urwid.LineBox): ...@@ -199,6 +201,18 @@ class TodoListWidget(urwid.LineBox):
def selectable(self): def selectable(self):
return True return True
def _toggle_marked_status(self):
try:
todo = self.listbox.focus.todo
todo_id = str(self.view.todolist.number(todo))
if urwid.emit_signal(self, 'toggle_mark', todo_id):
self.listbox.focus.mark()
else:
self.listbox.focus.unmark()
except AttributeError:
# No todo item selected
pass
def _execute_on_selected(self, p_cmd_str, p_execute_signal): def _execute_on_selected(self, p_cmd_str, p_execute_signal):
""" """
Executes command specified by p_cmd_str on selected todo item. Executes command specified by p_cmd_str on selected todo item.
...@@ -213,7 +227,7 @@ class TodoListWidget(urwid.LineBox): ...@@ -213,7 +227,7 @@ class TodoListWidget(urwid.LineBox):
todo = self.listbox.focus.todo todo = self.listbox.focus.todo
todo_id = str(self.view.todolist.number(todo)) todo_id = str(self.view.todolist.number(todo))
urwid.emit_signal(self, p_execute_signal, p_cmd_str.format(todo_id)) urwid.emit_signal(self, p_execute_signal, p_cmd_str, todo_id)
# force screen redraw after editing # force screen redraw after editing
if p_cmd_str.startswith('edit'): if p_cmd_str.startswith('edit'):
...@@ -250,8 +264,8 @@ class TodoListWidget(urwid.LineBox): ...@@ -250,8 +264,8 @@ class TodoListWidget(urwid.LineBox):
Currently supported actions are: 'up', 'down', 'home', 'end', Currently supported actions are: 'up', 'down', 'home', 'end',
'first_column', 'last_column', 'prev_column', 'next_column', 'first_column', 'last_column', 'prev_column', 'next_column',
'append_column', 'insert_column', 'edit_column', 'delete_column', 'append_column', 'insert_column', 'edit_column', 'delete_column',
'copy_column', swap_right', 'swap_left', 'postpone', 'postpone_s' and 'copy_column', swap_right', 'swap_left', 'postpone', 'postpone_s',
'pri'. 'pri', 'mark', 'reset' and 'repeat'.
""" """
column_actions = ['first_column', column_actions = ['first_column',
'last_column', 'last_column',
...@@ -264,6 +278,7 @@ class TodoListWidget(urwid.LineBox): ...@@ -264,6 +278,7 @@ class TodoListWidget(urwid.LineBox):
'copy_column', 'copy_column',
'swap_left', 'swap_left',
'swap_right', 'swap_right',
'reset',
] ]
if p_action_str in column_actions: if p_action_str in column_actions:
...@@ -278,6 +293,10 @@ class TodoListWidget(urwid.LineBox): ...@@ -278,6 +293,10 @@ class TodoListWidget(urwid.LineBox):
pass pass
elif p_action_str == 'pri': elif p_action_str == 'pri':
pass pass
elif p_action_str == 'mark':
self._toggle_marked_status()
elif p_action_str == 'repeat':
self._repeat_cmd()
def _add_pending_action(self, p_action, p_size): def _add_pending_action(self, p_action, p_size):
""" """
...@@ -328,3 +347,12 @@ class TodoListWidget(urwid.LineBox): ...@@ -328,3 +347,12 @@ class TodoListWidget(urwid.LineBox):
self._pp_offset = None self._pp_offset = None
result = False result = False
return result return result
def _repeat_cmd(self):
try:
todo = self.listbox.focus.todo
todo_id = str(self.view.todolist.number(todo))
except AttributeError:
todo_id = None
urwid.emit_signal(self, 'repeat_cmd', todo_id)
...@@ -35,6 +35,7 @@ def _to_urwid_color(p_color): ...@@ -35,6 +35,7 @@ def _to_urwid_color(p_color):
else: else:
return 'h{}'.format(p_color.color) return 'h{}'.format(p_color.color)
def _markup(p_todo, p_focus): def _markup(p_todo, p_focus):
""" """
Returns an attribute spec for the colors that correspond to the given todo Returns an attribute spec for the colors that correspond to the given todo
...@@ -83,8 +84,8 @@ class TodoWidget(urwid.WidgetWrap): ...@@ -83,8 +84,8 @@ class TodoWidget(urwid.WidgetWrap):
self.widget = urwid.AttrMap( self.widget = urwid.AttrMap(
self.columns, self.columns,
_markup(p_todo, False), # no focus _markup(p_todo, False), # no focus
_markup(p_todo, True) # focus _markup(p_todo, True) # focus
) )
super().__init__(self.widget) super().__init__(self.widget)
...@@ -99,3 +100,9 @@ class TodoWidget(urwid.WidgetWrap): ...@@ -99,3 +100,9 @@ class TodoWidget(urwid.WidgetWrap):
def selectable(self): def selectable(self):
# make sure that ListBox will highlight this widget # make sure that ListBox will highlight this widget
return True return True
def mark(self):
self.widget.set_attr_map({None: _markup(self.todo, True)})
def unmark(self):
self.widget.set_attr_map({None: _markup(self.todo, False)})
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment