Merge pull request #3 from ifl0w/rework

Rework
This commit is contained in:
ifl0w 2017-07-20 22:36:48 +02:00 committed by GitHub
commit 608f45c63b
21 changed files with 1380 additions and 512 deletions

3
.gitignore vendored
View file

@ -1,4 +1,7 @@
.idea
# ready to deploy to gnome extensions
random-wallpaper-gnome3.zip
# Temporary ui files
**/*~

View file

@ -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

13
build.sh Executable file
View file

@ -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 ..

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

83
icon.svg Normal file
View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="100"
height="100"
viewBox="0 0 100 100"
version="1.1"
id="svg4"
sodipodi:docname="icon.svg"
inkscape:version="0.92.1 r">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8">
<linearGradient
inkscape:collect="always"
id="linearGradient4545">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4541" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop4543" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4545"
id="linearGradient4547"
x1="0"
y1="12"
x2="24"
y2="12"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1145"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="41.499897"
inkscape:cy="40.854617"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<path
style="fill:#33334b;fill-opacity:1;stroke:none;stroke-width:13.66373348;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 5.515625,0 C 2.4604779,0 0,2.460478 0,5.515625 v 88.96875 C 0,97.539522 2.4604779,100 5.515625,100 h 88.970703 c 3.055148,0 5.513671,-2.460478 5.513671,-5.515625 V 5.515625 C 99.999999,2.460478 97.541476,0 94.486328,0 Z M 16.150391,14.357422 H 83.84961 c 0.981656,0.0014 1.791596,0.811308 1.792968,1.792969 v 53.591796 c -0.0014,0.981653 -0.805639,1.799405 -1.792968,1.800782 H 16.150391 c -0.987332,-0.0014 -1.791593,-0.819133 -1.792969,-1.800782 V 16.150391 c 0.0014,-0.981657 0.811312,-1.791594 1.792969,-1.792969 z m 7.857422,8.041016 c -0.988906,2.75e-4 -1.775116,0.786485 -1.775391,1.77539 v 37.542969 c 2.75e-4,0.988909 0.786484,1.777067 1.775391,1.777344 h 51.984375 c 0.988901,-2.77e-4 1.775114,-0.788439 1.77539,-1.777344 V 24.173828 c -2.76e-4,-0.988906 -0.786489,-1.775115 -1.77539,-1.77539 z m 3.439453,2.470703 h 45.107422 c 0.567933,-4.7e-5 1.12966,0.230279 1.53125,0.623047 0.401589,0.392767 0.636766,0.942589 0.636719,1.498046 v 31.863282 c 4.7e-5,0.555457 -0.23513,1.105279 -0.636719,1.498046 -0.40159,0.392768 -0.963317,0.621141 -1.53125,0.621094 H 27.447266 c -0.567934,4.7e-5 -1.131613,-0.228326 -1.533203,-0.621094 -0.40159,-0.392767 -0.636767,-0.942589 -0.636719,-1.498046 V 26.990234 c -4.8e-5,-0.555457 0.235129,-1.105279 0.636719,-1.498046 0.40159,-0.392768 0.965269,-0.623094 1.533203,-0.623047 z M 40.40625,73.59375 h 19.1875 c 0.521531,0.0013 1.013887,0.230804 1.351563,0.621094 l 7.335937,8.464843 c 0.483066,0.562457 0.543936,1.329127 0.275391,1.917969 -0.26855,0.588843 -0.885438,1.041378 -1.626953,1.044922 H 33.070313 c -0.741487,-0.0036 -1.358401,-0.456092 -1.626953,-1.044922 -0.268553,-0.58883 -0.207669,-1.35552 0.27539,-1.917969 l 7.335938,-8.464843 c 0.337681,-0.390282 0.830068,-0.619807 1.351562,-0.621094 z"
id="rect4614"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -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);
}
});
// -------------------------------------------------------------------------------

View file

@ -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;
}
});

View file

@ -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();

View file

@ -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);
}
},
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- License Agreement at http://iconmonstr.com/license/ -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path id="shuffle-icon" fill="#FFFFFF" d="M102.319,187.959H50v-40h52.319c38.263,0,75.373,15.736,102.147,44.485
c-1.163,1.716-22.359,35.536-22.359,35.536C163.371,202.782,134.117,187.959,102.319,187.959z M462,342.987l-88.489-55.225v36.195
h-28.664c-31.797,0-61.052-14.823-79.788-40.021c0,0-21.196,33.82-22.358,35.536c26.8,28.776,63.928,44.484,102.146,44.484h28.768
l0.086,33.082L462,342.987z M462,169.013l-88.3-54.053l-0.086,33.082h-28.768c-47.448,0-92.504,24.248-118.344,65.592l-39.76,63.616
c-18.312,29.299-49.872,46.791-84.424,46.791H50v40h52.319c47.448,0,92.504-24.248,118.344-65.592l39.76-63.616
c18.312-29.299,49.872-46.791,84.424-46.791h28.664l-0.095,36.195L462,169.013z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -22,7 +22,7 @@ let Logger = new Lang.Class({
this._log("INFO", message);
},
warning: function (message) {
warn: function (message) {
this._log("WARNING", message);
},

View file

@ -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"
}

View file

@ -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);
}
});

View file

@ -2,13 +2,13 @@
<schemalist gettext-domain='gnome-shell-extensions'>
<enum id='org.gnome.shell.extensions.space.iflow.randomwallpaper.sources'>
<value value='0' nick='desktoppr'/>
<value value='1' nick='unsplash'/>
<value value='2' nick='wallheaven'/>
<value value='0' nick='desktoppr'/>
<value value='1' nick='unsplash'/>
<value value='2' nick='wallheaven'/>
</enum>
<schema path="/org/gnome/shell/extensions/space-iflow-randomwallpaper-sources/"
id='org.gnome.shell.extensions.space.iflow.randomwallpaper'>
id='org.gnome.shell.extensions.space.iflow.randomwallpaper'>
<key type='i' name='history-length'>
<default>10</default>
@ -38,9 +38,9 @@
</key>
<key name='source' enum='org.gnome.shell.extensions.space.iflow.randomwallpaper.sources'>
<default>'desktoppr'</default>
<summary>Wallpaper Source</summary>
<description>Describs the adapter that will be used.</description>
<default>'desktoppr'</default>
<summary>Wallpaper Source</summary>
<description>Describs the adapter that will be used.</description>
</key>
<key type='as' name='history'>
@ -56,4 +56,77 @@
</key>
</schema>
<schema path="/org/gnome/shell/extensions/space-iflow-randomwallpaper-sources/"
id='org.gnome.shell.extensions.space.iflow.randomwallpaper.desktopper'>
<key type='b' name='allow-unsafe'>
<default>false</default>
<summary>Allow Unsafe</summary>
<description>Weather the extension should fetch images that are rated as unsafe.</description>
</key>
</schema>
<schema path="/org/gnome/shell/extensions/space-iflow-randomwallpaper-sources/"
id='org.gnome.shell.extensions.space.iflow.randomwallpaper.unsplash'>
<key type='s' name='unsplash-keyword'>
<default>""</default>
<summary>Keyword</summary>
<description>The keyword will be used to search images.</description>
</key>
<key type='s' name='username'>
<default>""</default>
<summary>Username</summary>
<description>Only fetch random images of a given user.</description>
</key>
<key type='i' name='image-width'>
<default>1920</default>
<summary>Image Width</summary>
<description>The width of the image.</description>
</key>
<key type='i' name='image-height'>
<default>1080</default>
<summary>Image Width</summary>
<description>The height of the image.</description>
</key>
</schema>
<schema path="/org/gnome/shell/extensions/space-iflow-randomwallpaper-sources/"
id='org.gnome.shell.extensions.space.iflow.randomwallpaper.wallheaven'>
<key type='s' name='wallheaven-keyword'>
<default>""</default>
<summary>Keyword</summary>
<description>The keyword will be used to search images.</description>
</key>
<key type='s' name='resolutions'>
<default>"1920x1200, 1920x1080, 2560x1440, 2560x1600, 3840x1080"</default>
<summary>Resolutions</summary>
<description>The acceptable resolutions.</description>
</key>
<key type='b' name='allow-sfw'>
<default>true</default>
<summary>SFW</summary>
<description>Weather safe images are allowed.</description>
</key>
<key type='b' name='allow-sketchy'>
<default>false</default>
<summary>Sketchy</summary>
<description>Weather sketchy images are allowed.</description>
</key>
<key type='b' name='category-general'>
<default>true</default>
<summary>Category General</summary>
<description>Weather the general category should be searched.</description>
</key>
<key type='b' name='category-anime'>
<default>true</default>
<summary>Category Anime</summary>
<description>Weather the anime category should be searched.</description>
</key>
<key type='b' name='category-people'>
<default>true</default>
<summary>Category People</summary>
<description>Weather the people category should be searched.</description>
</key>
</schema>
</schemalist>

View file

@ -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;
}
});

View file

@ -2,6 +2,29 @@
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkBox" id="desktopper-settings">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton" id="desktopper-allow-unsafe">
<property name="label" translatable="yes">Allow unsafe images (also fetch images that are marked as unsafe)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<object class="GtkAdjustment" id="duration-hours">
<property name="upper">23</property>
<property name="value">1</property>
@ -115,7 +138,7 @@
<property name="active_id">0</property>
<items>
<item id="desktoppr">desktoppr.co</item>
<item id="unsplash">unsplash.com (not implemented, defaults to desktopper)</item>
<item id="unsplash">unsplash.com (experimental)</item>
<item id="wallheaven">alpha.wallheaven.cc (experimental)</item>
</items>
</object>
@ -131,34 +154,22 @@
</packing>
</child>
<child>
<object class="GtkFrame">
<object class="GtkFrame" id="source-settings-frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkAlignment">
<placeholder/>
</child>
<child type="label">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">12</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="label" translatable="yes">No Settings Available</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<property name="label" translatable="yes">Source Settings</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
@ -466,4 +477,362 @@
</packing>
</child>
</object>
<object class="GtkLabel" id="no-settings">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="label" translatable="yes">No Settings Available</property>
<style>
<class name="dim-label"/>
</style>
</object>
<object class="GtkAdjustment" id="unsplash-image-height">
<property name="lower">1</property>
<property name="upper">1000000</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="unsplash-image-width">
<property name="lower">1</property>
<property name="upper">1000000</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkBox" id="unsplash-settings">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Username</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="unsplash-username">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_bottom">10</property>
<property name="placeholder_text" translatable="yes">@username</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Keyword</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="unsplash-keyword">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_bottom">10</property>
<property name="placeholder_text" translatable="yes">Enter a keyword ...</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">5</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Image Width</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="input_purpose">number</property>
<property name="adjustment">unsplash-image-width</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Image Height</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="input_purpose">number</property>
<property name="adjustment">unsplash-image-height</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<object class="GtkBox" id="wallheaven-settings">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Keyword</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="wallheaven-keyword">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_bottom">10</property>
<property name="placeholder_text" translatable="yes">Enter a keyword ...</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Allowed Content Ratings</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">10</property>
<property name="layout_style">start</property>
<child>
<object class="GtkCheckButton" id="wallheaven-allow-sfw">
<property name="label" translatable="yes">SFW (Safe for work)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="wallheaven-allow-sketchy">
<property name="label" translatable="yes">Sketchy</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Categories</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">10</property>
<property name="layout_style">start</property>
<child>
<object class="GtkCheckButton" id="wallheaven-category-general">
<property name="label" translatable="yes">General</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="wallheaven-category-anime">
<property name="label" translatable="yes">Anime</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="wallheaven-category-people">
<property name="label" translatable="yes">People</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Resolutions</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="wallheaven-resolutions">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="placeholder_text" translatable="yes">1920x1080, 1920x1200</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
</packing>
</child>
</object>
</interface>

View file

@ -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('');
}
});

View file

@ -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;
}

View file

@ -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() {