mirror of
https://github.com/topydo/topydo.git
synced 2024-05-20 05:48:35 +00:00
Merge branch 'master' into groups
This commit is contained in:
commit
a414f07d1c
|
@ -4,3 +4,5 @@ project_color =
|
|||
context_color =
|
||||
link_color =
|
||||
metadata_color =
|
||||
focus_background_color =
|
||||
marked_background_color =
|
||||
|
|
|
@ -24,3 +24,5 @@ project_color = junk
|
|||
context_color = junk
|
||||
metadata_color = junk
|
||||
link_color = junk
|
||||
focus_background_color = junk
|
||||
marked_background_color = junk
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
First
|
||||
first
|
||||
(A) Foo
|
||||
2014-06-14 Last
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
(A) Foo
|
||||
2014-06-14 Last
|
||||
First
|
||||
first
|
||||
|
|
|
@ -154,6 +154,14 @@ class ColorsTest(TopydoTest):
|
|||
self.assertEqual(color_b, '')
|
||||
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):
|
||||
config("test/data/ColorsTest5.conf")
|
||||
project_color = config().project_color().as_ansi(p_decoration='bold')
|
||||
|
|
|
@ -128,6 +128,18 @@ class ConfigTest(TopydoTest):
|
|||
self.assertEqual(config("test/data/ConfigTest5.conf").link_color().color, 6)
|
||||
|
||||
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. """
|
||||
keymap, keystates = config("test/data/ConfigTest6.conf").column_keymap()
|
||||
|
||||
|
|
|
@ -295,6 +295,39 @@ class DepCommandTest(CommandTest):
|
|||
self.assertEqual(self.output, "")
|
||||
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):
|
||||
command = DepCommand([p_subcommand], self.todolist, self.out,
|
||||
self.error)
|
||||
|
|
|
@ -33,14 +33,14 @@ class SortCommandTest(CommandTest):
|
|||
command.execute()
|
||||
|
||||
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):
|
||||
command = SortCommand([], self.todolist, self.out, self.error)
|
||||
command.execute()
|
||||
|
||||
self.assertEqual(self.todolist.print_todos(),
|
||||
"(A) Foo\n2014-06-14 Last\nFirst")
|
||||
"(A) Foo\n2014-06-14 Last\nfirst")
|
||||
|
||||
def test_sort3(self):
|
||||
""" Check that order does not influence the UID of a todo. """
|
||||
|
|
|
@ -50,6 +50,8 @@ append_parent_contexts = 0
|
|||
; context_color = magenta
|
||||
; metadata_color = green
|
||||
; link_color = light-cyan
|
||||
; focus_background_color = gray
|
||||
; marked_background_color = blue
|
||||
|
||||
[aliases]
|
||||
;showall = ls -x
|
||||
|
|
|
@ -135,17 +135,20 @@ class DepCommand(Command):
|
|||
""" Handles the dot subsubcommand. """
|
||||
self.printer = DotPrinter(self.todolist)
|
||||
|
||||
arg = self.argument(1)
|
||||
|
||||
try:
|
||||
arg = self.argument(1)
|
||||
todo = self.todolist.todo(arg)
|
||||
arg = self.argument(1)
|
||||
todos = set([self.todolist.todo(arg)])
|
||||
todos |= set(self.todolist.children(todo))
|
||||
todos |= set(self.todolist.parents(todo))
|
||||
todos = sorted(todos, key=lambda t: t.text())
|
||||
|
||||
self.out(self.printer.print_list(todos))
|
||||
except InvalidTodoException:
|
||||
self.error("Invalid todo number given.")
|
||||
except InvalidCommandArgument:
|
||||
self.error(self.usage())
|
||||
|
||||
|
||||
def execute(self):
|
||||
|
|
|
@ -107,6 +107,8 @@ class _Config:
|
|||
'metadata_color': 'green',
|
||||
'link_color': 'cyan',
|
||||
'priority_colors': 'A:cyan,B:yellow,C:blue',
|
||||
'focus_background_color': 'gray',
|
||||
'marked_background_color': 'blue'
|
||||
},
|
||||
|
||||
'aliases': {
|
||||
|
@ -371,6 +373,18 @@ class _Config:
|
|||
except ValueError:
|
||||
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):
|
||||
try:
|
||||
return self.cp.getboolean('add', 'auto_creation_date')
|
||||
|
|
|
@ -28,42 +28,8 @@ class TodoFile(object):
|
|||
to.
|
||||
"""
|
||||
|
||||
def __init__(self, p_path, p_on_update=None):
|
||||
def __init__(self, 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):
|
||||
""" Reads the todo.txt file and returns a list of todo items. """
|
||||
|
@ -85,10 +51,6 @@ class TodoFile(object):
|
|||
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")
|
||||
|
||||
if p_todos is list:
|
||||
|
@ -100,4 +62,3 @@ class TodoFile(object):
|
|||
todofile.write("\n")
|
||||
|
||||
todofile.close()
|
||||
self.write_lock = False
|
||||
|
|
76
topydo/lib/TodoFileWatched.py
Normal file
76
topydo/lib/TodoFileWatched.py
Normal file
|
@ -0,0 +1,76 @@
|
|||
# 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):
|
|||
children = set(p_todos) & set(self.todolist.children(todo,
|
||||
p_only_direct=True))
|
||||
|
||||
for child in children:
|
||||
for child in sorted(list(children), key=lambda t: t.text()):
|
||||
result += ' {} -> {}\n'.format(
|
||||
node_name(todo),
|
||||
node_name(child)
|
||||
|
|
|
@ -44,13 +44,13 @@ def main():
|
|||
from topydo.ui.prompt.Prompt import PromptApplication
|
||||
PromptApplication().run()
|
||||
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':
|
||||
try:
|
||||
from topydo.ui.columns.Main import UIApplication
|
||||
UIApplication().run()
|
||||
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:
|
||||
CLIApplication().run()
|
||||
except IndexError:
|
||||
|
|
|
@ -30,7 +30,7 @@ from topydo.lib.Sorter import Sorter
|
|||
from topydo.lib.Filter import get_filter_list, RelevanceFilter, DependencyFilter
|
||||
from topydo.lib.Utils import get_terminal_size
|
||||
from topydo.lib.View import View
|
||||
from topydo.lib import TodoFile
|
||||
from topydo.lib.TodoFileWatched import TodoFileWatched
|
||||
from topydo.lib import TodoList
|
||||
from topydo.ui.CLIApplicationBase import CLIApplicationBase, error
|
||||
from topydo.ui.columns.CommandLineWidget import CommandLineWidget
|
||||
|
@ -115,7 +115,7 @@ class UIApplication(CLIApplicationBase):
|
|||
self._redraw()
|
||||
|
||||
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.marked_todos = []
|
||||
|
@ -193,18 +193,20 @@ class UIApplication(CLIApplicationBase):
|
|||
context_color = to_urwid_color(config().context_color())
|
||||
metadata_color = to_urwid_color(config().metadata_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 = [
|
||||
(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_FOCUS, '', 'light gray', '', context_color, None),
|
||||
(PaletteItem.CONTEXT_FOCUS, '', 'light gray', '', context_color, focus_background_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_FOCUS, '', 'light gray', '', link_color, None),
|
||||
(PaletteItem.DEFAULT_FOCUS, 'black', 'light gray'),
|
||||
(PaletteItem.MARKED, '', 'light blue'),
|
||||
(PaletteItem.LINK_FOCUS, '', 'light gray', '', link_color, focus_background_color),
|
||||
(PaletteItem.DEFAULT_FOCUS, '', 'light gray', '', '', focus_background_color),
|
||||
(PaletteItem.MARKED, '', 'light blue', '', '', marked_background_color),
|
||||
]
|
||||
|
||||
for C in ascii_uppercase:
|
||||
|
@ -217,7 +219,7 @@ class UIApplication(CLIApplicationBase):
|
|||
'pri_' + C, '', '', '', pri_color, ''
|
||||
))
|
||||
palette.append((
|
||||
'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, None
|
||||
'pri_' + C + '_focus', '', 'light gray', '', pri_color_focus, focus_background_color
|
||||
))
|
||||
|
||||
return palette
|
||||
|
|
|
@ -37,7 +37,7 @@ except ConfigError as config_error:
|
|||
sys.exit(1)
|
||||
|
||||
from topydo.Commands import get_subcommand
|
||||
from topydo.lib import TodoFile
|
||||
from topydo.lib.TodoFileWatched import TodoFileWatched
|
||||
from topydo.lib import TodoList
|
||||
|
||||
|
||||
|
@ -52,7 +52,7 @@ class PromptApplication(CLIApplicationBase):
|
|||
|
||||
self._process_flags()
|
||||
self.completer = None
|
||||
self.todofile = TodoFile.TodoFile(config().todotxt(), self._load_file)
|
||||
self.todofile = TodoFileWatched(config().todotxt(), self._load_file)
|
||||
|
||||
def _load_file(self):
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue