nowfocus 0.2.12__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 +83 -80
- nowfocus/conf.py +4 -5
- nowfocus/connectors/todotxt.py +8 -3
- nowfocus/connectors/txt.py +43 -28
- nowfocus/new_task_dialog.py +3 -4
- nowfocus/settings.py +10 -51
- nowfocus/styles.css +9 -11
- nowfocus/task_window.py +117 -72
- nowfocus/user_idle_time.py +82 -0
- nowfocus/utils.py +169 -108
- {nowfocus-0.2.12.dist-info → nowfocus-0.4.0.dist-info}/METADATA +66 -48
- {nowfocus-0.2.12.dist-info → nowfocus-0.4.0.dist-info}/RECORD +16 -15
- {nowfocus-0.2.12.dist-info → nowfocus-0.4.0.dist-info}/WHEEL +0 -0
- {nowfocus-0.2.12.dist-info → nowfocus-0.4.0.dist-info}/entry_points.txt +0 -0
- {nowfocus-0.2.12.dist-info → nowfocus-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {nowfocus-0.2.12.dist-info → nowfocus-0.4.0.dist-info}/top_level.txt +0 -0
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
16
|
+
.subtle{
|
|
24
17
|
font-weight: 100;
|
|
25
18
|
font-size:16px;
|
|
26
|
-
opacity: .
|
|
19
|
+
opacity: .4;
|
|
27
20
|
}
|
|
28
21
|
|
|
29
|
-
|
|
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{
|
nowfocus/task_window.py
CHANGED
|
@@ -6,6 +6,7 @@ import utils
|
|
|
6
6
|
from utils import *
|
|
7
7
|
|
|
8
8
|
from settings import EditAddTargetDialog, EditTaskCommandDialog
|
|
9
|
+
from session_edit_dialog import SessionEditDialog
|
|
9
10
|
|
|
10
11
|
class TaskWindow(Gtk.Window):
|
|
11
12
|
css_provider = Gtk.CssProvider()
|
|
@@ -18,6 +19,8 @@ class TaskWindow(Gtk.Window):
|
|
|
18
19
|
|
|
19
20
|
def __init__(self,app,passed_data=None):
|
|
20
21
|
|
|
22
|
+
timeit()
|
|
23
|
+
|
|
21
24
|
if TaskWindow._instance:
|
|
22
25
|
try:
|
|
23
26
|
if TaskWindow._instance.taskEntry.get_text():
|
|
@@ -67,41 +70,56 @@ class TaskWindow(Gtk.Window):
|
|
|
67
70
|
self.shown_tasks = {}
|
|
68
71
|
|
|
69
72
|
self.set_border_width(20)
|
|
70
|
-
self.set_position(position=1)
|
|
73
|
+
self.set_position(position=1)
|
|
71
74
|
|
|
72
75
|
self.accel_group = Gtk.AccelGroup()
|
|
73
76
|
self.add_accel_group(self.accel_group)
|
|
74
77
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
# box.set_valign(Gtk.Align.CENTER)
|
|
78
|
-
|
|
79
|
-
self.add(box)
|
|
78
|
+
self.outer_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0, border_width=0)
|
|
79
|
+
self.add(self.outer_box)
|
|
80
80
|
|
|
81
|
-
self.
|
|
81
|
+
self.l_sidebar = Gtk.VBox(spacing=5)
|
|
82
|
+
self.l_sidebar.get_style_context().add_class("subtle")
|
|
83
|
+
|
|
84
|
+
self.outer_box.add(self.l_sidebar)
|
|
85
|
+
|
|
86
|
+
self.center_box = Gtk.VBox(spacing=10)
|
|
87
|
+
self.center_box.set_halign(Gtk.Align.CENTER)
|
|
82
88
|
|
|
83
|
-
|
|
89
|
+
self.outer_box.add(self.center_box)
|
|
84
90
|
|
|
85
|
-
|
|
91
|
+
self.header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15, border_width=10)
|
|
86
92
|
|
|
93
|
+
self.center_box.pack_start(self.header,False, False, 0)
|
|
94
|
+
|
|
87
95
|
self.recreate_header()
|
|
88
96
|
|
|
97
|
+
self.r_sidebar_outer = Gtk.ScrolledWindow()
|
|
98
|
+
self.r_sidebar_outer.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
|
99
|
+
self.outer_box.add(self.r_sidebar_outer)
|
|
100
|
+
|
|
101
|
+
self.r_sidebar = Gtk.VBox(spacing=5)
|
|
102
|
+
self.r_sidebar.get_style_context().add_class("subtle")
|
|
103
|
+
|
|
104
|
+
self.r_sidebar.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
|
|
105
|
+
self.r_sidebar.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK)
|
|
106
|
+
self.r_sidebar.connect("enter-notify-event",self.test)
|
|
107
|
+
|
|
108
|
+
self.r_sidebar_outer.add(self.r_sidebar)
|
|
109
|
+
|
|
89
110
|
self.tick_timer = GLib.timeout_add_seconds(1, self.tick)
|
|
90
111
|
|
|
91
112
|
self.task_entry_overlay = Gtk.Overlay()
|
|
92
113
|
|
|
93
|
-
# Large fuzzy task input
|
|
94
114
|
self.taskEntry = Gtk.Entry()
|
|
95
|
-
self.taskEntry.set_name("FuzzyTask")
|
|
96
|
-
# self.taskEntry.set_width_chars(59)
|
|
115
|
+
self.taskEntry.set_name("FuzzyTask")
|
|
97
116
|
self.taskEntry.set_max_width_chars(79)
|
|
98
|
-
|
|
99
117
|
self.taskEntry.set_placeholder_text("Find Task [Ctrl+F]")
|
|
100
118
|
self.taskEntry.set_property("tooltip-text", "Find Task [Ctrl+F], press Enter to start work on the first task in the list")
|
|
101
119
|
|
|
102
120
|
self.task_entry_overlay.add(self.taskEntry)
|
|
103
121
|
|
|
104
|
-
|
|
122
|
+
self.center_box.pack_start(self.task_entry_overlay,False, False, 0)
|
|
105
123
|
|
|
106
124
|
self.taskEntry.grab_focus()
|
|
107
125
|
|
|
@@ -126,10 +144,9 @@ class TaskWindow(Gtk.Window):
|
|
|
126
144
|
self.taskEntry.set_text(passed_data['task']['label'])
|
|
127
145
|
|
|
128
146
|
|
|
129
|
-
|
|
130
147
|
self.scrolled_window = Gtk.ScrolledWindow()
|
|
131
148
|
self.scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
132
|
-
|
|
149
|
+
self.center_box.pack_start(self.scrolled_window, True, True, 0)
|
|
133
150
|
self.scrolled_window.set_size_request(-1, 350)
|
|
134
151
|
|
|
135
152
|
self.scrolled_window.connect('scroll-event', self.on_scroll) # This doesn't catch scrollbar moves
|
|
@@ -142,6 +159,7 @@ class TaskWindow(Gtk.Window):
|
|
|
142
159
|
|
|
143
160
|
self.total_duration_label = Gtk.Label()
|
|
144
161
|
self.total_duration_label.set_margin_end(12)
|
|
162
|
+
self.total_duration_label.get_style_context().add_class('subtle')
|
|
145
163
|
self.total_duration_label.set_halign(Gtk.Align.END)
|
|
146
164
|
self.task_entry_overlay.add_overlay(self.total_duration_label)
|
|
147
165
|
|
|
@@ -156,14 +174,14 @@ class TaskWindow(Gtk.Window):
|
|
|
156
174
|
# self.timesheet_to_button.connect("clicked",self.timesheet_to_clipboard)
|
|
157
175
|
# self.timesheet_to_button.set_name("Footer")
|
|
158
176
|
# self.timesheet_to_button.set_property("tooltip-text","Click to copy CSV timesheet to clipboard")
|
|
159
|
-
#
|
|
177
|
+
# self.center_box.pack_start(self.timesheet_to_button,False, False, 0)
|
|
160
178
|
|
|
161
179
|
|
|
162
180
|
self.buttons_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
|
|
163
181
|
self.buttons_box.set_halign(Gtk.Align.CENTER)
|
|
164
|
-
self.buttons_box.
|
|
182
|
+
self.buttons_box.get_style_context().add_class("subtle")
|
|
165
183
|
|
|
166
|
-
|
|
184
|
+
self.center_box.pack_start(self.buttons_box,False, False, 0)
|
|
167
185
|
|
|
168
186
|
self.settings_button = Gtk.Button(label="Settings")
|
|
169
187
|
self.settings_button.connect("clicked",self.app.open_settings_window)
|
|
@@ -181,7 +199,8 @@ class TaskWindow(Gtk.Window):
|
|
|
181
199
|
openable = conf.todo_connectors[todo['type']].launch
|
|
182
200
|
openable_button = Gtk.Button(label=GLib.markup_escape_text(todo['label']))
|
|
183
201
|
openable_button.connect("clicked", lambda button_widget,todo=todo, openable=openable: openable(todo))
|
|
184
|
-
openable_button.set_property("tooltip-text","Open
|
|
202
|
+
openable_button.set_property("tooltip-text","Open "+GLib.markup_escape_text(todo['label']))
|
|
203
|
+
|
|
185
204
|
self.buttons_box.add(openable_button)
|
|
186
205
|
|
|
187
206
|
except Exception as e:
|
|
@@ -193,6 +212,8 @@ class TaskWindow(Gtk.Window):
|
|
|
193
212
|
self.buttons_box.add(self.new_task_button)
|
|
194
213
|
|
|
195
214
|
self.show_all()
|
|
215
|
+
timeit()
|
|
216
|
+
|
|
196
217
|
|
|
197
218
|
key, mod = Gtk.accelerator_parse('<Control>n')
|
|
198
219
|
self.new_task_button.add_accelerator("clicked", self.accel_group, key, mod, Gtk.AccelFlags.VISIBLE)
|
|
@@ -205,11 +226,17 @@ class TaskWindow(Gtk.Window):
|
|
|
205
226
|
self.connect("window-state-event", self.on_window_state_event)
|
|
206
227
|
|
|
207
228
|
self.taskEntry.connect("changed",self.task_search)
|
|
229
|
+
GLib.idle_add(self.refresh_search_cache)
|
|
208
230
|
|
|
209
|
-
self.refresh_search_cache()
|
|
210
231
|
self.connect("destroy", self.on_destroy)
|
|
211
232
|
|
|
212
|
-
|
|
233
|
+
def test(self=None, one=None, two=None, **kwargs):
|
|
234
|
+
print('task_window test')
|
|
235
|
+
print(one)
|
|
236
|
+
print(two)
|
|
237
|
+
print(kwargs)
|
|
238
|
+
|
|
239
|
+
|
|
213
240
|
def on_destroy(self, widget= None):
|
|
214
241
|
TaskWindow._instance = None
|
|
215
242
|
print("¡Adios Task Window!")
|
|
@@ -221,11 +248,15 @@ class TaskWindow(Gtk.Window):
|
|
|
221
248
|
if self.lazy_loadable_tasks:
|
|
222
249
|
print('Tasks to lazy load ', len(self.lazy_loadable_tasks))
|
|
223
250
|
for id, t in self.lazy_loadable_tasks.items():
|
|
224
|
-
self.
|
|
251
|
+
self.add_task_to_window(t)
|
|
225
252
|
self.tasks_box.show_all()
|
|
226
253
|
|
|
227
254
|
self.lazy_loadable_tasks = {}
|
|
228
255
|
|
|
256
|
+
def select_list_callback(self, widget, l):
|
|
257
|
+
print('widget',widget,'list',l)
|
|
258
|
+
self.taskEntry.set_text(l['extended_label']+" > ")
|
|
259
|
+
self.taskEntry.grab_focus()
|
|
229
260
|
|
|
230
261
|
def refresh_search_cache(self,w = None):
|
|
231
262
|
self.search_cache = {}
|
|
@@ -242,13 +273,11 @@ class TaskWindow(Gtk.Window):
|
|
|
242
273
|
|
|
243
274
|
self.session_time.set_label(sec_to_time(self.app.session['duration']))
|
|
244
275
|
if self.app.session['label'] != self.header_task:
|
|
245
|
-
self.recreate_header()
|
|
276
|
+
self.recreate_header()
|
|
246
277
|
|
|
247
278
|
if utils.taskindex_updated_time() > self.search_cache_refresh_time:
|
|
248
279
|
# print("taskindex was updated, utils.taskindex_updated_time()", utils.taskindex_updated_time(), 'self.search_cache_refresh_time',self.search_cache_refresh_time)
|
|
249
280
|
self.refresh_search_cache()
|
|
250
|
-
# else:
|
|
251
|
-
# print("taskindex was not updated",current_data_version)
|
|
252
281
|
|
|
253
282
|
return True # This continues the timer
|
|
254
283
|
|
|
@@ -292,13 +321,13 @@ class TaskWindow(Gtk.Window):
|
|
|
292
321
|
pause_button.connect("clicked", self.recreate_header)
|
|
293
322
|
|
|
294
323
|
pause_button.connect("clicked", self.refresh_search_cache)
|
|
295
|
-
pause_button.set_property("tooltip-text", "Pause Task (
|
|
324
|
+
pause_button.set_property("tooltip-text", "Pause Task (Control S)")
|
|
296
325
|
pause_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
297
326
|
self.header.add(pause_button)
|
|
298
327
|
|
|
299
328
|
done_button = Gtk.Button()
|
|
300
329
|
done_button.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/mark-done.png')))
|
|
301
|
-
done_button.set_property("tooltip-text", "Mark Task Done (
|
|
330
|
+
done_button.set_property("tooltip-text", "Mark Task Done (Control D)")
|
|
302
331
|
done_button.connect("clicked", self.app.stop_task,'mark_done')
|
|
303
332
|
done_button.connect("clicked", self.recreate_header)
|
|
304
333
|
done_button.connect("clicked", self.refresh_search_cache) # doesn't work, probably because it happens before stop_task
|
|
@@ -310,14 +339,17 @@ class TaskWindow(Gtk.Window):
|
|
|
310
339
|
cancel_button.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/cancel.png')))
|
|
311
340
|
cancel_button.connect("clicked", self.app.stop_task,"cancel")
|
|
312
341
|
cancel_button.connect("clicked", self.recreate_header)
|
|
313
|
-
cancel_button.set_property("tooltip-text", "Discard timer (
|
|
342
|
+
cancel_button.set_property("tooltip-text", "Discard timer (Control Q)")
|
|
314
343
|
cancel_button.set_relief(Gtk.ReliefStyle.NONE)
|
|
315
344
|
self.header.add(cancel_button)
|
|
316
345
|
|
|
317
|
-
key, mod = Gtk.accelerator_parse('<Control>p')
|
|
346
|
+
key, mod = Gtk.accelerator_parse('<Control>p') # for backward compatibility
|
|
347
|
+
pause_button.add_accelerator("clicked", self.accel_group, key, mod, Gtk.AccelFlags.VISIBLE)
|
|
348
|
+
|
|
349
|
+
key, mod = Gtk.accelerator_parse('<Control>S')
|
|
318
350
|
pause_button.add_accelerator("clicked", self.accel_group, key, mod, Gtk.AccelFlags.VISIBLE)
|
|
319
351
|
|
|
320
|
-
key, mod = Gtk.accelerator_parse('<Control>
|
|
352
|
+
key, mod = Gtk.accelerator_parse('<Control>q')
|
|
321
353
|
cancel_button.add_accelerator("clicked", self.accel_group, key, mod, Gtk.AccelFlags.VISIBLE)
|
|
322
354
|
|
|
323
355
|
key, mod = Gtk.accelerator_parse('<Control>d')
|
|
@@ -346,12 +378,33 @@ class TaskWindow(Gtk.Window):
|
|
|
346
378
|
|
|
347
379
|
|
|
348
380
|
self.header.show_all()
|
|
381
|
+
GLib.idle_add(self.create_sidebars)
|
|
349
382
|
|
|
350
383
|
try:
|
|
351
384
|
self.taskEntry.grab_focus()
|
|
352
385
|
except Exception:
|
|
353
386
|
pass
|
|
354
|
-
|
|
387
|
+
|
|
388
|
+
def create_sidebars(self):
|
|
389
|
+
timeit()
|
|
390
|
+
if conf.user['open_task_window_fullscreen'] and conf.user['show_task_window_sidebars']:
|
|
391
|
+
self.r_sidebar_outer.set_size_request(350, -1)
|
|
392
|
+
self.l_sidebar.set_size_request(350, -1)
|
|
393
|
+
|
|
394
|
+
self.SessionEditDialog = SessionEditDialog # passed to show_sessions
|
|
395
|
+
|
|
396
|
+
#show lists on left
|
|
397
|
+
self.l_sidebar.foreach(lambda child: child.destroy())
|
|
398
|
+
|
|
399
|
+
self.l_sidebar.add(choose_from_lists(self.select_list_callback, 'None', None, False))
|
|
400
|
+
|
|
401
|
+
#show sessions on right
|
|
402
|
+
show_sessions(None, self, self.r_sidebar, None, 'start_time', '30',None, 30)
|
|
403
|
+
self.l_sidebar.show_all()
|
|
404
|
+
self.r_sidebar.show_all()
|
|
405
|
+
timeit()
|
|
406
|
+
|
|
407
|
+
|
|
355
408
|
def on_notes_changed(self,buffer):
|
|
356
409
|
notes = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)
|
|
357
410
|
self.app.session['notes'] = notes
|
|
@@ -364,20 +417,18 @@ class TaskWindow(Gtk.Window):
|
|
|
364
417
|
self.refresh_search_cache()
|
|
365
418
|
|
|
366
419
|
|
|
367
|
-
def task_search(self,widget):
|
|
420
|
+
def task_search(self,widget = None):
|
|
368
421
|
|
|
369
422
|
self.tasks_box.foreach(lambda child: child.destroy())
|
|
370
|
-
self.tasks_box.show_all()
|
|
371
423
|
self.shown_tasks.clear()
|
|
372
424
|
|
|
373
|
-
self.search_term =
|
|
425
|
+
self.search_term = self.taskEntry.get_text()
|
|
374
426
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
tasks = self.search_cache[i]
|
|
427
|
+
if self.search_term in self.search_cache:
|
|
428
|
+
tasks = self.search_cache[self.search_term]
|
|
378
429
|
else:
|
|
379
|
-
tasks = utils.taskindex_search(
|
|
380
|
-
self.search_cache[
|
|
430
|
+
tasks = utils.taskindex_search(self.search_term)
|
|
431
|
+
self.search_cache[self.search_term] = tasks
|
|
381
432
|
|
|
382
433
|
total_duration = 0
|
|
383
434
|
self.lazy_loadable_tasks = {}
|
|
@@ -385,12 +436,12 @@ class TaskWindow(Gtk.Window):
|
|
|
385
436
|
if tasks:
|
|
386
437
|
count = 0
|
|
387
438
|
for id, t in tasks.items():
|
|
388
|
-
count
|
|
439
|
+
count += 1
|
|
389
440
|
if 'duration' in t and t['duration']:
|
|
390
441
|
total_duration += int(t['duration'])
|
|
391
442
|
|
|
392
443
|
if count < self.num_initial_tasks:
|
|
393
|
-
self.
|
|
444
|
+
self.add_task_to_window(t)
|
|
394
445
|
else:
|
|
395
446
|
self.lazy_loadable_tasks[id] = t
|
|
396
447
|
|
|
@@ -399,30 +450,27 @@ class TaskWindow(Gtk.Window):
|
|
|
399
450
|
self.total_duration_label.set_markup('<b>'+str(round(total_duration / 60 / 60,1))+'</b> hrs\n'+conf.user['hours_search_timeframe'] )
|
|
400
451
|
|
|
401
452
|
|
|
402
|
-
|
|
403
|
-
def add_task_to_list(self,t):
|
|
453
|
+
def add_task_to_window(self,t):
|
|
404
454
|
|
|
405
455
|
try:
|
|
406
|
-
search_str = self.search_term
|
|
407
456
|
utils.dbg("add_task_to_list "+ str(t['extended_label']), "status",t['status'], s='taskwindow',l=3)
|
|
408
457
|
|
|
409
|
-
# self.shown_tasks[t['id']] = Gtk.Button() # Works but does not accept popovers
|
|
410
458
|
self.shown_tasks[t['id']] = Gtk.MenuButton(popover=self.task_rclick_menu)
|
|
411
|
-
self.shown_tasks[t['id']]
|
|
412
|
-
|
|
459
|
+
button = self.shown_tasks[t['id']]
|
|
460
|
+
button.set_halign(Gtk.Align.START)
|
|
461
|
+
button.set_hexpand(True)
|
|
413
462
|
|
|
414
463
|
label = Gtk.Label()
|
|
415
464
|
|
|
416
|
-
# button_context = self.shown_tasks[t['id']].get_style_context().add_class("large")
|
|
417
465
|
extended_label = GLib.markup_escape_text(t['extended_label'],)
|
|
466
|
+
|
|
467
|
+
if len(extended_label) > 110:
|
|
468
|
+
extended_label = extended_label[:110]+"..."
|
|
418
469
|
|
|
419
|
-
if len(
|
|
420
|
-
|
|
421
|
-
# fts5 match highlighting, not that good because it highlights the whole word rather than the matching part...
|
|
422
|
-
# extended_label = extended_label.replace('[[',"<b>").replace(']]',"</b>")
|
|
470
|
+
if len(self.search_term) > 1:
|
|
423
471
|
|
|
424
|
-
extended_label = extended_label.replace(
|
|
425
|
-
extended_label = extended_label.replace(
|
|
472
|
+
extended_label = extended_label.replace(self.search_term,"<b>"+self.search_term+"</b>")
|
|
473
|
+
extended_label = extended_label.replace(self.search_term.capitalize(),"<b>"+self.search_term.capitalize()+"</b>") # Cheesy but kind of works
|
|
426
474
|
|
|
427
475
|
if "duration" in t and t['duration']:
|
|
428
476
|
extended_label += " ("+sec_to_time(t['duration'])+")"
|
|
@@ -430,28 +478,22 @@ class TaskWindow(Gtk.Window):
|
|
|
430
478
|
if t['id'] in conf.user['task_commands'] and conf.user['task_commands'][t['id']]['status']:
|
|
431
479
|
extended_label += " ["+GLib.markup_escape_text(conf.user['task_commands'][t['id']]['command'])+"]"
|
|
432
480
|
|
|
433
|
-
if t['priority']:
|
|
434
|
-
extended_label = str(t['priority']) +" "+ extended_label
|
|
435
481
|
|
|
436
482
|
if not t['status']:
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
label.set_markup('<s>'+extended_label+'</s>')
|
|
483
|
+
button.get_style_context().add_class("done")
|
|
484
|
+
extended_label = '<s>'+extended_label+'</s>'
|
|
440
485
|
|
|
441
486
|
elif t['priority'] > 0:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
else:
|
|
445
|
-
label.set_markup(extended_label)
|
|
487
|
+
extended_label = str(t['priority']) +" "+ extended_label
|
|
488
|
+
button.get_style_context().add_class("bold")
|
|
446
489
|
|
|
447
|
-
|
|
490
|
+
label.set_markup(extended_label)
|
|
491
|
+
button.add(label)
|
|
448
492
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
self.
|
|
453
|
-
self.tasks_box.add(self.shown_tasks[t['id']])
|
|
454
|
-
self.tasks_box.show_all()
|
|
493
|
+
button.connect("button-release-event", self.select_task, t['id'])
|
|
494
|
+
button.connect("activate", self.select_task, None, t['id'])
|
|
495
|
+
button.set_relief(Gtk.ReliefStyle.NONE)
|
|
496
|
+
self.tasks_box.add(button)
|
|
455
497
|
|
|
456
498
|
|
|
457
499
|
except Exception as e:
|
|
@@ -486,8 +528,12 @@ class TaskWindow(Gtk.Window):
|
|
|
486
528
|
w.set_halign(Gtk.Align.START)
|
|
487
529
|
w.set_relief(Gtk.ReliefStyle.NONE)
|
|
488
530
|
w.connect('clicked',self.app.mark_done,t)
|
|
531
|
+
|
|
532
|
+
w.connect('clicked',lambda button, self: self.task_rclick_menu.hide(),self)
|
|
533
|
+
w.connect('clicked',lambda button, self, t: self.shown_tasks[t['id']].destroy(),self,t)
|
|
489
534
|
vbox.pack_start(w, True, True, 5)
|
|
490
535
|
|
|
536
|
+
|
|
491
537
|
# Reassign session/randomness to be on this task,
|
|
492
538
|
w = Gtk.Button(label="Reassign last "+ str(round((self.app.session['duration'] / 60),1))+" minutes to this task" )
|
|
493
539
|
w.set_halign(Gtk.Align.START)
|
|
@@ -612,7 +658,6 @@ class TaskWindow(Gtk.Window):
|
|
|
612
658
|
# utils.dbg("key_press",key,s="taskwindow",l=1)
|
|
613
659
|
# utils.dbg("key_press event.state",event.state,s="taskwindow",l=1)
|
|
614
660
|
# utils.dbg("key_press_event",event,s="taskwindow",l=1)
|
|
615
|
-
# utils.dbg("ModifierType is CONTROL_MASK",Gdk.ModifierType.CONTROL_MASK,s="taskwindow",l=3)
|
|
616
661
|
|
|
617
662
|
if (event.state & Gdk.ModifierType.CONTROL_MASK) or key == 'Control_L':
|
|
618
663
|
self.modifyer_keys['control'] = True
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
class UserIdleTime():
|
|
6
|
+
def __init__(self, platform = None, **kwargs):
|
|
7
|
+
|
|
8
|
+
self.getters = {
|
|
9
|
+
'linux_wayland': self.get_idle_seconds_linux_wayland,
|
|
10
|
+
'linux_x': self.get_idle_seconds_linux_x,
|
|
11
|
+
'mac': self.get_idle_seconds_mac,
|
|
12
|
+
'windows': self.get_idle_seconds_windows,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if platform:
|
|
16
|
+
self.platform = platform
|
|
17
|
+
else:
|
|
18
|
+
self.detect_platform()
|
|
19
|
+
|
|
20
|
+
print('UserIdleTime platform set to',self.platform)
|
|
21
|
+
|
|
22
|
+
def get (self) :
|
|
23
|
+
''' Returns int of seconds since user was active '''
|
|
24
|
+
try:
|
|
25
|
+
return int(self.getters[self.platform]())
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(' CheckIdleTime failed')
|
|
28
|
+
print(e)
|
|
29
|
+
return 0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def detect_platform (self):
|
|
34
|
+
|
|
35
|
+
match (platform.system()):
|
|
36
|
+
case ('Linux'):
|
|
37
|
+
if os.environ.get('XDG_SESSION_TYPE') == "wayland":
|
|
38
|
+
self.platform = "linux_wayland"
|
|
39
|
+
else:
|
|
40
|
+
self.platform = "linux_x"
|
|
41
|
+
|
|
42
|
+
case ("Darwin"):
|
|
43
|
+
self.platform = "mac"
|
|
44
|
+
|
|
45
|
+
case ("Windows"):
|
|
46
|
+
self.platform = "windows"
|
|
47
|
+
|
|
48
|
+
return self.platform
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_idle_seconds_linux_wayland (self):
|
|
52
|
+
# https://askubuntu.com/a/1231995/292055
|
|
53
|
+
|
|
54
|
+
command = "dbus-send --print-reply --dest=org.gnome.Mutter.IdleMonitor /org/gnome/Mutter/IdleMonitor/Core org.gnome.Mutter.IdleMonitor.GetIdletime"
|
|
55
|
+
|
|
56
|
+
response = subprocess.getoutput(command)
|
|
57
|
+
|
|
58
|
+
idle_time = int(int(response.rsplit(None,1)[-1]) / 1000)
|
|
59
|
+
|
|
60
|
+
return idle_time
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_idle_seconds_linux_x (self):
|
|
64
|
+
idle_time = int(subprocess.getoutput('xprintidle')) / 1000 # Requires xprintidle (sudo apt install xprintidle)
|
|
65
|
+
return idle_time
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_idle_seconds_windows (self):
|
|
69
|
+
# https://stackoverflow.com/a/67091943/4692205
|
|
70
|
+
import win32api
|
|
71
|
+
|
|
72
|
+
idle_time = (win32api.GetTickCount() - win32api.GetLastInputInfo()) / 1000.0
|
|
73
|
+
|
|
74
|
+
return idle_time
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_idle_seconds_mac (self):
|
|
78
|
+
# https://stackoverflow.com/a/17966890/4692205
|
|
79
|
+
command = "ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {print $NF/1000000000; exit}'"
|
|
80
|
+
|
|
81
|
+
idle_time = int(float(subprocess.getoutput(command)))
|
|
82
|
+
return idle_time
|