nowfocus 0.2.13__py3-none-any.whl → 0.4.0__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/__main__.py CHANGED
@@ -13,6 +13,7 @@ from playsound3 import playsound
13
13
  import setproctitle
14
14
  import psutil
15
15
  import argparse
16
+ import traceback
16
17
 
17
18
  import gi
18
19
  gi.require_version('Gtk', '3.0')
@@ -28,7 +29,7 @@ except Exception as e:
28
29
  gi.require_version('AppIndicator3', '0.1')
29
30
  from gi.repository import AppIndicator3 as appindicator
30
31
 
31
- from dbus_idle import IdleMonitor
32
+ # from dbus_idle import IdleMonitor
32
33
 
33
34
  # Set working dir to file location
34
35
  os.chdir(os.path.dirname(os.path.realpath(__file__)))
@@ -39,6 +40,8 @@ sys.path.append(os.path.dirname(__file__))
39
40
  # from . import conf # this works in module context but not running as pile-of-files
40
41
  import conf # this works running as pile-of-files but not in module context without sys.path.append
41
42
 
43
+ from user_idle_time import UserIdleTime
44
+
42
45
  import utils
43
46
  from utils import *
44
47
 
@@ -55,19 +58,21 @@ print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file_
55
58
 
56
59
  class Application(Gtk.Application):
57
60
  def __init__(self, *args, **kwargs):
58
- super().__init__(*args, application_id="org.example.myapp", **kwargs)
61
+ super().__init__(*args, application_id="org.nowfocus.nowfocus", **kwargs)
62
+
63
+ # try:
64
+ # # To put everything here...
65
+ # # this doesn't work because as soon as an exception occurs ii jumps to the handler and breaks all the following code.
59
66
 
67
+ # except Exception as e:
68
+ # print(conf.app_name +' had a pretty bad error. Please submit the following trace as an issue on the git repo or email it to the developers ')
69
+ # traceback.print_tb(e.__traceback__)
70
+ # return None
71
+
60
72
  self.window = None
61
73
 
62
74
  self.is_running = False
63
- self.session = {
64
- "label":"Randomness",
65
- "extended_label": 'Randomness',
66
- "start_time":datetime.now(),
67
- "duration":0,
68
- 'task':{},
69
- 'notes':'',
70
- }
75
+ self.session = default_session()
71
76
 
72
77
  self.menu_tasks = {}
73
78
  self.list_menus = {}
@@ -80,7 +85,7 @@ class Application(Gtk.Application):
80
85
  # menu.set_reserve_toggle_size(False) # skip menu left padding, doesn't work
81
86
 
82
87
  utils.db_init()
83
- utils.db_update()
88
+ utils.db_schema_update()
84
89
 
85
90
 
86
91
  # self.update_menu()
@@ -90,7 +95,6 @@ class Application(Gtk.Application):
90
95
 
91
96
  self.indicator.set_menu(self.menu)
92
97
 
93
- # main_tick_timer = GLib.timeout_add_seconds(conf.user['tick_interval'], self.tick)
94
98
  main_tick_timer = GLib.timeout_add_seconds(1, self.tick)
95
99
 
96
100
  try:
@@ -105,7 +109,7 @@ class Application(Gtk.Application):
105
109
  except Exception as e:
106
110
  dbg("Error resuming session",e,l=1)
107
111
 
108
- utils.start_todo_file_watchers()
112
+ self.UserIdleTime = UserIdleTime()
109
113
 
110
114
  self.pipethread = threading.Thread(target=self.check_pipe)
111
115
  self.pipethread.daemon = True
@@ -157,39 +161,40 @@ class Application(Gtk.Application):
157
161
 
158
162
  def quit(self, widget_or_signal_source=None, condition=None):
159
163
  print("Adios ", conf.app_name)
160
- # print("widget_or_signal_source ", widget_or_signal_source)
161
- # print("condition ", condition)
162
164
 
163
165
  if self.is_running:
164
- print('Caching active session', self.session['label'])
166
+ dbg('Caching active session', self.session['label'])
165
167
  db_set_session_cache(self.session)
168
+
166
169
  try:
167
- # print("before os.remove(conf.pipe)")
168
170
  os.remove(conf.pipe)
