diff --git a/.gitignore b/.gitignore
index d0905e5..aa64aaa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
build
node_modules
-tests/_variants.js
+tests/**/_variants.js
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ae420cc..696cf22 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -34,6 +34,9 @@ before_script:
test:
stage: test
rules:
+ - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
+ - if: '$CI_COMMIT_TAG'
+ - if: '$CI_COMMIT_BRANCH == "master"'
- changes:
- src/
script:
diff --git a/.gitlab/issue_templates/Bug report.md b/.gitlab/issue_templates/Bug report.md
index 5a3a2b9..f771946 100644
--- a/.gitlab/issue_templates/Bug report.md
+++ b/.gitlab/issue_templates/Bug report.md
@@ -2,10 +2,7 @@
- Distribution (name and version):
- GNOME Shell version:
-- I installed the extension:
- - [ ] Manually from the repository
- - [x] From extensions.gnome.org
- - [ ] From my distribution package manager (or it was preinstalled)
+- I installed the extension:
# My problem
@@ -17,4 +14,5 @@
1.
+
/label ~"bug::not confirmed"
diff --git a/.gitlab/issue_templates/Feature request.md b/.gitlab/issue_templates/Feature request.md
new file mode 100644
index 0000000..4857180
--- /dev/null
+++ b/.gitlab/issue_templates/Feature request.md
@@ -0,0 +1,5 @@
+
+
+
+
+/label ~"feature::request"
diff --git a/.gitlab/issue_templates/Suggestion.md b/.gitlab/issue_templates/Suggestion.md
index be5e376..c79d3d8 100644
--- a/.gitlab/issue_templates/Suggestion.md
+++ b/.gitlab/issue_templates/Suggestion.md
@@ -1,3 +1,4 @@
+
/label ~discussion
diff --git a/.gitlab/issue_templates/Theme request.md b/.gitlab/issue_templates/Theme request.md
index 98c2834..5bb3448 100644
--- a/.gitlab/issue_templates/Theme request.md
+++ b/.gitlab/issue_templates/Theme request.md
@@ -1,4 +1,5 @@
- Theme name:
- Theme repository or download link:
-/label ~theme
+
+/label ~"theme::request"
diff --git a/Makefile b/Makefile
index 794cd9a..99efa37 100644
--- a/Makefile
+++ b/Makefile
@@ -29,8 +29,10 @@ build: build-clean
gnome-extensions pack \
--extra-source=../LICENSE \
--extra-source=./config.js \
+ --extra-source=./convenience.js \
--extra-source=./utils.js \
--extra-source=./modules/ \
+ --extra-source=./preferences/ \
--extra-source=./schemas/ \
--podir=./po/ \
--gettext-domain=$(UUID) \
@@ -54,7 +56,8 @@ clean: build-clean deps-clean
.PHONY: test
test:
- cat ./src/modules/Variants.js ./tests/_variants.js.template > ./tests/_variants.js
+ cat ./src/modules/GtkVariants.js ./tests/gtk_themes/_variants.js.template > ./tests/gtk_themes/_variants.js
+ cat ./src/modules/ShellVariants.js ./tests/shell_themes/_variants.js.template > ./tests/shell_themes/_variants.js
npm run test
.PHONY: deps-install
diff --git a/README.md b/README.md
index d4a08b5..916ea53 100644
--- a/README.md
+++ b/README.md
@@ -2,15 +2,15 @@

