HEIC import, reimplement image change system
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,7 +1,6 @@
|
|||||||
*.glade~
|
*.glade~
|
||||||
*.glade#
|
*.glade#
|
||||||
extracted/
|
extracted_images/
|
||||||
custom_images/
|
|
||||||
*.txt
|
*.txt
|
||||||
selected/
|
selected/
|
||||||
__pycache__
|
__pycache__
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ class PrefenceEnums(enumerate):
|
|||||||
LATITUDE_CUSTOM = "latitude_custom"
|
LATITUDE_CUSTOM = "latitude_custom"
|
||||||
LONGITUDE_CUSTOM = "longitude_custom"
|
LONGITUDE_CUSTOM = "longitude_custom"
|
||||||
|
|
||||||
PERIOD_0_STARTTIME = "period_0_start_time"
|
PERIOD_0_STARTTIME = "period_0_custom_start_time"
|
||||||
PERIOD_1_STARTTIME = "period_1_start_time"
|
PERIOD_1_STARTTIME = "period_1_custom_start_time"
|
||||||
PERIOD_2_STARTTIME = "period_2_start_time"
|
PERIOD_2_STARTTIME = "period_2_custom_start_time"
|
||||||
PERIOD_3_STARTTIME = "period_3_start_time"
|
PERIOD_3_STARTTIME = "period_3_custom_start_time"
|
||||||
PERIOD_4_STARTTIME = "period_4_start_time"
|
PERIOD_4_STARTTIME = "period_4_custom_start_time"
|
||||||
PERIOD_5_STARTTIME = "period_5_start_time"
|
PERIOD_5_STARTTIME = "period_5_custom_start_time"
|
||||||
PERIOD_6_STARTTIME = "period_6_start_time"
|
PERIOD_6_STARTTIME = "period_6_custom_start_time"
|
||||||
PERIOD_7_STARTTIME = "period_7_start_time"
|
PERIOD_7_STARTTIME = "period_7_custom_start_time"
|
||||||
PERIOD_8_STARTTIME = "period_8_start_time"
|
PERIOD_8_STARTTIME = "period_8_custom_start_time"
|
||||||
PERIOD_9_STARTTIME = "period_9_start_time"
|
PERIOD_9_STARTTIME = "period_9_custom_start_time"
|
||||||
@@ -14,10 +14,10 @@ const Mainloop = imports.mainloop;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const { find_program_in_path } = imports.gi.GLib;
|
const { find_program_in_path } = imports.gi.GLib;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
let suntimes = require('./scripts/suntimes')
|
|
||||||
let location = require('./scripts/location')
|
|
||||||
let communication = require('./scripts/communication')
|
|
||||||
|
|
||||||
|
|
||||||
/******************** Constants ********************/
|
/******************** Constants ********************/
|
||||||
@@ -36,9 +36,6 @@ let extension;
|
|||||||
// Time and date of the last location update
|
// Time and date of the last location update
|
||||||
let lastLocationUpdate = -1
|
let lastLocationUpdate = -1
|
||||||
|
|
||||||
// The last calculated suntime of the day
|
|
||||||
//let lastDayTime = suntimes.DAYPERIOD.NONE
|
|
||||||
|
|
||||||
// Loop state
|
// Loop state
|
||||||
let looping = true
|
let looping = true
|
||||||
|
|
||||||
@@ -62,65 +59,16 @@ CinnamonDynamicWallpaperExtension.prototype = {
|
|||||||
_init: function(uuid) {
|
_init: function(uuid) {
|
||||||
this.settings = new Settings.ExtensionSettings(this, uuid);
|
this.settings = new Settings.ExtensionSettings(this, uuid);
|
||||||
|
|
||||||
/** Configuration */
|
|
||||||
// Image set
|
|
||||||
// this.bindSettings("sw_image_stretch", "imageStretch", this.settingsUpdated)
|
|
||||||
|
|
||||||
// // Location estimation
|
|
||||||
// this.bindSettings("sw_auto_location", "autolocation", this.settingsUpdated)
|
|
||||||
// this.bindSettings("sc_location_refresh_time", "locationRefreshTime", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_last_update", "etrLastUpdate")
|
|
||||||
// this.bindSettings("etr_latitude", "latitude", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_longitude", "longitude", this.settingsUpdated)
|
|
||||||
|
|
||||||
// // Time periods
|
|
||||||
// this.bindSettings("tv_times", "tvTimes")
|
|
||||||
|
|
||||||
// Image Configurator
|
|
||||||
// this.bindSettings("etr_img_morning_twilight", "img_morning_twilight", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_sunrise", "img_sunrise", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_morning", "img_morning", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_noon", "img_noon", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_afternoon", "img_afternoon", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_evening", "img_evening", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_sunset", "img_sunset", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_night_twilight", "img_night_twilight", this.settingsUpdated)
|
|
||||||
// this.bindSettings("etr_img_night", "img_night", this.settingsUpdated)
|
|
||||||
|
|
||||||
// this.bindSettings("etr_morning_twilight_times", "img_morning_twilight_times")
|
|
||||||
// this.bindSettings("etr_sunrise_times", "img_sunrise_times")
|
|
||||||
// this.bindSettings("etr_morning_times", "img_morning_times")
|
|
||||||
// this.bindSettings("etr_noon_times", "img_noon_times")
|
|
||||||
// this.bindSettings("etr_afternoon_times", "img_afternoon_times")
|
|
||||||
// this.bindSettings("etr_evening_times", "img_evening_times")
|
|
||||||
// this.bindSettings("etr_sunset_times", "img_sunset_times")
|
|
||||||
// this.bindSettings("etr_night_twilight_times", "img_night_twilight_times")
|
|
||||||
// this.bindSettings("etr_night_times", "img_night_times")
|
|
||||||
|
|
||||||
|
|
||||||
// Check for the first startup
|
// Check for the first startup
|
||||||
if (this.settings.getValue("first_start")) {
|
if (this.settings.getValue("first_start")) {
|
||||||
|
|
||||||
// Welcome notification
|
// Welcome notification
|
||||||
communication.showNotification("Welcome to Cinnamon Dynamic Wallpaper",
|
this.showNotification("Welcome to Cinnamon Dynamic Wallpaper",
|
||||||
"Check the preferences to choose a dynamic wallpaper", true)
|
"Check the preferences to choose a dynamic wallpaper", true)
|
||||||
|
|
||||||
// Hide the notification on system restart
|
// Hide the notification on system restart
|
||||||
this.settings.setValue("first_start", false)
|
this.settings.setValue("first_start", false)
|
||||||
|
|
||||||
// Create the folder for the selected images
|
|
||||||
Util.spawnCommandLine("mkdir " + DIRECTORY.path + "/images/selected/")
|
|
||||||
|
|
||||||
// Link the default wallpaper to the folder
|
|
||||||
for (let i = 1; i <= 9; i++) {
|
|
||||||
Util.spawnCommandLine("ln -s " +
|
|
||||||
DIRECTORY.path + "/images/included_image_sets/lakeside/" + i + ".jpg " +
|
|
||||||
DIRECTORY.path + "/images/selected/" + i + ".jpg");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Set image initial at desktop wallpaper
|
|
||||||
//this.setImageToTime()
|
|
||||||
|
|
||||||
// Start the main loop, checks in fixed time periods the
|
// Start the main loop, checks in fixed time periods the
|
||||||
this._loop()
|
this._loop()
|
||||||
@@ -149,15 +97,11 @@ CinnamonDynamicWallpaperExtension.prototype = {
|
|||||||
*/
|
*/
|
||||||
_loop: function () {
|
_loop: function () {
|
||||||
if (looping) {
|
if (looping) {
|
||||||
//this.setImageToTime()
|
try {
|
||||||
|
Util.spawnCommandLine("/usr/bin/env python3 " + DIRECTORY.path + "/loop.py")
|
||||||
// Update the location, if the user choose "autoLocation" and the timer has expired
|
} catch(e) {
|
||||||
if ((lastLocationUpdate == -1 ||
|
this.showNotification("Error!",
|
||||||
lastLocationUpdate.getTime() < new Date().getTime() - this.locationRefreshTime * 60000) &&
|
"Cinnamon Dynamic Wallpaper got an error while running the loop script. Please create an issue on GitHub.")
|
||||||
this.autolocation)
|
|
||||||
{
|
|
||||||
this.updateLocation()
|
|
||||||
lastLocationUpdate = new Date()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh every 60 seconds
|
// Refresh every 60 seconds
|
||||||
@@ -166,101 +110,35 @@ CinnamonDynamicWallpaperExtension.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/******************** Other functions ********************/
|
showNotification(title, text, showOpenSettings = false) {
|
||||||
|
let source = new MessageTray.Source(this.uuid);
|
||||||
|
|
||||||
/**
|
// Parameter
|
||||||
* Changes the desktop background image
|
let params = {
|
||||||
*
|
icon: new St.Icon({
|
||||||
* @param {jpg} imageURI The new desktop image
|
icon_name: "icon",
|
||||||
*/
|
icon_type: St.IconType.FULLCOLOR,
|
||||||
changeWallpaper: function(imageURI) {
|
icon_size: source.ICON_SIZE
|
||||||
let gSetting = new Gio.Settings({schema: 'org.cinnamon.desktop.background'});
|
})
|
||||||
gSetting.set_string('picture-uri', imageURI);
|
};
|
||||||
|
// The notification itself
|
||||||
|
let notification = new MessageTray.Notification(source, title, text, params);
|
||||||
|
|
||||||
if (this.imageStretch) {
|
// Display the "Open settings" button, if showOpenSettings
|
||||||
gSetting.set_string('picture-options', 'spanned')
|
if (showOpenSettings) {
|
||||||
}
|
notification.addButton("open-settings", _("Open settings"));
|
||||||
else
|
|
||||||
{
|
notification.connect("action-invoked", () =>
|
||||||
gSetting.set_string('picture-options', 'zoom')
|
Util.spawnCommandLine("/usr/bin/env python3 " +
|
||||||
|
DIRECTORY.path + "/preferences/preferences.py"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Gio.Settings.sync();
|
// Put all together
|
||||||
gSetting.apply();
|
Main.messageTray.add(source);
|
||||||
},
|
|
||||||
|
|
||||||
|
// Display it
|
||||||
/**
|
source.notify(notification);
|
||||||
* Estimate the right image based on time period of the day
|
|
||||||
*/
|
|
||||||
/*setImageToTime: function() {
|
|
||||||
let times = suntimes.calcTimePeriod(this.latitude, this.longitude)
|
|
||||||
let now = new Date()
|
|
||||||
|
|
||||||
let timesArray = [
|
|
||||||
times["morning_twilight"], times["sunrise"], times["morning"],
|
|
||||||
times["noon"], times["afternoon"], times["evening"],
|
|
||||||
times["sunset"], times["night_twilight"], times["night"]
|
|
||||||
]
|
|
||||||
|
|
||||||
let imageSet = [
|
|
||||||
this.img_morning_twilight, this.img_sunrise, this.img_morning,
|
|
||||||
this.img_noon, this.img_afternoon, this.img_evening,
|
|
||||||
this.img_sunset, this.img_night_twilight, this.img_night
|
|
||||||
]
|
|
||||||
|
|
||||||
for(let i = 0; i < timesArray.length; i++) {
|
|
||||||
if(timesArray[i][0] <= now && now <= timesArray[i][1] && i != lastDayTime) {
|
|
||||||
this.changeWallpaper("file://" + PATH + "/images/selected/" + imageSet[i])
|
|
||||||
|
|
||||||
lastDayTime = i
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function convertToTimeString(time) {
|
|
||||||
return time.getHours().toString().padStart(2, "0") + ":" + time.getMinutes().toString().padStart(2, "0")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.img_morning_twilight_times = convertToTimeString(timesArray[0][0]) + " - " + convertToTimeString(timesArray[0][1])
|
|
||||||
this.img_sunrise_times = convertToTimeString(timesArray[1][0]) + " - " + convertToTimeString(timesArray[1][1])
|
|
||||||
this.img_morning_times = convertToTimeString(timesArray[2][0]) + " - " + convertToTimeString(timesArray[2][1])
|
|
||||||
this.img_noon_times = convertToTimeString(timesArray[3][0]) + " - " + convertToTimeString(timesArray[3][1])
|
|
||||||
this.img_afternoon_times = convertToTimeString(timesArray[4][0]) + " - " + convertToTimeString(timesArray[4][1])
|
|
||||||
this.img_evening_times = convertToTimeString(timesArray[5][0]) + " - " + convertToTimeString(timesArray[5][1])
|
|
||||||
this.img_sunset_times = convertToTimeString(timesArray[6][0]) + " - " + convertToTimeString(timesArray[6][1])
|
|
||||||
this.img_night_twilight_times = convertToTimeString(timesArray[7][0]) + " - " + convertToTimeString(timesArray[7][1])
|
|
||||||
this.img_night_times = convertToTimeString(timesArray[8][0]) + " - " + convertToTimeString(timesArray[8][1])
|
|
||||||
|
|
||||||
this.tvTimes =
|
|
||||||
"Morning Twilight:\t\t" + convertToTimeString(timesArray[0][0]) + " - " + convertToTimeString(timesArray[0][1]) +
|
|
||||||
"\nSunrise:\t\t\t\t" + convertToTimeString(timesArray[1][0]) + " - " + convertToTimeString(timesArray[1][1]) +
|
|
||||||
"\nMorning:\t\t\t" + convertToTimeString(timesArray[2][0]) + " - " + convertToTimeString(timesArray[2][1]) +
|
|
||||||
"\nNoon:\t\t\t\t" + convertToTimeString(timesArray[3][0]) + " - " + convertToTimeString(timesArray[3][1]) +
|
|
||||||
"\nAfternoon:\t\t\t" + convertToTimeString(timesArray[4][0]) + " - " + convertToTimeString(timesArray[4][1]) +
|
|
||||||
"\nEvening:\t\t\t" + convertToTimeString(timesArray[5][0]) + " - " + convertToTimeString(timesArray[5][1]) +
|
|
||||||
"\nSunset:\t\t\t\t" + convertToTimeString(timesArray[6][0]) + " - " + convertToTimeString(timesArray[6][1]) +
|
|
||||||
"\nNight Twilight:\t\t" + convertToTimeString(timesArray[7][0]) + " - " + convertToTimeString(timesArray[7][1]) +
|
|
||||||
"\nNight:\t\t\t\t" + convertToTimeString(timesArray[8][0]) + " - " + convertToTimeString(timesArray[8][1])
|
|
||||||
},*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the location of the user
|
|
||||||
* Callback for changes in preferences
|
|
||||||
*/
|
|
||||||
updateLocation: function () {
|
|
||||||
// Update the update information
|
|
||||||
lastLocationUpdate = new Date()
|
|
||||||
|
|
||||||
if (this.autolocation) {
|
|
||||||
let loc = location.estimateLocation()
|
|
||||||
this.latitude = loc["latitude"]
|
|
||||||
this.longitude = loc["longitude"]
|
|
||||||
|
|
||||||
this.etrLastUpdate = lastLocationUpdate.getHours() + ":" + lastLocationUpdate.getMinutes()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
72
cinnamon-dynamic-wallpaper@TobiZog/5.4/loop.py
Normal file
72
cinnamon-dynamic-wallpaper@TobiZog/5.4/loop.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from scripts.cinnamon_pref_handler import *
|
||||||
|
from scripts.suntimes import *
|
||||||
|
from datetime import datetime, time
|
||||||
|
from enums.PreferenceEnums import *
|
||||||
|
from enums.PeriodSourceEnum import *
|
||||||
|
from scripts.location import *
|
||||||
|
import gi
|
||||||
|
from gi.repository import Gio
|
||||||
|
|
||||||
|
prefs = Cinnamon_Pref_Handler()
|
||||||
|
suntimes = Suntimes()
|
||||||
|
location_thread = Location()
|
||||||
|
|
||||||
|
background_settings = Gio.Settings.new("org.cinnamon.desktop.background")
|
||||||
|
|
||||||
|
class Loop():
|
||||||
|
def __init__(self) -> None:
|
||||||
|
# Position should estimate by network
|
||||||
|
if prefs.prefs[PrefenceEnums.PERIOD_SOURCE] == PeriodSourceEnum.NETWORKLOCATION:
|
||||||
|
location_thread.start()
|
||||||
|
location_thread.join()
|
||||||
|
|
||||||
|
location = location_thread.result
|
||||||
|
|
||||||
|
suntimes.calc_suntimes(float(location["latitude"]), float(location["longitude"]))
|
||||||
|
self.start_times = suntimes.day_periods
|
||||||
|
|
||||||
|
# Position is given by user
|
||||||
|
elif prefs.prefs[PrefenceEnums.PERIOD_SOURCE] == PeriodSourceEnum.CUSTOMLOCATION:
|
||||||
|
suntimes.calc_suntimes(float(prefs.prefs[PrefenceEnums.LATITUDE_CUSTOM]), float(prefs.prefs[PrefenceEnums.LONGITUDE_CUSTOM]))
|
||||||
|
self.start_times = suntimes.day_periods
|
||||||
|
|
||||||
|
# No position, concrete times
|
||||||
|
else:
|
||||||
|
def string_to_time_converter(raw_str: str) -> time:
|
||||||
|
hour = raw_str[0:raw_str.find(":")]
|
||||||
|
minute = raw_str[raw_str.find(":") + 1:]
|
||||||
|
|
||||||
|
return time(hour=int(hour), minute=int(minute))
|
||||||
|
|
||||||
|
self.start_times = [
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_0_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_1_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_2_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_3_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_4_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_5_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_6_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_7_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_8_STARTTIME]),
|
||||||
|
string_to_time_converter(prefs.prefs[PrefenceEnums.PERIOD_9_STARTTIME])
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def exchange_image(self):
|
||||||
|
""" Replace the desktop image
|
||||||
|
"""
|
||||||
|
time_now = time(datetime.now().hour, datetime.now().minute)
|
||||||
|
|
||||||
|
for i in range(0, 9):
|
||||||
|
if self.start_times[i] <= time_now and time_now < self.start_times[i + 1]:
|
||||||
|
background_settings['picture-uri'] = "file://" + prefs.prefs[PrefenceEnums.SOURCE_FOLDER] + prefs.prefs["period_%d_image" % (i)]
|
||||||
|
return
|
||||||
|
|
||||||
|
background_settings['picture-uri'] = "file://" + prefs.prefs[PrefenceEnums.SOURCE_FOLDER] + prefs.prefs[PrefenceEnums.PERIOD_9_IMAGE]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
l = Loop()
|
||||||
|
l.exchange_image()
|
||||||
@@ -393,6 +393,7 @@
|
|||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="filter">filefilter1</property>
|
<property name="filter">filefilter1</property>
|
||||||
<property name="title" translatable="yes"/>
|
<property name="title" translatable="yes"/>
|
||||||
|
<signal name="file-set" handler="on_fc_heic_file_file_set" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@@ -438,6 +439,7 @@
|
|||||||
<property name="action">select-folder</property>
|
<property name="action">select-folder</property>
|
||||||
<property name="title" translatable="yes"/>
|
<property name="title" translatable="yes"/>
|
||||||
<signal name="file-set" handler="on_fc_source_folder_file_set" swapped="no"/>
|
<signal name="file-set" handler="on_fc_source_folder_file_set" swapped="no"/>
|
||||||
|
<signal name="selection-changed" handler="on_fc_source_folder_file_set" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@@ -56,6 +56,7 @@ class Preferences:
|
|||||||
|
|
||||||
# Source folder
|
# Source folder
|
||||||
self.lbr_source_folder = self.builder.get_object("lbr_source_folder")
|
self.lbr_source_folder = self.builder.get_object("lbr_source_folder")
|
||||||
|
self.fc_source_folder = self.builder.get_object("fc_source_folder")
|
||||||
|
|
||||||
# Time bar chart
|
# Time bar chart
|
||||||
self.img_bar_images = self.builder.get_object("img_bar_images")
|
self.img_bar_images = self.builder.get_object("img_bar_images")
|
||||||
@@ -147,14 +148,14 @@ class Preferences:
|
|||||||
elif self.c_prefs.prefs[PrefenceEnums.IMAGE_SOURCE] == ImageSourceEnum.SOURCEFOLDER:
|
elif self.c_prefs.prefs[PrefenceEnums.IMAGE_SOURCE] == ImageSourceEnum.SOURCEFOLDER:
|
||||||
self.tb_source_folder.set_active(True)
|
self.tb_source_folder.set_active(True)
|
||||||
|
|
||||||
image_set_choices = ["aurora", "beach", "bitday", "cliffs", "gradient", "lakeside", "mountains", "sahara"]
|
# image_set_choices = ["aurora", "beach", "bitday", "cliffs", "gradient", "lakeside", "mountains", "sahara"]
|
||||||
self.add_items_to_combo_box(self.cb_image_set, image_set_choices)
|
# self.add_items_to_combo_box(self.cb_image_set, image_set_choices)
|
||||||
|
|
||||||
self.set_active_combobox_item(self.cb_image_set, self.c_prefs.prefs[PrefenceEnums.SELECTED_IMAGE_SET])
|
# self.set_active_combobox_item(self.cb_image_set, self.c_prefs.prefs[PrefenceEnums.SELECTED_IMAGE_SET])
|
||||||
|
|
||||||
for i, combobox in enumerate(self.cb_periods):
|
# for i, combobox in enumerate(self.cb_periods):
|
||||||
selected_image_name = self.c_prefs.prefs["period_%s_image" % (i)]
|
# selected_image_name = self.c_prefs.prefs["period_%s_image" % (i)]
|
||||||
self.set_active_combobox_item(combobox, selected_image_name)
|
# self.set_active_combobox_item(combobox, selected_image_name)
|
||||||
|
|
||||||
|
|
||||||
self.sw_expand_over_all_displays.set_active(self.c_prefs.prefs[PrefenceEnums.EXPAND_OVER_ALL_DISPLAY])
|
self.sw_expand_over_all_displays.set_active(self.c_prefs.prefs[PrefenceEnums.EXPAND_OVER_ALL_DISPLAY])
|
||||||
@@ -317,6 +318,15 @@ class Preferences:
|
|||||||
self.lbr_heic_file.set_visible(False)
|
self.lbr_heic_file.set_visible(False)
|
||||||
self.lbr_source_folder.set_visible(False)
|
self.lbr_source_folder.set_visible(False)
|
||||||
|
|
||||||
|
image_set_choices = ["aurora", "beach", "bitday", "cliffs", "gradient", "lakeside", "mountains", "sahara"]
|
||||||
|
self.add_items_to_combo_box(self.cb_image_set, image_set_choices)
|
||||||
|
|
||||||
|
self.set_active_combobox_item(self.cb_image_set, self.c_prefs.prefs[PrefenceEnums.SELECTED_IMAGE_SET])
|
||||||
|
|
||||||
|
for i, combobox in enumerate(self.cb_periods):
|
||||||
|
selected_image_name = self.c_prefs.prefs["period_%s_image" % (i)]
|
||||||
|
self.set_active_combobox_item(combobox, selected_image_name)
|
||||||
|
|
||||||
# Make the comboboxes invisible
|
# Make the comboboxes invisible
|
||||||
for combobox in self.cb_periods:
|
for combobox in self.cb_periods:
|
||||||
combobox.set_visible(False)
|
combobox.set_visible(False)
|
||||||
@@ -347,26 +357,28 @@ class Preferences:
|
|||||||
self.lbr_heic_file.set_visible(False)
|
self.lbr_heic_file.set_visible(False)
|
||||||
self.lbr_source_folder.set_visible(True)
|
self.lbr_source_folder.set_visible(True)
|
||||||
|
|
||||||
# Make the comboboxes invisible
|
# Make the comboboxes visible
|
||||||
for combobox in self.cb_periods:
|
for combobox in self.cb_periods:
|
||||||
combobox.set_visible(True)
|
combobox.set_visible(True)
|
||||||
|
|
||||||
|
# Load the source folder to the view
|
||||||
|
# This will update the comboboxes in the preview to contain the right items
|
||||||
|
self.fc_source_folder.set_filename(self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_cb_image_set_changed(self, combobox: Gtk.ComboBox):
|
def on_cb_image_set_changed(self, combobox: Gtk.ComboBox):
|
||||||
tree_iter = combobox.get_active_iter()
|
tree_iter = combobox.get_active_iter()
|
||||||
|
|
||||||
if tree_iter is not None:
|
if tree_iter is not None and self.c_prefs.prefs[PrefenceEnums.IMAGE_SOURCE] == ImageSourceEnum.IMAGESET:
|
||||||
# Get the selected value
|
# Get the selected value
|
||||||
model = combobox.get_model()
|
model = combobox.get_model()
|
||||||
selected_image_set = model[tree_iter][0]
|
selected_image_set = model[tree_iter][0]
|
||||||
|
|
||||||
# Store to the preferences
|
# Store to the preferences
|
||||||
self.c_prefs.prefs[PrefenceEnums.SELECTED_IMAGE_SET] = selected_image_set
|
self.c_prefs.prefs[PrefenceEnums.SELECTED_IMAGE_SET] = selected_image_set
|
||||||
|
|
||||||
# Update the ComboBoxes for image selection
|
|
||||||
self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER] = os.path.abspath(os.path.join(PREFERENCES_URI, os.pardir)) + \
|
self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER] = os.path.abspath(os.path.join(PREFERENCES_URI, os.pardir)) + \
|
||||||
"/images/included_image_sets/" + selected_image_set + "/"
|
"/5.4/images/included_image_sets/" + selected_image_set + "/"
|
||||||
|
|
||||||
# Load all possible options to the comboboxes
|
# Load all possible options to the comboboxes
|
||||||
image_names = self.images.get_images_from_folder(self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER])
|
image_names = self.images.get_images_from_folder(self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER])
|
||||||
@@ -382,19 +394,50 @@ class Preferences:
|
|||||||
self.cb_periods[i].set_active(i - 1)
|
self.cb_periods[i].set_active(i - 1)
|
||||||
|
|
||||||
|
|
||||||
|
def on_fc_heic_file_file_set(self, fc_button: Gtk.FileChooser):
|
||||||
|
file_path = fc_button.get_filename()
|
||||||
|
extract_folder = os.path.abspath(os.path.join(PREFERENCES_URI, os.pardir)) + \
|
||||||
|
"/images/extracted_images/"
|
||||||
|
|
||||||
|
file_name = file_path[file_path.rfind("/") + 1:]
|
||||||
|
file_name = file_name[:file_name.rfind(".")]
|
||||||
|
|
||||||
|
# Update the preferences
|
||||||
|
self.c_prefs.prefs[PrefenceEnums.SELECTED_IMAGE_SET] = ""
|
||||||
|
self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER] = extract_folder
|
||||||
|
|
||||||
|
# Create the buffer folder
|
||||||
|
try:
|
||||||
|
os.mkdir(extract_folder)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Extract the HEIC file
|
||||||
|
for file in self.images.get_images_from_folder(extract_folder):
|
||||||
|
os.remove(extract_folder + file)
|
||||||
|
|
||||||
|
os.system("heif-convert " + file_path + " " + extract_folder + file_name + ".jpg")
|
||||||
|
|
||||||
|
# Collect all extracted images and push them to the comboboxes
|
||||||
|
image_names = self.images.get_images_from_folder(self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER])
|
||||||
|
self.load_image_options_to_combo_boxes(image_names)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_fc_source_folder_file_set(self, fc_button: Gtk.FileChooser):
|
def on_fc_source_folder_file_set(self, fc_button: Gtk.FileChooser):
|
||||||
files = self.images.get_images_from_folder(fc_button.get_filename())
|
files = self.images.get_images_from_folder(fc_button.get_filename())
|
||||||
|
|
||||||
# Update the ComboBoxes for image selection
|
# Update the preferences
|
||||||
|
self.c_prefs.prefs[PrefenceEnums.SELECTED_IMAGE_SET] = ""
|
||||||
self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER] = fc_button.get_filename() + "/"
|
self.c_prefs.prefs[PrefenceEnums.SOURCE_FOLDER] = fc_button.get_filename() + "/"
|
||||||
|
|
||||||
print(fc_button.get_filename())
|
|
||||||
|
|
||||||
if len(files) != 0:
|
if len(files) != 0:
|
||||||
self.load_image_options_to_combo_boxes(files)
|
self.load_image_options_to_combo_boxes(files)
|
||||||
|
|
||||||
|
# Load the values for the images from the preferences
|
||||||
|
for i in range(0, 10):
|
||||||
|
self.set_active_combobox_item(self.cb_periods[i], self.c_prefs.prefs["period_%s_image" % (i)])
|
||||||
else:
|
else:
|
||||||
#todo
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import os, json
|
import os, json
|
||||||
from enums.PreferenceEnums import PrefenceEnums
|
|
||||||
|
|
||||||
class Cinnamon_Pref_Handler:
|
class Cinnamon_Pref_Handler:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Cinnamon-Dynamic-Wallpaper
|
|
||||||
* @alias TobiZog
|
|
||||||
* @since 2023-08-25
|
|
||||||
*
|
|
||||||
* @description Handles communications with the user (notifications, logs)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/******************** Imports ********************/
|
|
||||||
|
|
||||||
const St = imports.gi.St;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const Util = imports.misc.util;
|
|
||||||
const MessageTray = imports.ui.messageTray;
|
|
||||||
const UUID = "cinnamon-dynamic-wallpaper@TobiZog";
|
|
||||||
const DIRECTORY = imports.ui.extensionSystem.extensionMeta[UUID];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************** Functions ********************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displaying a desktop notification
|
|
||||||
*
|
|
||||||
* @param {string} title The Title in the notification
|
|
||||||
* @param {string} text The text in the notification
|
|
||||||
* @param {boolean} showOpenSettings Display the "Open settings" button in the notification,
|
|
||||||
* defaults to false
|
|
||||||
*/
|
|
||||||
function showNotification(title, text, showOpenSettings = false) {
|
|
||||||
let source = new MessageTray.Source(this.uuid);
|
|
||||||
|
|
||||||
// Parameter
|
|
||||||
let params = {
|
|
||||||
icon: new St.Icon({
|
|
||||||
icon_name: "icon",
|
|
||||||
icon_type: St.IconType.FULLCOLOR,
|
|
||||||
icon_size: source.ICON_SIZE
|
|
||||||
})
|
|
||||||
};
|
|
||||||
// The notification itself
|
|
||||||
let notification = new MessageTray.Notification(source, title, text, params);
|
|
||||||
|
|
||||||
// Display the "Open settings" button, if showOpenSettings
|
|
||||||
if (showOpenSettings) {
|
|
||||||
notification.addButton("open-settings", _("Open settings"));
|
|
||||||
|
|
||||||
notification.connect("action-invoked", () =>
|
|
||||||
Util.spawnCommandLine("/usr/bin/env python3 " +
|
|
||||||
DIRECTORY.path + "/preferences/preferences.py"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put all together
|
|
||||||
Main.messageTray.add(source);
|
|
||||||
|
|
||||||
// Display it
|
|
||||||
source.notify(notification);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Cinnamon-Dynamic-Wallpaper
|
|
||||||
* @alias TobiZog
|
|
||||||
* @since 2023
|
|
||||||
*
|
|
||||||
* @description Functions to estimate the user location
|
|
||||||
*/
|
|
||||||
|
|
||||||
/******************** Imports ********************/
|
|
||||||
|
|
||||||
const Soup = imports.gi.Soup;
|
|
||||||
|
|
||||||
|
|
||||||
/******************** Functions ********************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Estimate the location of the user
|
|
||||||
*
|
|
||||||
* @returns Location data if succeded or -1 if failed
|
|
||||||
*/
|
|
||||||
function estimateLocation() {
|
|
||||||
let sessionSync = new Soup.SessionSync();
|
|
||||||
let msg = Soup.Message.new('GET', "https://get.geojs.io/v1/ip/geo.json");
|
|
||||||
sessionSync.send_message(msg);
|
|
||||||
|
|
||||||
if (msg.status_code == 200) {
|
|
||||||
return JSON.parse(msg.response_body.data);
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Cinnamon-Dynamic-Wallpaper
|
|
||||||
* @alias TobiZog
|
|
||||||
* @since 2023
|
|
||||||
*
|
|
||||||
* @description Functions to calculate sun time periods
|
|
||||||
*/
|
|
||||||
|
|
||||||
/******************** Constants ********************/
|
|
||||||
|
|
||||||
const DAYPERIOD = {
|
|
||||||
MTWILIGHT: 0,
|
|
||||||
SUNRISE: 1,
|
|
||||||
MORNING: 2,
|
|
||||||
NOON: 3,
|
|
||||||
AFTERNOON: 4,
|
|
||||||
EVENING: 5,
|
|
||||||
SUNSET: 6,
|
|
||||||
NTWILIGHT: 7,
|
|
||||||
NIGHT: 8,
|
|
||||||
NONE: 10
|
|
||||||
}
|
|
||||||
|
|
||||||
const DAYMS = 1000 * 60 * 60 * 24
|
|
||||||
const J1970 = 2440588
|
|
||||||
const J2000 = 2451545
|
|
||||||
|
|
||||||
|
|
||||||
/******************** Functions ********************/
|
|
||||||
|
|
||||||
|
|
||||||
function fromJulian(j) {
|
|
||||||
let ms_date = (j + 0.5 - J1970) * DAYMS
|
|
||||||
return new Date(ms_date)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculating specific events of the sun during a day
|
|
||||||
*
|
|
||||||
* @param {float} latitude Location latitude
|
|
||||||
* @param {float} longitude Location longitude
|
|
||||||
*
|
|
||||||
* @returns List of sun events in a day: dawn, sunrise, noon, sunset, dusk
|
|
||||||
*/
|
|
||||||
function sunEventsOfDay(latitude, longitude, date) {
|
|
||||||
let rad = Math.PI / 180
|
|
||||||
let lw = rad * (-longitude)
|
|
||||||
|
|
||||||
let d = (date / DAYMS) - 0.5 + J1970 - J2000
|
|
||||||
let n = Math.round(d - 0.0009 - lw / (2 * Math.PI))
|
|
||||||
let ds = 0.0009 + lw / (2 * Math.PI) + n
|
|
||||||
|
|
||||||
let M = rad * (357.5291 + 0.98560028 * ds)
|
|
||||||
let C = rad * (1.9148 * Math.sin(M) + 0.02 * Math.sin(2 * M) + 0.0003 * Math.sin(3 * M))
|
|
||||||
let P = rad * 102.9372
|
|
||||||
let L = M + C + P + Math.PI
|
|
||||||
|
|
||||||
let dec = Math.asin(Math.sin(rad * 23.4397) * Math.sin(L))
|
|
||||||
|
|
||||||
|
|
||||||
// Angles for the sun
|
|
||||||
let angles = [-0.833, -6]
|
|
||||||
|
|
||||||
for(var i = 0; i < angles.length; i++) {
|
|
||||||
angles[i] = angles[i] * rad
|
|
||||||
angles[i] = Math.acos((Math.sin(angles[i]) - Math.sin(rad * latitude) * Math.sin(dec)) / (Math.cos(rad * latitude) * Math.cos(dec)))
|
|
||||||
angles[i] = 0.0009 + (angles[i] + lw) / (2 * Math.PI) + n
|
|
||||||
}
|
|
||||||
|
|
||||||
let jnoon = J2000 + ds + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L)
|
|
||||||
|
|
||||||
return {
|
|
||||||
dawn: fromJulian(jnoon - (J2000 + angles[1] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L) - jnoon)),
|
|
||||||
sunrise: fromJulian(jnoon - (J2000 + angles[0] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L) - jnoon)),
|
|
||||||
noon: fromJulian(jnoon),
|
|
||||||
sunset: fromJulian(J2000 + angles[0] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L)),
|
|
||||||
dusk: fromJulian(J2000 + angles[1] + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMinutesToTime(date, minutes = 0) {
|
|
||||||
let newDate = new Date(date)
|
|
||||||
newDate.setMinutes(date.getMinutes() + minutes)
|
|
||||||
return newDate
|
|
||||||
}
|
|
||||||
|
|
||||||
function subTimesToMinutes(date1, date2) {
|
|
||||||
let diff = new Date(date1 - date2)
|
|
||||||
return diff.getUTCHours() * 60 + diff.getMinutes()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculating the start and end time of all time periods of the day
|
|
||||||
*
|
|
||||||
* @param {float} latitude Location latitude
|
|
||||||
* @param {float} longitude Location longitude
|
|
||||||
*
|
|
||||||
* @returns JSON of time periods
|
|
||||||
*/
|
|
||||||
function calcTimePeriod(latitude, longitude) {
|
|
||||||
let todaySunEventsDay = sunEventsOfDay(latitude, longitude, Date.now())
|
|
||||||
let tomorrowSunEventsDay = sunEventsOfDay(latitude, longitude, addMinutesToTime(new Date(), 1440))
|
|
||||||
|
|
||||||
return {
|
|
||||||
morning_twilight: [
|
|
||||||
todaySunEventsDay.dawn,
|
|
||||||
addMinutesToTime(todaySunEventsDay.sunrise, -1)
|
|
||||||
],
|
|
||||||
sunrise: [
|
|
||||||
todaySunEventsDay.sunrise,
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.sunrise,
|
|
||||||
subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8 - 1
|
|
||||||
)
|
|
||||||
],
|
|
||||||
morning: [
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.sunrise,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8) * 1
|
|
||||||
),
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.sunrise,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8)*6 - 1
|
|
||||||
)
|
|
||||||
],
|
|
||||||
noon: [
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.sunrise,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.noon, todaySunEventsDay.sunrise) / 8) * 6
|
|
||||||
),
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.noon,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 2 - 1
|
|
||||||
)
|
|
||||||
],
|
|
||||||
afternoon: [
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.noon,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 2
|
|
||||||
),
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.noon,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 5 - 1
|
|
||||||
)
|
|
||||||
],
|
|
||||||
evening: [
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.noon,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 5
|
|
||||||
),
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.noon,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 7 - 1
|
|
||||||
)
|
|
||||||
],
|
|
||||||
sunset: [
|
|
||||||
addMinutesToTime(
|
|
||||||
todaySunEventsDay.noon,
|
|
||||||
(subTimesToMinutes(todaySunEventsDay.sunset, todaySunEventsDay.noon) / 8) * 7 - 1
|
|
||||||
),
|
|
||||||
todaySunEventsDay.sunset
|
|
||||||
],
|
|
||||||
night_twilight: [
|
|
||||||
addMinutesToTime(todaySunEventsDay.sunset, 1),
|
|
||||||
todaySunEventsDay.dusk
|
|
||||||
],
|
|
||||||
night: [
|
|
||||||
addMinutesToTime(todaySunEventsDay.dusk, 1),
|
|
||||||
addMinutesToTime(tomorrowSunEventsDay.dawn, -1)
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
},
|
},
|
||||||
"selected_image_set": {
|
"selected_image_set": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "lakeside"
|
||||||
},
|
},
|
||||||
"source_folder": {
|
"source_folder": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
@@ -25,43 +25,43 @@
|
|||||||
},
|
},
|
||||||
"period_0_image": {
|
"period_0_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "9.jpg"
|
||||||
},
|
},
|
||||||
"period_1_image": {
|
"period_1_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "1.jpg"
|
||||||
},
|
},
|
||||||
"period_2_image": {
|
"period_2_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "2.jpg"
|
||||||
},
|
},
|
||||||
"period_3_image": {
|
"period_3_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "3.jpg"
|
||||||
},
|
},
|
||||||
"period_4_image": {
|
"period_4_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "4.jpg"
|
||||||
},
|
},
|
||||||
"period_5_image": {
|
"period_5_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "5.jpg"
|
||||||
},
|
},
|
||||||
"period_6_image": {
|
"period_6_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "6.jpg"
|
||||||
},
|
},
|
||||||
"period_7_image": {
|
"period_7_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "7.jpg"
|
||||||
},
|
},
|
||||||
"period_8_image": {
|
"period_8_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "8.jpg"
|
||||||
},
|
},
|
||||||
"period_9_image": {
|
"period_9_image": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
"default": ""
|
"default": "9.jpg"
|
||||||
},
|
},
|
||||||
"period_source": {
|
"period_source": {
|
||||||
"type": "generic",
|
"type": "generic",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"external-configuration-app": "preferences/preferences.py",
|
"external-configuration-app": "preferences.py",
|
||||||
"uuid": "cinnamon-dynamic-wallpaper@TobiZog",
|
"uuid": "cinnamon-dynamic-wallpaper@TobiZog",
|
||||||
"name": "Cinnamon Dynamic Wallpaper",
|
"name": "Cinnamon Dynamic Wallpaper",
|
||||||
"description": "Cinnamon extension for dynamic desktop backgrounds based on time and location",
|
"description": "Cinnamon extension for dynamic desktop backgrounds based on time and location",
|
||||||
|
|||||||
Reference in New Issue
Block a user