169
- print("Pipe removed")
170
171
  except Exception as e:
171
- print("Error removing conf.pipe in quit",e)
172
+ dbd("Error removing conf.pipe in quit",e)
172
173
 
173
174
  notify.uninit()
174
175
  Gtk.main_quit()
175
176
  exit()
176
177
 
177
178
 
178
- def check_afk_time(self):
179
+ def seconds_since_user_active(self):
179
180
  # returns seconds of inactivity
180
- # See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux or
181
+ # See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux or
181
182
 
182
183
  # Works on x11 but not wayland
183
184
  # Requires xprintidle (sudo apt install xprintidle)
184
185
  # idle_time = int(int(subprocess.getoutput('xprintidle')) / 1000)
185
186
 
186
- # Currently using: https://github.com/bkbilly/dbus_idle
187
+ # Version that works, using: https://github.com/bkbilly/dbus_idle
188
+ # but has many deps
187
189
  # Requires:
188
190
  # sudo apt install meson libdbus-glib-1-dev patchelf
189
191
  # pip install dbus-idle
190
192
 
191
- idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
192
- return idle_time
193
+ # idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
194
+ # return idle_time
195
+
196
+ return self.UserIdleTime.get()
197
+
193
198
 
194
199
 
195
200
  def toggle_do_not_disturb(self, widget):
@@ -210,7 +215,7 @@ class Application(Gtk.Application):
210
215
  if 'do-not-disturb' in self.session:
211
216
  return None
212
217
 
213
- afk_time = self.check_afk_time()
218
+ afk_time = self.seconds_since_user_active()
214
219
  # print("Idle time: "+str(afk_time))
215
220
 
216
221
  dbg('Last todo todo_sync_time', conf.todo_sync_time, 'Time diff',int(time_difference(conf.todo_sync_time)),'Auto refresh interval * 60', (conf.user['todolist_refresh_interval'] * 60), s="todoloading", l=3 )
@@ -224,7 +229,7 @@ class Application(Gtk.Application):
224
229
 
225
230
  if(self.is_running == False):
226
231
 
227
- if float(minutes / conf.user['randomness_interrupt_interval']).is_integer():
232
+ if num_is_multiple_of(minutes,conf.user['randomness_interrupt_interval']):
228
233
  if afk_time < 30:
229
234
 
230
235
  notify.Notification.new("What Am I doing?","Your randomness timer is at "+str(minutes)+" minutes. ", None).show()
@@ -243,7 +248,6 @@ class Application(Gtk.Application):
243
248
  # only show this once
244
249
  if afk_time < 181:
245
250
  self.open_task_window(None,{'afk_time':afk_time})
246
- # session_options_dialog(None, 'test input_data')
247
251
  else:
248
252
  if self.session['label'] in conf.user['custom_pomodoro_intervals']:
249
253
  check = conf.user['custom_pomodoro_intervals'][self.session['label']]
@@ -260,7 +264,7 @@ class Application(Gtk.Application):
260
264
  t = self.session['target']
261
265
 
262
266
  t['percent'] = round(( (t['starting_value'] + minutes) / t['value'] ) * 100,1)
263
- print("At ", t['percent'], "% of ", t['scope'], " target")
267
+ print("At", t['percent'], "% of ", t['scope'], " target")
264
268
 
265
269
  if t['type'] == 'max':
266
270
  if t['percent'] >= 100:
@@ -275,64 +279,76 @@ class Application(Gtk.Application):
275
279
  playsound('sound/xylophone-chord.mp3',False)
276
280
 
277
281
 
278
- # maybe add a target % to the session and show with tick
279
282
 
280
283
  icon_tick_number = 0
281
-
284
+
282
285
  def tick(self):
283
- menu = self.menu
284
- indicator = self.indicator
285
286
 
286
287
  # check for suspend indicated by gap in tick intervals
287
- time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
288
+ time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
288
289
  if time_since_last_tick > 10:
