nowfocus 0.2.7__py3-none-any.whl → 0.2.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nowfocus/__init__.py +0 -67
- nowfocus/__main__.py +49 -28
- nowfocus/conf.py +12 -5
- nowfocus/connectors/todotxt.py +2 -19
- nowfocus/connectors/txt.py +31 -20
- nowfocus/connectors/vikunja.py +6 -4
- nowfocus/icon/icon-red.svg +101 -0
- nowfocus/icon/icon.svg +12 -1
- nowfocus/new_task_dialog.py +4 -1
- nowfocus/settings.py +55 -25
- nowfocus/task_window.py +6 -18
- nowfocus/utils.py +143 -21
- {nowfocus-0.2.7.dist-info → nowfocus-0.2.12.dist-info}/METADATA +9 -6
- {nowfocus-0.2.7.dist-info → nowfocus-0.2.12.dist-info}/RECORD +18 -18
- nowfocus/desktop-extras/set_gsettings_keybinding.sh +0 -51
- {nowfocus-0.2.7.dist-info → nowfocus-0.2.12.dist-info}/WHEEL +0 -0
- {nowfocus-0.2.7.dist-info → nowfocus-0.2.12.dist-info}/entry_points.txt +0 -0
- {nowfocus-0.2.7.dist-info → nowfocus-0.2.12.dist-info}/licenses/LICENSE +0 -0
- {nowfocus-0.2.7.dist-info → nowfocus-0.2.12.dist-info}/top_level.txt +0 -0
nowfocus/__init__.py
CHANGED
|
@@ -1,68 +1 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
def startup():
|
|
4
|
-
# print("startup ",sys.argv)
|
|
5
|
-
|
|
6
|
-
''' if called with no arguments send shutdown to pipe, wait 2 seconds delete pipe file and launch application. if called with arguments send args to pipe if it exist otherwise '''
|
|
7
|
-
|
|
8
|
-
parser = argparse.ArgumentParser(
|
|
9
|
-
# prog='ProgramName',
|
|
10
|
-
# description='What the program does',
|
|
11
|
-
# epilog='Text at the bottom of help'
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
parser.add_argument('task',nargs='?') # optional positional argument
|
|
15
|
-
parser.add_argument('-s', '--debug_systems', nargs="*", default=[]) # option that takes a value
|
|
16
|
-
parser.add_argument('-l', '--debug_level', default=1) # option that takes a value
|
|
17
|
-
parser.add_argument('-f', '--force', action='store_true', help="Force restart by deleting existing named pipe") # on/off flag
|
|
18
|
-
# parser.add_argument('-v', '--verbose', action='store_true') # on/off flag
|
|
19
|
-
|
|
20
|
-
args = parser.parse_args()
|
|
21
|
-
print(args)
|
|
22
|
-
|
|
23
|
-
conf.debug_level = int(args.debug_level)
|
|
24
|
-
conf.debug_systems = args.debug_systems
|
|
25
|
-
|
|
26
|
-
if args.force:
|
|
27
|
-
print("Lanched with --force flag, forcibly deleting old pipe")
|
|
28
|
-
try:
|
|
29
|
-
os.remove(pipe_file)
|
|
30
|
-
except Exception as e:
|
|
31
|
-
print(e)
|
|
32
|
-
|
|
33
|
-
try:
|
|
34
|
-
os.mkfifo(pipe_file)
|
|
35
|
-
|
|
36
|
-
signal.signal(signal.SIGUSR1, Application.signal_handler)
|
|
37
|
-
app = Application()
|
|
38
|
-
|
|
39
|
-
if args.task:
|
|
40
|
-
print("Writing args.task to pipe", args.task)
|
|
41
|
-
with open(pipe_file, "w") as pipeout:
|
|
42
|
-
pipeout.write(args.task)
|
|
43
|
-
pipeout.close()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
except FileExistsError:
|
|
47
|
-
print("Named pipe exists, application must be running (or improperly shut down.) ")
|
|
48
|
-
|
|
49
|
-
# if args: pass to pipe and exit
|
|
50
|
-
if args.task:
|
|
51
|
-
pipe_line = args.task
|
|
52
|
-
else:
|
|
53
|
-
pipe_line = "open_task_window"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
print("Writing arg ",pipe_line," to pipe")
|
|
57
|
-
|
|
58
|
-
with open(pipe_file, "w") as pipeout:
|
|
59
|
-
pipeout.write(pipe_line)
|
|
60
|
-
pipeout.close()
|
|
61
|
-
|
|
62
|
-
exit()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
except Exception as e:
|
|
67
|
-
print(f"Named pipe creation failed: {e}")
|
|
68
1
|
|
nowfocus/__main__.py
CHANGED
|
@@ -50,7 +50,6 @@ from session_options import SessionOptionsDialog
|
|
|
50
50
|
dbg(conf.user,l=3,s='user_settings')
|
|
51
51
|
|
|
52
52
|
setproctitle.setproctitle(conf.app_name)
|
|
53
|
-
pipe_file = os.path.dirname(os.path.realpath(__file__))+"/"+conf.app_id+"_pipe"
|
|
54
53
|
|
|
55
54
|
print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file__)))
|
|
56
55
|
|
|
@@ -73,7 +72,7 @@ class Application(Gtk.Application):
|
|
|
73
72
|
self.menu_tasks = {}
|
|
74
73
|
self.list_menus = {}
|
|
75
74
|
|
|
76
|
-
self.indicator = appindicator.Indicator.new(conf.app_name, os.path.abspath('icon/icon-
|
|
75
|
+
self.indicator = appindicator.Indicator.new(conf.app_name, os.path.abspath('icon/icon-1.svg'), appindicator.IndicatorCategory.APPLICATION_STATUS)
|
|
77
76
|
|
|
78
77
|
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
|
|
79
78
|
|
|
@@ -106,6 +105,8 @@ class Application(Gtk.Application):
|
|
|
106
105
|
except Exception as e:
|
|
107
106
|
dbg("Error resuming session",e,l=1)
|
|
108
107
|
|
|
108
|
+
utils.start_todo_file_watchers()
|
|
109
|
+
|
|
109
110
|
self.pipethread = threading.Thread(target=self.check_pipe)
|
|
110
111
|
self.pipethread.daemon = True
|
|
111
112
|
self.pipethread.start()
|
|
@@ -163,11 +164,11 @@ class Application(Gtk.Application):
|
|
|
163
164
|
print('Caching active session', self.session['label'])
|
|
164
165
|
db_set_session_cache(self.session)
|
|
165
166
|
try:
|
|
166
|
-
# print("before os.remove(
|
|
167
|
-
os.remove(
|
|
167
|
+
# print("before os.remove(conf.pipe)")
|
|
168
|
+
os.remove(conf.pipe)
|
|
168
169
|
print("Pipe removed")
|
|
169
170
|
except Exception as e:
|
|
170
|
-
print("Error removing
|
|
171
|
+
print("Error removing conf.pipe in quit",e)
|
|
171
172
|
|
|
172
173
|
notify.uninit()
|
|
173
174
|
Gtk.main_quit()
|
|
@@ -217,6 +218,8 @@ class Application(Gtk.Application):
|
|
|
217
218
|
if (int(time_difference(conf.todo_sync_time)) / 60) > conf.user['todolist_refresh_interval'] * 60 :
|
|
218
219
|
self.async_refresh()
|
|
219
220
|
|
|
221
|
+
# TODO: use individual todo_refresh_times
|
|
222
|
+
|
|
220
223
|
minutes = (int(self.session['duration']) / 60)
|
|
221
224
|
|
|
222
225
|
if(self.is_running == False):
|
|
@@ -301,7 +304,7 @@ class Application(Gtk.Application):
|
|
|
301
304
|
if(self.is_running == True):
|
|
302
305
|
self.icon_tick_number = self.icon_tick_number + 1
|
|
303
306
|
|
|
304
|
-
if self.icon_tick_number >
|
|
307
|
+
if self.icon_tick_number > 8:
|
|
305
308
|
self.icon_tick_number = 1
|
|
306
309
|
|
|
307
310
|
label = self.session['label'] + ": " + sec_to_time(self.session['duration'])
|
|
@@ -312,12 +315,21 @@ class Application(Gtk.Application):
|
|
|
312
315
|
|
|
313
316
|
# label = random.choice(conf.idle_messages) # Cool but makes menu bounce around #Could be paused when the menu opens
|
|
314
317
|
label = conf.user['default_text']
|
|
315
|
-
|
|
316
|
-
|
|
318
|
+
if self.session['duration'] > 60 and self.session['duration'] % 2:
|
|
319
|
+
indicator.set_icon_full(os.path.abspath('icon/icon-red.svg'),label)
|
|
320
|
+
else:
|
|
321
|
+
indicator.set_icon_full(os.path.abspath('icon/icon-1.svg'),label)
|
|
317
322
|
|
|
318
323
|
# https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
|
|
319
324
|
indicator.set_label(label, "Wide")
|
|
320
325
|
|
|
326
|
+
for todo in conf.todo_sync_required:
|
|
327
|
+
# print('tick noticed a todo needing refreshment, time since refresh: ',time_difference(conf.todo_sync_times[todo]))
|
|
328
|
+
|
|
329
|
+
if time_difference(conf.todo_sync_times[todo]) > 4:
|
|
330
|
+
# print('tick noticed a todo needing refreshment')
|
|
331
|
+
self.async_refresh(None,conf.user['todolists'][todo])
|
|
332
|
+
conf.todo_sync_required = {}
|
|
321
333
|
return True
|
|
322
334
|
|
|
323
335
|
|
|
@@ -414,6 +426,7 @@ class Application(Gtk.Application):
|
|
|
414
426
|
|
|
415
427
|
if process:
|
|
416
428
|
print("waiting for already running command with psutils", command)
|
|
429
|
+
# subprocess.run('wmctrl', '-a', command) # Doesn't work on wayland
|
|
417
430
|
process.wait()
|
|
418
431
|
else:
|
|
419
432
|
print("Launching command with subprocess.run", command)
|
|
@@ -428,13 +441,23 @@ class Application(Gtk.Application):
|
|
|
428
441
|
|
|
429
442
|
def mark_done(self, w=None, task = None):
|
|
430
443
|
''' second (task) argument is required and must be a task object '''
|
|
444
|
+
|
|
431
445
|
print("Mark Task done")
|
|
432
446
|
print(task)
|
|
433
447
|
|
|
434
448
|
todolist_conf = conf.user['todolists'][task['todolist']]
|
|
435
449
|
|
|
436
450
|
try:
|
|
451
|
+
|
|
437
452
|
done_thread = threading.Thread(target=conf.todo_connectors[todolist_conf['type']].mark_task_done, args=(task,) )
|
|
453
|
+
conf.todo_sync_times[todolist_conf['id']] = now() # this is to avoid causing a refresh, perhaps not the best though
|
|
454
|
+
|
|
455
|
+
# Other Options:
|
|
456
|
+
# make a custom class extending Thread with callback method that runs del conf.file_watch_ignores[todolist_conf['id']]
|
|
457
|
+
# Complicated
|
|
458
|
+
# deal with file_watch_ignores in the connector
|
|
459
|
+
# poor seperation
|
|
460
|
+
#
|
|
438
461
|
done_thread.start()
|
|
439
462
|
|
|
440
463
|
db_query("UPDATE tasks set status = '0' WHERE id = ? ",(task['id'],) )
|
|
@@ -451,7 +474,7 @@ class Application(Gtk.Application):
|
|
|
451
474
|
|
|
452
475
|
|
|
453
476
|
def stop_task(self, w = '', action = 'save', custom_end_time=None):
|
|
454
|
-
''' supported actions are save,cancel,mark_done '''
|
|
477
|
+
''' supported actions are save, cancel, mark_done '''
|
|
455
478
|
|
|
456
479
|
list_menus = self.list_menus
|
|
457
480
|
menu_tasks = self.menu_tasks
|
|
@@ -540,7 +563,6 @@ class Application(Gtk.Application):
|
|
|
540
563
|
i = Gtk.ImageMenuItem.new_with_label("Edit Session")
|
|
541
564
|
i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/edit.svg')))
|
|
542
565
|
|
|
543
|
-
# i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/status/software-installed-symbolic.svg'))
|
|
544
566
|
i.set_always_show_image(True)
|
|
545
567
|
i.connect("activate", self.open_session_options_dialog,'from_menu')
|
|
546
568
|
self.menu.insert(i,0)
|
|
@@ -548,33 +570,34 @@ class Application(Gtk.Application):
|
|
|
548
570
|
i = Gtk.ImageMenuItem.new_with_label("Pause" )
|
|
549
571
|
i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/pause.svg')))
|
|
550
572
|
|
|
551
|
-
# i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/multimedia/pause-symbolic.svg'))
|
|
552
573
|
i.set_always_show_image(True)
|
|
553
574
|
i.connect("activate", self.stop_task)
|
|
554
575
|
self.menu.insert(i,0)
|
|
555
576
|
|
|
556
577
|
|
|
557
|
-
def async_refresh(self, w=None):
|
|
578
|
+
def async_refresh(self, w=None, single_todo = None):
|
|
558
579
|
|
|
559
580
|
self.indicator.set_label("Refreshing Todolists", "Wide")
|
|
560
581
|
menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
|
|
561
582
|
self.menu.append(menu_item)
|
|
562
583
|
self.menu.show_all()
|
|
563
584
|
|
|
564
|
-
connectors_thread = threading.Thread(target=self.async_refresh_inner)
|
|
585
|
+
connectors_thread = threading.Thread(target=self.async_refresh_inner,args=(single_todo,))
|
|
565
586
|
connectors_thread.start()
|
|
566
587
|
|
|
567
588
|
|
|
568
|
-
def async_refresh_inner(self):
|
|
589
|
+
def async_refresh_inner(self, single_todo = None):
|
|
569
590
|
# dbg("async refresh started",s="todoloading",l=3)
|
|
570
|
-
|
|
591
|
+
if single_todo:
|
|
592
|
+
utils.refresh_todolist(single_todo)
|
|
593
|
+
utils.reindex()
|
|
594
|
+
|
|
595
|
+
else:
|
|
596
|
+
utils.get_todolists()
|
|
597
|
+
|
|
571
598
|
dbg("async refresh complete",s="todoloading",l=3)
|
|
572
599
|
GLib.idle_add(self.update_menu)
|
|
573
600
|
|
|
574
|
-
try:
|
|
575
|
-
GLib.idle_add(self.taskwindow.refresh_search_cache)
|
|
576
|
-
except Exception:
|
|
577
|
-
pass
|
|
578
601
|
|
|
579
602
|
|
|
580
603
|
def update_menu(self, w = ''):
|
|
@@ -724,10 +747,10 @@ class Application(Gtk.Application):
|
|
|
724
747
|
dbg("no handler for received signal",s='signals',l=3)
|
|
725
748
|
|
|
726
749
|
def check_pipe(self):
|
|
727
|
-
# print("Listening to pipe at ",
|
|
750
|
+
# print("Listening to pipe at ",conf.pipe)
|
|
728
751
|
|
|
729
752
|
try:
|
|
730
|
-
with open(
|
|
753
|
+
with open(conf.pipe, "r") as pipe:
|
|
731
754
|
data = pipe.read().strip()
|
|
732
755
|
|
|
733
756
|
print("check_pipe ")
|
|
@@ -736,10 +759,8 @@ class Application(Gtk.Application):
|
|
|
736
759
|
pipe.close()
|
|
737
760
|
|
|
738
761
|
# TODO: add registry of special commands
|
|
762
|
+
# How to handle function arguments? (for example refresh a todolist with it's id)
|
|
739
763
|
# if data in ['quit','open_task_window']:
|
|
740
|
-
# TODO: how to all dynamic function name here?
|
|
741
|
-
# GLib.idle_add(self.open_task_window)
|
|
742
|
-
|
|
743
764
|
|
|
744
765
|
if data == 'quit':
|
|
745
766
|
GLib.idle_add(self.quit)
|
|
@@ -792,13 +813,13 @@ def startup():
|
|
|
792
813
|
if args.force:
|
|
793
814
|
print("Lanched with --force flag, forcibly deleting old pipe")
|
|
794
815
|
try:
|
|
795
|
-
os.remove(
|
|
816
|
+
os.remove(conf.pipe)
|
|
796
817
|
except Exception as e:
|
|
797
818
|
print(e)
|
|
798
819
|
|
|
799
820
|
|
|
800
821
|
try:
|
|
801
|
-
os.mkfifo(
|
|
822
|
+
os.mkfifo(conf.pipe)
|
|
802
823
|
dbg("Named pipe created successfully!", s="cli")
|
|
803
824
|
|
|
804
825
|
signal.signal(signal.SIGUSR1, Application.signal_handler)
|
|
@@ -806,7 +827,7 @@ def startup():
|
|
|
806
827
|
|
|
807
828
|
if args.task:
|
|
808
829
|
print("Writing args.task to pipe", args.task)
|
|
809
|
-
with open(
|
|
830
|
+
with open(conf.pipe, "w") as pipeout:
|
|
810
831
|
pipeout.write(args.task)
|
|
811
832
|
pipeout.close()
|
|
812
833
|
|
|
@@ -824,7 +845,7 @@ def startup():
|
|
|
824
845
|
|
|
825
846
|
dbg("Writing arg to pipe ",pipe_line, s="cli")
|
|
826
847
|
|
|
827
|
-
with open(
|
|
848
|
+
with open(conf.pipe, "w") as pipeout:
|
|
828
849
|
pipeout.write(pipe_line)
|
|
829
850
|
pipeout.close()
|
|
830
851
|
|
nowfocus/conf.py
CHANGED
|
@@ -22,20 +22,24 @@ Path(user_settings_dir).mkdir(parents=True, exist_ok=True)
|
|
|
22
22
|
|
|
23
23
|
debug_level = 1 # dev value
|
|
24
24
|
debug_systems = []
|
|
25
|
+
pipe = "/tmp/"+app_id+"-pipe" # Will that work?
|
|
25
26
|
|
|
26
27
|
# key and type must be the same, (Seems redundant but it's is quite helpful)
|
|
27
28
|
connectors = {
|
|
28
29
|
"todolists":{
|
|
29
|
-
'txt':{'id':'txt','type':'txt','label':'','file':'',"status":True,'timetracker':''},
|
|
30
|
+
'txt':{'id':'txt','type':'txt','label':'','file':'', "watch_file":True, "status":True,'timetracker':''},
|
|
30
31
|
|
|
31
32
|
'trello':{'id':'trello','type':'trello','label':'Trello','APIKey':'aecf1d7791b4f1a4bb7d6ca5827ba0d3', 'token':'',"status":True,'timetracker':''},
|
|
32
33
|
|
|
33
34
|
'vikunja':{'id':'vikunja','type':'vikunja','label':'Vikunja','url':'http://localhost:3456/', 'token':'',"username":"","status":True,'timetracker':''},
|
|
34
35
|
|
|
35
36
|
'caldav':{'id':'caldav','type':'caldav','label':'CalDav Todo','url':'http://localhost:3456/','password': "",'username': '',"status":True,'timetracker':''},
|
|
37
|
+
|
|
36
38
|
'psc_timetracker':{'id':'psc_timetracker','type':'psc_timetracker','label':'Timetracker','url':'https://photosynth.ca/timetracker/','key':'',"status":True,'timetracker':'Timetracker'},
|
|
39
|
+
|
|
37
40
|
'taskwarrior':{'id':'taskwarrior','type':'taskwarrior','label':'TaskWarrior',"status":True,'timetracker':''},
|
|
38
|
-
|
|
41
|
+
|
|
42
|
+
'todotxt':{'id':'todotxt','type':'todotxt','label':'','file':"", "watch_file":True, "status":True,'timetracker':''},
|
|
39
43
|
},
|
|
40
44
|
"timetrackers":{
|
|
41
45
|
'csv':{'id':'csv','type':'csv','label':'CSV file','file':'sessions.csv',"status":True},
|
|
@@ -46,18 +50,21 @@ connectors = {
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
todo_sync_time = datetime.now()
|
|
53
|
+
todo_sync_times = {}
|
|
49
54
|
|
|
50
55
|
todo_sync_required = {}
|
|
56
|
+
file_watchers = {}
|
|
57
|
+
file_watch_ignores = {}
|
|
51
58
|
|
|
52
59
|
prototype_settings = {
|
|
53
|
-
"pomodoro_interval":
|
|
60
|
+
"pomodoro_interval": 40,
|
|
54
61
|
"open_task_window_fullscreen": True,
|
|
55
62
|
"randomness_interrupt_interval":5,
|
|
56
63
|
"default_text": "What am I doing?",
|
|
57
|
-
"todolist_refresh_interval":
|
|
64
|
+
"todolist_refresh_interval":1,
|
|
58
65
|
"version":0.2,
|
|
59
66
|
"display_todolist_as_top_level_list":'auto',
|
|
60
|
-
'max_top_level_menu_items':
|
|
67
|
+
'max_top_level_menu_items':10,
|
|
61
68
|
# 'tick_interval':1,
|
|
62
69
|
'hours_search_timeframe':'this year',
|
|
63
70
|
'invoice_hourly_rate':0,
|
nowfocus/connectors/todotxt.py
CHANGED
|
@@ -20,24 +20,6 @@ import utils
|
|
|
20
20
|
# }
|
|
21
21
|
# }
|
|
22
22
|
|
|
23
|
-
# def watch(user_conf):
|
|
24
|
-
# import pyinotify
|
|
25
|
-
|
|
26
|
-
# """Watch for modifications of the todo file with pyinotify."""
|
|
27
|
-
# wm = pyinotify.WatchManager()
|
|
28
|
-
# notifier = pyinotify.ThreadedNotifier(wm, mark_changed, user_conf)
|
|
29
|
-
# notifier.start()
|
|
30
|
-
|
|
31
|
-
# # wm.add_watch(os.path.dirname(user_conf['file']), pyinotify.IN_MODIFY | pyinotify.IN_MOVED_TO) # Watch whole folder. Perhaps more reliable
|
|
32
|
-
# wm.add_watch(user_conf['file'], pyinotify.IN_MODIFY | pyinotify.IN_MOVED_TO) # just the file. Perhaps better
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# def mark_changed(event, user_conf):
|
|
36
|
-
# ''' This is in a thread '''
|
|
37
|
-
# if event.pathname == user_conf['file']:
|
|
38
|
-
# conf.todo_sync_required[user_conf['id']] = True
|
|
39
|
-
|
|
40
|
-
|
|
41
23
|
|
|
42
24
|
def task_id(user_conf, t):
|
|
43
25
|
''' The task parameter must have at least "label", "todolist" and "parent_id" '''
|
|
@@ -71,7 +53,8 @@ def add_new_task(user_conf,list,task_label):
|
|
|
71
53
|
}
|
|
72
54
|
}
|
|
73
55
|
return t
|
|
74
|
-
|
|
56
|
+
|
|
57
|
+
|
|
75
58
|
def mark_task_done(task):
|
|
76
59
|
'''Return True on success False on error'''
|
|
77
60
|
|
nowfocus/connectors/txt.py
CHANGED
|
@@ -11,24 +11,32 @@ def add_new_task(user_conf,list,task_label):
|
|
|
11
11
|
# read a list of lines into data
|
|
12
12
|
data = file.readlines()
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
# New Version
|
|
15
|
+
|
|
16
|
+
line_no = get_line_no(list,data) + 1
|
|
17
|
+
new_line = list['data']['indent']+task_label+"\n"
|
|
18
|
+
|
|
19
|
+
# # Old version without recorded indent
|
|
20
|
+
|
|
21
|
+
# if list['id'] == user_conf['id']:
|
|
22
|
+
# # Top level insert
|
|
23
|
+
# new_line = task_label+"\n"
|
|
24
|
+
# line_no = len(data) + 1
|
|
25
|
+
# else:
|
|
26
|
+
# Sub list insert
|
|
27
|
+
# TODO: Add it at the end of the list rather than the start ...
|
|
28
|
+
# Yup. rjust(indent + len)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# indent = len(list['data']['original_line']) - len(list['data']['original_line'].lstrip()) + 4 #BUG: check indent of next line instead of arbitrarily using 4 spaces
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
# new_line = task_label+"\n"
|
|
34
|
+
# # I'm sure there's a better way to add a bunch of spaces to to a string
|
|
35
|
+
# while indent > 0:
|
|
36
|
+
# new_line = " " + new_line
|
|
37
|
+
# indent = indent - 1
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
# line_no = get_line_no(list,data) + 1
|
|
32
40
|
|
|
33
41
|
data.insert(line_no,new_line)
|
|
34
42
|
|
|
@@ -128,7 +136,8 @@ def get_todos(user_conf):
|
|
|
128
136
|
'data':{
|
|
129
137
|
'line_no':0,
|
|
130
138
|
'original_line':'',
|
|
131
|
-
'accepts_tasks':True
|
|
139
|
+
'accepts_tasks':True,
|
|
140
|
+
'indent':'',
|
|
132
141
|
}
|
|
133
142
|
}
|
|
134
143
|
|
|
@@ -149,7 +158,7 @@ def get_todos(user_conf):
|
|
|
149
158
|
# file_uri = "todo.txt"
|
|
150
159
|
line_no = -1
|
|
151
160
|
|
|
152
|
-
with open(file_uri) as file:
|
|
161
|
+
with open(file_uri,"r") as file:
|
|
153
162
|
for line in file:
|
|
154
163
|
|
|
155
164
|
line_no = line_no + 1
|
|
@@ -171,11 +180,13 @@ def get_todos(user_conf):
|
|
|
171
180
|
#TODO: use markdown title syntax as list name in addition to indentation
|
|
172
181
|
|
|
173
182
|
indent = len(line) - len(line.lstrip())
|
|
183
|
+
indent_str = line[0:indent]
|
|
174
184
|
|
|
175
185
|
if indent > prev_indent:
|
|
176
|
-
# prev item is a sub_list header,
|
|
186
|
+
# prev item is a sub_list header, copy it to lists
|
|
177
187
|
lists[prev_id] = tasks[prev_id]
|
|
178
|
-
|
|
188
|
+
lists[prev_id]['data']['indent'] = indent_str
|
|
189
|
+
# del tasks[prev_id]
|
|
179
190
|
current_list.append(prev_id)
|
|
180
191
|
current_list_label.append(prev_label)
|
|
181
192
|
current_indent.append(indent)
|
nowfocus/connectors/vikunja.py
CHANGED
|
@@ -294,14 +294,16 @@ def get_todos(user_conf):
|
|
|
294
294
|
return todos
|
|
295
295
|
|
|
296
296
|
|
|
297
|
-
def launch(user_conf, item = None, category =
|
|
297
|
+
def launch(user_conf, item = None, category = 'task'):
|
|
298
298
|
''' Open Vikunja '''
|
|
299
|
+
utils.dbg("vikunja.py launch","user_conf", user_conf,"item",item,'category',category,l=3, s='vikunja')
|
|
300
|
+
|
|
299
301
|
url = user_conf['url']
|
|
300
302
|
|
|
301
|
-
if item
|
|
302
|
-
if category
|
|
303
|
+
if item:
|
|
304
|
+
if category in ('list','lists'):
|
|
303
305
|
url += 'projects/'+str(item['data']['id'])
|
|
304
|
-
|
|
306
|
+
else:
|
|
305
307
|
url += 'tasks/'+str(item['data']['id'])
|
|
306
308
|
|
|
307
309
|
utils.open_external(url)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
width="48"
|
|
6
|
+
height="48"
|
|
7
|
+
viewBox="0 0 12.7 12.7"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
12
|
+
<defs
|
|
13
|
+
id="defs1" />
|
|
14
|
+
<g
|
|
15
|
+
id="layer1"
|
|
16
|
+
transform="matrix(1.0567177,0,0,1.0567177,-0.36015771,-0.36015771)">
|
|
17
|
+
<rect
|
|
18
|
+
style="display:none;opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.316;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
19
|
+
id="rect1"
|
|
20
|
+
width="9.5708742"
|
|
21
|
+
height="9.5708742"
|
|
22
|
+
x="1.5645629"
|
|
23
|
+
y="1.5645629" />
|
|
24
|
+
<rect
|
|
25
|
+
style="display:none;opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.915798;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
26
|
+
id="rect38"
|
|
27
|
+
width="9.5708742"
|
|
28
|
+
height="9.5708742"
|
|
29
|
+
x="1.5645629"
|
|
30
|
+
y="1.5645629"
|
|
31
|
+
ry="3.110863" />
|
|
32
|
+
<rect
|
|
33
|
+
style="display:inline;opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.915798;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
34
|
+
id="rect2"
|
|
35
|
+
width="9.5708742"
|
|
36
|
+
height="9.5708742"
|
|
37
|
+
x="1.5645629"
|
|
38
|
+
y="1.5645629"
|
|
39
|
+
ry="4.7854371"
|
|
40
|
+
rx="4.7854371" />
|
|
41
|
+
</g>
|
|
42
|
+
<g
|
|
43
|
+
id="layer2"
|
|
44
|
+
style="display:inline">
|
|
45
|
+
<path
|
|
46
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
47
|
+
d="M 6.3410664,6.36546 4.4335013,4.0522236"
|
|
48
|
+
id="path2" />
|
|
49
|
+
<path
|
|
50
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
51
|
+
d="M 6.37713,6.3615649 11.260009,1.2965066"
|
|
52
|
+
id="use31" />
|
|
53
|
+
<path
|
|
54
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
55
|
+
d="M 6.3610066,6.3773634 13.39526,6.248543"
|
|
56
|
+
id="use32" />
|
|
57
|
+
<path
|
|
58
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
59
|
+
d="m 6.338434,6.3771308 5.065058,4.8828782"
|
|
60
|
+
id="use33" />
|
|
61
|
+
<path
|
|
62
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
63
|
+
d="M 6.322637,6.3610073 6.4514574,13.395261"
|
|
64
|
+
id="use34" />
|
|
65
|
+
<path
|
|
66
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
67
|
+
d="M 6.322869,6.3384356 1.4399903,11.403494"
|
|
68
|
+
id="use35" />
|
|
69
|
+
<path
|
|
70
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
71
|
+
d="M 6.3389932,6.3226376 -0.69526049,6.451458"
|
|
72
|
+
id="use36" />
|
|
73
|
+
<path
|
|
74
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
75
|
+
d="M 6.3615647,6.3228685 1.2965064,1.4399898"
|
|
76
|
+
id="use37" />
|
|
77
|
+
<path
|
|
78
|
+
style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
79
|
+
d="M 6.3773615,6.3389927 6.2485411,-0.69526102"
|
|
80
|
+
id="use38" />
|
|
81
|
+
</g>
|
|
82
|
+
<g
|
|
83
|
+
id="layer3"
|
|
84
|
+
style="display:inline">
|
|
85
|
+
<rect
|
|
86
|
+
style="display:inline;opacity:1;fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:0.915798;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
87
|
+
id="rect3"
|
|
88
|
+
width="9.5708742"
|
|
89
|
+
height="9.5708742"
|
|
90
|
+
x="1.5645629"
|
|
91
|
+
y="1.5645629"
|
|
92
|
+
ry="4.7854371"
|
|
93
|
+
rx="4.7854371"
|
|
94
|
+
transform="matrix(1.0567177,0,0,1.0567177,-0.36015771,-0.36015771)" />
|
|
95
|
+
<path
|
|
96
|
+
style="font-weight:bold;font-size:10.4396px;line-height:1.25;font-family:'Besley*';-inkscape-font-specification:'Besley*, Bold';letter-spacing:0px;word-spacing:0px;fill:#ffffff;stroke-width:0.198419"
|
|
97
|
+
d="m 5.2723149,3.6126858 c 0,-0.040213 0.03016,-0.080427 0.1005332,-0.1407465 0.06032,-0.06032 0.1709064,-0.1105865 0.3116529,-0.1507998 0.1407465,-0.040213 0.2915463,-0.070373 0.4624528,-0.070373 0.4121862,0 0.6936791,0.1105865 0.8545323,0.3317595 0.1507998,0.2211731 0.2312264,0.5428794 0.2312264,0.9550655 0,0.4222395 -0.1206399,0.7640524 -0.3518663,1.0254388 -0.2412797,0.2613863 -0.5830926,0.4825594 -1.0254387,0.6434125 -0.03016,0.020106 -0.080427,0.040213 -0.1608531,0.06032 -0.080427,0.020106 -0.1306932,0.040213 -0.1407465,0.050267 -0.020106,0.010054 -0.040213,0.03016 -0.080427,0.070373 -0.040213,0.040213 -0.06032,0.080427 -0.06032,0.1407464 -0.010054,0.06032 -0.010054,0.1306932 -0.010054,0.2211731 v 0.8243723 c 0,0.080427 0.010054,0.1407465 0.050267,0.1608532 0.03016,0.020106 0.1005332,0.03016 0.2211731,0.03016 h 0.9852264 c 0.1608531,0 0.2613863,-0.010054 0.3015996,-0.040213 C 6.991433,7.6943348 7.01154,7.603855 7.01154,7.4430018 V 6.9906026 c 0,-0.070373 0,-0.1306932 0.010054,-0.1809598 0,-0.040213 0.010054,-0.080427 0.020106,-0.1105865 0.010054,-0.020106 0.03016,-0.050267 0.06032,-0.070373 0.03016,-0.020106 0.050267,-0.03016 0.070373,-0.040213 0.020106,0 0.050267,-0.010054 0.1105865,-0.03016 0.06032,-0.010054 0.1005332,-0.020106 0.1407465,-0.040213 C 7.9263916,6.3572424 8.3184711,6.1159627 8.6100174,5.7842031 8.9015637,5.4624969 9.0523635,5.0402574 9.0523635,4.5375913 c 0,-0.6233059 -0.221173,-1.1159186 -0.6635191,-1.4778381 -0.4423462,-0.3518663 -1.1159187,-0.532826 -2.0408242,-0.532826 -0.8042657,0 -1.4376249,0.2111197 -1.8900243,0.6132525 -0.4624528,0.4121862 -0.6836258,0.8746389 -0.6836258,1.3873583 0,0.3418129 0.090479,0.6132526 0.2915463,0.814319 0.2010664,0.2010664 0.4423461,0.3015996 0.7338924,0.3015996 0.2714397,0 0.502666,-0.080427 0.6936791,-0.251333 0.1809598,-0.1709064 0.281493,-0.3920795 0.281493,-0.6534658 0,-0.2010665 -0.050267,-0.3820262 -0.1306931,-0.5428794 C 5.5638612,4.0349253 5.4733813,3.9142854 5.3929547,3.8238056 5.3125282,3.7333257 5.2723149,3.6629524 5.2723149,3.6126858 Z m -0.050267,5.8510328 c 0,0.2814929 0.090479,0.5127198 0.2915463,0.7137864 0.2010664,0.201066 0.4322928,0.291546 0.7137858,0.291546 0.2714396,0 0.502666,-0.09048 0.7037324,-0.291546 C 7.1321792,9.9764384 7.2327125,9.7452115 7.2327125,9.4637186 7.2327125,9.1922789 7.1321792,8.9610525 6.9311128,8.7599861 6.7300464,8.5589197 6.49882,8.4583865 6.2273804,8.4583865 c -0.281493,0 -0.5127194,0.1005332 -0.7137858,0.3015996 C 5.3125282,8.9610525 5.2220483,9.1922789 5.2220479,9.4637186 Z"
|
|
98
|
+
id="text38"
|
|
99
|
+
aria-label="?" />
|
|
100
|
+
</g>
|
|
101
|
+
</svg>
|
nowfocus/icon/icon.svg
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
id="svg1"
|
|
10
10
|
inkscape:version="1.3.2 (1:1.3.2+202404261509+091e20ef0f)"
|
|
11
11
|
sodipodi:docname="icon.svg"
|
|
12
|
-
inkscape:export-filename="icon-
|
|
12
|
+
inkscape:export-filename="icon-red.svg"
|
|
13
13
|
inkscape:export-xdpi="128"
|
|
14
14
|
inkscape:export-ydpi="128"
|
|
15
15
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
@@ -185,6 +185,17 @@
|
|
|
185
185
|
id="layer3"
|
|
186
186
|
inkscape:label="?"
|
|
187
187
|
style="display:inline">
|
|
188
|
+
<rect
|
|
189
|
+
style="display:inline;opacity:1;fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:0.915798;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
|
190
|
+
id="rect3"
|
|
191
|
+
width="9.5708742"
|
|
192
|
+
height="9.5708742"
|
|
193
|
+
x="1.5645629"
|
|
194
|
+
y="1.5645629"
|
|
195
|
+
inkscape:label="Red"
|
|
196
|
+
ry="4.7854371"
|
|
197
|
+
rx="4.7854371"
|
|
198
|
+
transform="matrix(1.0567177,0,0,1.0567177,-0.36015771,-0.36015771)" />
|
|
188
199
|
<path
|
|
189
200
|
style="font-weight:bold;font-size:10.4396px;line-height:1.25;font-family:'Besley*';-inkscape-font-specification:'Besley*, Bold';letter-spacing:0px;word-spacing:0px;fill:#ffffff;stroke-width:0.198419"
|
|
190
201
|
d="m 5.2723149,3.6126858 c 0,-0.040213 0.03016,-0.080427 0.1005332,-0.1407465 0.06032,-0.06032 0.1709064,-0.1105865 0.3116529,-0.1507998 0.1407465,-0.040213 0.2915463,-0.070373 0.4624528,-0.070373 0.4121862,0 0.6936791,0.1105865 0.8545323,0.3317595 0.1507998,0.2211731 0.2312264,0.5428794 0.2312264,0.9550655 0,0.4222395 -0.1206399,0.7640524 -0.3518663,1.0254388 -0.2412797,0.2613863 -0.5830926,0.4825594 -1.0254387,0.6434125 -0.03016,0.020106 -0.080427,0.040213 -0.1608531,0.06032 -0.080427,0.020106 -0.1306932,0.040213 -0.1407465,0.050267 -0.020106,0.010054 -0.040213,0.03016 -0.080427,0.070373 -0.040213,0.040213 -0.06032,0.080427 -0.06032,0.1407464 -0.010054,0.06032 -0.010054,0.1306932 -0.010054,0.2211731 v 0.8243723 c 0,0.080427 0.010054,0.1407465 0.050267,0.1608532 0.03016,0.020106 0.1005332,0.03016 0.2211731,0.03016 h 0.9852264 c 0.1608531,0 0.2613863,-0.010054 0.3015996,-0.040213 C 6.991433,7.6943348 7.01154,7.603855 7.01154,7.4430018 V 6.9906026 c 0,-0.070373 0,-0.1306932 0.010054,-0.1809598 0,-0.040213 0.010054,-0.080427 0.020106,-0.1105865 0.010054,-0.020106 0.03016,-0.050267 0.06032,-0.070373 0.03016,-0.020106 0.050267,-0.03016 0.070373,-0.040213 0.020106,0 0.050267,-0.010054 0.1105865,-0.03016 0.06032,-0.010054 0.1005332,-0.020106 0.1407465,-0.040213 C 7.9263916,6.3572424 8.3184711,6.1159627 8.6100174,5.7842031 8.9015637,5.4624969 9.0523635,5.0402574 9.0523635,4.5375913 c 0,-0.6233059 -0.221173,-1.1159186 -0.6635191,-1.4778381 -0.4423462,-0.3518663 -1.1159187,-0.532826 -2.0408242,-0.532826 -0.8042657,0 -1.4376249,0.2111197 -1.8900243,0.6132525 -0.4624528,0.4121862 -0.6836258,0.8746389 -0.6836258,1.3873583 0,0.3418129 0.090479,0.6132526 0.2915463,0.814319 0.2010664,0.2010664 0.4423461,0.3015996 0.7338924,0.3015996 0.2714397,0 0.502666,-0.080427 0.6936791,-0.251333 0.1809598,-0.1709064 0.281493,-0.3920795 0.281493,-0.6534658 0,-0.2010665 -0.050267,-0.3820262 -0.1306931,-0.5428794 C 5.5638612,4.0349253 5.4733813,3.9142854 5.3929547,3.8238056 5.3125282,3.7333257 5.2723149,3.6629524 5.2723149,3.6126858 Z m -0.050267,5.8510328 c 0,0.2814929 0.090479,0.5127198 0.2915463,0.7137864 0.2010664,0.201066 0.4322928,0.291546 0.7137858,0.291546 0.2714396,0 0.502666,-0.09048 0.7037324,-0.291546 C 7.1321792,9.9764384 7.2327125,9.7452115 7.2327125,9.4637186 7.2327125,9.1922789 7.1321792,8.9610525 6.9311128,8.7599861 6.7300464,8.5589197 6.49882,8.4583865 6.2273804,8.4583865 c -0.281493,0 -0.5127194,0.1005332 -0.7137858,0.3015996 C 5.3125282,8.9610525 5.2220483,9.1922789 5.2220479,9.4637186 Z"
|
nowfocus/new_task_dialog.py
CHANGED
|
@@ -77,7 +77,7 @@ class NewTaskWDialog(Gtk.Dialog):
|
|
|
77
77
|
|
|
78
78
|
if response == 1 or response == 2:
|
|
79
79
|
|
|
80
|
-
task_label = self.task_label_entry.get_text()
|
|
80
|
+
task_label = self.task_label_entry.get_text().strip()
|
|
81
81
|
|
|
82
82
|
if not task_label:
|
|
83
83
|
error_notice("Please Enter a name before saving new task")
|
|
@@ -86,7 +86,10 @@ class NewTaskWDialog(Gtk.Dialog):
|
|
|
86
86
|
parent_list = self.selected_list
|
|
87
87
|
todolist_conf = conf.user['todolists'][parent_list['todolist']]
|
|
88
88
|
try:
|
|
89
|
+
|
|
90
|
+
conf.file_watch_ignores[todolist_conf['id']] = True
|
|
89
91
|
task = conf.todo_connectors[todolist_conf['type']].add_new_task(todolist_conf,parent_list,task_label)
|
|
92
|
+
del conf.file_watch_ignores[todolist_conf['id']]
|
|
90
93
|
|
|
91
94
|
dbg('connector add task response',task)
|
|
92
95
|
|