From 77d3f0898122b60c391a1814672ecd17f3640575 Mon Sep 17 00:00:00 2001 From: Wolfgang R Date: Tue, 21 Oct 2014 23:13:21 +0200 Subject: [PATCH] implemented basic history - preview on hover - load from history by click - history size is 10 --- randomwallpaper@iflow.productions/Elements.js | 163 ++++++++++-------- .../extension.js | 134 ++++++++------ .../stylesheet.css | 20 ++- .../wallpaperController.js | 137 ++++++++++++--- 4 files changed, 301 insertions(+), 153 deletions(-) diff --git a/randomwallpaper@iflow.productions/Elements.js b/randomwallpaper@iflow.productions/Elements.js index 5498242..8f2252e 100644 --- a/randomwallpaper@iflow.productions/Elements.js +++ b/randomwallpaper@iflow.productions/Elements.js @@ -3,12 +3,33 @@ const PopupMenu = imports.ui.popupMenu; const St = imports.gi.St; const Slider = imports.ui.slider; -const Controls = new Lang.Class({ - Name: 'Controls', +const HistoryElement = new Lang.Class({ + Name: 'HistoryElement', Extends: PopupMenu.PopupBaseMenuItem, - _init: function() { - this.parent(); - + historyId: null, + + _init: function(historyId, params) { + 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._container = new St.BoxLayout({ + vertical: true + }); + + this.label = new St.Label({ text: dateString }); + this._container.add_child(this.label); + + this.label = new St.Label({ text: timeString }); + this._container.add_child(this.label); + + this.historyId = historyId; + + this.actor.add_child(this._container); } }); @@ -16,35 +37,35 @@ const Controls = new Lang.Class({ // borrowed from: https://github.com/eonpatapon/gnome-shell-extensions-mediaplayer const SliderItem = new Lang.Class({ - Name: 'SliderItem', - Extends: PopupMenu.PopupBaseMenuItem, + Name: 'SliderItem', + Extends: PopupMenu.PopupBaseMenuItem, - _init: function(value) { - this.parent(); + _init: function(value) { + this.parent(); - this._box = new St.Table({style_class: 'slider-item'}); + this._box = new St.Table({style_class: 'slider-item'}); - this._slider = new Slider.Slider(value); + 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}); - }, + 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); - }, + setValue: function(value) { + this._slider.setValue(value); + }, - getValue: function() { - return this._slider._getCurrentValue(); - }, + getValue: function() { + return this._slider._getCurrentValue(); + }, - setIcon: function(icon) { - this._icon.icon_name = icon + '-symbolic'; - }, + setIcon: function(icon) { + this._icon.icon_name = icon + '-symbolic'; + }, - connect: function(signal, callback) { - this._slider.connect(signal, callback); - } + connect: function(signal, callback) { + this._slider.connect(signal, callback); + } }); @@ -53,58 +74,58 @@ const SliderItem = new Lang.Class({ * @type {Lang.Class} */ const DelaySlider = new Lang.Class({ - Name: 'DelaySlider', - Extends: SliderItem, + Name: 'DelaySlider', + Extends: SliderItem, - _MINUTES_MAX: 59, - _MINUTES_MIN: 5, - _HOURS_MAX: 48, - _HOURS_MIN: 1, + _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. - }, + /** + * 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); - } + /** + * 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; - } + 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); - }, + 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; - } + /** + * 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); - } + return (minutes < this._MINUTES_MIN) ? this._MINUTES_MIN : Math.floor(minutes); + } }); // ------------------------------------------------------------------------------- \ No newline at end of file diff --git a/randomwallpaper@iflow.productions/extension.js b/randomwallpaper@iflow.productions/extension.js index 345c593..080b2d8 100644 --- a/randomwallpaper@iflow.productions/extension.js +++ b/randomwallpaper@iflow.productions/extension.js @@ -18,85 +18,117 @@ let wallpaperController; let extensionMeta; function init(metaData) { - extensionMeta = metaData; - wallpaperController = new WallpaperController.WallpaperController(metaData); - global.log("INIT"); + extensionMeta = metaData; + wallpaperController = new WallpaperController.WallpaperController(metaData); + global.log("INIT"); } let panelEntry; let RandomWallpaperEntry = new Lang.Class({ - Extends: PanelMenu.Button, - Name: "RandomWallpaperEntry", + Extends: PanelMenu.Button, + Name: "RandomWallpaperEntry", - _history: [], + _init: function(menuAlignment, nameText) { + this.parent(menuAlignment, nameText); - _init: function(menuAlignment, nameText){ - this.parent(menuAlignment, nameText); + //let gicon = Gio.Icon.new_for_string(extensionMeta.path + "/images/shuffle-icon.svg"); - //let gicon = Gio.Icon.new_for_string(extensionMeta.path + "/images/shuffle-icon.svg"); + /*let icon = new St.Icon({ + gicon: gicon, + style_class: 'rwg_status_icon' + });*/ - /*let icon = new St.Icon({ - gicon: gicon, - style_class: 'rwg_status_icon' - });*/ + let icon = new St.Icon({ + icon_name: 'preferences-desktop-wallpaper-symbolic', + style_class: 'rwg_system_status_icon' + }); - let icon = new St.Icon({ - icon_name: 'preferences-desktop-wallpaper-symbolic', - style_class: 'rwg_system_status_icon' - }); + this.actor.add_child(icon); - this.actor.add_child(icon); + let menu_item = new PopupMenu.PopupMenuItem('New Wallpaper'); - let menu_item = new PopupMenu.PopupMenuItem('Change Background'); - let lable = new PopupMenu.PopupMenuItem('Change Background', { reactive: false, activate: false, hover: false, style_class: 'rwg-lable', can_focus: false }); + this.menu.addMenuItem(menu_item, 1); + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - this.menu.addMenuItem(lable, 0); - this.menu.addMenuItem(menu_item, 1); - this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this.historySection = new PopupMenu.PopupMenuSection(); + this.menu.addMenuItem(this.historySection); - this.historySection = new PopupMenu.PopupMenuSection(); - this.menu.addMenuItem(this.historySection); + this.setHistoryList(); - this.clearHistory(); + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + //this.menu.addMenuItem(new CustomElements.DelaySlider(60)); - this.menu.addMenuItem(new CustomElements.DelaySlider(60)); + // add eventlistener + let _this = this; + menu_item.actor.connect('button-press-event', function() { + wallpaperController.fetchNewWallpaper(function() { + _this.setHistoryList(); + }); + }); - // add eventlistener - menu_item.actor.connect('button-press-event', function() { - wallpaperController._requestRandomImage(); - }); - }, + // when the popupmenu disapears, check if the wallpaper is the original and + // reset it if needed + this.menu.actor.connect('hide', function() { + wallpaperController.setWallpaper(_this.history[0]); + }); + }, - setHistoryList: function() { - this.historySection.addMenuItem(new PopupMenu.PopupMenuItem('Test Item 0')); - this.historySection.addMenuItem(new PopupMenu.PopupMenuItem('Test Item 1')); - this.historySection.addMenuItem(new PopupMenu.PopupMenuItem('Test Item 2')); + setHistoryList: function() { + this.historySection.removeAll(); - }, + let history = this.history = wallpaperController.getHistory(); - clearHistory: function() { - this.historySection.removeAll(); - - let empty = new PopupMenu.PopupMenuItem('No recent wallpaper ...', { reactive: false, activate: false, hover: false, style_class: 'rwg-lable', can_focus: false }); - this.historySection.addMenuItem(empty); - }, + if (!history.length) { + this.clearHistory(); + }; + + for (var i = 0; i < history.length; i++) { + let historyid = history[i]; + let tmp = new CustomElements.HistoryElement(historyid); + + tmp.actor.connect('key-focus-in', function(actor) { + wallpaperController.previewWallpaper(historyid); + }); + + tmp.actor.connect('button-press-event', function(actor) { + wallpaperController.setWallpaper(historyid); + _this.setHistoryList(); + }); + + this.historySection.addMenuItem(tmp); + }; + + }, + + + clearHistory: function() { + this.historySection.removeAll(); + + let empty = new PopupMenu.PopupMenuItem('No recent wallpaper ...', { + reactive: false, + activate: false, + hover: false, + style_class: 'rwg-recent-lable', + can_focus: false + }); + this.historySection.addMenuItem(empty); + }, }); function enable() { - global.log("ENABLE"); + global.log("ENABLE"); - // UI - panelEntry = new RandomWallpaperEntry(0, "Random wallpaper"); + // UI + panelEntry = new RandomWallpaperEntry(0, "Random wallpaper"); - // add to panel - Main.panel.addToStatusArea("random-wallpaper-menu", panelEntry); + // add to panel + Main.panel.addToStatusArea("random-wallpaper-menu", panelEntry); } function disable() { - global.log("DISABLE"); - panelEntry.destroy(); + global.log("DISABLE"); + panelEntry.destroy(); } \ No newline at end of file diff --git a/randomwallpaper@iflow.productions/stylesheet.css b/randomwallpaper@iflow.productions/stylesheet.css index be56fd2..636bcec 100644 --- a/randomwallpaper@iflow.productions/stylesheet.css +++ b/randomwallpaper@iflow.productions/stylesheet.css @@ -1,11 +1,11 @@ .helloworld-label { - font-size: 36px; - font-weight: bold; - color: #ffffff; - background-color: rgba(10,10,10,0.7); - border-radius: 5px; - padding: .5em; + font-size: 36px; + font-weight: bold; + color: #ffffff; + background-color: rgba(10,10,10,0.7); + border-radius: 5px; + padding: .5em; } .rwg_system_status_icon { @@ -21,4 +21,12 @@ /*font-size: 110%;*/ text-align: left; border: 0; +} + +.rwg-recent-lable { + width: 100%; + /*font-weight: bold;*/ + font-size: 90%; + text-align: left; + border: 0; } \ No newline at end of file diff --git a/randomwallpaper@iflow.productions/wallpaperController.js b/randomwallpaper@iflow.productions/wallpaperController.js index 86b16b1..2c67008 100644 --- a/randomwallpaper@iflow.productions/wallpaperController.js +++ b/randomwallpaper@iflow.productions/wallpaperController.js @@ -1,4 +1,5 @@ const Lang = imports.lang; +const Mainloop = imports.gi.GLib; // network requests const Soup = imports.gi.Soup; @@ -12,16 +13,23 @@ let WallpaperController = new Lang.Class({ extensionMeta: null, wallpaperlocation: '', - historySize: 5, + currentWallpaper: '', + historySize: 10, + history: [], _init: function(extensionMeta){ this.extensionMeta = extensionMeta; this.wallpaperlocation = this.extensionMeta.path + '/wallpapers/'; + this.history = this._loadHistory(); + this.currentWallpaper = this._getCurrentWallpaper(); }, - // fetch a random image url from desktopper.cc - _requestRandomImage: function(){ + /* + fetch a random image url from desktopper.cc + and call callback function with the URL of the image + */ + _requestRandomImageDesktopper: function(callback){ let session = new Soup.SessionAsync(); let message = Soup.Message.new('GET', 'https://api.desktoppr.co/1/wallpapers/random') @@ -36,20 +44,23 @@ let WallpaperController = new Lang.Class({ let response = data.get_object_member('response'); let imageUrl = response.get_object_member('image').get_string_member('url'); - _this._writeToFile(imageUrl); + if (callback) { + callback(imageUrl); + }; }); }, - // copy file from uri to local direcotry - _writeToFile: function(uri){ + /* + copy file from uri to local wallpaper direcotry and calls + the given callback with the name and the full filepath of + the written file as parameter. + */ + _fetchFile: function(uri, callback){ 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('.')); - - global.log(uri); - global.log(this.wallpaperlocation + String(name)); let output_file = Gio.file_new_for_path(this.wallpaperlocation + String(name)); let output_stream = output_file.create(0, null); @@ -61,25 +72,52 @@ let WallpaperController = new Lang.Class({ input_file.load_contents_async(null, function(file, result){ let contents = file.load_contents_finish(result)[1]; output_stream.write(contents, null); - _this._setBackground(output_file.get_path()); - }); - /*let fstream = input_file.copy(output_file, Gio.FileCopyFlags.OVERWRITE, null, function(){ - }, function(){ - }); */ + // call callback with the name and the full filepath of the written file as parameter + if (callback) { + callback(name, output_file.get_path()); + }; + }); }, + /* + 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(){ + }); + + this.history[i] = name; + + this.history.sort(); + this.history.reverse(); + + return name; + } + }; + + return false; + }, _setBackground: function(path){ let background_setting = new Gio.Settings({schema: "org.gnome.desktop.background"}); - - global.log("Current Background-Image: "+ background_setting.get_string("picture-uri")); - + + /* + inspired from: + https://bitbucket.org/LukasKnuth/backslide/src/7e36a49fc5e1439fa9ed21e39b09b61eca8df41a/backslide@codeisland.org/settings.js?at=master + */ // Set: if (background_setting.is_writable("picture-uri")){ // Set a new Background-Image (should show up immediately): if (background_setting.set_string("picture-uri", "file://"+path) ){ - //background_setting.apply(); Gio.Settings.sync(); // Necessary: http://stackoverflow.com/questions/9985140 } else { global.log("FAAILLEEDD"); @@ -91,9 +129,15 @@ let WallpaperController = new Lang.Class({ this.deleteOldPictures(); }, - getHistory: function () { + _getCurrentWallpaper: function() { + let background_setting = new Gio.Settings({schema: "org.gnome.desktop.background"}); + return background_setting.get_string("picture-uri"); + }, + + _loadHistory: function () { let directory = Gio.file_new_for_path(this.wallpaperlocation); let enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); + let fileinfo; let history = []; @@ -101,7 +145,6 @@ let WallpaperController = new Lang.Class({ fileinfo = enumerator.next_file(null); if (!fileinfo) { - global.log("SHOUT!!!"); break; }; @@ -115,18 +158,62 @@ let WallpaperController = new Lang.Class({ } while(fileinfo); history.sort(); + history.reverse(); - global.log(history); return history; }, deleteOldPictures: function() { - let history = this.getHistory(); let deleteFile; - - while(history.length > this.historySize) { - deleteFile = Gio.file_new_for_path(this.wallpaperlocation + history.shift()); + while(this.history.length > this.historySize) { + deleteFile = Gio.file_new_for_path(this.wallpaperlocation + this.history.pop()); deleteFile.delete(null); } + }, + + setWallpaper: function(historyEntry) { + historyEntry = this._setNewFileName(historyEntry); + this._setBackground(this.wallpaperlocation + historyEntry); + }, + + fetchNewWallpaper: function(callback) { + let _this = this; + + this._requestRandomImageDesktopper(function(imageUrl){ + _this._fetchFile(imageUrl, function(historyid, path) { + // insert file into history + _this.history.unshift(historyid); + + _this._setBackground(_this.wallpaperlocation + historyid); + + // call callback if given + if (callback) { + callback(); + }; + }); + }); + }, + + previewWallpaper: function(historyid) { + this.previewId = historyid; + let _this = this; + + if (_this.timeout) { + return; + }; + + this.timeout = Mainloop.timeout_add(Mainloop.PRIORITY_DEFAULT, 250, function(){ + _this.timeout = null; + _this._setBackground(_this.wallpaperlocation + _this.previewId); + return false; + }); + }, + + resetWallpaper: function() { + }, + + getHistory: function() { + return this.history; } + }); \ No newline at end of file