289
- print(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
290
-
290
+ dbg(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
291
291
  if self.is_running:
292
292
  self.open_task_window(None,{'afk_time':time_since_last_tick})
293
293
  else:
294
- print("resetting randomness timer")
295
- self.session['duration'] = 0
296
294
  self.session['start_time'] = now()
297
295
 
298
- # print("tick!")
299
- self.session['duration'] = int(time_difference(self.session['start_time']))
296
+ duration = self.session['duration'] = int(time_difference(self.session['start_time']))
300
297
 
301
- if(self.session['duration'] > 2 and (int(self.session['duration']) / 60).is_integer()):
298
+ if num_is_multiple_of(duration,60):
302
299
  self.tock()
303
300
 
304
- if(self.is_running == True):
305
- self.icon_tick_number = self.icon_tick_number + 1
301
+ if self.is_running == True:
302
+ self.icon_tick_number += 1
306
303
 
307
304
  if self.icon_tick_number > 8:
308
305
  self.icon_tick_number = 1
309
306
 
310
- label = self.session['label'] + ": " + sec_to_time(self.session['duration'])
307
+ label = self.session['label'] + ": " + sec_to_time(duration)
311
308
 
312
- indicator.set_icon_full(os.path.abspath('icon/icon-'+str(self.icon_tick_number)+'.svg'),label)
309
+ self.indicator.set_icon_full(os.path.abspath('icon/icon-'+str(self.icon_tick_number)+'.svg'),label)
313
310
 
314
311
  else:
315
312
 
316
- # label = random.choice(conf.idle_messages) # Cool but makes menu bounce around #Could be paused when the menu opens
317
313
  label = conf.user['default_text']
318
- if self.session['duration'] > 60 and self.session['duration'] % 2:
319
- indicator.set_icon_full(os.path.abspath('icon/icon-red.svg'),label)
314
+ if duration > 60 and duration % 2:
315
+ self.indicator.set_icon_full(os.path.abspath('icon/icon-red.svg'),label)
320
316
  else:
321
- indicator.set_icon_full(os.path.abspath('icon/icon-1.svg'),label)
317
+ self.indicator.set_icon_full(os.path.abspath('icon/icon-1.svg'),label)
322
318
 
323
319
  # https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
324
- indicator.set_label(label, "Wide")
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]))
320
+ self.indicator.set_label(label,label)
328
321
 
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 = {}
322
+ if num_is_multiple_of(self.icon_tick_number,3):
323
+ self.refresh_all_changed_todo_files()
324
+
333
325
  return True
334
326
 
335
327
 
328
+ def refresh_all_changed_todo_files(self):
329
+ for id, todo_config in conf.user['todolists'].items():
330
+ if todo_config['status'] and 'watch_file' in todo_config:
331
+ self.refresh_todo_if_file_changed(todo_config)
332
+
333
+
334
+ def refresh_todo_if_file_changed(self, todo_config):
335
+
336
+ try:
337
+ todo_m_time = round(os.stat(todo_config['file']).st_mtime)
338
+
339
+ if todo_config['id'] not in conf.todo_file_change_times:
340
+ conf.todo_file_change_times[todo_config['id']] = todo_m_time
341
+
342
+ elif todo_m_time != conf.todo_file_change_times[todo_config['id']]:
343
+ dbg(todo_config['id']+" file was changed!",s='todoloading')
344
+ self.async_refresh(None,todo_config)
345
+ conf.todo_file_change_times[todo_config['id']] = todo_m_time
346
+
347
+ except Exception as e:
348
+ # TODO: consider how to quietly handle errors when refresh is not prompted by the user and temporarily disabling
349
+ handle_todo_read_error(todo_config,e)
350
+
351
+
336
352
  def start_task(self, w = None, task_data_or_id = None, transfer_current_session_time = False):
337
353
 
338
354
  if isinstance(task_data_or_id, dict):
@@ -356,7 +372,10 @@ class Application(Gtk.Application):
356
372
 
357
373
  if(self.is_running == True):
358
374
  self.stop_task()
359
-
375
+ elif(self.session['duration'] > 60):
376
+ # Log randomness session (to internal db only) if longer than one minute
377
+ utils.db_save_session(self.session)
378
+
360
379
 
361
380
  self.is_running = True
362
381
 
@@ -450,26 +469,20 @@ class Application(Gtk.Application):
450
469
  try:
