rework of Files.Jsons

In the same way as Files.Images.
This commit is contained in:
abakkk 2020-09-13 23:50:09 +02:00
parent e53e69efcf
commit 60091a94b6
4 changed files with 190 additions and 106 deletions

83
area.js
View File

@ -1129,14 +1129,7 @@ var DrawingArea = new Lang.Class({
}
content += "\n</svg>";
let filename = `${Me.metadata['svg-file-name']} ${Files.getDateString()}.svg`;
let dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES);
let path = GLib.build_filenamev([dir, filename]);
if (GLib.file_test(path, GLib.FileTest.EXISTS))
return false;
let success = GLib.file_set_contents(path, content);
if (success) {
if (Files.saveSvg(content)) {
// pass the parent (bgContainer) to Flashspot because coords of this are relative
let flashspot = new Screenshot.Flashspot(this.get_parent());
flashspot.fire();
@ -1149,7 +1142,7 @@ var DrawingArea = new Lang.Class({
}
},
_saveAsJson: function(name, notify, callback) {
_saveAsJson: function(json, notify, callback) {
// stop drawing or writing
if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) {
this._stopWriting();
@ -1157,46 +1150,31 @@ var DrawingArea = new Lang.Class({
this._stopDrawing();
}
let json = new Files.Json({ name });
let oldContents;
if (name == Me.metadata['persistent-file-name']) {
let oldContents = json.contents;
// do not create a file to write just an empty array
if (!oldContents && this.elements.length == 0)
return;
}
// do not use "content = JSON.stringify(this.elements, null, 2);", neither "content = JSON.stringify(this.elements);"
// do compromise between disk usage and human readability
let contents = `[\n ` + new Array(...this.elements.map(element => JSON.stringify(element))).join(`,\n\n `) + `\n]`;
if (name == Me.metadata['persistent-file-name'] && contents == oldContents)
return;
let contents = this.elements.length ? `[\n ` + new Array(...this.elements.map(element => JSON.stringify(element))).join(`,\n\n `) + `\n]` : '[]';
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
json.contents = contents;
if (notify)
this.emit('show-osd', Files.Icons.SAVE, name, "", -1, false);
if (name != Me.metadata['persistent-file-name']) {
this.jsonName = name;
this.lastJsonContents = contents;
}
this.emit('show-osd', Files.Icons.SAVE, json.name, "", -1, false);
if (!json.isPersistent)
this.currentJson = json;
if (callback)
callback();
});
},
saveAsJsonWithName: function(name, callback) {
this._saveAsJson(name, false, callback);
this._saveAsJson(Files.Jsons.getNamed(name), false, callback);
},
saveAsJson: function() {
this._saveAsJson(Files.getDateString(), true);
this._saveAsJson(Files.Jsons.getDated(), true);
},
savePersistent: function() {
this._saveAsJson(Me.metadata['persistent-file-name']);
this._saveAsJson(Files.Jsons.getPersistent());
},
syncPersistent: function() {
@ -1208,7 +1186,7 @@ var DrawingArea = new Lang.Class({
},
_loadJson: function(name, notify) {
_loadJson: function(json, notify) {
// stop drawing or writing
if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) {
this._stopWriting();
@ -1218,56 +1196,45 @@ var DrawingArea = new Lang.Class({
this.elements = [];
this.currentElement = null;
let contents = (new Files.Json({ name })).contents;
if (!contents)
if (!json.contents)
return;
this.elements.push(...JSON.parse(contents).map(object => {
this.elements.push(...JSON.parse(json.contents).map(object => {
if (object.image)
object.image = new Files.Image(object.image);
return new Elements.DrawingElement(object);
}));
if (notify)
this.emit('show-osd', Files.Icons.OPEN, name, "", -1, false);
if (name != Me.metadata['persistent-file-name']) {
this.jsonName = name;
this.lastJsonContents = contents;
}
this.emit('show-osd', Files.Icons.OPEN, json.name, "", -1, false);
if (!json.isPersistent)
this.currentJson = json;
},
_loadPersistent: function() {
this._loadJson(Me.metadata['persistent-file-name']);
this._loadJson(Files.Jsons.getPersistent());
},
loadJson: function(name, notify) {
this._loadJson(name, notify);
loadJson: function(json, notify) {
this._loadJson(json, notify);
this._redisplay();
},
loadPreviousJson: function() {
let names = Files.getJsons().map(json => json.name);
if (!names.length)
return;
let previousName = names[this.jsonName && names.indexOf(this.jsonName) != names.length - 1 ? names.indexOf(this.jsonName) + 1 : 0];
this.loadJson(previousName, true);
let json = Files.Jsons.getPrevious(this.currentJson || null);
if (json)
this.loadJson(json, true);
},
loadNextJson: function() {
let names = Files.getJsons().map(json => json.name);
if (!names.length)
return;
let nextName = names[this.jsonName && names.indexOf(this.jsonName) > 0 ? names.indexOf(this.jsonName) - 1 : names.length - 1];
this.loadJson(nextName, true);
let json = Files.Jsons.getNext(this.currentJson || null);
if (json)
this.loadJson(json, true);
},
get drawingContentsHasChanged() {
let contents = `[\n ` + new Array(...this.elements.map(element => JSON.stringify(element))).join(`,\n\n `) + `\n]`;
return contents != this.lastJsonContents;
return contents != (this.currentJson && this.currentJson.contents);
}
});

View File

@ -524,6 +524,8 @@ const AreaManager = new Lang.Class({
Main.wm.removeKeybinding('toggle-modal');
Main.wm.removeKeybinding('erase-drawings');
this.removeAreas();
Files.Images.disable();
Files.Jsons.disable();
if (this.indicator)
this.indicator.disable();
}

199
files.js
View File

@ -1,5 +1,5 @@
/* jslint esversion: 6 */
/* exported Icons, Image, Images, Json, getJsons, getDateString */
/* exported Icons, Image, Images, Json, Jsons, getDateString, saveSvg */
/*
* Copyright 2019 Abakkk
@ -205,6 +205,12 @@ var Images = {
_clipboardImages: [],
_upToDate: false,
disable: function() {
this._images = [];
this._clipboardImages = [];
this._upToDate = false;
},
_clipboardImagesContains: function(file) {
return this._clipboardImages.some(image => image.file.equal(file));
},
@ -265,7 +271,7 @@ var Images = {
let file = this.enumerator.get_child(info);
if (info.get_content_type().indexOf('image') == 0 && !clipboardImagesContains(file)) {
let image = oldImages.find(image => image.file.equal(file)) || new ImageWithGicon({ file, info });
let image = oldImages.find(oldImage => oldImage.file.equal(file)) || new ImageWithGicon({ file, info });
newImages.push(image);
return { value: image, done: false };
} else {
@ -287,7 +293,7 @@ var Images = {
getPrevious: function(currentImage) {
let images = this.getSorted();
let index = currentImage ? images.findIndex(image => image.file.equal(currentImage.file)) : 0;
let index = currentImage ? images.findIndex(image => image.file.equal(currentImage.file)) : -1;
return images[index <= 0 ? images.length - 1 : index - 1] || null;
},
@ -333,6 +339,10 @@ var Json = new Lang.Class({
this[key] = params[key];
},
get isPersistent() {
return this.name == Me.metadata['persistent-file-name'];
},
toString: function() {
return this.displayName || this.name;
},
@ -342,10 +352,10 @@ var Json = new Lang.Class({
},
get file() {
if (!this._file && this.name)
if (!this._file)
this._file = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir'], `${this.name}.json`]));
return this._file || null;
return this._file;
},
set file(file) {
@ -353,62 +363,167 @@ var Json = new Lang.Class({
},
get contents() {
let success_, contents;
try {
[success_, contents] = this.file.load_contents(null);
if (contents instanceof Uint8Array)
contents = ByteArray.toString(contents);
} catch(e) {
return null;
if (this._contents === undefined) {
try {
[, this._contents] = this.file.load_contents(null);
if (this._contents instanceof Uint8Array)
this._contents = ByteArray.toString(this._contents);
} catch(e) {
this._contents = null;
}
}
return contents;
return this._contents;
},
set contents(contents) {
if (this.isPersistent && (this.contents == contents || !this.contents && contents == '[]'))
return;
try {
this.file.replace_contents(contents, null, false, Gio.FileCreateFlags.NONE, null);
} catch(e) {
this.file.get_parent().make_directory_with_parents(null);
this.file.replace_contents(contents, null, false, Gio.FileCreateFlags.NONE, null);
}
this._contents = contents;
}
});
var getJsons = function() {
let directory = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir']]));
var Jsons = {
_jsons: [],
_upToDate: false,
let enumerator;
try {
enumerator = directory.enumerate_children('standard::name,standard::display-name,standard::content-type,time::modified', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
return [];
}
let jsons = [];
let fileInfo = enumerator.next_file(null);
while (fileInfo) {
if (fileInfo.get_content_type().indexOf('json') != -1 && fileInfo.get_name() != `${Me.metadata['persistent-file-name']}.json`) {
let file = enumerator.get_child(fileInfo);
jsons.push(new Json({
file,
name: fileInfo.get_name().slice(0, -5),
displayName: fileInfo.get_display_name().slice(0, -5),
// fileInfo.get_modification_date_time: Gio 2.62+
modificationUnixTime: fileInfo.get_attribute_uint64('time::modified')
}));
disable: function() {
if (this._monitor) {
this._monitor.disconnect(this._monitorHandler);
this._monitor.cancel();
}
fileInfo = enumerator.next_file(null);
delete this._monitor;
delete this._persistent;
this._jsons = [];
this._upToDate = false;
},
_updateMonitor: function() {
if (this._monitor)
return;
let directory = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir']]));
this._monitor = directory.monitor(Gio.FileMonitorFlags.NONE, null);
this._monitorHandler = this._monitor.connect('changed', (monitor, file) => {
if (file.get_basename() != `${Me.metadata['persistent-file-name']}.json` && file.get_basename().indexOf('.goutputstream'))
this.reset();
});
},
[Symbol.iterator]: function() {
if (this._upToDate)
return this._jsons[Symbol.iterator]();
this._updateMonitor();
this._upToDate = true;
let newJsons = this._jsons = [];
return {
get enumerator() {
if (this._enumerator === undefined) {
try {
let directory = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir']]));
this._enumerator = directory.enumerate_children('standard::name,standard::display-name,standard::content-type,time::modified', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
this._enumerator = null;
}
}
return this._enumerator;
},
next: function() {
if (!this.enumerator || this.enumerator.is_closed())
return { done: true };
let info = this.enumerator.next_file(null);
if (!info) {
this.enumerator.close(null);
return this.next();
}
let file = this.enumerator.get_child(info);
if (info.get_content_type().indexOf('json') != -1 && info.get_name() != `${Me.metadata['persistent-file-name']}.json`) {
let json = new Json({
file, name: info.get_name().slice(0, -5),
displayName: info.get_display_name().slice(0, -5),
// info.get_modification_date_time: Gio 2.62+
modificationUnixTime: info.get_attribute_uint64('time::modified')
});
newJsons.push(json);
return { value: json, done: false };
} else {
return this.next();
}
}
};
},
getSorted: function() {
return [...this].sort((a, b) => b.modificationUnixTime - a.modificationUnixTime);
},
getNext: function(currentJson) {
let jsons = this.getSorted();
let index = currentJson ? jsons.findIndex(json => json.name == currentJson.name) : -1;
return jsons[index == jsons.length - 1 ? 0 : index + 1] || null;
},
getPrevious: function(currentJson) {
let jsons = this.getSorted();
let index = currentJson ? jsons.findIndex(json => json.name == currentJson.name) : -1;
return jsons[index <= 0 ? jsons.length - 1 : index - 1] || null;
},
getPersistent: function() {
if (!this._persistent)
this._persistent = new Json({ name: Me.metadata['persistent-file-name'] });
return this._persistent;
},
getDated: function() {
return new Json({ name: getDateString() });
},
getNamed: function(name) {
return [...this].find(json => json.name == name) || new Json({ name });
},
reset: function() {
this._upToDate = false;
}
enumerator.close(null);
jsons.sort((a, b) => {
return b.modificationUnixTime - a.modificationUnixTime;
});
return jsons;
};
var getDateString = function() {
let date = GLib.DateTime.new_now_local();
return `${date.format("%F")} ${date.format("%X")}`;
};
var saveSvg = function(content) {
let filename = `${Me.metadata['svg-file-name']} ${getDateString()}.svg`;
let dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES);
let path = GLib.build_filenamev([dir, filename]);
let file = Gio.File.new_for_path(path);
if (file.query_exists(null))
return false;
try {
return file.replace_contents(content, null, false, Gio.FileCreateFlags.NONE, null);
} catch(e) {
return false;
}
};

12
menu.js
View File

@ -562,10 +562,10 @@ var DrawingMenu = new Lang.Class({
},
_updateDrawingNameMenuItem: function() {
getActor(this.drawingNameMenuItem).visible = this.area.jsonName ? true : false;
if (this.area.jsonName) {
getActor(this.drawingNameMenuItem).visible = this.area.currentJson ? true : false;
if (this.area.currentJson) {
let prefix = this.area.drawingContentsHasChanged ? "* " : "";
this.drawingNameMenuItem.label.set_text(`<i>${prefix}${this.area.jsonName}</i>`);
this.drawingNameMenuItem.label.set_text(`<i>${prefix}${this.area.currentJson.name}</i>`);
this.drawingNameMenuItem.label.get_clutter_text().set_use_markup(true);
}
},
@ -574,7 +574,7 @@ var DrawingMenu = new Lang.Class({
let item = new PopupMenu.PopupSubMenuMenuItem(_("Open drawing"), true);
this.openDrawingSubMenuItem = item;
this.openDrawingSubMenu = item.menu;
item.setSensitive(Boolean(Files.getJsons().length));
item.setSensitive(Boolean(Files.Jsons.getSorted().length));
item.icon.set_gicon(icon);
item.menu.itemActivated = item.menu.close;
@ -591,10 +591,10 @@ var DrawingMenu = new Lang.Class({
_populateOpenDrawingSubMenu: function() {
this.openDrawingSubMenu.removeAll();
let jsons = Files.getJsons();
let jsons = Files.Jsons.getSorted();
jsons.forEach(json => {
let subItem = this.openDrawingSubMenu.addAction(`<i>${String(json)}</i>`, () => {
this.area.loadJson(json.name);
this.area.loadJson(json);
this._updateDrawingNameMenuItem();
this._updateSaveDrawingSubMenuItemSensitivity();
});