File: clientgui.py

package info (click to toggle)
timekpr-next 0.5.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,440 kB
  • sloc: python: 7,938; sh: 98; xml: 39; makefile: 6
file content (684 lines) | stat: -rw-r--r-- 38,810 bytes parent folder | download | duplicates (2)
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
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
"""
Created on Aug 28, 2018

@author: mjasnik
"""

import gi
import os
import re
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from datetime import datetime, timedelta

# timekpr imports
from timekpr.common.constants import constants as cons
from timekpr.common.log import log
from timekpr.common.constants import messages as msg

# constant
_NO_TIME_LABEL = "--:--:--"
_NO_TIME_LABEL_SHORT = "--:--"
_NO_TIME_LIMIT_LABEL = "--:--:--:--"
_HOUR_REGEXP = re.compile("^([0-9]{1,2})$")
_HOUR_MIN_REGEXP = re.compile("^([0-9]{1,2}):([0-9]{1,2})$")


class timekprGUI(object):
    """Main class for supporting timekpr forms"""

    def __init__(self, pTimekprVersion, pTimekprClientConfig, pUsername, pUserNameFull):
        """Initialize gui"""
        # set up base variables
        self._userName = pUsername
        self._timekprVersion = pTimekprVersion
        self._timekprClientConfig = pTimekprClientConfig
        self._timekprPTPageNr = 2

        # sets up limit variables
        self._timeSpent = None
        self._timeSpentWeek = None
        self._timeSpentMonth = None
        self._timeInactive = None
        self._timeLeftToday = None
        self._timeLeftContinous = None
        self._timeTrackInactive = True
        self._timeTimeLimitOverridePT = False
        self._timeUnaccountedIntervalsFlagPT = False
        self._timeSpentPT = None
        self._timeLeftPT = None
        self._timePTActivityCntStr = "0"
        self._limitConfig = {}

        # change tracking
        self._configChanged = False
        # ## forms builders ##
        # init about builder
        self._timekprAboutDialogBuilder = Gtk.Builder()
        # get our dialog
        self._timekprAboutDialogBuilder.add_from_file(os.path.join(self._timekprClientConfig.getTimekprSharedDir(), "client/forms", "about.glade"))
        # get main form (to set various runtime things)
        self._timekprAboutDialog = self._timekprAboutDialogBuilder.get_object("timekprAboutDialog")

        # init config builder
        self._timekprConfigDialogBuilder = Gtk.Builder()
        # get our dialog
        self._timekprConfigDialogBuilder.add_from_file(os.path.join(self._timekprClientConfig.getTimekprSharedDir(),  "client/forms", "client.glade"))
        # get main form (to set various runtime things)
        self._timekprConfigDialog = self._timekprConfigDialogBuilder.get_object("timekprConfigDialog")

        self._timekprAboutDialogBuilder.connect_signals(self)
        self._timekprConfigDialogBuilder.connect_signals(self)

        # set up username (this does not change)
        self._timekprConfigDialogBuilder.get_object("timekprUsernameLB").set_text("%s (%s)" % (self._userName, pUserNameFull) if pUserNameFull != "" else self._userName)

        # this sets up columns for days config
        col = Gtk.TreeViewColumn("Day", Gtk.CellRendererText(), text=1)
        col.set_min_width(100)
        self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").append_column(col)
        col = Gtk.TreeViewColumn("Limit", Gtk.CellRendererText(), text=2)
        col.set_min_width(60)
        self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").append_column(col)

        # this sets up columns for interval list
        # interval string
        col = Gtk.TreeViewColumn("Interval", Gtk.CellRendererText(), text=0)
        self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").append_column(col)
        # unaccountable interval column
        col = Gtk.TreeViewColumn("Unaccounted", Gtk.CellRendererText(), text=1)
        col.set_min_width(10)
        self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").append_column(col)

        # PlayTime
        # this sets up columns for limits list
        col = Gtk.TreeViewColumn("Day", Gtk.CellRendererText(), text=1)
        col.set_min_width(100)
        self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").append_column(col)
        col = Gtk.TreeViewColumn("Limit", Gtk.CellRendererText(), text=2)
        col.set_min_width(60)
        self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").append_column(col)
        # this sets up columns for process list
        col = Gtk.TreeViewColumn("Day", Gtk.CellRendererText(), text=0)
        col.set_min_width(140)
        self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsApplsTreeview").append_column(col)

        # hide PT page by default
        self._timekprConfigDialogBuilder.get_object("timekprConfigNotebook").get_nth_page(self._timekprPTPageNr).set_visible(False)
        # hide PT config as well
        self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigPlayTimeGrid").set_visible(False)

        # initial config (everything is to the max)
        for i in range(0, 7):
            # set up default limits
            self._limitConfig[str(i+1)] = {cons.TK_CTRL_LIMITD: None, cons.TK_CTRL_INT: [[None, None, False]]}

        # initialize week and month limits
        self._limitConfig[cons.TK_CTRL_LIMITW] = {cons.TK_CTRL_LIMITW: None}
        self._limitConfig[cons.TK_CTRL_LIMITM] = {cons.TK_CTRL_LIMITM: None}

        # ## notification configuration ##
        # Less than
        rend = Gtk.CellRendererText()
        rend.set_property("editable", True)
        rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_PHLD_LABEL"))
        rend.connect("edited", self.userTimeEdited)
        col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_LABEL"), rend, text=1)
        col.set_min_width(90)
        self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView").append_column(col)

        # importance
        rend = Gtk.CellRendererCombo()
        rend.set_property("editable", True)
        rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_PHLD_LABEL"))
        rend.set_property("model", self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS"))
        rend.set_property("text-column", 1)
        rend.set_property("has-entry", False)
        rend.connect("edited", self.userPriorityEdited)
        col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_LABEL"), rend, text=3)
        col.set_min_width(120)
        self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView").append_column(col)
        # clear
        self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS").clear()

        # ## PlayTime notification configuration ##
        # Less than
        rend = Gtk.CellRendererText()
        rend.set_property("editable", True)
        rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_PHLD_LABEL"))
        rend.connect("edited", self.userPlayTimeEdited)
        col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_TIME_LABEL"), rend, text=1)
        col.set_min_width(90)
        self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigTreeView").append_column(col)

        # importance
        rend = Gtk.CellRendererCombo()
        rend.set_property("editable", True)
        rend.set_property("placeholder-text", msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_PHLD_LABEL"))
        rend.set_property("model", self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS"))
        rend.set_property("text-column", 1)
        rend.set_property("has-entry", False)
        rend.connect("edited", self.userPlayTimePriorityEdited)
        col = Gtk.TreeViewColumn(msg.getTranslation("TK_MSG_NOTIF_CONFIG_IMPORTANCE_LABEL"), rend, text=3)
        col.set_min_width(120)
        self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigTreeView").append_column(col)
        # clear
        self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS").clear()

        # status
        self.setStatus(msg.getTranslation("TK_MSG_STATUS_STARTED"))

    # --------------- TMP (move to proper places later) --------------- #

    def userTimeEdited(self, widget, path, text):
        """Set internal representation of in-place edited value"""
        self.setTimeValue(path, text, pConfType="Time")

    def userPlayTimeEdited(self, widget, path, text):
        """Set internal representation of in-place edited value"""
        self.setTimeValue(path, text, pConfType="PlayTime")

    def userPriorityEdited(self, widget, path, text):
        """Set internal representation of in-place edited value"""
        self.setPriorityValue(path, text, "Time")

    def userPlayTimePriorityEdited(self, widget, path, text):
        """Set internal representation of in-place edited value"""
        self.setPriorityValue(path, text, "PlayTime")

    def setTimeValue(self, path, text, pConfType):
        """Verify and set time string values"""
        # element
        prioLs = "TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS"
        # store
        timelSt = self._timekprConfigDialogBuilder.get_object(prioLs)
        # value before
        secsBefore = timelSt[path][0]
        secs = None
        # verify values
        if _HOUR_REGEXP.match(text):
            # calculate seconds
            secs = min(int(_HOUR_REGEXP.sub(r"\1", text)) * cons.TK_LIMIT_PER_HOUR, cons.TK_LIMIT_PER_DAY)
        elif _HOUR_MIN_REGEXP.match(text):
            # calculate seconds
            secs = min(int(_HOUR_MIN_REGEXP.sub(r"\1", text)) * cons.TK_LIMIT_PER_HOUR + int(_HOUR_MIN_REGEXP.sub(r"\2", text)) * cons.TK_LIMIT_PER_MINUTE, cons.TK_LIMIT_PER_DAY)

        # if we could calculate seconds (i.e. entered text is correct)
        if secs is not None:
            # only if changed
            if secsBefore != secs:
                # check if we have this interval already
                dupl = [rPrio for rPrio in timelSt if rPrio[0] == secs]
                # we can not allow duplicates
                if not len(dupl) > 0:
                    # format secs
                    textStr = self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=secs), "s")
                    # set values
                    timelSt[path][0] = secs
                    timelSt[path][1] = textStr
                    # sort
                    self.sortNotificationConfig(pConfType)
                    # verify controls too
                    self.processConfigChanged()

    def setPriorityValue(self, path, text, pConfType):
        """Verify and set time string values"""
        # element
        prioLs = "TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS"
        # store
        priolSt = self._timekprConfigDialogBuilder.get_object(prioLs)
        # value before
        prioBefore = priolSt[path][3]
        # only if priority actuall changed
        if prioBefore != text:
            # find selected value
            val = [(rVal[0], rVal[1]) for rVal in self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS") if rVal[1] == text]
            # set values
            priolSt[path][2] = val[0][0]
            priolSt[path][3] = val[0][1]
            # verify controls too
            self.processConfigChanged()

    def addNotificationConfigClicked(self, evt):
        """Add notification interval to the list"""
        self.addNotificationConf("Time")

    def addPlayTimeNotificationConfigClicked(self, evt):
        """Add notification interval to the list"""
        self.addNotificationConf("PlayTime")

    def removeNotificationConfigClicked(self, evt):
        """Remove notification interval"""
        self.removeNotificationConf("Time")

    def removePlayTimeNotificationConfigClicked(self, evt):
        """Remove notification interval"""
        self.removeNotificationConf("PlayTime")

    def addNotificationConf(self, pConfType):
        """Add notification interval to the list"""
        prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS")
        prioTw = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigTreeView")
        prioLen = len(prioSt)
        # add
        addRow = True

        # check if the last one is not empty (no need to add more empty rows)
        if (prioLen > 0 and prioSt[prioLen-1][0] < 0):
            addRow = False
        # we can add the row
        if addRow:
            # add
            prioSt.append([-1, "", "", ""])
            # scroll to end
            prioTw.set_cursor(prioLen)
            prioTw.scroll_to_cell(prioLen)

    def removeNotificationConf(self, pConfType):
        """Remove notification interval"""
        # defaults
        prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS")
        # refresh the child
        (tm, ti) = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigTreeView" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigTreeView").get_selection().get_selected()
        # only if there is smth selected
        elemIdx = tm.get_path(ti)[0] if ti is not None else None
        # only if something is selected
        if elemIdx is not None:
            rIdx = 0
            # remove selected item
            for rIt in prioSt:
                if elemIdx == rIdx:
                    # remove
                    prioSt.remove(rIt.iter)
                # count further
                rIdx += 1
            # verify controls too
            self.processConfigChanged()

    def sortNotificationConfig(self, pConfType):
        """Sort notification config for ease of use"""
        # element
        prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS" if pConfType == "Time" else "TimekprUserPlayTimeNotificationConfigLS")
        # sort vairables
        prio = {}
        rIdx = 0
        # prepare sort
        for rIt in prioSt:
            prio[rIt[0]] = rIdx
            # count further
            rIdx += 1
        # set sort order
        sortedPrio = []
        # set up proper order
        for rKey in sorted(prio, reverse=True):
            # append to order
            sortedPrio.append(prio[rKey])
        # reorder rows in liststore
        prioSt.reorder(sortedPrio)

    # --------------- helper methods --------------- #

    def formatTimeStr(self, pTime, pFormatType="f"):
        """Format time for output on form"""
        # f - full, s - short, t - time
        # final result
        timeStr = None
        if pTime is None:
            timeStr = _NO_TIME_LABEL_SHORT if pFormatType == "s" else _NO_TIME_LABEL if pFormatType == "t" else _NO_TIME_LIMIT_LABEL
        else:
            # calculate days
            days = (pTime - cons.TK_DATETIME_START).days
            # calculate hours and mins
            hrMin = "%s:%s" % (("24" if pFormatType != "f" and days >= 1 else str(pTime.hour)).rjust(2, "0"), str(pTime.minute).rjust(2, "0"))
            # calculate secs
            secs = str(pTime.second).rjust(2, "0")
            # final composition
            # for limit time (h:m:s)
            if pFormatType == "t":
                timeStr = "%s:%s" % (hrMin, secs)
            # for full time (d:h:m:s)
            else:
                timeStr = "%s:%s:%s" % (str(days).rjust(2, "0"), hrMin, secs) if pFormatType != "s" else hrMin
        # return
        return timeStr

    def renewUserConfiguration(self):
        """Update configuration options"""
        # if speech is not supported, we disable and uncheck the box
        if self._timekprClientConfig.getIsNotificationSpeechSupported():
            # disable speech
            self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").set_sensitive(False)
        # if sound is not supported by libnotify implementation, we disable and uncheck the box
        if self._timekprClientConfig.getIsNotificationSoundSupported():
            # disable speech
            self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").set_sensitive(False)

        # user config
        self._timekprConfigDialogBuilder.get_object("timekprLimitChangeNotifCB").set_active(self._timekprClientConfig.getClientShowLimitNotifications())
        self._timekprConfigDialogBuilder.get_object("timekprShowAllNotifCB").set_active(self._timekprClientConfig.getClientShowAllNotifications())
        self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").set_active(self._timekprClientConfig.getClientUseSpeechNotifications())
        self._timekprConfigDialogBuilder.get_object("timekprShowSecondsCB").set_active(self._timekprClientConfig.getClientShowSeconds())
        self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").set_active(self._timekprClientConfig.getClientUseNotificationSound())
        self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutSB").set_value(self._timekprClientConfig.getClientNotificationTimeout())
        self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutCriticalSB").set_value(self._timekprClientConfig.getClientNotificationTimeoutCritical())
        self._timekprConfigDialogBuilder.get_object("timekprLogLevelSB").set_value(self._timekprClientConfig.getClientLogLevel())
        # priority config
        prioConfSt = self._timekprConfigDialogBuilder.get_object("TimekprNotificationPrioritiesLS")
        # load notification priorities
        prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS")
        prioSt.clear()
        for rPrio in self._timekprClientConfig.getClientNotificationLevels():
            # append intervals
            val = [(rVal[0], rVal[1]) for rVal in prioConfSt if rVal[0] == cons.TK_PRIO_LVL_MAP[rPrio[1]]]
            prioSt.append([rPrio[0], self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=rPrio[0]), "s"), val[0][0], val[0][1]])
        # sort configd
        self.sortNotificationConfig("Time")
        # load PlayTime notification priorities
        prioSt = self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS")
        prioSt.clear()
        for rPrio in self._timekprClientConfig.getClientPlayTimeNotificationLevels():
            # append intervals
            val = [(rVal[0], rVal[1]) for rVal in prioConfSt if rVal[0] == cons.TK_PRIO_LVL_MAP[rPrio[1]]]
            prioSt.append([rPrio[0], self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=rPrio[0]), "s"), val[0][0], val[0][1]])
        # sort config
        self.sortNotificationConfig("PlayTime")
        # verify controls too
        self.processConfigChanged()

    def renewLimits(self, pTimeInformation=None):
        """Renew information to be show for user in GUI"""
        # sets time left
        if pTimeInformation is not None:
            # limits
            self._timeSpent = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SPENT])
            self._timeSpentWeek = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SPENTW])
            self._timeSpentMonth = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SPENTM])
            self._timeInactive = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_SLEEP])
            self._timeLeftToday = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_LEFTD])
            self._timeLeftContinous = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_LEFT])
            self._timeTrackInactive = True if pTimeInformation[cons.TK_CTRL_TRACK] else False
            self._timeTimeLimitOverridePT = bool(pTimeInformation[cons.TK_CTRL_PTTLO]) if cons.TK_CTRL_PTTLO in pTimeInformation else False
            self._timeUnaccountedIntervalsFlagPT = bool(pTimeInformation[cons.TK_CTRL_PTAUH]) if cons.TK_CTRL_PTAUH in pTimeInformation else False
            self._timeSpentPT = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_PTSPD]) if cons.TK_CTRL_PTSPD in pTimeInformation else None
            self._timeLeftPT = cons.TK_DATETIME_START + timedelta(seconds=pTimeInformation[cons.TK_CTRL_PTLPD]) if cons.TK_CTRL_PTLPD in pTimeInformation else None
            self._timePTActivityCntStr = str(pTimeInformation[cons.TK_CTRL_PTLSTC] if cons.TK_CTRL_PTLSTC in pTimeInformation else 0)

        # calculate strings to show (and show only those, which have data)
        timeSpentStr = self.formatTimeStr(self._timeSpent)
        timeSpentWeekStr = self.formatTimeStr(self._timeSpentWeek)
        timeSpentMonthStr = self.formatTimeStr(self._timeSpentMonth)
        timeSleepStr = self.formatTimeStr(self._timeInactive)
        timeLeftTodayStr = self.formatTimeStr(self._timeLeftToday)
        timeLeftTotalStr = self.formatTimeStr(self._timeLeftContinous)
        timeSpentPTStr = self.formatTimeStr(self._timeSpentPT)
        timeLeftPTStr = self.formatTimeStr(self._timeLeftPT if self._timeLeftPT is None else min(self._timeLeftPT, self._timeLeftToday)) if not self._timeTimeLimitOverridePT else _NO_TIME_LABEL

        # sets up stuff
        self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeSpeneLB").set_text(timeSpentStr)
        self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeSpentWeeeLB").set_text(timeSpentWeekStr)
        self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeSpentMonteLB").set_text(timeSpentMonthStr)
        self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeInactiveLB").set_text(timeSleepStr)
        self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTimeLeftTodaeLB").set_text(timeLeftTodayStr)
        self._timekprConfigDialogBuilder.get_object("timekprLimitInfoContTimeLefeLB").set_text(timeLeftTotalStr)
        self._timekprConfigDialogBuilder.get_object("timekprLimitInfoTrackInactiveCB").set_active(self._timeTrackInactive)
        self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoTimeLimitOverrideLB").set_active(self._timeTimeLimitOverridePT)
        self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoUnaccountedIntervalsFlagLB").set_active(self._timeUnaccountedIntervalsFlagPT)
        self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoTimeSpentTodayLB").set_text(timeSpentPTStr)
        self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoTimeLeftTodayLB").set_text(timeLeftPTStr)
        self._timekprConfigDialogBuilder.get_object("timekprPTLimitInfoActivityCountLB").set_text(self._timePTActivityCntStr)

    def setStatus(self, pStatus):
        """Change status of timekpr"""
        if pStatus is not None:
            # get main status
            statusBar = self._timekprConfigDialogBuilder.get_object("timekprStatusBar")
            contextId = statusBar.get_context_id("status")
            # pop existing message and add new one
            statusBar.remove_all(contextId)
            statusBar.push(contextId, pStatus)

    def renewLimitConfiguration(self, pLimits=None):
        """Renew information to be show for user"""
        # if there is smth
        if pLimits is not None:
            # new limits appeared
            self._limitConfig = pLimits

        # hide PT page by default
        enablePT = False
        # clear out days / limits / processes
        self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysLS").clear()
        self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysLS").clear()
        self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsActsLS").clear()

        # go in sorted order
        for rKey in sorted(self._limitConfig):
            # some of configuration needs different approach
            if rKey in (cons.TK_CTRL_LIMITW, cons.TK_CTRL_LIMITM):
                # set locally
                if self._limitConfig[rKey][rKey] is not None:
                    # limit
                    timeLimitWKMON = cons.TK_DATETIME_START + timedelta(seconds=self._limitConfig[rKey][rKey])
                    # limit
                    timeLimitWKMONStr = self.formatTimeStr(timeLimitWKMON)
                else:
                    timeLimitWKMONStr = _NO_TIME_LIMIT_LABEL

                # set week limit
                if rKey == cons.TK_CTRL_LIMITW:
                    # set up limits
                    self._timekprConfigDialogBuilder.get_object("timekprLimitForWeeeLB").set_text(timeLimitWKMONStr)
                elif rKey == cons.TK_CTRL_LIMITM:
                    # set up limits
                    self._timekprConfigDialogBuilder.get_object("timekprLimitForMonteLB").set_text(timeLimitWKMONStr)
            # check for override
            elif rKey == cons.TK_CTRL_PTTLO:
                # if enabled
                self._timeTimeLimitOverridePT = True if bool(self._limitConfig[rKey][rKey]) else False
            # check for allowed during unaccounted intervals
            elif rKey == cons.TK_CTRL_PTAUH:
                # if enabled
                self._timeUnaccountedIntervalsFlagPT = True if bool(self._limitConfig[rKey][rKey]) else False
            # for the days limits
            elif rKey in ("1", "2", "3", "4", "5", "6", "7"):
                # get time limit string
                timeLimitStr = self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=self._limitConfig[rKey][cons.TK_CTRL_LIMITD]) if self._limitConfig[rKey][cons.TK_CTRL_LIMITD] is not None else None, "t")
                # add limit to the list
                self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysLS").append([rKey, (cons.TK_DATETIME_START + timedelta(days=int(rKey)-1)).strftime("%A"), "%s" % (timeLimitStr)])

        # current day
        currDay = datetime.now().isoweekday()-1
        # calculate day index for scrolling
        dayIdx = 0
        finalDayIdx = None

        # PT limits are processed separately due to override detection
        if cons.TK_CTRL_PTLMT in self._limitConfig and cons.TK_CTRL_PTLST in self._limitConfig and cons.TK_CTRL_PTTLE in self._limitConfig:
            # PlayTime
            for rKey in (cons.TK_CTRL_PTLMT, cons.TK_CTRL_PTLST, cons.TK_CTRL_PTTLE):
                # PT enable
                if rKey == cons.TK_CTRL_PTTLE:
                    # enable PT
                    enablePT = bool(self._limitConfig[rKey][cons.TK_CTRL_PTTLE])
                # PT limits
                elif rKey == cons.TK_CTRL_PTLMT:
                    # for all days
                    for rDay in self._limitConfig[rKey][cons.TK_CTRL_PTLMT]:
                        # count
                        dayIdx += 1
                        # if override enabled, we do not show limits because that's not meaningful
                        timeLimitStr = self.formatTimeStr(cons.TK_DATETIME_START + timedelta(seconds=rDay[1]) if not self._timeTimeLimitOverridePT else None, "t")
                        # add to the list
                        self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysLS").append([rDay[0], (cons.TK_DATETIME_START + timedelta(days=int(rDay[0])-1)).strftime("%A"), "%s" % (timeLimitStr)])
                        # if alllowed list has current day
                        if currDay == int(rDay[0]) + 1:
                            # index
                            finalDayIdx = dayIdx
                # PT process list
                elif rKey == cons.TK_CTRL_PTLST:
                    # all activities (source array format: 0 - friendly name, 1 - process name)
                    for rAppl in self._limitConfig[rKey][cons.TK_CTRL_PTLST]:
                        # add process to the list
                        self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsActsLS").append(["%s" % (rAppl[1] if rAppl[1] != "" else rAppl[0])])

        # determine curent day and point to it
        self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").set_cursor(currDay)
        self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").scroll_to_cell(currDay)
        # do the same for PT
        if finalDayIdx is not None:
            # scroll to current day
            self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").set_cursor(currDay)
            self._timekprConfigDialogBuilder.get_object("timekprPTAllowedDaysLimitsDaysTreeview").scroll_to_cell(currDay)

    def processConfigChanged(self):
        """Determine whether config has been changed and enable / disable apply"""
        # initial
        configChanged = False
        # determine what's changed
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprLimitChangeNotifCB").get_active() != self._timekprClientConfig.getClientShowLimitNotifications()
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprShowAllNotifCB").get_active() != self._timekprClientConfig.getClientShowAllNotifications()
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").get_active() != self._timekprClientConfig.getClientUseSpeechNotifications()
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprShowSecondsCB").get_active() != self._timekprClientConfig.getClientShowSeconds()
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").get_active() != self._timekprClientConfig.getClientUseNotificationSound()
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutSB").get_value_as_int() != self._timekprClientConfig.getClientNotificationTimeout()
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutCriticalSB").get_value_as_int() != self._timekprClientConfig.getClientNotificationTimeoutCritical()
        configChanged = configChanged or self._timekprConfigDialogBuilder.get_object("timekprLogLevelSB").get_value_as_int() != self._timekprClientConfig.getClientLogLevel()
        # interval changes
        tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
        configChanged = configChanged or self._timekprClientConfig.getClientNotificationLevels() != tmpVal
        # interval changes
        tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
        configChanged = configChanged or self._timekprClientConfig.getClientPlayTimeNotificationLevels() != tmpVal

        # this is it
        self._timekprConfigDialogBuilder.get_object("timekprSaveBT").set_sensitive(configChanged)

    # --------------- init methods --------------- #

    def initAboutForm(self):
        """Initialize about form"""
        # version
        self._timekprAboutDialog.set_version(self._timekprVersion)
        # comment
        self._timekprAboutDialog.set_comments(msg.getTranslation("TK_MSG_LOGO_LABEL"))

        # show up all
        self._timekprAboutDialog.show()
        self._timekprAboutDialog.run()

        # hide for later use
        self._timekprAboutDialog.hide()

    def initConfigForm(self):
        """Initialize config form"""
        # refresh info
        self.renewUserConfiguration()
        self.renewLimits()
        self.renewLimitConfiguration()
        self.configPageSwitchSignal()

        # show up all
        self._timekprConfigDialog.show()
        self._timekprConfigDialog.run()

        # hide for later use
        self._timekprConfigDialog.hide()

    # --------------- user clicked methods --------------- #

    def clientConfigChangedSignal(self, evt):
        """Process config changed signal"""
        self.processConfigChanged()

    def daysChangedSignal(self, evt):
        """Refresh intervals when days change"""
        # refresh the child
        (tm, ti) = self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysDaysTreeview").get_selection().get_selected()
        # only if there is smth selected
        if ti is not None:
            # get current seconds
            dt = datetime.now().replace(microsecond=0)
            dtd = str(datetime.date(dt).isoweekday())
            dts = int((dt - datetime.now().replace(microsecond=0, second=0, minute=0, hour=0)).total_seconds())
            idx = 0
            selIdx = 0
            # clear out existing intervals
            self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").clear()
            # fill intervals only if that day exists
            if tm.get_value(ti, 0) in self._limitConfig:
                # if no intervals
                if not self._limitConfig[tm.get_value(ti, 0)][cons.TK_CTRL_INT]:
                    # fill in the intervals with empty values
                    self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").append([("%s - %s") % (_NO_TIME_LABEL_SHORT, _NO_TIME_LABEL_SHORT), ""])
                else:
                    # fill the intervals
                    for r in self._limitConfig[tm.get_value(ti, 0)][cons.TK_CTRL_INT]:
                        # determine which is the current hour
                        selIdx = idx if r[0] is not None and r[0] <= dts <= r[1] and dtd == tm.get_value(ti, 0) else selIdx
                        # if we have no data, we fill this up with nothing
                        if r[0] is None or (r[0] == 0 and r[1] == 0):
                            # fill in the intervals with empty values
                            self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").append([("%s - %s") % (_NO_TIME_LABEL_SHORT, _NO_TIME_LABEL_SHORT), ""])
                        else:
                            start = (cons.TK_DATETIME_START + timedelta(seconds=r[0]))
                            end = (cons.TK_DATETIME_START + timedelta(seconds=r[1]))
                            uacc = "∞" if r[2] else ""
                            # fill in the intervals
                            self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsLS").append([("%s:%s - %s:%s") % (str(start.hour).rjust(2, "0"), str(start.minute).rjust(2, "0"), str(end.hour).rjust(2, "0") if r[1] < cons.TK_LIMIT_PER_DAY else "24", str(end.minute).rjust(2, "0")), uacc])
                        # count
                        idx += 1
                # set selection to found row
                self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").set_cursor(selIdx)
                self._timekprConfigDialogBuilder.get_object("timekprAllowedDaysIntervalsTreeview").scroll_to_cell(selIdx)

    def configPageSwitchSignal(self, nb=None, pg=None, pgn=None):
        """Enable or disable apply on page change"""
        # nothing here
        pass

    def saveUserConfigSignal(self, evt):
        """Save the configuration using config file manager"""
        # get config, set config to manager and save it
        self._timekprClientConfig.setClientShowLimitNotifications(self._timekprConfigDialogBuilder.get_object("timekprLimitChangeNotifCB").get_active())
        self._timekprClientConfig.setClientShowAllNotifications(self._timekprConfigDialogBuilder.get_object("timekprShowAllNotifCB").get_active())
        self._timekprClientConfig.setClientUseSpeechNotifications(self._timekprConfigDialogBuilder.get_object("timekprUseSpeechNotifCB").get_active())
        self._timekprClientConfig.setClientShowSeconds(self._timekprConfigDialogBuilder.get_object("timekprShowSecondsCB").get_active())
        self._timekprClientConfig.setClientUseNotificationSound(self._timekprConfigDialogBuilder.get_object("timekprUseNotificationSoundCB").get_active())
        self._timekprClientConfig.setClientNotificationTimeout(self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutSB").get_value_as_int())
        self._timekprClientConfig.setClientNotificationTimeoutCritical(self._timekprConfigDialogBuilder.get_object("timekprNotificationTimeoutCriticalSB").get_value_as_int())
        self._timekprClientConfig.setClientLogLevel(self._timekprConfigDialogBuilder.get_object("timekprLogLevelSB").get_value_as_int())
        # save notification priorities
        tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
        self._timekprClientConfig.setClientNotificationLevels(tmpVal)
        # save PlayTime notification priorities
        tmpVal = [[rVal[0], cons.TK_PRIO_LVL_MAP[rVal[2]]] for rVal in self._timekprConfigDialogBuilder.get_object("TimekprUserPlayTimeNotificationConfigLS") if rVal[2] in cons.TK_PRIO_LVL_MAP and rVal[0] > 0]
        self._timekprClientConfig.setClientPlayTimeNotificationLevels(tmpVal)

        # save config
        self._timekprClientConfig.saveClientConfig()
        # disable apply for now
        self._timekprConfigDialogBuilder.get_object("timekprSaveBT").set_sensitive(False)
        # enable as well
        log.setLogLevel(self._timekprClientConfig.getClientLogLevel())

    def closePropertiesSignal(self, evt):
        """Close the config form"""
        # close
        self._timekprConfigDialog.hide()

    def preventDestroyingDialogSignal(self, evt, bs):
        """Prevent destroying the dialog"""
        return False

    # --------------- helper methods --------------- #

    def isPlayTimeAccountingInfoEnabled(self):
        """Whether PlayTime controls are enabled"""
        return self._timekprConfigDialogBuilder.get_object("timekprConfigNotebook").get_nth_page(self._timekprPTPageNr).get_visible()

    def setPlayTimeAccountingInfoEnabled(self, pState):
        """Whether PlayTime controls are enabled"""
        # enable page
        self._timekprConfigDialogBuilder.get_object("timekprConfigNotebook").get_nth_page(self._timekprPTPageNr).set_visible(pState)
        # enable config
        self._timekprConfigDialogBuilder.get_object("TimekprUserNotificationConfigPlayTimeGrid").set_visible(pState)