├── .gitignore ├── AppImageBuilder.yml ├── LICENSE ├── README.md ├── appimage-appdir.sh ├── appimage-build-fix.sh ├── appimage-build.sh ├── audio ├── Fajr │ ├── Azzam Douik Takbir.ogg │ ├── Azzam Douik.ogg │ ├── Mishary Al-Afasy.ogg │ ├── Muhammad Muhsen.ogg │ └── Nasir Al-Qatami Takbir.ogg └── Normal │ ├── Abdul Basit.ogg │ ├── Abdur Ra'uf.ogg │ ├── Al-Minshawi.ogg │ ├── Azzam Douik Takbir.ogg │ ├── Essam Al-Khan.ogg │ ├── Mishary Al-Afasy.ogg │ ├── Nasir Al-Qatami Takbir.ogg │ ├── Nasir Al-Qatami.ogg │ └── Yusuf Islam.ogg ├── data └── Locations.xml ├── hijra.py ├── hijrical.py ├── home.py ├── icons ├── arrows │ ├── arrow-left.svg │ └── arrow-right.svg ├── hicolor │ ├── 128x128 │ │ └── apps │ │ │ ├── silaty.png │ │ │ └── silaty.svg │ ├── 24x24 │ │ └── apps │ │ │ └── silaty.svg │ ├── 48x48 │ │ └── apps │ │ │ └── silaty.svg │ └── scalable │ │ └── silaty-indicator.svg └── sidebar │ ├── aboutA.svg │ ├── aboutN.svg │ ├── calendarA.svg │ ├── calendarN.svg │ ├── homeA.svg │ ├── homeN.svg │ ├── notifyA.svg │ ├── notifyN.svg │ ├── qiblaA.svg │ ├── qiblaN.svg │ ├── settingsA.svg │ └── settingsN.svg ├── install.sh ├── lang ├── ar.json ├── en.json ├── es.json └── fr.json ├── location.py ├── options.py ├── prayertime.py ├── qiblacompass.py ├── screenshots ├── Silaty.png ├── Silaty_calendar.png ├── Silaty_notification.png ├── Silaty_qibla.png ├── Silaty_settings.png ├── linux-download.png └── windows-download.png ├── settingspane.py ├── sidebar.py ├── silaty-indicator.py ├── silaty.desktop ├── silaty.py ├── silatycal.py ├── snap └── snapcraft.yaml ├── translate.py └── uninstall.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | *.*~ 3 | AppDir/* 4 | appimage-builder-cache/* 5 | *.AppImage 6 | 7 | # Python cache 8 | __pycache__/ 9 | *.pyc 10 | -------------------------------------------------------------------------------- /AppImageBuilder.yml: -------------------------------------------------------------------------------- 1 | # appimage-builder recipe see https://appimage-builder.readthedocs.io for details 2 | version: 1 3 | AppDir: 4 | path: ./AppDir 5 | app_info: 6 | id: com.github.AXeL-dev.silaty 7 | name: Silaty 8 | icon: silaty 9 | version: '1.4' 10 | exec: /usr/local/bin/silaty-indicator 11 | exec_args: $@ 12 | runtime: 13 | env: 14 | APPDIR_LIBRARY_PATH: $APPDIR/usr/lib/x86_64-linux-gnu/gvfs:$APPDIR/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders:$APPDIR/usr/lib/python3.6/lib-dynload:$APPDIR/usr/lib/x86_64-linux-gnu:$APPDIR/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules:$APPDIR/usr/lib/python3/dist-packages/gi:$APPDIR/usr/lib/x86_64-linux-gnu/gio/modules:$APPDIR/usr/lib/x86_64-linux-gnu/gtk-3.0/modules:$APPDIR/lib/x86_64-linux-gnu 15 | apt: 16 | arch: amd64 17 | allow_unauthenticated: true 18 | sources: 19 | - sourceline: deb http://fr.archive.ubuntu.com/ubuntu/ bionic main restricted 20 | - sourceline: deb http://fr.archive.ubuntu.com/ubuntu/ bionic-updates main restricted 21 | - sourceline: deb http://fr.archive.ubuntu.com/ubuntu/ bionic universe 22 | - sourceline: deb http://fr.archive.ubuntu.com/ubuntu/ bionic-updates universe 23 | - sourceline: deb http://fr.archive.ubuntu.com/ubuntu/ bionic multiverse 24 | - sourceline: deb http://fr.archive.ubuntu.com/ubuntu/ bionic-updates multiverse 25 | - sourceline: deb http://fr.archive.ubuntu.com/ubuntu/ bionic-backports main restricted 26 | universe multiverse 27 | - sourceline: deb http://security.ubuntu.com/ubuntu bionic-security main restricted 28 | - sourceline: deb http://security.ubuntu.com/ubuntu bionic-security universe 29 | - sourceline: deb http://security.ubuntu.com/ubuntu bionic-security multiverse 30 | - sourceline: deb http://ppa.launchpad.net/lubomir-brindza/nautilus-typeahead/ubuntu 31 | bionic main 32 | - sourceline: deb http://ppa.launchpad.net/ubuntubudgie/backports/ubuntu bionic 33 | main 34 | - sourceline: deb [arch=amd64] http://packages.microsoft.com/repos/edge/ stable 35 | main 36 | - sourceline: deb [arch=amd64] https://repo.skype.com/deb stable main 37 | - sourceline: deb http://ppa.launchpad.net/bluetooth/bluez/ubuntu bionic main 38 | include: 39 | - appmenu-gtk3-module 40 | - dconf-gsettings-backend 41 | - gvfs 42 | - ibus-gtk3 43 | - libappindicator3-1 44 | - libbz2-1.0 45 | - libcanberra-gtk3-module 46 | - libgcrypt20 47 | - libgstreamer1.0-0 48 | - liblz4-1 49 | - libpcre3 50 | - libpython3.6-stdlib 51 | - librsvg2-common 52 | - libsystemd0 53 | - libxau6 54 | - libxdmcp6 55 | - libxext6 56 | - libxfixes3 57 | - libxinerama1 58 | - libxrender1 59 | - python3-gi 60 | exclude: [] 61 | files: 62 | exclude: 63 | - usr/share/man 64 | - usr/share/doc/*/README.* 65 | - usr/share/doc/*/changelog.* 66 | - usr/share/doc/*/NEWS.* 67 | - usr/share/doc/*/TODO.* 68 | test: 69 | fedora: 70 | image: appimagecrafters/tests-env:fedora-30 71 | command: ./AppRun 72 | use_host_x: true 73 | debian: 74 | image: appimagecrafters/tests-env:debian-stable 75 | command: ./AppRun 76 | use_host_x: true 77 | arch: 78 | image: appimagecrafters/tests-env:archlinux-latest 79 | command: ./AppRun 80 | use_host_x: true 81 | centos: 82 | image: appimagecrafters/tests-env:centos-7 83 | command: ./AppRun 84 | use_host_x: true 85 | ubuntu: 86 | image: appimagecrafters/tests-env:ubuntu-xenial 87 | command: ./AppRun 88 | use_host_x: true 89 | AppImage: 90 | arch: x86_64 91 | update-information: guess 92 | sign-key: None 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Silaty 2 | 3 | A neat prayer reminder app in GTK 4 | 5 | ![screenshot](screenshots/Silaty.png) 6 | 7 | ## Installation 8 | 9 | [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/silaty) 10 | [![احصل عليه من Snap Store](https://snapcraft.io/static/images/badges/ar/snap-store-black.svg)](https://snapcraft.io/silaty) 11 | [Download the AppImage](https://github.com/LinuxForGeeks/Silaty/releases/download/v1.4/Silaty-1.4-x86_64.AppImage) 12 | [Download on Windows](https://github.com/MustafaKhalaf69/SilatyCrossPlatform/releases/download/1.4-updated/SilatyWindows.zip) 13 | 14 | [How to install on windows?](https://github.com/LinuxForGeeks/Silaty/tree/platform/win#windows-configuration) 15 | 16 | 29 | 30 | ### Install from source 31 | 32 | Download or clone this repository with git: 33 | 34 | ```bash 35 | git clone https://github.com/AXeL-dev/Silaty.git 36 | ``` 37 | 38 | Make sure that you have all the dependencies installed using the following command: 39 | 40 |
41 | Ubuntu / Debian 42 | 43 | ```bash 44 | sudo apt install gir1.2-gtk-3.0 gir1.2-appindicator3-0.1 gir1.2-notify-0.7 gir1.2-gstreamer-1.0 45 | ``` 46 | 47 |
48 | 49 |
50 | Arch Linux 51 | 52 | ```bash 53 | pacman -Syu gtk3 libappindicator-gtk3 libnotify gstreamer 54 | ``` 55 | 56 |
57 | 58 | **Note:** this command may change depending on your linux distribution. 59 | 60 | To install run: 61 | 62 | ```bash 63 | cd /path/to/silaty 64 | sudo ./install.sh 65 | ``` 66 | 67 | Once installed, you can run Silaty from your applications menu, or using the command bellow: 68 | 69 | ```bash 70 | silaty-indicator 71 | ``` 72 | 73 | To uninstall run: 74 | 75 | ```bash 76 | sudo ./uninstall.sh 77 | ``` 78 | 79 | ## Changelog 80 | 81 | ### v1.5 82 | 83 | - added [windows support](https://github.com/LinuxForGeeks/Silaty/tree/platform/win), thanks to [@MustafaKhalaf69](https://github.com/MustafaKhalaf69). 84 | - added short takbir audios. 85 | - fixed notify package version on arch linux. 86 | - enhanced translation mechanism. 87 | - added french & spanish translations, thanks to [@DBChoco](https://github.com/DBChoco). 88 | - fixed prayer times alignment. 89 | 90 | ### v1.4 91 | 92 | - added snap & appimage packages. 93 | - fixed prayer times issues on some locations. 94 | 95 | ### v1.3 96 | 97 | - arabic language is now supported. 98 | - app crash on KDE desktop fixed. 99 | 100 | ### v1.2 101 | 102 | - [Silaty unable to sync prayer times correctly !! #9](https://github.com/Jessewb786/Silaty/issues/9) fixed. 103 | - [Hijri Date #8](https://github.com/Jessewb786/Silaty/issues/8) fixed. 104 | - update home title when stack changes. 105 | 106 | ### v1.1 107 | 108 | - LICENSE added. 109 | - [Add screenshots #3](https://github.com/Jessewb786/Silaty/issues/3). 110 | - README updated. 111 | - attempt to clean code. 112 | - GTK warnings fixed. 113 | - switching between sidebar tabs from indicator menu fixed. 114 | - about tab added/implemented into sidebar. 115 | - fixed a crash when closing the main window. 116 | - [Settings won't open #2](https://github.com/Jessewb786/Silaty/issues/2) fixed. 117 | - load notification icon from its path instead of using an icon name. 118 | - use default layout size, instead of using a fixed window size. 119 | - refresh prayer times on home tab when changing settings. 120 | - useless debug messages commented (especially those used in main loop). 121 | - attempt to fix [installing error #4](https://github.com/Jessewb786/Silaty/issues/4) by ignoring pycompile cmd. 122 | - timezone added to location settings. 123 | - get location using Google Maps API disabled (no longer works). 124 | - daylight saving time setting added. 125 | - display/hide main window from indicator menu fixed. 126 | - location search button added. 127 | - wrong time suffix (AM/PM) when using 12h clock format fixed. 128 | - solve some class naming conflicts. 129 | - [Locations.xml](data/Locations.xml) updated from [libgweather](https://github.com/GNOME/libgweather) repository. 130 | - grabbing timezone from locations xml file done. 131 | - remaining time until next prayer corrected. 132 | 133 | ### v1.0 134 | 135 | - first release 136 | 137 | ## ToDo 138 | 139 | - [x] fix settings issues (especially Location settings). 140 | - [x] use a custom list to get/set location (@see [Minbar](https://github.com/fajran/minbar)). 141 | - [x] remove duplicate locations/cities in [Locations.xml](data/Locations.xml). 142 | - [ ] translate to other languages. 143 | - [ ] support dark mode. 144 | 145 | ## Credits 146 | 147 | - [Jessewb786](https://github.com/Jessewb786) 148 | 149 | ## License 150 | 151 | Silaty is licensed under the [GPL license](LICENSE). 152 | -------------------------------------------------------------------------------- /appimage-appdir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p AppDir/usr/share/silaty 4 | mkdir -p AppDir/usr/share/icons/hicolor/scalable/apps/ 5 | mkdir -p AppDir/usr/share/icons/hicolor/128x128/apps/ 6 | mkdir -p AppDir/usr/share/icons/hicolor/48x48/apps/ 7 | mkdir -p AppDir/usr/share/icons/hicolor/24x24/apps/ 8 | mkdir -p AppDir/usr/share/applications 9 | mkdir -p AppDir/etc/xdg/autostart 10 | mkdir -p AppDir/usr/local/bin 11 | cp -R icons AppDir/usr/share/silaty/ 12 | cp -R audio AppDir/usr/share/silaty/ 13 | cp -R data AppDir/usr/share/silaty/ 14 | 15 | cp icons/hicolor/128x128/apps/silaty.svg AppDir/usr/share/icons/hicolor/scalable/apps/ 16 | cp icons/hicolor/128x128/apps/silaty.png AppDir/usr/share/icons/hicolor/scalable/apps/ 17 | cp icons/hicolor/128x128/apps/silaty.svg AppDir/usr/share/icons/ 18 | cp icons/hicolor/128x128/apps/silaty.png AppDir/usr/share/icons/ 19 | chmod 644 AppDir/usr/share/icons/hicolor/scalable/apps/silaty.svg 20 | 21 | cp icons/hicolor/128x128/apps/silaty.svg AppDir/usr/share/icons/hicolor/128x128/apps/ 22 | chmod 644 AppDir/usr/share/icons/hicolor/128x128/apps/silaty.svg 23 | 24 | cp icons/hicolor/48x48/apps/silaty.svg AppDir/usr/share/icons/hicolor/48x48/apps/ 25 | chmod 644 AppDir/usr/share/icons/hicolor/48x48/apps/silaty.svg 26 | 27 | cp icons/hicolor/24x24/apps/silaty.svg AppDir/usr/share/icons/hicolor/24x24/apps/ 28 | chmod 644 AppDir/usr/share/icons/hicolor/24x24/apps/silaty.svg 29 | 30 | cp *.py AppDir/usr/share/silaty/ 31 | chmod 755 -R AppDir/usr/share/silaty/ 32 | 33 | cp silaty.desktop AppDir/etc/xdg/autostart/ 34 | chmod 755 AppDir/etc/xdg/autostart/silaty.desktop 35 | 36 | cp silaty.desktop AppDir/usr/share/applications/ 37 | chmod 755 AppDir/usr/share/applications/silaty.desktop 38 | 39 | ln -sf /usr/share/silaty/silaty-indicator.py AppDir/usr/local/bin/silaty-indicator 40 | #chmod 755 AppDir/usr/local/bin/silaty-indicator 41 | -------------------------------------------------------------------------------- /appimage-build-fix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$(id -u)" != "0" ]; then 4 | echo “This script must be run as root” 2>&1 5 | exit 1 6 | fi 7 | 8 | sed -i '/raise RuntimeError(.*/i \ return "x86_64"' /usr/local/lib/python3.6/dist-packages/appimagebuilder/common/elf.py 9 | -------------------------------------------------------------------------------- /appimage-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sh appimage-appdir.sh 4 | 5 | appimage-builder --skip-test --skip-appimage 6 | 7 | gdk-pixbuf-query-loaders > AppDir/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache 8 | 9 | # For test purpose 10 | #AppDir/AppRun 11 | 12 | appimage-builder --skip-test --skip-build 13 | -------------------------------------------------------------------------------- /audio/Fajr/Azzam Douik Takbir.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Fajr/Azzam Douik Takbir.ogg -------------------------------------------------------------------------------- /audio/Fajr/Azzam Douik.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Fajr/Azzam Douik.ogg -------------------------------------------------------------------------------- /audio/Fajr/Mishary Al-Afasy.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Fajr/Mishary Al-Afasy.ogg -------------------------------------------------------------------------------- /audio/Fajr/Muhammad Muhsen.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Fajr/Muhammad Muhsen.ogg -------------------------------------------------------------------------------- /audio/Fajr/Nasir Al-Qatami Takbir.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Fajr/Nasir Al-Qatami Takbir.ogg -------------------------------------------------------------------------------- /audio/Normal/Abdul Basit.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Abdul Basit.ogg -------------------------------------------------------------------------------- /audio/Normal/Abdur Ra'uf.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Abdur Ra'uf.ogg -------------------------------------------------------------------------------- /audio/Normal/Al-Minshawi.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Al-Minshawi.ogg -------------------------------------------------------------------------------- /audio/Normal/Azzam Douik Takbir.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Azzam Douik Takbir.ogg -------------------------------------------------------------------------------- /audio/Normal/Essam Al-Khan.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Essam Al-Khan.ogg -------------------------------------------------------------------------------- /audio/Normal/Mishary Al-Afasy.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Mishary Al-Afasy.ogg -------------------------------------------------------------------------------- /audio/Normal/Nasir Al-Qatami Takbir.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Nasir Al-Qatami Takbir.ogg -------------------------------------------------------------------------------- /audio/Normal/Nasir Al-Qatami.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Nasir Al-Qatami.ogg -------------------------------------------------------------------------------- /audio/Normal/Yusuf Islam.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/audio/Normal/Yusuf Islam.ogg -------------------------------------------------------------------------------- /hijra.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Hijri Islamic Calendar converting functions, 5 | Copyright (c) 2006-2008 Muayyad Saleh Alsadi 6 | Based on an enhanced algorithm designed by me 7 | the algorithm is discussed in a book titled "حتى لا ندخل جحور الضباب" 8 | (not yet published) 9 | 10 | This file can be used to implement apps, gdesklets or karamba ..etc 11 | 12 | This algorithm is based on integer operations 13 | which that there is no round errors (given accurate coefficients) 14 | the accuracy of this algorithm is based on 3 constants (p,q and a) 15 | where p/q is the full months percentage [ gcd(p,q) must be 1 ] 16 | currently it's set to 191/360 which mean that there is 191 months 17 | having 30 days in a span of 360 years, other months are 29 days. 18 | and a is just a shift. 19 | 20 | 21 | Released under terms on Waqf Public License. 22 | This program is free software; you can redistribute it and/or modify 23 | it under the terms of the latest version Waqf Public License as 24 | published by Ojuba.org. 25 | 26 | This program is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 29 | 30 | The Latest version of the license can be found on 31 | "http://www.ojuba.org/wiki/doku.php/waqf/license" 32 | 33 | 34 | Portions of this algorithm is based on that found on GNU EMACS 35 | the difference is that this algorithm does not set 36 | all months to a fixed number of days (in the original algorithm 37 | first month always have 30 days) 38 | 39 | 40 | The original GNU Emacs LISP algorithm 41 | Copyright (C) 1995, 1997, 2001 Free Software Foundation, Inc. 42 | Edward M. Reingold 43 | Technical details of all the calendrical calculations can be found in 44 | ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold, 45 | Cambridge University Press (1997). 46 | Comments, corrections, and improvements should be sent to 47 | Edward M. Reingold Department of Computer Science 48 | (217) 333-6733 University of Illinois at Urbana-Champaign 49 | reingold@cs.uiuc.edu 1304 West Springfield Avenue 50 | """ 51 | __p_const=191 52 | __q_const=360 53 | __a_const=48 54 | __hijri_epoch=227015 # = Julian 0622-7-16 = gregorian 0759-6-11 (I think it should be 622, 7, 19) 55 | # TODO: Why does the the hijri_epoch 227015 does not give the expected value when converted to gregorian 56 | 57 | def get_consts(): 58 | """Return a tuple of the 3 constants (p,q,a) used by this algothim, for debuging""" 59 | return (__p_const, __q_const, __a_const) 60 | 61 | def get_epoch(): 62 | """Return Hijri epoch, number of days since gregorian epoch, (should be Julian 0622-7-16 (ie. 227015 days)""" 63 | return __hijri_epoch 64 | 65 | def hijri_month_days(Y,M): 66 | """Return the number of days in a given hijri month M in a given Y""" 67 | Mc = ( Y -1) *12 + M 68 | if (((Mc+ __a_const) * __p_const) % __q_const) < __p_const : return 30 69 | else: return 29 70 | 71 | # NOTE: trivial implementation 72 | #def hijri_days_before_month_(Y,M): # simple mothod, optimization is possible by reusing Mc ..etc. 73 | # """Return the number of days before a given moth M in a given year Y""" 74 | # sum=0 75 | # for i in range(1,M): sum+=hijri_month_days(Y,i); 76 | # return sum 77 | 78 | def hijri_days_before_month(Y,M): 79 | """Return the number of days before a given moth M in a given year Y (0 for M=1)""" 80 | Mc = (Y -1) *12 + 1 + __a_const 81 | McM=Mc * __p_const 82 | sum=0 83 | for i in range(1,M): 84 | if (McM % __q_const) < __p_const : sum+=30 85 | else: sum+=29 86 | McM+=__p_const 87 | return sum 88 | 89 | #TEST: PASSED 90 | # test that the faster hijri_days_before_month is ok 91 | #def test_hijri_days_before_month(): 92 | # l=[(y,m) for y in range(1400,1499) for m in range(1,13)] 93 | # for y,m in l: 94 | # d1=hijri_days_before_month(y,m) 95 | # d2=hijri_days_before_month_(y,m) 96 | # if d1!=d2: print y,m,d1,d2 97 | 98 | 99 | def hijri_year_days(Y): 100 | """Return the number of days in a given year Y""" 101 | return hijri_days_before_month(Y,13) 102 | 103 | def hijri_day_number (Y, M, D): 104 | """Return the day number within the year of the Islamic date (Y, M, D), 1 for 1/1 in any year""" 105 | return hijri_days_before_month(Y,M)+D 106 | 107 | 108 | # BAD fast implementation 109 | #def hijri_to_absolute_ (Y, M, D): 110 | # """Return absolute date of Hijri (Y,M,D), eg. ramadan (9),1,1427 -> 732578 """ 111 | # Mc=(Y-1)*12 112 | # # days before Hijra + days in the years before + days from the begining of that year 113 | # return __hijri_epoch + \ 114 | # Mc*29 + Mc*__p_const/__q_const + \ # this line should involve __a_const 115 | # hijri_day_number (Y, M, D) - 1 116 | 117 | # correct implementation # TODO: optimize it more and test that after optimization 118 | def hijri_to_absolute (Y, M, D): 119 | """Return absolute date of Hijri (Y,M,D), eg. ramadan (9),1,1427 -> 732578 """ 120 | Mc=(Y-1)*12 121 | # day count=days before Hijra plus (...) 122 | dc=__hijri_epoch 123 | # plus days in the years before till first multiples of q plus (...) 124 | Mc-=Mc % __q_const 125 | y=Y-Mc//12 126 | dc+=Mc*29 + Mc*__p_const//__q_const 127 | # plus those after the multiples plus (...) 128 | for i in range(1,y): dc += hijri_year_days(i) 129 | # plus days from the begining of that year 130 | dc+=hijri_day_number (Y, M, D) - 1 131 | return dc 132 | 133 | def hijri_month_days_(y,m): 134 | """Return the number of days in a given hijri month M in a given Y""" 135 | return hijri_to_absolute(y+m//12,m%12+1,1)-hijri_to_absolute(y,m,1) 136 | 137 | # TEST: PASSED 138 | #def test_hijri_to_absolute_v_month_days(): 139 | # #l=[(y,m) for y in range(1,31) for m in range(1,13)] 140 | # l=[(y,m) for y in range(1400,1499) for m in range(1,13)] 141 | # for y,m in l: 142 | # d1=hijri_month_days(y,m) 143 | # d2=hijri_to_absolute(y+m/12,m%12+1,1)-hijri_to_absolute(y,m,1) 144 | # if d1!=d2: print y,m,y+m/12,m%12+1,'d1=',d1,", d2=",d2 145 | 146 | # round then move to exact, very slow perfect implementation 147 | #def absolute_to_hijri_ (date): # TODO: check if it's always compatible with absolute_from_hijri 148 | # """Return Hijri date (Y,M,D) corresponding to the given absolute number of days.""" 149 | # if date < __hijri_epoch: return None; # pre-Islamic date 150 | # dd=date-__hijri_epoch 151 | # Mc=dd/(29*(__q_const-__p_const)+ 30*__p_const)*__q_const # mounth count till multibles of q 152 | # Y=y=Mc/12+1; M=m=(Mc%12)+1 153 | # while(date > hijri_to_absolute(Y,M,1)): 154 | # y,m=Y,M 155 | # M+=1 156 | # if M>12: M=1; Y+=1 157 | # Y=y; M=m 158 | # D=1 + date - hijri_to_absolute(Y,M,1) 159 | # if D>hijri_month_days(Y,M): 160 | # M+=1 161 | # if M>12: M=1; Y+=1 162 | # D=1 + date - hijri_to_absolute(Y,M,1) 163 | # return (Y,M,D) 164 | 165 | 166 | # direct way, test PASSED 167 | def absolute_to_hijri (date): 168 | """Return Hijri date (Y,M,D) corresponding to the given absolute number of days.""" 169 | if date < __hijri_epoch: return None; # pre-Islamic date 170 | Mc=(date-__hijri_epoch+1)*__q_const//(29*__q_const+__p_const) 171 | Y=Mc//12+1; M=(Mc%12)+1 172 | # consistency check 173 | d=hijri_to_absolute(Y,M,1) # TODO: this is an expensive call 174 | if (date < d): # go one month back if needed 175 | M-=1 176 | if M==0: Y-=1; M=12 177 | d-=hijri_month_days(Y,M) # this call is fast 178 | # 179 | D=1 + date - d 180 | return (Y,M,D) 181 | 182 | # TEST: PASSED 183 | #def test_c(): 184 | # l=[(y,m) for y in range(1400,1499) for m in range(1,13)] 185 | # for y,m in l: 186 | # d=hijri_month_days(y,m) 187 | # if absolute_to_hijri(hijri_to_absolute(y,m,1))!=(y,m,1): print y,m,1, absolute_to_hijri(hijri_to_absolute(y,m,1)) 188 | # if absolute_to_hijri(hijri_to_absolute(y,m,d))!=(y,m,d): print y,m,d, absolute_to_hijri(hijri_to_absolute(y,m,d)) 189 | 190 | def hijri_day_of_week (Y, M, D): 191 | """Return the day-of-the-week index of hijri (Y,M,D) Date, 0 for Sunday, 1 for Monday, etc.""" 192 | return hijri_to_absolute (Y,M, D) % 7 193 | # /////////////////////////////// 194 | # high level converting functions 195 | 196 | def hijri_to_gregorian (year, month, day): 197 | """Return gregorian (year, month, day) converted from Islamic Hijri calender""" 198 | return absolute_to_gregorian( hijri_to_absolute (year, month, day)) 199 | 200 | def gregorian_to_hijri (year, month, day): 201 | """Return Hijri (year, month, day) converted from gregorian calender""" 202 | return absolute_to_hijri( gregorian_to_absolute (year, month, day)) 203 | 204 | #/////////////////////////////////// 205 | # Gregorian functions 206 | #/////////////////////////////////// 207 | # This Portions of is based on that found on GNU EMACS 208 | 209 | #The original GNU Emacs LISP algorithm 210 | #Copyright (C) 1995, 1997, 2001 Free Software Foundation, Inc. 211 | # Edward M. Reingold 212 | # Technical details of all the calendrical calculations can be found in 213 | # ``Calendrical Calculations'' by Nachum Dershowitz and Edward M. Reingold, 214 | # Cambridge University Press (1997). 215 | # Comments, corrections, and improvements should be sent to 216 | # Edward M. Reingold Department of Computer Science 217 | # (217) 333-6733 University of Illinois at Urbana-Champaign 218 | # reingold@cs.uiuc.edu 1304 West Springfield Avenue 219 | 220 | days_in_month=( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); 221 | def gregorian_leap_year_p (year): 222 | """Return 1 (True) if YEAR is a Gregorian leap year.""" 223 | if ((year % 4) == 0 and ((year % 100) or (year % 400) == 0)): return 1; 224 | return 0; 225 | 226 | def gregorian_month_days (year, month): 227 | """The last day in MONTH during YEAR.""" 228 | if (month == 2 and gregorian_leap_year_p (year)): return 29; 229 | return days_in_month[month-1]; 230 | 231 | def gregorian_day_number (year, month, day): 232 | """Return the day number within the year of the date (year,month, day)""" 233 | if month<3: return day + (31 * (month - 1)) 234 | return day + (31 * (month - 1)) - \ 235 | ((month << 2) + 23) // 10 + (gregorian_leap_year_p (year) & 1); 236 | 237 | def gregorian_to_absolute (year, month, day): 238 | prior_years = year - 1 239 | return gregorian_day_number (year, month, day) + \ 240 | (365 * prior_years + (prior_years >> 2)) - \ 241 | (prior_years // 100) + (prior_years // 400) 242 | 243 | def absolute_to_gregorian(date): 244 | """return (year month day) corresponding to the absolute DATE. 245 | The absolute date is the number of days elapsed since the (imaginary) 246 | Gregorian date Sunday, December 31, 1 BC.""" 247 | 248 | # See the footnote on page 384 of ``Calendrical Calculations, Part II: 249 | # Three Historical Calendars'' by E. M. Reingold, N. Dershowitz, and S. M. 250 | # Clamen, Software--Practice and Experience, Volume 23, Number 4 251 | # (April, 1993), pages 383-404 for an explanation. 252 | d0 = date - 1; 253 | n400 = d0 // 146097; 254 | d1 = d0 % 146097; 255 | n100 = d1 // 36524; 256 | d2 = d1 % 36524; 257 | n4 = d2 // 1461; 258 | d3 = d2 % 1461; 259 | n1 = d3 // 365; 260 | dd = (d3 % 365) + 1; 261 | yy = ((400 * n400) + (100 * n100) + (n4 * 4) + n1); 262 | if (n100 == 4) or (n1 == 4): return (yy, 12, 31); 263 | yy=yy+1; 264 | mm = 1; 265 | while(date >= gregorian_to_absolute (yy,mm, 1)): mm+=1; 266 | d=gregorian_to_absolute (yy, mm-1, 1); 267 | return (yy, mm-1,date-d+1); 268 | # d = calendar_absolute_from_gregorian (1, 1, yy); 269 | # mm=1; 270 | # while(mm <= 12): 271 | # dd = date - d + 1; 272 | # dm = calendar_last_day_of_month (mm, yy); 273 | # if dd <= dm: return (mm,dd+1,yy); 274 | # d += dm; 275 | # mm=mm+1; 276 | # return 0; # should not happened 277 | def gregorian_day_of_week (yy, mm, dd): 278 | """Return the day-of-the-week index of gregorian (yy, mm, dd) DATE, 0 for Sunday, 1 for Monday, etc.""" 279 | return gregorian_to_absolute (yy,mm, dd) % 7; 280 | # /////////////////////////////// 281 | # some tests for debuging to be removed 282 | 283 | def test1(): 284 | global __a_const; 285 | __a_const=48 286 | for __a_const in range(0,100): unmatched=0; from_y=1; to_y=4001 287 | for y in range(from_y,to_y): 288 | if hijri_days(y)!=emacs_hijri_days(y): unmatched+=1 289 | print ("%d years (%g %%) unmatched when a=%d", (unmatched, float(float(unmatched)/(to_y-from_y)), __a_const)) 290 | __a_const=48 291 | sum=0.0 292 | for y in range(1,4001): sum+=hijri_days(y) 293 | print ("year len=%f ", float(float(sum)/4000.0*100.0)) 294 | __a_const=47 295 | sum=0.0 296 | for y in range(1,4001): sum+=hijri_days(y) 297 | print ("year len=%f ", float(float(sum)/4000.0*100.0)) 298 | ########################## 299 | if __name__ == "__main__": 300 | # conclusion 301 | # 0% for a=16 48 65 302 | # 7% for a=1 31 33 50 80 82 97 99 303 | # 13% for a=14 18 46 63 67 95 304 | # 20% for a=12 29 3 35 52 78 84 305 | # ... 306 | # 73% for a=45 47 49 ..etc. 307 | ########################## 308 | __a_const=16 309 | print ("for a=%d", __a_const) 310 | sum=0.0 311 | for y in range(1,4001): sum+= float(hijri_month_days(y,12)==30) 312 | print ("perfect thu-hijja months is %f %% ", float(float(sum)/4000.0*100.0)) 313 | sum=0.0 314 | for y in range(1,4001): sum+= float(hijri_month_days(y,9)==30) 315 | print ("perfect Ramadan months is %f %% ", float(float(sum)/4000.0*100.0)) 316 | 317 | __a_const=48 318 | print ("for a=%d", __a_const) 319 | sum=0.0 320 | for y in range(1,4001): sum+= float(hijri_month_days(y,12)==30) 321 | print ("perfect thu-hijja months is %f %% ", float(float(sum)/4000.0*100.0)) 322 | sum=0.0 323 | for y in range(1,4001): sum+= float(hijri_month_days(y,9)==30) 324 | print ("perfect Ramadan months is %f %% ", float(float(sum)/4000.0*100.0)) 325 | 326 | __a_const=65 327 | print ("for a=%d", __a_const) 328 | sum=0.0 329 | for y in range(1,4001): sum+= float(hijri_month_days(y,12)==30) 330 | print ("perfect thu-hijja months is %f %% ", float(float(sum)/4000.0*100.0)) 331 | sum=0.0 332 | for y in range(1,4001): sum+= float(hijri_month_days(y,9)==30) 333 | print ("perfect Ramadan months is %f %% ", float(float(sum)/4000.0*100.0)) 334 | 335 | __a_const=48 336 | print ("for a=%d", __a_const) 337 | for m in range(1,13): 338 | sum=0.0 339 | for y in range(1,4001): sum+= float(hijri_month_days(y,m)==30) 340 | print ("perfect %d months is %f %% ", (m,float(float(sum)/4000.0*100.0))) 341 | -------------------------------------------------------------------------------- /hijrical.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Hijri Islamic High level Calendar API, 5 | Copyright (c) 2006-2008 Muayyad Saleh Alsadi 6 | Based on an enhanced algorithm designed by me 7 | the algorithm is discussed in a book titled "حتى لا ندخل جحور الضباب" 8 | (not yet published) 9 | 10 | This file can be used to implement apps, gdesklets or karamba ..etc 11 | 12 | The algorith itself is not here, it's in another file called hijra.py 13 | 14 | Released under terms on Waqf Public License. 15 | This program is free software; you can redistribute it and/or modify 16 | it under the terms of the latest version Waqf Public License as 17 | published by Ojuba.org. 18 | 19 | This program is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 22 | 23 | The Latest version of the license can be found on 24 | "http://www.ojuba.org/wiki/doku.php/waqf/license" 25 | 26 | """ 27 | from time import localtime 28 | from hijra import * 29 | 30 | class HijriCal: 31 | """a Class that provide a high level Islamic Hijri calendar API""" 32 | def __init__(self, adjustment=0): 33 | self.adjustment = adjustment 34 | """Create HijriCal Object""" 35 | self.__md = [[""]*7,[""]*7,[""]*7,[""]*7,[""]*7,[""]*7] # 7 days on at most 6 weeks 36 | self.__g_md = [[""]*7,[""]*7,[""]*7,[""]*7,[""]*7,[""]*7] # 7 days on at most 6 weeks 37 | self.__direct =- 1 38 | self.__ws = 6 # make Sat is first day of week, and fill rows directly 39 | self.goto_today() 40 | 41 | def goto_today(self): 42 | """Jump to today""" 43 | yy, mm, dd = localtime()[:3] 44 | self.g_today = (yy, mm, dd) 45 | Y, M, D = self.goto_gregorian_day(yy, mm, dd) 46 | wd = hijri_day_of_week (Y, M, D) 47 | self.today = (Y, M, D, wd) 48 | 49 | def refresh_today(self): 50 | """check is today is uptodate, update them if not and return True""" 51 | if self.g_today==localtime()[:3]: 52 | return False 53 | else: 54 | yy, mm, dd = localtime()[:3] 55 | self.g_today= (yy, mm, dd) 56 | dd += int(self.adjustment) # adjust hijri calendar 57 | Y, M, D = gregorian_to_hijri(yy, mm, dd) 58 | wd = hijri_day_of_week (Y, M, D) 59 | self.today= (Y, M, D, wd) 60 | return True 61 | 62 | def goto_gregorian_day(self,yy, mm, dd): 63 | """Jump to some Hijri day""" 64 | try: 65 | dd += int(self.adjustment) # adjust hijri calendar 66 | Y, M, D = gregorian_to_hijri(yy, mm, dd) 67 | self.goto_hijri_day(Y, M, D) 68 | except: 69 | self.validate() 70 | 71 | return (self.Y,self.M,self.D) 72 | 73 | def goto_hijri_day(self,Y,M,D): 74 | """Jump to some Hijri day""" 75 | self.Y, self.M, self.D = Y, M, D 76 | self.gy,self.gm,self.gd = hijri_to_gregorian(Y, M, D) 77 | self.validate() 78 | 79 | self.mn=hijri_month_days(self.Y, self.M) 80 | self.ms=(7 - self.__ws + hijri_day_of_week(self.Y, self.M, 1))% 7 81 | self.fill_month_days() 82 | return (Y,M,D) 83 | 84 | def fill_month_days(self): 85 | """for internal usage""" 86 | Y, M, D = self.Y,self.M, 1 87 | gy,gm,gd=hijri_to_gregorian(Y,M,D) 88 | gn=gregorian_month_days(gy,gm) 89 | for i in range(6): 90 | for j in range(7): 91 | self.__md[i][j]="" 92 | self.__g_md[i][j]="" 93 | row=0 94 | if self.__direct>0: 95 | col=self.ms 96 | endcol=7 97 | icol=0 98 | else: 99 | col=6-self.ms 100 | endcol=-1 101 | icol=6 102 | for i in range(self.mn): 103 | self.__md[row][col]=i+1 104 | self.__g_md[row][col]=(gd,gm,gy) 105 | gd+=1 106 | if (gd>gn): 107 | gd=1 108 | gm+=1 109 | 110 | if gm>12: 111 | gm=1 112 | gy+=1 113 | 114 | col+=self.__direct 115 | if (col==endcol): 116 | row+=1 117 | col=icol 118 | 119 | def validate(self): 120 | """Make sure the the current Y,M,D is a a valid date, return 0 if it's already valid""" 121 | f=0 122 | if self.M<1: 123 | self.M=12 124 | self.Y-=1 125 | f=1 126 | if self.M>12: 127 | self.M=1 128 | self.Y+=1 129 | f=1 130 | if self.Y<1: 131 | self.Y=1 132 | f=1 133 | if self.D<1: 134 | self.D=1 135 | f=1 136 | 137 | d=hijri_month_days(self.Y, self.M) 138 | if self.D>d : 139 | self.D=d 140 | f=1 141 | return f 142 | 143 | def get_array(self): return tuple(self.__md) 144 | def get_g_array(self): return tuple(self.__g_md) 145 | 146 | def get_week_start(self): return self.__ws 147 | def set_week_start(self,ws): 148 | """Set the first day of the week, 0:Sun, 1:Mon, ..., 6:Sat.""" 149 | self.__ws=ws 150 | self.goto_hijri_day(self.Y, self.M, self.D) 151 | 152 | def get_direction(self): return self.__direct 153 | def set_direction(self, direct): 154 | """Set the BiDi RTL direction, 1 direct, -1 reversed""" 155 | self.__direct=direct 156 | self.goto_hijri_day(self.Y, self.M, self.D) 157 | -------------------------------------------------------------------------------- /home.py: -------------------------------------------------------------------------------- 1 | # Silaty 2 | # Copyright (c) 2018 - 2021 AXeL 3 | # Copyright (c) 2014 - 2015 Jessewb786 4 | 5 | import gi 6 | gi.require_version('Gtk', '3.0') 7 | from gi.repository import GObject, Gtk, Gdk 8 | from hijrical import * 9 | from translate import translate_text as _ 10 | import datetime 11 | 12 | class Home(Gtk.Box): 13 | def __init__(self, parent): 14 | Gtk.Box.__init__(self) 15 | self.parent = parent 16 | self.set_orientation(Gtk.Orientation.VERTICAL) 17 | self.set_margin_bottom(6) 18 | self.titlelabel = Gtk.Label(halign=Gtk.Align.FILL, margin_bottom=12, margin_top=12, margin_left=6, margin_right=6) 19 | self.set_title() 20 | self.pack_start(self.titlelabel, False, True, 0) 21 | self.prayers = [] 22 | self.nprayers = 0 23 | self.connect("prayers-updated", self.update_prayers_highlight) 24 | 25 | def set_title(self): 26 | # Set the Date in the Title 27 | now_time = datetime.datetime.now().strftime("%H:%M") 28 | now_wd = datetime.datetime.now().strftime("%A") 29 | g_day = datetime.datetime.now().strftime("%d") 30 | g_month = datetime.datetime.now().strftime("%B") 31 | g_year = datetime.datetime.now().strftime("%Y") 32 | g_date = '%s %s %s' % (g_day, _(g_month), g_year) 33 | calc = HijriCal(self.parent.prayertimes.options.hijrical_adjustment) 34 | h_months = ['Muharram', 'Safar', 'Rabi al Awwal', 'Rabi al Akhira', 'Jumada al Ula', 'Jumada al Akhira', 'Rajab', "Sha'ban", 'Ramadan', 'Shawwal', "Dhu al Qa'da", 'Dhu al Hijja'] 35 | h_year, h_month, h_day, h_week_day = calc.today 36 | h_date = '%i %s %i' % (h_day, _(h_months[int(h_month-1)]), h_year) 37 | 38 | self.titlelabel.set_label(_('%s - %s, %s / %s') % (now_time, _(now_wd), h_date, g_date)) 39 | 40 | def add_prayer(self, prayer, prayertime, colored): 41 | # Prayer Listbox 42 | prayercontainer = Gtk.Box(orientation = Gtk.Orientation.VERTICAL) 43 | prayercontainer.set_size_request(380, 0) 44 | prayerbox = Prayer(prayer, prayertime, colored) 45 | prayerbox.set_halign(Gtk.Align.FILL) 46 | prayerbox.set_valign(Gtk.Align.CENTER) 47 | 48 | if self.nprayers % 2 == 0: 49 | color = 235.0/256.0 50 | prayerbox.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA.from_color(Gdk.Color.from_floats(color,color,color))) 51 | color = 204.0/256.0 52 | prayercontainer.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA.from_color(Gdk.Color.from_floats(color,color,color))) 53 | prayerbox.props.margin_top = 1 54 | prayerbox.props.margin_bottom = 1 55 | 56 | self.nprayers += 1 57 | self.prayers.append(prayerbox) 58 | prayercontainer.pack_start(prayerbox, True, False, 0) 59 | self.pack_start(prayercontainer, False, True, 0) 60 | 61 | def update_prayers_highlight(self, widget, prayername): 62 | print ("DEBUG: received update prayers signal") 63 | for prayer in self.prayers: 64 | if prayer.name == prayername: 65 | prayer.prayerlabel.set_markup(""+prayer.name+"") 66 | prayer.timelabel.set_markup(""+prayer.time+"") 67 | else: 68 | prayer.prayerlabel.set_markup(prayer.name) 69 | prayer.timelabel.set_markup(prayer.time) 70 | 71 | class Prayer(Gtk.Box): 72 | def __init__(self, prayer, prayertime, state): 73 | Gtk.Box.__init__(self) 74 | self.set_orientation(Gtk.Orientation.HORIZONTAL) 75 | self.set_halign(Gtk.Align.FILL) 76 | self.set_valign(Gtk.Align.CENTER) 77 | self._state = state 78 | self._name = prayer 79 | self._time = prayertime 80 | 81 | self.prayerlabel = Gtk.Label(halign=Gtk.Align.START, margin_bottom=6, margin_top=6, margin_left=12, margin_right=12) 82 | self.prayerlabel.set_use_markup(True) 83 | 84 | if state == True: 85 | self.prayerlabel.set_markup(""+prayer+"") 86 | else: 87 | self.prayerlabel.set_label(prayer) 88 | 89 | self.pack_start(self.prayerlabel, True, True, 0) 90 | 91 | self.timelabel = Gtk.Label(label=prayertime, halign=Gtk.Align.END, margin_bottom=6, margin_top=6, margin_right=12, margin_left=12) 92 | self.timelabel.set_use_markup(True) 93 | 94 | if state == True: 95 | self.timelabel.set_markup(""+prayertime+"") 96 | else: 97 | self.timelabel.set_label(prayertime) 98 | 99 | self.pack_end(self.timelabel, True, True, 0) 100 | 101 | @property 102 | def time(self): 103 | return self._time 104 | @time.setter 105 | def time(self, value): 106 | self._time = value 107 | 108 | @property 109 | def name(self): 110 | return self._name 111 | 112 | @name.setter 113 | def name(self, value): 114 | self._name = value 115 | 116 | GObject.type_register(Home) 117 | GObject.signal_new("prayers-updated", Home, GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, [GObject.TYPE_STRING]) 118 | -------------------------------------------------------------------------------- /icons/arrows/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /icons/arrows/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /icons/hicolor/128x128/apps/silaty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/icons/hicolor/128x128/apps/silaty.png -------------------------------------------------------------------------------- /icons/hicolor/scalable/silaty-indicator.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 62 | 68 | 74 | 80 | 86 | 87 | 93 | 99 | 105 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /icons/sidebar/aboutA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 70 | 76 | 80 | 85 | 86 | 90 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /icons/sidebar/aboutN.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 70 | 76 | 80 | 85 | 86 | 90 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /icons/sidebar/calendarA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 67 | 74 | 78 | 83 | 84 | 85 | 89 | 96 | 100 | 105 | 106 | 107 | 110 | 117 | 121 | 126 | 127 | 128 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /icons/sidebar/calendarN.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 67 | 74 | 78 | 83 | 84 | 85 | 89 | 96 | 100 | 105 | 106 | 107 | 110 | 117 | 121 | 126 | 127 | 128 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /icons/sidebar/homeA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 68 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /icons/sidebar/homeN.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 68 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /icons/sidebar/notifyA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 69 | 74 | 80 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /icons/sidebar/notifyN.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 68 | 73 | 74 | 77 | 82 | 87 | 93 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /icons/sidebar/qiblaA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 65 | 69 | 75 | 81 | 82 | 88 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /icons/sidebar/qiblaN.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 65 | 69 | 75 | 81 | 82 | 88 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /icons/sidebar/settingsA.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 69 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /icons/sidebar/settingsN.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 64 | 69 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$(id -u)" != "0" ]; then 4 | echo “This script must be run as root” 2>&1 5 | exit 1 6 | fi 7 | 8 | sh uninstall.sh 9 | 10 | mkdir /usr/share/silaty 11 | cp -R icons /usr/share/silaty/ 12 | cp -R audio /usr/share/silaty/ 13 | cp -R data /usr/share/silaty/ 14 | cp -R lang /usr/share/silaty/ 15 | 16 | cp icons/hicolor/128x128/apps/silaty.svg /usr/share/icons/hicolor/scalable/apps/ 17 | chmod 644 /usr/share/icons/hicolor/scalable/apps/silaty.svg 18 | 19 | cp icons/hicolor/128x128/apps/silaty.svg /usr/share/icons/hicolor/128x128/apps/ 20 | chmod 644 /usr/share/icons/hicolor/128x128/apps/silaty.svg 21 | 22 | cp icons/hicolor/48x48/apps/silaty.svg /usr/share/icons/hicolor/48x48/apps/ 23 | chmod 644 /usr/share/icons/hicolor/48x48/apps/silaty.svg 24 | 25 | cp icons/hicolor/24x24/apps/silaty.svg /usr/share/icons/hicolor/24x24/apps/ 26 | chmod 644 /usr/share/icons/hicolor/24x24/apps/silaty.svg 27 | 28 | cp *.py /usr/share/silaty/ 29 | chmod 755 -R /usr/share/silaty/ 30 | 31 | cp silaty.desktop /etc/xdg/autostart/ 32 | chmod 755 /etc/xdg/autostart/silaty.desktop 33 | 34 | cp silaty.desktop /usr/share/applications/ 35 | chmod 755 /usr/share/applications/silaty.desktop 36 | 37 | ln -s /usr/share/silaty/silaty-indicator.py /usr/local/bin/silaty-indicator 38 | chmod 755 /usr/local/bin/silaty-indicator 39 | 40 | gtk-update-icon-cache 41 | -------------------------------------------------------------------------------- /lang/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "Silaty": "صلاتي", 3 | "Fajr": "الفجر", 4 | "Shuruk": "الشروق", 5 | "Dhuhr": "الظهر", 6 | "Asr": "العصر", 7 | "Maghrib": "المغرب", 8 | "Isha": "العشاء", 9 | "System": "إعدادات التطبيق", 10 | "Start Minimized:": "إخفاء نافذة التطبيق عند بدء التشغيل:", 11 | "Daylight Saving Time:": "تفعيل التوقيت الصيفي:", 12 | "Clock Format:": "تنسيق الوقت:", 13 | "12h": "12 ساعة", 14 | "24h": "24 ساعة", 15 | "Adjust Hijri Calendar:": "ضبط التقويم الهجري:", 16 | "Language:": "اللغة:", 17 | "English": "الإنجليزية", 18 | "French": "الفرنسية", 19 | "Arabic": "العربية", 20 | "Spanish": "الإسبانية", 21 | "Notifications": "الإشعارات", 22 | "Show Time left with Icon:": "عرض الوقت المتبقي بجانب الأيقونة:", 23 | "Enable audio notifications:": "تفعيل الإشعارات الصوتية:", 24 | "Time before notification:": "الوقت قبل الإشعار:", 25 | "Fajr Adhan:": "أذان الفجر:", 26 | "Normal Adhan:": "أذان عادي:", 27 | "Jurisprudence": "الفقه", 28 | "Calculation Method:": "طريقة الحساب:", 29 | "Makkah": "مكة المكرمة", 30 | "Egypt": "مصر", 31 | "Karachi": "كراتشي", 32 | "ISNA": "الجمعية الإسلامية لأمريكا الشمالية", 33 | "MWL": "رابطة العالم الإسلامي", 34 | "Madhab:": "المذهب:", 35 | "Hanafi": "حنفي", 36 | "Default": "افتراضي", 37 | "Location": "الموقع", 38 | "City:": "المدينة:", 39 | "Latitude:": "خط العرض:", 40 | "Longitude:": "خط الطول:", 41 | "Time Zone:": "النطاق الزمني:", 42 | "Location: %s": "الموقع: %s", 43 | "Qibla is %.2f° from True North": "القبلة على بعد %.2f درجة من الشمال الحقيقي", 44 | "Fajr\t\t\t\t\t%s": "الفجر\t\t\t\t\t%s", 45 | "Shuruk\t\t\t%s": "الشروق\t\t\t\t%s", 46 | "Dhuhr\t\t\t\t%s": "الظهر\t\t\t\t\t%s", 47 | "Asr\t\t\t\t\t%s": "العصر\t\t\t\t\t%s", 48 | "Maghrib\t\t\t\t%s": "المغرب\t\t\t\t\t%s", 49 | "Isha\t\t\t\t\t%s": "العشاء\t\t\t\t\t%s", 50 | "Next Prayer": "الصلاة التالية", 51 | "About": "حول التطبيق", 52 | "Settings": "الإعدادات", 53 | "Quit": "خروج", 54 | "%s until %s": "%s حتى صلاة %s", 55 | "%s in %s": "%s بعد %s", 56 | "%s Hours": "%s ساعة", 57 | "%s Minutes": "%s دقيقة", 58 | "%s Hours and %s Minutes": "%s ساعة و %s دقيقة", 59 | "%shr": "%s ساعة", 60 | "%smin": "%s دقيقة", 61 | "%shr %smin": "%s ساعة %s دقيقة", 62 | "GitHub Project Page": "صفحة المشروع على GitHub", 63 | "A neat Prayer Time Reminder App.\n Simple and complete so no prayer is missed": "تطبيق أوقات الصلاة.\n بسيط و شامل حتى لا تفوتك أي صلاة", 64 | "Copyright © %s Silaty Team": "حقوق النشر © %s فريق صلاتي", 65 | "Silaty needs to be restarted, restart now?": "يحتاج التطبيق إلى إعادة تشغيل، إعادة التشغيل الآن؟", 66 | "Hijri:": "هجري:", 67 | "Sunday": "الأحد", 68 | "Monday": "الإثنين", 69 | "Tuesday": "الثلاثاء", 70 | "Wednesday": "الأربعاء", 71 | "Thursday": "الخميس", 72 | "Friday": "الجمعة", 73 | "Saturday": "السبت", 74 | "SundayShort": "ح", 75 | "MondayShort": "ن", 76 | "TuesdayShort": "ث", 77 | "WednesdayShort": "ر", 78 | "ThursdayShort": "خ", 79 | "FridayShort": "ج", 80 | "SaturdayShort": "س", 81 | "January": "يناير", 82 | "February": "فبراير", 83 | "March": "مارس", 84 | "April": "أبريل", 85 | "May": "مايو", 86 | "June": "يونيو", 87 | "July": "يوليو", 88 | "August": "أغسطس", 89 | "September": "سبتمبر", 90 | "October": "أكتوبر", 91 | "November": "نوفمبر", 92 | "December": "ديسمبر", 93 | "Muharram": "محرم", 94 | "Safar": "صفر", 95 | "Rabi al Awwal": "ربيع الأول", 96 | "Rabi al Akhira": "ربيع الآخر", 97 | "Jumada al Ula": "جمادى الأولى", 98 | "Jumada al Akhira": "جمادى الآخرة", 99 | "Rajab": "رجب", 100 | "Sha'ban": "شعبان", 101 | "Ramadhan": "رمضان", 102 | "Shawwal": "شوال", 103 | "Dhu al Qa'da": "ذو القعدة", 104 | "Dhu al Hijja": "ذو الحجة", 105 | "Qibla direction :": "اتجاه القبلة :", 106 | "Country : %s": "الدولة : %s", 107 | "City : %s": "المدينة : %s", 108 | "Search:": "بحث:", 109 | "Check your location on %s": "يمكنك التحقق من موقعك على %s", 110 | "Get Ready": "إستعد", 111 | "%s minutes left until the %s prayer.": "%s دقائق متبقية حتى صلاة %s.", 112 | "Prayer time for %s": "وقت صلاة %s", 113 | "It's time for the %s prayer.": "حان الوقت لصلاة %s.", 114 | "%s, %s": "%s، %s", 115 | "%s - %s, %s / %s": "%s - %s، %s / %s", 116 | "AM": "ص", 117 | "PM": "م", 118 | "Apply": "ضبط", 119 | "Cancel": "إلغاء" 120 | } 121 | -------------------------------------------------------------------------------- /lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Silaty": "Silaty", 3 | "Fajr": "Fajr", 4 | "Shuruk": "Shuruk", 5 | "Dhuhr": "Dhuhr", 6 | "Asr": "Asr", 7 | "Maghrib": "Maghrib", 8 | "Isha": "Isha", 9 | "System": "System", 10 | "Start Minimized:": "Start Minimized:", 11 | "Daylight Saving Time:": "Daylight Saving Time:", 12 | "Clock Format:": "Clock Format:", 13 | "12h": "12h", 14 | "24h": "24h", 15 | "Adjust Hijri Calendar:": "Adjust Hijri Calendar:", 16 | "Language:": "Language:", 17 | "English": "English", 18 | "French": "French", 19 | "Arabic": "Arabic", 20 | "Spanish": "Spanish", 21 | "Notifications": "Notifications", 22 | "Show Time left with Icon:": "Show Time left with Icon:", 23 | "Enable audio notifications:": "Enable audio notifications:", 24 | "Time before notification:": "Time before notification:", 25 | "Fajr Adhan:": "Fajr Adhan:", 26 | "Normal Adhan:": "Normal Adhan:", 27 | "Jurisprudence": "Jurisprudence", 28 | "Calculation Method:": "Calculation Method:", 29 | "Makkah": "Makkah", 30 | "Egypt": "Egypt", 31 | "Karachi": "Karachi", 32 | "ISNA": "ISNA", 33 | "MWL": "MWL", 34 | "Madhab:": "Madhab:", 35 | "Hanafi": "Hanafi", 36 | "Default": "Default", 37 | "Location": "Location", 38 | "City:": "City:", 39 | "Latitude:": "Latitude:", 40 | "Longitude:": "Longitude:", 41 | "Time Zone:": "Time Zone:", 42 | "Location: %s": "Location: %s", 43 | "Qibla is %.2f° from True North": "Qibla is %.2f° from True North", 44 | "Fajr\t\t\t\t\t%s": "Fajr\t\t\t\t\t%s", 45 | "Shuruk\t\t\t%s": "Shuruk\t\t\t%s", 46 | "Dhuhr\t\t\t\t%s": "Dhuhr\t\t\t\t%s", 47 | "Asr\t\t\t\t\t%s": "Asr\t\t\t\t\t%s", 48 | "Maghrib\t\t\t\t%s": "Maghrib\t\t\t\t%s", 49 | "Isha\t\t\t\t\t%s": "Isha\t\t\t\t\t%s", 50 | "Next Prayer": "Next Prayer:", 51 | "About": "About", 52 | "Settings": "Settings", 53 | "Quit": "Quit", 54 | "%s until %s": "%s until %s", 55 | "%s in %s": "%s in %s", 56 | "%s Hours": "%s Hours", 57 | "%s Minutes": "%s Minutes", 58 | "%s Hours and %s Minutes": "%s Hours and %s Minutes", 59 | "%shr": "%shr", 60 | "%smin": "%smin", 61 | "%shr %smin": "%shr %smin", 62 | "GitHub Project Page": "GitHub Project Page", 63 | "A neat Prayer Time Reminder App.\n Simple and complete so no prayer is missed": "A neat Prayer Time Reminder App.\n Simple and complete so no prayer is missed", 64 | "Copyright © %s Silaty Team": "Copyright © %s Silaty Team", 65 | "Silaty needs to be restarted, restart now?": "Silaty needs to be restarted, restart now?", 66 | "Hijri:": "Hijri:", 67 | "Sunday": "Sunday", 68 | "Monday": "Monday", 69 | "Tuesday": "Tuesday", 70 | "Wednesday": "Wednesday", 71 | "Thursday": "Thursday", 72 | "Friday": "Friday", 73 | "Saturday": "Saturday", 74 | "SundayShort": "S", 75 | "MondayShort": "M", 76 | "TuesdayShort": "T", 77 | "WednesdayShort": "W", 78 | "ThursdayShort": "T", 79 | "FridayShort": "F", 80 | "SaturdayShort": "S", 81 | "January": "January", 82 | "February": "February", 83 | "March": "March", 84 | "April": "April", 85 | "May": "May", 86 | "June": "June", 87 | "July": "July", 88 | "August": "August", 89 | "September": "September", 90 | "October": "October", 91 | "November": "November", 92 | "December": "December", 93 | "Muharram": "Muharram", 94 | "Safar": "Safar", 95 | "Rabi al Awwal": "Rabi al Awwal", 96 | "Rabi al Akhira": "Rabi al Akhira", 97 | "Jumada al Ula": "Jumada al Ula", 98 | "Jumada al Akhira": "Jumada al Akhira", 99 | "Rajab": "Rajab", 100 | "Sha'ban": "Sha'ban", 101 | "Ramadhan": "Ramadhan", 102 | "Shawwal": "Shawwal", 103 | "Dhu al Qa'da": "Dhu al Qa'da", 104 | "Dhu al Hijja": "Dhu al Hijja", 105 | "Qibla direction :": "Qibla direction :", 106 | "Country : %s": "Country : %s", 107 | "City : %s": "City : %s", 108 | "Search:": "Search:", 109 | "Check your location on %s": "Check your location on %s", 110 | "Get Ready": "Get Ready", 111 | "%s minutes left until the %s prayer.": "%s minutes left until the %s prayer.", 112 | "Prayer time for %s": "Prayer time for %s", 113 | "It's time for the %s prayer.": "It's time for the %s prayer.", 114 | "%s, %s": "%s، %s", 115 | "%s - %s, %s / %s": "%s - %s، %s / %s", 116 | "AM": "AM", 117 | "PM": "PM", 118 | "Apply": "Apply", 119 | "Cancel": "Cancel" 120 | } 121 | -------------------------------------------------------------------------------- /lang/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Silaty": "Silaty", 3 | "Fajr": "Fajr", 4 | "Shuruk": "Amanecer", 5 | "Dhuhr": "Dhuhr", 6 | "Asr": "Asr", 7 | "Maghrib": "Maghrib", 8 | "Isha": "Isha", 9 | "System": "Systema", 10 | "Start Minimized:": "Inicial Minimizado:", 11 | "Daylight Saving Time:": "Horario de verano:", 12 | "Clock Format:": "Formato de reloj:", 13 | "12h": "12 horas", 14 | "24h": "24 horas", 15 | "Adjust Hijri Calendar:": "Ajustar el Calendario Hijri:", 16 | "Language:": "Idioma:", 17 | "English": "Ingles", 18 | "French": "Frances", 19 | "Arabic": "Arabe", 20 | "Spanish": "Español", 21 | "Notifications": "Notificaciones", 22 | "Show Time left with Icon:": "Monstrar tiempo restante con icono:", 23 | "Enable audio notifications:": "Activar las notificaciones audio:", 24 | "Time before notification:": "Tiempo antes de la notificacion:", 25 | "Fajr Adhan:": "Adhan de Fajr:", 26 | "Normal Adhan:": "Adhan normal:", 27 | "Jurisprudence": "Jurisprudencia", 28 | "Calculation Method:": "Método de cálculo:", 29 | "Makkah": "Meca", 30 | "Egypt": "Egipto", 31 | "Karachi": "Karachi", 32 | "ISNA": "ISNA (Norteamérica)", 33 | "MWL": "Liga Mundial Musulmana", 34 | "Madhab:": "Madhab:", 35 | "Hanafi": "Hanafi", 36 | "Default": "Defecto", 37 | "Location": "Locacion", 38 | "City:": "Ciudad:", 39 | "Latitude:": "Latitud:", 40 | "Longitude:": "Longitud:", 41 | "Time Zone:": "Zona horaria:", 42 | "Location: %s": "Locacion: %s", 43 | "Qibla is %.2f° from True North": "La Qibla esta a %.2f del verdadero Norte", 44 | "Fajr\t\t\t\t\t%s": "Fajr\t\t\t\t\t%s", 45 | "Shuruk\t\t\t%s": "Amanecer\t\t\t\t%s", 46 | "Dhuhr\t\t\t\t%s": "Dhurh\t\t\t\t%s", 47 | "Asr\t\t\t\t\t%s": "Asr\t\t\t\t\t%s", 48 | "Maghrib\t\t\t\t%s": "Maghrib\t\t\t\t%s", 49 | "Isha\t\t\t\t\t%s": "Isha\t\t\t\t\t%s", 50 | "Next Prayer": "Proximo rezo:", 51 | "About": "À propos", 52 | "Settings": "Acerca de", 53 | "Quit": "Quitar", 54 | "%s until %s": "%s antes de %s", 55 | "%s in %s": "%s en %s", 56 | "%s Hours": "%s Horas", 57 | "%s Minutes": "%s Minutos", 58 | "%s Hours and %s Minutes": "%s Heros et %s Minutos", 59 | "%shr": "%shoras", 60 | "%smin": "%smin", 61 | "%shr %smin": "%shoras %smin", 62 | "GitHub Project Page": "Pagina GitHub del Proyecto", 63 | "A neat Prayer Time Reminder App.\n Simple and complete so no prayer is missed": "Una excelente aplicación para los tiempos de oracion.\n Simple y completa para que no se olvide ninguna oración", 64 | "Copyright © %s Silaty Team": "Copyright © %s Silaty Team", 65 | "Silaty needs to be restarted, restart now?": "Silaty necesita ser reiniciado, ¿reiniciar ahora?", 66 | "Hijri:": "Hijri:", 67 | "Sunday": "Domindo", 68 | "Monday": "Lunes", 69 | "Tuesday": "Martes", 70 | "Wednesday": "Miercoles", 71 | "Thursday": "Jueves", 72 | "Friday": "Viernes", 73 | "Saturday": "Sabado", 74 | "SundayShort": "D", 75 | "MondayShort": "L", 76 | "TuesdayShort": "Ma", 77 | "WednesdayShort": "Mi", 78 | "ThursdayShort": "J", 79 | "FridayShort": "V", 80 | "SaturdayShort": "S", 81 | "January": "Enero", 82 | "February": "Febrero", 83 | "March": "Marzo", 84 | "April": "April", 85 | "May": "mayo", 86 | "June": "Junio", 87 | "July": "Julio", 88 | "August": "Agosto", 89 | "September": "Septiembre", 90 | "October": "Octubre", 91 | "November": "Noviembre", 92 | "December": "Diciembre", 93 | "Muharram": "Muharram", 94 | "Safar": "Safar", 95 | "Rabi al Awwal": "Rabi al Awwal", 96 | "Rabi al Akhira": "Rabi al Akhira", 97 | "Jumada al Ula": "Jumada al Ula", 98 | "Jumada al Akhira": "Jumada al Akhira", 99 | "Rajab": "Rajab", 100 | "Sha'ban": "Sha'ban", 101 | "Ramadhan": "Ramadhan", 102 | "Shawwal": "Shawwal", 103 | "Dhu al Qa'da": "Dhu al Qa'da", 104 | "Dhu al Hijja": "Dhu al Hijja", 105 | "Qibla direction :": "Direccion de la Qibla :", 106 | "Country : %s": "País : %s", 107 | "City : %s": "Ciudad : %s", 108 | "Search:": "Burcar:", 109 | "Check your location on %s": "Consulta tu ubicación sobre %s", 110 | "Get Ready": "Prepararse", 111 | "%s minutes left until the %s prayer.": "%s minutos antes de la oracion de %s.", 112 | "Prayer time for %s": "Hora de la oracian para %s", 113 | "It's time for the %s prayer.": "Es la hora de la oracion %s.", 114 | "%s, %s": "%s، %s", 115 | "%s - %s, %s / %s": "%s - %s، %s / %s", 116 | "AM": "AM", 117 | "PM": "PM", 118 | "Apply": "Aplicar", 119 | "Cancel": "Cancelar" 120 | } 121 | -------------------------------------------------------------------------------- /lang/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Silaty": "Silaty", 3 | "Fajr": "Fajr", 4 | "Shuruk": "Lever du Soleil", 5 | "Dhuhr": "Dhuhr", 6 | "Asr": "Asr", 7 | "Maghrib": "Maghrib", 8 | "Isha": "Isha", 9 | "System": "Système", 10 | "Start Minimized:": "Lancer l'application minimisée", 11 | "Daylight Saving Time:": "Heure d'été:", 12 | "Clock Format:": "Format de l'heure", 13 | "12h": "12 heures", 14 | "24h": "24 heures", 15 | "Adjust Hijri Calendar:": "Ajuster le calendrier Hijri", 16 | "Language:": "Langue:", 17 | "English": "Anglais", 18 | "French": "Français", 19 | "Arabic": "Arabe", 20 | "Spanish": "Espagnol", 21 | "Notifications": "Notifications", 22 | "Show Time left with Icon:": "Montrer temps restant avec icone:", 23 | "Enable audio notifications:": "Activer les notifications audio:", 24 | "Time before notification:": "Temps avant la notification:", 25 | "Fajr Adhan:": "Adhan de Fajr:", 26 | "Normal Adhan:": "Adhan normal:", 27 | "Jurisprudence": "Jurisprudence", 28 | "Calculation Method:": "Methode de Calcul:", 29 | "Makkah": "Makkah", 30 | "Egypt": "Egypte", 31 | "Karachi": "Karachi", 32 | "ISNA": "ISNA (Amérique du Nord)", 33 | "MWL": "Ligue Musulmane Mondiale", 34 | "Madhab:": "Madhab:", 35 | "Hanafi": "Hanafi", 36 | "Default": "Défaut", 37 | "Location": "Location", 38 | "City:": "Ville:", 39 | "Latitude:": "Latitude:", 40 | "Longitude:": "Longitude:", 41 | "Time Zone:": "Fuseau Horaire:", 42 | "Location: %s": "Location: %s", 43 | "Qibla is %.2f° from True North": "La Qibla est à %.2f du vrai Nord", 44 | "Fajr\t\t\t\t\t%s": "Fajr\t\t\t\t\t%s", 45 | "Shuruk\t\t\t%s": "Lever du Soleil\t\t\t\t%s", 46 | "Dhuhr\t\t\t\t%s": "Dhurh\t\t\t\t%s", 47 | "Asr\t\t\t\t\t%s": "Asr\t\t\t\t\t%s", 48 | "Maghrib\t\t\t\t%s": "Maghrib\t\t\t\t%s", 49 | "Isha\t\t\t\t\t%s": "Isha\t\t\t\t\t%s", 50 | "Next Prayer": "Prochaine Prière:", 51 | "About": "À propos", 52 | "Settings": "Options", 53 | "Quit": "Quitter", 54 | "%s until %s": "%s avant %s", 55 | "%s in %s": "%s dans %s", 56 | "%s Hours": "%s Heures", 57 | "%s Minutes": "%s Minutes", 58 | "%s Hours and %s Minutes": "%s Heures et %s Minutes", 59 | "%shr": "%sheures", 60 | "%smin": "%smin", 61 | "%shr %smin": "%sheures %smin", 62 | "GitHub Project Page": "Page GitHub du Projet", 63 | "A neat Prayer Time Reminder App.\n Simple and complete so no prayer is missed": "Une application polie de rappel pour le temps de Prièr.\n Simple et complète pour qu'aucune prière ne soit ratée", 64 | "Copyright © %s Silaty Team": "Copyright © %s Silaty Team", 65 | "Silaty needs to be restarted, restart now?": "Silaty a besoin d'être redémarrée, redémarrer maintenent ?", 66 | "Hijri:": "Hijri:", 67 | "Sunday": "Dimanche", 68 | "Monday": "Lundi", 69 | "Tuesday": "Mardi", 70 | "Wednesday": "Mercredi", 71 | "Thursday": "Jeudi", 72 | "Friday": "Vendredi", 73 | "Saturday": "Saturday", 74 | "SundayShort": "D", 75 | "MondayShort": "L", 76 | "TuesdayShort": "Ma", 77 | "WednesdayShort": "Me", 78 | "ThursdayShort": "J", 79 | "FridayShort": "V", 80 | "SaturdayShort": "S", 81 | "January": "Janvier", 82 | "February": "Février", 83 | "March": "Mars", 84 | "April": "Avril", 85 | "May": "Mai", 86 | "June": "Juin", 87 | "July": "Juillet", 88 | "August": "Aôut", 89 | "September": "Septembre", 90 | "October": "Octobre", 91 | "November": "Novembre", 92 | "December": "Decembre", 93 | "Muharram": "Muharram", 94 | "Safar": "Safar", 95 | "Rabi al Awwal": "Rabi al Awwal", 96 | "Rabi al Akhira": "Rabi al Akhira", 97 | "Jumada al Ula": "Jumada al Ula", 98 | "Jumada al Akhira": "Jumada al Akhira", 99 | "Rajab": "Rajab", 100 | "Sha'ban": "Sha'ban", 101 | "Ramadhan": "Ramadhan", 102 | "Shawwal": "Shawwal", 103 | "Dhu al Qa'da": "Dhu al Qa'da", 104 | "Dhu al Hijja": "Dhu al Hijja", 105 | "Qibla direction :": "Direction de la Qibla :", 106 | "Country : %s": "Pays : %s", 107 | "City : %s": "Ville : %s", 108 | "Search:": "Chercher:", 109 | "Check your location on %s": "Vérifiez votre location sur %s", 110 | "Get Ready": "Préparez-vous", 111 | "%s minutes left until the %s prayer.": "%s minutes avant la prière de %s.", 112 | "Prayer time for %s": "Temps de prière pour %s", 113 | "It's time for the %s prayer.": "C'est le temps pour la prière de %s.", 114 | "%s, %s": "%s، %s", 115 | "%s - %s, %s / %s": "%s - %s، %s / %s", 116 | "AM": "AM", 117 | "PM": "PM", 118 | "Apply": "Appliquer", 119 | "Cancel": "Annuler" 120 | } 121 | -------------------------------------------------------------------------------- /location.py: -------------------------------------------------------------------------------- 1 | # Silaty 2 | # Copyright (c) 2018 - 2021 AXeL 3 | # Copyright (c) 2014 - 2015 Jessewb786 4 | 5 | import gi 6 | gi.require_version('Gtk', '3.0') 7 | from gi.repository import Gtk 8 | from translate import translate_text as _ 9 | import os 10 | try: 11 | import xml.etree.cElementTree as ET # C implementation is much faster 12 | except ImportError: 13 | import xml.etree.ElementTree as ET 14 | 15 | class TimeZone(): 16 | 17 | def __init__(self, zone_name, value): 18 | self.zone_name = zone_name 19 | self.value = value 20 | 21 | class Country(): 22 | 23 | def __init__(self, name, timezone): 24 | self.name = name 25 | self.timezone = timezone 26 | 27 | class Location(): 28 | 29 | def __init__(self, num, name, coordinates, country, is_city): 30 | self.num = num 31 | self.name = name 32 | self.coordinates = coordinates 33 | self.country = country 34 | self.is_city = is_city 35 | 36 | def get_latitude(self): 37 | if self.coordinates is None: 38 | return None 39 | else: 40 | coordinates = self.coordinates.split(' ') 41 | return float(coordinates[0]) 42 | 43 | def get_longitude(self): 44 | if self.coordinates is None: 45 | return None 46 | else: 47 | coordinates = self.coordinates.split(' ') 48 | if len(coordinates) <= 1: 49 | return None 50 | else: 51 | return float(coordinates[1]) 52 | 53 | class LocationDialog(Gtk.Dialog): 54 | 55 | def __init__(self, parent, title='Silaty'): 56 | Gtk.Dialog.__init__(self, modal=True, transient_for=parent, title=_(title), flags=Gtk.DialogFlags.DESTROY_WITH_PARENT) 57 | self.parent = parent 58 | self.set_border_width(10) 59 | self.set_resizable(False) 60 | self.connect('delete-event', self.hide_dialog) 61 | self.connect('response', self.hide_dialog) 62 | self.locations = [] 63 | self.locations_count = 0 64 | # Location 65 | content_area = self.get_content_area() 66 | content_area.set_spacing(5) 67 | scrolledwindow = Gtk.ScrolledWindow() 68 | scrolledwindow.set_size_request(150, 250) 69 | content_area.add(scrolledwindow) 70 | self.treestore = Gtk.TreeStore(str, int) 71 | self.parse_locations() 72 | treemodelsort = Gtk.TreeModelSort(self.treestore) 73 | treemodelsort.set_sort_column_id(0, Gtk.SortType.ASCENDING) 74 | self.treeview = Gtk.TreeView() 75 | self.treeview.set_model(treemodelsort) 76 | scrolledwindow.add(self.treeview) 77 | cellrenderertext = Gtk.CellRendererText() 78 | treeviewcolumn = Gtk.TreeViewColumn(_('Location'), cellrenderertext, text=0) 79 | self.treeview.append_column(treeviewcolumn) 80 | #self.treeview.set_headers_visible(False) 81 | treeselection = self.treeview.get_selection() 82 | treeselection.set_mode(Gtk.SelectionMode.SINGLE) 83 | treeselection.connect('changed', self.on_selection_changed) 84 | # Search 85 | searchbox = Gtk.Box(halign=Gtk.Align.FILL, spacing=10) 86 | searchlabel = Gtk.Label(_('Search:'), halign=Gtk.Align.START) 87 | searchbox.pack_start(searchlabel, False, False, 0) 88 | searchentry = Gtk.Entry(halign=Gtk.Align.FILL) 89 | searchentry.connect('changed', self.on_search_entry_changed) 90 | self.treeview.set_search_entry(searchentry) 91 | self.treeview.set_search_equal_func(self.search_function) 92 | self.treeview.set_search_column(0) 93 | searchbox.pack_start(searchentry, True, True, 0) 94 | content_area.add(searchbox) 95 | # Help 96 | helpbox = Gtk.Box(halign=Gtk.Align.FILL, spacing=3, margin=10) 97 | helpimage = Gtk.Image(icon_name='system-help') 98 | helpbox.pack_start(helpimage, False, False, 0) 99 | helplabel = Gtk.Label(_('Check your location on %s') % 'www.islamicfinder.org', use_markup=True) 100 | helpbox.pack_start(helplabel, True, True, 0) 101 | content_area.add(helpbox) 102 | # Buttons 103 | self.action_area.set_layout(Gtk.ButtonBoxStyle.END) 104 | self.apply_button = Gtk.Button(_('Apply')) 105 | self.apply_button.set_sensitive(False) 106 | self.apply_button.connect('clicked', self.on_apply_button_clicked) 107 | cancel_button = Gtk.Button(_('Cancel')) 108 | cancel_button.connect('clicked', self.on_cancel_button_clicked) 109 | self.add_action_widget(self.apply_button, Gtk.ResponseType.OK) 110 | self.add_action_widget(cancel_button, Gtk.ResponseType.CANCEL) 111 | self.show_all() 112 | 113 | def hide_dialog(self, widget, response): 114 | self.hide() 115 | return True 116 | 117 | def on_apply_button_clicked(self, widget): 118 | location_num = self.get_selected_location() 119 | location = self.get_location(location_num) 120 | if location is not None: 121 | self.parent.lock_location_updates = True 122 | self.parent.prayertimes.options.country = location.country.name 123 | self.parent.prayertimes.options.city = location.name 124 | self.parent.cityentry.set_text(location.name) 125 | latitude = location.get_latitude() 126 | longitude = location.get_longitude() 127 | timezone = location.country.timezone 128 | if latitude is not None: 129 | self.parent.latentry.set_value(latitude) 130 | if longitude is not None: 131 | self.parent.longentry.set_value(longitude) 132 | if timezone is not None: 133 | self.parent.tzentry.set_value(timezone.value) 134 | self.parent.update_location() 135 | self.parent.lock_location_updates = False 136 | 137 | def on_cancel_button_clicked(self, widget): 138 | pass 139 | 140 | def on_search_entry_changed(self, widget): 141 | if not widget.get_text(): 142 | self.apply_button.set_sensitive(False) 143 | 144 | def get_location(self, location_num): 145 | if location_num is not None: 146 | for location in self.locations: 147 | if location.num == location_num: 148 | return location 149 | 150 | return None 151 | 152 | def is_location(self, location_num): 153 | if self.get_location(location_num) is None: 154 | return False 155 | else: 156 | return True 157 | 158 | def get_selected_location(self, selection = None): 159 | if selection is None: 160 | selection = self.treeview.get_selection() 161 | model, tree_iter = selection.get_selected() 162 | if tree_iter and not model.iter_has_child(tree_iter): # a location should not have children 163 | value = model.get_value(tree_iter, 1) # 0 => location name, 1 => location num 164 | return value 165 | else: 166 | return None 167 | 168 | def on_selection_changed(self, selection): 169 | location_num = self.get_selected_location(selection) 170 | if self.is_location(location_num): 171 | self.apply_button.set_sensitive(True) 172 | else: 173 | self.apply_button.set_sensitive(False) 174 | 175 | def toggle_rows(self, row, key, column): 176 | for child in row.iterchildren(): 177 | if key.lower() in list(child)[column].lower(): 178 | self.treeview.expand_to_path(row.path) 179 | return True 180 | else: 181 | if self.toggle_rows(child, key, column): 182 | return True 183 | self.treeview.collapse_row(row.path) 184 | 185 | return False 186 | 187 | # @see https://stackoverflow.com/questions/23355866/user-search-collapsed-rows-in-a-gtk-treeview 188 | def search_function(self, model, column, key, rowiter): 189 | row = model[rowiter] 190 | if key.lower() in list(row)[column-2].lower(): 191 | return False # Search matches 192 | 193 | # Search in child rows. If one of the rows matches, expand the row so that it will be open in later checks. 194 | self.toggle_rows(row, key, column-2) 195 | 196 | return True # Search does not match 197 | 198 | def add_location(self, location, country, is_city = False): 199 | name = location[0].text 200 | location_coordinates = location.find('coordinates') 201 | if location_coordinates is not None: 202 | coordinates = location_coordinates.text 203 | else: 204 | print ('WARNING: %s > %s has no coordinates.' % (country.name, name)) 205 | coordinates = None 206 | self.locations_count += 1 207 | new_location = Location(self.locations_count, name, coordinates, country, is_city) 208 | # add new location 209 | self.locations.append(new_location) 210 | 211 | def parse_locations(self): 212 | # Parse XML file 213 | tree = ET.parse(os.path.dirname(os.path.realpath(__file__)) + '/data/Locations.xml') 214 | root = tree.getroot() 215 | for child in root: 216 | if child.tag == 'region': 217 | region_name = child.find('name').text 218 | #print ('%s' % region_name) 219 | region_node = self.treestore.append(None, [region_name, 0]) 220 | for region_child in child: 221 | if region_child.tag == 'country': 222 | country_name = region_child.find('name').text 223 | #print ('├── %s' % country_name) 224 | timezones = region_child.find('timezones') 225 | if timezones is not None: 226 | zone = timezones[0].attrib['id'] 227 | value = float(timezones[0].attrib['value']) 228 | timezone = TimeZone(zone, value) 229 | else: 230 | print ('WARNING: %s has no timezone.' % (country_name)) 231 | timezone = None 232 | country = Country(country_name, timezone) 233 | country_node = self.treestore.append(region_node, [country_name, 0]) 234 | for country_child in region_child: 235 | if country_child.tag == 'location': 236 | location_name = country_child.find('name').text 237 | #print ('│ ├── %s' % location_name) 238 | self.add_location(country_child, country) 239 | self.treestore.append(country_node, [location_name, self.locations_count]) 240 | elif country_child.tag == 'city': 241 | city_name = country_child.find('name').text 242 | #print ('│ ├── %s' % city_name) 243 | self.add_location(country_child, country, True) 244 | self.treestore.append(country_node, [city_name, self.locations_count]) 245 | elif country_child.tag == 'state': 246 | state_name = country_child.find('name').text 247 | #print ('│ ├── %s' % state_name) 248 | state_node = self.treestore.append(country_node, [state_name, 0]) 249 | for state_child in country_child: 250 | if state_child.tag == 'location': 251 | location_name = state_child.find('name').text 252 | #print ('│ │ ├── %s' % location_name) 253 | self.add_location(state_child, country) 254 | self.treestore.append(state_node, [location_name, self.locations_count]) 255 | elif state_child.tag == 'city': 256 | city_name = state_child.find('name').text 257 | #print ('│ │ ├── %s' % city_name) 258 | self.add_location(state_child, country, True) 259 | self.treestore.append(state_node, [city_name, self.locations_count]) 260 | -------------------------------------------------------------------------------- /options.py: -------------------------------------------------------------------------------- 1 | # Silaty 2 | # Copyright (c) 2018 - 2021 AXeL 3 | # Copyright (c) 2014 - 2015 Jessewb786 4 | 5 | import configparser 6 | import os 7 | import datetime 8 | from translate import languages 9 | 10 | class Calendar(object): 11 | UmmAlQuraUniv, \ 12 | EgyptianGeneralAuthorityOfSurvey,\ 13 | UnivOfIslamicSciencesKarachi,\ 14 | IslamicSocietyOfNorthAmerica,\ 15 | MuslimWorldLeague = range(5) 16 | 17 | class Madhab(object): 18 | Default, Hanafi = 0, 1 19 | 20 | class Options: 21 | def __init__(self): 22 | print ("DEBUG: Initializing the Options module @", (str(datetime.datetime.now()))) 23 | 24 | cparse = configparser.ConfigParser() 25 | cparse.read([os.path.expanduser('~/.silaty')]) 26 | 27 | try: 28 | self._city = cparse.get('DEFAULT', 'city') 29 | self._country = cparse.get('DEFAULT', 'country') 30 | self._calcmethodname = cparse.get('DEFAULT', 'calculation-method') 31 | self._madhab = cparse.get('DEFAULT', 'madhab') 32 | self._clockformat = cparse.get('DEFAULT', 'clock-format') 33 | self._latitude = cparse.get('DEFAULT', 'latitude') 34 | self._longitude = cparse.get('DEFAULT', 'longitude') 35 | self._timezone = cparse.get('DEFAULT', 'timezone') 36 | self._notif = cparse.get('DEFAULT', 'notif') 37 | self._iconlabel = cparse.get('DEFAULT', 'iconlabel') 38 | self._startminimized = cparse.get('DEFAULT', 'minimized') 39 | self._fajradhan = cparse.get('DEFAULT', 'fajr-adhan') 40 | self._normaladhan = cparse.get('DEFAULT', 'normal-adhan') 41 | self._audionotifications = cparse.get('DEFAULT', 'audio-notifications') 42 | self._daylightsavingtime = cparse.get('DEFAULT', 'daylight-saving-time') 43 | self._hijricaladjustment = cparse.get('DEFAULT', 'hijrical-adjustment') 44 | self._language = cparse.get('DEFAULT', 'language') 45 | 46 | except configparser.NoOptionError: 47 | print ("DEBUG: No configration file using default settings") 48 | self._city = 'Makkah' 49 | self._country = 'Saudi Arabia' 50 | self._latitude = '21.25' 51 | self._longitude = '39.49' 52 | self._timezone = '3' 53 | self._calcmethodname = 'Makkah' 54 | self._madhab = 'Default' 55 | self._clockformat = '24h' 56 | self._notif = '10' 57 | self._iconlabel = '1' 58 | self._startminimized = '1' 59 | self._fajradhan = (self.get_fajr_adhans())[0] 60 | self._normaladhan = (self.get_normal_adhans())[0] 61 | self._audionotifications = '1' 62 | self._daylightsavingtime = '1' 63 | self._hijricaladjustment = '0' 64 | self._language = 'English' 65 | self.save_options() 66 | 67 | except ValueError: 68 | print ("DEBUG: Problem while reading setting file, using the default settings") 69 | os.system("rm ~/.silaty") 70 | self._city = 'Makkah' 71 | self._country = 'Saudi Arabia' 72 | self._latitude = '21.25' 73 | self._longitude = '39.49' 74 | self._timezone = '3' 75 | self._calcmethodname = 'Makkah' 76 | self._madhab = 'Default' 77 | self._clockformat = '24h' 78 | self._notif = '10' 79 | self._iconlabel = '1' 80 | self._startminimized = '1' 81 | self._fajradhan = (self.get_fajr_adhans())[0] 82 | self._normaladhan = (self.get_normal_adhans())[0] 83 | self._audionotifications = '1' 84 | self._daylightsavingtime = '1' 85 | self._hijricaladjustment = '0' 86 | self._language = 'English' 87 | self.save_options() 88 | 89 | ## Functions with lists for the Buttons 90 | def get_cal_methods(self): 91 | return ['Makkah', 'Egypt', 'Karachi', 'ISNA', 'MWL'] 92 | 93 | def get_madhahed(self): 94 | return ['Hanafi', 'Default'] 95 | 96 | def get_clock_formats(self): 97 | return ['12h', '24h'] 98 | 99 | def get_languages(self): 100 | return list(languages) 101 | 102 | def get_fajr_adhans(self): 103 | dirfiles = os.listdir(os.path.dirname(os.path.realpath(__file__)) + "/audio/Fajr/") 104 | wavfiles = filter(lambda song: song.endswith(".ogg"), dirfiles) 105 | adhans = list(map(lambda x: os.path.splitext(x)[0], wavfiles)) 106 | return adhans 107 | 108 | def get_normal_adhans(self): 109 | dirfiles = os.listdir(os.path.dirname(os.path.realpath(__file__)) + "/audio/Normal/") 110 | wavfiles = filter(lambda song: song.endswith(".ogg"), dirfiles) 111 | adhans = list(map(lambda x: os.path.splitext(x)[0], wavfiles)) 112 | return adhans 113 | 114 | ## Functions to get and set settings 115 | @property 116 | def audio_notifications_num(self): 117 | return self._audionotifications 118 | 119 | @audio_notifications_num.setter 120 | def audio_notifications_num(self, value): 121 | self._audionotifications = value 122 | 123 | @property 124 | def audio_notifications(self): 125 | #print ("DEBUG: getting audio notifications settings @", (str(datetime.datetime.now()))) 126 | if self.audio_notifications_num == '1': 127 | return True 128 | else: 129 | return False 130 | 131 | @audio_notifications.setter 132 | def audio_notifications(self, data): 133 | #print ("DEBUG: setting audio notifications settings @", (str(datetime.datetime.now()))) 134 | if data == True: 135 | self.audio_notifications_num = '1' 136 | else: 137 | self.audio_notifications_num = '0' 138 | 139 | @property 140 | def fajr_adhan(self): 141 | return self._fajradhan 142 | 143 | @fajr_adhan.setter 144 | def fajr_adhan(self, value): 145 | self._fajradhan = value 146 | 147 | @property 148 | def normal_adhan(self): 149 | return self._normaladhan 150 | 151 | @normal_adhan.setter 152 | def normal_adhan(self, value): 153 | self._normaladhan = value 154 | 155 | @property 156 | def city(self): 157 | #print ("DEBUG: getting city settings @", (str(datetime.datetime.now()))) 158 | return self._city 159 | 160 | @city.setter 161 | def city(self, data): 162 | #print ("DEBUG: setting city settings @", (str(datetime.datetime.now()))) 163 | self._city = data 164 | 165 | @property 166 | def country(self): 167 | #print ("DEBUG: getting country settings @", (str(datetime.datetime.now()))) 168 | return self._country 169 | @country.setter 170 | def country(self, value): 171 | #print ("DEBUG: setting country settings @", (str(datetime.datetime.now()))) 172 | self._country = value 173 | 174 | @property 175 | def calculation_method_name(self): 176 | return self._calcmethodname 177 | 178 | @calculation_method_name.setter 179 | def calculation_method_name(self, value): 180 | self._calcmethodname = value 181 | 182 | @property 183 | def calculation_method(self): 184 | #print ("DEBUG: getting calculation method settings @", (str(datetime.datetime.now()))) 185 | if self.calculation_method_name == 'Makkah': 186 | return Calendar.UmmAlQuraUniv 187 | elif self.calculation_method_name == 'Egypt': 188 | return Calendar.EgyptianGeneralAuthorityOfSurvey 189 | elif self.calculation_method_name == 'Karachi': 190 | return Calendar.UnivOfIslamicSciencesKarachi 191 | elif self.calculation_method_name == 'ISNA': 192 | return Calendar.IslamicSocietyOfNorthAmerica 193 | elif self.calculation_method_name == 'MWL': 194 | return Calendar.MuslimWorldLeague 195 | 196 | @calculation_method.setter 197 | def calculation_method(self, data): 198 | #print ("DEBUG: setting calculation method settings @", (str(datetime.datetime.now()))) 199 | self.calculation_method_name = data 200 | 201 | @property 202 | def madhab_name(self): 203 | return self._madhab 204 | 205 | @madhab_name.setter 206 | def madhab_name(self, value): 207 | self._madhab = value 208 | 209 | @property 210 | def madhab(self): 211 | #print ("DEBUG: getting madhab settings @", (str(datetime.datetime.now()))) 212 | if self.madhab_name == 'Default': 213 | return Madhab.Default 214 | if self.madhab_name == 'Hanafi': 215 | return Madhab.Hanafi 216 | 217 | @madhab.setter 218 | def madhab(self, data): 219 | #print ("DEBUG: setting madhab settings @", (str(datetime.datetime.now()))) 220 | self._madhab = data 221 | 222 | @property 223 | def latitude(self): 224 | #print ("DEBUG: getting latitude settings @", (str(datetime.datetime.now()))) 225 | return float(self._latitude) 226 | 227 | @latitude.setter 228 | def latitude(self, data): 229 | #print ("DEBUG: setting latitude settings @", (str(datetime.datetime.now()))) 230 | self._latitude = str(data) 231 | 232 | @property 233 | def longitude(self): 234 | #print ("DEBUG: getting longitude settings @", (str(datetime.datetime.now()))) 235 | return float(self._longitude) 236 | 237 | @longitude.setter 238 | def longitude(self, data): 239 | #print ("DEBUG: setting longitude settings @", (str(datetime.datetime.now()))) 240 | self._longitude = str(data) 241 | 242 | @property 243 | def timezone(self): 244 | #print ("DEBUG: getting timezone settings @", (str(datetime.datetime.now()))) 245 | return float(self._timezone) 246 | 247 | @timezone.setter 248 | def timezone(self, data): 249 | #print ("DEBUG: setting timezone settings @", (str(datetime.datetime.now()))) 250 | self._timezone = str(data) 251 | 252 | @property 253 | def notification_time(self): 254 | #print ("DEBUG: getting notification time settings @", (str(datetime.datetime.now()))) 255 | return float(self._notif) 256 | 257 | @notification_time.setter 258 | def notification_time(self, data): 259 | #print ("DEBUG: setting notification time settings @", (str(datetime.datetime.now()))) 260 | self._notif = str(data) 261 | 262 | @property 263 | def hijrical_adjustment(self): 264 | #print ("DEBUG: getting hijri cal adjustment settings @", (str(datetime.datetime.now()))) 265 | return float(self._hijricaladjustment) 266 | 267 | @hijrical_adjustment.setter 268 | def hijrical_adjustment(self, data): 269 | #print ("DEBUG: setting hijri cal adjustment settings @", (str(datetime.datetime.now()))) 270 | self._hijricaladjustment = str(data) 271 | 272 | @property 273 | def language(self): 274 | return self._language 275 | 276 | @language.setter 277 | def language(self, value): 278 | self._language = value 279 | 280 | @property 281 | def iconlabel_num(self): 282 | return self._iconlabel 283 | 284 | @iconlabel_num.setter 285 | def iconlabel_num(self, value): 286 | self._iconlabel = value 287 | 288 | @property 289 | def iconlabel(self): 290 | #print ("DEBUG: getting icon label settings @", (str(datetime.datetime.now()))) 291 | if self.iconlabel_num == '1': 292 | return True 293 | else: 294 | return False 295 | 296 | @iconlabel.setter 297 | def iconlabel(self, data): 298 | #print ("DEBUG: setting icon label settings @", (str(datetime.datetime.now()))) 299 | if data == True: 300 | self.iconlabel_num = '1' 301 | else: 302 | self.iconlabel_num = '0' 303 | 304 | @property 305 | def start_minimized_num(self): 306 | return self._startminimized 307 | 308 | @start_minimized_num.setter 309 | def start_minimized_num(self, value): 310 | self._startminimized = value 311 | 312 | @property 313 | def start_minimized(self): 314 | #print ("DEBUG: getting start minimized settings @", (str(datetime.datetime.now()))) 315 | if self.start_minimized_num == '1': 316 | return True 317 | else: 318 | return False 319 | 320 | @start_minimized.setter 321 | def start_minimized(self, data): 322 | #print ("DEBUG: setting start minimized settings @", (str(datetime.datetime.now()))) 323 | if data == True: 324 | self.start_minimized_num = '1' 325 | else: 326 | self.start_minimized_num = '0' 327 | 328 | @property 329 | def daylight_saving_time_num(self): 330 | return self._daylightsavingtime 331 | 332 | @daylight_saving_time_num.setter 333 | def daylight_saving_time_num(self, value): 334 | self._daylightsavingtime = value 335 | 336 | @property 337 | def daylight_saving_time(self): 338 | #print ("DEBUG: getting daylight saving time settings @", (str(datetime.datetime.now()))) 339 | if self.daylight_saving_time_num == '1': 340 | return True 341 | else: 342 | return False 343 | 344 | @daylight_saving_time.setter 345 | def daylight_saving_time(self, data): 346 | #print ("DEBUG: setting daylight saving time settings @", (str(datetime.datetime.now()))) 347 | if data == True: 348 | self.daylight_saving_time_num = '1' 349 | else: 350 | self.daylight_saving_time_num = '0' 351 | 352 | @property 353 | def clock_format(self): 354 | #print ("DEBUG: getting clock format settings @", (str(datetime.datetime.now()))) 355 | return self._clockformat 356 | 357 | @clock_format.setter 358 | def clock_format(self, data): 359 | #print ("DEBUG: setting clock format settings @", (str(datetime.datetime.now()))) 360 | self._clockformat = data 361 | 362 | ## Function to save the options 363 | def save_options(self): 364 | print ("DEBUG: saving settings file @", (str(datetime.datetime.now()))) 365 | config = open(os.path.expanduser('~/.silaty'), 'w') 366 | Text='''# Silaty Settings File 367 | 368 | [DEFAULT] 369 | # Location Information 370 | city = %s 371 | country = %s 372 | latitude = %s 373 | longitude = %s 374 | timezone = %s 375 | language = %s 376 | 377 | # Possible Values for Calculation Methods 378 | # Makkah 379 | # Egypt 380 | # Karachi 381 | # ISNA 382 | # MWL 383 | calculation-method = %s 384 | 385 | # Possible Values for Madhaheb 386 | # Default 387 | # Hanafi 388 | madhab = %s 389 | 390 | # Possible Values for Clock Format 391 | # 24h 392 | # 12h 393 | clock-format = %s 394 | 395 | # Time before prayer for notification 396 | notif = %s 397 | 398 | # Display Time by the indicator icon 399 | iconlabel = %s 400 | 401 | # Should audio notifications be enabled 402 | audio-notifications = %s 403 | 404 | # Should the application start minimized 405 | minimized = %s 406 | 407 | # Should the application use daylight saving time 408 | daylight-saving-time = %s 409 | 410 | # Adjust Hijri Calendar 411 | hijrical-adjustment = %s 412 | 413 | # Paths to the audio files 414 | fajr-adhan = %s 415 | normal-adhan = %s 416 | 417 | ''' % (self.city, self.country, self.latitude, self.longitude, self.timezone, self.language, \ 418 | self.calculation_method_name, self.madhab_name, self.clock_format, \ 419 | self.notification_time, self.iconlabel_num, self.audio_notifications_num, self.start_minimized_num, \ 420 | self.daylight_saving_time_num, self.hijrical_adjustment, self.fajr_adhan, self.normal_adhan) 421 | config.write(Text) 422 | config.close() 423 | -------------------------------------------------------------------------------- /prayertime.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # prayertime.py 5 | # 6 | # Copyright 2010 ahmed youssef 7 | # Copyright 2014 Jesse Brandao 8 | # Copyright 2019 AXeL-dev 9 | # 10 | # This program is free software you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation either version 2 of the License, or 13 | # (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program if not, write to the Free Software 22 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 23 | # MA 02110-1301, USA. 24 | 25 | __author__ = "Jesse Wayde Brandão (Abdul Hakim)" 26 | __all__ = ['Calendar', 'Prayertime', 'Madhab', 'as_pytime', 'as_pydatetime'] 27 | 28 | import gi 29 | gi.require_version('Gtk', '3.0') 30 | gi.require_version('Notify', '0.7') 31 | from gi.repository import Gtk, Gst, GObject, Gio, GLib, GdkPixbuf, Notify 32 | from math import degrees, radians, atan, atan2, asin, acos, cos, sin, tan, fabs 33 | from datetime import date, timedelta 34 | from time import strptime 35 | from options import * 36 | from translate import translate_text as _ 37 | import datetime 38 | import time 39 | import os 40 | import subprocess 41 | 42 | class Prayertime(object): 43 | 44 | def __init__(self): 45 | GLib.threads_init() 46 | Gst.init(None) 47 | 48 | self.options = Options() 49 | 50 | year = datetime.datetime.now().year 51 | month = datetime.datetime.now().month 52 | day = datetime.datetime.now().day 53 | self.date = date(year, month, day) 54 | 55 | self._shrouk = None 56 | self._fajr = None 57 | self._zuhr = None 58 | self._asr = None 59 | self._maghrib = None 60 | self._isha = None 61 | self._nextprayer = "" 62 | self._tnprayer = 0 63 | self.dec = 0 64 | 65 | def shrouk_time(self): 66 | """Gets the time of shrouk.""" 67 | fmt = to_hrtime(self._shrouk, True) 68 | if "00" in fmt: 69 | fmt = fmt.replace('00','12') 70 | return fmt 71 | 72 | def fajr_time(self): 73 | """Gets the time of fajr.""" 74 | fmt = to_hrtime(self._fajr, True) 75 | if "00" in fmt: 76 | fmt = fmt.replace('00','12') 77 | return fmt 78 | 79 | def zuhr_time(self): 80 | """Gets the time of zuhr.""" 81 | fmt = to_hrtime(self._zuhr, True) 82 | if "00" in fmt: 83 | fmt = fmt.replace('00','12') 84 | return fmt 85 | 86 | def asr_time(self): 87 | """Gets the time of asr.""" 88 | fmt = to_hrtime(self._asr) 89 | if "00" in fmt: 90 | fmt = fmt.replace('00','12') 91 | return fmt 92 | 93 | def maghrib_time(self): 94 | """Gets the time of maghrib""" 95 | fmt = to_hrtime(self._maghrib) 96 | if "00" in fmt: 97 | fmt = fmt.replace('00','12') 98 | return fmt 99 | 100 | def isha_time(self): 101 | """Gets the time of isha""" 102 | fmt = to_hrtime(self._isha) 103 | if "00" in fmt: 104 | fmt = fmt.replace('00','12') 105 | return fmt 106 | 107 | def next_prayer(self): 108 | return self._nextprayer 109 | 110 | def time_to_next_prayer(self): 111 | return self._tnprayer 112 | 113 | def calculate(self, notify_also = True): 114 | """Calculations of prayertimes.""" 115 | year = self.date.year 116 | month = self.date.month 117 | day = self.date.day 118 | longitude = self.options.longitude 119 | latitude = self.options.latitude 120 | zone = self.options.timezone 121 | julian_day = (367*year)-int(((year+int((month+9)/12))*7)/4)+int(275*month/9)+day-730531.5 122 | sun_length = (280.461+0.9856474*julian_day)%360 123 | middle_sun = (357.528+0.9856003*julian_day)%360 124 | 125 | lamda = (sun_length+1.915*sin(radians(middle_sun))+0.02*sin(radians(2*middle_sun)))%360 126 | 127 | obliquity = 23.439-0.0000004*julian_day 128 | 129 | alpha = degrees(atan(cos(radians(obliquity))*tan(radians(lamda)))) 130 | 131 | if 90 < lamda < 180: 132 | alpha += 180 133 | elif 179 < lamda < 360: 134 | alpha += 360 135 | 136 | ST = (100.46+0.985647352*julian_day)%360 137 | 138 | self.dec = degrees(asin(sin(radians(obliquity))*sin(radians(lamda)))) 139 | 140 | noon = alpha-ST 141 | 142 | if noon < 0: 143 | noon += 360 144 | 145 | UTNoon = noon-longitude 146 | zuhr = (UTNoon/15)+zone # Zuhr Time. 147 | maghrib = zuhr+self._equation(-0.8333)/15 # Maghrib Time 148 | shrouk = zuhr-self._equation(-0.8333)/15 # Shrouk Time 149 | 150 | fajr_alt = 0 151 | isha_alt = 0 152 | 153 | if self.options.calculation_method == Calendar.UmmAlQuraUniv: 154 | fajr_alt = -19 155 | elif self.options.calculation_method == Calendar.EgyptianGeneralAuthorityOfSurvey: 156 | fajr_alt = -19.5 157 | isha_alt = -17.5 158 | elif self.options.calculation_method == Calendar.MuslimWorldLeague: 159 | fajr_alt = -18 160 | isha_alt = -17 161 | elif self.options.calculation_method == Calendar.IslamicSocietyOfNorthAmerica: 162 | fajr_alt = isha_alt = -15 163 | elif self.options.calculation_method == Calendar.UnivOfIslamicSciencesKarachi: 164 | fajr_alt = isha_alt = -18 165 | 166 | fajr = zuhr-self._equation(fajr_alt)/15 # Fajr Time 167 | isha = zuhr+self._equation(isha_alt)/15 # Isha Time 168 | 169 | if self.options.calculation_method == Calendar.UmmAlQuraUniv : 170 | isha = maghrib+1.5 171 | 172 | asr_alt = 0 173 | 174 | if self.options.madhab == Madhab.Hanafi : 175 | asr_alt = 90 - degrees(atan(2+tan(radians(abs(latitude - self.dec))))) 176 | else: 177 | asr_alt = 90 - degrees(atan(1 + tan(radians(abs(latitude - self.dec))))) 178 | 179 | asr = zuhr+self._equation(asr_alt)/15 # Asr Time. 180 | 181 | # Add one hour to all times if the season is Summmer. 182 | if self.options.daylight_saving_time and is_dst() : 183 | fajr += 1 184 | shrouk += 1 185 | zuhr += 1 186 | asr += 1 187 | maghrib += 1 188 | isha += 1 189 | 190 | self._shrouk = shrouk 191 | self._fajr = fajr 192 | self._zuhr = zuhr 193 | self._asr = asr 194 | self._maghrib = maghrib 195 | self._isha = isha 196 | 197 | # Transform Times To DateTimes ,so We Can Make Calculations on it 198 | Fajr = datetime.datetime.strptime(self.fajr_time(),"%I:%M:%S %p") 199 | Dhuhr = datetime.datetime.strptime(self.zuhr_time(),"%I:%M:%S %p") 200 | Asr = datetime.datetime.strptime(self.asr_time(),"%I:%M:%S %p") 201 | Maghrib = datetime.datetime.strptime(self.maghrib_time(),"%I:%M:%S %p") 202 | Isha = datetime.datetime.strptime(self.isha_time(),"%I:%M:%S %p") 203 | 204 | # Assign Times to A List 205 | PrayerTimes = [Fajr, Dhuhr, Asr, Maghrib, Isha] 206 | Time = datetime.datetime.now()# Time Now 207 | Time = Time.replace(microsecond=0, year=1900, month=1, day=1)# Replace year,month and day to be the same on PrayerTimes 208 | 209 | NotifTime = Time+datetime.timedelta(minutes=self.options.notification_time)# Ten Minutes Before Next Prayer 210 | ClosestPrayer = self.closest(Time, PrayerTimes)# Get The Closest Prayer Time 211 | PrayerIndex = PrayerTimes.index(ClosestPrayer)# Get Index From List 212 | 213 | if PrayerIndex == 0: 214 | CurrentPrayer='Fajr' 215 | if Time < Fajr: 216 | PrevPrayer = 'Isha' 217 | NextPrayer = 'Fajr' 218 | NextPrayerDT = Fajr 219 | else: 220 | PrevPrayer = 'Fajr' 221 | NextPrayer = 'Dhuhr' 222 | NextPrayerDT = Dhuhr 223 | 224 | if PrayerIndex == 1: 225 | CurrentPrayer = 'Dhuhr' 226 | if Time < Dhuhr: 227 | PrevPrayer = 'Fajr' 228 | NextPrayer = 'Dhuhr' 229 | NextPrayerDT = Dhuhr 230 | else: 231 | PrevPrayer = 'Dhuhr' 232 | NextPrayer = 'Asr' 233 | NextPrayerDT = Asr 234 | 235 | elif PrayerIndex == 2: 236 | CurrentPrayer = 'Asr' 237 | if Time < Asr: 238 | PrevPrayer = 'Dhuhr' 239 | NextPrayer = 'Asr' 240 | NextPrayerDT = Asr 241 | else: 242 | PrevPrayer = 'Asr' 243 | NextPrayer = 'Maghrib' 244 | NextPrayerDT = Maghrib 245 | 246 | elif PrayerIndex == 3: 247 | CurrentPrayer = 'Maghrib' 248 | if Time < Maghrib: 249 | PrevPrayer = 'Asr' 250 | NextPrayer = 'Maghrib' 251 | NextPrayerDT = Maghrib 252 | else: 253 | PrevPrayer = 'Maghrib' 254 | NextPrayer = 'Isha' 255 | NextPrayerDT = Isha 256 | 257 | elif PrayerIndex == 4: 258 | CurrentPrayer = 'Isha' 259 | if Time < Isha: 260 | PrevPrayer = 'Maghrib' 261 | NextPrayer = 'Isha' 262 | NextPrayerDT = Isha 263 | else: 264 | PrevPrayer = 'Isha' 265 | NextPrayer = 'Fajr' 266 | NextPrayerDT = Fajr 267 | 268 | # Calculate Time to The Next Prayer 269 | TimeToNextPrayer = NextPrayerDT-Time 270 | if TimeToNextPrayer.total_seconds() < 0: 271 | # Add a day to fix the timining 272 | TimeToNextPrayer = TimeToNextPrayer + timedelta(days=1) 273 | 274 | self._nextprayer = NextPrayer 275 | self._tnprayer = TimeToNextPrayer 276 | 277 | # Notification 278 | if notify_also: 279 | for time in PrayerTimes: 280 | if time == NotifTime: 281 | self.notify(_('Get Ready'), _('%s minutes left until the %s prayer.') % (str(int(self.options.notification_time)), _(NextPrayer))) 282 | elif time == Time: 283 | self.notify(_('Prayer time for %s') % _(CurrentPrayer), _("It's time for the %s prayer.") % _(CurrentPrayer), self.options.audio_notifications, CurrentPrayer) 284 | 285 | def muteApps(self, value = 'true'): 286 | try: 287 | inputs = subprocess.getoutput('pacmd list-sink-inputs | sed -n "s/^\\s*index: \\([[:digit:]]*\\)/\\1/p"').split('\n') 288 | clients = subprocess.getoutput('pacmd list-sink-inputs | sed -n "s/^\\s*client: [[:digit:]]* <\\(.*\\)>/\\1/p"').split('\n') 289 | for i in range(len(inputs)): 290 | if clients[i] not in ['silaty-indicator', 'ZOOM VoiceEngine']: 291 | subprocess.Popen(['pacmd', 'set-sink-input-mute', inputs[i], value]) 292 | except: 293 | print ("DEBUG: Cannot mute apps @", (str(datetime.datetime.now()))) 294 | 295 | def notify(self, title, message, play_audio = False, current_prayer = ''): 296 | Notify.init("Silaty") 297 | notif = Notify.Notification.new(title, message) 298 | icon = GdkPixbuf.Pixbuf.new_from_file(os.path.dirname(os.path.realpath(__file__)) + "/icons/hicolor/128x128/apps/silaty.svg") 299 | notif.set_icon_from_pixbuf(icon) 300 | 301 | if play_audio: 302 | self.muteApps() 303 | if current_prayer == 'Fajr': 304 | uri = "file://" + os.path.dirname(os.path.realpath(__file__)) + "/audio/Fajr/" + self.options.fajr_adhan + ".ogg" 305 | self.fajrplayer = Gst.ElementFactory.make("playbin", "player") 306 | fakesink = Gst.ElementFactory.make("fakesink", "fakesink") 307 | self.fajrplayer.set_property('uri', uri) 308 | self.fajrplayer.set_property("video-sink", fakesink) 309 | self.fajrplayer.set_state(Gst.State.PLAYING) 310 | bus = self.fajrplayer.get_bus() 311 | bus.add_signal_watch() 312 | bus.connect("message", self.on_fajrplayer_message) 313 | else: 314 | uri = "file://" + os.path.dirname(os.path.realpath(__file__)) + "/audio/Normal/" + self.options.normal_adhan + ".ogg" 315 | self.normalplayer = Gst.ElementFactory.make("playbin", "player") 316 | fakesink = Gst.ElementFactory.make("fakesink", "fakesink") 317 | self.normalplayer.set_property('uri', uri) 318 | self.normalplayer.set_property("video-sink", fakesink) 319 | self.normalplayer.set_state(Gst.State.PLAYING) 320 | bus = self.normalplayer.get_bus() 321 | bus.add_signal_watch() 322 | bus.connect("message", self.on_normalplayer_message) 323 | 324 | notif.set_app_name('Silaty') 325 | notif.show() 326 | 327 | def on_fajrplayer_message(self, bus, message): 328 | t = message.type 329 | if t == Gst.MessageType.EOS: # track is finished 330 | self.fajrplayer.set_state(Gst.State.NULL) 331 | self.muteApps('false') 332 | elif t == Gst.MessageType.ERROR: 333 | self.fajrplayer.set_state(Gst.State.NULL) 334 | self.muteApps('false') 335 | err, debug = message.parse_error() 336 | print ("Error: %s" % err, debug) 337 | 338 | def on_normalplayer_message(self, bus, message): 339 | t = message.type 340 | if t == Gst.MessageType.EOS: # track is finished 341 | self.normalplayer.set_state(Gst.State.NULL) 342 | self.muteApps('false') 343 | elif t == Gst.MessageType.ERROR: 344 | self.normalplayer.set_state(Gst.State.NULL) 345 | self.muteApps('false') 346 | err, debug = message.parse_error() 347 | print ("Error: %s" % err, debug) 348 | 349 | def closest(self, target, collection) :# Returns the closest Adhan 350 | return min((abs(target - i), i) for i in collection)[1] 351 | 352 | def _equation(self, alt): 353 | return degrees( acos( (sin(radians(alt)) - sin(radians(self.dec)) * sin(radians(self.options.latitude)))/(cos(radians(self.dec))*cos(radians(self.options.latitude))))) 354 | 355 | def report(self): 356 | """Simple report of all prayertimes.""" 357 | print ('Fajr Time is %s' % self.fajr_time()) 358 | print ('Shrouk Time is %s' % self.shrouk_time()) 359 | print ('Zuhr Time is %s' % self.zuhr_time()) 360 | print ('Asr Time is %s' % self.asr_time()) 361 | print ('Maghrib Time is %s' % self.maghrib_time()) 362 | print ('Ishaa Time is %s' % self.isha_time()) 363 | 364 | def get_qibla(self): 365 | k_lat = radians(21.423333); 366 | k_lon = radians(39.823333); 367 | 368 | longitude = radians(self.options.longitude) 369 | latitude = radians(self.options.latitude) 370 | 371 | numerator = sin(k_lon - longitude) 372 | denominator = (cos(latitude) * tan(k_lat)) - (sin(latitude) * cos(k_lon - longitude)) 373 | 374 | q = atan2(numerator,denominator) 375 | q = degrees(q) 376 | if q < 0 : q += 360 377 | 378 | return q 379 | 380 | def qibla_distance(self): 381 | k_lat = radians(21.423333); 382 | k_lon = radians(39.823333); 383 | 384 | longitude = radians(self.options.longitude) 385 | latitude = radians(self.options.latitude) 386 | 387 | r = 6378.7 # kilometers 388 | 389 | return acos(sin(k_lat) * sin(latitude) + cos(k_lat) * cos(latitude) * cos(longitude-k_lon)) * r 390 | 391 | if __name__=="__main__": 392 | pt = Prayertime() 393 | print ("%s" % pt.get_qibla()) 394 | print ("%s" % pt.qibla_distance()) 395 | pt.calculate() 396 | pt.report() 397 | 398 | def is_dst(): 399 | # Find out whether or not daylight savings is in effect 400 | return bool(time.localtime().tm_isdst) 401 | 402 | def fill_zeros(time): 403 | fill = lambda var: [var, '0'+var] [len(var) <2] 404 | return ':'.join(map(fill, time.split(':'))) 405 | 406 | def to_hrtime(var, isAM=False): 407 | """var: double -> human readable string of format "%I:%M:%S %p" """ 408 | time = '' 409 | hours = int(var) # cast var (initially a double) as an int 410 | hours_mod = hours % 12 411 | 412 | # Check if time correspond to morning or afternoon 413 | #print ('%s %s' % (hours, hours_mod)) 414 | if isAM: 415 | if hours_mod > 1 and hours_mod < 12: 416 | zone = "AM" 417 | else: 418 | zone = "PM" 419 | else: 420 | zone = "PM" 421 | 422 | # This will give us the time from 0 to 12 423 | if hours > 12: 424 | time += str(hours_mod) 425 | else: 426 | time += str(hours) 427 | time += ":" 428 | var -= hours 429 | 430 | # Get the minutes from the left over time 431 | minutes = int(var*60) 432 | time += str(abs(minutes)) 433 | time += ":" 434 | var -= minutes/100 435 | 436 | # And get the seconds from what's left over of that 437 | sec = int(fabs(60*var)) 438 | 439 | time += str(sec) 440 | time = fill_zeros(time) 441 | time += " " 442 | 443 | # Add the AM or PM 444 | time += zone 445 | 446 | #print ('%s' % time) 447 | return time 448 | 449 | def as_pytime(string_to_parse, fmt="%I:%M:%SS %p"): 450 | """returns time.tm_struct by parsing string_to_parse.""" 451 | return strptime(string_to_parse, fmt) 452 | 453 | def as_pydatetime(d, ts): 454 | """returns a datetime object. 455 | d: date object 456 | ts: tm_struct 457 | """ 458 | return datetime(year=d.year, month=d.month, day=d.day, \ 459 | hour=ts.tm_hour, minute=ts.tm_min, second=ts.tm_sec) 460 | -------------------------------------------------------------------------------- /qiblacompass.py: -------------------------------------------------------------------------------- 1 | # Silaty 2 | # Copyright (c) 2018 - 2021 AXeL 3 | # Copyright (c) 2014 - 2015 Jessewb786 4 | 5 | import gi 6 | gi.require_version('Gtk', '3.0') 7 | from gi.repository import Gtk, GLib, Gio, Gdk, GdkPixbuf 8 | from translate import translate_text as _ 9 | 10 | class QiblaCompass(Gtk.Box): 11 | def __init__(self, qibladirection, country, city): 12 | Gtk.Box.__init__(self) 13 | 14 | self.mainbox = Gtk.Box() 15 | self.mainbox.set_orientation(Gtk.Orientation.VERTICAL) 16 | 17 | qibla = self.set_compass(qibladirection) 18 | qibla.props.valign = Gtk.Align.START 19 | 20 | ## Make the top label 21 | qiblatitle = Gtk.Label(label=_("Qibla direction :"), margin_left=20, margin_right=20) 22 | qiblatitle.props.halign = Gtk.Align.START 23 | self.mainbox.pack_start(qiblatitle, False, False, 12) 24 | 25 | ## Set the compass image 26 | self.mainbox.pack_start(qibla, False, True, 0) 27 | 28 | ## Set the country and city 29 | vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,spacing=6) 30 | qiblalabel = Gtk.Label(label=_("Country : %s") % country) 31 | qiblalabel.props.halign = Gtk.Align.CENTER 32 | qiblalabel.props.valign = Gtk.Align.CENTER 33 | vbox.pack_start(qiblalabel, True,True, 0) 34 | 35 | qiblalabel = Gtk.Label(label=_("City : %s") % city) 36 | qiblalabel.props.halign = Gtk.Align.CENTER 37 | qiblalabel.props.valign = Gtk.Align.CENTER 38 | vbox.pack_start(qiblalabel, True, True, 0) 39 | 40 | ## Add it all in the end 41 | self.mainbox.pack_start(vbox, False, False, 12) 42 | self.pack_start(self.mainbox, True, True, 0) 43 | 44 | def set_compass(self, qibladirection): 45 | print ("DEBUG: Showing Qibla window") 46 | img=''' 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Layer 1 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | %.2f° 67 | 68 | 69 | ''' % (qibladirection-90, qibladirection) 70 | stream = Gio.MemoryInputStream.new_from_bytes(GLib.Bytes.new(img.encode('utf-8'))) 71 | pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, None) 72 | svgwidget = Gtk.Image.new_from_pixbuf(pixbuf) 73 | return svgwidget 74 | 75 | def update_compass(self, qibladirection, country, city): 76 | ## The only way to update so far is to remove the whole thing and create it again 77 | self.remove(self.mainbox) 78 | 79 | self.mainbox = Gtk.Box() 80 | self.mainbox.set_orientation(Gtk.Orientation.VERTICAL) 81 | 82 | qibla = self.set_compass(qibladirection) 83 | qibla.props.valign = Gtk.Align.START 84 | 85 | ## Make the top label 86 | qiblatitle = Gtk.Label(label=_("Qibla direction :"), margin_left=20, margin_right=20) 87 | qiblatitle.props.halign = Gtk.Align.START 88 | self.mainbox.pack_start(qiblatitle, False, False, 12) 89 | 90 | ## Set the compass image 91 | self.mainbox.pack_start(qibla, False, True, 0) 92 | 93 | ## Set the country and city 94 | vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,spacing=6) 95 | qiblalabel = Gtk.Label(label=_("Country : %s") % country) 96 | qiblalabel.props.halign = Gtk.Align.CENTER 97 | qiblalabel.props.valign = Gtk.Align.CENTER 98 | vbox.pack_start(qiblalabel, True,True, 0) 99 | 100 | qiblalabel = Gtk.Label(label=_("City : %s") % city) 101 | qiblalabel.props.halign = Gtk.Align.CENTER 102 | qiblalabel.props.valign = Gtk.Align.CENTER 103 | vbox.pack_start(qiblalabel, True, True, 0) 104 | 105 | ## Add it all in the end 106 | self.mainbox.pack_start(vbox, False, False, 12) 107 | self.pack_start(self.mainbox, True, True, 0) 108 | self.show_all() -------------------------------------------------------------------------------- /screenshots/Silaty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/screenshots/Silaty.png -------------------------------------------------------------------------------- /screenshots/Silaty_calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/screenshots/Silaty_calendar.png -------------------------------------------------------------------------------- /screenshots/Silaty_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/screenshots/Silaty_notification.png -------------------------------------------------------------------------------- /screenshots/Silaty_qibla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/screenshots/Silaty_qibla.png -------------------------------------------------------------------------------- /screenshots/Silaty_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/screenshots/Silaty_settings.png -------------------------------------------------------------------------------- /screenshots/linux-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/screenshots/linux-download.png -------------------------------------------------------------------------------- /screenshots/windows-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxForGeeks/Silaty/4b0a851b9a931ab14caa4311a0512aa322bf06f0/screenshots/windows-download.png -------------------------------------------------------------------------------- /settingspane.py: -------------------------------------------------------------------------------- 1 | # Silaty 2 | # Copyright (c) 2018 - 2021 AXeL 3 | # Copyright (c) 2014 - 2015 Jessewb786 4 | 5 | import gi 6 | gi.require_version('Gtk', '3.0') 7 | from gi.repository import Gtk 8 | 9 | class SettingsPane(Gtk.Box): 10 | def __init__(self): 11 | Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, margin_top=12, margin_bottom=6, margin_left=12, margin_right=12) 12 | self.nrows = 0 13 | self.ngrids = -1 14 | self.grids = [] 15 | 16 | def add_grid(self): 17 | grid = Gtk.Grid(column_spacing=3, row_spacing=6, margin_right=12, margin_left=12, margin_top=6, margin_bottom=6) 18 | #grid.set_row_homogeneous(True) 19 | grid.set_column_homogeneous(True) 20 | grid.set_halign(Gtk.Align.FILL) 21 | grid.set_valign(Gtk.Align.START) 22 | self.pack_start(grid, True, True, 0) 23 | self.grids.append(grid) 24 | 25 | def add_setting(self, setting, label): 26 | label.set_halign(Gtk.Align.START) 27 | self.grids[self.ngrids].attach(label, 0, self.nrows, 1, 1) 28 | self.grids[self.ngrids].attach_next_to(setting, label, Gtk.PositionType.RIGHT, 1, 1) 29 | self.nrows += 1 30 | 31 | def add_category(self, category): 32 | label = Gtk.Label(label=""+category+"") 33 | label.set_use_markup(True) 34 | label.set_halign(Gtk.Align.START) 35 | self.pack_start(label, True, True, 0) 36 | self.add_grid() 37 | self.nrows = 0 38 | self.ngrids += 1 39 | -------------------------------------------------------------------------------- /sidebar.py: -------------------------------------------------------------------------------- 1 | # Silaty 2 | # Copyright (c) 2018 - 2021 AXeL 3 | # Copyright (c) 2014 - 2015 Jessewb786 4 | 5 | import gi 6 | gi.require_version('Gtk', '3.0') 7 | from gi.repository import Gtk, Gdk, GLib, GdkPixbuf, GObject 8 | 9 | class SideBar(Gtk.Grid): 10 | def __init__(self, stack = Gtk.Stack()): 11 | Gtk.Grid.__init__(self) 12 | self.set_orientation(Gtk.Orientation.VERTICAL) 13 | color = 70.0/256.0 14 | self.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA.from_color(Gdk.Color.from_floats(color,color,color))) 15 | self._childlength = 0 16 | self.stackchildnames = [] 17 | self.stack = stack 18 | self.connect("window-shown", self.activate_button) 19 | 20 | @property 21 | def childlength(self): 22 | return self._childlength 23 | 24 | @childlength.setter 25 | def childlength(self, value): 26 | self._childlength = value 27 | 28 | def new_button(self, inact_icon, act_icon, on_press_callback = None): 29 | sidebaricon = SideBarButton(inact_icon, act_icon, on_press_callback) 30 | self.attach(sidebaricon, 0, self.childlength, 1, 1) 31 | 32 | sidebaricon.position = self.childlength 33 | if on_press_callback is None: 34 | sidebaricon.connect("sidebar-button-pressed", self.change_visible_stack, sidebaricon.position) 35 | 36 | self.childlength += 1 37 | 38 | def add_to_stack(self, widget, name): 39 | self.stack.add_named(widget, name) 40 | self.stackchildnames.append(name) 41 | 42 | def change_visible_stack(self, widget, position): 43 | self.stack.set_visible_child_name(self.stackchildnames[position]) 44 | self.emit("stack-changed", self.stackchildnames[position]) 45 | 46 | def activate_button(self, widget): 47 | visiblechild = self.stack.get_visible_child_name() 48 | index = self.stackchildnames.index(visiblechild) 49 | self.get_child(index).state = SideBarButtonState.ON 50 | self.get_child(index).iconstack.set_visible_child_name('active') 51 | self.emit("stack-changed", visiblechild) 52 | 53 | def get_child(self, index): 54 | return self.get_child_at(0, index) 55 | 56 | class SideBarButton(Gtk.EventBox): 57 | def __init__(self, inactive_icon, active_icon, on_press_callback = None): 58 | 59 | Gtk.EventBox.__init__(self) 60 | self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) 61 | if on_press_callback is None: 62 | self.connect("button-press-event", self.on_icon_pressed) 63 | else: 64 | self.connect("button-press-event", on_press_callback) 65 | 66 | self.inactive_icon = inactive_icon 67 | self.active_icon = active_icon 68 | self._position = 0 69 | self._state = SideBarButtonState.OFF 70 | 71 | self.iconstack = Gtk.Stack(margin_left=12, margin_right=12, margin_top=6, margin_bottom=6) 72 | self.iconstack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) 73 | self.iconstack.set_transition_duration(300) 74 | 75 | icon = self.set_image_from_file(self.inactive_icon) 76 | self.iconstack.add_named(icon, 'inactive') 77 | 78 | icon = self.set_image_from_file(self.active_icon) 79 | self.iconstack.add_named(icon, 'active') 80 | 81 | self.add(self.iconstack) 82 | 83 | @property 84 | def position(self): 85 | return self._position 86 | 87 | @position.setter 88 | def position(self, value): 89 | self._position = value 90 | 91 | @property 92 | def state(self): 93 | return self._state 94 | 95 | @state.setter 96 | def state(self, value): 97 | if value == SideBarButtonState.ON: 98 | self.iconstack.set_visible_child_name('active') 99 | else: 100 | self.iconstack.set_visible_child_name('inactive') 101 | self._state = value 102 | 103 | def on_icon_pressed(self, widget, data): 104 | parent = self.get_parent() 105 | for i in range(0, parent.childlength): 106 | if i == self.position: 107 | parent.get_child(i).state = SideBarButtonState.ON 108 | parent.get_child(i).emit("sidebar-button-pressed") 109 | else: 110 | parent.get_child(i).state = SideBarButtonState.OFF 111 | 112 | def set_image_from_file(self, iconpath): 113 | try: 114 | pixbuf = GdkPixbuf.Pixbuf.new_from_file(iconpath) 115 | icon = Gtk.Image.new_from_pixbuf(pixbuf) 116 | except GLib.GError: 117 | icon = Gtk.Image.new_from_stock(Gtk.STOCK_MISSING_IMAGE, Gtk.IconSize.LARGE_TOOLBAR) 118 | return icon 119 | 120 | class SideBarButtonState(object): 121 | ON, OFF = True, False 122 | 123 | GObject.type_register(SideBarButton) 124 | GObject.signal_new("sidebar-button-pressed", SideBarButton, GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()) 125 | 126 | GObject.type_register(SideBar) 127 | GObject.signal_new("window-shown", SideBar, GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()) 128 | GObject.signal_new("stack-changed", SideBar, GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, [GObject.TYPE_STRING]) -------------------------------------------------------------------------------- /silaty-indicator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Silaty 5 | # 6 | # Copyright (c) 2018 - 2021 AXeL 7 | # Copyright (c) 2014 - 2015 Jessewb786 8 | # 9 | # TODO: Help document 10 | # TODO: Good Code Documentation 11 | 12 | import gi 13 | gi.require_version('Gtk', '3.0') 14 | gi.require_version('AppIndicator3', '0.1') 15 | from gi.repository import Gtk, GObject, Gio, GLib, Gdk, GdkPixbuf 16 | from gi.repository import AppIndicator3 as AI 17 | from datetime import date 18 | from hijrical import * 19 | from silaty import * 20 | from translate import translate_text as _ 21 | import locale 22 | import sys 23 | 24 | # you might need to install the locale: sudo apt install language-pack-en 25 | locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') 26 | 27 | class SilatyIndicator(): 28 | def __init__(self): 29 | # Setup Indicator Applet 30 | self.Indicator = AI.Indicator.new("silaty-indicator", "silaty-indicator", AI.IndicatorCategory.APPLICATION_STATUS) 31 | self.Indicator.set_status(AI.IndicatorStatus.ACTIVE) 32 | self.Indicator.set_icon(self.icon()) 33 | 34 | # Activate the Silaty Window 35 | self.silaty = Silaty(self) 36 | 37 | self.silaty.prayertimes.calculate() 38 | print ("DEBUG: Silaty started! @", (str(datetime.datetime.now()))) 39 | print ("DEBUG: started prayer times report: @", (str(datetime.datetime.now()))) 40 | self.silaty.prayertimes.report() 41 | print ("DEBUG: end of report @", (str(datetime.datetime.now()))) 42 | 43 | # Setup the Menu 44 | print ("DEBUG: initialize the menu @", (str(datetime.datetime.now()))) 45 | self.Menu = Gtk.Menu() 46 | 47 | # Add Hijri date 48 | print ("DEBUG: Adding hijri date to menu @", (str(datetime.datetime.now()))) 49 | self.HijriDateItem = Gtk.MenuItem(self.get_hijri_date()) 50 | self.HijriDateItem.connect("activate", self.show_home) 51 | self.Menu.append(self.HijriDateItem) 52 | self.Menu.append(Gtk.SeparatorMenuItem()) 53 | 54 | # Add City 55 | print ("DEBUG: Adding city to menu @", (str(datetime.datetime.now()))) 56 | self.CityItem = Gtk.MenuItem(_("Location: %s") % self.silaty.prayertimes.options.city, sensitive=False) 57 | self.Menu.append(self.CityItem) 58 | 59 | # Add Qibla Direction 60 | print ("DEBUG: Adding qibla direction to menu @", (str(datetime.datetime.now()))) 61 | self.QiblaItem = Gtk.MenuItem(_("Qibla is %.2f° from True North") % self.silaty.prayertimes.get_qibla()) 62 | self.QiblaItem.connect("activate", self.show_qibla) 63 | self.Menu.append(self.QiblaItem) 64 | self.Menu.append(Gtk.SeparatorMenuItem()) 65 | 66 | # Add Prayer Times 67 | print ("DEBUG: Adding the prayer times to menu @", (str(datetime.datetime.now()))) 68 | self.FajrItem = Gtk.MenuItem(_("Fajr\t\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.fajr_time()), sensitive=False) 69 | #self.ShurukItem = Gtk.MenuItem(_("Shuruk\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.shrouk_time()), sensitive=False) 70 | self.DhuhrItem = Gtk.MenuItem(_("Dhuhr\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.zuhr_time()), sensitive=False) 71 | self.AsrItem = Gtk.MenuItem(_("Asr\t\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.asr_time()), sensitive=False) 72 | self.MaghribItem = Gtk.MenuItem(_("Maghrib\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.maghrib_time()), sensitive=False) 73 | self.IshaItem = Gtk.MenuItem(_("Isha\t\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.isha_time()), sensitive=False) 74 | self.Menu.append(self.FajrItem) 75 | #self.Menu.append(self.ShurukItem) 76 | self.Menu.append(self.DhuhrItem) 77 | self.Menu.append(self.AsrItem) 78 | self.Menu.append(self.MaghribItem) 79 | self.Menu.append(self.IshaItem) 80 | self.Menu.append(Gtk.SeparatorMenuItem()) 81 | 82 | print ("DEBUG: Adding Next prayer to menu @", (str(datetime.datetime.now()))) 83 | self.NextPrayerItem = Gtk.MenuItem(_('Next Prayer'), sensitive=False)# Next PrayerTime's Item, it shows you information about the next prayer 84 | self.Menu.append(self.NextPrayerItem) 85 | self.Menu.append(Gtk.SeparatorMenuItem()) 86 | 87 | print ("DEBUG: Adding About, Settings and Quit to menu @", (str(datetime.datetime.now()))) 88 | # The Last 3 menu items never change and don't need to be updated 89 | AboutItem = Gtk.MenuItem(_('About')) 90 | self.Menu.append(AboutItem) 91 | AboutItem.connect('activate',self.about_dialog, None) 92 | 93 | SettingsItem = Gtk.MenuItem(_('Settings')) 94 | self.Menu.append(SettingsItem) 95 | SettingsItem.connect('activate', self.show_settings, None) 96 | 97 | ExitItem = Gtk.MenuItem(_('Quit')) 98 | self.Menu.append(ExitItem) 99 | ExitItem.connect('activate', self.quit) 100 | 101 | print ("DEBUG: starting mainloop @", (str(datetime.datetime.now()))) 102 | self.currentprayer = self.silaty.prayertimes.next_prayer() 103 | self.loop()# Run Application's loop 104 | self.Menu.show_all()# Show All Items 105 | self.Indicator.set_menu(self.Menu)# Assign Menu To Indicator 106 | self.Gobjectloop = GLib.timeout_add_seconds(1, self.loop)# Run loop 107 | 108 | def loop(self): 109 | global NextPrayerDT 110 | self.silaty.prayertimes.calculate()# Calculate PrayerTimes 111 | 112 | # Update City menu item 113 | self.CityItem.set_label(_("Location: %s") % self.silaty.prayertimes.options.city) 114 | 115 | # Update Hijri Date Menu item 116 | self.HijriDateItem.set_label(self.get_hijri_date()) 117 | 118 | # Update Qibla Menu item 119 | self.QiblaItem.set_label(_("Qibla is %.2f° from True North") % self.silaty.prayertimes.get_qibla()) 120 | 121 | # Update Prayer Times items 122 | self.FajrItem.set_label(_("Fajr\t\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.fajr_time())) 123 | #self.ShurukItem.set_label(_("Shuruk\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.shrouk_time())) 124 | self.DhuhrItem.set_label(_("Dhuhr\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.zuhr_time())) 125 | self.AsrItem.set_label(_("Asr\t\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.asr_time())) 126 | self.MaghribItem.set_label(_("Maghrib\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.maghrib_time())) 127 | self.IshaItem.set_label(_("Isha\t\t\t\t\t%s") % self.silaty.get_times(self.silaty.prayertimes.isha_time())) 128 | 129 | nextprayer = self.silaty.prayertimes.next_prayer() 130 | tonextprayer = self.silaty.prayertimes.time_to_next_prayer() 131 | 132 | # Update displayed prayer 133 | if (nextprayer != self.currentprayer) and self.silaty.is_visible(): 134 | self.silaty.homebox.emit("prayers-updated", _(nextprayer)) 135 | self.currentprayer = nextprayer 136 | 137 | self.NextPrayerItem.set_label(_("%s until %s") % (self.secs_to_hrtime(tonextprayer.seconds), _(nextprayer))) 138 | self.silaty.headerbar.set_title(_("%s until %s") % (self.secs_to_hrtime(tonextprayer.seconds), _(nextprayer))) 139 | self.Indicator.set_title(_("%s in %s") % (_(nextprayer), (self.secs_to_nrtime(tonextprayer.seconds)))) 140 | 141 | if self.silaty.prayertimes.options.iconlabel == True: 142 | self.Indicator.set_label(_("%s in %s") % (_(nextprayer), (self.secs_to_nrtime(tonextprayer.seconds))),"") 143 | else: 144 | self.Indicator.set_label("","") 145 | return True 146 | 147 | def secs_to_hrtime(self, secs): 148 | # Transform Seconds into Hours and Minutes 149 | hours = secs//3600 150 | minutes = (secs//60)%60 151 | minutes += 1 # correct minutes (to avoid values like "0min") 152 | if minutes == 60: 153 | hours += 1 154 | return _("%s Hours") % str(hours) 155 | elif hours == 0: 156 | return _("%s Minutes") % str(minutes) 157 | else: 158 | return _("%s Hours and %s Minutes") % (str(hours), str(minutes)) 159 | 160 | def secs_to_nrtime(self, secs): 161 | # Transform Seconds into Hours and Minutes 162 | # Using the same standard in iPray 163 | hours = secs//3600 164 | minutes = (secs//60)%60 165 | minutes += 1 # correct minutes (to avoid values like "0min") 166 | if minutes == 60: 167 | hours += 1 168 | return _("%shr") % str(hours) 169 | elif hours == 0: 170 | return _("%smin") % str(minutes) 171 | else: 172 | return _("%shr %smin") % (str(hours), str(minutes)) 173 | 174 | def icon(self): 175 | # Get Icon 176 | print ("DEBUG: getting Icons @", (str(datetime.datetime.now()))) 177 | PathDir = os.path.dirname(os.path.realpath(__file__)) + "/icons/hicolor/scalable/silaty-indicator.svg" 178 | #print (PathDir) 179 | 180 | if os.path.exists(PathDir): 181 | print ("DEBUG: icon found in the OS @", (str(datetime.datetime.now()))) 182 | return PathDir 183 | 184 | else: 185 | print ("ERROR: Cannot find icon : silaty-indicator.svg @ %s" % (str(datetime.datetime.now())), file=sys.stderr) 186 | print ("DEBUG: silaty-indicator QUITING @", (str(datetime.datetime.now()))) 187 | sys.exit(1) 188 | 189 | def get_hijri_date(self): 190 | wd = datetime.datetime.now().strftime("%A") 191 | calc = HijriCal(self.silaty.prayertimes.options.hijrical_adjustment) 192 | h_months = ['Muharram ', 'Safar', 'Rabi al Awwal', 'Rabi al Akhira', 'Jumada al Ula', 'Jumada al Akhira', 'Rajab', "Sha'ban", 'Ramadan', 'Shawwal', "Dhu al Qa'da", 'Dhu al Hijja'] 193 | h_year, h_month, h_day, h_week_day = calc.today 194 | h_date = '%i %s %i' % ( h_day, _(h_months[int(h_month-1)]), h_year) 195 | return (_('%s, %s') % (_(wd), h_date)) 196 | 197 | def show_home(self, widget): 198 | self.show_window("home") 199 | 200 | def show_qibla(self, widget): 201 | self.show_window("qibla") 202 | 203 | def show_settings(self, widget, data): 204 | self.show_window("options") 205 | 206 | def show_window(self, active_tab_name): 207 | # Show main window 208 | #print ('DEBUG: window is visible: %s, active: %s' % (self.silaty.is_visible(), self.silaty.is_active())) 209 | if not self.silaty.is_visible(): 210 | self.silaty.show_all() 211 | elif not self.silaty.is_active(): 212 | self.silaty.present() 213 | # Set active tab 214 | current_tab_name = self.silaty.sidebar.stack.get_visible_child_name() 215 | if (current_tab_name != active_tab_name): 216 | # If another tab was activated before, set its state to OFF 217 | index = self.silaty.sidebar.stackchildnames.index(current_tab_name) 218 | self.silaty.sidebar.get_child(index).state = SideBarButtonState.OFF 219 | # Activate/show new tab 220 | self.silaty.sidebar.stack.set_visible_child_name(active_tab_name) 221 | self.silaty.sidebar.emit("window-shown") 222 | 223 | def about_dialog(self, widget, data=None):# The About Dialog 224 | print ("DEBUG: opening about dialog @", (str(datetime.datetime.now()))) 225 | about_dialog = Gtk.AboutDialog() 226 | if self.silaty.is_visible(): 227 | about_dialog.set_transient_for(self.silaty) 228 | else: 229 | about_dialog.set_position(Gtk.WindowPosition.CENTER) 230 | logo = GdkPixbuf.Pixbuf.new_from_file(os.path.dirname(os.path.realpath(__file__)) + "/icons/hicolor/48x48/apps/silaty.svg") 231 | about_dialog.set_logo(logo) 232 | about_dialog.set_program_name(_("Silaty")) 233 | about_dialog.set_website("https://github.com/AXeL-dev/Silaty") 234 | about_dialog.set_website_label(_("GitHub Project Page")) 235 | about_dialog.set_authors(["AXeL-dev (Maintainer)", "Jesse Wayde Brandão (Lead Developer)",\ 236 | "Mohamed Alaa (Developer)","Eslam Mostafa (Developer)",\ 237 | "Ahmed Youssef (Developer)"]) 238 | about_dialog.set_artists(["Mustapha Asbbar (Designer)"]) 239 | about_dialog.set_license('''Silaty, A Prayer Times Reminder Application. 240 | Copyright © 2021 Silaty Team 241 | 242 | This program is free software: you can redistribute it and/or modify 243 | it under the terms of the GNU General Public License as published by 244 | the Free Software Foundation, either version 3 of the License, or 245 | (at your option) any later version. 246 | 247 | This program is distributed in the hope that it will be useful, 248 | but WITHOUT ANY WARRANTY; without even the implied warranty of 249 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 250 | GNU General Public License for more details. 251 | 252 | You should have received a copy of the GNU General Public License 253 | along with this program. If not, see .''') 254 | about_dialog.set_version("1.5") 255 | about_dialog.set_comments(_("A neat Prayer Time Reminder App.\n Simple and complete so no prayer is missed")) 256 | about_dialog.set_copyright(_("Copyright © %s Silaty Team") % '2021') 257 | about_dialog.run() 258 | about_dialog.destroy() 259 | 260 | def quit(self, widget): 261 | self.silaty.prayertimes.options.save_options() 262 | self.silaty.destroy() 263 | Gtk.main_quit() 264 | 265 | def main(self): 266 | Gtk.main() 267 | print ("DEBUG: starting/stopping GTK @", (str(datetime.datetime.now()))) 268 | 269 | if __name__ == '__main__': 270 | ipm = SilatyIndicator() 271 | ipm.main() 272 | -------------------------------------------------------------------------------- /silaty.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Name=Silaty 4 | Comment=A neat prayer reminder application 5 | Exec=/usr/local/bin/silaty-indicator 6 | Icon=silaty 7 | Terminal=false 8 | StartupNotify=true 9 | Type=Application 10 | Categories=GNOME;GTK;Utility; 11 | Name[en_US]=Silaty -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: silaty # you probably want to 'snapcraft register ' 2 | base: core18 # the base snap is the execution environment for this snap 3 | version: '1.4' # just for humans, typically '1.2+git' or '1.3.2' 4 | summary: A neat prayer reminder app # 79 char long summary 5 | description: | 6 | A neat prayer reminder app 7 | 8 | grade: stable # must be 'stable' to release into candidate/stable channels 9 | confinement: strict # use 'strict' once you have the right plugs and slots 10 | 11 | parts: 12 | silaty: 13 | plugin: python 14 | python-version: python3 15 | source: . 16 | build-packages: 17 | - python3-pip 18 | - libpulse-dev 19 | stage-packages: 20 | - gir1.2-gtk-3.0 21 | - gir1.2-appindicator3-0.1 22 | - gir1.2-notify-0.7 23 | - libgstreamer1.0-0 24 | - gstreamer1.0-plugins-base 25 | - gstreamer1.0-plugins-good 26 | - gstreamer1.0-pulseaudio 27 | - python3-gst-1.0 28 | - libpulse0 29 | - libglu1-mesa 30 | - freeglut3 31 | - libgpm2 32 | - libslang2 33 | python-packages: 34 | - setuptools 35 | - PyGObject 36 | - pip 37 | - wheel 38 | override-stage: | 39 | # copy project files 40 | cp -r $SNAPCRAFT_PART_BUILD/* $SNAPCRAFT_PART_INSTALL 41 | # copy icons 42 | cp $SNAPCRAFT_PART_BUILD/silaty-indicator.py $SNAPCRAFT_PART_INSTALL/silaty-indicator 43 | cp $SNAPCRAFT_PART_BUILD/icons/hicolor/128x128/apps/silaty.svg $SNAPCRAFT_PART_INSTALL/usr/share/icons/hicolor/scalable/apps/ 44 | cp $SNAPCRAFT_PART_BUILD/icons/hicolor/128x128/apps/silaty.svg $SNAPCRAFT_PART_INSTALL/usr/share/icons/hicolor/128x128/apps/ 45 | cp $SNAPCRAFT_PART_BUILD/icons/hicolor/48x48/apps/silaty.svg $SNAPCRAFT_PART_INSTALL/usr/share/icons/hicolor/48x48/apps/ 46 | cp $SNAPCRAFT_PART_BUILD/icons/hicolor/24x24/apps/silaty.svg $SNAPCRAFT_PART_INSTALL/usr/share/icons/hicolor/24x24/apps/ 47 | snapcraftctl stage 48 | 49 | apps: 50 | silaty: 51 | command: usr/bin/python3 $SNAP/silaty-indicator 52 | desktop: $SNAPCRAFT_PROJECT_DIR/silaty.desktop 53 | extensions: [gnome-3-34] 54 | plugs: 55 | - desktop 56 | - audio-playback 57 | slots: 58 | - dbus-daemon 59 | common-id: com.github.AXeL-dev.silaty 60 | 61 | slots: 62 | dbus-daemon: 63 | interface: dbus 64 | bus: session 65 | name: com.github.AXeL-dev.silaty -------------------------------------------------------------------------------- /translate.py: -------------------------------------------------------------------------------- 1 | # Silaty 2 | # Copyright (c) 2018 - 2021 AXeL 3 | # Copyright (c) 2014 - 2015 Jessewb786 4 | 5 | import os 6 | import json 7 | 8 | languages = { 9 | 'English': 'en.json', 10 | 'Arabic': 'ar.json', 11 | 'French': 'fr.json', 12 | 'Spanish': 'es.json', 13 | } 14 | language = 'English' 15 | translations = {} 16 | 17 | def set_language(lang): 18 | global language 19 | language = lang 20 | load_translations() 21 | 22 | def load_language_file(lang): 23 | try: 24 | return open(os.path.dirname(os.path.realpath(__file__)) + '/lang/' + languages[lang]) 25 | except FileNotFoundError: 26 | return open(os.path.dirname(os.path.realpath(__file__)) + '/lang/' + languages['English']) 27 | 28 | def load_translations(): 29 | global translations 30 | file = load_language_file(language) 31 | try: 32 | translations = json.load(file) 33 | except json.decoder.JSONDecodeError: 34 | translations = {} 35 | 36 | def translate_text(text): 37 | #print ("DEBUG: translate text '%s' in '%s'" % (text, language)) 38 | if text in translations: 39 | return translations[text] 40 | else: 41 | return text 42 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$(id -u)" != "0" ]; then 4 | echo “This script must be run as root” 2>&1 5 | exit 1 6 | fi 7 | 8 | rm -rf /usr/share/silaty 9 | rm -f /usr/share/applications/silaty.desktop 10 | rm -f /etc/xdg/autostart/silaty.desktop 11 | rm -f /usr/local/bin/silaty-indicator 12 | 13 | rm -f /usr/share/icons/hicolor/128x128/apps/silaty.svg 14 | rm -f /usr/share/icons/hicolor/48x48/apps/silaty.svg 15 | rm -f /usr/share/icons/hicolor/24x24/apps/silaty.svg 16 | rm -f /usr/share/icons/hicolor/scalable/apps/silaty.svg 17 | 18 | rm -f /usr/share/icons/hicolor/scalable/apps/silaty-indicator.svg --------------------------------------------------------------------------------