diff --git a/area.js b/area.js
index 85fa316..4bdb9a5 100644
--- a/area.js
+++ b/area.js
@@ -1129,14 +1129,7 @@ var DrawingArea = new Lang.Class({
}
content += "\n";
- 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);
}
});
diff --git a/extension.js b/extension.js
index 95c1dd7..ca34b88 100644
--- a/extension.js
+++ b/extension.js
@@ -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();
}
diff --git a/files.js b/files.js
index 15c2b91..a443c8b 100644
--- a/files.js
+++ b/files.js
@@ -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;
+ }
+};
+
diff --git a/menu.js b/menu.js
index bc10dfa..c460073 100644
--- a/menu.js
+++ b/menu.js
@@ -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(`${prefix}${this.area.jsonName}`);
+ this.drawingNameMenuItem.label.set_text(`${prefix}${this.area.currentJson.name}`);
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(`${String(json)}`, () => {
- this.area.loadJson(json.name);
+ this.area.loadJson(json);
this._updateDrawingNameMenuItem();
this._updateSaveDrawingSubMenuItemSensitivity();
});