Commit a414f07d authored by Bram Schoenmakers's avatar Bram Schoenmakers

Merge branch 'master' into groups

parents 3bb55c37 c9e1cd0d
...@@ -4,3 +4,5 @@ project_color = ...@@ -4,3 +4,5 @@ project_color =
context_color = context_color =
link_color = link_color =
metadata_color = metadata_color =
focus_background_color =
marked_background_color =
...@@ -24,3 +24,5 @@ project_color = junk ...@@ -24,3 +24,5 @@ project_color = junk
context_color = junk context_color = junk
metadata_color = junk metadata_color = junk
link_color = junk link_color = junk
focus_background_color = junk
marked_background_color = junk
First first
(A) Foo (A) Foo
2014-06-14 Last 2014-06-14 Last
(A) Foo (A) Foo
2014-06-14 Last 2014-06-14 Last
First first
...@@ -154,6 +154,14 @@ class ColorsTest(TopydoTest): ...@@ -154,6 +154,14 @@ class ColorsTest(TopydoTest):
self.assertEqual(color_b, '') self.assertEqual(color_b, '')
self.assertEqual(color_c, '') self.assertEqual(color_c, '')
def test_focus_color(self):
config(p_overrides={('colorscheme', 'focus_background_color'): 'gray'})
self.assertEqual(config().focus_background_color().as_ansi(), '\033[0;37m')
def test_mark_color(self):
config(p_overrides={('colorscheme', 'marked_background_color'): 'blue'})
self.assertEqual(config().marked_background_color().as_ansi(), '\033[0;34m')
def test_empty_color_values(self): def test_empty_color_values(self):
config("test/data/ColorsTest5.conf") config("test/data/ColorsTest5.conf")
project_color = config().project_color().as_ansi(p_decoration='bold') project_color = config().project_color().as_ansi(p_decoration='bold')
......
...@@ -128,6 +128,18 @@ class ConfigTest(TopydoTest): ...@@ -128,6 +128,18 @@ class ConfigTest(TopydoTest):
self.assertEqual(config("test/data/ConfigTest5.conf").link_color().color, 6) self.assertEqual(config("test/data/ConfigTest5.conf").link_color().color, 6)
def test_config24(self): def test_config24(self):
""" No focus background color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").focus_background_color().color, 7)
def test_config25(self):
""" No mark background color value. """
self.assertEqual(config("test/data/ConfigTest5.conf").marked_background_color().color, 4)
def test_config26(self):
self.assertTrue(config("test/data/ConfigTest4.conf").focus_background_color().is_neutral())
self.assertTrue(config("test/data/ConfigTest4.conf").marked_background_color().is_neutral())
def test_config27(self):
""" column_keymap test. """ """ column_keymap test. """
keymap, keystates = config("test/data/ConfigTest6.conf").column_keymap() keymap, keystates = config("test/data/ConfigTest6.conf").column_keymap()
......
...@@ -295,6 +295,39 @@ class DepCommandTest(CommandTest): ...@@ -295,6 +295,39 @@ class DepCommandTest(CommandTest):
self.assertEqual(self.output, "") self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n") self.assertEqual(self.errors, command.usage() + "\n")
def test_dot1(self):
command = DepCommand(["dot"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, command.usage() + "\n")
def test_dot2(self):
self.maxDiff = None
command = DepCommand(["dot", "1"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, """digraph topydo {
node [ shape="none" margin="0" fontsize="9" fontname="Helvetica" ]
_2 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>2</B></TD><TD BALIGN="LEFT"><B>Bar</B></TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_3 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>3</B></TD><TD BALIGN="LEFT"><B>Baz</B></TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_1 [label=<<TABLE CELLBORDER="0" CELLSPACING="1" VALIGN="top"><TR><TD><B>1</B></TD><TD BALIGN="LEFT"><B>Foo</B></TD></TR></TABLE>> style=filled fillcolor="#008000" fontcolor="#ffffff"]
_1 -> _2
_1 -> _3
}\n
""")
self.assertEqual(self.errors, "")
def test_dot3(self):
command = DepCommand(["dot", "99"], self.todolist, self.out, self.error)
command.execute()
self.assertFalse(self.todolist.dirty)
self.assertEqual(self.output, "")
self.assertEqual(self.errors, "Invalid todo number given." + "\n")
def gc_helper(self, p_subcommand): def gc_helper(self, p_subcommand):
command = DepCommand([p_subcommand], self.todolist, self.out, command = DepCommand([p_subcommand], self.todolist, self.out,
self.error) self.error)
......
...@@ -33,14 +33,14 @@ class SortCommandTest(CommandTest): ...@@ -33,14 +33,14 @@ class SortCommandTest(CommandTest):
command.execute() command.execute()
self.assertEqual(self.todolist.print_todos(), self.assertEqual(self.todolist.print_todos(),
"First\n(A) Foo\n2014-06-14 Last") "first\n(A) Foo\n2014-06-14 Last")
def test_sort2(self): def test_sort2(self):
command = SortCommand([], self.todolist, self.out, self.error) command = SortCommand([], self.todolist, self.out, self.error)
command.execute() command.execute()
self.assertEqual(self.todolist.print_todos(), self.assertEqual(self.todolist.print_todos(),
"(A) Foo\n2014-06-14 Last\nFirst") "(A) Foo\n2014-06-14 Last\nfirst")
def test_sort3(self): def test_sort3(self):
""" Check that order does not influence the UID of a todo. """ """ Check that order does not influence the UID of a todo. """
......
...@@ -50,6 +50,8 @@ append_parent_contexts = 0 ...@@ -50,6 +50,8 @@ append_parent_contexts = 0
; context_color = magenta ; context_color = magenta
; metadata_color = green ; metadata_color = green
; link_color = light-cyan ; link_color = light-cyan
; focus_background_color = gray
; marked_background_color = blue
[aliases] [aliases]
;showall = ls -x ;showall = ls -x
......
...@@ -135,17 +135,20 @@ class DepCommand(Command): ...@@ -135,17 +135,20 @@ class DepCommand(Command):
""" Handles the dot subsubcommand. """ """ Handles the dot subsubcommand. """
self.printer = DotPrinter(self.todolist) self.printer = DotPrinter(self.todolist)
arg = self.argument(1)
try: try:
arg = self.argument(1)
todo = self.todolist.todo(arg) todo = self.todolist.todo(arg)
arg = self.argument(1)
todos = set([self.todolist.todo(arg)]) todos = set([self.todolist.todo(arg)])
todos |= set(self.todolist.children(todo)) todos |= set(self.todolist.children(todo))
todos |= set(self.todolist.parents(todo)) todos |= set(self.todolist.parents(todo))
todos = sorted(todos, key=lambda t: t.text())
self.out(self.printer.print_list(todos)) self.out(self.printer.print_list(todos))
except InvalidTodoException: except InvalidTodoException:
self.error("Invalid todo number given.") self.error("Invalid todo number given.")
except InvalidCommandArgument:
self.error(self.usage())
def execute(self): def execute(self):
......
...@@ -107,6 +107,8 @@ class _Config: ...@@ -107,6 +107,8 @@ class _Config:
'metadata_color': 'green', 'metadata_color': 'green',
'link_color': 'cyan', 'link_color': 'cyan',
'priority_colors': 'A:cyan,B:yellow,C:blue', 'priority_colors': 'A:cyan,B:yellow,C:blue',
'focus_background_color': 'gray',
'marked_background_color': 'blue'
}, },
'aliases': { 'aliases': {
...@@ -371,6 +373,18 @@ class _Config: ...@@ -371,6 +373,18 @@ class _Config:
except ValueError: except ValueError:
return Color(self.cp.get('colorscheme', 'link_color')) return Color(self.cp.get('colorscheme', 'link_color'))
def focus_background_color(self):
try:
return Color(self.cp.getint('colorscheme', 'focus_background_color'))
except ValueError:
return Color(self.cp.get('colorscheme', 'focus_background_color'))
def marked_background_color(self):
try:
return Color(self.cp.getint('colorscheme', 'marked_background_color'))
except ValueError:
return Color(self.cp.get('colorscheme', 'marked_background_color'))
def auto_creation_date(self): def auto_creation_date(self):
try: try:
return self.cp.getboolean('add', 'auto_creation_date') return self.cp.getboolean('add', 'auto_creation_date')
......
...@@ -28,42 +28,8 @@ class TodoFile(object): ...@@ -28,42 +28,8 @@ class TodoFile(object):
to. to.
""" """
def __init__(self, p_path, p_on_update=None): def __init__(self, p_path):
self.path = os.path.abspath(p_path) self.path = os.path.abspath(p_path)
self.write_lock = False
if p_on_update:
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, FileModifiedEvent, FileCreatedEvent
class EventHandler(FileSystemEventHandler):
"""
Event handler to catch modifications (or creations) of the
current todo.txt file.
"""
def __init__(self, p_file):
super().__init__()
self.file = p_file
def _handle(self, p_event):
right_type = isinstance(p_event, FileModifiedEvent) or isinstance(p_event, FileCreatedEvent)
if not self.file.write_lock and right_type and p_event.src_path == self.file.path:
p_on_update()
def on_created(self, p_event):
"""
Because vim deletes and creates a file on buffer save, also
catch a creation event.
"""
self._handle(p_event)
def on_modified(self, p_event):
self._handle(p_event)
observer = Observer()
observer.schedule(EventHandler(self), os.path.dirname(self.path))
observer.start()
def read(self): def read(self):
""" Reads the todo.txt file and returns a list of todo items. """ """ Reads the todo.txt file and returns a list of todo items. """
...@@ -85,10 +51,6 @@ class TodoFile(object): ...@@ -85,10 +51,6 @@ class TodoFile(object):
to the file. to the file.
""" """
# make sure not to reread the todo file because this instance is
# actually writing it
self.write_lock = True
todofile = codecs.open(self.path, 'w', encoding="utf-8") todofile = codecs.open(self.path, 'w', encoding="utf-8")
if p_todos is list: if p_todos is list:
...@@ -100,4 +62,3 @@ class TodoFile(object): ...@@ -100,4 +62,3 @@ class TodoFile(object):
todofile.write("\n") todofile.write("\n")
todofile.close() todofile.close()
self.write_lock = False
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2014 - 2016 Bram Schoenmakers <bram@topydo.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This module deals with todo.txt files while putting a watch on them for file
changes.
"""
import os.path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, FileModifiedEvent, FileCreatedEvent
from topydo.lib.TodoFile import TodoFile
class TodoFileWatched(TodoFile):
"""
This class represents a todo.txt file, which can be read from or written
to.
"""
def __init__(self, p_path, p_on_update):
super().__init__(p_path)
class EventHandler(FileSystemEventHandler):
"""
Event handler to catch modifications (or creations) of the
current todo.txt file.
"""
def __init__(self, p_file):
super().__init__()
self.file = p_file
def _handle(self, p_event):
right_type = isinstance(p_event, FileModifiedEvent) or isinstance(p_event, FileCreatedEvent)
should_trigger = right_type and p_event.src_path == self.file.path
if self.file.self_write and should_trigger:
# the file was written by topydo, unmark that so we can
# record external writes again.
self.file.self_write = False
elif should_trigger:
p_on_update()
def on_created(self, p_event):
"""
Because vim deletes and creates a file on buffer save, also
catch a creation event.
"""
self._handle(p_event)
def on_modified(self, p_event):
self._handle(p_event)
observer = Observer()
observer.schedule(EventHandler(self), os.path.dirname(self.path))
observer.start()
def write(self, p_todos):
# make sure not to reread the todo file because this instance is
# actually writing it
self.self_write = True
super().write(p_todos)
...@@ -111,7 +111,7 @@ class DotPrinter(Printer): ...@@ -111,7 +111,7 @@ class DotPrinter(Printer):
children = set(p_todos) & set(self.todolist.children(todo, children = set(p_todos) & set(self.todolist.children(todo,
p_only_direct=True)) p_only_direct=True))
for child in children: for child in sorted(list(children), key=lambda t: t.text()):
result += ' {} -> {}\n'.format( result += ' {} -> {}\n'.format(
node_name(todo), node_name(todo),
node_name(child) node_name(child)
......
...@@ -44,13 +44,13 @@ def main(): ...@@ -44,13 +44,13 @@ def main():
from topydo.ui.prompt.Prompt import PromptApplication from topydo.ui.prompt.Prompt import PromptApplication
PromptApplication().run() PromptApplication().run()
except ImportError: except ImportError:
error("You have to install prompt-toolkit to run prompt mode.") error("Some additional dependencies for prompt mode were not installed, please install with 'pip install topydo[prompt]'")
elif args[0] == 'columns': elif args[0] == 'columns':
try: try:
from topydo.ui.columns.Main import UIApplication from topydo.ui.columns.Main import UIApplication
UIApplication().run() UIApplication().run()
except ImportError: except ImportError:
error("You have to install urwid to run column mode.") error("Some additional dependencies for column mode were not installed, please install with 'pip install topydo[columns]'")
else: else:
CLIApplication().run() CLIApplication().run()
except IndexError: except IndexError:
......
...@@ -30,7 +30,7 @@ from topydo.lib.Sorter import Sorter ...@@ -30,7 +30,7 @@ from topydo.lib.Sorter import Sorter
from topydo.lib.Filter import get_filter_list, RelevanceFilter, DependencyFilter from topydo.lib.Filter import get_filter_list, RelevanceFilter, DependencyFilter
from topydo.lib.Utils import get_terminal_size from topydo.lib.Utils import get_terminal_size
from topydo.lib.View import View from topydo.lib.View import View
from topydo.lib import TodoFile from topydo.lib.TodoFileWatched import TodoFileWatched
from topydo.lib import TodoList from topydo.lib import TodoList
from topydo.ui.CLIApplicationBase import CLIApplicationBase, error from topydo.ui.CLIApplicationBase import CLIApplicationBase, error
from topydo.ui.columns.CommandLineWidget import CommandLineWidget from topydo.ui.columns.CommandLineWidget import CommandLineWidget
...@@ -115,7 +115,7 @@ class UIApplication(CLIApplicationBase): ...@@ -115,7 +115,7 @@ class UIApplication(CLIApplicationBase):
self._redraw() self._redraw()
self.column_width = config().column_width() self.column_width = config().column_width()
self.todofile = TodoFile.TodoFile(config().todotxt(), callback) self.todofile = TodoFileWatched(config().todotxt(), callback)
self.todolist = TodoList.TodoList(self.todofile.read()) self.todolist = TodoList.TodoList(self.todofile.read())
self.marked_todos = [] self.marked_todos = []
...@@ -193,18 +193,20 @@ class UIApplication(CLIApplicationBase): ...@@ -193,18 +193,20 @@ class UIApplication(CLIApplicationBase):
context_color = to_urwid_color(config().context_color()) context_color = to_urwid_color(config().context_color())
metadata_color = to_urwid_color(config().metadata_color()) metadata_color = to_urwid_color(config().metadata_color())
link_color = to_urwid_color(config().link_color()) link_color = to_urwid_color(config().link_color())
focus_background_color = to_urwid_color(config().focus_background_color())
marked_background_color = to_urwid_color(config().marked_background_color())
palette = [ palette = [
(PaletteItem.PROJECT, '', '', '', project_color, ''), (PaletteItem.PROJECT, '', '', '', project_color, ''),
(PaletteItem.PROJECT_FOCUS, '', 'light gray', '', project_color, None), (PaletteItem.PROJECT_FOCUS, '', 'light gray', '', project_color, focus_background_color),
(PaletteItem.CONTEXT, '', '', '', context_color, ''), (PaletteItem.CONTEXT, '', '', '', context_color, ''),
(PaletteItem.CONTEXT_FOCUS, '', 'light gray', '', context_color, None), (PaletteItem.CONTEXT_FOCUS, '', 'light gray', '', context_color, focus_background_color),
(PaletteItem.METADATA, '', '', '', metadata_color, ''), (PaletteItem.METADATA, '', '', '', metadata_color, ''),
(PaletteItem.METADATA_FOCUS, '', 'light gray', '', metadata_color, None), (PaletteItem.METADATA_FOCUS, '', 'light gray', '', metadata_color, focus_background_color),
(PaletteItem.LINK, '', '', '', link_color, ''), (PaletteItem.LINK, '', '', '', link_color, ''),
(PaletteItem.LINK_FOCUS, '', 'light gray', '', link_color, None), (PaletteItem.LINK_FOCUS, '', 'light gray', '', link_color, focus_background_color),
(PaletteItem.DEFAULT_FOCUS, 'black', 'light gray'), (PaletteItem.DEFAULT_FOCUS, '', 'light gray', '', '', focus_background_color),
(PaletteItem.MARKED, '', 'light blue'), (PaletteItem.MARKED, '', 'light blue', '', '', marked_background_color),
] ]
for C in ascii_uppercase: for C in ascii_uppercase:
...@@ -217,7 +219,7 @@ class UIApplication(CLIApplicationBase): ...@@ -217,7 +219,7 @@ class UIApplication(CLIApplicationBase):
'pri_' + C, '', '', '', pri_color, '' 'pri_' + C, '', '', '', pri_color, ''
)) ))
palette.append(( palette.append((
'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, None 'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, focus_background_color
)) ))
return palette return palette
......
...@@ -37,7 +37,7 @@ except ConfigError as config_error: ...@@ -37,7 +37,7 @@ except ConfigError as config_error:
sys.exit(1) sys.exit(1)
from topydo.Commands import get_subcommand from topydo.Commands import get_subcommand
from topydo.lib import TodoFile from topydo.lib.TodoFileWatched import TodoFileWatched
from topydo.lib import TodoList from topydo.lib import TodoList
...@@ -52,7 +52,7 @@ class PromptApplication(CLIApplicationBase): ...@@ -52,7 +52,7 @@ class PromptApplication(CLIApplicationBase):
self._process_flags() self._process_flags()
self.completer = None self.completer = None
self.todofile = TodoFile.TodoFile(config().todotxt(), self._load_file) self.todofile = TodoFileWatched(config().todotxt(), self._load_file)
def _load_file(self): def _load_file(self):
""" """
......
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