-Automatically toggle between your light and dark GTK theme variants.
+Automatically toggle your light and dark GTK and GNOME Shell theme variants, switch backgrounds and launch custom commands at sunset and sunrise.
Supports Night Light, Location Services and manual schedule.
-_Do you need to change your GNOME shell theme as well? Try [Night Shell Switcher](https://gitlab.com/rmnvgr/nightshellswitcher-gnome-shell-extension/)!_
-
## Theme compatibility
-These themes have been tested and work:
+These themes have been tested and work out of the box without any configuration. Other themes might work as well, let me know if there is a specific theme you'd like supported! And you can also manually set your day and night variants.
+
+### GTK themes
- Adapta
- Adwaita (GNOME & Fedora default)
@@ -37,7 +37,25 @@ These themes have been tested and work:
- Vimix
- Yaru (Ubuntu default)
-Other themes with a dark variant might work as well, let me know if there is a specific theme you'd like supported! And you can also manually set your day and night theme.
+### Shell themes
+
+- Adapta
+- Arc
+- Canta
+- ChromeOS
+- Flat-Remix
+- Kimi
+- Layan
+- Matcha (Manjaro default)
+- Materia
+- Mojave
+- Orchis
+- Plata (Solus default)
+- Pop (Pop!_OS default)
+- Qogir
+- Simply Circles
+- Teja
+- Vimix
## Graphical installation
diff --git a/ego_description.txt b/ego_description.txt
index 87e4687..2ebdbe2 100644
--- a/ego_description.txt
+++ b/ego_description.txt
@@ -1,34 +1,5 @@
-Night mode for GNOME apps! Automatically toggle between your light and dark GTK theme variants.
+Night mode for GNOME! Automatically toggle your light and dark GTK and GNOME Shell theme variants, switch backgrounds and launch custom commands at sunset and sunrise.
Supports Night Light, Location Services and manual schedule.
-These themes have been tested and work:
-- Adapta
-- Adwaita (GNOME & Fedora default)
-- Arc
-- Cabinet
-- Canta
-- ChromeOS
-- Flat-Remix-GTK
-- HighContrast
-- Kimi
-- Layan
-- Macwaita
-- Matcha (Manjaro default)
-- Materia
-- Mojave
-- Nextwaita
-- Orchis
-- Plata (Solus default)
-- Pop (Pop!_OS default)
-- Prof-Gnome-3
-- Qogir
-- Simply Circles
-- Teja
-- Vimix
-- Yaru (Ubuntu default)
-
-Other themes with a dark variant might work as well, let me know if there is a specific theme you'd like supported! And you can also manually set your day and night theme.
-
-If you need to change your GNOME shell theme as well, try Night Shell Switcher:
-https://extensions.gnome.org/extension/2356/night-shell-switcher/
+It works out of the box with numerous themes (see the list on the repository), and you can manually choose the variants you want.
diff --git a/package-lock.json b/package-lock.json
index dfc4c3c..a1c94d8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,14 +11,20 @@
"@babel/highlight": "^7.8.3"
}
},
+ "@babel/helper-validator-identifier": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+ "dev": true
+ },
"@babel/highlight": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
- "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
"dev": true,
"requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
"chalk": "^2.0.0",
- "esutils": "^2.0.2",
"js-tokens": "^4.0.0"
},
"dependencies": {
@@ -162,9 +168,9 @@
"dev": true
},
"@types/node": {
- "version": "13.7.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz",
- "integrity": "sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==",
+ "version": "14.0.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz",
+ "integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==",
"dev": true
},
"@types/normalize-package-data": {
@@ -173,6 +179,18 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
+ "acorn": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
+ "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==",
+ "dev": true
+ },
+ "acorn-walk": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz",
+ "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==",
+ "dev": true
+ },
"aggregate-error": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
@@ -298,20 +316,23 @@
"dev": true
},
"ava": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/ava/-/ava-3.3.0.tgz",
- "integrity": "sha512-RaH6jpiS/9ZdIuP1QkyhHvhprk0h38LVdkkpL7Mw25XR3z/6w1N3Nq9O2qm3awnw38CfIztmipzkv+671nWH2w==",
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/ava/-/ava-3.8.2.tgz",
+ "integrity": "sha512-sph3oUsVTGsq4qbgeWys03QKCmXjkZUO3oPnFWXEW6g1SReCY9vuONGghMgw1G6VOzkg1k+niqJsOzwfO8h9Ng==",
"dev": true,
"requires": {
"@concordance/react": "^2.0.0",
+ "acorn": "^7.1.1",
+ "acorn-walk": "^7.1.1",
"ansi-styles": "^4.2.1",
"arrgv": "^1.0.2",
"arrify": "^2.0.1",
- "chalk": "^3.0.0",
- "chokidar": "^3.3.1",
+ "callsites": "^3.1.0",
+ "chalk": "^4.0.0",
+ "chokidar": "^3.4.0",
"chunkd": "^2.0.1",
+ "ci-info": "^2.0.0",
"ci-parallel-vars": "^1.0.0",
- "clean-stack": "^2.2.0",
"clean-yaml-object": "^0.1.0",
"cli-cursor": "^3.1.0",
"cli-truncate": "^2.1.0",
@@ -322,39 +343,39 @@
"currently-unhandled": "^0.4.1",
"debug": "^4.1.1",
"del": "^5.1.0",
- "emittery": "^0.5.1",
+ "emittery": "^0.6.0",
"equal-length": "^1.0.0",
- "figures": "^3.1.0",
+ "figures": "^3.2.0",
"globby": "^11.0.0",
"ignore-by-default": "^1.0.0",
"import-local": "^3.0.2",
"indent-string": "^4.0.0",
- "is-ci": "^2.0.0",
"is-error": "^2.2.2",
"is-plain-object": "^3.0.0",
- "is-promise": "^2.1.0",
+ "is-promise": "^4.0.0",
"lodash": "^4.17.15",
- "matcher": "^2.1.0",
+ "matcher": "^3.0.0",
"md5-hex": "^3.0.1",
+ "mem": "^6.1.0",
"ms": "^2.1.2",
- "ora": "^4.0.3",
- "p-map": "^3.0.0",
- "picomatch": "^2.2.1",
+ "ora": "^4.0.4",
+ "p-map": "^4.0.0",
+ "picomatch": "^2.2.2",
"pkg-conf": "^3.1.0",
- "plur": "^3.1.1",
- "pretty-ms": "^5.1.0",
+ "plur": "^4.0.0",
+ "pretty-ms": "^7.0.0",
"read-pkg": "^5.2.0",
"resolve-cwd": "^3.0.0",
"slash": "^3.0.0",
- "source-map-support": "^0.5.16",
- "stack-utils": "^2.0.1",
+ "source-map-support": "^0.5.19",
+ "stack-utils": "^2.0.2",
"strip-ansi": "^6.0.0",
"supertap": "^1.0.0",
"temp-dir": "^2.0.0",
"trim-off-newlines": "^1.0.1",
- "update-notifier": "^4.0.0",
- "write-file-atomic": "^3.0.1",
- "yargs": "^15.1.0"
+ "update-notifier": "^4.1.0",
+ "write-file-atomic": "^3.0.3",
+ "yargs": "^15.3.1"
}
},
"balanced-match": {
@@ -370,9 +391,9 @@
"dev": true
},
"blueimp-md5": {
- "version": "2.12.0",
- "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.12.0.tgz",
- "integrity": "sha512-zo+HIdIhzojv6F1siQPqPFROyVy7C50KzHv/k/Iz+BtvtVzSHXiMXOpq2wCfNkeBqdCv+V8XOV96tsEt2W/3rQ==",
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.16.0.tgz",
+ "integrity": "sha512-j4nzWIqEFpLSbdhUApHRGDwfXbV8ALhqOn+FY5L6XBdKPAXU9BpGgFSbDsgqogfqPPR9R2WooseWCsfhfEC6uQ==",
"dev": true
},
"boxen": {
@@ -391,6 +412,16 @@
"widest-line": "^3.1.0"
},
"dependencies": {
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
@@ -456,6 +487,12 @@
}
}
},
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -463,9 +500,9 @@
"dev": true
},
"chalk": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
- "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
+ "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -473,9 +510,9 @@
}
},
"chokidar": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
- "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
+ "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
@@ -485,7 +522,7 @@
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
- "readdirp": "~3.3.0"
+ "readdirp": "~3.4.0"
}
},
"chunkd": {
@@ -534,9 +571,9 @@
}
},
"cli-spinners": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz",
- "integrity": "sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz",
+ "integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==",
"dev": true
},
"cli-truncate": {
@@ -770,6 +807,15 @@
"merge2": "^1.2.3",
"slash": "^3.0.0"
}
+ },
+ "p-map": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
+ "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
}
}
},
@@ -798,9 +844,9 @@
"dev": true
},
"emittery": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.5.1.tgz",
- "integrity": "sha512-sYZXNHH9PhTfs98ROEFVC3bLiR8KSqXQsEHIwZ9J6H0RaQObC3JYq4G8IvDd0b45/LxfGKYBpmaUN4LiKytaNw==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.6.0.tgz",
+ "integrity": "sha512-6EMRGr9KzYWp8DzHFZsKVZBsMO6QhAeHMeHND8rhyBNCHKMLpgW9tZv40bwN3rAIKRS5CxcK8oLRKUJSB9h7yQ==",
"dev": true
},
"emoji-regex": {
@@ -833,6 +879,12 @@
"is-arrayish": "^0.2.1"
}
},
+ "escape-goat": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
+ "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==",
+ "dev": true
+ },
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -858,31 +910,32 @@
"dev": true
},
"fast-glob": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz",
- "integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==",
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz",
+ "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==",
"dev": true,
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.0",
"merge2": "^1.3.0",
- "micromatch": "^4.0.2"
+ "micromatch": "^4.0.2",
+ "picomatch": "^2.2.1"
}
},
"fastq": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz",
- "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
+ "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
"dev": true,
"requires": {
- "reusify": "^1.0.0"
+ "reusify": "^1.0.4"
}
},
"figures": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
- "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
"dev": true,
"requires": {
"escape-string-regexp": "^1.0.5"
@@ -914,9 +967,9 @@
"dev": true
},
"fsevents": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
- "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"dev": true,
"optional": true
},
@@ -950,9 +1003,9 @@
}
},
"glob-parent": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
- "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
@@ -1001,9 +1054,9 @@
}
},
"graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"has-flag": {
@@ -1019,21 +1072,21 @@
"dev": true
},
"hosted-git-info": {
- "version": "2.8.5",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
- "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==",
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
"http-cache-semantics": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
- "integrity": "sha512-Z2EICWNJou7Tr9Bd2M2UqDJq3A9F2ePG9w3lIpjoyuSyXFP9QbniJVu3XQYytuw5ebmG7dXSXO9PgAjJG8DDKA==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"dev": true
},
"ignore": {
- "version": "5.1.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
- "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.6.tgz",
+ "integrity": "sha512-cgXgkypZBcCnOgSihyeqbo6gjIaIyDqPQB7Ra4vhE9m6kigdGoQDMHjviFhRZo3IMlRy6yElosoviMs5YxZXUA==",
"dev": true
},
"ignore-by-default": {
@@ -1093,9 +1146,9 @@
"dev": true
},
"irregular-plurals": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz",
- "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.2.0.tgz",
+ "integrity": "sha512-YqTdPLfwP7YFN0SsD3QUVCkm9ZG2VzOXv3DOrw5G5mkMbVwptTwVcFv7/C0vOpBmgTxAeTG19XpUs1E522LW9Q==",
"dev": true
},
"is-arrayish": {
@@ -1150,9 +1203,9 @@
}
},
"is-installed-globally": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.1.tgz",
- "integrity": "sha512-oiEcGoQbGc+3/iijAijrK2qFpkNoNjsHOm/5V5iaeydyrS/hnwaRCEgH5cpW0P3T1lSjV5piB7S5b5lEugNLhg==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz",
+ "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==",
"dev": true,
"requires": {
"global-dirs": "^2.0.1",
@@ -1205,9 +1258,9 @@
}
},
"is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
- "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"dev": true
},
"is-typedarray": {
@@ -1241,9 +1294,9 @@
"dev": true
},
"js-yaml": {
- "version": "3.13.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
- "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@@ -1406,9 +1459,9 @@
"dev": true
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
@@ -1422,19 +1475,28 @@
}
}
},
- "matcher": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.1.0.tgz",
- "integrity": "sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ==",
+ "map-age-cleaner": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
"dev": true,
"requires": {
- "escape-string-regexp": "^2.0.0"
+ "p-defer": "^1.0.0"
+ }
+ },
+ "matcher": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
+ "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^4.0.0"
},
"dependencies": {
"escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true
}
}
@@ -1454,6 +1516,24 @@
"integrity": "sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=",
"dev": true
},
+ "mem": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-6.1.0.tgz",
+ "integrity": "sha512-RlbnLQgRHk5lwqTtpEkBTQ2ll/CG/iB+J4Hy2Wh97PjgZgXgWJWrFF+XXujh3UUVLvR4OOTgZzcWMMwnehlEUg==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "^0.1.3",
+ "mimic-fn": "^3.0.0"
+ },
+ "dependencies": {
+ "mimic-fn": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.0.0.tgz",
+ "integrity": "sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==",
+ "dev": true
+ }
+ }
+ },
"merge2": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz",
@@ -1492,9 +1572,9 @@
}
},
"minimist": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"ms": {
@@ -1552,9 +1632,9 @@
}
},
"ora": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.3.tgz",
- "integrity": "sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-4.0.4.tgz",
+ "integrity": "sha512-77iGeVU1cIdRhgFzCK8aw1fbtT1B/iZAvWjS+l/o1x0RShMgxHUZaD2yDpWsNCPwXg9z1ZA78Kbdvr8kBmG/Ww==",
"dev": true,
"requires": {
"chalk": "^3.0.0",
@@ -1565,6 +1645,18 @@
"mute-stream": "0.0.8",
"strip-ansi": "^6.0.0",
"wcwidth": "^1.0.1"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ }
}
},
"p-cancelable": {
@@ -1573,10 +1665,16 @@
"integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
"dev": true
},
+ "p-defer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+ "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+ "dev": true
+ },
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
@@ -1592,9 +1690,9 @@
}
},
"p-map": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
- "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
"requires": {
"aggregate-error": "^3.0.0"
@@ -1667,9 +1765,9 @@
"dev": true
},
"picomatch": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
- "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"dev": true
},
"pify": {
@@ -1734,12 +1832,12 @@
}
},
"plur": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz",
- "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz",
+ "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==",
"dev": true,
"requires": {
- "irregular-plurals": "^2.0.0"
+ "irregular-plurals": "^3.2.0"
}
},
"prepend-http": {
@@ -1749,9 +1847,9 @@
"dev": true
},
"pretty-ms": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-5.1.0.tgz",
- "integrity": "sha512-4gaK1skD2gwscCfkswYQRmddUb2GJZtzDGRjHWadVHtK/DIKFufa12MvES6/xu1tVbUYeia5bmLcwJtZJQUqnw==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.0.tgz",
+ "integrity": "sha512-J3aPWiC5e9ZeZFuSeBraGxSkGMOvulSWsxDByOcbD1Pr75YL3LSNIKIb52WXbCLE1sS5s4inBBbryjF4Y05Ceg==",
"dev": true,
"requires": {
"parse-ms": "^2.1.0"
@@ -1767,6 +1865,15 @@
"once": "^1.3.1"
}
},
+ "pupa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz",
+ "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==",
+ "dev": true,
+ "requires": {
+ "escape-goat": "^2.0.0"
+ }
+ },
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -1812,12 +1919,12 @@
}
},
"readdirp": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
- "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
"dev": true,
"requires": {
- "picomatch": "^2.0.7"
+ "picomatch": "^2.2.1"
}
},
"registry-auth-token": {
@@ -1851,9 +1958,9 @@
"dev": true
},
"resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
@@ -1956,9 +2063,9 @@
"dev": true
},
"signal-exit": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
"dev": true
},
"slash": {
@@ -1985,9 +2092,9 @@
"dev": true
},
"source-map-support": {
- "version": "0.5.16",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
- "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@@ -1995,9 +2102,9 @@
}
},
"spdx-correct": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
- "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0",
@@ -2005,15 +2112,15 @@
}
},
"spdx-exceptions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
- "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
},
"spdx-expression-parse": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
- "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
@@ -2033,9 +2140,9 @@
"dev": true
},
"stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-BvBTnHGm8boe+HiJFqP19ywEsGlfQAKqW78pbfvUuzCbUuxPPUyLrH5dYFY+Xn9IpLY3b5ZmMcl8jAqXB4wddg==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz",
+ "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==",
"dev": true,
"requires": {
"escape-string-regexp": "^2.0.0"
@@ -2196,14 +2303,14 @@
}
},
"update-notifier": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.0.0.tgz",
- "integrity": "sha512-p9zf71hWt5GVXM4iEBujpUgx8mK9AWiCCapEJm/O1z5ntCim83Z1ATqzZFBHFYqx03laMqv8LiDgs/7ikXjf/g==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz",
+ "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==",
"dev": true,
"requires": {
"boxen": "^4.2.0",
"chalk": "^3.0.0",
- "configstore": "^5.0.0",
+ "configstore": "^5.0.1",
"has-yarn": "^2.1.0",
"import-lazy": "^2.1.0",
"is-ci": "^2.0.0",
@@ -2211,8 +2318,21 @@
"is-npm": "^4.0.0",
"is-yarn-global": "^0.3.0",
"latest-version": "^5.0.0",
+ "pupa": "^2.0.1",
"semver-diff": "^3.1.1",
"xdg-basedir": "^4.0.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ }
}
},
"url-parse-lax": {
@@ -2282,9 +2402,9 @@
"dev": true
},
"write-file-atomic": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz",
- "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
+ "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
"dev": true,
"requires": {
"imurmurhash": "^0.1.4",
@@ -2306,9 +2426,9 @@
"dev": true
},
"yargs": {
- "version": "15.1.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz",
- "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==",
+ "version": "15.3.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
+ "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
"dev": true,
"requires": {
"cliui": "^6.0.0",
@@ -2321,13 +2441,13 @@
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
- "yargs-parser": "^16.1.0"
+ "yargs-parser": "^18.1.1"
}
},
"yargs-parser": {
- "version": "16.1.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz",
- "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==",
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
diff --git a/package.json b/package.json
index dd9404f..0a5c979 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,6 @@
"test": "ava"
},
"devDependencies": {
- "ava": "^3.3.0"
+ "ava": "^3.8.2"
}
}
diff --git a/screenshot.png b/screenshot.png
index dc2c051..84ddf77 100644
Binary files a/screenshot.png and b/screenshot.png differ
diff --git a/src/config.js b/src/config.js
index ccf6a17..c0ddd31 100644
--- a/src/config.js
+++ b/src/config.js
@@ -16,68 +16,4 @@ You should have received a copy of the GNU General Public License along with
this program. If not, see .
*/
-var THEME_GSETTINGS_SCHEMA = 'org.gnome.desktop.interface';
-var THEME_GSETTINGS_PROPERTY = 'gtk-theme';
-
-var NIGHTLIGHT_GSETTINGS_SCHEMA = 'org.gnome.settings-daemon.plugins.color';
-var NIGHTLIGHT_GSETTINGS_PROPERTY = 'night-light-enabled';
-
-var LOCATION_GSETTINGS_SCHEMA = 'org.gnome.system.location';
-var LOCATION_GSETTINGS_PROPERTY = 'enabled';
-
-// GeoClue2 interfaces from https://gitlab.freedesktop.org/geoclue/geoclue/
-var GEOCLUE_MANAGER_INTERFACE = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
-var GEOCLUE_CLIENT_INTERFACE = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
-var GEOCLUE_LOCATION_INTERFACE = `
-
-
-
-
-
-
-
-
-
-
-
-`;
-
var debug = false;
diff --git a/src/convenience.js b/src/convenience.js
new file mode 100644
index 0000000..25ba4b0
--- /dev/null
+++ b/src/convenience.js
@@ -0,0 +1,91 @@
+/*
+ Copyright (c) 2011-2012, Giovanni Campagna
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the GNOME nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+const Gettext = imports.gettext;
+const Gio = imports.gi.Gio;
+
+const Config = imports.misc.config;
+const ExtensionUtils = imports.misc.extensionUtils;
+
+/**
+ * initTranslations:
+ * @domain: (optional): the gettext domain to use
+ *
+ * Initialize Gettext to load translations from extensionsdir/locale.
+ * If @domain is not provided, it will be taken from metadata['gettext-domain']
+ */
+function initTranslations(domain) {
+ let extension = ExtensionUtils.getCurrentExtension();
+
+ domain = domain || extension.metadata['gettext-domain'];
+
+ // check if this extension was built with "make zip-file", and thus
+ // has the locale files in a subfolder
+ // otherwise assume that extension has been installed in the
+ // same prefix as gnome-shell
+ let localeDir = extension.dir.get_child('locale');
+ if (localeDir.query_exists(null))
+ Gettext.bindtextdomain(domain, localeDir.get_path());
+ else
+ Gettext.bindtextdomain(domain, Config.LOCALEDIR);
+}
+
+/**
+ * getSettings:
+ * @schema: (optional): the GSettings schema id
+ *
+ * Builds and return a GSettings schema for @schema, using schema files
+ * in extensionsdir/schemas. If @schema is not provided, it is taken from
+ * metadata['settings-schema'].
+ */
+function getSettings(schema) {
+ let extension = ExtensionUtils.getCurrentExtension();
+
+ schema = schema || extension.metadata['settings-schema'];
+
+ const GioSSS = Gio.SettingsSchemaSource;
+
+ // check if this extension was built with "make zip-file", and thus
+ // has the schema files in a subfolder
+ // otherwise assume that extension has been installed in the
+ // same prefix as gnome-shell (and therefore schemas are available
+ // in the standard folders)
+ let schemaDir = extension.dir.get_child('schemas');
+ let schemaSource;
+ if (schemaDir.query_exists(null))
+ schemaSource = GioSSS.new_from_directory(schemaDir.get_path(),
+ GioSSS.get_default(),
+ false);
+ else
+ schemaSource = GioSSS.get_default();
+
+ let schemaObj = schemaSource.lookup(schema, true);
+ if (!schemaObj)
+ throw new Error('Schema ' + schema + ' could not be found for extension '
+ + extension.metadata.uuid + '. Please check your installation.');
+
+ return new Gio.Settings({ settings_schema: schemaObj });
+}
diff --git a/src/extension.js b/src/extension.js
index dd2c5de..10d81b4 100644
--- a/src/extension.js
+++ b/src/extension.js
@@ -1,7 +1,7 @@
/*
Night Theme Switcher Gnome Shell extension
-Copyright (C) 2019, 2020 Romain Vigier
+Copyright (C) 2020 Romain Vigier
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -19,11 +19,94 @@ this program. If not, see .
'use strict';
const { extensionUtils } = imports.misc;
+const { main } = imports.ui;
const Me = extensionUtils.getCurrentExtension();
-const { Switcher } = Me.imports.modules.Switcher;
+
+const { log_debug } = Me.imports.utils;
+const { SettingsManager } = Me.imports.modules.SettingsManager;
+const { Timer } = Me.imports.modules.Timer;
+const { GtkThemer } = Me.imports.modules.GtkThemer;
+const { ShellThemer } = Me.imports.modules.ShellThemer;
+const { Backgrounder } = Me.imports.modules.Backgrounder;
+const { Commander } = Me.imports.modules.Commander;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.initTranslations = Me.imports.convenience.initTranslations;
+}
+
+
+var enabled = false;
+var settingsManager = null;
+var timer = null;
+var gtkThemer = null;
+var shellThemer = null;
+var backgrounder = null;
+var commander = null;
function init() {
- return new Switcher();
+ log_debug('Initializing extension...');
+ extensionUtils.initTranslations(Me.metadata.uuid);
+ log_debug('Extension initialized.');
+}
+
+function enable() {
+ this._await_extensionManager_init().then(() => {
+ log_debug('Enabling extension...');
+ settingsManager = new SettingsManager();
+ timer = new Timer();
+ gtkThemer = new GtkThemer();
+ shellThemer = new ShellThemer();
+ backgrounder = new Backgrounder();
+ commander = new Commander();
+
+ settingsManager.enable();
+ timer.enable();
+ gtkThemer.enable();
+ shellThemer.enable();
+ backgrounder.enable();
+ commander.enable();
+
+ enabled = true;
+ log_debug('Extension enabled.');
+ });
+}
+
+function disable() {
+ log_debug('Disabling extension...');
+ enabled = false;
+
+ gtkThemer.disable();
+ shellThemer.disable();
+ backgrounder.disable();
+ commander.disable();
+ timer.disable();
+ settingsManager.disable();
+
+ settingsManager = null;
+ timer = null;
+ gtkThemer = null;
+ shellThemer = null;
+ backgrounder = null;
+ commander = null;
+ log_debug('Extension disabled.');
+}
+
+async function _await_extensionManager_init() {
+ log_debug('Waiting for the Extension Manager to be initialized...');
+ if ( shell_minor_version > 32 ) {
+ while ( true ) {
+ if ( main.extensionManager._initialized ) return;
+ await null;
+ }
+ }
+ else {
+ while ( true ) {
+ if ( imports.ui.extensionSystem.initted ) return;
+ await null;
+ }
+ }
}
diff --git a/src/metadata.json b/src/metadata.json
index 288a2df..8709a0d 100644
--- a/src/metadata.json
+++ b/src/metadata.json
@@ -1,10 +1,10 @@
{
"name": "Night Theme Switcher",
- "description": "Automatically toggle between your light and dark GTK theme variants.",
+ "description": "Automatically toggle your light and dark GTK and GNOME Shell theme variants, switch backgrounds and launch custom commands at sunset and sunrise.",
"uuid": "nightthemeswitcher@romainvigier.fr",
"gettext-domain": "nightthemeswitcher@romainvigier.fr",
"settings-schema": "org.gnome.shell.extensions.nightthemeswitcher",
"url": "https://gitlab.com/rmnvgr/nightthemeswitcher-gnome-shell-extension/",
- "shell-version": ["3.32", "3.34", "3.36"],
- "version": 29
+ "shell-version": ["3.28", "3.30", "3.32", "3.34", "3.36"],
+ "version": 30
}
diff --git a/src/modules/Backgrounder.js b/src/modules/Backgrounder.js
new file mode 100644
index 0000000..1c3ccf0
--- /dev/null
+++ b/src/modules/Backgrounder.js
@@ -0,0 +1,131 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { GLib } = imports.gi;
+const { extensionUtils } = imports.misc;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const e = Me.imports.extension;
+const { log_debug } = Me.imports.utils;
+
+/**
+ * The Backgrounder is responsible for changing the desktop background
+ * according to the time.
+ *
+ * When the user changes its desktop background (for example via the system
+ * settings), it will use it as the current time background.
+ */
+var Backgrounder = class {
+
+ enable() {
+ log_debug('Enabling Backgrounder...');
+ this._watch_status();
+ if ( e.settingsManager.backgrounds_enabled ) {
+ this._change_background(e.timer.time);
+ this._connect_settings();
+ this._connect_timer();
+ }
+ log_debug('Backgrounder enabled.');
+ }
+
+ disable() {
+ log_debug('Disabling Backgrounder...');
+ this._disconnect_timer();
+ this._disconnect_settings();
+ this._unwatch_status();
+ log_debug('Backgrounder disabled.');
+ }
+
+
+ _watch_status() {
+ log_debug('Watching backgrounds status...');
+ this._backgrounds_status_changed_connect = e.settingsManager.connect('backgrounds-status-changed', this._on_backgrounds_status_changed.bind(this));
+ }
+
+ _unwatch_status() {
+ if ( this._backgrounds_status_changed_connect ) {
+ e.settingsManager.disconnect(this._backgrounds_status_changed_connect);
+ this._backgrounds_status_changed_connect = null;
+ }
+ log_debug('Stopped watching backgrounds status.');
+ }
+
+ _connect_settings() {
+ log_debug('Connecting Backgrounder to settings...');
+ this._background_time_changed_connect = e.settingsManager.connect('background-time-changed', this._on_background_time_changed.bind(this));
+ this._background_changed_connect = e.settingsManager.connect('background-changed', this._on_background_changed.bind(this));
+ }
+
+ _disconnect_settings() {
+ if ( this._background_time_changed_connect ) {
+ e.settingsManager.disconnect(this._background_time_changed_connect);
+ this._background_time_changed_connect = null;
+ }
+ if ( this._background_changed_connect ) {
+ e.settingsManager.disconnect(this._background_changed_connect);
+ this._background_changed_connect = null;
+ }
+ log_debug('Disconnected Backgrounder from settings.');
+ }
+
+ _connect_timer() {
+ log_debug('Connecting Backgrounder to Timer...');
+ this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
+ }
+
+ _disconnect_timer() {
+ if ( this._time_changed_connect ) {
+ e.timer.disconnect(this._time_changed_connect);
+ this._time_changed_connect = null;
+ }
+ log_debug('Disconnecting Backgrounder from Timer.');
+ }
+
+
+ _on_backgrounds_status_changed(settings, enabled) {
+ this.disable();
+ this.enable();
+ }
+
+ _on_background_time_changed(settings, changed_background_time) {
+ if ( changed_background_time === e.timer.time ) {
+ this._change_background(changed_background_time);
+ }
+ }
+
+ _on_background_changed(settings, new_background) {
+ switch (e.timer.time) {
+ case 'day':
+ e.settingsManager.background_day = new_background;
+ break;
+ case 'night':
+ e.settingsManager.background_night = new_background;
+ }
+ }
+
+ _on_time_changed(timer, new_time) {
+ this._change_background(new_time);
+ }
+
+
+ _change_background(time) {
+ e.settingsManager.background = time === 'day' ? e.settingsManager.background_day : e.settingsManager.background_night;
+ }
+
+}
diff --git a/src/modules/Commander.js b/src/modules/Commander.js
new file mode 100644
index 0000000..9a66383
--- /dev/null
+++ b/src/modules/Commander.js
@@ -0,0 +1,93 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { GLib } = imports.gi;
+const { extensionUtils } = imports.misc;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const e = Me.imports.extension;
+const { log_debug } = Me.imports.utils;
+
+
+/**
+ * The Commander is responsible for spawning commands according to the time.
+ */
+var Commander = class {
+
+ enable() {
+ log_debug('Enabling Commander...');
+ this._watch_status();
+ if ( e.settingsManager.commands_enabled ) {
+ this._connect_timer();
+ }
+ log_debug('Commander enabled.');
+ }
+
+ disable() {
+ log_debug('Disabling Commander...');
+ this._disconnect_timer();
+ this._unwatch_status();
+ log_debug('Commander disabled.');
+ }
+
+
+ _watch_status() {
+ log_debug('Watching commands status...');
+ this._commands_status_changed_connect = e.settingsManager.connect('commands-status-changed', this._on_commands_status_changed.bind(this));
+ }
+
+ _unwatch_status() {
+ if ( this._commands_status_changed_connect ) {
+ e.settingsManager.disconnect(this._commands_status_changed_connect);
+ this._commands_status_changed_connect = null;
+ }
+ log_debug('Stopped watching commands status.');
+ }
+
+ _connect_timer() {
+ log_debug('Connecting Commander to Timer...');
+ this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
+ }
+
+ _disconnect_timer() {
+ if ( this._time_changed_connect ) {
+ e.timer.disconnect(this._time_changed_connect);
+ this._time_changed_connect = null;
+ }
+ log_debug('Disconnecting Commander from Timer.');
+ }
+
+
+ _on_commands_status_changed(settings, enabled) {
+ this.disable();
+ this.enable();
+ }
+
+ _on_time_changed(timer, new_time) {
+ this._spawn_command(new_time);
+ }
+
+
+ _spawn_command(time) {
+ const command = time === 'day' ? e.settingsManager.command_sunrise : e.settingsManager.command_sunset;
+ GLib.spawn_async(null, ['sh', '-c', command], null, GLib.SpawnFlags.SEARCH_PATH, null);
+ log_debug(`Spawned ${time} command.`);
+ }
+
+}
diff --git a/src/modules/GtkThemer.js b/src/modules/GtkThemer.js
new file mode 100644
index 0000000..a371194
--- /dev/null
+++ b/src/modules/GtkThemer.js
@@ -0,0 +1,171 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio, GLib, Gtk } = imports.gi;
+const { extensionUtils } = imports.misc;
+const { main } = imports.ui;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const e = Me.imports.extension;
+const { log_debug, log_error, get_installed_gtk_themes } = Me.imports.utils;
+const { GtkVariants } = Me.imports.modules.GtkVariants;
+
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
+
+
+/**
+ * The GTK Themer is responsible for changing the GTK theme according to the
+ * time.
+ *
+ * When the user changes its GTK theme (for example via GNOME Tweaks), it will
+ * try to automatically guess the day and night variants for this theme. It
+ * will warn the user if it is unable to guess.
+ *
+ * In manual mode, it will not attempt to update the variants. The user can
+ * change the GTK variants in the extension's preferences.
+ */
+var GtkThemer = class {
+
+ enable() {
+ log_debug('Enabling GTK Themer...');
+ try {
+ this._update_variants();
+ this._set_variant(e.timer.time);
+ this._connect_settings();
+ this._connect_timer();
+ }
+ catch(e) {
+ log_error(e);
+ }
+ log_debug('GTK Themer enabled.');
+ }
+
+ disable() {
+ log_debug('Disabling GTK Themer...');
+ this._disconnect_timer();
+ this._disconnect_settings();
+ this._reset_theme();
+ log_debug('GTK Themer disabled.');
+ }
+
+
+ _connect_settings() {
+ log_debug('Connecting GTK Themer to settings...');
+ this._gtk_variant_changed_connect = e.settingsManager.connect('gtk-variant-changed', this._on_gtk_variant_changed.bind(this));
+ this._gtk_theme_changed_connect = e.settingsManager.connect('gtk-theme-changed', this._on_gtk_theme_changed.bind(this));
+ }
+
+ _disconnect_settings() {
+ if ( this._gtk_variant_changed_connect ) {
+ e.settingsManager.disconnect(this._gtk_variant_changed_connect);
+ this._gtk_variant_changed_connect = null;
+ }
+ if ( this._gtk_theme_changed_connect ) {
+ e.settingsManager.disconnect(this._gtk_theme_changed_connect);
+ this._gtk_theme_changed_connect = null;
+ }
+ log_debug('Disconnected GTK Themer from settings.');
+ }
+
+ _connect_timer() {
+ log_debug('Connecting GTK Themer to Timer...');
+ this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
+ }
+
+ _disconnect_timer() {
+ if ( this._time_changed_connect ) {
+ e.timer.disconnect(this._time_changed_connect);
+ this._time_changed_connect = null;
+ }
+ log_debug('Disconnected GTK Themer from Timer.');
+ }
+
+
+ _on_gtk_variant_changed(settings, changed_variant_time) {
+ if ( changed_variant_time === e.timer.time ) {
+ this._set_variant(changed_variant_time);
+ }
+ }
+
+ _on_gtk_theme_changed(settings, new_theme) {
+ try {
+ this._update_variants();
+ this._set_variant(e.timer.time);
+ }
+ catch(e) {
+ log_error(e);
+ }
+ }
+
+ _on_time_changed(timer, new_time) {
+ this._set_variant(new_time);
+ }
+
+
+ _are_variants_up_to_date() {
+ return ( e.settingsManager.gtk_theme === e.settingsManager.gtk_variant_day || e.settingsManager.gtk_theme === e.settingsManager.gtk_variant_night );
+ }
+
+ _set_variant(time) {
+ log_debug(`Setting the GTK ${time} variant...`);
+ switch (time) {
+ case 'day':
+ e.settingsManager.gtk_theme = e.settingsManager.gtk_variant_day;
+ break;
+ case 'night':
+ e.settingsManager.gtk_theme = e.settingsManager.gtk_variant_night;
+ break;
+ case 'original':
+ e.settingsManager.gtk_theme = e.settingsManager.gtk_variant_original;
+ break;
+ }
+ }
+
+ _update_variants() {
+ if ( e.settingsManager.manual_gtk_variants || this._are_variants_up_to_date() ) {
+ return;
+ }
+
+ log_debug('Updating GTK variants...');
+ const variants = GtkVariants.guess_from(e.settingsManager.gtk_theme);
+ const installed_themes = get_installed_gtk_themes();
+
+ if ( !installed_themes.has(variants.get('day')) || !installed_themes.has(variants.get('night')) ) {
+ e.settingsManager.gtk_variant_original = variants.get('original');
+ const message = _('Unable to automatically detect the day and night variants for the "%s" GTK theme. Please manually choose them in the extension\'s preferences.').format(variants.get('original'));
+ throw new Error(message);
+ }
+
+ e.settingsManager.gtk_variant_day = variants.get('day');
+ e.settingsManager.gtk_variant_night = variants.get('night');
+ e.settingsManager.gtk_variant_original = variants.get('original');
+ log_debug(`New GTK variants. { day: '${variants.get('day')}'; night: '${variants.get('night')}' }`);
+ }
+
+ _reset_theme() {
+ // We don't reset the theme when locking the session to prevent
+ // flicker on unlocking
+ if ( !main.screenShield.locked ) {
+ log_debug('Resetting to the user\'s original GTK theme...');
+ this._set_variant('original');
+ }
+ }
+
+}
diff --git a/src/modules/Variants.js b/src/modules/GtkVariants.js
similarity index 83%
rename from src/modules/Variants.js
rename to src/modules/GtkVariants.js
index b233a79..03921a4 100644
--- a/src/modules/Variants.js
+++ b/src/modules/GtkVariants.js
@@ -17,32 +17,30 @@ You should have received a copy of the GNU General Public License along with
this program. If not, see .
*/
-
-/*
-The magic of guessing theme variants happens here.
-
-If the theme doesn't fit a particular case, we'll do the following:
- - Remove any signs of a dark variant to the theme name to get the day
- variant
- - Remove any signs of a light variant to the day variant and add '-dark' to
- get the night variant
-
-For themes that don't work with the general rule, a particular case must be
-written. Day and night variants should be guessed with the most generic light
-and dark variants the theme offer, except if the user explicitly chose a
-specific variant.
-
-Light variants, from the most to the least generic:
- - ''
- - '-light'
- - '-darker'
-
-Dark variants, from the most the least generic:
- - '-dark'
- - '-darkest'
-*/
-
-var Variants = class {
+/**
+ * The magic of guessing theme variants happens here.
+ *
+ * If the theme doesn't fit a particular case, we'll do the following:
+ * - Remove any signs of a dark variant to the theme name to get the day
+ * variant
+ * - Remove any signs of a light variant to the day variant and add '-dark' to
+ * get the night variant
+ *
+ * For themes that don't work with the general rule, a particular case must be
+ * written. Day and night variants should be guessed with the most generic light
+ * and dark variants the theme offer, except if the user explicitly chose a
+ * specific variant.
+ *
+ * Light variants, from the most to the least generic:
+ * - ''
+ * - '-light'
+ * - '-darker'
+ *
+ * Dark variants, from the most the least generic:
+ * - '-dark'
+ * - '-darkest'
+ */
+var GtkVariants = class {
static guess_from(name) {
const variants = new Map();
diff --git a/src/modules/SettingsManager.js b/src/modules/SettingsManager.js
new file mode 100644
index 0000000..780095b
--- /dev/null
+++ b/src/modules/SettingsManager.js
@@ -0,0 +1,427 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio } = imports.gi;
+const { extensionUtils } = imports.misc;
+const Signals = imports.signals;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const { log_debug, get_userthemes_extension, get_userthemes_settings } = Me.imports.utils;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.getSettings = Me.imports.convenience.getSettings;
+}
+
+
+/**
+ * The Settings Manager centralizes all the different settings the extension
+ * needs. It handles getting and settings values as well as signaling any
+ * changes.
+ */
+var SettingsManager = class {
+
+ constructor() {
+ log_debug('Initializing settings...');
+ this._extensionsSettings = extensionUtils.getSettings();
+ this._colorSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.plugins.color' });
+ this._locationSettings = new Gio.Settings({ schema: 'org.gnome.system.location' });
+ this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
+ this._backgroundSettings = new Gio.Settings({ schema: 'org.gnome.desktop.background' });
+ this._userthemesSettings = get_userthemes_settings();
+ log_debug('Settings initialized.');
+ }
+
+ enable() {
+ log_debug('Connecting settings signals...');
+ this._gtk_variant_day_changed_connect = this._extensionsSettings.connect('changed::gtk-variant-day', this._on_gtk_variant_day_changed.bind(this));
+ this._gtk_variant_night_changed_connect = this._extensionsSettings.connect('changed::gtk-variant-night', this._on_gtk_variant_night_changed.bind(this));
+ this._gtk_variant_original_changed_connect = this._extensionsSettings.connect('changed::gtk-variant-original', this._on_gtk_variant_original_changed.bind(this));
+ this._shell_variant_day_changed_connect = this._extensionsSettings.connect('changed::shell-variant-day', this._on_shell_variant_day_changed.bind(this));
+ this._shell_variant_night_changed_connect = this._extensionsSettings.connect('changed::shell-variant-night', this._on_shell_variant_night_changed.bind(this));
+ this._shell_variant_original_changed_connect = this._extensionsSettings.connect('changed::shell-variant-original', this._on_shell_variant_original_changed.bind(this));
+ this._time_source_changed_connect = this._extensionsSettings.connect('changed::time-source', this._on_time_source_changed.bind(this));
+ this._manual_time_source_changed_connect = this._extensionsSettings.connect('changed::manual-time-source', this._on_manual_time_source_changed.bind(this));
+ this._commands_status_connect = this._extensionsSettings.connect('changed::commands-enabled', this._on_commands_status_changed.bind(this));
+ this._backgrounds_status_connect = this._extensionsSettings.connect('changed::backgrounds-enabled', this._on_backgrounds_status_changed.bind(this));
+ this._background_day_changed_connect = this._extensionsSettings.connect('changed::background-day', this._on_background_day_changed.bind(this));
+ this._background_night_changed_connect = this._extensionsSettings.connect('changed::background-night', this._on_background_night_changed.bind(this));
+ this._nightlight_status_connect = this._colorSettings.connect('changed::night-light-enabled', this._on_nightlight_status_changed.bind(this));
+ this._location_status_connect = this._locationSettings.connect('changed::enabled', this._on_location_status_changed.bind(this));
+ this._gtk_theme_changed_connect = this._interfaceSettings.connect('changed::gtk-theme', this._on_gtk_theme_changed.bind(this));
+ this._background_changed_connect = this._backgroundSettings.connect('changed::picture-uri', this._on_background_changed.bind(this));
+ if ( this._userthemesSettings ) {
+ this._shell_theme_changed_connect = this._userthemesSettings.connect('changed::name', this._on_shell_theme_changed.bind(this));
+ }
+ log_debug('Settings signals connected.');
+ }
+
+ disable() {
+ log_debug('Disconnecting settings signals...');
+ this._extensionsSettings.disconnect(this._gtk_variant_day_changed_connect);
+ this._extensionsSettings.disconnect(this._gtk_variant_night_changed_connect);
+ this._extensionsSettings.disconnect(this._gtk_variant_original_changed_connect);
+ this._extensionsSettings.disconnect(this._shell_variant_day_changed_connect);
+ this._extensionsSettings.disconnect(this._shell_variant_night_changed_connect);
+ this._extensionsSettings.disconnect(this._shell_variant_original_changed_connect);
+ this._extensionsSettings.disconnect(this._time_source_changed_connect);
+ this._extensionsSettings.disconnect(this._manual_time_source_changed_connect);
+ this._extensionsSettings.disconnect(this._commands_status_connect);
+ this._extensionsSettings.disconnect(this._backgrounds_status_connect);
+ this._extensionsSettings.disconnect(this._background_day_changed_connect);
+ this._extensionsSettings.disconnect(this._background_night_changed_connect);
+ this._colorSettings.disconnect(this._nightlight_status_connect);
+ this._locationSettings.disconnect(this._location_status_connect);
+ this._interfaceSettings.disconnect(this._gtk_theme_changed_connect);
+ this._backgroundSettings.disconnect(this._background_changed_connect);
+ if ( this._userthemesSettings ) {
+ this._userthemesSettings.disconnect(this._shell_theme_changed_connect);
+ }
+ log_debug('Settings signals disconnected.');
+ }
+
+ /**
+ * SETTERS AND GETTERS
+ */
+
+ /* GTK variants settings */
+
+ get gtk_variant_day() {
+ return this._extensionsSettings.get_string('gtk-variant-day');
+ }
+
+ set gtk_variant_day(value) {
+ if ( value !== this.gtk_variant_day ) {
+ this._extensionsSettings.set_string('gtk-variant-day', value);
+ log_debug(`The GTK day variant has been set to '${value}'.`);
+ }
+ }
+
+ get gtk_variant_night() {
+ return this._extensionsSettings.get_string('gtk-variant-night');
+ }
+
+ set gtk_variant_night(value) {
+ if ( value !== this.gtk_variant_night ) {
+ this._extensionsSettings.set_string('gtk-variant-night', value);
+ log_debug(`The GTK night variant has been set to '${value}'.`);
+ }
+ }
+
+ get gtk_variant_original() {
+ return this._extensionsSettings.get_string('gtk-variant-original');
+ }
+
+ set gtk_variant_original(value) {
+ if ( value !== this.gtk_variant_original ) {
+ this._extensionsSettings.set_string('gtk-variant-original', value);
+ log_debug(`The GTK original variant has been set to '${value}'.`);
+ }
+ }
+
+ get manual_gtk_variants() {
+ return this._extensionsSettings.get_boolean('manual-gtk-variants');
+ }
+
+
+ /* Shell variants settings */
+
+ get shell_variant_day() {
+ return this._extensionsSettings.get_string('shell-variant-day');
+ }
+
+ set shell_variant_day(value) {
+ if ( value !== this.shell_variant_day ) {
+ this._extensionsSettings.set_string('shell-variant-day', value);
+ log_debug(`The shell day variant has been set to '${value}'.`);
+ }
+ }
+
+ get shell_variant_night() {
+ return this._extensionsSettings.get_string('shell-variant-night');
+ }
+
+ set shell_variant_night(value) {
+ if ( value !== this.shell_variant_night ) {
+ this._extensionsSettings.set_string('shell-variant-night', value);
+ log_debug(`The shell night variant has been set to '${value}'.`);
+ }
+ }
+
+ get shell_variant_original() {
+ return this._extensionsSettings.get_string('shell-variant-original');
+ }
+
+ set shell_variant_original(value) {
+ if ( value !== this.shell_variant_original ) {
+ this._extensionsSettings.set_string('shell-variant-original', value);
+ log_debug(`The shell original variant has been set to '${value}'.`);
+ }
+ }
+
+ get manual_shell_variants() {
+ return this._extensionsSettings.get_boolean('manual-shell-variants');
+ }
+
+
+ /* Time source settings */
+
+ get time_source() {
+ return this._extensionsSettings.get_string('time-source');
+ }
+
+ get manual_time_source() {
+ return this._extensionsSettings.get_boolean('manual-time-source');
+ }
+
+ set time_source(value) {
+ if ( value !== this.time_source ) {
+ this._extensionsSettings.set_string('time-source', value);
+ log_debug(`The time source has been set to ${value}.`);
+ }
+ }
+
+ get schedule_sunrise() {
+ return this._extensionsSettings.get_double('schedule-sunrise');
+ }
+
+ get schedule_sunset() {
+ return this._extensionsSettings.get_double('schedule-sunset');
+ }
+
+
+ /* Commands settings */
+
+ get commands_enabled() {
+ return this._extensionsSettings.get_boolean('commands-enabled');
+ }
+
+ get command_sunrise() {
+ return this._extensionsSettings.get_string('command-sunrise');
+ }
+
+ get command_sunset() {
+ return this._extensionsSettings.get_string('command-sunset');
+ }
+
+
+ /* Background settings */
+
+ get backgrounds_enabled() {
+ return this._extensionsSettings.get_boolean('backgrounds-enabled');
+ }
+
+ get background_day() {
+ return this._extensionsSettings.get_string('background-day') || this.background;
+ }
+
+ set background_day(value) {
+ this._extensionsSettings.set_string('background-day', value);
+ }
+
+ get background_night() {
+ return this._extensionsSettings.get_string('background-night') || this.background;
+ }
+
+ set background_night(value) {
+ this._extensionsSettings.set_string('background-night', value);
+ }
+
+
+ /* Night Light settings */
+
+ get nightlight_enabled() {
+ return this._colorSettings.get_boolean('night-light-enabled');
+ }
+
+
+ /* Location settings */
+
+ get location_enabled() {
+ return this._locationSettings.get_boolean('enabled');
+ }
+
+
+ /* GTK theme settings */
+
+ get gtk_theme() {
+ return this._interfaceSettings.get_string('gtk-theme');
+ }
+
+ set gtk_theme(value) {
+ if ( value !== this.gtk_theme ) {
+ this._interfaceSettings.set_string('gtk-theme', value);
+ log_debug(`GTK theme has been set to '${value}.'`);
+ }
+ }
+
+ /* Shell theme settings */
+
+ get shell_theme() {
+ if ( this._userthemesSettings ) {
+ return this._userthemesSettings.get_string('name');
+ }
+ }
+
+ set shell_theme(value) {
+ if ( this._userthemesSettings && value !== this.shell_theme ) {
+ this._userthemesSettings.set_string('name', value);
+ }
+ }
+
+ get use_userthemes() {
+ const extension = get_userthemes_extension();
+ return (extension && extension.state === 1);
+ }
+
+
+ /* Background settings */
+
+ get background() {
+ return this._backgroundSettings.get_string('picture-uri');
+ }
+
+ set background(value) {
+ if ( value !== this.background ) {
+ this._backgroundSettings.set_string('picture-uri', value);
+ }
+ }
+
+
+ /**
+ * SIGNALS
+ */
+
+ /* GTK variants */
+
+ _on_gtk_variant_day_changed(settings, changed_key) {
+ log_debug(`GTK day variant has changed to '${this.gtk_variant_day}'.`);
+ this.emit('gtk-variant-changed', 'day');
+ }
+
+ _on_gtk_variant_night_changed(settings, changed_key) {
+ log_debug(`GTK night variant has changed to '${this.gtk_variant_night}'.`);
+ this.emit('gtk-variant-changed', 'night');
+ }
+
+ _on_gtk_variant_original_changed(settings, changed_key) {
+ log_debug(`GTK original variant has changed to '${this.gtk_variant_original}'.`);
+ this.emit('gtk-variant-changed', 'original');
+ }
+
+
+ /* Shell variants */
+
+ _on_shell_variant_day_changed(settings, changed_key) {
+ log_debug(`Shell day variant has changed to '${this.shell_variant_day}'.`);
+ this.emit('shell-variant-changed', 'day');
+ }
+
+ _on_shell_variant_night_changed(settings, changed_key) {
+ log_debug(`Shell night variant has changed to '${this.shell_variant_night}'.`);
+ this.emit('shell-variant-changed', 'night');
+ }
+
+ _on_shell_variant_original_changed(settings, changed_key) {
+ log_debug(`Shell original variant has changed to '${this.shell_variant_original}'.`);
+ this.emit('shell-variant-changed', 'original');
+ }
+
+
+ /* Time source */
+
+ _on_time_source_changed(settings, changed_key) {
+ log_debug(`Time source has changed to ${this.time_source}.`);
+ this.emit('time-source-changed', this.time_source);
+ }
+
+ _on_manual_time_source_changed(settings, changed_key) {
+ log_debug('Manual time source has been ' + (this.manual_time_source ? 'ena' : 'disa') + 'bled.');
+ this.emit('manual-time-source-changed', this.manual_time_source);
+ }
+
+
+ /* Commands */
+
+ _on_commands_status_changed(settings, changed_key) {
+ log_debug('Commands have been ' + (this.commands_enabled ? 'ena' : 'disa') + 'bled.');
+ this.emit('commands-status-changed', this.commands_enabled);
+ }
+
+
+ /* Backgrounds */
+
+ _on_backgrounds_status_changed(settings, changed_key) {
+ log_debug('Backgrounds have been ' + (this.backgrounds_enabled ? 'ena' : 'disa') + 'bled.');
+ this.emit('backgrounds-status-changed', this.backgrounds_enabled);
+ }
+
+ _on_background_day_changed(settings, changed_key) {
+ log_debug(`Day background has changed to '${this.background_day}'.`);
+ this.emit('background-time-changed', 'day');
+ }
+
+ _on_background_night_changed(settings, changed_key) {
+ log_debug(`Night background has changed to '${this.background_night}'.`);
+ this.emit('background-time-changed', 'night');
+ }
+
+
+ /* Night Light */
+
+ _on_nightlight_status_changed(settings, changed_key) {
+ log_debug('Night Light has been ' + (this.nightlight_enabled ? 'ena' : 'disa') + 'bled.');
+ this.emit('nightlight-status-changed', this.nightlight_enabled);
+ }
+
+
+ /* Location */
+
+ _on_location_status_changed(settings, changed_key) {
+ log_debug('Location has been ' + (this.location_enabled ? 'ena' : 'disa') + 'bled.');
+ this.emit('location-status-changed', this.location_enabled);
+ }
+
+
+ /* GTK theme */
+
+ _on_gtk_theme_changed(settings, changed_key) {
+ log_debug(`GTK theme has changed to '${this.gtk_theme}'.`);
+ this.emit('gtk-theme-changed', this.gtk_theme);
+ }
+
+
+ /* Background */
+
+ _on_background_changed(settings, changed_key) {
+ log_debug(`Background has changed to '${this.background}'.`);
+ this.emit('background-changed', this.background);
+ }
+
+
+ /* Shell theme */
+
+ _on_shell_theme_changed(settings, changed_key) {
+ log_debug(`Shell theme has changed to '${this.shell_theme}'.`);
+ this.emit('shell-theme-changed', this.shell_theme);
+ }
+
+}
+Signals.addSignalMethods(SettingsManager.prototype);
diff --git a/src/modules/ShellThemer.js b/src/modules/ShellThemer.js
new file mode 100644
index 0000000..20ef54c
--- /dev/null
+++ b/src/modules/ShellThemer.js
@@ -0,0 +1,179 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { extensionUtils } = imports.misc;
+const Signals = imports.signals;
+const { main } = imports.ui;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const e = Me.imports.extension;
+const { log_debug, log_error, get_theme_dirs_paths, get_installed_shell_themes, get_shell_theme_stylesheet, apply_shell_stylesheet } = Me.imports.utils;
+const { ShellVariants } = Me.imports.modules.ShellVariants;
+
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
+
+/**
+ * The Shell Themer is responsible for changing the GTK theme according to the
+ * time. It will use the User Themes extension to do so if it is enabled.
+ *
+ * When the user changes its shell theme (for example via GNOME Tweaks), it will
+ * try to automatically guess the day and night variants for this theme. It
+ * will warn the user if it is unable to guess.
+ *
+ * In manual mode, it will not attempt to update the variants. The user can
+ * change the shell variants in the extension's preferences.
+ */
+var ShellThemer = class {
+
+ enable() {
+ log_debug('Enabling Shell Themer...');
+ try {
+ this._update_variants();
+ this._set_variant(e.timer.time);
+ this._connect_settings();
+ this._connect_timer();
+ }
+ catch(e) {
+ log_error(e);
+ }
+ log_debug('Shell Themer enabled.');
+ }
+
+ disable() {
+ log_debug('Disabling Shell Themer...');
+ this._disconnect_timer();
+ this._disconnect_settings();
+ this._reset_theme();
+ log_debug('Shell Themer disabled.');
+ }
+
+
+ _connect_settings() {
+ log_debug('Connecting Shell Themer to settings...');
+ this._shell_variant_changed_connect = e.settingsManager.connect('shell-variant-changed', this._on_shell_variant_changed.bind(this));
+ this._shell_theme_changed_connect = e.settingsManager.connect('shell-theme-changed', this._on_shell_theme_changed.bind(this));
+ }
+
+ _disconnect_settings() {
+ if ( this._shell_variant_changed_connect ) {
+ e.settingsManager.disconnect(this._shell_variant_changed_connect);
+ this._shell_variant_changed_connect = null;
+ }
+ if ( this._shell_theme_changed_connect ) {
+ e.settingsManager.disconnect(this._shell_theme_changed_connect);
+ this._shell_theme_changed_connect = null;
+ }
+ log_debug('Disconnected Shell Themer from settings.');
+ }
+
+ _connect_timer() {
+ log_debug('Connecting Shell Themer to Timer...');
+ this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
+ }
+
+ _disconnect_timer() {
+ if ( this._time_changed_connect ) {
+ e.timer.disconnect(this._time_changed_connect);
+ this._time_changed_connect = null;
+ }
+ log_debug('Disconnected Shell Themer from Timer.');
+ }
+
+
+ _on_shell_variant_changed(settings, changed_variant_time) {
+ if ( changed_variant_time === e.timer.time ) {
+ this._set_variant(e.timer.time);
+ }
+ }
+
+ _on_shell_theme_changed(settings, new_theme) {
+ try {
+ this._update_variants();
+ this._set_variant(e.timer.time);
+ }
+ catch(e) {
+ log_error(e);
+ }
+ }
+
+ _on_time_changed(timer, new_time) {
+ this._set_variant(new_time);
+ }
+
+
+ _are_variants_up_to_date() {
+ return ( e.settingsManager.shell_theme === e.settingsManager.shell_variant_day || e.settingsManager.shell_theme === e.settingsManager.shell_variant_night );
+ }
+
+ _set_variant(time) {
+ log_debug(`Setting the shell ${time} variant...`);
+ let shell_theme;
+ switch (time) {
+ case 'day':
+ shell_theme = e.settingsManager.shell_variant_day;
+ break;
+ case 'night':
+ shell_theme = e.settingsManager.shell_variant_night;
+ break;
+ case 'original':
+ shell_theme = e.settingsManager.shell_variant_original;
+ break;
+ }
+ if ( e.settingsManager.use_userthemes ) {
+ e.settingsManager.shell_theme = shell_theme;
+ }
+ else {
+ const stylesheet = get_shell_theme_stylesheet(shell_theme);
+ apply_shell_stylesheet(stylesheet);
+ }
+ }
+
+ _update_variants() {
+ if ( !e.settingsManager.use_userthemes || e.settingsManager.manual_shell_variants || this._are_variants_up_to_date() ) {
+ return;
+ }
+
+ log_debug('Updating Shell variants...');
+ const variants = ShellVariants.guess_from(e.settingsManager.shell_theme);
+ const installed_themes = get_installed_shell_themes();
+
+ if ( !installed_themes.has(variants.get('day')) || !installed_themes.has(variants.get('night')) ) {
+ e.settingsManager.shell_variant_original = variants.get('original');
+ const message = _('Unable to automatically detect the day and night variants for the "%s" GNOME Shell theme. Please manually choose them in the extension\'s preferences.').format(variants.get('original'));
+ throw new Error(message);
+ }
+
+ e.settingsManager.shell_variant_day = variants.get('day');
+ e.settingsManager.shell_variant_night = variants.get('night');
+ e.settingsManager.shell_variant_original = variants.get('original');
+ log_debug(`New Shell variants. { day: '${variants.get('day')}'; night: '${variants.get('night')}' }`);
+ }
+
+ _reset_theme() {
+ // We don't reset the theme when locking the session to prevent
+ // flicker on unlocking
+ if ( !main.screenShield.locked ) {
+ log_debug('Resetting to the user\'s original Shell theme...');
+ this._set_variant('original');
+ }
+ }
+
+}
+Signals.addSignalMethods(ShellThemer.prototype);
diff --git a/src/modules/ShellVariants.js b/src/modules/ShellVariants.js
new file mode 100644
index 0000000..f2520d4
--- /dev/null
+++ b/src/modules/ShellVariants.js
@@ -0,0 +1,103 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2019, 2020 Romain Vigier
+Copyright (C) 2020 Matti Hyttinen
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+
+/**
+ * The magic of guessing theme variants happens here.
+ *
+ * If the theme doesn't fit a particular case, we'll do the following:
+ * - Remove any signs of a dark variant to the theme name to get the day
+ * variant
+ * - Remove any signs of a light variant to the day variant and add '-dark' to
+ * get the night variant
+ *
+ * For themes that don't work with the general rule, a particular case must be
+ * written. Day and night variants should be guessed with the most generic light
+ * and dark variants the theme offer, except if the user explicitly chose a
+ * specific variant.
+ *
+ * Light variants, from the most to the least generic:
+ * - ''
+ * - '-light'
+ * - '-darker'
+ *
+ * Dark variants, from the most the least generic:
+ * - '-dark'
+ * - '-darkest'
+ */
+var ShellVariants = class {
+
+ static guess_from(name) {
+ const variants = new Map();
+ variants.set('original', name);
+
+ if ( name.includes('Adapta') ) {
+ variants.set('day', name.replace('-Nokto', ''));
+ variants.set('night', variants.get('day').replace('Adapta', 'Adapta-Nokto'));
+ }
+ else if ( name.includes('Arc') ) {
+ variants.set('day', name.replace('-Dark', ''));
+ variants.set('night', variants.get('day').replace('Arc', 'Arc-Dark'));
+ }
+ else if ( name.match(/^(Canta|ChromeOS|Materia|Orchis).*-compact/) ) {
+ variants.set('day', name.replace('-dark', ''));
+ variants.set('night', variants.get('day').replace(/(-light)?-compact/, '-dark-compact'));
+ }
+ else if ( name.includes('Flat-Remix') ) {
+ const color = ( name.split('-')[2] && !['Dark', 'Darkest', 'fullPanel'].includes(name.split('-')[2]) ) ? '-' + name.split('-')[2] : '';
+ const dark_variant = name.includes('Darkest') ? '-Darkest' : '-Dark';
+ const size = name.includes('fullPanel')? '-fullPanel' : '';
+ variants.set('day', name.replace(/-Dark(est)?/, ''));
+ variants.set('night', `Flat-Remix${color}${dark_variant}${size}`);
+ }
+ else if ( name.match(/^(Layan|Matcha)/) ) {
+ const basename = name.split('-')[0];
+ variants.set('day', name.replace('-dark', ''));
+ variants.set('night', variants.get('day').replace(new RegExp(`${basename}(-light)?`), `${basename}-dark`));
+ }
+ else if ( name.includes('Mojave') ) {
+ variants.set('day', name.replace('-dark', '-light'));
+ variants.set('night', variants.get('day').replace('-light', '-dark'));
+ }
+ else if ( name.includes('Plata') ) {
+ variants.set('day', name.replace('-Noir', ''));
+ variants.set('night', variants.get('day').replace(/Plata(-Lumine)?/, 'Plata-Noir'));
+ }
+ else if ( name.includes('Simply_Circles') ) {
+ variants.set('day', name.replace('_Dark', '_Light'));
+ variants.set('night', name.replace('_Light', '_Dark'));
+ }
+ else if ( name.includes('Teja') ) {
+ const dark_variant = '_' + (name.replace('_Light').split('_')[1] || 'Dark');
+ variants.set('day', name.replace(/(_Dark(est)?|_Black)/, ''));
+ variants.set('night', variants.get('day').replace('_Light', '') + dark_variant);
+ }
+ else if ( name.includes('vimix') ) {
+ variants.set('day', name.replace('-dark', ''));
+ variants.set('night', variants.get('day').replace(/vimix(-light)?/, 'vimix-dark'));
+ }
+ else {
+ variants.set('day', name.replace(/-dark(?!er)(est)?/, ''));
+ variants.set('night', variants.get('day').replace(/(-light|-darker)/, '') + (name.includes('-darkest') ? '-darkest' : '-dark'));
+ }
+
+ return variants;
+ }
+
+}
diff --git a/src/modules/Switcher.js b/src/modules/Switcher.js
deleted file mode 100644
index f23ba45..0000000
--- a/src/modules/Switcher.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
-Night Theme Switcher Gnome Shell extension
-
-Copyright (C) 2020 Romain Vigier
-
-This program is free software: you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later
-version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-this program. If not, see .
-*/
-
-const { extensionUtils } = imports.misc;
-const { main } = imports.ui;
-
-const Me = extensionUtils.getCurrentExtension();
-const config = Me.imports.config;
-
-const { log_debug, log_error } = Me.imports.utils;
-
-const { Themer } = Me.imports.modules.Themer;
-const { Timer } = Me.imports.modules.Timer;
-
-
-/*
-The Switcher is the orchestrator of the extension.
-
-When the extension is enabled, it asks the Themer to listen to theme changes
-from the user and the Timer to listen to changes in the current time:
- - On theme change, it asks the Themer to guess the new day and night
- variants for that theme, and to apply the relevant variant depending on the
- time of the day.
- - On time of the day change, it asks the Themer to apply the relevant
- variant.
-
-When the extension is disabled, it asks the Themer and the Timer to disable
-themselves.
-*/
-
-var Switcher = class {
-
- constructor() {
- log_debug('Initializing extension...');
- extensionUtils.initTranslations(Me.metadata.uuid);
- log_debug('Extension initialized.');
- }
-
- enable() {
- log_debug('Enabling extension...');
- try {
- this.theme = new Themer();
- this.theme.enable();
- this.theme.subscribe(this._on_theme_changed.bind(this));
-
- this.time = new Timer();
- this.time.enable();
- this.time.subscribe(this._on_time_changed.bind(this));
-
- this.theme.set_variant(this.time.current);
-
- log_debug('Extension enabled.');
- }
- catch(e) {
- log_error(e);
- }
- }
-
- disable() {
- log_debug('Disabling extension...');
- try {
- this.theme.disable();
- this.time.disable();
- }
- catch(e) {} // Since we're disabling, we'll just ignore errors.
- finally {
- this.theme = null;
- this.time = null;
- }
- log_debug('Extension disabled.');
- }
-
- _on_theme_changed() {
- if ( !this.theme || !this.time ) {
- return;
- }
-
- try {
- this.theme.update_variants();
- this.theme.set_variant(this.time.current);
- }
- catch(e) {
- log_error(e);
- }
- }
-
- _on_time_changed() {
- if ( !this.theme || !this.time ) {
- return;
- }
-
- try {
- this.theme.set_variant(this.time.current);
- }
- catch(e) {
- log_error(e);
- }
- }
-
-}
diff --git a/src/modules/Themer.js b/src/modules/Themer.js
deleted file mode 100644
index 22a8cca..0000000
--- a/src/modules/Themer.js
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
-Night Theme Switcher Gnome Shell extension
-
-Copyright (C) 2020 Romain Vigier
-
-This program is free software: you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later
-version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-this program. If not, see .
-*/
-
-const { extensionUtils } = imports.misc;
-const { Gio } = imports.gi;
-const { main } = imports.ui;
-
-const Me = extensionUtils.getCurrentExtension();
-const config = Me.imports.config;
-const utils = Me.imports.utils;
-
-const { log_debug, log_error } = Me.imports.utils;
-
-const { Variants } = Me.imports.modules.Variants;
-
-const Gettext = imports.gettext.domain(Me.metadata.uuid);
-const _ = Gettext.gettext;
-
-
-/*
-The Themer communicates with the system to get the current theme or set a new
-one, and get the day and night variants of a theme. It listens to theme changes
-and reports them.
-*/
-
-var Themer = class {
-
- constructor() {
- log_debug('Initializing Themer...');
- this.settings = extensionUtils.getSettings();
- this.theme_gsettings = new Gio.Settings({ schema: config.THEME_GSETTINGS_SCHEMA });
- log_debug('Themer initialized.');
- }
-
- enable() {
- try {
- log_debug('Enabling Themer...');
- this.ready = false;
- this._listen_to_force_manual_status();
- if ( this._is_force_manual_enabled() ) {
- log_debug('Using manually set variants.');
- this._listen_to_prefs_theme_change();
- }
- else {
- log_debug('Automatically detecting variants.');
- this._listen_to_theme_changes();
- this.update_variants();
- }
- this.ready = true;
- this.emit();
- log_debug('Themer enabled.');
- }
- catch(e) {
- log_error(e);
- }
- }
-
- disable() {
- log_debug('Disabling Themer...');
- this._stop_listening_to_force_manual_status();
- if ( this._is_force_manual_enabled() ) {
- this._stop_listening_to_prefs_theme_change();
- }
- else {
- this._stop_listening_to_theme_changes();
- // GNOME Shell disables extensions when locking the screen. We'll
- // only reset the theme if the user disables the extension to
- // prevent flickering when unlocking.
- if ( !main.screenShield.locked ) {
- this.reset_theme();
- }
- }
- log_debug('Themer disabled.');
- }
-
- get current_theme() {
- return this.theme_gsettings.get_string(config.THEME_GSETTINGS_PROPERTY);
- }
-
- set current_theme(theme) {
- if ( theme !== this.current_theme ) {
- this.theme_gsettings.set_string(config.THEME_GSETTINGS_PROPERTY, theme);
- log_debug(`Theme has been set to "${theme}".`);
- }
- }
-
- subscribe(callback) {
- this.theme_change_callback = callback;
- }
-
- emit() {
- if ( this.theme_change_callback ) {
- this.theme_change_callback();
- }
- }
-
- set_variant(variant) {
- if ( this.ready ) {
- log_debug(`Setting theme to the "${variant}" variant...`);
- this.current_theme = this.settings.get_string(`theme-${variant}`);
- }
- }
-
- reset_theme() {
- this.set_variant('original');
- log_debug('Theme has been reset to the user\'s original variant.')
- }
-
- update_variants() {
- if ( !this._are_variants_up_to_date() && !this._is_force_manual_enabled() ) {
- this._update_variants();
- }
- }
-
- _update_variants() {
- if ( this.current_theme ) {
- const variants = Variants.guess_from(this.current_theme);
- if ( utils.is_theme_installed(variants.get('day')) && utils.is_theme_installed(variants.get('night')) ) {
- variants.forEach( (theme, variant) => this.settings.set_string(`theme-${variant}`, theme) );
- log_debug(`Variants updated: {day: "${variants.get('day')}", night: "${variants.get('night')}"}`);
- }
- else {
- const message = _('The extension cannot detect the day and night variants for the "%s" theme. Please choose another theme or manually set variants in the extension preferences.').format(variants.get('original'));
- throw new Error(message);
- }
- }
- }
-
- _are_variants_up_to_date() {
- return ( this.current_theme === this.settings.get_string('theme-day') || this.current_theme === this.settings.get_string('theme-night') );
- }
-
- _is_force_manual_enabled() {
- return this.settings.get_boolean('theme-force-manual');
- }
-
- _listen_to_force_manual_status() {
- if ( !this.force_manual_status_connect ) {
- this.force_manual_status_connect = this.settings.connect(
- 'changed::theme-force-manual',
- this._on_force_manual_status_changed.bind(this)
- );
- log_debug('Listening to manual variants status changes...');
- }
- }
-
- _stop_listening_to_force_manual_status() {
- if ( this.settings && this.force_manual_status_connect ) {
- this.settings.disconnect(this.force_manual_status_connect);
- this.force_manual_status_connect = null;
- log_debug('Stopped listening to manual variants status changes.');
- }
- }
-
- _on_force_manual_status_changed() {
- log_debug('Manual variants status has changed.');
- this.enable();
- }
-
- _listen_to_theme_changes() {
- if ( !this.theme_change_connect ) {
- this.theme_change_connect = this.theme_gsettings.connect(
- 'changed::' + config.THEME_GSETTINGS_PROPERTY,
- this._on_theme_changed.bind(this)
- );
- log_debug('Listening for theme changes...');
- }
- }
-
- _stop_listening_to_theme_changes() {
- if ( this.theme_gsettings && this.theme_change_connect ){
- this.theme_gsettings.disconnect(this.theme_change_connect);
- this.theme_change_connect = null;
- log_debug('Stopped listening for theme changes.');
- }
- }
-
- _on_theme_changed() {
- log_debug(`Theme has changed to "${this.current_theme}".`);
- this.ready ? this.emit() : this.enable();
- }
-
- _listen_to_prefs_theme_change() {
- if ( !this.prefs_theme_change_connect ) {
- this.prefs_theme_change_connect = new Map();
- ['day', 'night'].forEach(time => {
- this.prefs_theme_change_connect.set(time, this.settings.connect(
- `changed::theme-${time}`,
- this._on_theme_changed.bind(this)
- ));
- log_debug(`Listening for ${time} theme preference changes...`);
- });
- }
- }
-
- _stop_listening_to_prefs_theme_change() {
- if ( this.settings && this.prefs_theme_change_connect ) {
- this.prefs_theme_change_connect.forEach(connect => this.settings.disconnect(connect));
- this.prefs_theme_change_connect = null;
- log_debug('Stopped listening to theme preferences changes.');
- }
- }
-
-}
diff --git a/src/modules/Timer.js b/src/modules/Timer.js
index 6681554..b6303dd 100644
--- a/src/modules/Timer.js
+++ b/src/modules/Timer.js
@@ -16,527 +16,174 @@ You should have received a copy of the GNU General Public License along with
this program. If not, see .
*/
-const MainLoop = imports.mainloop;
-const { Gio, GLib } = imports.gi;
-const { extensionUtils, fileUtils } = imports.misc;
+const { extensionUtils } = imports.misc;
+const Signals = imports.signals;
const Me = extensionUtils.getCurrentExtension();
-const config = Me.imports.config;
-const { log_debug, log_error } = Me.imports.utils;
-
-const Gettext = imports.gettext.domain(Me.metadata.uuid);
-const _ = Gettext.gettext;
+const e = Me.imports.extension;
+const { log_debug } = Me.imports.utils;
+const { TimerNightlight } = Me.imports.modules.TimerNightlight;
+const { TimerLocation } = Me.imports.modules.TimerLocation;
+const { TimerSchedule } = Me.imports.modules.TimerSchedule;
-/*
-The Timer checks for changes in the time of day and reports them.
-
-I can either use Night Light or Location Services as a source.
-
-As Night Light or Location Services are essential for the extension to work,
-it continuously checks if one of them is enabled and warns the user if that's
-not the case. To not overwhelm the user with notifications, it only warns once
-and then only listens to Night Light or Location Services changes to reactivate
-itself automatically.
-*/
-
+/**
+ * The Timer is responsible for signaling any time change to the other modules.
+ *
+ * They can connect to its 'time-changed' signal and ask its 'time' property
+ * for the current time.
+ *
+ * It will try to use one of this three different time sources, in this order of
+ * preference:
+ * - Night Light
+ * - Location Services
+ * - Manual schedule
+ *
+ * The user can manually force a specific time source and set the manual
+ * schedule in the extensions's preferences.
+ */
var Timer = class {
constructor() {
- log_debug('Initializing Timer...');
- this.settings = extensionUtils.getSettings();
- this.nightlight_gsettings = new Gio.Settings({ schema: config.NIGHTLIGHT_GSETTINGS_SCHEMA });
- this.location_gsettings = new Gio.Settings({ schema: config.LOCATION_GSETTINGS_SCHEMA });
- log_debug('Timer initialized.');
+ this._source = null;
+ this._previous_time = null;
}
enable() {
log_debug('Enabling Timer...');
- try {
- this.ready = false;
- this.settings.set_string('time-source', this._get_source());
- this._listen_to_nightlight_status();
- this._listen_to_location_status();
- this._listen_to_force_manual_status();
- switch (this.settings.get_string('time-source')) {
- case 'nightlight':
- log_debug('Using Night Light as a source.');
- this._connect_to_color_dbus_proxy();
- this._listen_to_nightlight_changes();
- break;
- case 'location':
- log_debug('Using Location Services as a source.');
- this._connect_to_geoclue_dbus_proxy();
- this._listen_to_location_changes();
- this._connect_to_geoclue_location_dbus_proxy();
- this._update_location();
- this._update_location_suntimes();
- this._watch_for_location_time_change();
- this._regularly_update_location_suntimes();
- break;
- case 'manual':
- log_debug('Using manual schedule as a source.');
- this._watch_for_location_time_change();
- }
- this.ready = true;
- this.emit();
- log_debug('Timer enabled.');
- }
- catch(e) {
- log_error(e);
- }
+ this._connect_settings();
+ this._create_source();
+ this._connect_source();
+ this._enable_source();
+ log_debug('Timer enabled.');
}
disable() {
log_debug('Disabling Timer...');
- this._stop_listening_to_nightlight_status();
- this._stop_listening_to_location_status();
- this._stop_listening_to_force_manual_status();
- switch (this.settings.get_string('time-source')) {
- case 'nightlight':
- this._stop_listening_to_nightlight_changes();
- this._disconnect_from_color_dbus_proxy();
- break;
- case 'location':
- this._stop_regularly_updating_location_suntimes();
- this._stop_watching_for_location_time_change();
- this._stop_listening_to_location_changes();
- this._disconnect_from_geoclue_location_dbus_proxy();
- this._disconnect_from_geoclue_dbus_proxy();
- break;
- }
+ this._disconnect_source();
+ this._disable_source();
+ this._disconnect_settings();
log_debug('Timer disabled.');
}
- get current() {
- if ( this.ready ) {
- switch (this.settings.get_string('time-source')) {
- case 'nightlight':
- return this._is_nightlight_active() ? 'night' : 'day';
- case 'location':
- case 'manual':
- return this._is_location_daytime() ? 'day' : 'night';
- }
+
+ get time() {
+ return this._source.time;
+ }
+
+
+ _connect_settings() {
+ log_debug('Connecting Timer to settings...');
+ this._nightlight_status_changed_connect = e.settingsManager.connect('nightlight-status-changed', this._on_source_changed.bind(this));
+ this._location_status_changed_connect = e.settingsManager.connect('location-status-changed', this._on_source_changed.bind(this));
+ this._manual_time_source_changed_connect = e.settingsManager.connect('manual-time-source-changed', this._on_source_changed.bind(this));
+ this._time_source_changed_connect = e.settingsManager.connect('time-source-changed', this._on_time_source_changed.bind(this));
+ }
+
+ _disconnect_settings() {
+ if ( this._nightlight_status_changed_connect ) {
+ e.settingsManager.disconnect(this._nightlight_status_changed_connect);
+ this._nightlight_status_changed_connect = null;
}
- else {
- return 'day';
+ if ( this._location_status_changed_connect ) {
+ e.settingsManager.disconnect(this._location_status_changed_connect);
+ this._location_status_changed_connect = null;
+ }
+ if ( this._manual_time_source_changed_connect ) {
+ e.settingsManager.disconnect(this._manual_time_source_changed_connect);
+ this._manual_time_source_changed_connect = null;
+ }
+ if ( this._time_source_changed_connect ) {
+ e.settingsManager.disconnect(this._time_source_changed_connect);
+ this._time_source_changed_connect = null;
+ }
+ log_debug('Disconnected Timer from settings.');
+ }
+
+ _create_source() {
+ switch ( this._get_source() ) {
+ case 'nightlight':
+ this._source = new TimerNightlight();
+ break;
+ case 'location':
+ this._source = new TimerLocation();
+ break;
+ case 'schedule':
+ this._source = new TimerSchedule();
+ break;
+ }
+ this._source.enable();
+ }
+
+ _enable_source() {
+ this._source.enable();
+ }
+
+ _disable_source() {
+ if ( this._source ) {
+ this._source.disable();
+ this._source = null;
}
}
- subscribe(callback) {
- this.time_change_callback = callback;
+ _connect_source() {
+ log_debug('Connecting to time source...');
+ this._time_changed_connect = this._source.connect('time-changed', this._on_time_changed.bind(this));
}
- emit() {
- if ( this.time_change_callback ) {
- this.time_change_callback();
+ _disconnect_source() {
+ if ( this._time_changed_connect ) {
+ this._source.disconnect(this._time_changed_connect);
+ this._time_changed_connect = null;
+ }
+ log_debug('Disconnected from time source.');
+ }
+
+
+ _on_source_changed() {
+ this.disable();
+ this.enable();
+ }
+
+ _on_time_source_changed(settings, new_source) {
+ if ( e.settingsManager.manual_time_source ) {
+ this._on_source_changed();
}
}
+ _on_time_changed(source, new_time) {
+ if ( new_time !== this._previous_time) {
+ log_debug(`Time has changed to ${new_time}.`);
+ this.emit('time-changed', new_time);
+ this._previous_time = new_time;
+ }
+ }
+
+
_get_source() {
- if ( this._is_force_manual_enabled() ) {
- return 'manual';
+ log_debug('Getting time source...');
+
+ if ( e.settingsManager.manual_time_source ) {
+ log_debug(`Time source is forced to ${e.settingsManager.time_source}.`);
+ return e.settingsManager.time_source;
}
- else if ( this._is_nightlight_enabled() ) {
- return 'nightlight';
+
+ let source;
+ if ( e.settingsManager.nightlight_enabled ) {
+ source = 'nightlight';
}
- else if ( this._is_location_enabled() ) {
- return 'location';
+ else if ( e.settingsManager.location_enabled ) {
+ source = 'location';
}
else {
- return 'manual';
- }
- }
-
-
- /*
- Check if Night Light is enabled and restart the Timer on changes.
- */
-
- _is_nightlight_enabled() {
- return this.nightlight_gsettings.get_boolean(config.NIGHTLIGHT_GSETTINGS_PROPERTY);
- }
-
- _listen_to_nightlight_status() {
- if ( !this.nightlight_status_connect ) {
- this.nightlight_status_connect = this.nightlight_gsettings.connect(
- 'changed::' + config.NIGHTLIGHT_GSETTINGS_PROPERTY,
- this._on_nightlight_status_changed.bind(this)
- );
- log_debug('Listening to Night Light status changes...');
- }
- }
-
- _stop_listening_to_nightlight_status() {
- if ( this.nightlight_gsettings && this.nightlight_status_connect ) {
- this.nightlight_gsettings.disconnect(this.nightlight_status_connect);
- this.nightlight_status_connect = null;
- log_debug('Stopped listening to Night Light status changes.');
- }
- }
-
- _on_nightlight_status_changed() {
- log_debug('Night Light status has changed.');
- this.enable();
- }
-
-
- /*
- Check if Location Services are enabled and restart the Timer on changes.
- */
-
- _is_location_enabled() {
- return this.location_gsettings.get_boolean(config.LOCATION_GSETTINGS_PROPERTY);
- }
-
- _listen_to_location_status() {
- if ( !this.location_status_connect ) {
- this.location_status_connect = this.location_gsettings.connect(
- 'changed::' + config.LOCATION_GSETTINGS_PROPERTY,
- this._on_location_status_changed.bind(this)
- );
- log_debug('Listening to Location Services status changes...');
- }
- }
-
- _stop_listening_to_location_status() {
- if ( this.location_gsettings && this.location_status_connect ) {
- this.location_gsettings.disconnect(this.location_status_connect);
- this.location_status_connect = null;
- log_debug('Stopped listening to Location Services status changes.');
- }
- }
-
- _on_location_status_changed() {
- log_debug('Location Services status has changed.');
- this.enable();
- }
-
- /*
- Check if the user wants to force a manual schedule and restart the Timer on
- changes.
- */
-
- _is_force_manual_enabled() {
- return this.settings.get_boolean('time-force-manual');
- }
-
- _listen_to_force_manual_status() {
- if ( !this.force_manual_status_connect ) {
- this.force_manual_status_connect = this.settings.connect(
- 'changed::time-force-manual',
- this._on_force_manual_status_changed.bind(this)
- );
- log_debug('Listening to manual schedule status changes...');
- }
- }
-
- _stop_listening_to_force_manual_status() {
- if ( this.settings && this.force_manual_status_connect ) {
- this.settings.disconnect(this.force_manual_status_connect);
- this.force_manual_status_connect = null;
- log_debug('Stopped listening to manual schedule status changes.');
- }
- }
-
- _on_force_manual_status_changed() {
- log_debug('Manual schedule status has changed.');
- this.enable();
- }
-
-
- /*
- Use Night Light as a time source.
- */
-
- _connect_to_color_dbus_proxy() {
- try {
- log_debug('Connecting to Color DBus proxy...');
- const color_interface = fileUtils.loadInterfaceXML('org.gnome.SettingsDaemon.Color');
- const ColorProxy = Gio.DBusProxy.makeProxyWrapper(color_interface);
- this.color_dbus_proxy = new ColorProxy(
- Gio.DBus.session,
- 'org.gnome.SettingsDaemon.Color',
- '/org/gnome/SettingsDaemon/Color'
- );
- log_debug('Connected to Color DBus proxy.');
- }
- catch(e) {
- const message = _('Unable to connect to Color DBus proxy.');
- throw new Error(message);
- }
- }
-
- _disconnect_from_color_dbus_proxy() {
- if ( this.color_dbus_proxy ) {
- log_debug('Disconnecting from Color DBus Proxy...');
- this.color_dbus_proxy = null;
- log_debug('Disconnected from Color DBus Proxy.');
- }
- }
-
- _listen_to_nightlight_changes() {
- if ( !this.nightlight_changes_connect ) {
- this.nightlight_changes_connect = this.color_dbus_proxy.connect(
- 'g-properties-changed',
- this._on_nightlight_changed.bind(this)
- );
- log_debug('Listening to Night Light changes...');
- }
- }
-
- _stop_listening_to_nightlight_changes() {
- if ( this.color_dbus_proxy && this.nightlight_changes_connect ) {
- this.color_dbus_proxy.disconnect(this.nightlight_changes_connect);
- this.nightlight_changes_connect = null;
- log_debug('Stopped listening to Night Light changes.');
- }
- }
-
- _on_nightlight_changed(sender, dbus_properties) {
- const properties = dbus_properties.deep_unpack();
- if ( properties.NightLightActive ) {
- log_debug('Night Light has become ' + (properties.NightLightActive.unpack() ? '' : 'in') + 'active.');
- this.emit();
- }
- }
-
- _is_nightlight_active() {
- if ( this.color_dbus_proxy ) {
- return this.color_dbus_proxy.NightLightActive;
- }
- }
-
-
- /*
- Use location as a time source.
- */
-
- _connect_to_geoclue_dbus_proxy() {
- try {
- log_debug('Connecting to GeoClue Manager DBus proxy...');
- const GeoClueManagerProxy = Gio.DBusProxy.makeProxyWrapper(config.GEOCLUE_MANAGER_INTERFACE);
- this.geoclue_manager_dbus_proxy = new GeoClueManagerProxy(
- Gio.DBus.system,
- 'org.freedesktop.GeoClue2',
- '/org/freedesktop/GeoClue2/Manager'
- );
- log_debug('Connected to GeoClue Manager DBus proxy.');
- }
- catch(e) {
- const message = _('Unable to connect to GeoClue Manager DBus proxy.');
- throw new Error(message);
+ source = 'schedule';
}
- try {
- log_debug('Requesting new GeoClue Client...');
- this.geoclue_client = this.geoclue_manager_dbus_proxy.GetClientSync()[0];
- log_debug(`Got a GeoClue Client at ${this.geoclue_client}`);
- }
- catch(e) {
- const message = _('Unable to get a GeoClue Client.');
- throw new Error(message);
- }
-
- try {
- log_debug('Connecting to GeoClue Client DBus proxy...');
- const GeoClueClientProxy = Gio.DBusProxy.makeProxyWrapper(config.GEOCLUE_CLIENT_INTERFACE);
- this.geoclue_client_dbus_proxy = new GeoClueClientProxy(
- Gio.DBus.system,
- 'org.freedesktop.GeoClue2',
- this.geoclue_client
- );
- this.geoclue_client_dbus_proxy.DesktopId = Me.metadata.uuid;
- this.geoclue_client_dbus_proxy.DistanceThreshold = 10000;
- this.geoclue_client_dbus_proxy.RequestedAccuracyLevel = 4;
- log_debug('Connected to GeoClue Client DBus proxy.');
- }
- catch(e) {
- const message = _('Unable to connect to GeoClue Client DBus proxy.');
- throw new Error(message);
- }
- }
-
- _disconnect_from_geoclue_dbus_proxy() {
- if ( this.geoclue_client_dbus_proxy ) {
- log_debug('Disconnecting from GeoClue Client DBus proxy...');
- if ( this.geoclue_manager_dbus_proxy && this.geoclue_client ) {
- this.geoclue_manager_dbus_proxy.DeleteClientSync(this.geoclue_client);
- this.geoclue_client = null;
- }
- this.geoclue_client_dbus_proxy = null;
- log_debug('Disconnected from GeoClue Client DBus Proxy.');
- }
-
- if ( this.geoclue_manager_dbus_proxy ) {
- log_debug('Disconnecting from GeoClue Manager DBus proxy...');
- this.geoclue_manager_dbus_proxy = null;
- log_debug('Disconnected from GeoClue Manager DBus proxy.');
- }
- }
-
- _listen_to_location_changes() {
- if ( !this.location_changes_connect ) {
- this.location_changes_connect = this.geoclue_client_dbus_proxy.connectSignal('LocationUpdated', this._on_location_changed.bind(this));
- this.geoclue_client_dbus_proxy.StartSync();
- log_debug('Listening to location changes...');
- }
- }
-
- _stop_listening_to_location_changes() {
- if ( this.geoclue_client_dbus_proxy && this.location_changes_connect ) {
- this.geoclue_client_dbus_proxy.disconnectSignal(this.location_changes_connect);
- this.location_changes_connect = null;
- this.geoclue_client_dbus_proxy.StopSync();
- log_debug('Stopped listening to location changes.');
- }
- }
-
- _on_location_changed(proxy, sender, [old_location_path, new_location_path]) {
- log_debug('Location has changed.');
- this._connect_to_geoclue_location_dbus_proxy(new_location_path);
- this._update_location();
- this._update_location_suntimes();
- }
-
- _connect_to_geoclue_location_dbus_proxy(path) {
- if ( !path && this.geoclue_client_dbus_proxy ) {
- path = this.geoclue_client_dbus_proxy.Location;
- }
- if ( path !== '/' ) {
- try {
- log_debug('Connecting to GeoClue Location DBus proxy...');
- const GeoClueLocationProxy = Gio.DBusProxy.makeProxyWrapper(config.GEOCLUE_LOCATION_INTERFACE);
- this.geoclue_location_dbus_proxy = new GeoClueLocationProxy(
- Gio.DBus.system,
- 'org.freedesktop.GeoClue2',
- path
- );
- log_debug('Connected to GeoClue Location DBus proxy.');
- }
- catch(e) {
- const message = _('Unable to connect to GeoClue Location DBus proxy.');
- throw new Error(message);
- }
- }
- }
-
- _disconnect_from_geoclue_location_dbus_proxy() {
- if ( this.geoclue_location_dbus_proxy ) {
- log_debug('Disconnecting from GeoClue Location DBus proxy...');
- this.geoclue_location_dbus_proxy = null;
- log_debug('Disconnected from GeoClue Location DBus proxy.')
- }
- }
-
- _update_location() {
- if ( this.geoclue_location_dbus_proxy ) {
- try {
- log_debug('Updating location...');
- const latitude = this.geoclue_location_dbus_proxy.Latitude;
- const longitude = this.geoclue_location_dbus_proxy.Longitude;
-
- this.location = new Map([
- ['latitude', latitude],
- ['longitude', longitude]
- ]);
- log_debug(`Current location: (${latitude};${longitude})`);
- }
- catch(e) {
- const message = _('Unable to get current location.');
- throw new Error(message);
- }
- }
- }
-
- _update_location_suntimes() {
- if ( !this.location ) {
- return;
- }
-
- log_debug('Updating sun times...');
-
- Math.rad = degrees => degrees * Math.PI / 180;
- Math.deg = radians => radians * 180 / Math.PI;
-
- // Calculations from https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
- const latitude = this.location.get('latitude');
- const longitude = this.location.get('longitude');
-
- const dt_now = GLib.DateTime.new_now_local();
- const dt_zero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
-
- const time_span = dt_now.difference(dt_zero);
-
- const date = time_span / 1000 / 1000 / 60 / 60 / 24 + 2;
- const tz_offset = dt_now.get_utc_offset() / 1000 / 1000 / 60 / 60;
- const time_past_local_midnight = 0;
-
- const julian_day = date + 2415018.5 + time_past_local_midnight - tz_offset / 24;
- const julian_century = (julian_day - 2451545) / 36525;
- const geom_mean_long_sun = (280.46646 + julian_century * (36000.76983 + julian_century * 0.0003032)) % 360;
- const geom_mean_anom_sun = 357.52911 + julian_century * (35999.05029 - 0.0001537 * julian_century);
- const eccent_earth_orbit = 0.016708634 - julian_century * (0.000042037 + 0.0000001267 * julian_century);
- const sun_eq_of_ctr = Math.sin(Math.rad(geom_mean_anom_sun)) * (1.914602 - julian_century * (0.004817 + 0.000014 * julian_century)) + Math.sin(Math.rad(2 * geom_mean_anom_sun)) * (0.019993 - 0.000101 * julian_century) + Math.sin(Math.rad(3 * geom_mean_anom_sun)) * 0.000289;
- const sun_true_long = geom_mean_long_sun + sun_eq_of_ctr;
- const sun_app_long = sun_true_long - 0.00569 - 0.00478 * Math.sin(Math.rad(125.04 - 1934.136 * julian_century));
- const mean_obliq_ecliptic = 23 + (26 + ((21.448 - julian_century * (46.815 + julian_century * (0.00059 - julian_century * 0.001813)))) / 60) / 60;
- const obliq_corr = mean_obliq_ecliptic + 0.00256 * Math.cos(Math.rad(125.04 - 1934.136 * julian_century));
- const sun_declin = Math.deg(Math.asin(Math.sin(Math.rad(obliq_corr)) * Math.sin(Math.rad(sun_app_long))));
- const var_y = Math.tan(Math.rad(obliq_corr / 2)) * Math.tan(Math.rad(obliq_corr / 2));
- const eq_of_time = 4 * Math.deg(var_y * Math.sin(2 * Math.rad(geom_mean_long_sun)) - 2 * eccent_earth_orbit * Math.sin(Math.rad(geom_mean_anom_sun)) + 4 * eccent_earth_orbit * var_y * Math.sin(Math.rad(geom_mean_anom_sun)) * Math.cos(2 * Math.rad(geom_mean_long_sun)) - 0.5 * var_y * var_y * Math.sin(4 * Math.rad(geom_mean_long_sun)) - 1.25 * eccent_earth_orbit * eccent_earth_orbit * Math.sin(2 * Math.rad(geom_mean_anom_sun)));
- const ha_sunrise = Math.deg(Math.acos(Math.cos(Math.rad(90.833)) / (Math.cos(Math.rad(latitude)) * Math.cos(Math.rad(sun_declin))) - Math.tan(Math.rad(latitude)) * Math.tan(Math.rad(sun_declin))));
- const solar_noon = (720 - 4 * longitude - eq_of_time + tz_offset * 60) / 1440;
-
- const sunrise_time = solar_noon - ha_sunrise * 4 / 1440;
- const sunset_time = solar_noon + ha_sunrise * 4 / 1440;
-
- const sunrise = sunrise_time * 24;
- const sunset = sunset_time * 24;
-
- this.settings.set_double('time-sunrise', sunrise);
- this.settings.set_double('time-sunset', sunset);
- log_debug(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
- }
-
- _regularly_update_location_suntimes() {
- this.regularly_update_suntimes_timer = MainLoop.timeout_add_seconds(3600, () => {
- this._update_location_suntimes();
- return true; // Repeat the loop
- });
- }
-
- _stop_regularly_updating_location_suntimes() {
- if ( this.regularly_update_suntimes_timer ) {
- MainLoop.source_remove(this.regularly_update_suntimes_timer);
- this.regularly_update_suntimes_timer = null;
- }
- }
-
- _is_location_daytime() {
- const time = GLib.DateTime.new_now_local();
- const hour = time.get_hour() + time.get_minute() / 60 + time.get_second() / 3600;
- return ( hour >= this.settings.get_double('time-sunrise') && hour <= this.settings.get_double('time-sunset') );
- }
-
- _watch_for_location_time_change() {
- this.location_time_change_timer = MainLoop.timeout_add_seconds(1, () => {
- const previous_time = this.location_daytime;
- if ( previous_time !== this._is_location_daytime() ) {
- log_debug('Time of the day has changed.');
- this.location_daytime = this._is_location_daytime();
- this.emit();
- }
- return true; // Repeat the loop
- }, null);
- }
-
- _stop_watching_for_location_time_change() {
- if ( this.location_time_change_timer ) {
- MainLoop.source_remove(this.location_time_change_timer);
- this.location_time_change_timer = null;
- }
+ log_debug(`Time source is ${source}.`);
+ e.settingsManager.time_source = source;
+ return source;
}
}
+Signals.addSignalMethods(Timer.prototype);
diff --git a/src/modules/TimerLocation.js b/src/modules/TimerLocation.js
new file mode 100644
index 0000000..d5e329e
--- /dev/null
+++ b/src/modules/TimerLocation.js
@@ -0,0 +1,322 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio, GLib } = imports.gi;
+const { extensionUtils } = imports.misc;
+const Signals = imports.signals;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const e = Me.imports.extension;
+const { log_debug } = Me.imports.utils;
+
+
+// GeoClue2 interfaces from https://gitlab.freedesktop.org/geoclue/geoclue/
+const GEOCLUE_MANAGER_INTERFACE = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+const GEOCLUE_CLIENT_INTERFACE = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+const GEOCLUE_LOCATION_INTERFACE = `
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+
+/**
+ * The Location Timer uses Location Services to get the current sunrise and
+ * sunset times.
+ *
+ * It gets the current user's location with the GeoClue2 DBus proxy and
+ * calculate the times.
+ *
+ * It will recalculate every hour and when the user's location changed to stay
+ * up to date.
+ *
+ * Every second, it will check if the time has changed and signal if that's the
+ * case.
+ */
+var TimerLocation = class {
+
+ constructor() {
+ this._previously_daytime = null;
+ // Before we have the location suntimes, we'll use the manual schedule
+ // times
+ this._suntimes = new Map([
+ ['sunrise', e.settingsManager.schedule_sunrise],
+ ['sunset', e.settingsManager.schedule_sunrise]
+ ]);
+ }
+
+ enable() {
+ log_debug('Enabling Location Timer...');
+ this._connect_to_geoclue_dbus_proxy();
+ this._listen_to_location_updates();
+ this._connect_to_geoclue_location_dbus_proxy();
+ this._update_location();
+ this._update_suntimes();
+ this._watch_for_time_change();
+ this._regularly_update_suntimes();
+ log_debug('Location Timer enabled.');
+ }
+
+ disable() {
+ log_debug('Disabling Location Timer...');
+ this._stop_regularly_updating_suntimes();
+ this._stop_watching_for_time_change();
+ this._stop_listening_to_location_updates();
+ this._disconnect_from_geoclue_location_dbus_proxy();
+ this._disconnect_from_geoclue_dbus_proxy();
+ log_debug('Location Timer disabled.');
+ }
+
+
+ get time() {
+ return this._is_daytime() ? 'day' : 'night';
+ }
+
+ _connect_to_geoclue_dbus_proxy() {
+ log_debug('Connecting to GeoClue manager DBus proxy...');
+ const GeoClueManagerProxy = Gio.DBusProxy.makeProxyWrapper(GEOCLUE_MANAGER_INTERFACE);
+ this._geoclue_manager_dbus_proxy = new GeoClueManagerProxy(
+ Gio.DBus.system,
+ 'org.freedesktop.GeoClue2',
+ '/org/freedesktop/GeoClue2/Manager'
+ );
+ log_debug('Connected to GeoClue manager DBus proxy.');
+
+ log_debug('Getting a GeoClue client...');
+ this._geoclue_client = this._geoclue_manager_dbus_proxy.GetClientSync()[0];
+ log_debug(`Got a GeoClue client at ${this._geoclue_client}`);
+
+ log_debug('Connecting to GeoClue client DBus proxy...');
+ const GeoClueClientProxy = Gio.DBusProxy.makeProxyWrapper(GEOCLUE_CLIENT_INTERFACE);
+ this._geoclue_client_dbus_proxy = new GeoClueClientProxy(
+ Gio.DBus.system,
+ 'org.freedesktop.GeoClue2',
+ this._geoclue_client
+ );
+ this._geoclue_client_dbus_proxy.DesktopId = Me.metadata.uuid;
+ this._geoclue_client_dbus_proxy.DistanceThreshold = 10000;
+ this._geoclue_client_dbus_proxy.RequestedAccuracyLevel = 4;
+ log_debug('Connected to GeoClue client DBus proxy.');
+ }
+
+ _disconnect_from_geoclue_dbus_proxy() {
+ log_debug('Disconnecting from GeoClue DBus proxy...')
+ this._geoclue_manager_dbus_proxy.DeleteClientSync(this._geoclue_client);
+ this._geoclue_client = null;
+ this._geoclue_client_dbus_proxy = null;
+ this._geoclue_manager_dbus_proxy = null;
+ log_debug('Disconnected from GeoClue DBus proxy.')
+ }
+
+ _listen_to_location_updates() {
+ log_debug('Listening to location updates...');
+ this._location_updates_connect = this._geoclue_client_dbus_proxy.connectSignal('LocationUpdated', this._on_location_updated.bind(this));
+ this._geoclue_client_dbus_proxy.StartSync();
+ }
+
+ _stop_listening_to_location_updates() {
+ this._geoclue_client_dbus_proxy.disconnectSignal(this._location_updates_connect);
+ this._location_updates_connect = null;
+ this._geoclue_client_dbus_proxy.StopSync();
+ log_debug('Stopped listening to location updates.');
+ }
+
+ _on_location_updated(proxy, sender, [old_location_path, new_location_path]) {
+ log_debug('Location has changed.');
+ this._connect_to_geoclue_location_dbus_proxy(new_location_path);
+ this._update_location();
+ this._update_suntimes();
+ }
+
+ _connect_to_geoclue_location_dbus_proxy(path) {
+ if ( !path ) {
+ path = this._geoclue_client_dbus_proxy.Location;
+ }
+ if ( path !== '/' ) {
+ log_debug('Connecting to GeoClue location DBus proxy...');
+ const GeoClueLocationProxy = Gio.DBusProxy.makeProxyWrapper(GEOCLUE_LOCATION_INTERFACE);
+ this._geoclue_location_dbus_proxy = new GeoClueLocationProxy(
+ Gio.DBus.system,
+ 'org.freedesktop.GeoClue2',
+ path
+ );
+ log_debug('Connected to GeoClue location DBus proxy.');
+ }
+ }
+
+ _disconnect_from_geoclue_location_dbus_proxy() {
+ log_debug('Disconnecting from GeoClue location DBus proxy...');
+ this._geoclue_location_dbus_proxy = null;
+ log_debug('Disconnected from GeoClue location DBus proxy.');
+ }
+
+ _update_location() {
+ if ( this._geoclue_location_dbus_proxy ) {
+ log_debug('Updating location...');
+ const latitude = this._geoclue_location_dbus_proxy.Latitude;
+ const longitude = this._geoclue_location_dbus_proxy.Longitude;
+
+ this.location = new Map([
+ ['latitude', latitude],
+ ['longitude', longitude]
+ ]);
+ log_debug(`Current location: (${latitude};${longitude})`);
+ }
+ }
+
+ _update_suntimes() {
+ if ( !this.location ) {
+ return;
+ }
+
+ log_debug('Updating sun times...');
+
+ Math.rad = degrees => degrees * Math.PI / 180;
+ Math.deg = radians => radians * 180 / Math.PI;
+
+ // Calculations from https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
+ const latitude = this.location.get('latitude');
+ const longitude = this.location.get('longitude');
+
+ const dt_now = GLib.DateTime.new_now_local();
+ const dt_zero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
+
+ const time_span = dt_now.difference(dt_zero);
+
+ const date = time_span / 1000 / 1000 / 60 / 60 / 24 + 2;
+ const tz_offset = dt_now.get_utc_offset() / 1000 / 1000 / 60 / 60;
+ const time_past_local_midnight = 0;
+
+ const julian_day = date + 2415018.5 + time_past_local_midnight - tz_offset / 24;
+ const julian_century = (julian_day - 2451545) / 36525;
+ const geom_mean_long_sun = (280.46646 + julian_century * (36000.76983 + julian_century * 0.0003032)) % 360;
+ const geom_mean_anom_sun = 357.52911 + julian_century * (35999.05029 - 0.0001537 * julian_century);
+ const eccent_earth_orbit = 0.016708634 - julian_century * (0.000042037 + 0.0000001267 * julian_century);
+ const sun_eq_of_ctr = Math.sin(Math.rad(geom_mean_anom_sun)) * (1.914602 - julian_century * (0.004817 + 0.000014 * julian_century)) + Math.sin(Math.rad(2 * geom_mean_anom_sun)) * (0.019993 - 0.000101 * julian_century) + Math.sin(Math.rad(3 * geom_mean_anom_sun)) * 0.000289;
+ const sun_true_long = geom_mean_long_sun + sun_eq_of_ctr;
+ const sun_app_long = sun_true_long - 0.00569 - 0.00478 * Math.sin(Math.rad(125.04 - 1934.136 * julian_century));
+ const mean_obliq_ecliptic = 23 + (26 + ((21.448 - julian_century * (46.815 + julian_century * (0.00059 - julian_century * 0.001813)))) / 60) / 60;
+ const obliq_corr = mean_obliq_ecliptic + 0.00256 * Math.cos(Math.rad(125.04 - 1934.136 * julian_century));
+ const sun_declin = Math.deg(Math.asin(Math.sin(Math.rad(obliq_corr)) * Math.sin(Math.rad(sun_app_long))));
+ const var_y = Math.tan(Math.rad(obliq_corr / 2)) * Math.tan(Math.rad(obliq_corr / 2));
+ const eq_of_time = 4 * Math.deg(var_y * Math.sin(2 * Math.rad(geom_mean_long_sun)) - 2 * eccent_earth_orbit * Math.sin(Math.rad(geom_mean_anom_sun)) + 4 * eccent_earth_orbit * var_y * Math.sin(Math.rad(geom_mean_anom_sun)) * Math.cos(2 * Math.rad(geom_mean_long_sun)) - 0.5 * var_y * var_y * Math.sin(4 * Math.rad(geom_mean_long_sun)) - 1.25 * eccent_earth_orbit * eccent_earth_orbit * Math.sin(2 * Math.rad(geom_mean_anom_sun)));
+ const ha_sunrise = Math.deg(Math.acos(Math.cos(Math.rad(90.833)) / (Math.cos(Math.rad(latitude)) * Math.cos(Math.rad(sun_declin))) - Math.tan(Math.rad(latitude)) * Math.tan(Math.rad(sun_declin))));
+ const solar_noon = (720 - 4 * longitude - eq_of_time + tz_offset * 60) / 1440;
+
+ const sunrise_time = solar_noon - ha_sunrise * 4 / 1440;
+ const sunset_time = solar_noon + ha_sunrise * 4 / 1440;
+
+ const sunrise = sunrise_time * 24;
+ const sunset = sunset_time * 24;
+
+ this._suntimes.set('sunrise', sunrise);
+ this._suntimes.set('sunset', sunset);
+ log_debug(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
+ }
+
+ _regularly_update_suntimes() {
+ log_debug('Regularly updating sun times...');
+ this._regularly_update_suntimes_timer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3600, () => {
+ this._update_suntimes();
+ return true; // Repeat the loop
+ });
+ }
+
+ _stop_regularly_updating_suntimes() {
+ GLib.Source.remove(this._regularly_update_suntimes_timer);
+ this._regularly_update_suntimes_timer = null;
+ log_debug('Stopped regularly updating sun times.');
+ }
+
+ _is_daytime() {
+ const time = GLib.DateTime.new_now_local();
+ const hour = time.get_hour() + time.get_minute() / 60 + time.get_second() / 3600;
+ return ( hour >= this._suntimes.get('sunrise') && hour <= this._suntimes.get('sunset') );
+ }
+
+ _watch_for_time_change() {
+ log_debug('Watching for time change...');
+ this._time_change_timer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
+ if ( !Me.imports.extension.enabled ) {
+ // The extension doesn't exist anymore, quit the loop
+ return false;
+ }
+ if ( this._previously_daytime !== this._is_daytime() ) {
+ this._previously_daytime = this._is_daytime();
+ this.emit('time-changed', this.time);
+ }
+ return true; // Repeat the loop
+ });
+ }
+
+ _stop_watching_for_time_change() {
+ GLib.Source.remove(this._time_change_timer);
+ log_debug('Stopped watching for time change.');
+ }
+
+}
+Signals.addSignalMethods(TimerLocation.prototype);
diff --git a/src/modules/TimerNightlight.js b/src/modules/TimerNightlight.js
new file mode 100644
index 0000000..24cbb41
--- /dev/null
+++ b/src/modules/TimerNightlight.js
@@ -0,0 +1,114 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio, GLib } = imports.gi;
+const { extensionUtils, fileUtils } = imports.misc;
+const Signals = imports.signals;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const { log_debug } = Me.imports.utils;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ fileUtils.loadInterfaceXML = Me.imports.convenience.loadInterfaceXML;
+}
+
+
+const COLOR_INTERFACE = `
+
+
+
+
+`;
+
+
+/**
+ * The Night Light Timer uses Night Light as a time source.
+ *
+ * It connects to the Color SettingsDaemon DBus proxy to listen to the
+ * 'NightLightActive' property and will signal any change.
+ */
+var TimerNightlight = class {
+
+ enable() {
+ log_debug('Enabling Night Light Timer...');
+ this._connect_to_color_dbus_proxy();
+ this._listen_to_nightlight_state();
+ this.emit('time-changed', this.time);
+ log_debug('Night Light Timer enabled.');
+ }
+
+ disable() {
+ log_debug('Disabling Night Light Timer...');
+ this._stop_listening_to_nightlight_state();
+ this._disconnect_from_color_dbus_proxy();
+ log_debug('Night Light Timer disabled.');
+ }
+
+
+ get time() {
+ return this._is_nightlight_active() ? 'night' : 'day';
+ }
+
+
+ _connect_to_color_dbus_proxy() {
+ log_debug('Connecting to Color DBus proxy...');
+ const ColorProxy = Gio.DBusProxy.makeProxyWrapper(COLOR_INTERFACE);
+ this._color_dbus_proxy = new ColorProxy(
+ Gio.DBus.session,
+ 'org.gnome.SettingsDaemon.Color',
+ '/org/gnome/SettingsDaemon/Color'
+ );
+ log_debug('Connected to Color DBus proxy.');
+ }
+
+ _disconnect_from_color_dbus_proxy() {
+ log_debug('Disconnecting from Color DBus proxy...');
+ this._color_dbus_proxy = null;
+ log_debug('Disconnected from Color DBus proxy.');
+ }
+
+ _listen_to_nightlight_state() {
+ log_debug('Listening to Night Light state...');
+ this._nightlight_state_connect = this._color_dbus_proxy.connect(
+ 'g-properties-changed',
+ this._on_nightlight_state_changed.bind(this)
+ );
+ }
+
+ _stop_listening_to_nightlight_state() {
+ this._color_dbus_proxy.disconnect(this._nightlight_state_connect);
+ log_debug('Stopped listening to Night Light state.');
+ }
+
+ _on_nightlight_state_changed(sender, dbus_properties) {
+ const properties = dbus_properties.deep_unpack();
+ if ( properties.NightLightActive ) {
+ log_debug('Night Light has become ' + (properties.NightLightActive.unpack() ? '' : 'in') + 'active.');
+ this.emit('time-changed', this.time);
+ }
+ }
+
+ _is_nightlight_active() {
+ return this._color_dbus_proxy.NightLightActive;
+ }
+
+}
+Signals.addSignalMethods(TimerNightlight.prototype);
diff --git a/src/modules/TimerSchedule.js b/src/modules/TimerSchedule.js
new file mode 100644
index 0000000..9f8bc0c
--- /dev/null
+++ b/src/modules/TimerSchedule.js
@@ -0,0 +1,88 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { GLib } = imports.gi;
+const { extensionUtils } = imports.misc;
+const Signals = imports.signals;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const e = Me.imports.extension;
+const { log_debug } = Me.imports.utils;
+
+
+/**
+ * The Schedule Timer uses a manual schedule to get the current time.
+ *
+ * Every second, it will check if the time has changed and signal if that's the
+ * case.
+ *
+ * The user can change the schedule in the extension's preferences.
+ */
+var TimerSchedule = class {
+
+ constructor() {
+ this._previously_daytime = null;
+ }
+
+ enable() {
+ log_debug('Enabling Schedule Timer...');
+ this._watch_for_time_change();
+ log_debug('Schedule Timer enabled.');
+ }
+
+ disable() {
+ log_debug('Disabling Schedule Timer...');
+ this._stop_watching_for_time_change();
+ log_debug('Schedule Timer disabled.');
+ }
+
+
+ get time() {
+ return this._is_daytime() ? 'day' : 'night';
+ }
+
+
+ _is_daytime() {
+ const time = GLib.DateTime.new_now_local();
+ const hour = time.get_hour() + time.get_minute() / 60 + time.get_second() / 3600;
+ return ( hour >= e.settingsManager.schedule_sunrise && hour <= e.settingsManager.schedule_sunset );
+ }
+
+ _watch_for_time_change() {
+ log_debug('Watching for time change...');
+ this._time_change_timer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
+ if ( !Me.imports.extension.enabled ) {
+ // The extension doesn't exist anymore, quit the loop
+ return false;
+ }
+ if ( this._previously_daytime !== this._is_daytime() ) {
+ this._previously_daytime = this._is_daytime();
+ this.emit('time-changed', this.time);
+ }
+ return true; // Repeat the loop
+ });
+ }
+
+ _stop_watching_for_time_change() {
+ GLib.Source.remove(this._time_change_timer);
+ log_debug('Stopped watching for time change.');
+ }
+
+}
+Signals.addSignalMethods(TimerSchedule.prototype);
diff --git a/src/po/fr.po b/src/po/fr.po
index 2cd63a4..e27e94e 100644
--- a/src/po/fr.po
+++ b/src/po/fr.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Night Theme Switcher 7\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-05-09 10:52+0200\n"
-"PO-Revision-Date: 2020-05-09 10:58+0200\n"
+"POT-Creation-Date: 2020-05-25 10:09+0200\n"
+"PO-Revision-Date: 2020-05-24 14:51+0200\n"
"Last-Translator: Romain Vigier \n"
"Language-Team: French - France \n"
"Language: fr_FR\n"
@@ -18,165 +18,405 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"X-Generator: Gtranslator 3.36.0\n"
-#: src/prefs.js:65
-msgid "Theme"
-msgstr "Thème"
+#: src/modules/GtkThemer.js:152
+#, javascript-format
+msgid ""
+"Unable to automatically detect the day and night variants for the \"%s\" GTK "
+"theme. Please manually choose them in the extension's preferences."
+msgstr ""
+"Impossible de détecter automatiquement les variantes diurnes et nocturnes "
+"pour le thème GTK \"%s\". Veuillez les sélectionner manuellement dans les "
+"préférences de l’extension."
-#: src/prefs.js:71
+#: src/modules/ShellThemer.js:159
+#, javascript-format
+msgid ""
+"Unable to automatically detect the day and night variants for the \"%s\" "
+"GNOME Shell theme. Please manually choose them in the extension's "
+"preferences."
+msgstr ""
+"Impossible de détecter automatiquement les variantes diurnes et nocturnes "
+"pour le thème GNOME Shell \"%s\". Veuillez les sélectionner manuellement "
+"dans les préférences de l’extension."
+
+#: src/preferences/Backgrounds.js:39
+msgid "Backgrounds"
+msgstr "Arrière-plans"
+
+#: src/preferences/Backgrounds.js:40
+msgid "You can set different backgrounds for day and night."
+msgstr "Vous pouvez définir des arrière-plans diurnes et nocturnes différents."
+
+#: src/preferences/Backgrounds.js:43
+msgid "Switch backgrounds"
+msgstr "Changer les arrière-plans"
+
+#: src/preferences/Backgrounds.js:44
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:91
+msgid "Day background"
+msgstr "Arrière-plan diurne"
+
+#: src/preferences/Backgrounds.js:45
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:96
+msgid "Night background"
+msgstr "Arrière-plan nocturne"
+
+#: src/preferences/Backgrounds.js:87
+msgid "Select your background image"
+msgstr "Sélectionnez votre image d’arrière-plan"
+
+#: src/preferences/Commands.js:39
+msgid "Commands"
+msgstr "Commandes"
+
+#: src/preferences/Commands.js:40
+msgid ""
+"You can set custom commands that will be run when the time of the day "
+"changes."
+msgstr ""
+"Vous pouvez définir des commandes personnalisées qui seront lancées au "
+"moment du changement."
+
+#: src/preferences/Commands.js:43
+msgid "Use custom commands"
+msgstr "Utiliser des commandes personnalisées"
+
+#: src/preferences/Commands.js:44 src/preferences/Schedule.js:45
+msgid "Sunrise"
+msgstr "Lever du soleil"
+
+#: src/preferences/Commands.js:45 src/preferences/Schedule.js:46
+msgid "Sunset"
+msgstr "Coucher du soleil"
+
+#: src/preferences/Commands.js:76
+msgid "Hello sunshine!"
+msgstr "Bonjour!"
+
+#: src/preferences/Commands.js:77
+msgid "Hello moonshine!"
+msgstr "Bonsoir!"
+
+#: src/preferences/GtkTheme.js:40
+msgid "GTK theme"
+msgstr "Thème GTK"
+
+#: src/preferences/GtkTheme.js:41
msgid ""
"The extension will try to automatically detect the day and night variants of "
-"your theme.\n"
+"your GTK theme.\n"
+"\n"
"If the theme you use isn't supported, please submit a request"
-"a>.\n"
-"If you prefer, you can manually set variants."
+"a>. You can also manually set variants."
msgstr ""
-"L’extension essayera de détecter automatiquement les variantes diurne et "
-"nocturne de votre thème.\n"
-"Si le thème que vous utilisez n’est pas supporté, merci d’en "
-"faire la requête.\n"
-"Si vous préférez, vous pouvez définir manuellement les variantes."
+"L’extension essayera de déterminer automatiquement les variantes diurnes et "
+"nocturnes de votre thème GTK.\n"
+"\n"
+"Si le thème que vous utilisez n’est pas pris en charge, merci d’ouvrir une demande. Vous pouvez aussi sélectionner manuellement les "
+"variantes."
-#: src/prefs.js:107
+#: src/preferences/GtkTheme.js:44 src/preferences/ShellTheme.js:44
msgid "Manual variants"
msgstr "Variantes manuelles"
-#: src/prefs.js:151
+#: src/preferences/GtkTheme.js:45 src/preferences/ShellTheme.js:45
msgid "Day variant"
msgstr "Variante diurne"
-#: src/prefs.js:152
+#: src/preferences/GtkTheme.js:46 src/preferences/ShellTheme.js:46
msgid "Night variant"
msgstr "Variante nocturne"
-#: src/prefs.js:197
+#: src/preferences/Schedule.js:39
msgid "Schedule"
msgstr "Horaires"
-#: src/prefs.js:203
+#: src/preferences/Schedule.js:40
msgid ""
"The extension will try to use Night Light or Location Services to "
"automatically set your current sunrise and sunset times if they are "
"enabled.\n"
-"If you prefer, you can set a manual schedule."
+"\n"
+"If you prefer, you can manually choose a time source."
msgstr ""
"S’ils sont activés, l’extension essayera d’utiliser le mode nuit ou les "
-"services de localisation pour définir automatiquement les heures de lever et "
-"de coucher du soleil.\n"
-"Si vous préférez, vous pouvez définir manuellement un horaire."
+"services de localisation pour déterminer automatiquement les heures "
+"actuelles de lever et de coucher du soleil.\n"
+"\n"
+"Si vous préférez, vous pouvez sélectionner manuellement une source "
+"d’horaires."
-#: src/prefs.js:238
+#: src/preferences/Schedule.js:43
+msgid "Manual time source"
+msgstr "Source d’horaires manuelle"
+
+#: src/preferences/Schedule.js:44
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:51
+msgid "Time source"
+msgstr "Source d’horaires"
+
+#: src/preferences/Schedule.js:87
+msgid "Night Light"
+msgstr "Mode nuit"
+
+#: src/preferences/Schedule.js:102
+msgid "Location Services"
+msgstr "Services de localisation"
+
+#: src/preferences/Schedule.js:118
msgid "Manual schedule"
msgstr "Horaire manuel"
-#: src/prefs.js:283
-msgid "Sunrise"
-msgstr "Lever du soleil"
+#: src/preferences/ShellTheme.js:40
+msgid "Shell theme"
+msgstr "Thème du shell"
-#: src/prefs.js:284
-msgid "Sunset"
-msgstr "Coucher du soleil"
-
-#: src/modules/Themer.js:140
-#, javascript-format
+#: src/preferences/ShellTheme.js:41
msgid ""
-"The extension cannot detect the day and night variants for the \"%s\" theme. "
-"Please choose another theme or manually set variants in the extension "
-"preferences."
+"The extension will try to automatically detect the day and night variants of "
+"your GNOME Shell theme.\n"
+"\n"
+"If the theme you use isn't supported, please submit a request"
+"a>. You can also manually set variants."
msgstr ""
-"L’extension ne parvient pas à détecter les variantes diurne et nocturne pour "
-"le thème \"%s\". Merci de sélectionner un autre thème ou de définir "
-"manuellement les variantes dans les préférences de l’extension."
+"L’extension essayera de déterminer automatiquement les variantes diurnes et "
+"nocturnes de votre thème GNOME Shell.\n"
+"\n"
+"Si le thème que vous utilisez n’est pas pris en charge, merci d’ouvrir une demande. Vous pouvez aussi sélectionner manuellement les "
+"variantes."
-#: src/modules/Timer.js:269
-msgid "Unable to connect to Color DBus proxy."
-msgstr "Impossible de se connecter à la passerelle DBus Color."
-
-#: src/modules/Timer.js:331
-msgid "Unable to connect to GeoClue Manager DBus proxy."
-msgstr "Impossible de se connecter à la passerelle DBus GeoClue Manager."
-
-#: src/modules/Timer.js:341
-msgid "Unable to get a GeoClue Client."
-msgstr "Impossible d’obtenir un client GeoClue."
-
-#: src/modules/Timer.js:359
-msgid "Unable to connect to GeoClue Client DBus proxy."
-msgstr "Impossible de se connecter à la passerelle DBus GeoClue Client."
-
-#: src/modules/Timer.js:422
-msgid "Unable to connect to GeoClue Location DBus proxy."
-msgstr "Impossible de se connecter à la passerelle DBus GeoClue Location."
-
-#: src/modules/Timer.js:450
-msgid "Unable to get current location."
-msgstr "Impossible d’obtenir l’emplacement actuel."
+#: src/preferences/ShellTheme.js:78
+msgid "Default"
+msgstr "Par défaut"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:6
-msgid "Use manual variants"
-msgstr "Utiliser des variantes manuelles"
+msgid "Day GTK theme"
+msgstr "Thème GTK diurne"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:7
-msgid "Disable automatic variants detection"
-msgstr "Désactive la détection automatique des variantes"
+msgid "The GTK theme to use during daytime"
+msgstr "Le thème GTK à utiliser le jour"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:11
-msgid "Day theme"
-msgstr "Thème diurne"
+msgid "Night GTK theme"
+msgstr "Thème GTK nocturne"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:12
-msgid "The theme to use during daytime"
-msgstr "Thème à utiliser pendant la journée"
+msgid "The GTK theme to use during nighttime"
+msgstr "Le thème GTK à utiliser la nuit"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:16
-msgid "Night theme"
-msgstr "Thème nocturne"
+msgid "Original GTK theme"
+msgstr "Thème GTK original"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:17
-msgid "The theme to use during nighttime"
-msgstr "Thème à utiliser pendant la nuit"
+msgid "The GTK theme to restore when the extension is disabled"
+msgstr "Le thème GTK à restaurer quand l’extension est désactivée"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:21
-msgid "Original theme"
-msgstr "Thème original"
+msgid "Use manual GTK variants"
+msgstr "Utiliser des variantes GTK manuelles"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:22
-msgid "The theme to restore if the extension is disabled"
-msgstr "Thème à restaurer si l’extension est désactivée"
+msgid "Disable automatic GTK theme variants detection"
+msgstr "Désactiver la détection automatique des variantes du thème GTK"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:26
-msgid "Use manual schedule"
-msgstr "Utiliser un horaire manuel"
+msgid "Day shell theme"
+msgstr "Thème shell diurne"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:27
-msgid "Disable automatic times detection"
-msgstr "Désactive la détection automatique des horaires"
+msgid "The shell theme to use during daytime"
+msgstr "Le thème shell à utiliser le jour"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:31
+msgid "Night shell theme"
+msgstr "Thème shell nocturne"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:32
+msgid "The shell theme to use during nighttime"
+msgstr "Le thème shell à utiliser la nuit"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:36
-msgid "Time source"
-msgstr "Source des horaires"
+msgid "Original shell theme"
+msgstr "Thème shell original"
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:37
+msgid "The shell theme to restore when the extension is disabled"
+msgstr "Le thème shell à restaurer quand l’extension est désactivée"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:41
+msgid "Use manual shell variants"
+msgstr "Utiliser des variantes shell manuelles"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:42
+msgid "Disable automatic shell theme variants detection"
+msgstr "Désactiver la détection automatique des variantes du thème shell"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:52
msgid "The source used to check current time"
msgstr "Source utilisée pour vérifier les horaires"
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:41
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:56
+msgid "Use manual time source"
+msgstr "Utiliser une source d’horaires manuelle"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:57
+msgid "Disable automatic time source detection"
+msgstr "Désactive la détection automatique de la source d’horaires"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:61
msgid "Sunrise time"
msgstr "Heure du lever"
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:42
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:62
msgid "When the day starts"
msgstr "Quand le jour se lève"
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:46
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:66
msgid "Sunset time"
msgstr "Heure du coucher"
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:47
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:67
msgid "When the day ends"
msgstr "Quand le jour se couche"
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:71
+msgid "Enable commands"
+msgstr "Activer les commandes"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:72
+msgid "Commands will be spawned on time change"
+msgstr "Les commandes seront lancées au changement d’horaire"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:76
+msgid "Sunrise command"
+msgstr "Commande du lever de soleil"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:77
+msgid "The command to spawn at sunrise"
+msgstr "La commande à lancer quand le soleil se lève"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:81
+msgid "Sunset command"
+msgstr "Commande du coucher de soleil"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:82
+msgid "The command to spawn at sunset"
+msgstr "La commande à lancer quand le soleil se couche"
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:86
+msgid "Enable backgrounds"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:87
+msgid "Background will be changed on time change"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:92
+msgid "Path to the day background"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:97
+msgid "Path to the night background"
+msgstr ""
+
+#~ msgid "Change backgrounds"
+#~ msgstr "Changer les arrière-plans"
+
+#~ msgid "Theme"
+#~ msgstr "Thème"
+
+#~ msgid ""
+#~ "The extension will try to automatically detect the day and night variants "
+#~ "of your theme.\n"
+#~ "If the theme you use isn't supported, please submit a "
+#~ "request.\n"
+#~ "If you prefer, you can manually set variants."
+#~ msgstr ""
+#~ "L’extension essayera de détecter automatiquement les variantes diurne et "
+#~ "nocturne de votre thème.\n"
+#~ "Si le thème que vous utilisez n’est pas supporté, merci d’en faire la requête.\n"
+#~ "Si vous préférez, vous pouvez définir manuellement les variantes."
+
+#~ msgid ""
+#~ "The extension will try to use Night Light or Location Services to "
+#~ "automatically set your current sunrise and sunset times if they are "
+#~ "enabled.\n"
+#~ "If you prefer, you can set a manual schedule."
+#~ msgstr ""
+#~ "S’ils sont activés, l’extension essayera d’utiliser le mode nuit ou les "
+#~ "services de localisation pour définir automatiquement les heures de lever "
+#~ "et de coucher du soleil.\n"
+#~ "Si vous préférez, vous pouvez définir manuellement un horaire."
+
+#, javascript-format
+#~ msgid ""
+#~ "The extension cannot detect the day and night variants for the \"%s\" "
+#~ "theme. Please choose another theme or manually set variants in the "
+#~ "extension preferences."
+#~ msgstr ""
+#~ "L’extension ne parvient pas à détecter les variantes diurne et nocturne "
+#~ "pour le thème \"%s\". Merci de sélectionner un autre thème ou de définir "
+#~ "manuellement les variantes dans les préférences de l’extension."
+
+#~ msgid "Unable to connect to Color DBus proxy."
+#~ msgstr "Impossible de se connecter à la passerelle DBus Color."
+
+#~ msgid "Unable to connect to GeoClue Manager DBus proxy."
+#~ msgstr "Impossible de se connecter à la passerelle DBus GeoClue Manager."
+
+#~ msgid "Unable to get a GeoClue Client."
+#~ msgstr "Impossible d’obtenir un client GeoClue."
+
+#~ msgid "Unable to connect to GeoClue Client DBus proxy."
+#~ msgstr "Impossible de se connecter à la passerelle DBus GeoClue Client."
+
+#~ msgid "Unable to connect to GeoClue Location DBus proxy."
+#~ msgstr "Impossible de se connecter à la passerelle DBus GeoClue Location."
+
+#~ msgid "Unable to get current location."
+#~ msgstr "Impossible d’obtenir l’emplacement actuel."
+
+#~ msgid "Use manual variants"
+#~ msgstr "Utiliser des variantes manuelles"
+
+#~ msgid "Disable automatic variants detection"
+#~ msgstr "Désactive la détection automatique des variantes"
+
+#~ msgid "Day theme"
+#~ msgstr "Thème diurne"
+
+#~ msgid "The theme to use during daytime"
+#~ msgstr "Thème à utiliser pendant la journée"
+
+#~ msgid "Night theme"
+#~ msgstr "Thème nocturne"
+
+#~ msgid "The theme to use during nighttime"
+#~ msgstr "Thème à utiliser pendant la nuit"
+
+#~ msgid "Original theme"
+#~ msgstr "Thème original"
+
+#~ msgid "The theme to restore if the extension is disabled"
+#~ msgstr "Thème à restaurer si l’extension est désactivée"
+
+#~ msgid "Use manual schedule"
+#~ msgstr "Utiliser un horaire manuel"
+
+#~ msgid "Disable automatic times detection"
+#~ msgstr "Désactive la détection automatique des horaires"
+
#~ msgid "Manual schedule:"
#~ msgstr "Horaire manuel :"
diff --git a/src/po/nightthemeswitcher@romainvigier.fr.pot b/src/po/nightthemeswitcher@romainvigier.fr.pot
index 9849bc5..6094625 100644
--- a/src/po/nightthemeswitcher@romainvigier.fr.pot
+++ b/src/po/nightthemeswitcher@romainvigier.fr.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: Night Theme Switcher 26\n"
+"Project-Id-Version: Night Theme Switcher 30\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-05-09 10:52+0200\n"
+"POT-Creation-Date: 2020-05-25 10:09+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -17,148 +17,283 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: src/prefs.js:65
-msgid "Theme"
+#: src/modules/GtkThemer.js:152
+#, javascript-format
+msgid ""
+"Unable to automatically detect the day and night variants for the \"%s\" GTK "
+"theme. Please manually choose them in the extension's preferences."
msgstr ""
-#: src/prefs.js:71
+#: src/modules/ShellThemer.js:159
+#, javascript-format
+msgid ""
+"Unable to automatically detect the day and night variants for the \"%s\" "
+"GNOME Shell theme. Please manually choose them in the extension's "
+"preferences."
+msgstr ""
+
+#: src/preferences/Backgrounds.js:39
+msgid "Backgrounds"
+msgstr ""
+
+#: src/preferences/Backgrounds.js:40
+msgid "You can set different backgrounds for day and night."
+msgstr ""
+
+#: src/preferences/Backgrounds.js:43
+msgid "Switch backgrounds"
+msgstr ""
+
+#: src/preferences/Backgrounds.js:44
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:91
+msgid "Day background"
+msgstr ""
+
+#: src/preferences/Backgrounds.js:45
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:96
+msgid "Night background"
+msgstr ""
+
+#: src/preferences/Backgrounds.js:87
+msgid "Select your background image"
+msgstr ""
+
+#: src/preferences/Commands.js:39
+msgid "Commands"
+msgstr ""
+
+#: src/preferences/Commands.js:40
+msgid ""
+"You can set custom commands that will be run when the time of the day "
+"changes."
+msgstr ""
+
+#: src/preferences/Commands.js:43
+msgid "Use custom commands"
+msgstr ""
+
+#: src/preferences/Commands.js:44 src/preferences/Schedule.js:45
+msgid "Sunrise"
+msgstr ""
+
+#: src/preferences/Commands.js:45 src/preferences/Schedule.js:46
+msgid "Sunset"
+msgstr ""
+
+#: src/preferences/Commands.js:76
+msgid "Hello sunshine!"
+msgstr ""
+
+#: src/preferences/Commands.js:77
+msgid "Hello moonshine!"
+msgstr ""
+
+#: src/preferences/GtkTheme.js:40
+msgid "GTK theme"
+msgstr ""
+
+#: src/preferences/GtkTheme.js:41
msgid ""
"The extension will try to automatically detect the day and night variants of "
-"your theme.\n"
+"your GTK theme.\n"
+"\n"
"If the theme you use isn't supported, please submit a request"
-"a>.\n"
-"If you prefer, you can manually set variants."
+"a>. You can also manually set variants."
msgstr ""
-#: src/prefs.js:107
+#: src/preferences/GtkTheme.js:44 src/preferences/ShellTheme.js:44
msgid "Manual variants"
msgstr ""
-#: src/prefs.js:151
+#: src/preferences/GtkTheme.js:45 src/preferences/ShellTheme.js:45
msgid "Day variant"
msgstr ""
-#: src/prefs.js:152
+#: src/preferences/GtkTheme.js:46 src/preferences/ShellTheme.js:46
msgid "Night variant"
msgstr ""
-#: src/prefs.js:197
+#: src/preferences/Schedule.js:39
msgid "Schedule"
msgstr ""
-#: src/prefs.js:203
+#: src/preferences/Schedule.js:40
msgid ""
"The extension will try to use Night Light or Location Services to "
"automatically set your current sunrise and sunset times if they are "
"enabled.\n"
-"If you prefer, you can set a manual schedule."
+"\n"
+"If you prefer, you can manually choose a time source."
msgstr ""
-#: src/prefs.js:238
-msgid "Manual schedule"
+#: src/preferences/Schedule.js:43
+msgid "Manual time source"
msgstr ""
-#: src/prefs.js:283
-msgid "Sunrise"
-msgstr ""
-
-#: src/prefs.js:284
-msgid "Sunset"
-msgstr ""
-
-#: src/modules/Themer.js:140
-#, javascript-format
-msgid ""
-"The extension cannot detect the day and night variants for the \"%s\" theme. "
-"Please choose another theme or manually set variants in the extension "
-"preferences."
-msgstr ""
-
-#: src/modules/Timer.js:269
-msgid "Unable to connect to Color DBus proxy."
-msgstr ""
-
-#: src/modules/Timer.js:331
-msgid "Unable to connect to GeoClue Manager DBus proxy."
-msgstr ""
-
-#: src/modules/Timer.js:341
-msgid "Unable to get a GeoClue Client."
-msgstr ""
-
-#: src/modules/Timer.js:359
-msgid "Unable to connect to GeoClue Client DBus proxy."
-msgstr ""
-
-#: src/modules/Timer.js:422
-msgid "Unable to connect to GeoClue Location DBus proxy."
-msgstr ""
-
-#: src/modules/Timer.js:450
-msgid "Unable to get current location."
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:6
-msgid "Use manual variants"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:7
-msgid "Disable automatic variants detection"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:11
-msgid "Day theme"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:12
-msgid "The theme to use during daytime"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:16
-msgid "Night theme"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:17
-msgid "The theme to use during nighttime"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:21
-msgid "Original theme"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:22
-msgid "The theme to restore if the extension is disabled"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:26
-msgid "Use manual schedule"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:27
-msgid "Disable automatic times detection"
-msgstr ""
-
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:36
+#: src/preferences/Schedule.js:44
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:51
msgid "Time source"
msgstr ""
+#: src/preferences/Schedule.js:87
+msgid "Night Light"
+msgstr ""
+
+#: src/preferences/Schedule.js:102
+msgid "Location Services"
+msgstr ""
+
+#: src/preferences/Schedule.js:118
+msgid "Manual schedule"
+msgstr ""
+
+#: src/preferences/ShellTheme.js:40
+msgid "Shell theme"
+msgstr ""
+
+#: src/preferences/ShellTheme.js:41
+msgid ""
+"The extension will try to automatically detect the day and night variants of "
+"your GNOME Shell theme.\n"
+"\n"
+"If the theme you use isn't supported, please submit a request"
+"a>. You can also manually set variants."
+msgstr ""
+
+#: src/preferences/ShellTheme.js:78
+msgid "Default"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:6
+msgid "Day GTK theme"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:7
+msgid "The GTK theme to use during daytime"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:11
+msgid "Night GTK theme"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:12
+msgid "The GTK theme to use during nighttime"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:16
+msgid "Original GTK theme"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:17
+msgid "The GTK theme to restore when the extension is disabled"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:21
+msgid "Use manual GTK variants"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:22
+msgid "Disable automatic GTK theme variants detection"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:26
+msgid "Day shell theme"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:27
+msgid "The shell theme to use during daytime"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:31
+msgid "Night shell theme"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:32
+msgid "The shell theme to use during nighttime"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:36
+msgid "Original shell theme"
+msgstr ""
+
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:37
-msgid "The source used to check current time"
+msgid "The shell theme to restore when the extension is disabled"
msgstr ""
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:41
-msgid "Sunrise time"
+msgid "Use manual shell variants"
msgstr ""
#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:42
+msgid "Disable automatic shell theme variants detection"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:52
+msgid "The source used to check current time"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:56
+msgid "Use manual time source"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:57
+msgid "Disable automatic time source detection"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:61
+msgid "Sunrise time"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:62
msgid "When the day starts"
msgstr ""
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:46
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:66
msgid "Sunset time"
msgstr ""
-#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:47
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:67
msgid "When the day ends"
msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:71
+msgid "Enable commands"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:72
+msgid "Commands will be spawned on time change"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:76
+msgid "Sunrise command"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:77
+msgid "The command to spawn at sunrise"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:81
+msgid "Sunset command"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:82
+msgid "The command to spawn at sunset"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:86
+msgid "Enable backgrounds"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:87
+msgid "Background will be changed on time change"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:92
+msgid "Path to the day background"
+msgstr ""
+
+#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:97
+msgid "Path to the night background"
+msgstr ""
diff --git a/src/preferences/Backgrounds.js b/src/preferences/Backgrounds.js
new file mode 100644
index 0000000..9f0c686
--- /dev/null
+++ b/src/preferences/Backgrounds.js
@@ -0,0 +1,111 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { GdkPixbuf, Gio, Gtk } = imports.gi;
+const { extensionUtils } = imports.misc;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const { SettingsPage, SettingsList, SettingsListRow } = Me.imports.preferences.bases;
+
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.getSettings = Me.imports.convenience.getSettings;
+}
+
+
+var BackgroundsPreferences = class {
+
+ constructor() {
+ const label = _('Backgrounds');
+ const description = _('You can set different backgrounds for day and night.');
+ const content = new SettingsList();
+
+ content.add_row(new SettingsListRow(_('Switch backgrounds'), new BackgroundsEnabledControl()));
+ content.add_row(new SettingsListRow(_('Day background'), new TimeBackgroundControl('day')));
+ content.add_row(new SettingsListRow(_('Night background'), new TimeBackgroundControl('night')));
+
+ return new SettingsPage(label, description, content);
+ }
+
+}
+
+
+class BackgroundsEnabledControl {
+
+ constructor() {
+ const settings = extensionUtils.getSettings();
+ const toggle = new Gtk.Switch({
+ active: settings.get_boolean('backgrounds-enabled')
+ });
+ settings.bind(
+ 'backgrounds-enabled',
+ toggle,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return toggle;
+ }
+
+}
+
+
+class TimeBackgroundControl {
+
+ constructor(time) {
+ const settings = extensionUtils.getSettings();
+
+ const filter = new Gtk.FileFilter();
+ filter.add_mime_type('image/jpeg');
+ filter.add_mime_type('image/png');
+ filter.add_mime_type('image/tiff');
+
+ const preview = new Gtk.Image({
+ pixel_size: 256
+ });
+
+ const button = new Gtk.FileChooserButton({
+ title: _('Select your background image'),
+ action: Gtk.FileChooserAction.OPEN,
+ filter: filter,
+ preview_widget: preview,
+ use_preview_label: false
+ });
+ button.set_uri(settings.get_string(`background-${time}`));
+ button.connect('update-preview', () => {
+ const file = button.get_preview_filename();
+ const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(file, 256, 256);
+ preview.set_from_pixbuf(pixbuf);
+ });
+ button.connect('file-set', () => settings.set_string(`background-${time}`, button.get_uri()));
+ settings.bind(
+ 'backgrounds-enabled',
+ button,
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ settings.connect(`changed::background-${time}`, () => button.set_uri(settings.get_string(`background-${time}`)));
+
+ return button;
+ }
+
+}
diff --git a/src/preferences/Commands.js b/src/preferences/Commands.js
new file mode 100644
index 0000000..0aa425f
--- /dev/null
+++ b/src/preferences/Commands.js
@@ -0,0 +1,99 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio, Gtk } = imports.gi;
+const { extensionUtils } = imports.misc;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const { SettingsPage, SettingsList, SettingsListRow } = Me.imports.preferences.bases;
+
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.getSettings = Me.imports.convenience.getSettings;
+}
+
+
+var CommandsPreferences = class {
+
+ constructor() {
+ const label = _('Commands');
+ const description = _('You can set custom commands that will be run when the time of the day changes.');
+ const content = new SettingsList();
+
+ content.add_row(new SettingsListRow(_('Use custom commands'), new CommandsEnabledControl()));
+ content.add_row(new SettingsListRow(_('Sunrise'), new SuntimeCommandControl('sunrise')));
+ content.add_row(new SettingsListRow(_('Sunset'), new SuntimeCommandControl('sunset')));
+
+ return new SettingsPage(label, description, content);
+ }
+
+}
+
+
+class CommandsEnabledControl {
+
+ constructor() {
+ const settings = extensionUtils.getSettings();
+ const toggle = new Gtk.Switch({
+ active: settings.get_boolean('commands-enabled')
+ });
+ settings.bind(
+ 'commands-enabled',
+ toggle,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return toggle;
+ }
+
+}
+
+
+class SuntimeCommandControl {
+
+ constructor(suntime) {
+ const message = new Map([
+ ['sunrise', _('Hello sunshine!')],
+ ['sunset', _('Hello moonshine!')]
+ ]);
+ const settings = extensionUtils.getSettings();
+ const entry = new Gtk.Entry({
+ width_request: 300,
+ placeholder_text: _(`notify-send "${message.get(suntime)}"`)
+ });
+ settings.bind(
+ `command-${suntime}`,
+ entry,
+ 'text',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ settings.bind(
+ 'commands-enabled',
+ entry,
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return entry;
+ }
+
+}
diff --git a/src/preferences/GtkTheme.js b/src/preferences/GtkTheme.js
new file mode 100644
index 0000000..20c356d
--- /dev/null
+++ b/src/preferences/GtkTheme.js
@@ -0,0 +1,95 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio, Gtk } = imports.gi;
+const { extensionUtils } = imports.misc;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const { SettingsPage, SettingsList, SettingsListRow } = Me.imports.preferences.bases;
+const { get_installed_gtk_themes } = Me.imports.utils;
+
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.getSettings = Me.imports.convenience.getSettings;
+}
+
+
+var GtkThemePreferences = class {
+
+ constructor() {
+ const label = _('GTK theme');
+ const description = _('The extension will try to automatically detect the day and night variants of your GTK theme.\n\nIf the theme you use isn\'t supported, please submit a request. You can also manually set variants.');
+ const content = new SettingsList();
+
+ content.add_row(new SettingsListRow(_('Manual variants'), new ManualGtkVariantsControl()));
+ content.add_row(new SettingsListRow(_('Day variant'), new TimeGtkVariantControl('day')));
+ content.add_row(new SettingsListRow(_('Night variant'), new TimeGtkVariantControl('night')));
+
+ return new SettingsPage(label, description, content);
+ }
+
+}
+
+
+class ManualGtkVariantsControl {
+
+ constructor() {
+ const settings = extensionUtils.getSettings();
+ const toggle = new Gtk.Switch({
+ active: false
+ });
+ settings.bind(
+ 'manual-gtk-variants',
+ toggle,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return toggle;
+ }
+
+}
+
+
+class TimeGtkVariantControl {
+
+ constructor(time) {
+ const settings = extensionUtils.getSettings();
+ const combo = new Gtk.ComboBoxText();
+ const themes = Array.from(get_installed_gtk_themes()).sort();
+ themes.forEach(theme => combo.append(theme, theme));
+ settings.bind(
+ `gtk-variant-${time}`,
+ combo,
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ settings.bind(
+ 'manual-gtk-variants',
+ combo,
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return combo;
+ }
+
+}
diff --git a/src/preferences/Schedule.js b/src/preferences/Schedule.js
new file mode 100644
index 0000000..07bd3d0
--- /dev/null
+++ b/src/preferences/Schedule.js
@@ -0,0 +1,224 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio, Gtk } = imports.gi;
+const { extensionUtils } = imports.misc;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const { SettingsPage, SettingsList, SettingsListRow } = Me.imports.preferences.bases;
+
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.getSettings = Me.imports.convenience.getSettings;
+}
+
+
+var SchedulePreferences = class {
+
+ constructor() {
+ const label = _('Schedule');
+ const description = _('The extension will try to use Night Light or Location Services to automatically set your current sunrise and sunset times if they are enabled.\n\nIf you prefer, you can manually choose a time source.');
+ const content = new SettingsList();
+
+ content.add_row(new SettingsListRow(_('Manual time source'), new ManualTimeSourceControl()));
+ content.add_row(new SettingsListRow(_('Time source'), new TimeSourceControl()));
+ content.add_row(new SettingsListRow(_('Sunrise'), new SuntimeControl('sunrise')));
+ content.add_row(new SettingsListRow(_('Sunset'), new SuntimeControl('sunset')));
+
+ return new SettingsPage(label, description, content);
+ }
+
+}
+
+
+class ManualTimeSourceControl {
+
+ constructor() {
+ const settings = extensionUtils.getSettings();
+ const toggle = new Gtk.Switch({
+ active: false
+ });
+ settings.bind(
+ 'manual-time-source',
+ toggle,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return toggle;
+ }
+
+}
+
+
+class TimeSourceControl {
+
+ constructor() {
+ const settings = extensionUtils.getSettings();
+ const colorSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.plugins.color' });
+ const locationSettings = new Gio.Settings({ schema: 'org.gnome.system.location' });
+
+ const box = new Gtk.Box({
+ spacing: 8,
+ orientation: Gtk.Orientation.HORIZONTAL,
+ can_focus: false
+ });
+
+ const nightlight_radio = new Gtk.RadioButton({
+ label: _('Night Light'),
+ active: (settings.get_string('time-source') === 'nightlight')
+ });
+ const update_nightlight_radio_sensitivity = () => nightlight_radio.set_sensitive(!!(settings.get_boolean('manual-time-source') && colorSettings.get_boolean('night-light-enabled')));
+ settings.connect('changed::manual-time-source', update_nightlight_radio_sensitivity);
+ colorSettings.connect('changed::night-light-enabled', update_nightlight_radio_sensitivity);
+ update_nightlight_radio_sensitivity();
+ nightlight_radio.connect('toggled', () => {
+ if ( nightlight_radio.get_active() ) {
+ settings.set_string('time-source', 'nightlight')
+ }
+ });
+ box.pack_start(nightlight_radio, false, false, 0);
+
+ const location_radio = new Gtk.RadioButton({
+ label: _('Location Services'),
+ group: nightlight_radio,
+ active: (settings.get_string('time-source') === 'location')
+ });
+ const update_location_radio_sensitivity = () => location_radio.set_sensitive(!!(settings.get_boolean('manual-time-source') && locationSettings.get_boolean('enabled')));
+ settings.connect('changed::manual-time-source', update_location_radio_sensitivity);
+ locationSettings.connect('changed::enabled', update_location_radio_sensitivity);
+ update_location_radio_sensitivity();
+ location_radio.connect('toggled', () => {
+ if ( location_radio.get_active() ) {
+ settings.set_string('time-source', 'location')
+ }
+ });
+ box.pack_start(location_radio, false, false, 0);
+
+ const schedule_radio = new Gtk.RadioButton({
+ label: _('Manual schedule'),
+ group: nightlight_radio,
+ active: (settings.get_string('time-source') === 'schedule')
+ });
+ settings.bind(
+ 'manual-time-source',
+ schedule_radio,
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ schedule_radio.connect('toggled', () => {
+ if ( schedule_radio.get_active() ) {
+ settings.set_string('time-source', 'schedule')
+ }
+ });
+ box.pack_start(schedule_radio, false, false, 0);
+
+ return box;
+ }
+
+}
+
+
+class SuntimeControl {
+
+ constructor(suntime) {
+ const settings = extensionUtils.getSettings();
+ const box = new Gtk.Box({
+ spacing: 8,
+ orientation: Gtk.Orientation.HORIZONTAL,
+ can_focus: false
+ });
+
+ const update_spin_sensitivity = (spin) => spin.set_sensitive(!!(settings.get_boolean('manual-time-source') && settings.get_string('time-source') === 'schedule'));
+
+ const time = settings.get_double(`schedule-${suntime}`);
+ const hours = Math.trunc(time);
+ const minutes = Math.round((time - hours) * 60);
+
+ const hours_spin = new Gtk.SpinButton({
+ adjustment: new Gtk.Adjustment({
+ value: hours,
+ lower: 0,
+ upper: 23,
+ step_increment: 1,
+ page_increment: 0,
+ page_size: 0
+ }),
+ value: hours,
+ numeric: true,
+ wrap: true,
+ orientation: Gtk.Orientation.VERTICAL
+ });
+ hours_spin.connect('output', () => {
+ const text = hours_spin.adjustment.value.toString().padStart(2, '0');
+ hours_spin.set_text(text);
+ return true;
+ });
+ hours_spin.connect('value-changed', () => {
+ const old_time = settings.get_double(`schedule-${suntime}`);
+ const old_hour = Math.trunc(old_time);
+ const minutes = old_time - old_hour;
+ const new_time = hours_spin.value + minutes;
+ settings.set_double(`schedule-${suntime}`, new_time);
+ });
+ settings.connect('changed::manual-time-source', () => update_spin_sensitivity(hours_spin));
+ settings.connect('changed::time-source', () => update_spin_sensitivity(hours_spin));
+ update_spin_sensitivity(hours_spin);
+
+ const separator = new Gtk.Label({ label: ':' });
+
+ const minutes_spin = new Gtk.SpinButton({
+ adjustment: new Gtk.Adjustment({
+ value: minutes,
+ lower: 0,
+ upper: 59,
+ step_increment: 1,
+ page_increment: 0,
+ page_size: 0
+ }),
+ value: minutes,
+ numeric: true,
+ wrap: true,
+ orientation: Gtk.Orientation.VERTICAL
+ });
+ minutes_spin.connect('output', () => {
+ const text = minutes_spin.adjustment.value.toString().padStart(2, '0');
+ minutes_spin.set_text(text);
+ return true;
+ });
+ minutes_spin.connect('value-changed', () => {
+ const hour = Math.trunc(settings.get_double(`schedule-${suntime}`));
+ const minutes = minutes_spin.value / 60;
+ const new_time = hour + minutes;
+ settings.set_double(`schedule-${suntime}`, new_time);
+ });
+ settings.connect('changed::manual-time-source', () => update_spin_sensitivity(minutes_spin));
+ settings.connect('changed::time-source', () => update_spin_sensitivity(minutes_spin));
+ update_spin_sensitivity(minutes_spin);
+
+ box.pack_start(hours_spin, false, false, 0);
+ box.pack_start(separator, false, false, 0);
+ box.pack_start(minutes_spin, false, false, 0);
+ return box;
+ }
+
+}
diff --git a/src/preferences/ShellTheme.js b/src/preferences/ShellTheme.js
new file mode 100644
index 0000000..d9d6902
--- /dev/null
+++ b/src/preferences/ShellTheme.js
@@ -0,0 +1,94 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gio, Gtk } = imports.gi;
+const { extensionUtils } = imports.misc;
+
+const Me = extensionUtils.getCurrentExtension();
+
+const { SettingsPage, SettingsList, SettingsListRow } = Me.imports.preferences.bases;
+const { get_installed_shell_themes } = Me.imports.utils;
+
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
+
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.getSettings = Me.imports.convenience.getSettings;
+}
+
+
+var ShellThemePreferences = class {
+
+ constructor() {
+ const label = _('Shell theme');
+ const description = _('The extension will try to automatically detect the day and night variants of your GNOME Shell theme.\n\nIf the theme you use isn\'t supported, please submit a request. You can also manually set variants.');
+ const content = new SettingsList();
+
+ content.add_row(new SettingsListRow(_('Manual variants'), new ManualShellVariantsControl()));
+ content.add_row(new SettingsListRow(_('Day variant'), new TimeShellVariantControl('day')));
+ content.add_row(new SettingsListRow(_('Night variant'), new TimeShellVariantControl('night')));
+
+ return new SettingsPage(label, description, content);
+ }
+
+}
+
+class ManualShellVariantsControl {
+
+ constructor() {
+ const settings = extensionUtils.getSettings();
+ const toggle = new Gtk.Switch({
+ active: false
+ });
+ settings.bind(
+ 'manual-shell-variants',
+ toggle,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return toggle;
+ }
+
+}
+
+
+class TimeShellVariantControl {
+
+ constructor(time) {
+ const settings = extensionUtils.getSettings();
+ const combo = new Gtk.ComboBoxText();
+ const themes = Array.from(get_installed_shell_themes()).sort();
+ themes.forEach(theme => combo.append(theme, (theme === '' ? _('Default') : theme)));
+ settings.bind(
+ `shell-variant-${time}`,
+ combo,
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ settings.bind(
+ 'manual-shell-variants',
+ combo,
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ return combo;
+ }
+
+}
diff --git a/src/preferences/bases.js b/src/preferences/bases.js
new file mode 100644
index 0000000..b8637ac
--- /dev/null
+++ b/src/preferences/bases.js
@@ -0,0 +1,106 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const { Gtk } = imports.gi;
+
+
+var SettingsPage = class {
+
+ constructor(label, description, content_widget) {
+ this.label = new Gtk.Label({
+ label: label
+ });
+
+ this.page = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ spacing: 30,
+ margin_top: 30,
+ margin_end: 36,
+ margin_start: 36,
+ margin_bottom: 36,
+ valign: Gtk.Align.START,
+ halign: Gtk.Align.CENTER
+ });
+
+ const description_widget = new Gtk.Label({
+ label: description,
+ use_markup: true,
+ wrap: true,
+ halign: Gtk.Align.START
+ });
+ this.page.pack_start(description_widget, false, false, 0);
+
+ this.page.pack_start(content_widget, false, false, 0);
+ }
+
+}
+
+var SettingsList = class {
+
+ constructor() {
+ const frame = new Gtk.Frame({
+ can_focus: false
+ });
+
+ const list = new Gtk.ListBox({
+ border_width: 0,
+ margin: 0,
+ can_focus: false,
+ selection_mode: Gtk.SelectionMode.NONE
+ });
+ frame.add(list);
+
+ frame.add_row = (widget) => {
+ if ( list.get_children().length > 0 ) {
+ const separator = new Gtk.Separator({
+ orientation: Gtk.Orientation.VERTICAL,
+ can_focus: false
+ });
+ list.add(separator);
+ }
+ list.add(widget);
+ }
+
+ return frame;
+ }
+
+}
+
+var SettingsListRow = class {
+
+ constructor(label, widget) {
+ const row = new Gtk.Box({
+ margin: 16,
+ spacing: 30,
+ orientation: Gtk.Orientation.HORIZONTAL,
+ can_focus: false
+ });
+
+ const label_widget = new Gtk.Label({
+ label: label,
+ halign: Gtk.Align.START
+ });
+ row.pack_start(label_widget, true, true, 0);
+
+ widget.set_halign(Gtk.Align.END);
+ row.pack_start(widget, false, false, 0);
+
+ return row;
+ }
+
+}
diff --git a/src/prefs.js b/src/prefs.js
index 16e70c2..4015885 100644
--- a/src/prefs.js
+++ b/src/prefs.js
@@ -18,27 +18,26 @@ this program. If not, see .
'use strict';
-const { Gio, GLib, Gtk } = imports.gi;
+const { Gtk } = imports.gi;
const { extensionUtils } = imports.misc;
const Me = extensionUtils.getCurrentExtension();
-const config = Me.imports.config;
-const utils = Me.imports.utils;
-const { log_debug } = Me.imports.utils;
-
-const Gettext = imports.gettext.domain(Me.metadata.uuid);
-const _ = Gettext.gettext;
+const { BackgroundsPreferences } = Me.imports.preferences.Backgrounds;
+const { CommandsPreferences } = Me.imports.preferences.Commands;
+const { GtkThemePreferences } = Me.imports.preferences.GtkTheme;
+const { SchedulePreferences } = Me.imports.preferences.Schedule;
+const { ShellThemePreferences } = Me.imports.preferences.ShellTheme;
-let settings;
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+if ( shell_minor_version <= 30 ) {
+ extensionUtils.initTranslations = Me.imports.convenience.initTranslations;
+}
function init() {
- log_debug('Initializing preferences...');
- settings = extensionUtils.getSettings();
extensionUtils.initTranslations(Me.metadata.uuid);
- log_debug('Preferences initialized.');
}
function buildPrefsWidget() {
@@ -46,339 +45,22 @@ function buildPrefsWidget() {
visible: true
});
- /*
- Theme preferences
- */
+ const schedulePreferences = new SchedulePreferences();
+ prefs_widget.append_page(schedulePreferences.page, schedulePreferences.label);
- const prefs_theme_box = new Gtk.Box({
- orientation: Gtk.Orientation.VERTICAL,
- spacing: 30,
- margin_top: 30,
- margin_end: 36,
- margin_start: 36,
- margin_bottom: 36,
- valign: Gtk.Align.START,
- halign: Gtk.Align.CENTER,
- visible: true
- });
- const prefs_theme_box_label = new Gtk.Label({
- label: _('Theme'),
- visible: true
- });
- prefs_widget.append_page(prefs_theme_box, prefs_theme_box_label);
+ const gtkThemePreferences = new GtkThemePreferences();
+ prefs_widget.append_page(gtkThemePreferences.page, gtkThemePreferences.label);
- const prefs_theme_info = new Gtk.Label({
- label: _('The extension will try to automatically detect the day and night variants of your theme.\nIf the theme you use isn\'t supported, please submit a request.\nIf you prefer, you can manually set variants.'),
- use_markup: true,
- max_width_chars: 60,
- wrap: true,
- halign: Gtk.Align.START,
- opacity: 0.7,
- visible: true
- });
- prefs_theme_box.pack_start(prefs_theme_info, false, false, 0);
+ const shellThemePreferences = new ShellThemePreferences();
+ prefs_widget.append_page(shellThemePreferences.page, shellThemePreferences.label);
- const prefs_theme_frame = new Gtk.Frame({
- can_focus: false,
- visible: true
- });
- prefs_theme_box.pack_start(prefs_theme_frame, false, false, 0);
+ const backgroundsPreferences = new BackgroundsPreferences();
+ prefs_widget.append_page(backgroundsPreferences.page, backgroundsPreferences.label);
- const prefs_theme_list = new Gtk.ListBox({
- border_width: 0,
- margin: 0,
- can_focus: false,
- selection_mode: Gtk.SelectionMode.NONE,
- visible: true
- });
- prefs_theme_frame.add(prefs_theme_list);
+ const commandsPreferences = new CommandsPreferences();
+ prefs_widget.append_page(commandsPreferences.page, commandsPreferences.label);
- // Force manual toggle
- const theme_force_manual_box = new Gtk.Box({
- margin: 16,
- spacing: 12,
- orientation: Gtk.Orientation.HORIZONTAL,
- can_focus: false,
- visible: true
- });
- prefs_theme_list.add(theme_force_manual_box);
-
- const theme_force_manual_label = new Gtk.Label({
- label: _('Manual variants'),
- halign: Gtk.Align.START,
- visible: true
- });
- theme_force_manual_box.pack_start(theme_force_manual_label, true, true, 0);
-
- const theme_force_manual_toggle = new Gtk.Switch({
- active: this.settings.get_boolean('time-force-manual'),
- halign: Gtk.Align.END,
- visible: true
- });
- settings.bind(
- 'theme-force-manual',
- theme_force_manual_toggle,
- 'active',
- Gio.SettingsBindFlags.DEFAULT
- );
- theme_force_manual_box.pack_start(theme_force_manual_toggle, false, false, 0);
-
- prefs_theme_list.add(new Gtk.Separator({
- orientation: Gtk.Orientation.VERTICAL,
- can_focus: false,
- visible: true
- }));
-
- ['day', 'night'].forEach((time, i, times) => {
- const variant_box = new Gtk.Box({
- margin: 16,
- spacing: 12,
- orientation: Gtk.Orientation.HORIZONTAL,
- can_focus: false,
- visible: true
- });
- prefs_theme_list.add(variant_box);
-
- if ( i < times.length - 1 ) {
- prefs_theme_list.add(new Gtk.Separator({
- orientation: Gtk.Orientation.VERTICAL,
- can_focus: false,
- visible: true
- }));
- }
-
- const variant_labels = new Map([
- ['day', _('Day variant')],
- ['night', _('Night variant')]
- ]);
- const variant_label = new Gtk.Label({
- label: variant_labels.get(time),
- halign: Gtk.Align.START,
- visible: true
- })
- variant_box.pack_start(variant_label, true, true, 0);
-
- const variant_combo = new Gtk.ComboBoxText({
- visible: true
- });
- const themes = Array.from(utils.get_installed_themes()).sort();
- themes.forEach(theme => variant_combo.append(theme, theme));
- settings.bind(
- `theme-${time}`,
- variant_combo,
- 'active-id',
- Gio.SettingsBindFlags.DEFAULT
- );
- settings.bind(
- 'theme-force-manual',
- variant_combo,
- 'sensitive',
- Gio.SettingsBindFlags.DEFAULT
- );
- variant_box.pack_start(variant_combo, false, false, 0);
-
- });
-
- /*
- Schedule preferences
- */
- const prefs_time_box = new Gtk.Box({
- orientation: Gtk.Orientation.VERTICAL,
- spacing: 30,
- margin_top: 30,
- margin_end: 36,
- margin_start: 36,
- margin_bottom: 36,
- valign: Gtk.Align.START,
- halign: Gtk.Align.CENTER,
- visible: true
- });
- const prefs_time_box_label = new Gtk.Label({
- label: _('Schedule'),
- visible: true
- });
- prefs_widget.append_page(prefs_time_box, prefs_time_box_label);
-
- const prefs_time_info = new Gtk.Label({
- label: _('The extension will try to use Night Light or Location Services to automatically set your current sunrise and sunset times if they are enabled.\nIf you prefer, you can set a manual schedule.'),
- max_width_chars: 60,
- wrap: true,
- halign: Gtk.Align.START,
- opacity: 0.7,
- visible: true
- });
- prefs_time_box.pack_start(prefs_time_info, false, false, 0);
-
- const prefs_time_frame = new Gtk.Frame({
- can_focus: false,
- visible: true
- });
- prefs_time_box.pack_start(prefs_time_frame, false, false, 0);
-
- const prefs_time_list = new Gtk.ListBox({
- border_width: 0,
- margin: 0,
- can_focus: false,
- selection_mode: Gtk.SelectionMode.NONE,
- visible: true
- });
- prefs_time_frame.add(prefs_time_list);
-
- // Force manual toggle
- const force_manual_box = new Gtk.Box({
- margin: 16,
- spacing: 12,
- orientation: Gtk.Orientation.HORIZONTAL,
- can_focus: false,
- visible: true
- });
- prefs_time_list.add(force_manual_box);
-
- const force_manual_label = new Gtk.Label({
- label: _('Manual schedule'),
- halign: Gtk.Align.START,
- visible: true
- });
- force_manual_box.pack_start(force_manual_label, true, true, 0);
-
- const force_manual_toggle = new Gtk.Switch({
- active: this.settings.get_boolean('time-force-manual'),
- halign: Gtk.Align.END,
- visible: true
- });
- settings.bind(
- 'time-force-manual',
- force_manual_toggle,
- 'active',
- Gio.SettingsBindFlags.DEFAULT
- );
- force_manual_box.pack_start(force_manual_toggle, false, false, 0);
-
- prefs_time_list.add(new Gtk.Separator({
- orientation: Gtk.Orientation.VERTICAL,
- can_focus: false,
- visible: true
- }));
-
- // Times schedule
- ['sunrise', 'sunset'].forEach((suntime, i, suntimes) => {
- const time_box = new Gtk.Box({
- margin: 16,
- spacing: 12,
- orientation: Gtk.Orientation.HORIZONTAL,
- can_focus: false,
- visible: true
- });
- prefs_time_list.add(time_box);
-
- if ( i < suntimes.length - 1 ) {
- prefs_time_list.add(new Gtk.Separator({
- orientation: Gtk.Orientation.VERTICAL,
- can_focus: false,
- visible: true
- }));
- }
-
- const time_labels = new Map([
- ['sunrise', _('Sunrise')],
- ['sunset', _('Sunset')]
- ]);
- const time_label = new Gtk.Label({
- label: time_labels.get(suntime),
- halign: Gtk.Align.START,
- visible: true
- })
- time_box.pack_start(time_label, true, true, 0);
-
- const time = settings.get_double(`time-${suntime}`);
- const hour = Math.trunc(time);
- const minutes = Math.round((time - hour) * 60);
-
- const time_hour_spin = new Gtk.SpinButton({
- adjustment: new Gtk.Adjustment({
- value: hour,
- lower: 0,
- upper: 23,
- step_increment: 1,
- page_increment: 0,
- page_size: 0
- }),
- value: hour,
- numeric: true,
- wrap: true,
- orientation: Gtk.Orientation.VERTICAL,
- visible: true
- });
- settings.bind(
- 'time-force-manual',
- time_hour_spin,
- 'sensitive',
- Gio.SettingsBindFlags.DEFAULT
- );
- settings.connect(`changed::time-${suntime}`, () => {
- const hour = Math.trunc(settings.get_double(`time-${suntime}`));
- time_hour_spin.value = hour;
- });
- time_hour_spin.connect('output', () => {
- const text = time_hour_spin.adjustment.value.toString().padStart(2, '0');
- time_hour_spin.set_text(text);
- return true;
- });
- time_hour_spin.connect('value-changed', () => {
- const old_time = settings.get_double(`time-${suntime}`);
- const old_hour = Math.trunc(old_time);
- const minutes = old_time - old_hour;
- const new_time = time_hour_spin.value + minutes;
- settings.set_double(`time-${suntime}`, new_time);
- });
- time_box.pack_start(time_hour_spin, false, false, 0);
-
- const time_separator = new Gtk.Label({
- label: ':',
- visible: true
- })
- time_box.pack_start(time_separator, false, false, 0);
-
- const time_minutes_spin = new Gtk.SpinButton({
- adjustment: new Gtk.Adjustment({
- value: minutes,
- lower: 0,
- upper: 59,
- step_increment: 1,
- page_increment: 0,
- page_size: 0
- }),
- value: minutes,
- numeric: true,
- wrap: true,
- orientation: Gtk.Orientation.VERTICAL,
- visible: true
- });
- settings.bind(
- 'time-force-manual',
- time_minutes_spin,
- 'sensitive',
- Gio.SettingsBindFlags.DEFAULT
- );
- settings.connect(`changed::time-${suntime}`, () => {
- const time = settings.get_double(`time-${suntime}`);
- const hour = Math.trunc(time);
- const minutes = Math.round((time - hour) * 60);
- time_minutes_spin.value = minutes;
- });
- time_minutes_spin.connect('output', () => {
- const text = time_minutes_spin.adjustment.value.toString().padStart(2, '0');
- time_minutes_spin.set_text(text);
- return true;
- });
- time_minutes_spin.connect('value-changed', () => {
- const hour = Math.trunc(settings.get_double(`time-${suntime}`));
- const minutes = time_minutes_spin.value / 60;
- const new_time = hour + minutes;
- settings.set_double(`time-${suntime}`, new_time);
- });
- time_box.pack_start(time_minutes_spin, false, false, 0);
- });
+ prefs_widget.show_all();
return prefs_widget;
}
diff --git a/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml b/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml
index 569918e..148ddea 100644
--- a/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml
+++ b/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml
@@ -1,50 +1,100 @@
-
- false
- Use manual variants
- Disable automatic variants detection
-
-
+
"Adwaita"
- Day theme
- The theme to use during daytime
+ Day GTK theme
+ The GTK theme to use during daytime
-
+
"Adwaita-dark"
- Night theme
- The theme to use during nighttime
+ Night GTK theme
+ The GTK theme to use during nighttime
-
- ""
- Original theme
- The theme to restore if the extension is disabled
+
+ "Adwaita"
+ Original GTK theme
+ The GTK theme to restore when the extension is disabled
-
+
false
- Use manual schedule
- Disable automatic times detection
+ Use manual GTK variants
+ Disable automatic GTK theme variants detection
+
+
+ ""
+ Day shell theme
+ The shell theme to use during daytime
+
+
+ ""
+ Night shell theme
+ The shell theme to use during nighttime
+
+
+ ""
+ Original shell theme
+ The shell theme to restore when the extension is disabled
+
+
+ false
+ Use manual shell variants
+ Disable automatic shell theme variants detection
-
+
- "manual"
+ "schedule"
Time source
The source used to check current time
-
+
+ false
+ Use manual time source
+ Disable automatic time source detection
+
+
6
Sunrise time
When the day starts
-
+
20
Sunset time
When the day ends
+
+ false
+ Enable commands
+ Commands will be spawned on time change
+
+
+ ""
+ Sunrise command
+ The command to spawn at sunrise
+
+
+ ""
+ Sunset command
+ The command to spawn at sunset
+
+
+ false
+ Enable backgrounds
+ Background will be changed on time change
+
+
+ ""
+ Day background
+ Path to the day background
+
+
+ ""
+ Night background
+ Path to the night background
+
diff --git a/src/utils.js b/src/utils.js
index 13e3aae..ff91b47 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -22,51 +22,79 @@ const { extensionUtils } = imports.misc;
const Me = extensionUtils.getCurrentExtension();
const config = Me.imports.config;
+const Gettext = imports.gettext.domain(Me.metadata.uuid);
+const _ = Gettext.gettext;
-var log_debug = function(message) {
- if ( config.debug ) {
+
+const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
+
+
+/**
+ * Output a debug message to the console if the debug config is active.
+ * @param {string} message The message to log.
+ */
+function log_debug(message) {
+ if (config.debug) {
log(`[DEBUG] ${Me.metadata.name}: ${message}`);
}
}
-var log_error = function(error) {
- logError(error, Me.metadata.name);
+/**
+ * Log an error and show a notification if it has a message.
+ * @param {Error} error The error to log.
+ */
+function log_error(error) {
+ if (config.debug) {
+ logError(error, Me.metadata.name);
+ }
if ( error.message && imports.ui ) {
imports.ui.main.notifyError(Me.metadata.name, error.message);
}
}
-var get_installed_themes = function() {
+/**
+ * Get all the theme directories of the system.
+ * @returns {string[]} An array of paths.
+ */
+function get_theme_dirs_paths() {
+ return [
+ GLib.build_filenamev([GLib.get_home_dir(), '.themes']),
+ GLib.build_filenamev([GLib.get_user_data_dir(), 'themes']),
+ ...GLib.get_system_data_dirs().map(path => GLib.build_filenamev([path, 'themes']))
+ ];
+}
+
+/**
+ * Get all the installed GTK themes on the system.
+ * @returns {Set} A set containing all the installed GTK themes names.
+ */
+function get_installed_gtk_themes() {
const themes = new Set(['Adwaita', 'HighContrast', 'HighContrastInverse']);
- const data_dirs_paths = GLib.get_system_data_dirs().concat(GLib.get_user_data_dir());
- const themes_dirs_paths = data_dirs_paths.map(path => GLib.build_filenamev([path, 'themes']));
- themes_dirs_paths.push(GLib.build_filenamev([GLib.get_home_dir(), '.themes']));
-
- themes_dirs_paths.forEach(themes_dir_path => {
+ get_theme_dirs_paths().forEach(themes_dir_path => {
const themes_dir = Gio.File.new_for_path(themes_dir_path);
- if ( themes_dir.query_file_type(Gio.FileQueryInfoFlags.NONE, null) !== Gio.FileType.DIRECTORY ) {
+ if (themes_dir.query_file_type(Gio.FileQueryInfoFlags.NONE, null) !== Gio.FileType.DIRECTORY) {
return;
}
const theme_dirs_enumerator = themes_dir.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null);
- while ( true ) {
+ while (true) {
let theme_dir_info = theme_dirs_enumerator.next_file(null);
- if ( theme_dir_info === null ) {
+ if (theme_dir_info === null) {
break;
}
const theme_dir = theme_dirs_enumerator.get_child(theme_dir_info);
[0, Gtk.MINOR_VERSION].forEach(gtk_version => {
- if ( gtk_version % 2 ) {
+ if (gtk_version % 2) {
gtk_version += 1;
}
const css_file = Gio.File.new_for_path(GLib.build_filenamev([theme_dir.get_path(), `gtk-3.${gtk_version}`, 'gtk.css']));
- if ( css_file.query_exists(null) ) {
+ if (css_file.query_exists(null)) {
themes.add(theme_dir.get_basename());
}
})
@@ -77,6 +105,105 @@ var get_installed_themes = function() {
return themes;
}
-var is_theme_installed = function(theme) {
- return get_installed_themes().has(theme);
+/**
+ * Get all the installed shell themes on the system.
+ * @returns {Set} A set containing all the installed shell themes names.
+ */
+function get_installed_shell_themes() {
+ const themes = new Set(['']);
+
+ get_theme_dirs_paths().forEach(themes_dir_path => {
+ const themes_dir = Gio.File.new_for_path(themes_dir_path);
+
+ if (themes_dir.query_file_type(Gio.FileQueryInfoFlags.NONE, null) !== Gio.FileType.DIRECTORY) {
+ return;
+ }
+
+ const theme_dirs_enumerator = themes_dir.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null);
+
+ while (true) {
+ let theme_dir_info = theme_dirs_enumerator.next_file(null);
+
+ if (theme_dir_info === null) {
+ break;
+ }
+
+ const theme_dir = theme_dirs_enumerator.get_child(theme_dir_info);
+ const css_file = Gio.File.new_for_path(GLib.build_filenamev([theme_dir.get_path(), 'gnome-shell', 'gnome-shell.css']));
+ if (css_file.query_exists(null)) {
+ themes.add(theme_dir.get_basename());
+ }
+ }
+ theme_dirs_enumerator.close(null);
+ });
+
+ return themes;
+}
+
+/**
+ * Get the User Themes extension.
+ * @returns {Object|undefined} The User Themes extension object or undefined if
+ * it isn't installed.
+ */
+function get_userthemes_extension() {
+ if ( shell_minor_version > 32 ) {
+ return imports.ui.main.extensionManager.lookup('user-theme@gnome-shell-extensions.gcampax.github.com');
+ }
+ else {
+ return extensionUtils.extensions['user-theme@gnome-shell-extensions.gcampax.github.com'];
+ }
+}
+
+/**
+ * Get the User Themes extension settings.
+ * @returns {Gio.Settings|null} The User Themes extension settings or null if
+ * the extension isn't installed.
+ */
+function get_userthemes_settings() {
+ let extension = get_userthemes_extension();
+
+ if ( !extension ) {
+ return null;
+ }
+
+ const schema_dir = extension.dir.get_child('schemas');
+ const GioSSS = Gio.SettingsSchemaSource;
+ let schemaSource;
+ if ( schema_dir.query_exists(null) ) {
+ schemaSource = GioSSS.new_from_directory(schema_dir.get_path(), GioSSS.get_default(), false);
+ }
+ else {
+ schemaSource = GioSSS.get_default();
+ }
+ const schemaObj = schemaSource.lookup('org.gnome.shell.extensions.user-theme', true);
+ return new Gio.Settings({ settings_schema: schemaObj });
+}
+
+/**
+ * Get the shell theme stylesheet.
+ * @param {string} theme The shell theme name.
+ * @returns {string} Path to the shell theme stylesheet.
+ */
+function get_shell_theme_stylesheet(theme) {
+ log_debug('Getting the ' + (theme ? `'${theme}'` : 'default') + ' theme shell stylesheet...');
+ let stylesheet = null;
+ if ( theme ) {
+ const stylesheet_paths = get_theme_dirs_paths().map(path => `${path}/${theme}/gnome-shell/gnome-shell.css`);
+ stylesheet = stylesheet_paths.find(path => {
+ let file = Gio.file_new_for_path(path);
+ return file.query_exists(null);
+ });
+ }
+ return stylesheet;
+}
+
+/**
+ * Apply a stylesheet to the shell.
+ * @param {string} stylesheet The shell stylesheet to apply.
+ */
+function apply_shell_stylesheet(stylesheet) {
+ log_debug('Applying shell stylesheet...');
+ imports.ui.main.setThemeStylesheet(stylesheet);
+ imports.ui.main.loadTheme();
+ log_debug('Shell stylesheet applied.');
}
diff --git a/tests/_variants.js.template b/tests/_variants.js.template
deleted file mode 100644
index 328d489..0000000
--- a/tests/_variants.js.template
+++ /dev/null
@@ -1 +0,0 @@
-exports.Variants = Variants;
diff --git a/tests/Adapta.js b/tests/gtk_themes/Adapta.js
similarity index 100%
rename from tests/Adapta.js
rename to tests/gtk_themes/Adapta.js
diff --git a/tests/Adwaita.js b/tests/gtk_themes/Adwaita.js
similarity index 100%
rename from tests/Adwaita.js
rename to tests/gtk_themes/Adwaita.js
diff --git a/tests/Arc.js b/tests/gtk_themes/Arc.js
similarity index 100%
rename from tests/Arc.js
rename to tests/gtk_themes/Arc.js
diff --git a/tests/Cabinet.js b/tests/gtk_themes/Cabinet.js
similarity index 100%
rename from tests/Cabinet.js
rename to tests/gtk_themes/Cabinet.js
diff --git a/tests/Canta.js b/tests/gtk_themes/Canta.js
similarity index 100%
rename from tests/Canta.js
rename to tests/gtk_themes/Canta.js
diff --git a/tests/ChromeOS.js b/tests/gtk_themes/ChromeOS.js
similarity index 100%
rename from tests/ChromeOS.js
rename to tests/gtk_themes/ChromeOS.js
diff --git a/tests/Flat-Remix-GTK.js b/tests/gtk_themes/Flat-Remix-GTK.js
similarity index 100%
rename from tests/Flat-Remix-GTK.js
rename to tests/gtk_themes/Flat-Remix-GTK.js
diff --git a/tests/HighContrast.js b/tests/gtk_themes/HighContrast.js
similarity index 100%
rename from tests/HighContrast.js
rename to tests/gtk_themes/HighContrast.js
diff --git a/tests/Layan.js b/tests/gtk_themes/Layan.js
similarity index 100%
rename from tests/Layan.js
rename to tests/gtk_themes/Layan.js
diff --git a/tests/Macwaita.js b/tests/gtk_themes/Macwaita.js
similarity index 100%
rename from tests/Macwaita.js
rename to tests/gtk_themes/Macwaita.js
diff --git a/tests/Matcha.js b/tests/gtk_themes/Matcha.js
similarity index 100%
rename from tests/Matcha.js
rename to tests/gtk_themes/Matcha.js
diff --git a/tests/Materia.js b/tests/gtk_themes/Materia.js
similarity index 100%
rename from tests/Materia.js
rename to tests/gtk_themes/Materia.js
diff --git a/tests/Mojave.js b/tests/gtk_themes/Mojave.js
similarity index 100%
rename from tests/Mojave.js
rename to tests/gtk_themes/Mojave.js
diff --git a/tests/Nextwaita.js b/tests/gtk_themes/Nextwaita.js
similarity index 100%
rename from tests/Nextwaita.js
rename to tests/gtk_themes/Nextwaita.js
diff --git a/tests/Orchis.js b/tests/gtk_themes/Orchis.js
similarity index 100%
rename from tests/Orchis.js
rename to tests/gtk_themes/Orchis.js
diff --git a/tests/Plata.js b/tests/gtk_themes/Plata.js
similarity index 100%
rename from tests/Plata.js
rename to tests/gtk_themes/Plata.js
diff --git a/tests/Prof-Gnome-3.js b/tests/gtk_themes/Prof-Gnome-3.js
similarity index 100%
rename from tests/Prof-Gnome-3.js
rename to tests/gtk_themes/Prof-Gnome-3.js
diff --git a/tests/Qogir.js b/tests/gtk_themes/Qogir.js
similarity index 100%
rename from tests/Qogir.js
rename to tests/gtk_themes/Qogir.js
diff --git a/tests/Simply-Circles.js b/tests/gtk_themes/Simply-Circles.js
similarity index 100%
rename from tests/Simply-Circles.js
rename to tests/gtk_themes/Simply-Circles.js
diff --git a/tests/Teja.js b/tests/gtk_themes/Teja.js
similarity index 100%
rename from tests/Teja.js
rename to tests/gtk_themes/Teja.js
diff --git a/tests/Vimix.js b/tests/gtk_themes/Vimix.js
similarity index 100%
rename from tests/Vimix.js
rename to tests/gtk_themes/Vimix.js
diff --git a/tests/Yaru.js b/tests/gtk_themes/Yaru.js
similarity index 100%
rename from tests/Yaru.js
rename to tests/gtk_themes/Yaru.js
diff --git a/tests/gtk_themes/_variants.js.template b/tests/gtk_themes/_variants.js.template
new file mode 100644
index 0000000..cb44119
--- /dev/null
+++ b/tests/gtk_themes/_variants.js.template
@@ -0,0 +1 @@
+exports.Variants = GtkVariants;
diff --git a/tests/default.js b/tests/gtk_themes/default.js
similarity index 100%
rename from tests/default.js
rename to tests/gtk_themes/default.js
diff --git a/tests/shell_themes/Adapta.js b/tests/shell_themes/Adapta.js
new file mode 100644
index 0000000..2c8aa35
--- /dev/null
+++ b/tests/shell_themes/Adapta.js
@@ -0,0 +1,45 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+test('Adapta', t => {
+ const variants = Variants.guess_from('Adapta');
+ t.is(variants.get('day'), 'Adapta');
+ t.is(variants.get('night'), 'Adapta-Nokto');
+});
+
+test('Adapta-Nokto', t => {
+ const variants = Variants.guess_from('Adapta-Nokto');
+ t.is(variants.get('day'), 'Adapta');
+ t.is(variants.get('night'), 'Adapta-Nokto');
+});
+
+test('Adapta-Eta', t => {
+ const variants = Variants.guess_from('Adapta-Eta');
+ t.is(variants.get('day'), 'Adapta-Eta');
+ t.is(variants.get('night'), 'Adapta-Nokto-Eta');
+});
+
+test('Adapta-Nokto-Eta', t => {
+ const variants = Variants.guess_from('Adapta-Nokto-Eta');
+ t.is(variants.get('day'), 'Adapta-Eta');
+ t.is(variants.get('night'), 'Adapta-Nokto-Eta');
+});
diff --git a/tests/shell_themes/Arc.js b/tests/shell_themes/Arc.js
new file mode 100644
index 0000000..e1ae6ef
--- /dev/null
+++ b/tests/shell_themes/Arc.js
@@ -0,0 +1,45 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+test('Arc', t => {
+ const variants = Variants.guess_from('Arc');
+ t.is(variants.get('day'), 'Arc');
+ t.is(variants.get('night'), 'Arc-Dark');
+});
+
+test('Arc-Dark', t => {
+ const variants = Variants.guess_from('Arc-Dark');
+ t.is(variants.get('day'), 'Arc');
+ t.is(variants.get('night'), 'Arc-Dark');
+});
+
+test('Arc-Dark-solid', t => {
+ const variants = Variants.guess_from('Arc-Dark-solid');
+ t.is(variants.get('day'), 'Arc-solid');
+ t.is(variants.get('night'), 'Arc-Dark-solid');
+});
+
+test('Arc-solid', t => {
+ const variants = Variants.guess_from('Arc-solid');
+ t.is(variants.get('day'), 'Arc-solid');
+ t.is(variants.get('night'), 'Arc-Dark-solid');
+});
diff --git a/tests/shell_themes/Canta.js b/tests/shell_themes/Canta.js
new file mode 100644
index 0000000..731c042
--- /dev/null
+++ b/tests/shell_themes/Canta.js
@@ -0,0 +1,59 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '-blue', '-indigo'].forEach(color => {
+ test(`Canta${color}`, t => {
+ const variants = Variants.guess_from(`Canta${color}`);
+ t.is(variants.get('day'), `Canta${color}`);
+ t.is(variants.get('night'), `Canta${color}-dark`);
+ });
+
+ test(`Canta${color}-dark`, t => {
+ const variants = Variants.guess_from(`Canta${color}-dark`);
+ t.is(variants.get('day'), `Canta${color}`);
+ t.is(variants.get('night'), `Canta${color}-dark`);
+ });
+
+ test(`Canta${color}-light`, t => {
+ const variants = Variants.guess_from(`Canta${color}-light`);
+ t.is(variants.get('day'), `Canta${color}-light`);
+ t.is(variants.get('night'), `Canta${color}-dark`);
+ });
+
+ test(`Canta${color}-compact`, t => {
+ const variants = Variants.guess_from(`Canta${color}-compact`);
+ t.is(variants.get('day'), `Canta${color}-compact`);
+ t.is(variants.get('night'), `Canta${color}-dark-compact`);
+ });
+
+ test(`Canta${color}-dark-compact`, t => {
+ const variants = Variants.guess_from(`Canta${color}-dark-compact`);
+ t.is(variants.get('day'), `Canta${color}-compact`);
+ t.is(variants.get('night'), `Canta${color}-dark-compact`);
+ });
+
+ test(`Canta${color}-light-compact`, t => {
+ const variants = Variants.guess_from(`Canta${color}-light-compact`);
+ t.is(variants.get('day'), `Canta${color}-light-compact`);
+ t.is(variants.get('night'), `Canta${color}-dark-compact`);
+ });
+});
diff --git a/tests/shell_themes/ChromeOS.js b/tests/shell_themes/ChromeOS.js
new file mode 100644
index 0000000..a50c706
--- /dev/null
+++ b/tests/shell_themes/ChromeOS.js
@@ -0,0 +1,40 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+['', '-compact'].forEach(size => {
+ test(`ChromeOS${size}`, t => {
+ const variants = Variants.guess_from(`ChromeOS${size}`);
+ t.is(variants.get('day'), `ChromeOS${size}`);
+ t.is(variants.get('night'), `ChromeOS-dark${size}`);
+ });
+
+ test(`ChromeOS-dark${size}`, t => {
+ const variants = Variants.guess_from(`ChromeOS-dark${size}`);
+ t.is(variants.get('day'), `ChromeOS${size}`);
+ t.is(variants.get('night'), `ChromeOS-dark${size}`);
+ });
+
+ test(`ChromeOS-light${size}`, t => {
+ const variants = Variants.guess_from(`ChromeOS-light${size}`);
+ t.is(variants.get('day'), `ChromeOS-light${size}`);
+ t.is(variants.get('night'), `ChromeOS-dark${size}`);
+ });
+})
diff --git a/tests/shell_themes/Flat-Remix.js b/tests/shell_themes/Flat-Remix.js
new file mode 100644
index 0000000..1fcc28c
--- /dev/null
+++ b/tests/shell_themes/Flat-Remix.js
@@ -0,0 +1,42 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+['', '-Miami'].forEach(color => {
+ ['', '-fullPanel'].forEach(size => {
+ test(`Flat-Remix${color}${size}`, t => {
+ const variants = Variants.guess_from(`Flat-Remix${color}${size}`);
+ t.is(variants.get('day'), `Flat-Remix${color}${size}`);
+ t.is(variants.get('night'), `Flat-Remix${color}-Dark${size}`);
+ });
+
+ test(`Flat-Remix${color}-Dark${size}`, t => {
+ const variants = Variants.guess_from(`Flat-Remix${color}-Dark${size}`);
+ t.is(variants.get('day'), `Flat-Remix${color}${size}`);
+ t.is(variants.get('night'), `Flat-Remix${color}-Dark${size}`);
+ });
+
+ test(`Flat-Remix${color}-Darkest${size}`, t => {
+ const variants = Variants.guess_from(`Flat-Remix${color}-Darkest${size}`);
+ t.is(variants.get('day'), `Flat-Remix${color}${size}`);
+ t.is(variants.get('night'), `Flat-Remix${color}-Darkest${size}`);
+ });
+ })
+})
diff --git a/tests/shell_themes/Layan.js b/tests/shell_themes/Layan.js
new file mode 100644
index 0000000..a7e8f17
--- /dev/null
+++ b/tests/shell_themes/Layan.js
@@ -0,0 +1,57 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+test('Layan', t => {
+ const variants = Variants.guess_from('Layan');
+ t.is(variants.get('day'), 'Layan');
+ t.is(variants.get('night'), 'Layan-dark');
+});
+
+test('Layan-dark', t => {
+ const variants = Variants.guess_from('Layan-dark');
+ t.is(variants.get('day'), 'Layan');
+ t.is(variants.get('night'), 'Layan-dark');
+});
+
+test('Layan-light', t => {
+ const variants = Variants.guess_from('Layan-light');
+ t.is(variants.get('day'), 'Layan-light');
+ t.is(variants.get('night'), 'Layan-dark');
+});
+
+test('Layan-solid', t => {
+ const variants = Variants.guess_from('Layan-solid');
+ t.is(variants.get('day'), 'Layan-solid');
+ t.is(variants.get('night'), 'Layan-dark-solid');
+});
+
+test('Layan-dark-solid', t => {
+ const variants = Variants.guess_from('Layan-dark-solid');
+ t.is(variants.get('day'), 'Layan-solid');
+ t.is(variants.get('night'), 'Layan-dark-solid');
+});
+
+test('Layan-light-solid', t => {
+ const variants = Variants.guess_from('Layan-light-solid');
+ t.is(variants.get('day'), 'Layan-light-solid');
+ t.is(variants.get('night'), 'Layan-dark-solid');
+});
diff --git a/tests/shell_themes/Matcha.js b/tests/shell_themes/Matcha.js
new file mode 100644
index 0000000..f74323b
--- /dev/null
+++ b/tests/shell_themes/Matcha.js
@@ -0,0 +1,41 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['aliz', 'azul', 'sea'].forEach(color => {
+ test(`Matcha-${color}`, t => {
+ const variants = Variants.guess_from(`Matcha-${color}`);
+ t.is(variants.get('day'), `Matcha-${color}`);
+ t.is(variants.get('night'), `Matcha-dark-${color}`);
+ });
+
+ test(`Matcha-dark-${color}`, t => {
+ const variants = Variants.guess_from(`Matcha-dark-${color}`);
+ t.is(variants.get('day'), `Matcha-${color}`);
+ t.is(variants.get('night'), `Matcha-dark-${color}`);
+ });
+
+ test(`Matcha-light-${color}`, t => {
+ const variants = Variants.guess_from(`Matcha-light-${color}`);
+ t.is(variants.get('day'), `Matcha-light-${color}`);
+ t.is(variants.get('night'), `Matcha-dark-${color}`);
+ });
+})
diff --git a/tests/shell_themes/Materia.js b/tests/shell_themes/Materia.js
new file mode 100644
index 0000000..275ef95
--- /dev/null
+++ b/tests/shell_themes/Materia.js
@@ -0,0 +1,41 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '-compact'].forEach(size => {
+ test(`Materia${size}`, t => {
+ const variants = Variants.guess_from(`Materia${size}`);
+ t.is(variants.get('day'), `Materia${size}`);
+ t.is(variants.get('night'), `Materia-dark${size}`);
+ });
+
+ test(`Materia-dark${size}`, t => {
+ const variants = Variants.guess_from(`Materia-dark${size}`);
+ t.is(variants.get('day'), `Materia${size}`);
+ t.is(variants.get('night'), `Materia-dark${size}`);
+ });
+
+ test(`Materia-light${size}`, t => {
+ const variants = Variants.guess_from(`Materia-light${size}`);
+ t.is(variants.get('day'), `Materia-light${size}`);
+ t.is(variants.get('night'), `Materia-dark${size}`);
+ });
+})
diff --git a/tests/shell_themes/Mojave.js b/tests/shell_themes/Mojave.js
new file mode 100644
index 0000000..374eaae
--- /dev/null
+++ b/tests/shell_themes/Mojave.js
@@ -0,0 +1,37 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '-solid'].forEach(transparency => {
+ ['', '-alt'].forEach(alt => {
+ test(`Mojave-light${transparency}${alt}`, t => {
+ const variants = Variants.guess_from(`Mojave-light${transparency}${alt}`);
+ t.is(variants.get('day'), `Mojave-light${transparency}${alt}`);
+ t.is(variants.get('night'), `Mojave-dark${transparency}${alt}`);
+ });
+
+ test(`Mojave-dark${transparency}${alt}`, t => {
+ const variants = Variants.guess_from(`Mojave-dark${transparency}${alt}`);
+ t.is(variants.get('day'), `Mojave-light${transparency}${alt}`);
+ t.is(variants.get('night'), `Mojave-dark${transparency}${alt}`);
+ });
+ });
+});
diff --git a/tests/shell_themes/Orchis.js b/tests/shell_themes/Orchis.js
new file mode 100644
index 0000000..4796296
--- /dev/null
+++ b/tests/shell_themes/Orchis.js
@@ -0,0 +1,35 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '-compact'].forEach(size => {
+ test(`Orchis${size}`, t => {
+ const variants = Variants.guess_from(`Orchis${size}`);
+ t.is(variants.get('day'), `Orchis${size}`);
+ t.is(variants.get('night'), `Orchis-dark${size}`);
+ });
+
+ test(`Orchis-dark${size}`, t => {
+ const variants = Variants.guess_from(`Orchis-dark${size}`);
+ t.is(variants.get('day'), `Orchis${size}`);
+ t.is(variants.get('night'), `Orchis-dark${size}`);
+ });
+})
diff --git a/tests/shell_themes/Plata.js b/tests/shell_themes/Plata.js
new file mode 100644
index 0000000..9529341
--- /dev/null
+++ b/tests/shell_themes/Plata.js
@@ -0,0 +1,41 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '-Compact'].forEach(size => {
+ test(`Plata${size}`, t => {
+ const variants = Variants.guess_from(`Plata${size}`);
+ t.is(variants.get('day'), `Plata${size}`);
+ t.is(variants.get('night'), `Plata-Noir${size}`);
+ });
+
+ test(`Plata-Noir${size}`, t => {
+ const variants = Variants.guess_from(`Plata-Noir${size}`);
+ t.is(variants.get('day'), `Plata${size}`);
+ t.is(variants.get('night'), `Plata-Noir${size}`);
+ });
+
+ test(`Plata-Lumine${size}`, t => {
+ const variants = Variants.guess_from(`Plata-Lumine${size}`);
+ t.is(variants.get('day'), `Plata-Lumine${size}`);
+ t.is(variants.get('night'), `Plata-Noir${size}`);
+ });
+})
diff --git a/tests/shell_themes/Qogir.js b/tests/shell_themes/Qogir.js
new file mode 100644
index 0000000..5f07e29
--- /dev/null
+++ b/tests/shell_themes/Qogir.js
@@ -0,0 +1,43 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '-manjaro', '-ubuntu'].forEach(color => {
+ ['', '-win'].forEach(alt => {
+ test(`Qogir${color}${alt}`, t => {
+ const variants = Variants.guess_from(`Qogir${color}${alt}`);
+ t.is(variants.get('day'), `Qogir${color}${alt}`);
+ t.is(variants.get('night'), `Qogir${color}${alt}-dark`);
+ });
+
+ test(`Qogir${color}${alt}-dark`, t => {
+ const variants = Variants.guess_from(`Qogir${color}${alt}-dark`);
+ t.is(variants.get('day'), `Qogir${color}${alt}`);
+ t.is(variants.get('night'), `Qogir${color}${alt}-dark`);
+ });
+
+ test(`Qogir${color}${alt}light`, t => {
+ const variants = Variants.guess_from(`Qogir${color}${alt}-light`);
+ t.is(variants.get('day'), `Qogir${color}${alt}-light`);
+ t.is(variants.get('night'), `Qogir${color}${alt}-dark`);
+ });
+ });
+});
diff --git a/tests/shell_themes/Simply-Circles.js b/tests/shell_themes/Simply-Circles.js
new file mode 100644
index 0000000..125e214
--- /dev/null
+++ b/tests/shell_themes/Simply-Circles.js
@@ -0,0 +1,37 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '_Blue', '_Cyan', '_Green', '_Orange', '_Purple'].forEach(color => {
+ ['', '_Envolved'].forEach(variant => {
+ test(`Simply_Circles${color}_Light${variant}`, t => {
+ const variants = Variants.guess_from(`Simply_Circles${color}_Light${variant}`);
+ t.is(variants.get('day'), `Simply_Circles${color}_Light${variant}`);
+ t.is(variants.get('night'), `Simply_Circles${color}_Dark${variant}`);
+ });
+
+ test(`Simply_Circles${color}${variant}`, t => {
+ const variants = Variants.guess_from(`Simply_Circles${color}_Dark${variant}`);
+ t.is(variants.get('day'), `Simply_Circles${color}_Light${variant}`);
+ t.is(variants.get('night'), `Simply_Circles${color}_Dark${variant}`);
+ });
+ })
+});
diff --git a/tests/shell_themes/Teja.js b/tests/shell_themes/Teja.js
new file mode 100644
index 0000000..e099598
--- /dev/null
+++ b/tests/shell_themes/Teja.js
@@ -0,0 +1,50 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+test('Teja', t => {
+ const variants = Variants.guess_from('Teja');
+ t.is(variants.get('day'), 'Teja');
+ t.is(variants.get('night'), 'Teja_Dark');
+});
+
+test('Teja_Dark', t => {
+ const variants = Variants.guess_from('Teja_Dark');
+ t.is(variants.get('day'), 'Teja');
+ t.is(variants.get('night'), 'Teja_Dark');
+});
+
+test('Teja_Darkest', t => {
+ const variants = Variants.guess_from('Teja_Darkest');
+ t.is(variants.get('day'), 'Teja');
+ t.is(variants.get('night'), 'Teja_Darkest');
+});
+
+test('Teja_Black', t => {
+ const variants = Variants.guess_from('Teja_Black');
+ t.is(variants.get('day'), 'Teja');
+ t.is(variants.get('night'), 'Teja_Black');
+});
+
+test('Teja_Light', t => {
+ const variants = Variants.guess_from('Teja_Light');
+ t.is(variants.get('day'), 'Teja_Light');
+ t.is(variants.get('night'), 'Teja_Dark');
+});
diff --git a/tests/shell_themes/Vimix.js b/tests/shell_themes/Vimix.js
new file mode 100644
index 0000000..472a594
--- /dev/null
+++ b/tests/shell_themes/Vimix.js
@@ -0,0 +1,42 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+['', '-laptop'].forEach(size => {
+ ['', '-beryl', '-doder', '-ruby'].forEach(color => {
+ test(`vimix${size}${color}`, t => {
+ const variants = Variants.guess_from(`vimix${size}${color}`);
+ t.is(variants.get('day'), `vimix${size}${color}`);
+ t.is(variants.get('night'), `vimix-dark${size}${color}`);
+ });
+
+ test(`vimix-dark${size}${color}`, t => {
+ const variants = Variants.guess_from(`vimix-dark${size}${color}`);
+ t.is(variants.get('day'), `vimix${size}${color}`);
+ t.is(variants.get('night'), `vimix-dark${size}${color}`);
+ });
+ test(`vimix-light${size}${color}`, t => {
+ const variants = Variants.guess_from(`vimix-light${size}${color}`);
+ t.is(variants.get('day'), `vimix-light${size}${color}`);
+ t.is(variants.get('night'), `vimix-dark${size}${color}`);
+ });
+ });
+});
diff --git a/tests/shell_themes/Yaru.js b/tests/shell_themes/Yaru.js
new file mode 100644
index 0000000..2ed79d7
--- /dev/null
+++ b/tests/shell_themes/Yaru.js
@@ -0,0 +1,39 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+
+test('Yaru', t => {
+ const variants = Variants.guess_from('Yaru');
+ t.is(variants.get('day'), 'Yaru');
+ t.is(variants.get('night'), 'Yaru-dark');
+});
+
+test('Yaru-dark', t => {
+ const variants = Variants.guess_from('Yaru-dark');
+ t.is(variants.get('day'), 'Yaru');
+ t.is(variants.get('night'), 'Yaru-dark');
+});
+
+test('Yaru-light', t => {
+ const variants = Variants.guess_from('Yaru-light');
+ t.is(variants.get('day'), 'Yaru-light');
+ t.is(variants.get('night'), 'Yaru-dark');
+});
diff --git a/tests/shell_themes/_variants.js.template b/tests/shell_themes/_variants.js.template
new file mode 100644
index 0000000..a06df37
--- /dev/null
+++ b/tests/shell_themes/_variants.js.template
@@ -0,0 +1 @@
+exports.Variants = ShellVariants;
diff --git a/tests/shell_themes/default.js b/tests/shell_themes/default.js
new file mode 100644
index 0000000..d9bed78
--- /dev/null
+++ b/tests/shell_themes/default.js
@@ -0,0 +1,50 @@
+/*
+Night Theme Switcher Gnome Shell extension
+
+Copyright (C) 2020 Romain Vigier
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see .
+*/
+
+const test = require('ava');
+const { Variants } = require('./_variants');
+
+test('default', t => {
+ const variants = Variants.guess_from('default');
+ t.is(variants.get('day'), 'default');
+ t.is(variants.get('night'), 'default-dark');
+});
+
+test('default-dark', t => {
+ const variants = Variants.guess_from('default-dark');
+ t.is(variants.get('day'), 'default');
+ t.is(variants.get('night'), 'default-dark');
+});
+
+test('default-light', t => {
+ const variants = Variants.guess_from('default-light');
+ t.is(variants.get('day'), 'default-light');
+ t.is(variants.get('night'), 'default-dark');
+});
+
+test('default-darker', t => {
+ const variants = Variants.guess_from('default-darker');
+ t.is(variants.get('day'), 'default-darker');
+ t.is(variants.get('night'), 'default-dark');
+});
+
+test('default-darkest', t => {
+ const variants = Variants.guess_from('default-darkest');
+ t.is(variants.get('day'), 'default');
+ t.is(variants.get('night'), 'default-darkest');
+});