451
470
 
452
471
  done_thread = threading.Thread(target=conf.todo_connectors[todolist_conf['type']].mark_task_done, args=(task,) )
472
+
453
473
  conf.todo_sync_times[todolist_conf['id']] = now() # this is to avoid causing a refresh, perhaps not the best though
454
474
 
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
- #
461
475
  done_thread.start()
462
476
 
463
477
  db_query("UPDATE tasks set status = '0' WHERE id = ? ",(task['id'],) )
464
478
  utils.reindex_one(task)
465
479
 
466
- # print('remove menu item')
467
480
  self.menu_tasks[task['id']].destroy()
468
-
481
+
469
482
  playsound('sound/xylophone-chord.mp3',False)
470
483
 
471
484
  except Exception as e:
472
- error_notice('Error Marking Task Done'," Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure",e )
485
+ error_notice('Error Marking Task Done'," Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure", e=e )
473
486
 
474
487
 
475
488
 
@@ -533,19 +546,12 @@ class Application(Gtk.Application):
533
546
  self.menu.show_all()
534
547
 
535
548
  self.is_running = False
536
- # print(utils.get_times(task))
537
549
  if action != 'cancel':
538
550
  notify.Notification.new("Focused on "+session['label']+" for "+sec_to_time(session['duration']),utils.pretty_dict(utils.get_times(task)), None).show()
539
551
  # notify.Notification.new(action.capitalize()+" "+session['label']+" "+sec_to_time(session['duration']),utils.pretty_dict(utils.get_times(task)), None).show()
540
552
 
541
553
  # Start randomness timer
542
- self.session = {
543
- "label": 'Randomness',
544
- "extended_label": 'Randomness',
545
- "start_time": now(),
546
- "duration":0,
547
- "task":{}
548
- }
554
+ self.session = default_session()
549
555
 
550
556
  self.tick()
551
557
 
@@ -554,9 +560,6 @@ class Application(Gtk.Application):
554
560
  self.menu.get_children()[0].destroy()
555
561
  self.do_not_disturb_menu_item.set_label("Do Not Disturb")
556
562
 
557
- else:
558
- print('no task running!')
559
-
560
563
 
561
564
  def task_running_menu_additions(self):
562
565
 
nowfocus/conf.py CHANGED
@@ -52,20 +52,19 @@ connectors = {
52
52
  todo_sync_time = datetime.now()
53
53
  todo_sync_times = {}
54
54
 
55
- todo_sync_required = {}
56
- file_watchers = {}
57
- file_watch_ignores = {}
55
+ todo_file_change_times = {}
56
+ timers = {}
58
57
 
59
58
  prototype_settings = {
60
59
  "pomodoro_interval": 40,
61
60
  "open_task_window_fullscreen": True,
61
+ "show_task_window_sidebars": False,
62
62
  "randomness_interrupt_interval":5,
63
63
  "default_text": "What am I doing?",
64
64
  "todolist_refresh_interval":1,
65
- "version":0.2,
65
+ "version":0.4,
66
66
  "display_todolist_as_top_level_list":'auto',
67
67
  'max_top_level_menu_items':10,
68
- # 'tick_interval':1,
69
68
  'hours_search_timeframe':'this year',
70
69
  'invoice_hourly_rate':0,
71
70
 
@@ -39,7 +39,6 @@ def add_new_task(user_conf,list,task_label):
39
39
 
40
40
  todotxt.add(task)
41
41
  todotxt.save()
42
- # task.add_project
43
42
 
44
43
  t = {
45
44
  'id':task_label,
@@ -87,6 +86,7 @@ def get_todos(user_conf):
87
86
  }
88
87
  }
89
88
 
89
+ priority_letter_to_number_map = {'A':1,'B':2,'C':3,'D':4}
90
90
 
91
91
  todotxt = pytodotxt.TodoTxt(user_conf['file'])
92
92
  for t in todotxt.parse():
@@ -99,7 +99,6 @@ def get_todos(user_conf):
99
99
  'parent_id':user_conf['id'],
100
100
  'parent_label':user_conf['label'],
101
101
  'status':1,
102
- # 'priority':1 max, 5 min, 0 none,
103
102
  'todolist':user_conf['id'],
104
103
  'data':t.attributes #TODO: add other things like date etc
105
104
  }
