-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.ahk
471 lines (419 loc) · 14.5 KB
/
main.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
;@Ahk2Exe-SetMainIcon icon.ico
;@Ahk2Exe-ExeName cronometro
; Script Name - Cronometro - Working hours counter
/**
This script provides a personalised and privacy focused way to pitch in
your working hours by creating a small widget on your screen's top left
corner with simple three button interface.
TODO: Configurable widget location
*/
IniRead , csv_file, config.ini, file, location
; Use a default csv_file in case of non-existent location
if not csv_file {
csv_file := A_ScriptDir . "\" . get_filename()
} else {
csv_file := csv_file . "\" . get_filename()
}
; Checks for the autostart
if FileExist(A_Startup . "\oc.lnk")
is_auto_start:= "checked"
global timestamp ; Timestamp to compare the time with
; Checks the current state running/paused
global is_running := false
global is_paused := false
; Time difference/delta to keep track of between each pause/start
global time_delta := 0
; Set gui positioning to bottom right corner
x_gui:= (A_ScreenWidth - 350*A_ScreenDPI/96)
y_gui := (A_ScreenHeight - 150*A_ScreenDPI/96)
global gui_name := "Cronometro"
; Enabled icon set
play_icon := "res\icons8-play-64.png"
pause_icon := "res\icons8-pause-64.png"
stop_icon := "res\icons8-stop-64.png"
edit_file_icon := "res\icons8-edit-file-64.png"
opened_folder_icon := "res\icons8-opened-folder-64.png"
settings_icon := "res\icons8-settings-64.png"
; Disabled icon set
play_icon_dis := "res\icons8-play-disabled-64.png"
pause_icon_dis := "res\icons8-pause-disabled-64.png"
stop_icon_dis := "res\icons8-stop-disabled-64.png"
edit_file_icon_dis := "res\icons8-edit-file-disabled-64.png"
opened_folder_icon_dis := "res\icons8-opened-folder-disabled-64.png"
settings_icon_dis := "res\icons8-settings-disabled-64.png"
; Minimise to tray icon
mini_tray := "res\icons8-close-32.png"
; Main Gui
Gui, Color, 2f3337, 000000
Gui, Add, Pic, vstart cWhite gstart x10 y10 w25 h25 , % play_icon
start_TT := "Start/resume a task/session"
Gui, Add, Pic, vstop cWhite gpause xp+35 yp w25 h25 , % pause_icon_dis
stop_TT := "Pause an already running session"
Gui, Add, Pic, vfinish cWhite gfinish xp+35 yp w25 h25 , % stop_icon_dis
finish_TT := "Finish a task/session"
Gui, Font, s15, Arial
Gui, Add, Text, vtimer cWhite xp+35 yp w90 h30 +Center, 00:00:00
Gui, Add, Pic, vdesc cWhite gdesc xp+100 yp w25 h25 , % edit_file_icon_dis
desc_TT := "Add task's description"
Gui, Add, Pic, xp+35 yp cWhite vopen_folder gopen w25 h25 , % opened_folder_icon
open_folder_TT := "Open worksheet directory"
Gui, Add, Pic, xp+35 yp cWhite vconfigure_crono gconfigure w25 h25 , % settings_icon
configure_crono_TT := "Cronometro settings"
Gui, Add, Pic, xp+27 y5 gGuiClose w12 h12 , % mini_tray
; Show the main gui
Gui, Show, w325 h15 x%x_gui% y%y_gui%, % gui_name
Gui, +LastFound +AlwaysOnTop -Caption +Owner
; Disable irrelevant controls (Since this is our first run)
GuiControl , Disable, stop
GuiControl , Disable, desc
GuiControl , Disable, finish
; Task window to add task descriptions
Gui, task: Add, Text, x12 y10 w210 , Add Task Description`r`n(Describe complete details of your Task)
Gui, task: Font, s15, Arial
Gui, task: Add, Button, Disabled x262 y9 w60 h30,% chr(128247)
Gui, task: Font, ,
Gui, task: Add, Edit, x12 y49 w310 h120 vdescription,
Gui, task: Add, Button, x122 y179 w100 h30 , Done
; Retrieve folder from csv_file to show inside the configuration window
SplitPath , csv_file, , csv_folder
split_checked := get_filename(true)
; labels are based according to the config
; Do not mess this up unless you are absolutely sure about what you are doing
radio_labels := ["Current day", "Current month", "App run", "Current year"]
; Create a configuration window
Gui, config: Add, CheckBox, von_start gautoStart %is_auto_start% x32 y9 w210 h20 , Start with windows
Gui, config: Add, Text, x32 y39 w210 h20 , Split worksheet files based on
Gui, config: Add, Radio, vworsheets_split xp yp w0 h0, ; Fake label to store radio info
; This will make sure that your last configuration will be selected on the radio control
for key, label in radio_labels {
radio_checked :=
if (key = split_checked)
radio_checked := "Checked"
Gui, config: Add, Radio, %radio_checked% xp yp+20 h20 , % label
}
Gui, config: Add, Edit, vcsv_folder_browser ReadOnly x22 y169 w200 h20 , %csv_folder%
Gui, config: Add, Button, x122 y199 w100 h20 , Browse
Gui, config: Add, Button, x62 y239 w100 h30 , Save
Gui, config: Add, Text, x22 y149 w200 h20 , Save worksheets to
; Make sure to log the App run
if not FileExist(csv_file)
FileAppend , % "ACTION, DATE, TIME, TOTAL`r`n", % csv_file
; Customise the tray menu
Menu, Tray, NoStandard
Menu, Tray, Add, Show, show
Menu, Tray, Add, Configure, configure
Menu, Tray, Add, Open Worksheet Folder, open
Menu, Tray, Add, Exit, exit
; Handles System Shutdown and Logoff
OnMessage(0x11, "WM_QUERYENDSESSION")
; Handles Tray clicks
OnMessage(0x404, "AHK_NOTIFYICON")
; Handles Mouse hovers for tooltips
OnMessage(0x200, "WM_MOUSEMOVE")
; Make window a bit transparent
WinSet, Transparent, 240, % gui_name
return
; Show the main gui
show:
WinShow , % gui_name
return
; Pressing cross button won't let you exit the app
; TODO: Make it configurable
GuiClose:
WinHide , % gui_name
return
; Actual app exit
; TODO : Prevent shutdown action to destroy app data
; while the app is tracking time in the background
exit:
ex_action("exit")
ExitApp
; Start the time tracking
start:
; Enable and disable controls according to this action
; TODO: optimise disabling by removing redundant code
GuiControl , Disable, start
GuiControl , Enable, stop
GuiControl , Enable, desc
GuiControl , Enable, finish
; Set Icons accordingly
GuiControl,, start, % play_icon_dis
GuiControl,, stop, % pause_icon
GuiControl,, finish, % stop_icon
GuiControl,, desc, % edit_file_icon
if is_paused{
ex_action("resume")
} else {
ex_action("start")
}
; start a timer watch
; for aesthetics purpose only
SetTimer , stopwatch, 1
return
; Pauses the time tracking
pause:
GuiControl , Enable, start
GuiControl , Disable, stop
GuiControl , Disable, desc
; Set Icons accordingly
GuiControl,, start, % play_icon
GuiControl,, stop, % pause_icon_dis
GuiControl,, desc, % edit_file_icon_dis
ex_action("pause")
SetTimer , stopwatch, Off
return
; Add task desctiption (Will only work when you are running a task)
desc:
Gui, task: Show, w336 h219, Add Task Description
return
; finish a task
finish:
GuiControl, Enable, start
GuiControl, Disable, stop
GuiControl , Disable, finish
; Set Icons accordingly
GuiControl,, start, % play_icon
GuiControl,, stop, % pause_icon_dis
GuiControl,, finish, % stop_icon_dis
ex_action("finish")
SetTimer , stopwatch, Off
return
; Description of a task is updated.
taskButtonDone:
Gui, task: submit, Nohide
ex_action("update", description)
WinHide , Add Task Description
return
; Configure Chronometro
configure:
Gui, config: Show, w240 h281 , Configure
return
; Open the folder containing worksheets
open:
Run, % csv_folder
return
; A time controlled subroutine to show timer on gui
stopwatch:
GuiControl , , timer, % FormatTimeStamp(A_TickCount - timestamp + time_delta)
return
; A g-label to make sure that this script link
; will be added into the startup folder
autoStart:
Gui, config:submit, nohide
if on_start
FileCreateShortcut, %A_ScriptFullPath%, %A_Startup%\oc.lnk, %A_ScriptDir%
else
FileDelete, %A_Startup%\oc.lnk
return
; Save configurations and make them effective
configButtonSave:
Gui, config:submit, nohide
; worksheets_split -1 because, we are using a fake radio button
; to store our variable
set_filename(worsheets_split-1)
; Make sure that we are using the same file based on
; worksheet settings
IniWrite , %csv_folder% , config.ini, file, location
csv_file := csv_folder . "/" . get_filename()
; Hide the main configuration window
WinHide , Configure
return
; Browse Button for selecting the worksheet folder
configButtonBrowse:
Gui, config:submit, nohide
FileSelectFolder , csv_folder, csv_folder, , Select a folder to save your worksheets
if csv_folder {
GuiControl , config: , csv_folder_browser, %csv_folder%
}
return
/**
Method name - ex_action - execute action
params:-
command - action name based on button pressed. start, stop/pause, resume, finish, update, exit
desc - Task description (optional)
returns :-
null
*/
ex_action(command:="start", desc := ""){
; Use the golbal namespace variables
global timestamp, is_running, csv_file, is_paused
; There are two main parts of a particular action
; 1. Appending the CSV file based on the action selected
; 2. Updating the is_running/is_paused status
switch (command){
case "start":
timestamp := A_TickCount
FileAppend , % "`r`nSession Started, " . ( A_DD . "/" . A_MMM . "/" . A_YYYY ) . ", " . A_Hour . ":" . A_Min . ":" . A_Sec . ", `r`n", % csv_file
WinSetTitle, % gui_name . "| Running..."
is_running := true
return
case "stop":
case "pause":
FileAppend, % "Paused (Total Session - " . FormatTimeStamp(A_TickCount - timestamp + time_delta) . ") , " . ( A_DD . "/" . A_MMM . "/" . A_YYYY ) . ", " . A_Hour . ":" . A_Min . ":" . A_Sec . ", `r`n", % csv_file
WinSetTitle, % gui_name . "| Paused"
is_running := false
is_paused := true
time_delta := A_TickCount - timestamp + time_delta
return
case "resume":
timestamp := A_TickCount
FileAppend , % "Session Resumed, " . ( A_DD . "/" . A_MMM . "/" . A_YYYY ) . ", " . A_Hour . ":" . A_Min . ":" . A_Sec . ", `r`n", % csv_file
WinSetTitle, % gui_name . "| Running (Resumed)..."
is_paused := false
is_running := true
return
case "finish":
FileAppend , % "Finished, " . ( A_DD . "/" . A_MMM . "/" . A_YYYY ) . ", " . A_Hour . ":" . A_Min . ":" . A_Sec . ", " . (is_running ? FormatTimeStamp(A_TickCount - timestamp + time_delta): FormatTimeStamp(time_delta)) . "`r`n", % csv_file
WinSetTitle, % gui_name . "| Finished"
is_running := false
is_paused := false
time_delta := 0
GuiControl , , timer, % FormatTimeStamp(0)
return
case "update":
FileAppend , % " " """" . desc . " "" [Info Added], " . ( A_DD . "/" . A_MMM . "/" . A_YYYY ) . ", " . A_Hour . ":" . A_Min . ":" . A_Sec . ",`r`n", % csv_file
return
case "exit":
; Only log the elapsed time when our app is actively running/paused as a failsafe against aburptly exiting the app
if (is_running or is_paused)
FileAppend , % "Exited/Finished, " . ( A_DD . "/" . A_MMM . "/" . A_YYYY ) . ", " . A_Hour . ":" . A_Min . ":" . A_Sec . ", "
. (is_running ? FormatTimeStamp(A_TickCount - timestamp + time_delta): ( is_paused ? FormatTimeStamp(time_delta) : "" )) . " `r`n`r`n", % csv_file
return
}
}
/**
Method name - get_filename
params:-
raw - whether to return the actual index of radio control (i.e. raw index) or not
returns :-
filename based on the selection of our worksheet split type in configuration (default month based)
*/
get_filename(raw:=false){
IniRead , csv_split, config.ini, file, split
switch (csv_split){
case "day":
return raw ? 1 : "working_hours_raw_" . A_DD . "_" . A_MMM . "_" . A_YYYY . ".csv"
case "month":
return raw ? 2 : "working_hours_raw_" . A_MMM . "_" . A_YYYY . ".csv"
case "app":
return raw ? 3 : "working_hours_app_run_" . A_Now . A_MMM . "_" . A_YYYY . ".csv"
case "year":
return raw ? 4 : "working_hours_raw_" . A_YYYY . ".csv"
case Default:
return raw ? 2: "working_hours_raw_" . A_MMM . "_" . A_YYYY . ".csv"
}
}
/**
Method name - set_filename - used internally for the configuration purpose only
params:-
Index - Index of the radiobutton
returns :-
null
*/
set_filename(index){
switch (index){
case 1: IniWrite , % "day" , config.ini, file, split
case 2: IniWrite , % "month" , config.ini, file, split
case 3: IniWrite , % "app" , config.ini, file, split
case 4: IniWrite , % "year" , config.ini, file, split
}
}
/**
Method name - FormatTimeStamp - Convert the specified number of milliseconds to hh:mm:ss format.
params:-
delta - Total time elapsed in ms
returns :-
Time string -> Number of hours:mins:secs elapsed
*/
FormatTimeStamp(delta)
{
delta := delta/1000
hours := Format("{:02}", floor(delta/3600))
mins := Format("{:02}", floor((delta/60) - (hours*60)))
sec := Format("{:02}", floor(delta - hours*3600 - mins*60))
return % hours . ":" . mins . ":" . sec
}
/**
The following snippets has been taken from the Autohotkey Documentation
*/
;Clicking the icon will lead to Main window
AHK_NOTIFYICON(wParam, lParam, uMsg, hWnd)
{
global gui_name
if (lParam = 0x0201)
WinShow , % gui_name
}
WM_QUERYENDSESSION(wParam, lParam)
{
global is_running, is_paused
ENDSESSION_LOGOFF := 0x80000000
if (lParam & ENDSESSION_LOGOFF) ; User is logging off.
EventType := "Logoff"
else ; System is either shutting down or restarting.
EventType := "Shutdown"
try
{
if (is_running or is_paused) {
; Set a prompt for the OS shutdown UI to display. We do not display
; our own confirmation prompt because we have only 5 seconds before
; the OS displays the shutdown UI anyway. Also, a program without
; a visible window cannot block shutdown without providing a reason.
BlockShutdown("Chronometro attempting to prevent " EventType ".")
return false
} else {
ex_action("exit")
return true
}
}
catch
{
; ShutdownBlockReasonCreate is not available, so this is probably
; Windows XP, 2003 or 2000, where we can actually prevent shutdown.
MsgBox, 4,, %EventType% in progress. Allow it?
IfMsgBox Yes
{
ex_action("exit")
return true ; Tell the OS to allow the shutdown/logoff to continue.
} else {
return false ; Tell the OS to abort the shutdown/logoff.
}
}
}
BlockShutdown(Reason)
{
; If your script has a visible GUI, use it instead of A_ScriptHwnd.
DllCall("ShutdownBlockReasonCreate", "ptr", A_ScriptHwnd, "wstr", Reason)
OnExit("StopBlockingShutdown")
}
StopBlockingShutdown()
{
ex_action("exit")
OnExit(A_ThisFunc, 0)
DllCall("ShutdownBlockReasonDestroy", "ptr", A_ScriptHwnd)
}
; This snippet is taken from AHK forums for tooltip on hover
WM_MOUSEMOVE(wparam, lParam, msg, hwnd)
{
static CurrControl, PrevControl, _TT ; _TT is kept blank for use by the ToolTip command below.
CurrControl := A_GuiControl
If (CurrControl <> PrevControl and not InStr(CurrControl, " "))
{
ToolTip ; Turn off any previous tooltip.
SetTimer, DisplayToolTip, 1000
PrevControl := CurrControl
} else {
if wparam = 1 ; LButton
PostMessage, 0xA1, 2,,, A ; WM_NCLBUTTONDOWN
}
return
DisplayToolTip:
try
ToolTip % %CurrControl%_TT
catch
ToolTip
SetTimer, RemoveToolTip, -2000
return
RemoveToolTip:
ToolTip
return
}