diff --git a/.gitignore b/.gitignore index ff4d610..9d6886d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ .idea +# ready to deploy to gnome extensions +random-wallpaper-gnome3.zip + # Temporary ui files **/*~ diff --git a/LICENSE b/LICENSE index 8ebd522..52271fd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 ifl0w +Copyright (c) 2014 Wolfgang Rumpler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..e178ac5 --- /dev/null +++ b/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +BASEDIR='randomwallpaper@iflow.space' +ZIPNAME='random-wallpaper-gnome3.zip' + +rm $ZIPNAME +rm $BASEDIR/schemas/gschemas.compiled +rm $BASEDIR/wallpapers/* +glib-compile-schemas $BASEDIR/schemas/ + +cd $BASEDIR +zip -r $ZIPNAME . +mv $ZIPNAME .. \ No newline at end of file diff --git a/icon.png b/icon.png index f241bc5..da3594c 100644 Binary files a/icon.png and b/icon.png differ diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..65a781f --- /dev/null +++ b/icon.svg @@ -0,0 +1,83 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/randomwallpaper@iflow.space/Elements.js b/randomwallpaper@iflow.space/Elements.js deleted file mode 100644 index 97401be..0000000 --- a/randomwallpaper@iflow.space/Elements.js +++ /dev/null @@ -1,260 +0,0 @@ -const Lang = imports.lang; -const PopupMenu = imports.ui.popupMenu; -const St = imports.gi.St; -const Slider = imports.ui.slider; -const Tweener = imports.ui.tweener; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const Timer = Self.imports.timer; - -const HistoryElement = new Lang.Class({ - Name: 'HistoryElement', - Extends: PopupMenu.PopupBaseMenuItem, - historyId: null, - - _init: function(historyId, index, params) { - index = String(index)+'.' || '0.'; - - this.parent(params); - - let timestamp = parseInt(historyId.slice(0, historyId.lastIndexOf('.'))); - let date = new Date(timestamp); - - let timeString = date.toLocaleTimeString(); - let dateString = date.toLocaleDateString(); - - this.label = new St.Label({ - text: index, - style_class: 'rwg-history-index' - }); - - this.actor.add_child(this.label); - - this._container = new St.BoxLayout({ - vertical: true - }); - - this.dateLabel = new St.Label({ - text: dateString, - style_class: 'rwg-history-date' - }); - this._container.add_child(this.dateLabel); - - this.timeLabel = new St.Label({ - text: timeString, - style_class: 'rwg-history-time' - }); - this._container.add_child(this.timeLabel); - - this.historyId = historyId; - this.actor.historyId = historyId; // extend the actor with the historyId - - this.actor.add_child(this._container); - } -}); - -/** - * Element for the New Wallpaper button and the remaining time for the auto fetch - * feature. - * The remaining time will only be displayed if the af-feature is activated. - * - * @type {Lang.Class} - */ -const NewWallpaperElement = new Lang.Class({ - Name: 'NewWallpaperElement', - Extends: PopupMenu.PopupBaseMenuItem, - - _init: function(params) { - this.parent(params); - - this._timer = new Timer.AFTimer(); - - this._container = new St.BoxLayout({ - vertical: true - }); - - this._newWPLabel = new St.Label({ - text: 'New Wallpaper', - style_class: 'rwg-new-lable' - }); - this._container.add_child(this._newWPLabel); - - this._remainingLabel = new St.Label({ - text: '1 minute remaining' - }); - this._container.add_child(this._remainingLabel); - - this.actor.add_child(this._container); - }, - - show: function() { - if (this._timer.isActive()) { - let remainingMinutes = this._timer.remainingMinutes(); - let minutes = remainingMinutes % 60; - let hours = Math.floor(remainingMinutes / 60); - - let hoursText = hours.toString(); - hoursText += (hours == 1) ? ' hour' : ' hours'; - let minText = minutes.toString(); - minText += (minutes == 1) ? ' minute' : ' minutes'; - - if (hours >= 1) { - this._remainingLabel.text = '... ' + hoursText + ' and ' + minText + ' remaining.' - } else { - this._remainingLabel.text = '... ' + minText + ' remaining.' - } - - this._remainingLabel.show(); - } else { - this._remainingLabel.hide(); - } - } -}); - -const StatusElement = new Lang.Class({ - Name: 'StatusElement', - Extends: St.Icon, - - _init: function() { - - this.parent({ - icon_name: 'preferences-desktop-wallpaper-symbolic', - style_class: 'system-status-icon' - }); - - let _this = this; - - this.loadingTweenIn = { - opacity:20, - time:1, - transition:'easeInOutSine', - onComplete: function() { - Tweener.addTween(_this, _this.loadingTweenOut); - } - } - - this.loadingTweenOut = { - opacity:255, - time:1, - transition:'easeInOutSine', - onComplete: function() { - if (_this.isLoading) { - Tweener.addTween(_this, _this.loadingTweenIn); - } else { - return false; - } - return true; - } - } - - }, - - startLoading: function() { - this.isLoading = true; - Tweener.addTween(this, this.loadingTweenOut); - }, - - stopLoading: function() { - this.isLoading = false; - Tweener.removeTweens(this); - this.opacity = 255; - } - -}); - -// ------------------------------------------------------------------------------- - -// borrowed from: https://github.com/eonpatapon/gnome-shell-extensions-mediaplayer -const SliderItem = new Lang.Class({ - Name: 'SliderItem', - Extends: PopupMenu.PopupBaseMenuItem, - - _init: function(value) { - this.parent(); - - this._box = new St.Table({style_class: 'slider-item'}); - - this._slider = new Slider.Slider(value); - - this._box.add(this._slider.actor, {row: 0, col: 2, x_expand: true}); - this.actor.add(this._box, {span: -1, expand: true}); - }, - - setValue: function(value) { - this._slider.setValue(value); - }, - - getValue: function() { - return this._slider._getCurrentValue(); - }, - - setIcon: function(icon) { - this._icon.icon_name = icon + '-symbolic'; - }, - - connect: function(signal, callback) { - this._slider.connect(signal, callback); - } -}); - - -/** - * Widget for setting the delay for the next Wallpaper-change. - * @type {Lang.Class} - */ -const DelaySlider = new Lang.Class({ - Name: 'DelaySlider', - Extends: SliderItem, - - _MINUTES_MAX: 59, - _MINUTES_MIN: 5, - _HOURS_MAX: 48, - _HOURS_MIN: 1, - - /** - * Construct a new Widget. - * @private - */ - _init: function(minutes){ - this.parent(0, ''); // value MUST be specified! - this.setMinutes(minutes); // Set the real value. - }, - - /** - * Set the value of the slider to x minutes. - * @param minutes the value in minutes between _MINUTES_MAX and _MINUTES_MIN - */ - setMinutes: function(minutes){ - // Validate: - if (isNaN(minutes) || minutes < this._MINUTES_MIN || minutes > this._HOURS_MAX*60){ - throw TypeError("'minutes' should be an integer between " - +this._MINUTES_MIN+" and "+this._HOURS_MAX*60); - } - - let value = 0; - if (minutes <= this._MINUTES_MAX){ - value = (minutes - this._MINUTES_MIN) / (this._MINUTES_MAX - this._MINUTES_MIN) / 2; - } else { - value = (((minutes / 60) - this._HOURS_MIN) / (this._HOURS_MAX - this._HOURS_MIN) / 2) + 0.5; - } - - this.setValue(value); - }, - - /** - * Get the value in minutes from the slider. - * @return int the value in minutes. - */ - getMinutes: function(){ - let minutes = 0; - if (this.getValue() < 0.5) { - minutes = this._MINUTES_MIN + (this.getValue() * 2) * (this._MINUTES_MAX - this._MINUTES_MIN); - } else { - minutes = (this._HOURS_MIN + (this.getValue() - 0.5) * 2 * (this._HOURS_MAX - this._HOURS_MIN)) * 60; - } - - return (minutes < this._MINUTES_MIN) ? this._MINUTES_MIN : Math.floor(minutes); - } -}); - -// ------------------------------------------------------------------------------- diff --git a/randomwallpaper@iflow.space/elements.js b/randomwallpaper@iflow.space/elements.js new file mode 100644 index 0000000..856cd1f --- /dev/null +++ b/randomwallpaper@iflow.space/elements.js @@ -0,0 +1,213 @@ +const Lang = imports.lang; +const PopupMenu = imports.ui.popupMenu; +const St = imports.gi.St; +const Tweener = imports.ui.tweener; +const Util = imports.misc.util; + +const Self = imports.misc.extensionUtils.getCurrentExtension(); +const Timer = Self.imports.timer; + +const HistoryElement = new Lang.Class({ + Name: 'HistoryElement', + Extends: PopupMenu.PopupSubMenuMenuItem, + historyEntry: null, + + _init: function (historyEntry, index) { + this.parent("", false); + + let timestamp = historyEntry.timestamp; + let date = new Date(timestamp); + + let timeString = date.toLocaleTimeString(); + let dateString = date.toLocaleDateString(); + + let prefixText; + if (index === 0) { + prefixtext = "Current Background"; + } else { + prefixtext = String(index) + '.'; + } + this.prefixLabel = new St.Label({ + text: prefixtext, + style_class: 'rwg-history-index' + }); + + this.actor.insert_child_above(this.prefixLabel, this.label); + this.label.destroy(); + + this._container = new St.BoxLayout({ + vertical: true + }); + + this.dateLabel = new St.Label({ + text: dateString, + style_class: 'rwg-history-date' + }); + this._container.add_child(this.dateLabel); + + this.timeLabel = new St.Label({ + text: timeString, + style_class: 'rwg-history-time' + }); + this._container.add_child(this.timeLabel); + + this.historyEntry = historyEntry; + this.actor.historyId = historyEntry.id; // extend the actor with the historyId + + if (index !== 0) { + this.actor.insert_child_above(this._container, this.prefixLabel); + } + + if (this.historyEntry.source && this.historyEntry.source !== null) { + + if (this.historyEntry.source.author !== null + && this.historyEntry.source.authorUrl !== null) { + this.authorItem = new PopupMenu.PopupMenuItem('Image By: ' + this.historyEntry.source.author); + this.authorItem.connect('activate', () => { + Util.spawn(['xdg-open', this.historyEntry.source.authorUrl]); + }); + + this.menu.addMenuItem(this.authorItem); + } + + if (this.historyEntry.source.source !== null + && this.historyEntry.source.sourceUrl !== null) { + this.sourceItem = new PopupMenu.PopupMenuItem('Image From: ' + this.historyEntry.source.source); + this.sourceItem.connect('activate', () => { + Util.spawn(['xdg-open', this.historyEntry.source.sourceUrl]); + }); + + this.menu.addMenuItem(this.sourceItem); + } + + this.imageUrlItem = new PopupMenu.PopupMenuItem('Open Image In Browser'); + this.imageUrlItem.connect('activate', () => { + Util.spawn(['xdg-open', this.historyEntry.source.imageUrl]); + }); + + this.menu.addMenuItem(this.imageUrlItem); + + } else { + this.menu.addMenuItem(new PopupMenu.PopupMenuItem('Unknown source.')); + } + + } +}); + +const CurrentImageElement = new Lang.Class({ + Name: 'CurrentImageElement', + Extends: HistoryElement, + + _init: function(historyElement) { + this.parent(historyElement, 0); + } +}); + +/** + * Element for the New Wallpaper button and the remaining time for the auto fetch + * feature. + * The remaining time will only be displayed if the af-feature is activated. + * + * @type {Lang.Class} + */ +const NewWallpaperElement = new Lang.Class({ + Name: 'NewWallpaperElement', + Extends: PopupMenu.PopupBaseMenuItem, + + _init: function (params) { + this.parent(params); + + this._timer = new Timer.AFTimer(); + + this._container = new St.BoxLayout({ + vertical: true + }); + + this._newWPLabel = new St.Label({ + text: 'New Wallpaper', + style_class: 'rwg-new-lable' + }); + this._container.add_child(this._newWPLabel); + + this._remainingLabel = new St.Label({ + text: '1 minute remaining' + }); + this._container.add_child(this._remainingLabel); + + this.actor.add_child(this._container); + }, + + show: function () { + if (this._timer.isActive()) { + let remainingMinutes = this._timer.remainingMinutes(); + let minutes = remainingMinutes % 60; + let hours = Math.floor(remainingMinutes / 60); + + let hoursText = hours.toString(); + hoursText += (hours == 1) ? ' hour' : ' hours'; + let minText = minutes.toString(); + minText += (minutes == 1) ? ' minute' : ' minutes'; + + if (hours >= 1) { + this._remainingLabel.text = '... ' + hoursText + ' and ' + minText + ' remaining.' + } else { + this._remainingLabel.text = '... ' + minText + ' remaining.' + } + + this._remainingLabel.show(); + } else { + this._remainingLabel.hide(); + } + } +}); + +const StatusElement = new Lang.Class({ + Name: 'StatusElement', + Extends: St.Icon, + + _init: function () { + + this.parent({ + icon_name: 'preferences-desktop-wallpaper-symbolic', + style_class: 'system-status-icon' + }); + + let _this = this; + + this.loadingTweenIn = { + opacity: 20, + time: 1, + transition: 'easeInOutSine', + onComplete: function () { + Tweener.addTween(_this, _this.loadingTweenOut); + } + }; + + this.loadingTweenOut = { + opacity: 255, + time: 1, + transition: 'easeInOutSine', + onComplete: function () { + if (_this.isLoading) { + Tweener.addTween(_this, _this.loadingTweenIn); + } else { + return false; + } + return true; + } + } + + }, + + startLoading: function () { + this.isLoading = true; + Tweener.addTween(this, this.loadingTweenOut); + }, + + stopLoading: function () { + this.isLoading = false; + Tweener.removeTweens(this); + this.opacity = 255; + } + +}); diff --git a/randomwallpaper@iflow.space/extension.js b/randomwallpaper@iflow.space/extension.js index bfebcc7..0dc81e8 100644 --- a/randomwallpaper@iflow.space/extension.js +++ b/randomwallpaper@iflow.space/extension.js @@ -6,12 +6,14 @@ const Shell = imports.gi.Shell; const Self = imports.misc.extensionUtils.getCurrentExtension(); const WallpaperController = Self.imports.wallpaperController; +const LoggerModule = Self.imports.logger; + // UI Imports const Main = imports.ui.main; const St = imports.gi.St; const PanelMenu = imports.ui.panelMenu; const PopupMenu = imports.ui.popupMenu; -const CustomElements = Self.imports.Elements; +const CustomElements = Self.imports.elements; const Tweener = imports.ui.tweener; // Filesystem @@ -33,9 +35,11 @@ let panelEntry; let RandomWallpaperEntry = new Lang.Class({ Extends: PanelMenu.Button, Name: "RandomWallpaperEntry", + logger: null, _init: function(menuAlignment, nameText) { this.parent(menuAlignment, nameText); + this.logger = new LoggerModule.Logger('RWG3', 'RandomWallpaperEntry'); // Panelmenu Icon this.statusIcon = new CustomElements.StatusElement(); @@ -48,6 +52,11 @@ let RandomWallpaperEntry = new Lang.Class({ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + // current background section + this.currentBackgroundSection = new PopupMenu.PopupMenuSection(); + this.menu.addMenuItem(this.currentBackgroundSection); + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + // history section this.historySection = new PopupMenu.PopupMenuSection(); this.menu.addMenuItem(this.historySection); @@ -64,7 +73,10 @@ let RandomWallpaperEntry = new Lang.Class({ this.openFolder = new PopupMenu.PopupMenuItem('Open Wallpaper Folder'); this.menu.addMenuItem(this.openFolder); - //this.menu.addMenuItem(new CustomElements.DelaySlider(60)); + // settings button + this.openSettings = new PopupMenu.PopupMenuItem('Settings'); + this.menu.addMenuItem(this.openSettings); + /* add eventlistener */ @@ -89,6 +101,17 @@ let RandomWallpaperEntry = new Lang.Class({ Gio.AppInfo.launch_default_for_uri(uri, global.create_app_launch_context(0, -1)) }); + this.openSettings.connect("activate", function(){ + // call gnome settings tool for this extension + let app = Shell.AppSystem.get_default().lookup_app("gnome-shell-extension-prefs.desktop"); + if( app!=null ) { + // only works in Gnome >= 3.12 + let info = app.get_app_info(); + let timestamp = global.display.get_current_time_roundtrip(); + info.launch_uris([Self.uuid], global.create_app_launch_context(timestamp, -1)); + } + }); + this.menu.actor.connect('show', function() { this.newWallpaperItem.show(); wallpaperController.menuShowHook(); @@ -107,19 +130,34 @@ let RandomWallpaperEntry = new Lang.Class({ }, + setCurrentBackgroundElement: function () { + this.currentBackgroundSection.removeAll(); + + let historyController = wallpaperController.getHistoryController(); + let history = historyController.history; + + if (history.length > 0) { + let currentImage = new CustomElements.CurrentImageElement(history[0]); + this.currentBackgroundSection.addMenuItem(currentImage); + } + }, + setHistoryList: function() { + this.setCurrentBackgroundElement(); + this.historySection.removeAll(); - let history = this.history = wallpaperController.getHistory(); + let historyController = wallpaperController.getHistoryController(); + let history = historyController.history; if (history.length <= 1) { this.clearHistoryList(); return; - }; + } - for (var i = 1; i < history.length; i++) { - let historyid = history[i]; - let tmp = new CustomElements.HistoryElement(historyid, i); + for (let i = 1; i < history.length; i++) { + let historyid = history[i].id; + let tmp = new CustomElements.HistoryElement(history[i], i); tmp.actor.connect('key-focus-in', onEnter); tmp.actor.connect('key-focus-out', onLeave); @@ -128,7 +166,7 @@ let RandomWallpaperEntry = new Lang.Class({ tmp.connect('activate', onSelect); this.historySection.addMenuItem(tmp); - }; + } function onLeave(actor) { wallpaperController.resetWallpaper(); diff --git a/randomwallpaper@iflow.space/history.js b/randomwallpaper@iflow.space/history.js new file mode 100644 index 0000000..405fcdb --- /dev/null +++ b/randomwallpaper@iflow.space/history.js @@ -0,0 +1,159 @@ +const Lang = imports.lang; +const Mainloop = imports.gi.GLib; + +// Filesystem +const Gio = imports.gi.Gio; + +const Self = imports.misc.extensionUtils.getCurrentExtension(); +const Prefs = Self.imports.settings; + +const LoggerModule = Self.imports.logger; + +let HistoryEntry = new Lang.Class({ + Name: "HistoryEntry", + timestamp: null, + id: null, + path: null, + source: null, + + _init: function(author, source, url) { + this.timestamp = new Date().getTime(); + + this.source = { + author: author, + authorUrl: null, + source: source, + sourceUrl: null, + imageUrl: url + }; + }, +}); + +let HistoryController = new Lang.Class({ + Name: "HistoryController", + _settings: null, + _wallpaperlocation: null, + + logger: null, + size: 10, + history: [], + + _init: function(wallpaperlocation) { + this.logger = new LoggerModule.Logger('RWG3', 'HistoryController'); + + this._settings = new Prefs.Settings(); + this._wallpaperlocation = wallpaperlocation; + + this.load(); + }, + + insert: function(historyElement) { + this.history.unshift(historyElement); + this._deleteOldPictures(); + this.save(); + }, + + /** + * Set the given id to to the first history element (the current one) + * @param id + * @returns {boolean} + */ + promoteToActive: function(id) { + let element = this.get(id); + if (element === null) { + return false; + } + + element.timestamp = new Date().getTime(); + this.history = this.history.sort((elem1, elem2) => { return elem1.timestamp < elem2.timestamp }); + this.save(); + + return true; + }, + + /** + * Returns the corresponding HistoryEntry or null + * @param id + * @returns {*} + */ + get: function(id) { + for (let elem of this.history) { + if (elem.id == id) { + return elem; + } + } + + return null; + }, + + /** + * Load the history from the gschema + */ + load: function() { + this.size = this._settings.get('history-length', 'int'); + let stringHistory = this._settings.get('history', 'strv'); + this.history = stringHistory.map(elem => { + return JSON.parse(elem) + }); + }, + + /** + * Save the history to the gschema + */ + save: function() { + let stringHistory = this.history.map(elem => { return JSON.stringify(elem) }); + this._settings.set('history', 'strv', stringHistory); + }, + + /** + * Clear the history and delete all photos except the current one. + * @returns {boolean} + */ + clear() { + let firstHistoryElement = this.history[0]; + + if (firstHistoryElement) + this.history = [firstHistoryElement]; + + let directory = Gio.file_new_for_path(this._wallpaperlocation); + let enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); + + let fileinfo; + let deleteFile; + + do { + + fileinfo = enumerator.next_file(null); + + if (!fileinfo) { + break; + } + + let id = fileinfo.get_name(); + + // ignore hidden files and first element + if (id[0] != '.' && id != firstHistoryElement.id) { + deleteFile = Gio.file_new_for_path(this._wallpaperlocation + id); + deleteFile.delete(null); + } + + } while(fileinfo); + + this.save(); + return true; + }, + + /** + * Delete all pictures that have no slot in the history. + * @private + */ + _deleteOldPictures: function() { + this.size = this._settings.get('history-length', 'int'); + let deleteFile; + while(this.history.length > this.size) { + deleteFile = Gio.file_new_for_path(this.history.pop().path); + deleteFile.delete(null); + } + }, + +}); \ No newline at end of file diff --git a/randomwallpaper@iflow.space/images/shuffle-icon.png b/randomwallpaper@iflow.space/images/shuffle-icon.png deleted file mode 100644 index c2e2a88..0000000 Binary files a/randomwallpaper@iflow.space/images/shuffle-icon.png and /dev/null differ diff --git a/randomwallpaper@iflow.space/images/shuffle-icon.svg b/randomwallpaper@iflow.space/images/shuffle-icon.svg deleted file mode 100644 index c651f29..0000000 --- a/randomwallpaper@iflow.space/images/shuffle-icon.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/randomwallpaper@iflow.space/logger.js b/randomwallpaper@iflow.space/logger.js index 61b2356..d0a4fc0 100644 --- a/randomwallpaper@iflow.space/logger.js +++ b/randomwallpaper@iflow.space/logger.js @@ -22,7 +22,7 @@ let Logger = new Lang.Class({ this._log("INFO", message); }, - warning: function (message) { + warn: function (message) { this._log("WARNING", message); }, diff --git a/randomwallpaper@iflow.space/metadata.json b/randomwallpaper@iflow.space/metadata.json index a7ad97f..68770b7 100644 --- a/randomwallpaper@iflow.space/metadata.json +++ b/randomwallpaper@iflow.space/metadata.json @@ -11,6 +11,7 @@ "uuid": "randomwallpaper@iflow.space", "settings-schema": "org.gnome.shell.extensions.space.iflow.randomwallpaper", "name": "Random Wallpaper", - "description": "Fetches a random wallpaper from desktopper.co and sets it as desktop background", - "version": 1.1 + "description": "Fetches a random wallpaper from an online source and sets it as desktop background. \nThe desktop background can be updated periodically or manually.", + "version": 2.0, + "url": "https://github.com/ifl0w/RandomWallpaperGnome3" } diff --git a/randomwallpaper@iflow.space/prefs.js b/randomwallpaper@iflow.space/prefs.js index e088260..4fbd998 100644 --- a/randomwallpaper@iflow.space/prefs.js +++ b/randomwallpaper@iflow.space/prefs.js @@ -12,6 +12,11 @@ const Gettext = imports.gettext.domain('space.iflow.randomwallpaper'); //const _ = Gettext.gettext; const RWG_SETTINGS_SCHEMA = 'org.gnome.shell.extensions.space.iflow.randomwallpaper'; +const RWG_SETTINGS_SCHEMA_DESKTOPPER = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.desktopper'; +const RWG_SETTINGS_SCHEMA_UNSPLASH = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.unsplash'; +const RWG_SETTINGS_SCHEMA_WALLHEAVEN = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.wallheaven'; + +const LoggerModule = Self.imports.logger; function init() { //Convenience.initTranslations(); @@ -28,6 +33,14 @@ function buildPrefsWidget() { /* UI Setup */ const RandomWallpaperSettings = new Lang.Class({ Name: 'RandomWallpaper.Settings', + logger: null, + + currentSourceSettingsWidget: null, + + noSettings: null, + desktopperSettings: null, + unsplashSettings: null, + wallheavenSettings: null, _init: function () { this._settings = Convenience.getSettings(RWG_SETTINGS_SCHEMA); @@ -35,6 +48,25 @@ const RandomWallpaperSettings = new Lang.Class({ //this._builder.set_translation_domain(Self.metadata['gettext-domain']); this._builder.add_from_file(Self.path + '/settings.ui'); + this.logger = new LoggerModule.Logger('RWG3', 'RandomWallpaper.Settings'); + + this.noSettings = this._builder.get_object('no-settings'); + + // Desktopper Settings + this._desktopper_settings = Convenience.getSettings(RWG_SETTINGS_SCHEMA_DESKTOPPER); + this.desktopperSettings = this._builder.get_object('desktopper-settings'); + this.bindDesktopper(); + + // Unsplash Settings + this._unsplash_settings = Convenience.getSettings(RWG_SETTINGS_SCHEMA_UNSPLASH); + this.unsplashSettings = this._builder.get_object('unsplash-settings'); + this.bindUnsplash(); + + // Wallheaven Settings + this._wallheaven_settings = Convenience.getSettings(RWG_SETTINGS_SCHEMA_WALLHEAVEN); + this.wallheavenSettings = this._builder.get_object('wallheaven-settings'); + this.bindWallheaven(); + this._toggleAfSliders(); this.widget = this._builder.get_object('main-widget'); @@ -43,6 +75,31 @@ const RandomWallpaperSettings = new Lang.Class({ this._toggleAfSliders(); }.bind(this)); + this._builder.get_object('source-combo').connect('changed', (sourceCombo) => { + let container = this._builder.get_object('source-settings-frame'); + if (this.currentSourceSettingsWidget !== null) { + container.remove(this.currentSourceSettingsWidget); + } + + switch (sourceCombo.active) { + case 0: // desktopper + this.currentSourceSettingsWidget = this.desktopperSettings; + break; + case 1: // unsplash + this.currentSourceSettingsWidget = this.unsplashSettings; + break; + case 2: // wallheaven + this.currentSourceSettingsWidget = this.wallheavenSettings; + break; + default: + this.currentSourceSettingsWidget = this.noSettings; + break; + } + + container.add(this.currentSourceSettingsWidget); + + }); + this._settings.bind('history-length', this._builder.get_object('history-length'), 'value', @@ -73,6 +130,66 @@ const RandomWallpaperSettings = new Lang.Class({ this._builder.get_object('duration-slider-hours').set_sensitive(false); this._builder.get_object('duration-slider-minutes').set_sensitive(false); } + }, + + bindDesktopper: function () { + this._desktopper_settings.bind('allow-unsafe', + this._builder.get_object('desktopper-allow-unsafe'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + }, + + bindUnsplash: function () { + this._unsplash_settings.bind('unsplash-keyword', + this._builder.get_object('unsplash-keyword'), + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._unsplash_settings.bind('username', + this._builder.get_object('unsplash-username'), + 'text', + Gio.SettingsBindFlags.DEFAULT); + + this._unsplash_settings.bind('image-width', + this._builder.get_object('unsplash-image-width'), + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._unsplash_settings.bind('image-height', + this._builder.get_object('unsplash-image-height'), + 'value', + Gio.SettingsBindFlags.DEFAULT); + }, + + bindWallheaven: function () { + this._wallheaven_settings.bind('wallheaven-keyword', + this._builder.get_object('wallheaven-keyword'), + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._wallheaven_settings.bind('resolutions', + this._builder.get_object('wallheaven-resolutions'), + 'text', + Gio.SettingsBindFlags.DEFAULT); + + this._wallheaven_settings.bind('category-general', + this._builder.get_object('wallheaven-category-general'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._wallheaven_settings.bind('category-anime', + this._builder.get_object('wallheaven-category-anime'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._wallheaven_settings.bind('category-people', + this._builder.get_object('wallheaven-category-people'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + + this._wallheaven_settings.bind('allow-sfw', + this._builder.get_object('wallheaven-allow-sfw'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._wallheaven_settings.bind('allow-sketchy', + this._builder.get_object('wallheaven-allow-sketchy'), + 'active', + Gio.SettingsBindFlags.DEFAULT); } }); diff --git a/randomwallpaper@iflow.space/schemas/gschemas.compiled b/randomwallpaper@iflow.space/schemas/gschemas.compiled index 16e327e..bfc7a76 100644 Binary files a/randomwallpaper@iflow.space/schemas/gschemas.compiled and b/randomwallpaper@iflow.space/schemas/gschemas.compiled differ diff --git a/randomwallpaper@iflow.space/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/randomwallpaper@iflow.space/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 71b93bd..715a32b 100644 --- a/randomwallpaper@iflow.space/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/randomwallpaper@iflow.space/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -2,13 +2,13 @@ - - - + + + + id='org.gnome.shell.extensions.space.iflow.randomwallpaper'> 10 @@ -38,9 +38,9 @@ - 'desktoppr' - Wallpaper Source - Describs the adapter that will be used. + 'desktoppr' + Wallpaper Source + Describs the adapter that will be used. @@ -56,4 +56,77 @@ + + + + false + Allow Unsafe + Weather the extension should fetch images that are rated as unsafe. + + + + + + "" + Keyword + The keyword will be used to search images. + + + "" + Username + Only fetch random images of a given user. + + + 1920 + Image Width + The width of the image. + + + 1080 + Image Width + The height of the image. + + + + + + "" + Keyword + The keyword will be used to search images. + + + "1920x1200, 1920x1080, 2560x1440, 2560x1600, 3840x1080" + Resolutions + The acceptable resolutions. + + + true + SFW + Weather safe images are allowed. + + + false + Sketchy + Weather sketchy images are allowed. + + + + true + Category General + Weather the general category should be searched. + + + true + Category Anime + Weather the anime category should be searched. + + + true + Category People + Weather the people category should be searched. + + diff --git a/randomwallpaper@iflow.space/settings.js b/randomwallpaper@iflow.space/settings.js index bb1970a..84fe953 100644 --- a/randomwallpaper@iflow.space/settings.js +++ b/randomwallpaper@iflow.space/settings.js @@ -6,31 +6,37 @@ const Self = imports.misc.extensionUtils.getCurrentExtension(); const Convenience = Self.imports.convenience; let Settings = new Lang.Class({ - Name: "Settings", + Name: "Settings", _settings: null, - _init: function() { - this._settings = Convenience.getSettings(); - }, + /** + * Settings object. + * + * @param [schema] + * @private + */ + _init: function (schema) { + this._settings = Convenience.getSettings(schema); + }, - observe: function(key, callback) { - this._settings.connect('changed::'+key, callback); - }, + observe: function (key, callback) { + this._settings.connect('changed::' + key, callback); + }, - set: function(key, type, value) { - if (this._settings['set_'+type](key, value)){ - Gio.Settings.sync(); // wait for write - } else { - throw "Could not set " + key + " (type: " + type + ") with the value " + value; - } - }, + set: function (key, type, value) { + if (this._settings['set_' + type](key, value)) { + Gio.Settings.sync(); // wait for write + } else { + throw "Could not set " + key + " (type: " + type + ") with the value " + value; + } + }, - get: function(key, type) { - return this._settings['get_'+type](key); - }, + get: function (key, type) { + return this._settings['get_' + type](key); + }, - getSourceAdapter: function() { + getSourceAdapter: function () { global.log(this._settings.get_enum('source')); - return null; - } + return null; + } }); diff --git a/randomwallpaper@iflow.space/settings.ui b/randomwallpaper@iflow.space/settings.ui index 58bb260..13eb4c8 100644 --- a/randomwallpaper@iflow.space/settings.ui +++ b/randomwallpaper@iflow.space/settings.ui @@ -2,6 +2,29 @@ + + True + False + 10 + 10 + 10 + 10 + vertical + + + Allow unsafe images (also fetch images that are marked as unsafe) + True + True + False + True + + + False + True + 0 + + + 23 1 @@ -115,7 +138,7 @@ 0 desktoppr.co - unsplash.com (not implemented, defaults to desktopper) + unsplash.com (experimental) alpha.wallheaven.cc (experimental) @@ -131,34 +154,22 @@ - + True False True 0 in - + + + + True False - 12 - - - True - False - 10 - 10 - No Settings Available - - - + Source Settings - - - 0 @@ -466,4 +477,362 @@ + + True + False + 10 + 10 + 10 + 10 + No Settings Available + + + + 1 + 1000000 + 1 + 10 + + + 1 + 1000000 + 1 + 10 + + + True + False + 10 + 10 + 10 + 10 + vertical + + + True + False + start + Username + + + False + True + 0 + + + + + True + True + 10 + @username + + + False + True + 1 + + + + + True + False + start + Keyword + + + False + True + 2 + + + + + True + True + 10 + Enter a keyword ... + + + False + True + 3 + + + + + True + False + + + True + False + 5 + True + vertical + + + True + False + start + Image Width + + + False + True + 0 + + + + + True + True + number + unsplash-image-width + + + True + True + 1 + + + + + False + True + 0 + + + + + True + False + 5 + True + vertical + + + True + False + start + Image Height + + + False + True + 0 + + + + + True + True + number + unsplash-image-height + + + True + True + 1 + + + + + False + True + 1 + + + + + False + True + 4 + + + + + True + False + 10 + 10 + 10 + 10 + vertical + + + True + False + start + Keyword + + + False + True + 0 + + + + + True + True + 10 + Enter a keyword ... + + + False + True + 1 + + + + + True + False + start + Allowed Content Ratings + + + False + True + 2 + + + + + True + False + 10 + start + + + SFW (Safe for work) + True + True + False + True + + + True + True + 0 + + + + + Sketchy + True + True + False + True + + + True + True + 1 + + + + + False + True + 3 + + + + + True + False + start + Categories + + + False + True + 4 + + + + + True + False + 10 + start + + + General + True + True + False + True + + + True + True + 0 + + + + + Anime + True + True + False + True + + + True + True + 1 + + + + + People + True + True + False + True + + + True + True + 2 + + + + + False + True + 5 + + + + + True + False + start + Resolutions + + + False + True + 6 + + + + + True + True + 1920x1080, 1920x1200 + + + False + True + 7 + + + diff --git a/randomwallpaper@iflow.space/sourceAdapter.js b/randomwallpaper@iflow.space/sourceAdapter.js index d510ba1..3305226 100644 --- a/randomwallpaper@iflow.space/sourceAdapter.js +++ b/randomwallpaper@iflow.space/sourceAdapter.js @@ -1,78 +1,198 @@ const Lang = imports.lang; +const Self = imports.misc.extensionUtils.getCurrentExtension(); // network requests const Soup = imports.gi.Soup; const Json = imports.gi.Json; -let DesktopperAdapter = new Lang.Class({ - Name: "DesktopperAdapter", - /* - fetch a random image url from desktopper.cc - and call callback function with the URL of the image +const RWG_SETTINGS_SCHEMA_DESKTOPPER = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.desktopper'; +const RWG_SETTINGS_SCHEMA_UNSPLASH = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.unsplash'; +const RWG_SETTINGS_SCHEMA_WALLHEAVEN = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.wallheaven'; + +const SettingsModule = Self.imports.settings; +const HistoryModule = Self.imports.history; + +const LoggerModule = Self.imports.logger; + +let BaseAdapter = new Lang.Class({ + Name: "BaseAdapter", + logger: null, + + _init: function () { + this.logger = new LoggerModule.Logger('RWG3', 'BaseAdapter'); + }, + + /** + * Retrieves a new url for an image and calls the given callback with an HistoryEntry as parameter. + * @param callback */ + requestRandomImage: function (callback) { + this.logger.error("requestRandomImage not implemented"); + + callback(null); + }, + + fileName: function (uri) { + let base = new String(uri).substring(uri.lastIndexOf('/') + 1); + return base; + }, + +}); + +let DesktopperAdapter = new Lang.Class({ + Name: "DesktopperAdapter", + Extends: BaseAdapter, + + _settings: null, + + _init: function () { + this.parent(); + + this._settings = new SettingsModule.Settings(RWG_SETTINGS_SCHEMA_DESKTOPPER); + }, + requestRandomImage: function (callback) { let session = new Soup.SessionAsync(); - let message = Soup.Message.new('GET', 'https://api.desktoppr.co/1/wallpapers/random'); - let parser = new Json.Parser(); + let url = 'https://api.desktoppr.co/1/wallpapers/random'; + let allowUnsafe = this._settings.get('allow-unsafe', 'boolean'); + if (allowUnsafe) { + url += '?safe_filter=all'; + } else { + url += '?safe_filter=safe'; + } + url = encodeURI(url); + this.logger.debug("Base URL: " + url); - var _this = this; + let message = Soup.Message.new('GET', url); - session.queue_message(message, function (session, message) { - parser.load_from_data(message.response_body.data, -1); - - let data = parser.get_root().get_object(); - let response = data.get_object_member('response'); - let imageUrl = response.get_object_member('image').get_string_member('url'); + session.queue_message(message, (session, message) => { + let data = JSON.parse(message.response_body.data); + let response = data.response; + let imageUrl = encodeURI(response.image.url); if (callback) { - callback(imageUrl); + let historyEntry = new HistoryModule.HistoryEntry(null, 'desktopper.co', imageUrl); + historyEntry.source.sourceUrl = 'https://www.desktoppr.co/'; + callback(historyEntry); } }); } }); +let UnsplashAdapter = new Lang.Class({ + Name: "UnsplashAdapter", + Extends: BaseAdapter, + + sourceName: 'Unsplash', + sourceUrl: 'https://unsplash.com/', + + _settings: null, + + // query options + options: { + 'username': '', + 'query': '', + 'w': 1920, + 'h': 1080, + }, + + _init: function () { + this.parent(); + + this._settings = new SettingsModule.Settings(RWG_SETTINGS_SCHEMA_UNSPLASH); + }, + + requestRandomImage: function (callback) { + let session = new Soup.SessionAsync(); + + this._readOptionsFromSettings(); + let optionsString = this._generateOptionsString(); + let url = 'https://api.unsplash.com/photos/random?' + optionsString; + url += 'client_id=64daf439e9b579dd566620c0b07022706522d87b255d06dd01d5470b7f193b8d'; + url = encodeURI(url); + this.logger.debug("Base URL: " + url); + + let message = Soup.Message.new('GET', url); + + let utmParameters = '?utm_source=RandomWallpaperGnome3&utm_medium=referral&utm_campaign=api-credit'; + + session.queue_message(message, (session, message) => { + let data = JSON.parse(message.response_body.data); + + let imageUrl = encodeURI(data.links.download + utmParameters); + let authorName = data.user.name; + let authorUrl = encodeURI(data.user.links.html); + + if (callback) { + let historyEntry = new HistoryModule.HistoryEntry(authorName, this.sourceName, encodeURI(imageUrl)); + historyEntry.source.sourceUrl = encodeURI(this.sourceUrl + utmParameters); + historyEntry.source.authorUrl = encodeURI(authorUrl + utmParameters); + callback(historyEntry); + } + }); + }, + + _generateOptionsString: function () { + let options = this.options; + let optionsString = ""; + + for (let key in options) { + if (options.hasOwnProperty(key)) { + if (options[key]) { + optionsString += key + "=" + options[key] + "&"; + } + } + } + + return optionsString; + }, + + _readOptionsFromSettings: function () { + this.options.query = this._settings.get('unsplash-keyword', 'string'); + + this.options.username = this._settings.get('username', 'string'); + if (this.options.username[0] === '@') { + this.options.username = this.options.username.substring(1); // remove @ prefix + } + + this.options.w = this._settings.get('image-width', 'int'); + this.options.h = this._settings.get('image-height', 'int'); + } +}); + let WallheavenAdapter = new Lang.Class({ Name: "WallheavenAdapter", + Extends: BaseAdapter, + _settings: null, // query options options: { 'q': '', 'purity': '110', // SFW, sketchy 'sorting': 'random', - 'category': '111', // General, Anime, People + 'categories': '111', // General, Anime, People 'resolutions': ['1920x1200', '2560x1440'] }, - /* - fetch a random image url from wallheaven.cc with the given options - and call callback function with the URL of the image - */ + _init: function () { + this.parent(); + + this._settings = new SettingsModule.Settings(RWG_SETTINGS_SCHEMA_WALLHEAVEN); + }, + requestRandomImage: function (callback) { let session = new Soup.SessionAsync(); - let options = this.options; - let optionsString = ""; - - for (var key in options) { - if (options.hasOwnProperty(key)) { - if (Array.isArray(options[key])) { - optionsString += key + "=" + options[key].join() + "&"; - } else { - optionsString += key + "=" + options[key] + "&"; - } - } - } - // remove last '&' - optionsString = optionsString.slice(0, -1); - + this._readOptionsFromSettings(); + let optionsString = this._generateOptionsString(); let url = 'http://alpha.wallhaven.cc/search?' + optionsString; + url = encodeURI(url); + this.logger.debug("Base URL: " + url); let message = Soup.Message.new('GET', url); - var _this = this; - - session.queue_message(message, function (session, message) { + session.queue_message(message, (session, message) => { let body = message.response_body.data; let urlArray = body.match(new RegExp(/http[s]*:\/\/alpha.wallhaven.cc\/wallpaper\/[0-9]+/g)); @@ -82,22 +202,65 @@ let WallheavenAdapter = new Lang.Class({ }); // get a random entry from the array - var url = uniqueUrlArray[Math.floor(Math.random() * uniqueUrlArray.length)]; + let url = uniqueUrlArray[Math.floor(Math.random() * uniqueUrlArray.length)]; message = Soup.Message.new('GET', url); - session.queue_message(message, function () { + session.queue_message(message, () => { let body = message.response_body.data; let imageUrl = body.match(new RegExp(/\/\/wallpapers.wallhaven.cc\/wallpapers\/full\/.*?"/))[0]; imageUrl = imageUrl.slice(0, -1); imageUrl = 'http:' + imageUrl; + imageUrl = encodeURI(imageUrl); if (callback) { - callback(imageUrl); + let historyEntry = new HistoryModule.HistoryEntry(null, 'wallhaven.cc', imageUrl); + historyEntry.source.sourceUrl = 'https://alpha.wallhaven.cc/'; + callback(historyEntry); } }) }); + }, + + _generateOptionsString: function () { + let options = this.options; + let optionsString = ""; + + for (let key in options) { + if (options.hasOwnProperty(key)) { + if (Array.isArray(options[key])) { + optionsString += key + "=" + options[key].join() + "&"; + } else { + if (options[key]) { + optionsString += key + "=" + options[key] + "&"; + } + } + } + } + + return optionsString; + }, + + _readOptionsFromSettings: function () { + this.options.q = this._settings.get('wallheaven-keyword', 'string'); + + this.options.resolutions = this._settings.get('resolutions', 'string').split(','); + this.options.resolutions = this.options.resolutions.map((elem) => { + return elem.trim(); + }); + + let categories = []; + categories.push(+this._settings.get('category-general', 'boolean')); // + is implicit conversion to int + categories.push(+this._settings.get('category-anime', 'boolean')); + categories.push(+this._settings.get('category-people', 'boolean')); + this.options.categories = categories.join(''); + + let purity = []; + purity.push(+this._settings.get('allow-sfw', 'boolean')); + purity.push(+this._settings.get('allow-sketchy', 'boolean')); + purity.push(0); // required by wallheaven + this.options.purity = purity.join(''); } }); diff --git a/randomwallpaper@iflow.space/stylesheet.css b/randomwallpaper@iflow.space/stylesheet.css index 2e7a466..872fd5f 100644 --- a/randomwallpaper@iflow.space/stylesheet.css +++ b/randomwallpaper@iflow.space/stylesheet.css @@ -1,9 +1,7 @@ .rwg_system_status_icon { - /*icon-size: 1.09em;*/ icon-size: 1em; - /*padding: 0 5px;*/ - padding: 0 0; + padding: 0 0; position: absolute; -webkit-animation:spin 4s linear infinite; -moz-animation:spin 4s linear infinite; @@ -26,25 +24,23 @@ .rwg-recent-lable { width: 100%; - /*font-weight: bold;*/ font-size: 90%; text-align: left; border: 0; } .rwg-history-index { + text-align: left; padding-top: .35em; font-size: 130%; - font-weight: lighter; + font-weight: lighter; } .rwg-history-date { - /*padding-top: .35em;*/ font-size: 100%; } .rwg-history-time { - /*padding-top: .35em;*/ font-size: 100%; font-weight: lighter; } \ No newline at end of file diff --git a/randomwallpaper@iflow.space/wallpaperController.js b/randomwallpaper@iflow.space/wallpaperController.js index 0025fc8..a4c015c 100644 --- a/randomwallpaper@iflow.space/wallpaperController.js +++ b/randomwallpaper@iflow.space/wallpaperController.js @@ -14,6 +14,7 @@ const SourceAdapter = Self.imports.sourceAdapter; const Convenience = Self.imports.convenience; const Prefs = Self.imports.settings; const Timer = Self.imports.timer; +const HistoryModule = Self.imports.history; const LoggerModule = Self.imports.logger; @@ -24,9 +25,8 @@ let WallpaperController = new Lang.Class({ wallpaperlocation: '', currentWallpaper: '', - historySize: 10, - history: [], - imageSourceAdapter: undefined, + _historyController: null, + imageSourceAdapter: null, _timer: null, _autoFetch : { @@ -44,6 +44,7 @@ let WallpaperController = new Lang.Class({ this.wallpaperlocation = this.extensionMeta.path + '/wallpapers/'; this._timer = new Timer.AFTimer(); + this._historyController = new HistoryModule.HistoryController(this.wallpaperlocation); this._settings = new Prefs.Settings(); this._settings.observe('history-length', this._updateHistory.bind(this)); @@ -54,17 +55,17 @@ let WallpaperController = new Lang.Class({ this._updateHistory(); this._updateAutoFetching(); - this.history = this._loadHistory(); this.currentWallpaper = this._getCurrentWallpaper(); this._desktopperAdapter = new SourceAdapter.DesktopperAdapter(); + this._unsplashAdapter = new SourceAdapter.UnsplashAdapter(); this._wallheavenAdapter = new SourceAdapter.WallheavenAdapter(); this.logger = new LoggerModule.Logger('RWG3', 'WallpaperController'); }, _updateHistory: function() { - this.historySize = this._settings.get('history-length', 'int'); + this._historyController.load(); }, _updateAutoFetching: function() { @@ -91,6 +92,9 @@ let WallpaperController = new Lang.Class({ case 0: this.imageSourceAdapter = this._desktopperAdapter; break; + case 1: + this.imageSourceAdapter = this._unsplashAdapter; + break; case 2: this.imageSourceAdapter = this._wallheavenAdapter; break; @@ -108,21 +112,17 @@ let WallpaperController = new Lang.Class({ the written file as parameter. */ _fetchFile: function(uri, callback){ + //extract the name from the url and let date = new Date(); - let inputbuffer; - - //extract the name from the desktopper url and add timestamp prefix - let name = date.getTime() + uri.substr(uri.lastIndexOf('.')); + let name = date.getTime()+'_'+this.imageSourceAdapter.fileName(uri); // timestamp ensures uniqueness let output_file = Gio.file_new_for_path(this.wallpaperlocation + String(name)); let output_stream = output_file.create(0, null); let input_file = Gio.file_new_for_uri(uri); - let _this = this; - - input_file.load_contents_async(null, function(file, result){ - let contents = file.load_contents_finish(result)[1]; + input_file.load_contents_async(null, (file, result) => { + let contents = file.load_contents_finish(result)[1]; // TODO: error handling. This failes due to: "An unexpected TLS packet was received." output_stream.write(contents, null); // call callback with the name and the full filepath of the written file as parameter @@ -132,38 +132,8 @@ let WallpaperController = new Lang.Class({ }); }, - /* - Set a new timestamp as name for the given file - and adapt the history - */ - _setNewFileName: function(historyid) { - let date = new Date(); - let file = Gio.file_new_for_path(this.wallpaperlocation + historyid); - let name = date.getTime() + historyid.substr(historyid.lastIndexOf('.')); - let newFile = Gio.file_new_for_path(this.wallpaperlocation + name); - - for (var i = 0; i < this.history.length; i++) { - if(this.history[i] == historyid) { - file.move(newFile, Gio.FileCopyFlags.NONE, null, function(){ - }); - - // TODO: error handling, what if move fails? - - this.history[i] = name; - - this.history.sort(); - this.history.reverse(); - - return name; - } - } - - return false; - }, - _setBackground: function(path, callback){ let background_setting = new Gio.Settings({schema: "org.gnome.desktop.background"}); - this.deleteOldPictures(); /* inspired from: @@ -192,69 +162,37 @@ let WallpaperController = new Lang.Class({ return background_setting.get_string("picture-uri").replace(/^(file:\/\/)/, ""); }, - _loadHistory: function () { - let directory = Gio.file_new_for_path(this.wallpaperlocation); - let enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); + setWallpaper: function(historyId) { + let historyElement = this._historyController.get(historyId); - let fileinfo; - let history = []; - - do { - fileinfo = enumerator.next_file(null); - - if (!fileinfo) { - break; - } - - let name = fileinfo.get_name(); - - // ignore hidden files - if (name[0] != '.') { - history.push(fileinfo.get_name()); - } - - } while(fileinfo); - - history.sort(); - history.reverse(); - - return history; - }, - - deleteOldPictures: function() { - this.historySize = this._settings.get('history-length', 'int'); - let deleteFile; - while(this.history.length > this.historySize) { - deleteFile = Gio.file_new_for_path(this.wallpaperlocation + this.history.pop()); - deleteFile.delete(null); + if(this._historyController.promoteToActive(historyElement.id)) { + this._setBackground(historyElement.path); + this.currentWallpaper = this._getCurrentWallpaper(); + } else { + this.logger.warn("The history id ("+historyElement.id+") could not be found.") + // TODO: Error handling history id not found. } }, - setWallpaper: function(historyEntry, keepName) { - if (!keepName) { - historyEntry = this._setNewFileName(historyEntry); - } - this._setBackground(this.wallpaperlocation + historyEntry); - this.currentWallpaper = this._getCurrentWallpaper(); - }, - fetchNewWallpaper: function(callback) { this._startLoadingHooks.forEach((element) => { element(); }); this._timer.begin(); // reset timer - let _this = this; - this._requestRandomImageFromAdapter((imageUrl) => { - this.logger.debug("Requesting image: "+imageUrl); + this._requestRandomImageFromAdapter((historyElement) => { + this.logger.info("Requesting image: "+historyElement.source.imageUrl); - _this._fetchFile(imageUrl, (historyid, path) => { - // insert file into history - _this.history.unshift(historyid); + this._fetchFile(historyElement.source.imageUrl, (historyId, path) => { + historyElement.path = path; + historyElement.id = historyId; + + this._setBackground(path, () => { + // insert file into history + this._historyController.insert(historyElement); - _this._setBackground(_this.wallpaperlocation + historyid, function(){ // call callback if given - _this._stopLoadingHooks.forEach((element) => { + this._stopLoadingHooks.forEach((element) => { element(null); }); if (callback) { @@ -270,16 +208,15 @@ let WallpaperController = new Lang.Class({ return; } - let _this = this; delay = delay || 200; - this.timeout = Mainloop.timeout_add(Mainloop.PRIORITY_DEFAULT, delay, function(){ - _this.timeout = null; - if (_this._resetWallpaper) { - _this._setBackground(_this.currentWallpaper); - _this._resetWallpaper = false; + this.timeout = Mainloop.timeout_add(Mainloop.PRIORITY_DEFAULT, delay, () => { + this.timeout = null; + if (this._resetWallpaper) { + this._setBackground(this.currentWallpaper); + this._resetWallpaper = false; } else { - _this._setBackground(_this.wallpaperlocation + _this.previewId); + this._setBackground(this.wallpaperlocation + this.previewId); } return false; }); @@ -297,41 +234,12 @@ let WallpaperController = new Lang.Class({ this._backgroundTimout(); }, - getHistory: function() { - return this.history; + getHistoryController: function() { + return this._historyController; }, deleteHistory: function() { - let firstHistoryElement = this.history[0]; - - if (firstHistoryElement) - this.history = [firstHistoryElement]; - - let directory = Gio.file_new_for_path(this.wallpaperlocation); - let enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); - - let fileinfo; - let deleteFile; - - do { - - fileinfo = enumerator.next_file(null); - - if (!fileinfo) { - break; - }; - - let name = fileinfo.get_name(); - - // ignore hidden files and first element - if (name[0] != '.' && name != firstHistoryElement) { - deleteFile = Gio.file_new_for_path(this.wallpaperlocation + name); - deleteFile.delete(null); - }; - - } while(fileinfo); - - return true; + this._historyController.clear(); }, menuShowHook: function() {