@@ -107,6 +106,12 @@ def get_todos(user_conf):
107
106
  if t.is_completed:
108
107
  tasks[id]['status'] = 0
109
108
 
109
+ if t.priority:
110
+ try:
111
+ tasks[id]['priority'] = priority_letter_to_number_map[t.priority]
112
+ except:
113
+ tasks[id]['priority'] = 5
114
+
110
115
 
111
116
  if t.projects:
112
117
  l = t.projects[0]
@@ -137,5 +142,5 @@ def get_todos(user_conf):
137
142
  def launch(user_conf, item = None, category = None):
138
143
  ''' Open todolist '''
139
144
 
140
- # It would b very nice to open a the right line number but xdg-open doesn't support that...
145
+ # It would b very nice to open the right line number but xdg-open doesn't support that...
141
146
  utils.open_external(user_conf['file'])
@@ -108,7 +108,7 @@ def mark_task_done(task):
108
108
  if data[line_no].strip().startswith('[ ]'):
109
109
  data[line_no] = data[line_no].replace('[ ]', '[x]')
110
110
  else:
111
- data[line_no] = data[line_no].replace(data[line_no].lstrip(), '[x] '+ data[line_no].lstrip() )
111
+ data[line_no] = data[line_no].replace(data[line_no].lstrip(), '[x]'+ data[line_no].lstrip() )
112
112
 
113
113
  # write everything back
114
114
  with open(file_uri, 'w') as file:
@@ -176,7 +176,18 @@ def get_todos(user_conf):
176
176
  status = 0
177
177
 
178
178
  label = label.removeprefix('[ ]').removeprefix('[]').strip()
179
- #TODO: use markdown title syntax as list name in addition to indentation
179
+ #TODO: use markdown title syntax as list name in addition to indentation?
180
+
181
+ # tags = ''
182
+ # # hashtag tagging
183
+ # if label.split("#").len() > 1:
184
+ # parts = label.split("#")
185
+ # print("label has hashtag",parts)
186
+
187
+ # label = parts[0].strip()
188
+ # del parts[0]
189
+ # tags = ",".join(parts)
190
+
180
191
 
181
192
  indent = len(line) - len(line.lstrip())
182
193
  indent_str = line[0:indent]
@@ -229,6 +240,7 @@ def get_todos(user_conf):
229
240
  'todolist':user_conf['id'],
230
241
  'status': status,
231
242
  'priority': priority,
