diff --git a/README.md b/README.md index 813043e8..550b5243 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ sudo apt-get install safeeyes ### Fedora ```bash -sudo dnf install libappindicator-gtk3 python3-psutil python3-packaging cairo-devel python3-devel gobject-introspection-devel cairo-gobject-devel +sudo dnf install python3-psutil python3-packaging cairo-devel python3-devel gobject-introspection-devel cairo-gobject-devel sudo pip3 install safeeyes sudo gtk-update-icon-cache /usr/share/icons/hicolor ``` @@ -105,9 +105,7 @@ flatpak install flathub io.github.slgobinath.SafeEyes Ensure to meet the following dependencies: -- gir1.2-appindicator3-0.1 or gir1.2-ayatanaappindicator3-0.1 - gir1.2-notify-0.7 -- libappindicator-gtk3 - python3-psutil - xprintidle (optional) - wlrctl (wayland optional) diff --git a/debian/control b/debian/control index 85544b33..c07cbb88 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,7 @@ Homepage: https://github.com/slgobinath/SafeEyes/ Package: safeeyes Architecture: all -Depends: ${misc:Depends}, ${python3:Depends}, gir1.2-ayatanaappindicator3-0.1, python3 (>= 3.10.0), python3-xlib, python3-dbus, gir1.2-notify-0.7, python3-babel, x11-utils, xprintidle, alsa-utils, python3-psutil, python3-croniter, python3-packaging +Depends: ${misc:Depends}, ${python3:Depends}, python3 (>= 3.10.0), python3-xlib, gir1.2-notify-0.7, python3-babel, x11-utils, xprintidle, alsa-utils, python3-psutil, python3-croniter, python3-packaging Description: Safe Eyes Safe Eyes is a simple tool to remind you to take periodic breaks for your eyes. This is essential for anyone spending more time on the computer to avoid eye strain and other physical problems. . diff --git a/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po index 17e1ba99..8dbc9caf 100644 --- a/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ar/LC_MESSAGES/safeeyes.po @@ -542,6 +542,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "مهلة تصفير الإعدادات (ساعات)" diff --git a/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po index abac29dc..fc24276d 100644 --- a/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/bg/LC_MESSAGES/safeeyes.po @@ -527,3 +527,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/ca/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ca/LC_MESSAGES/safeeyes.po index f02951f3..d4e8a1fa 100644 --- a/safeeyes/config/locale/ca/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ca/LC_MESSAGES/safeeyes.po @@ -531,3 +531,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po index f1d76abe..cfcd9412 100644 --- a/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/cs/LC_MESSAGES/safeeyes.po @@ -538,6 +538,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Interval pro resetování statistik (v hodinách)" diff --git a/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po index a851c123..378302b5 100644 --- a/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/da/LC_MESSAGES/safeeyes.po @@ -531,6 +531,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Interval for nulstilling af statistik (i timer)" diff --git a/safeeyes/config/locale/de/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/de/LC_MESSAGES/safeeyes.po index 98b33075..b90b1658 100644 --- a/safeeyes/config/locale/de/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/de/LC_MESSAGES/safeeyes.po @@ -540,6 +540,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "Permanent deaktivieren" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Zeit bis die Statistik zurückgesetzt wird (in Stunden)" diff --git a/safeeyes/config/locale/en_US/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/en_US/LC_MESSAGES/safeeyes.po index e0122f77..564464c0 100644 --- a/safeeyes/config/locale/en_US/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/en_US/LC_MESSAGES/safeeyes.po @@ -533,6 +533,33 @@ msgstr "Skipped or postponed %d/%d breaks in a row" msgid "RSI Prevention" msgstr "RSI Prevention" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Interval to reset statistics (in hours)" diff --git a/safeeyes/config/locale/eo/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/eo/LC_MESSAGES/safeeyes.po index f2159dcf..cb40dc55 100644 --- a/safeeyes/config/locale/eo/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/eo/LC_MESSAGES/safeeyes.po @@ -533,6 +533,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Intertempo por restarigi statistikojn (en horoj)" diff --git a/safeeyes/config/locale/es/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/es/LC_MESSAGES/safeeyes.po index 40d76ba7..9ae2b3ff 100644 --- a/safeeyes/config/locale/es/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/es/LC_MESSAGES/safeeyes.po @@ -541,6 +541,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Intervalo para restablecer estadísticas (en horas)" diff --git a/safeeyes/config/locale/et/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/et/LC_MESSAGES/safeeyes.po index 1dea9df5..4054b5e4 100644 --- a/safeeyes/config/locale/et/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/et/LC_MESSAGES/safeeyes.po @@ -532,6 +532,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Statistika lähtestamise intervall (tundides)" diff --git a/safeeyes/config/locale/eu/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/eu/LC_MESSAGES/safeeyes.po index d7f2e2ea..ec893896 100644 --- a/safeeyes/config/locale/eu/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/eu/LC_MESSAGES/safeeyes.po @@ -537,3 +537,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/fa/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/fa/LC_MESSAGES/safeeyes.po index ec0afe41..7b39ab29 100644 --- a/safeeyes/config/locale/fa/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/fa/LC_MESSAGES/safeeyes.po @@ -532,6 +532,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "فاصله برای تنظیم مجدد آمار (در چند ساعت)" diff --git a/safeeyes/config/locale/fr/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/fr/LC_MESSAGES/safeeyes.po index a0dd520b..b4b45ffa 100644 --- a/safeeyes/config/locale/fr/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/fr/LC_MESSAGES/safeeyes.po @@ -545,6 +545,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Fréquence de réinitialisation (en heures)" diff --git a/safeeyes/config/locale/he/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/he/LC_MESSAGES/safeeyes.po index 3948beef..0405706c 100644 --- a/safeeyes/config/locale/he/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/he/LC_MESSAGES/safeeyes.po @@ -530,6 +530,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "הפרש בין איפוסי סטטיסטיקה (בשעות)" diff --git a/safeeyes/config/locale/hi/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/hi/LC_MESSAGES/safeeyes.po index 442307fc..3f8117a3 100644 --- a/safeeyes/config/locale/hi/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/hi/LC_MESSAGES/safeeyes.po @@ -527,3 +527,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po index 00f3c0ff..d6357474 100644 --- a/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/hu/LC_MESSAGES/safeeyes.po @@ -528,3 +528,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/id/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/id/LC_MESSAGES/safeeyes.po index 6b105fc9..7c6b8be6 100644 --- a/safeeyes/config/locale/id/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/id/LC_MESSAGES/safeeyes.po @@ -533,6 +533,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Interval untuk mengatur ulang statistik (dalam jam)" diff --git a/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po index 2a718eb0..0cd6d158 100644 --- a/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/it/LC_MESSAGES/safeeyes.po @@ -538,3 +538,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/kn/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/kn/LC_MESSAGES/safeeyes.po index 5029564e..a5b61856 100644 --- a/safeeyes/config/locale/kn/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/kn/LC_MESSAGES/safeeyes.po @@ -526,3 +526,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/ko/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ko/LC_MESSAGES/safeeyes.po index 8167b9b8..c46f5da3 100644 --- a/safeeyes/config/locale/ko/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ko/LC_MESSAGES/safeeyes.po @@ -527,6 +527,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "통계 초기화 간격 (시)" diff --git a/safeeyes/config/locale/lt/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/lt/LC_MESSAGES/safeeyes.po index f4dcfebf..cb383056 100644 --- a/safeeyes/config/locale/lt/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/lt/LC_MESSAGES/safeeyes.po @@ -540,6 +540,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Statistikos atstatymo intervalas (valandomis)" diff --git a/safeeyes/config/locale/lv/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/lv/LC_MESSAGES/safeeyes.po index 8c9c0751..4bb98d4e 100644 --- a/safeeyes/config/locale/lv/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/lv/LC_MESSAGES/safeeyes.po @@ -446,5 +446,32 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Statistikas atstatīšanas intervāls (stundās)" diff --git a/safeeyes/config/locale/mk/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/mk/LC_MESSAGES/safeeyes.po index 88095195..070996ae 100644 --- a/safeeyes/config/locale/mk/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/mk/LC_MESSAGES/safeeyes.po @@ -529,3 +529,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/mr/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/mr/LC_MESSAGES/safeeyes.po index 70548d32..657a4d79 100644 --- a/safeeyes/config/locale/mr/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/mr/LC_MESSAGES/safeeyes.po @@ -528,3 +528,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/nb/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/nb/LC_MESSAGES/safeeyes.po index f9dd9abf..870498a6 100644 --- a/safeeyes/config/locale/nb/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/nb/LC_MESSAGES/safeeyes.po @@ -535,6 +535,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #, fuzzy #~ msgid "Interval to reset statistics (in hours)" diff --git a/safeeyes/config/locale/nl/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/nl/LC_MESSAGES/safeeyes.po index 1ddefdf5..7e2a114f 100644 --- a/safeeyes/config/locale/nl/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/nl/LC_MESSAGES/safeeyes.po @@ -538,6 +538,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Tussenpoos om statistieken te herstellen (in uren)" diff --git a/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po index 0c9d74a1..78279175 100644 --- a/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/pl/LC_MESSAGES/safeeyes.po @@ -538,6 +538,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Czas do wyzerowania statystyk (w godzinach)" diff --git a/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po index 01e0bed3..b099b000 100644 --- a/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/pt/LC_MESSAGES/safeeyes.po @@ -536,6 +536,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Intervalo para reiniciar estatísticas (em horas)" diff --git a/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po index b454b41a..e38d21b8 100644 --- a/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/pt_BR/LC_MESSAGES/safeeyes.po @@ -539,3 +539,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po index 68b32802..cd43479b 100644 --- a/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ru/LC_MESSAGES/safeeyes.po @@ -539,6 +539,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Интервал для сброса статистики (в часах)" diff --git a/safeeyes/config/locale/safeeyes.pot b/safeeyes/config/locale/safeeyes.pot index 665e8e0f..6e9fe389 100644 --- a/safeeyes/config/locale/safeeyes.pot +++ b/safeeyes/config/locale/safeeyes.pot @@ -511,3 +511,27 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "Please install the dependencies or disable the plugin. To hide this message, you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po index 85783c0b..0b0b3962 100644 --- a/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/sk/LC_MESSAGES/safeeyes.po @@ -535,3 +535,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po index b8acfe2f..addbb96c 100644 --- a/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/sr/LC_MESSAGES/safeeyes.po @@ -542,3 +542,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/sv/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/sv/LC_MESSAGES/safeeyes.po index 39c1aa40..0894ef3f 100644 --- a/safeeyes/config/locale/sv/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/sv/LC_MESSAGES/safeeyes.po @@ -532,6 +532,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Intervall för att återställa statistik (i timmar)" diff --git a/safeeyes/config/locale/ta/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ta/LC_MESSAGES/safeeyes.po index 9aac0309..f855d244 100644 --- a/safeeyes/config/locale/ta/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ta/LC_MESSAGES/safeeyes.po @@ -535,6 +535,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "திரை நேரத்தை மீட்டமைப்பதற்கான இடைவேளை (மணித்தியாலங்களில்)" diff --git a/safeeyes/config/locale/tr/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/tr/LC_MESSAGES/safeeyes.po index f28bf84d..60b0a8b2 100644 --- a/safeeyes/config/locale/tr/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/tr/LC_MESSAGES/safeeyes.po @@ -536,6 +536,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "İstatistikleri sıfırlama aralığı (saat olarak)" diff --git a/safeeyes/config/locale/ug/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/ug/LC_MESSAGES/safeeyes.po index cd2ac114..aaabab11 100644 --- a/safeeyes/config/locale/ug/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/ug/LC_MESSAGES/safeeyes.po @@ -523,3 +523,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po index 0c63cd23..854d1696 100644 --- a/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/uk/LC_MESSAGES/safeeyes.po @@ -539,6 +539,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "Інтервал скидання статистики (в годинах)" diff --git a/safeeyes/config/locale/uz_Latn/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/uz_Latn/LC_MESSAGES/safeeyes.po index 3b4f4ad7..78c5e760 100644 --- a/safeeyes/config/locale/uz_Latn/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/uz_Latn/LC_MESSAGES/safeeyes.po @@ -523,3 +523,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po index 047a99f9..3ffb4ab0 100644 --- a/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/vi/LC_MESSAGES/safeeyes.po @@ -532,3 +532,30 @@ msgstr "" # safeeyes/platform/io.github.slgobinath.SafeEyes.desktop msgid "RSI Prevention" msgstr "" + +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" diff --git a/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po index 8c47259d..93f9df62 100644 --- a/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/zh_CN/LC_MESSAGES/safeeyes.po @@ -528,6 +528,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "重置统计间隔(小时)" diff --git a/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po b/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po index f50f34a7..f103963b 100644 --- a/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po +++ b/safeeyes/config/locale/zh_TW/LC_MESSAGES/safeeyes.po @@ -525,6 +525,33 @@ msgstr "" msgid "RSI Prevention" msgstr "" +msgid "" +"Please install service providing tray icons for your desktop environment." +msgstr "" + +msgid "Next long break at %s" +msgstr "" + +msgid "Next breaks at %s/%s" +msgstr "" + +msgid "The required plugin '%s' is missing dependencies!" +msgstr "" + +msgid "" +"Please install the dependencies or disable the plugin. To hide this message, " +"you can also deactivate the plugin in the settings." +msgstr "" + +msgid "Click here for more information" +msgstr "" + +msgid "Disable plugin temporarily" +msgstr "" + +msgid "Disable permanently" +msgstr "" + # plugin/healthstats #~ msgid "Interval to reset statistics (in hours)" #~ msgstr "重置數據間隔(時)" diff --git a/safeeyes/config/style/safeeyes_style.css b/safeeyes/config/style/safeeyes_style.css index 35e508c6..6df25a45 100644 --- a/safeeyes/config/style/safeeyes_style.css +++ b/safeeyes/config/style/safeeyes_style.css @@ -90,6 +90,10 @@ color: #9B9B9B; } +.btn_plugin_extra_link { + padding-left: 0px; +} + .info_bar_long_break { opacity: 0.9; border-radius: 5px; @@ -120,4 +124,4 @@ border-image: None; background: whitesmoke; border-color: transparent; -} \ No newline at end of file +} diff --git a/safeeyes/glade/item_plugin.glade b/safeeyes/glade/item_plugin.glade index aa190378..6cea3f3a 100644 --- a/safeeyes/glade/item_plugin.glade +++ b/safeeyes/glade/item_plugin.glade @@ -26,6 +26,11 @@ False gtk-properties + + True + False + _Cancel + True False @@ -91,6 +96,20 @@ 1 + + + Click here for more information + https://slgobinath.github.io/SafeEyes + 0 + 0 + 5 + 5 + start + + + True @@ -110,11 +129,19 @@ end center - - False - True - 0 - + + + + False + center + center + img_disable + Disable permanently + True + + @@ -129,11 +156,6 @@ - - False - False - 1 - diff --git a/safeeyes/glade/required_plugin_dialog.glade b/safeeyes/glade/required_plugin_dialog.glade new file mode 100644 index 00000000..2a28837e --- /dev/null +++ b/safeeyes/glade/required_plugin_dialog.glade @@ -0,0 +1,117 @@ + + + + + + + Safe Eyes - Error + 0 + io.github.slgobinath.SafeEyes + + + 5 + 5 + 5 + 5 + 1 + 1 + vertical + top + + + start + 1 + vertical + + + center + 10 + 10 + A required plugin is missing dependencies! + + + + + + + + + 5 + 5 + + + + + + + + + Click here for more information + https://slgobinath.github.io/SafeEyes + 0 + + + + + 5 + 1 + center + 60 + Please install the dependencies or disable the plugin. To hide this message, you can also deactivate the plugin in the settings. + + + + + + + + + + 5 + 5 + + + + + start + end + 5 + 1 + 1 + 5 + + + Quit + 1 + 1 + + + + + Disable plugin temporarily + + + + + + + + diff --git a/safeeyes/model.py b/safeeyes/model.py index c43dc570..e57a1f79 100644 --- a/safeeyes/model.py +++ b/safeeyes/model.py @@ -23,6 +23,7 @@ import logging import random from enum import Enum +from dataclasses import dataclass from packaging.version import parse @@ -419,3 +420,30 @@ def build(cls, name, icon_path, icon_id, action): return TrayAction(name, icon_id, action, True) else: return TrayAction(name, icon_path, action, False) + +@dataclass +class PluginDependency: + message: str + link: str|None = None + +class RequiredPluginException(Exception): + def __init__(self, plugin_id, plugin_name: str, message: str|PluginDependency): + if isinstance(message, PluginDependency): + msg = message.message + else: + msg = message + + super().__init__(msg) + + self.plugin_id = plugin_id + self.plugin_name = plugin_name + self.message = message + + def get_plugin_id(self): + return self.plugin_id + + def get_plugin_name(self): + return self.plugin_name + + def get_message(self): + return self.message diff --git a/safeeyes/plugin_manager.py b/safeeyes/plugin_manager.py index e35c6921..1bc3f8f6 100644 --- a/safeeyes/plugin_manager.py +++ b/safeeyes/plugin_manager.py @@ -57,6 +57,7 @@ import sys from safeeyes import utility +from safeeyes.model import RequiredPluginException sys.path.append(os.path.abspath(utility.SYSTEM_PLUGINS_DIR)) sys.path.append(os.path.abspath(utility.USER_PLUGINS_DIR)) @@ -94,6 +95,8 @@ def init(self, context, config): for plugin in config.get('plugins'): try: self.__load_plugin(plugin) + except RequiredPluginException as e: + raise e except BaseException as e: traceback_wanted = logging.getLogger().getEffectiveLevel() == logging.DEBUG if traceback_wanted: @@ -298,7 +301,14 @@ def __load_plugin(self, plugin): else: # This is the first time to load the plugin # Check for dependencies - if utility.check_plugin_dependencies(plugin['id'], plugin_config, plugin.get('settings', {}), plugin_path): + message = utility.check_plugin_dependencies(plugin['id'], plugin_config, plugin.get('settings', {}), plugin_path) + if message: + if plugin_config.get('required_plugin', False): + raise RequiredPluginException( + plugin['id'], + plugin_config['meta']['name'], + message + ) return # Load the plugin module diff --git a/safeeyes/plugins/mediacontrol/config.json b/safeeyes/plugins/mediacontrol/config.json index b4bdcef3..5c5d3326 100644 --- a/safeeyes/plugins/mediacontrol/config.json +++ b/safeeyes/plugins/mediacontrol/config.json @@ -5,7 +5,7 @@ "version": "0.0.1" }, "dependencies": { - "python_modules": ["dbus"], + "python_modules": [], "shell_commands": [], "operating_systems": [], "desktop_environments": [], diff --git a/safeeyes/plugins/mediacontrol/plugin.py b/safeeyes/plugins/mediacontrol/plugin.py index df471cd3..5c43ac33 100644 --- a/safeeyes/plugins/mediacontrol/plugin.py +++ b/safeeyes/plugins/mediacontrol/plugin.py @@ -22,12 +22,11 @@ import logging import os -import dbus import re import gi from safeeyes.model import TrayAction gi.require_version('Gtk', '3.0') -from gi.repository import Gtk +from gi.repository import Gtk, Gio tray_icon_path = None @@ -37,13 +36,31 @@ def __active_players(): List of all media players which are playing now. """ players = [] - bus = dbus.SessionBus() - for service in bus.list_names(): + dbus_proxy = Gio.DBusProxy.new_for_bus_sync( + bus_type=Gio.BusType.SESSION, + flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, + info=None, + name='org.freedesktop.DBus', + object_path='/org/freedesktop/DBus', + interface_name='org.freedesktop.DBus', + cancellable=None, + ) + + for service in dbus_proxy.ListNames(): if re.match('org.mpris.MediaPlayer2.', service): - player = bus.get_object(service, "/org/mpris/MediaPlayer2") - interface = dbus.Interface(player, 'org.freedesktop.DBus.Properties') - status = str(interface.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')).lower() + player = Gio.DBusProxy.new_for_bus_sync( + bus_type=Gio.BusType.SESSION, + flags=Gio.DBusProxyFlags.NONE, + info=None, + name=service, + object_path='/org/mpris/MediaPlayer2', + interface_name='org.mpris.MediaPlayer2.Player', + cancellable=None, + ) + + status = player.get_cached_property('PlaybackStatus').unpack().lower() + if status == "playing": players.append(player) return players @@ -54,8 +71,7 @@ def __pause_players(players): Pause all playing media players using dbus. """ for player in players: - interface = dbus.Interface(player, dbus_interface='org.mpris.MediaPlayer2.Player') - interface.Pause() + player.Pause() def init(ctx, safeeyes_config, plugin_config): diff --git a/safeeyes/plugins/trayicon/config.json b/safeeyes/plugins/trayicon/config.json index 5ed2e5b1..4cc8c96d 100644 --- a/safeeyes/plugins/trayicon/config.json +++ b/safeeyes/plugins/trayicon/config.json @@ -11,6 +11,7 @@ "desktop_environments": [], "resources": [] }, + "required_plugin": true, "settings": [ { "id": "show_time_in_tray", diff --git a/safeeyes/plugins/trayicon/dependency_checker.py b/safeeyes/plugins/trayicon/dependency_checker.py new file mode 100644 index 00000000..d7a92f97 --- /dev/null +++ b/safeeyes/plugins/trayicon/dependency_checker.py @@ -0,0 +1,55 @@ +# Safe Eyes is a utility to remind you to take break frequently +# to protect your eyes from eye strain. + +# Copyright (C) 2017 Gobinath + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from safeeyes import utility +from safeeyes.model import PluginDependency + +import gi +gi.require_version('Gio', '2.0') +from gi.repository import Gio + +def validate(plugin_config, plugin_settings): + dbus_proxy = Gio.DBusProxy.new_for_bus_sync( + bus_type=Gio.BusType.SESSION, + flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, + info=None, + name='org.freedesktop.DBus', + object_path='/org/freedesktop/DBus', + interface_name='org.freedesktop.DBus', + cancellable=None, + ) + + if dbus_proxy.NameHasOwner('(s)', 'org.kde.StatusNotifierWatcher'): + return None + else: + return PluginDependency( + message=_("Please install service providing tray icons for your desktop environment."), + link="https://github.com/slgobinath/SafeEyes/wiki/How-to-install-backend-for-Safe-Eyes-tray-icon" + ) + + command = None + if utility.IS_WAYLAND: + if utility.DESKTOP_ENVIRONMENT == "gnome": + return None + command = "wlrctl" + else: + command = "xprop" + if not utility.command_exist(command): + return _("Please install the command-line tool '%s'") % command + else: + return None diff --git a/safeeyes/plugins/trayicon/plugin.py b/safeeyes/plugins/trayicon/plugin.py index f8ba1e37..a149f014 100644 --- a/safeeyes/plugins/trayicon/plugin.py +++ b/safeeyes/plugins/trayicon/plugin.py @@ -20,14 +20,7 @@ from safeeyes.model import BreakType import gi gi.require_version('Gtk', '3.0') -try: - gi.require_version('AppIndicator3', '0.1') - from gi.repository import AppIndicator3 as appindicator -except: - #fall back to Ayatana - gi.require_version('AyatanaAppIndicator3', '0.1') - from gi.repository import AyatanaAppIndicator3 as appindicator -from gi.repository import Gtk +from gi.repository import Gio, GLib import logging from safeeyes import utility import threading @@ -37,11 +30,373 @@ Safe Eyes tray icon plugin """ -APPINDICATOR_ID = 'safeeyes_2' context = None tray_icon = None safeeyes_config = None +SNI_NODE_INFO = Gio.DBusNodeInfo.new_for_xml(""" + + + + + + + + + + + + + + + +""").interfaces[0] + +MENU_NODE_INFO = Gio.DBusNodeInfo.new_for_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""").interfaces[0] + +class DBusService: + def __init__(self, interface_info, object_path, bus): + self.interface_info = interface_info + self.bus = bus + self.object_path = object_path + self.registration_id = None + + def register(self): + self.registration_id = self.bus.register_object( + object_path=self.object_path, + interface_info=self.interface_info, + method_call_closure=self.on_method_call, + get_property_closure=self.on_get_property + ) + + if not self.registration_id: + raise GLib.Error(f"Failed to register object with path {self.object_path}") + + self.interface_info.cache_build() + + def unregister(self): + self.interface_info.cache_release() + + if self.registration_id is not None: + self.bus.unregister_object(self.registration_id) + self.registration_id = None + + def on_method_call(self, _connection, _sender, _path, _interface_name, method_name, parameters, invocation): + method_info = self.interface_info.lookup_method(method_name) + method = getattr(self, method_name) + result = method(*parameters.unpack()) + out_arg_types = "".join([arg.signature for arg in method_info.out_args]) + return_value = None + + if method_info.out_args: + return_value = GLib.Variant(f"({out_arg_types})", result) + + invocation.return_value(return_value) + + def on_get_property(self, _connection, _sender, _path, _interface_name, property_name): + property_info = self.interface_info.lookup_property(property_name) + return GLib.Variant(property_info.signature, getattr(self, property_name)) + + def emit_signal(self, signal_name, args = None): + signal_info = self.interface_info.lookup_signal(signal_name) + if len(signal_info.args) == 0: + parameters = None + else: + arg_types = "".join([arg.signature for arg in signal_info.args]) + parameters = GLib.Variant(f"({arg_types})", args) + self.bus.emit_signal( + destination_bus_name=None, + object_path=self.object_path, + interface_name=self.interface_info.name, + signal_name=signal_name, + parameters=parameters + ) + +class DBusMenuService(DBusService): + DBUS_SERVICE_PATH = '/io/github/slgobinath/SafeEyes/Menu' + + revision = 0 + + items = [] + idToItems = {} + + def __init__(self, session_bus, context, items): + super().__init__( + interface_info=MENU_NODE_INFO, + object_path=self.DBUS_SERVICE_PATH, + bus=session_bus + ) + + self.set_items(items) + + def set_items(self, items): + self.items = items + + self.idToItems = self.getItemsFlat(items, {}) + + self.revision += 1 + + self.LayoutUpdated(self.revision, 0) + + @staticmethod + def getItemsFlat(items, idToItems): + for item in items: + if item.get('hidden', False) == True: + continue + + idToItems[item['id']] = item + + if 'children' in item: + idToItems = DBusMenuService.getItemsFlat(item['children'], idToItems) + + return idToItems + + @staticmethod + def singleItemToDbus(item): + props = DBusMenuService.itemPropsToDbus(item) + + return (item['id'], props) + + @staticmethod + def itemPropsToDbus(item): + result = {} + + string_props = ['label', 'icon-name', 'type', 'children-display'] + for key in string_props: + if key in item: + result[key] = GLib.Variant('s', item[key]) + + bool_props = ['enabled'] + for key in bool_props: + if key in item: + result[key] = GLib.Variant('b', item[key]) + + return result + + @staticmethod + def itemToDbus(item, recursion_depth): + if item.get('hidden', False) == True: + return None + + props = DBusMenuService.itemPropsToDbus(item) + + children = [] + if recursion_depth > 1 or recursion_depth == -1: + if "children" in item: + children = [DBusMenuService.itemToDbus(item, recursion_depth - 1) for item in item['children']] + children = [i for i in children if i is not None] + + return GLib.Variant("(ia{sv}av)", (item['id'], props, children)) + + def findItemsWithParent(self, parent_id, items): + for item in items: + if item.get('hidden', False) == True: + continue + if 'children' in item: + if item['id'] == parent_id: + return item['children'] + else: + ret = self.findItemsWithParent(parent_id, item['children']) + if ret is not None: + return ret + return None + + def GetLayout(self, parent_id, recursion_depth, property_names): + children = [] + + if parent_id == 0: + children = self.items + else: + children = self.findItemsWithParent(parent_id, self.items) + if children is None: + children = [] + + children = [self.itemToDbus(item, recursion_depth) for item in children] + children = [i for i in children if i is not None] + + ret = ( + self.revision, + ( + 0, + { 'children-display': GLib.Variant('s', 'submenu') }, + children + ) + ) + + return ret + + def GetGroupProperties(self, ids, property_names): + ret = [] + + for idx in ids: + if idx in self.idToItems: + props = DBusMenuService.singleItemToDbus(self.idToItems[idx]) + if props is not None: + ret.append(props) + + return (ret,) + + def GetProperty(self, idx, name): + ret = None + + if idx in self.idToItems: + props = DBusMenuService.singleItemToDbus(self.idToItems[idx]) + if props is not None and name in props: + ret = props[name] + + return ret + + def Event(self, idx, event_id, data, timestamp): + if event_id != "clicked": + return + + if idx in self.idToItems: + item = self.idToItems[idx] + if 'callback' in item: + item['callback']() + + def EventGroup(self, events): + not_found = [] + + for (idx, event_id, data, timestamp) in events: + if idx not in self.idToItems: + not_found.append(idx) + continue + + if event_id != "clicked": + continue + + item = self.idToItems[idx] + if 'callback' in item: + item['callback']() + + return not_found + + def AboutToShow(self, item_id): + return (False,) + + def AboutToShowGroup(self, ids): + not_found = [] + + for idx in ids: + if idx not in self.idToItems: + not_found.append(idx) + continue + + return ([], not_found) + + def LayoutUpdated(self, revision, parent): + self.emit_signal( + 'LayoutUpdated', + (revision, parent) + ) + +class StatusNotifierItemService(DBusService): + DBUS_SERVICE_PATH = '/org/ayatana/NotificationItem/io_github_slgobinath_SafeEyes' + + Category = 'ApplicationStatus' + Id = 'io.github.slgobinath.SafeEyes' + Title = 'Safe Eyes' + Status = 'Active' + IconName = 'io.github.slgobinath.SafeEyes-enabled' + IconThemePath = '' + ToolTip = ('', [], 'Safe Eyes', '') + ItemIsMenu = True + Menu = None + + def __init__(self, session_bus, context, menu_items): + super().__init__( + interface_info=SNI_NODE_INFO, + object_path=self.DBUS_SERVICE_PATH, + bus=session_bus + ) + + self.bus = session_bus + + self._menu = DBusMenuService(session_bus, context, menu_items) + self.Menu = self._menu.DBUS_SERVICE_PATH + + def register(self): + self._menu.register() + super().register() + + watcher = Gio.DBusProxy.new_sync( + connection=self.bus, + flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, + info=None, + name='org.kde.StatusNotifierWatcher', + object_path='/StatusNotifierWatcher', + interface_name='org.kde.StatusNotifierWatcher', + cancellable=None, + ) + + watcher.RegisterStatusNotifierItem('(s)', self.DBUS_SERVICE_PATH) + + def unregister(self): + super().unregister() + self._menu.unregister() + + def set_items(self, items): + self._menu.set_items(items) + + def set_icon(self, icon): + self.IconName = icon + + self.emit_signal( + 'NewIcon' + ) + + def set_tooltip(self, title, description): + self.ToolTip = ('', [], title, description) + + self.emit_signal( + 'NewTooltip' + ) class TrayIcon: """ @@ -66,185 +421,185 @@ def __init__(self, context, plugin_config): self.lock = threading.Lock() self.allow_disabling = plugin_config['allow_disabling'] self.animate = False + self.menu_locked = False - # Construct the tray icon - self.indicator = appindicator.Indicator.new( - APPINDICATOR_ID, "io.github.slgobinath.SafeEyes-enabled", appindicator.IndicatorCategory.APPLICATION_STATUS) - self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) - - # Construct the context menu - self.menu = Gtk.Menu() + session_bus = Gio.bus_get_sync(Gio.BusType.SESSION) - # Next break info menu item - self.item_info = Gtk.ImageMenuItem() - img_timer = Gtk.Image() - img_timer.set_from_icon_name("io.github.slgobinath.SafeEyes-timer", 16) - self.item_info.set_image(img_timer) + self.sni_service = StatusNotifierItemService( + session_bus, + context, + menu_items = self.get_items() + ) + self.sni_service.register() - self.item_separator = Gtk.SeparatorMenuItem() - - self.item_enable = Gtk.MenuItem() - self.item_enable.connect('activate', self.on_enable_clicked) - - self.item_disable = Gtk.MenuItem() - self.sub_menu_disable = Gtk.Menu() - self.sub_menu_disable_items = [] - - # Read disable options and build the sub menu - for disable_option in plugin_config['disable_options']: - time_in_minutes = disable_option['time'] - label = [] - # Validate time value - if not isinstance(time_in_minutes, int) or time_in_minutes <= 0: - logging.error('Invalid time in disable option: ' + str(time_in_minutes)) - continue - time_unit = disable_option['unit'].lower() - if time_unit == 'seconds' or time_unit == 'second': - time_in_minutes = int(time_in_minutes / 60) - label = ['For %d Second', 'For %d Seconds'] - elif time_unit == 'minutes' or time_unit == 'minute': - time_in_minutes = int(time_in_minutes * 1) - label = ['For %d Minute', 'For %d Minutes'] - elif time_unit == 'hours' or time_unit == 'hour': - time_in_minutes = int(time_in_minutes * 60) - label = ['For %d Hour', 'For %d Hours'] - else: - # Invalid unit - logging.error('Invalid unit in disable option: ' + str(disable_option)) - continue - - # Create submenu - sub_menu_item = Gtk.MenuItem() - sub_menu_item.connect('activate', self.on_disable_clicked, time_in_minutes) - self.sub_menu_disable_items.append([sub_menu_item, label, disable_option['time']]) - self.sub_menu_disable.append(sub_menu_item) - - # Disable until restart submenu - self.sub_menu_item_until_restart = Gtk.MenuItem() - self.sub_menu_item_until_restart.connect('activate', self.on_disable_clicked, -1) - self.sub_menu_disable.append(self.sub_menu_item_until_restart) - - # Add the sub menu to the enable/disable menu - self.item_disable.set_submenu(self.sub_menu_disable) - - # Manual break menu item - self.item_manual_break = Gtk.MenuItem() - - self.sub_menu_manual_next_break = Gtk.MenuItem() - self.sub_menu_manual_next_break.connect('activate', self.on_manual_break_clicked, None) - self.sub_menu_manual_next_short_break = Gtk.MenuItem() - self.sub_menu_manual_next_short_break.connect('activate', self.on_manual_break_clicked, BreakType.SHORT_BREAK) - self.sub_menu_manual_next_long_break = Gtk.MenuItem() - self.sub_menu_manual_next_long_break.connect('activate', self.on_manual_break_clicked, BreakType.LONG_BREAK) - - self.sub_menu_manual_break = Gtk.Menu() - self.sub_menu_manual_break.append(self.sub_menu_manual_next_break) - self.sub_menu_manual_break.append(self.sub_menu_manual_next_short_break) - self.sub_menu_manual_break.append(self.sub_menu_manual_next_long_break) - self.item_manual_break.set_submenu(self.sub_menu_manual_break) - - # Settings menu item - self.item_settings = Gtk.MenuItem() - self.item_settings.connect('activate', self.show_settings) - - # About menu item - self.item_about = Gtk.MenuItem() - self.item_about.connect('activate', self.show_about) - - # Quit menu item - self.item_quit = Gtk.MenuItem() - self.item_quit.connect('activate', self.quit_safe_eyes) - - self.set_labels() - - # At startup, no need for activate menu - self.item_enable.set_sensitive(False) - - # Append all menu items and show the menu - self.menu.append(self.item_info) - self.menu.append(self.item_separator) - self.menu.append(self.item_enable) - self.menu.append(self.item_disable) - self.menu.append(self.item_manual_break) - self.menu.append(self.item_settings) - self.menu.append(self.item_about) - self.menu.append(self.item_quit) - self.menu.show_all() - - self.item_enable.set_visible(self.allow_disabling) - self.item_disable.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - - self.indicator.set_menu(self.menu) + self.update_tooltip() def initialize(self, plugin_config): """ Initialize the tray icon by setting the config. """ self.plugin_config = plugin_config - self.set_labels() self.allow_disabling = plugin_config['allow_disabling'] - self.item_enable.set_visible(self.allow_disabling) - self.item_disable.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - - def set_labels(self): - """ - Update the text of menu items based on the selected language. - """ - for entry in self.sub_menu_disable_items: - # print(self.context['locale'].ngettext('For %d Hour', 'For %d Hours', 1) % 1) - entry[0].set_label(self.context['locale'].ngettext(entry[1][0], entry[1][1], entry[2]) % entry[2]) - self.sub_menu_item_until_restart.set_label(_('Until restart')) - self.item_enable.set_label(_('Enable Safe Eyes')) - self.item_disable.set_label(_('Disable Safe Eyes')) + self.update_menu() + self.update_tooltip() + def get_items(self): breaks_found = self.has_breaks() + + info_message = _('No Breaks Available') + if breaks_found: if self.active: - if self.date_time: - self.__set_next_break_info() - self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled") + next_break = self.get_next_break_time() + + if next_break is not None: + (next_time, next_long_time, next_is_long) = next_break + + if next_long_time: + if next_is_long: + info_message = _('Next long break at %s') % (next_long_time) + else: + info_message = _('Next breaks at %s/%s') % (next_time, next_long_time) + else: + info_message = _('Next break at %s') % (next_time) else: if self.wakeup_time: - self.item_info.set_label(_('Disabled until %s') % utility.format_time(self.wakeup_time)) + info_message = _('Disabled until %s') % utility.format_time(self.wakeup_time) else: - self.item_info.set_label(_('Disabled until restart')) - self.indicator.set_label('', '') - self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") + info_message = _('Disabled until restart') + + disable_items = [] + + if self.allow_disabling: + disable_option_dynamic_id = 13 + + for disable_option in self.plugin_config['disable_options']: + time_in_minutes = time_in_x = disable_option['time'] + label = [] + # Validate time value + if not isinstance(time_in_minutes, int) or time_in_minutes <= 0: + logging.error('Invalid time in disable option: ' + str(time_in_minutes)) + continue + time_unit = disable_option['unit'].lower() + if time_unit == 'seconds' or time_unit == 'second': + time_in_minutes = int(time_in_minutes / 60) + label = ['For %d Second', 'For %d Seconds'] + elif time_unit == 'minutes' or time_unit == 'minute': + time_in_minutes = int(time_in_minutes * 1) + label = ['For %d Minute', 'For %d Minutes'] + elif time_unit == 'hours' or time_unit == 'hour': + time_in_minutes = int(time_in_minutes * 60) + label = ['For %d Hour', 'For %d Hours'] + else: + # Invalid unit + logging.error('Invalid unit in disable option: ' + str(disable_option)) + continue + + label = self.context['locale'].ngettext(label[0], label[1], time_in_x) % time_in_x + + disable_items.append({ + 'id': disable_option_dynamic_id, + 'label': label, + 'callback': lambda: self.on_disable_clicked(time_in_minutes), + }) + + disable_option_dynamic_id += 1 + + disable_items.append({ + 'id': 12, + 'label': _('Until restart'), + 'callback': lambda: self.on_disable_clicked(-1), + }) + + return [ + { + 'id': 1, + 'label': info_message, + 'icon-name': "io.github.slgobinath.SafeEyes-timer", + 'enabled': breaks_found and self.active, + }, + { + 'id': 2, + 'type': "separator", + }, + { + 'id': 3, + 'label': _("Enable Safe Eyes"), + 'enabled': breaks_found and not self.active, + 'callback': self.on_enable_clicked, + 'hidden': not self.allow_disabling, + }, + { + 'id': 4, + 'label': _("Disable Safe Eyes"), + 'enabled': breaks_found and self.active and not self.menu_locked, + 'children-display': 'submenu', + 'children': disable_items, + 'hidden': not self.allow_disabling, + }, + { + 'id': 5, + 'label': _('Take a break now'), + 'enabled': breaks_found and self.active and not self.menu_locked, + 'children-display': 'submenu', + 'children': [ + { + 'id': 9, + 'label': _('Any break'), + 'callback': lambda: self.on_manual_break_clicked(None), + }, + { + 'id': 10, + 'label': _('Short break'), + 'callback': lambda: self.on_manual_break_clicked(BreakType.SHORT_BREAK), + }, + { + 'id': 11, + 'label': _('Long break'), + 'callback': lambda: self.on_manual_break_clicked(BreakType.LONG_BREAK), + }, + ] + }, + { + 'id': 6, + 'label': _('Settings'), + 'enabled': not self.menu_locked, + 'callback': self.show_settings, + }, + { + 'id': 7, + 'label': _('About'), + 'callback': self.show_about, + }, + { + 'id': 8, + 'label': _('Quit'), + 'enabled': not self.menu_locked, + 'callback': self.quit_safe_eyes, + 'hidden': not self.allow_disabling, + }, + ] + + def update_menu(self): + self.sni_service.set_items(self.get_items()) + + def update_tooltip(self): + next_break = self.get_next_break_time() + + if next_break is not None and self.plugin_config.get('show_time_in_tray', False): + (next_time, next_long_time, _next_is_long) = next_break + + if next_long_time and self.plugin_config.get('show_long_time_in_tray', False): + description = next_long_time + else: + description = next_time else: - self.item_info.set_label(_('No breaks available')) - self.indicator.set_label('', '') - self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") - self.item_info.set_sensitive(breaks_found and self.active) - self.item_enable.set_sensitive(breaks_found and not self.active) - self.item_disable.set_sensitive(breaks_found and self.active) - self.item_manual_break.set_sensitive(breaks_found and self.active) - - self.item_manual_break.set_label(_('Take a break now')) - self.sub_menu_manual_next_break.set_label(_('Any break')) - self.sub_menu_manual_next_short_break.set_label(_('Short break')) - self.sub_menu_manual_next_long_break.set_label(_('Long break')) - self.item_settings.set_label(_('Settings')) - self.item_about.set_label(_('About')) - self.item_quit.set_label(_('Quit')) - - def show_icon(self): - """ - Show the tray icon. - """ - self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) + description = '' - def hide_icon(self): - """ - Hide the tray icon. - """ - self.indicator.set_status(appindicator.IndicatorStatus.PASSIVE) + self.sni_service.set_tooltip('Safe Eyes', description) - def quit_safe_eyes(self, *args): + def quit_safe_eyes(self): """ Handle Quit menu action. This action terminates the application. @@ -257,14 +612,14 @@ def quit_safe_eyes(self, *args): self.idle_condition.release() self.quit() - def show_settings(self, *args): + def show_settings(self): """ Handle Settings menu action. This action shows the Settings dialog. """ self.on_show_settings() - def show_about(self, *args): + def show_about(self): """ Handle About menu action. This action shows the About dialog. @@ -277,50 +632,37 @@ def next_break_time(self, dateTime): """ logging.info("Update next break information") self.date_time = dateTime - self.__set_next_break_info() + self.update_menu() + self.update_tooltip() + + def get_next_break_time(self): + if not (self.has_breaks() and self.active and self.date_time): + return None - def __set_next_break_info(self): - """ - A private method to be called within this class to update the next break information using self.dateTime. - """ formatted_time = utility.format_time(self.get_break_time()) long_time = self.get_break_time(BreakType.LONG_BREAK) if long_time: long_time = utility.format_time(long_time) if long_time == formatted_time: - message = _('Next long break at %s') % (long_time) + return (long_time, long_time, True) else: - message = _('Next breaks at %s/%s') % (formatted_time, long_time) - else: - message = _('Next break at %s') % (formatted_time) + return (formatted_time, long_time, False) - # Update the menu item label - utility.execute_main_thread(self.item_info.set_label, message) + return (formatted_time, None, False) - # Update the tray icon label - if self.plugin_config.get('show_time_in_tray', False): - show_long = long_time and self.plugin_config.get('show_long_time_in_tray', False) - self.indicator.set_label(long_time if show_long else formatted_time, '') - else: - self.indicator.set_label('', '') - def on_manual_break_clicked(self, *args): + def on_manual_break_clicked(self, break_type): """ Trigger a break manually. """ - if len(args) > 1: - break_type = args[1] - self.take_break(break_type) - else: - self.take_break() + self.take_break(break_type) - def on_enable_clicked(self, *args): + def on_enable_clicked(self): """ Handle 'Enable Safe Eyes' menu action. This action enables the application if it is currently disabled. """ - # active = self.item_enable.get_active() if not self.active: with self.lock: self.enable_ui() @@ -330,41 +672,40 @@ def on_enable_clicked(self, *args): self.idle_condition.notify_all() self.idle_condition.release() - def on_disable_clicked(self, *args): + def on_disable_clicked(self, time_to_wait): """ Handle the menu actions of all the sub menus of 'Disable Safe Eyes'. This action disables the application if it is currently active. """ - # active = self.item_enable.get_active() - if self.active and len(args) > 1: + if self.active: self.disable_ui() - time_to_wait = args[1] if time_to_wait <= 0: info = _('Disabled until restart') self.disable_safeeyes(info) self.wakeup_time = None - self.item_info.set_label(info) else: self.wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait) info = _('Disabled until %s') % utility.format_time(self.wakeup_time) self.disable_safeeyes(info) - self.item_info.set_label(info) utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait) + self.update_menu() def lock_menu(self): """ This method is called by the core to prevent user from disabling Safe Eyes after the notification. """ if self.active: - self.menu.set_sensitive(False) + self.menu_locked = True + self.update_menu() def unlock_menu(self): """ This method is called by the core to activate the menu after the the break. """ if self.active: - self.menu.set_sensitive(True) + self.menu_locked = False + self.update_menu() def disable_ui(self): """ @@ -373,13 +714,9 @@ def disable_ui(self): if self.active: logging.info('Disable Safe Eyes') self.active = False - self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") - self.item_info.set_label(_('Disabled until restart')) - self.indicator.set_label('', '') - self.item_info.set_sensitive(False) - self.item_enable.set_sensitive(True) - self.item_disable.set_sensitive(False) - self.item_manual_break.set_sensitive(False) + + self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled") + self.update_menu() def enable_ui(self): """ @@ -388,11 +725,9 @@ def enable_ui(self): if not self.active: logging.info('Enable Safe Eyes') self.active = True - self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled") - self.item_info.set_sensitive(True) - self.item_enable.set_sensitive(False) - self.item_disable.set_sensitive(True) - self.item_manual_break.set_sensitive(True) + + self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled") + self.update_menu() def __schedule_resume(self, time_minutes): """ @@ -404,14 +739,14 @@ def __schedule_resume(self, time_minutes): with self.lock: if not self.active: - utility.execute_main_thread(self.item_enable.activate) + utility.execute_main_thread(self.on_enable_clicked) def start_animation(self): if not self.active or not self.animate: return - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled")) time.sleep(0.5) - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled")) if self.animate and self.active: time.sleep(0.5) if self.animate and self.active: @@ -420,9 +755,9 @@ def start_animation(self): def stop_animation(self): self.animate = False if self.active: - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled")) else: - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled")) def init(ctx, safeeyes_cfg, plugin_config): """ diff --git a/safeeyes/safeeyes.py b/safeeyes/safeeyes.py index a10c4a28..4b68d259 100644 --- a/safeeyes/safeeyes.py +++ b/safeeyes/safeeyes.py @@ -25,13 +25,12 @@ import os from threading import Timer -import dbus import gi -from dbus.mainloop.glib import DBusGMainLoop from safeeyes import utility from safeeyes.ui.about_dialog import AboutDialog from safeeyes.ui.break_screen import BreakScreen -from safeeyes.model import State +from safeeyes.ui.required_plugin_dialog import RequiredPluginDialog +from safeeyes.model import State, RequiredPluginException from safeeyes.rpc import RPCServer from safeeyes.plugin_manager import PluginManager from safeeyes.core import SafeEyesCore @@ -48,6 +47,8 @@ class SafeEyes(Gtk.Application): This class represents a runnable Safe Eyes instance. """ + required_plugin_dialog_active = False + def __init__(self, system_locale, config, cli_args): super().__init__( application_id="io.github.slgobinath.SafeEyes", @@ -102,7 +103,12 @@ def __init__(self, system_locale, config, cli_args): self.context['api']['has_breaks'] = self.safe_eyes_core.has_breaks self.context['api']['postpone'] = self.safe_eyes_core.postpone self.context['api']['get_break_time'] = self.safe_eyes_core.get_break_time - self.plugins_manager.init(self.context, self.config) + + try: + self.plugins_manager.init(self.context, self.config) + except RequiredPluginException as e: + self.show_required_plugin_dialog(e.get_plugin_id(), e.get_plugin_name(), e.get_message()) + self.required_plugin_dialog_active = True self.hold() @@ -115,7 +121,7 @@ def start(self): if self.config.get('use_rpc_server', True): self.__start_rpc_server() - if self.safe_eyes_core.has_breaks(): + if not self.required_plugin_dialog_active and self.safe_eyes_core.has_breaks(): self.active = True self.context['state'] = State.START self.plugins_manager.start() # Call the start method of all plugins @@ -149,6 +155,31 @@ def show_settings(self): self.config.clone(), self.save_settings) settings_dialog.show() + def show_required_plugin_dialog(self, plugin_id, plugin_name, message): + logging.info("Show RequiredPlugin dialog") + dialog = RequiredPluginDialog( + plugin_id, + plugin_name, + message, + self.quit, + lambda: self.disable_plugin(plugin_id) + ) + dialog.show() + + def disable_plugin(self, plugin_id): + """ + Temporarily disable plugin, and restart SafeEyes. + """ + config = self.config.clone() + + for plugin in config.get('plugins'): + if plugin['id'] == plugin_id: + plugin['enabled'] = False + + self.required_plugin_dialog_active = False + + self.restart(config, set_active=True) + def show_about(self): """ Listen to tray icon About action and send the signal to About dialog. @@ -189,14 +220,28 @@ def handle_suspend_callback(self, sleeping): self.plugins_manager.start() self.safe_eyes_core.start() + def handle_suspend_signal(self, proxy, sender, signal, parameters): + if signal != "PrepareForSleep": + return + + (sleeping, ) = parameters + + self.handle_suspend_callback(sleeping) + def handle_system_suspend(self): """ Setup system suspend listener. """ - DBusGMainLoop(set_as_default=True) - bus = dbus.SystemBus() - bus.add_signal_receiver(self.handle_suspend_callback, 'PrepareForSleep', - 'org.freedesktop.login1.Manager', 'org.freedesktop.login1') + self.suspend_proxy = Gio.DBusProxy.new_for_bus_sync( + bus_type=Gio.BusType.SYSTEM, + flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, + info=None, + name='org.freedesktop.login1', + object_path='/org/freedesktop/login1', + interface_name='org.freedesktop.login1.Manager', + cancellable=None, + ) + self.suspend_proxy.connect('g-signal', self.handle_suspend_signal) def on_skipped(self): """ @@ -234,6 +279,9 @@ def save_settings(self, config): config.save() self.persist_session() + self.restart(config) + + def restart(self, config, set_active=False): logging.info("Initialize SafeEyesCore with modified settings") if self.rpc_server is None and config.get('use_rpc_server'): @@ -247,7 +295,17 @@ def save_settings(self, config): self.config = config self.safe_eyes_core.initialize(config) self.break_screen.initialize(config) - self.plugins_manager.init(self.context, self.config) + + try: + self.plugins_manager.init(self.context, self.config) + except RequiredPluginException as e: + self.show_required_plugin_dialog(e.get_plugin_id(), e.get_plugin_name(), e.get_message()) + self.required_plugin_dialog_active = True + return + + if set_active: + self.active = True + if self.active and self.safe_eyes_core.has_breaks(): # 1 sec delay is required to give enough time for core to be stopped Timer(1.0, self.safe_eyes_core.start).start() @@ -257,7 +315,7 @@ def enable_safeeyes(self, scheduled_next_break_time=-1, reset_breaks=False): """ Listen to tray icon enable action and send the signal to core. """ - if not self.active and self.safe_eyes_core.has_breaks(): + if not self.required_plugin_dialog_active and not self.active and self.safe_eyes_core.has_breaks(): self.active = True self.safe_eyes_core.start(scheduled_next_break_time, reset_breaks) self.plugins_manager.start() diff --git a/safeeyes/ui/required_plugin_dialog.py b/safeeyes/ui/required_plugin_dialog.py new file mode 100644 index 00000000..debe53b3 --- /dev/null +++ b/safeeyes/ui/required_plugin_dialog.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# Safe Eyes is a utility to remind you to take break frequently +# to protect your eyes from eye strain. + +# Copyright (C) 2016 Gobinath + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +This module creates the RequiredPluginDialog which shows the error for a required plugin. +""" + +import os + +from safeeyes import utility +from safeeyes.model import PluginDependency + +REQUIRED_PLUGIN_DIALOG_GLADE = os.path.join(utility.BIN_DIRECTORY, "glade/required_plugin_dialog.glade") + + +class RequiredPluginDialog: + """ + RequiredPluginDialog shows an error when a plugin has required dependencies. + """ + + def __init__(self, plugin_id, plugin_name, message, on_quit, on_disable_plugin): + self.on_quit = on_quit + self.on_disable_plugin = on_disable_plugin + + builder = utility.create_gtk_builder(REQUIRED_PLUGIN_DIALOG_GLADE) + self.window = builder.get_object('window_required_plugin') + + self.window.connect("delete-event", self.on_window_delete) + builder.get_object('btn_close').connect('clicked', self.on_close_clicked) + builder.get_object('btn_disable_plugin').connect('clicked', self.on_disable_plugin_clicked) + + builder.get_object('lbl_header').set_label(_("The required plugin '%s' is missing dependencies!") % _(plugin_name)) + + builder.get_object('lbl_main').set_label(_("Please install the dependencies or disable the plugin. To hide this message, you can also deactivate the plugin in the settings.")) + + builder.get_object('btn_close').set_label(_("Quit")) + builder.get_object('btn_disable_plugin').set_label(_("Disable plugin temporarily")) + + if isinstance(message, PluginDependency): + builder.get_object('lbl_message').set_label(_(message.message)) + btn_extra_link = builder.get_object('btn_extra_link') + btn_extra_link.set_label(_("Click here for more information")) + btn_extra_link.set_uri(message.link) + btn_extra_link.set_visible(True) + else: + builder.get_object('lbl_message').set_label(_(message)) + + def show(self): + """ + Show the dialog. + """ + self.window.show_all() + + def on_window_delete(self, *args): + """ + Window close event handler. + """ + self.window.destroy() + self.on_quit() + + def on_close_clicked(self, *args): + self.window.destroy() + self.on_quit() + + def on_disable_plugin_clicked(self, *args): + self.window.destroy() + self.on_disable_plugin() diff --git a/safeeyes/ui/settings_dialog.py b/safeeyes/ui/settings_dialog.py index f4bc4e33..62f54f4b 100644 --- a/safeeyes/ui/settings_dialog.py +++ b/safeeyes/ui/settings_dialog.py @@ -21,7 +21,7 @@ import gi from safeeyes import utility -from safeeyes.model import Config +from safeeyes.model import Config, PluginDependency gi.require_version('Gtk', '3.0') from gi.repository import Gtk @@ -220,22 +220,36 @@ def __create_plugin_item(self, plugin_config): lbl_plugin_name.set_label(_(plugin_config['meta']['name'])) switch_enable.set_active(plugin_config['enabled']) if plugin_config['error']: - lbl_plugin_description.set_label(_(plugin_config['meta']['description'])) + message = plugin_config['meta']['dependency_description'] + if isinstance(message, PluginDependency): + lbl_plugin_description.set_label(_(message.message)) + btn_plugin_extra_link = builder.get_object('btn_plugin_extra_link') + btn_plugin_extra_link.set_label(_("Click here for more information")) + btn_plugin_extra_link.set_uri(message.link) + btn_plugin_extra_link.set_visible(True) + else: + lbl_plugin_description.set_label(_(message)) lbl_plugin_name.set_sensitive(False) lbl_plugin_description.set_sensitive(False) switch_enable.set_sensitive(False) + btn_properties.set_sensitive(False) + if plugin_config['enabled']: + btn_disable_errored = builder.get_object('btn_disable_errored') + btn_disable_errored.set_visible(True) + btn_disable_errored.connect('clicked', lambda button: self.__disable_errored_plugin(button, plugin_config)) + else: lbl_plugin_description.set_label(_(plugin_config['meta']['description'])) + if plugin_config['settings']: + btn_properties.set_sensitive(True) + btn_properties.connect('clicked', lambda button: self.__show_plugins_properties_dialog(plugin_config)) + else: + btn_properties.set_sensitive(False) self.plugin_switches[plugin_config['id']] = switch_enable if plugin_config.get('break_override_allowed', False): self.plugin_map[plugin_config['id']] = plugin_config['meta']['name'] if plugin_config['icon']: builder.get_object('img_plugin_icon').set_from_file(plugin_config['icon']) - if plugin_config['settings']: - btn_properties.set_sensitive(True) - btn_properties.connect('clicked', lambda button: self.__show_plugins_properties_dialog(plugin_config)) - else: - btn_properties.set_sensitive(False) box = builder.get_object('box') box.set_visible(True) return box @@ -247,6 +261,13 @@ def __show_plugins_properties_dialog(self, plugin_config): dialog = PluginSettingsDialog(plugin_config) dialog.show() + def __disable_errored_plugin(self, button, plugin_config): + """ + Permanently disable errored plugin + """ + button.set_sensitive(False) + self.plugin_switches[plugin_config['id']].set_active(False) + def __show_break_properties_dialog(self, break_config, is_short, parent, on_close, on_add, on_remove): """ Show the BreakProperties dialog @@ -258,7 +279,7 @@ def show(self): """ Show the SettingsDialog. """ - self.window.show_all() + self.window.show() def on_switch_postpone_activate(self, switch, state): """ diff --git a/safeeyes/utility.py b/safeeyes/utility.py index fb487f10..022d63c2 100644 --- a/safeeyes/utility.py +++ b/safeeyes/utility.py @@ -241,9 +241,8 @@ def load_plugins_config(safeeyes_config): continue dependency_description = check_plugin_dependencies(plugin['id'], config, plugin.get('settings', {}), plugin_path) if dependency_description: - plugin['enabled'] = False config['error'] = True - config['meta']['description'] = dependency_description + config['meta']['dependency_description'] = dependency_description icon = get_resource_path('ic_warning.png') else: config['error'] = False diff --git a/setup.py b/setup.py index b8ef4d29..3e3f069a 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,6 @@ 'babel', 'psutil', 'croniter', - 'dbus-python', 'PyGObject', 'packaging', 'python-xlib'