243
+ # 'tags':tags,
232
244
  'data':{
233
245
  'line_no':line_no,
234
246
  'original_line':line,
@@ -87,9 +87,9 @@ class NewTaskWDialog(Gtk.Dialog):
87
87
  todolist_conf = conf.user['todolists'][parent_list['todolist']]
88
88
  try:
89
89
 
90
- conf.file_watch_ignores[todolist_conf['id']] = True
91
90
  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']]
91
+
92
+ clear_todo_file_change_time(todolist_conf)
93
93
 
94
94
  dbg('connector add task response',task)
95
95
 
@@ -121,9 +121,8 @@ class NewTaskWDialog(Gtk.Dialog):
121
121
  if conf.debug_level > 1:
122
122
  raise e
123
123
 
124
- error_notice('Error adding tasks',"Adding "+ task_label+" to "+todolist_conf['label']+" had a serious failure",e )
124
+ error_notice('Error adding tasks',"Adding "+ task_label+" to "+todolist_conf['label']+" had a serious failure",e) # NOTE: e, in the case of a key error, only prints the bad key, not the error type
125
125
 
126
-
127
126
 
128
127
  elif response == Gtk.ResponseType.CANCEL:
129
128
  print("Cancel button clicked")
nowfocus/settings.py CHANGED
@@ -64,60 +64,19 @@ def after_todo_settings_change(app, todo = None):
64
64
  if todo:
65
65
  todo = utils.get_todo_by_id(todo)
66
66
  if todo['status']:
67
-
68
67
  app.async_refresh(todo)
69
- utils.stop_todo_file_watchers(todo)
70
- utils.start_todo_file_watchers(todo)
71
-
72
68
  else:
73
69
  utils.db_deactivate_todo(todo['id'])
74
70
  utils.reindex()
75
- utils.stop_todo_file_watchers(todo)
76
71
  app.update_menu()
77
72
 
78
73
  else:
79
74
  app.async_refresh()
80
- utils.stop_todo_file_watchers()
81
- utils.start_todo_file_watchers()
82
75
 
83
76
 
84
77
  class SettingsWindow(Gtk.Window):
85
78
 
86
79
 
87
- def show_sessions(self, widget = None, label_text = "Most Recent Sessions", order_by = 'start_time', limit = 35, passed_sessions = None):
88
-
89
- self.sessions_box.foreach(lambda child: child.destroy())
90
-
91
- if passed_sessions:
92
- dbg("using Passed_sessions",passed_sessions,s='settings')
93
- sessions = passed_sessions
94
- else:
95
- dbg("show_sessions: order_by:",order_by, 'limit',limit, s='settings')
96
- sessions = db_query(" SELECT * FROM sessions WHERE "+sessions_timeframe_sql()+" ORDER BY "+order_by+" DESC LIMIT ? ",(limit,))
97
-
98
- # print('sessions')
99
- # print(sessions)
100
-
101
- label = Gtk.Label()
102
- if passed_sessions:
103
- label.set_markup('<b>'+label_text+'</b>')
104
- else:
105
- label.set_markup('<b>'+str(limit)+' '+label_text+' of '+conf.user['hours_search_timeframe']+'</b>')
106
- self.sessions_box.add(label)
107
-
108
- for ls in sessions:
109
- dbg('Add session to session_box',ls['extended_label'],s='settings')
110
- btn = Gtk.Button(label=ls['extended_label']+' '+str(ls['duration'] / 60 / 60)[:4]+' hr on '+ls['start_time'][:10])
111
-
112
- btn.set_halign(Gtk.Align.START)
113
- btn.set_relief(Gtk.ReliefStyle.NONE)
114
-
115
- btn.connect('clicked', lambda button_widget, ls: SessionEditDialog(None, self, ls),ls )
116
-
117
- self.sessions_box.add(btn)
118
-
119
- self.sessions_box.show_all()
120
-
121
80
  def scroll_box(self, parent_widget = None, height = 300):
122
81
 
123
82
  # print('parent_widget')
@@ -136,7 +95,7 @@ class SettingsWindow(Gtk.Window):
136
95
 
137
96
 
138
97
  def __init__(self, parent=None, **kwargs):
139
- self.app = parent #NOTE: This doens't look like it will work when called from task_window...
98
+ self.app = parent #NOTE: This doesn't look like it will work when called from task_window...
140
99
  Gtk.Window.__init__(self, title="Settings")
141
100
  self.set_border_width(15)
142
101
  self.set_position(position=1)
@@ -241,6 +200,7 @@ class SettingsWindow(Gtk.Window):
241
200
  grid.attach(self.settings_updater('default_text','What am I Doing?','Entry'),0,(row:=row+1),5,1)
242
201
 
243
202
  grid.attach(self.settings_updater('open_task_window_fullscreen',True,'Switch'),0,(row:=row+1),5,1)
203
+ grid.attach(self.settings_updater('show_task_window_sidebars',True,'Switch'),0,(row:=row+1),5,1)
244
204
 
245
205
  grid.attach(self.settings_updater('pomodoro_interval',25,'SpinButton',"(minutes)"),0,(row:=row+1),5,1)
246
206
 
@@ -248,8 +208,7 @@ class SettingsWindow(Gtk.Window):
248
208
 
249
209
  grid.attach(self.settings_updater('todolist_refresh_interval', 3, 'SpinButton',"(hours)"),0,(row:=row+1),5,1)
250
210
 
251
- # grid.attach(self.settings_updater('tick_interval', 18, 'SpinButton',"\nMust be an even number fraction of 60. (requires restart)"),0,(row:=row+1),5,1)
252
-
211
+
253
212
  # grid.attach(self.settings_updater('invoice_hourly_rate', 33, 'SpinButton',""),0,(row:=row+1),5,1)
254
213
 
255
214
  grid.attach(self.settings_updater('hours_search_timeframe', 'auto', 'ComboBoxText',"\nDefault timetracking range for hours shown in main window and invoicing", options = list(hours_search_timeframes().keys())),0,(row:=row+1),5,1)
@@ -360,11 +319,9 @@ class SettingsWindow(Gtk.Window):
360
319
  self.sessions_page.add(print_time_totals_button)
361
320
 
362
321
  show_recent_sessions_button = Gtk.Button(label="Show recent sessions")
363
- show_recent_sessions_button.connect("clicked", self.show_sessions, 'Recent Sessions', 'start_time')
364
322
  self.sessions_page.add(show_recent_sessions_button)
365
323
 
366
324
  show_long_sessions_button = Gtk.Button(label="Show long sessions")
367
- show_long_sessions_button.connect("clicked", self.show_sessions, 'Longest Sessions', 'duration')
368
325
  self.sessions_page.add(show_long_sessions_button)
369
326
 
370
327
  self.sessions_scroller = self.scroll_box(self.sessions_page)
@@ -372,7 +329,12 @@ class SettingsWindow(Gtk.Window):
372
329
  self.sessions_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
373
330
  self.sessions_scroller.add(self.sessions_box)
374
331
 
375
- self.show_sessions()
332
+ show_sessions(None, self, self.sessions_box)
333
+ show_recent_sessions_button.connect("clicked", show_sessions, self, self.sessions_box, 'Recent Sessions', 'start_time')
334
+ show_long_sessions_button.connect("clicked", show_sessions, self, self.sessions_box, 'Longest Sessions', 'duration')
335
+
336
+ self.SessionEditDialog = SessionEditDialog # passed to show_sessions
337
+
376
338
 
377
339
  self.show_all()
378
340
 
@@ -585,7 +547,6 @@ class SettingsWindow(Gtk.Window):
585
547
  db_query("DELETE FROM tasks WHERE todolist = ? AND id NOT IN (SELECT task_id FROM sessions)",(id,))
586
548
  db_query("DELETE FROM lists WHERE todolist = ?",(id,))
587
549
  utils.reindex()
588
- utils.stop_todo_file_watchers(connector)
589
550
  self.app.update_menu()
590
551
 
591
552
  del conf.user[connector_category][id]
@@ -1040,9 +1001,7 @@ class EditAddConnectorDialog(Gtk.Dialog):
1040
1001
  if connector_category == 'todolists':
1041
1002
 
1042
1003
  conf.todo_connectors[new['type']] = importlib.import_module('connectors.'+new['type'])
1043
-
1044
- utils.stop_todo_file_watchers(new)
1045
- utils.start_todo_file_watchers(new)
1004
+
1046
1005
  self.app.async_refresh(new)
1047
1006
 
1048
1007
 
nowfocus/styles.css CHANGED
@@ -1,9 +1,3 @@
1
- /* *{
2
- font-size:18px;
3
- } */
4
-
5
-
6
- /* #TaskWindow{ */
7
1
  .large{
8
2
  font-size:25px;
9
3
  }
@@ -11,23 +5,25 @@
11
5
  #FuzzyTask{
12
6
  font-size:30px;
13
7
  padding:12px;
14
-
15
8
  }
16
9
 
17
10
  #RelevantQuestion{
18
11
  font-weight: 100;
19
- font-size:35px;
12
+ font-size: 35px;
20
13
  opacity: .3;
21
14
  }
22
15
 
23
- #Footer{
16
+ .subtle{
24
17
  font-weight: 100;
25
18
  font-size:16px;
26
- opacity: .5;
19
+ opacity: .4;
27
20
  }
28
21
 
29
- #priorityTask{
22
+ .subtle:hover{
23
+ opacity: .7;
24
+ }
30
25
 
26
+ #priorityTask{
31
27
  font-weight: bold;
32
28
  }
33
29
 
@@ -38,6 +34,8 @@
38
34
  .done{
39
35
  text-decoration-line: line-through;
40
36
  text-decoration:line-through;
37
+ opacity: .5;
38
+ font-weight: 100;
41
39
  }
42
40
 
43
41
  #task_rclick_menu{