diff --git a/release/antos-1.1.2.tar.gz b/release/antos-1.1.2.tar.gz index 7c3a3a2..f43352a 100644 Binary files a/release/antos-1.1.2.tar.gz and b/release/antos-1.1.2.tar.gz differ diff --git a/src/core/BaseApplication.ts b/src/core/BaseApplication.ts index 643c050..79de271 100644 --- a/src/core/BaseApplication.ts +++ b/src/core/BaseApplication.ts @@ -166,10 +166,10 @@ namespace OS { * * @protected * @param {Promise} promise the promise on a task to be performed - * @returns {Promise} + * @returns {Promise} * @memberof BaseApplication */ - protected load(promise: Promise): Promise { + protected load(promise: Promise): Promise { const q = this._api.mid(); return new Promise(async (resolve, reject) => { this._api.loading(q, this.name); diff --git a/src/core/BaseDialog.ts b/src/core/BaseDialog.ts index 26a9cb1..26b5c44 100644 --- a/src/core/BaseDialog.ts +++ b/src/core/BaseDialog.ts @@ -1001,7 +1001,7 @@ namespace OS { fileview.fetch = (path: string) => new Promise(function (resolve, reject) { if (!path) { - return resolve(); + return resolve(undefined); } return path .asFileHandle() diff --git a/src/core/core.ts b/src/core/core.ts index 408b364..a62c62e 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -1379,9 +1379,9 @@ namespace OS { * @export * @param {string} l VFS path to the library * @param {string} force force reload library - * @returns {Promise} a promise on the result data + * @returns {Promise} a promise on the result data */ - export function requires(l: string, force: boolean = false): Promise { + export function requires(l: string, force: boolean = false): Promise { return new Promise(function(resolve, reject) { if (!API.shared[l] || force) { const libfp = l.asFileHandle(); @@ -1428,9 +1428,9 @@ namespace OS { * * @export * @param {string[]} libs list of shared libraries - * @returns {Promise} + * @returns {Promise} */ - export function require(libs: string[]): Promise { + export function require(libs: string[]): Promise { return new Promise(function(resolve, reject) { if (!(libs.length > 0)) { return resolve(); diff --git a/src/core/gui.ts b/src/core/gui.ts index 595079d..d45ae9e 100644 --- a/src/core/gui.ts +++ b/src/core/gui.ts @@ -228,7 +228,6 @@ namespace OS { * dialog definition for the format of the input data * @returns {Promise} A promise on the callback data of the dialog, refer * to each dialog definition for the format of the callback data - * @returns {Promise} */ export function openDialog( d: string | BaseDialog, @@ -237,7 +236,7 @@ namespace OS { return new Promise(function (resolve, reject) { if (dialog) { dialog.show(); - return resolve(); + return resolve(undefined); } if (typeof d === "string") { if (!dialogs[d]) { @@ -536,9 +535,9 @@ namespace OS { * * @export * @param {string[]} srvs list of service class names - * @returns {Promise} + * @returns {Promise} */ - export function pushServices(srvs: string[]): Promise { + export function pushServices(srvs: string[]): Promise { return new Promise(function (resolve, reject) { if (!(srvs.length > 0)) { return resolve(); diff --git a/src/core/languages/en_GB.json.old b/src/core/languages/en_GB.json.old deleted file mode 100644 index 3f31d08..0000000 --- a/src/core/languages/en_GB.json.old +++ /dev/null @@ -1,217 +0,0 @@ -{ - "About":"About", - "About: {0}":"About: {0}", - "Add category":"Add category", - "Add repository":"Add repository", - "Address":"Address", - "Alive (ms)":"Alive (ms)", - "Application installed":"Application installed", - "Application {0} is not executable":"Application {0} is not executable", - "Application":"Application", - "Applications":"Applications", - "April":"April", - "August":"August", - "Authentication":"Authentication", - "Cancel":"Cancel", - "Cannot Edit category":"Cannot Edit category", - "Cannot add new category":"Cannot add new category", - "Cannot create {0}":"Cannot create {0}", - "Cannot delete all content of: {0} [{1}]":"Cannot delete all content of: {0} [{1}]", - "Cannot delete the category: {0} [{1}]":"Cannot delete the category: {0} [{1}]", - "Cannot delete the section: {0}":"Cannot delete the section: {0}", - "Cannot delete: {0}":"Cannot delete: {0}", - "Cannot down load the app {0}":"Cannot down load the app {0}", - "Cannot export file for embedding to text":"Cannot export file for embedding to text", - "Cannot fetch CV categories":"Cannot fetch CV categories", - "Cannot fetch the entry content":"Cannot fetch the entry content", - "Cannot fetch user data":"Cannot fetch user data", - "Cannot install {0}":"Cannot install {0}", - "Cannot load 3rd library at: {0}":"Cannot load 3rd library at: {0}", - "Cannot move section":"Cannot move section", - "Cannot read service script: {0}":"Cannot read service script: {0}", - "Cannot render the PDF file":"Cannot render the PDF file", - "Cannot save blog: {0}":"Cannot save blog: {0}", - "Cannot save section: {0}":"Cannot save section: {0}", - "Cannot save system setting":"Cannot save system setting", - "Cannot save user data":"Cannot save user data", - "Cannot share file: {0}":"Cannot share file: {0}", - "Cannot uninstall package: {0}":"Cannot uninstall package: {0}", - "Categories":"Categories", - "Clear all":"Clear all", - "Close tab":"Close tab", - "Close without saving ?":"Close without saving ?", - "Close":"Close", - "Copy not yet implemented":"Copy not yet implemented", - "Copy":"Copy", - "Created: {0}":"Created: {0}", - "Cut":"Cut", - "December":"December", - "Delete a post":"Delete a post", - "Delete category":"Delete category", - "Delete section":"Delete section", - "Delete":"Delete", - "Desktop":"Desktop", - "Dialog {0} not found":"Dialog {0} not found", - "Do you really want to delete this post ?":"Do you really want to delete this post ?", - "Do you really want to delete: {0}?":"Do you really want to delete: {0}?", - "Download":"Download", - "Edit category":"Edit category", - "Edit repository":"Edit repository", - "Edit":"Edit", - "Email":"Email", - "Error find app by mimes {0}":"Error find app by mimes {0}", - "Error reading package meta data: {0}":"Error reading package meta data: {0}", - "Error saving file {0}":"Error saving file {0}", - "Exit":"Exit", - "Fail to create directory: {0}":"Fail to create directory: {0}", - "Fail to create {0}: {1}":"Fail to create {0}: {1}", - "Fail to delete {0}: {1}":"Fail to delete {0}: {1}", - "Fail to delete: {0}":"Fail to delete: {0}", - "Fail to fetch packages list from: {0}":"Fail to fetch packages list from: {0}", - "Fail to get file meta data: {0}":"Fail to get file meta data: {0}", - "Fail to make request: {0}":"Fail to make request: {0}", - "Fail to move file: {0} -> {1}":"Fail to move file: {0} -> {1}", - "Fail to paste: {0}":"Fail to paste: {0}", - "Fail to publish file: {0}":"Fail to publish file: {0}", - "Fail to query data from database: {0}":"Fail to query data from database: {0}", - "Fail to read file: {0}":"Fail to read file: {0}", - "Fail to rename to {0}: {1}":"Fail to rename to {0}: {1}", - "Fail to scan directory: {0}":"Fail to scan directory: {0}", - "Fail to upload file to: {0}":"Fail to upload file to: {0}", - "Fail to upload to {0}: {1}":"Fail to upload to {0}: {1}", - "Fail to write to file: {0}":"Fail to write to file: {0}", - "Fail to {0} package":"Fail to {0} package", - "February":"February", - "File name":"File name", - "File not found {0}":"File not found {0}", - "File {0} copied":"File {0} copied", - "File {0} cut":"File {0} cut", - "File":"File", - "Folder name":"Folder name", - "Format : [name] url":"Format : [name] url", - "Found {0} sections":"Found {0} sections", - "Fri":"Fri", - "From":"From", - "Full name must be entered":"Full name must be entered", - "Full name":"Full name", - "Google Drive":"Google Drive", - "Hidden files":"Hidden files", - "Home":"Home", - "Icon view":"Icon view", - "Ignore all {0} unsaved files ?":"Ignore all {0} unsaved files ?", - "Install":"Install", - "Invalid package: Meta data file not found":"Invalid package: Meta data file not found", - "January":"January", - "July":"July", - "June":"June", - "Kill process":"Kill process", - "Language file {0} not found":"Language file {0} not found", - "Launch":"Launch", - "List view":"List view", - "Location":"Location", - "Log out":"Log out", - "Logout":"Logout", - "March":"March", - "May":"May", - "Mime type {0} is not supported":"Mime type {0} is not supported", - "Modify section entry":"Modify section entry", - "Mon":"Mon", - "Move to":"Move to", - "Name":"Name", - "Navigation bar":"Navigation bar", - "New file":"New file", - "New folder":"New folder", - "New section entry for {0}":"New section entry for {0}", - "New":"New", - "No application available to open {0}":"No application available to open {0}", - "No post found: {0}":"No post found: {0}", - "No":"No", - "November":"November", - "OS":"OS", - "October":"October", - "Ok":"Ok", - "Only {0} could be selected":"Only {0} could be selected", - "Open file":"Open file", - "Open with":"Open with", - "Open":"Open", - "Options":"Options", - "Package uninstalled":"Package uninstalled", - "Parent can not be the category itself":"Parent can not be the category itself", - "Paste":"Paste", - "Phone":"Phone", - "Pid":"Pid", - "Please enter category name":"Please enter category name", - "Please enter tags":"Please enter tags", - "Please insert a title in the text: beginning with heading":"Please insert a title in the text: beginning with heading", - "Please select a category":"Please select a category", - "Please select a date":"Please select a date", - "Please select a file":"Please select a file", - "Please select a parent category":"Please select a parent category", - "Please select a section to edit":"Please select a section to edit", - "Please select a section to move":"Please select a section to move", - "Preview":"Preview", - "Properties":"Properties", - "Quit without saving ?":"Quit without saving ?", - "Quit":"Quit", - "Read more":"Read more", - "Refresh":"Refresh", - "Rename":"Rename", - "Repositories":"Repositories", - "Resource not found {0}":"Resource not found {0}", - "Resource not found: {0}":"Resource not found: {0}", - "Row {0}, col {1}, lines: {2}":"Row {0}, col {1}, lines: {2}", - "Sat":"Sat", - "Save as":"Save as", - "Save":"Save", - "Section list is empty, please add one":"Section list is empty, please add one", - "Select image file":"Select image file", - "Selected: {0} ({1} bytes)":"Selected: {0} ({1} bytes)", - "September":"September", - "Service":"Service", - "Share file":"Share file", - "Shared url: {0}":"Shared url: {0}", - "Shared":"Shared", - "Short biblio":"Short biblio", - "Sidebar":"Sidebar", - "Size":"Size", - "Subtitle":"Subtitle", - "Sun":"Sun", - "System fail: Cannot init desktop manager":"System fail: Cannot init desktop manager", - "System fail: Cannot init login screen":"System fail: Cannot init login screen", - "Tags":"Tags", - "This feature is not implemented yet":"This feature is not implemented yet", - "Thu":"Thu", - "Title or content must not be blank":"Title or content must not be blank", - "Title":"Title", - "Toggle Full screen":"Toggle Full screen", - "Tree view":"Tree view", - "Tue":"Tue", - "Type":"Type", - "Uninstall : {0}?":"Uninstall : {0}?", - "Uninstall":"Uninstall", - "Unknown API setting for {0}":"Unknown API setting for {0}", - "Updated: {0}":"Updated: {0}", - "Upload":"Upload", - "Url":"Url", - "User abort the authentication":"User abort the authentication", - "User data updated":"User data updated", - "VDB Unknown condition for delete command":"VDB Unknown condition for delete command", - "VFS Cannot encode file: {0}":"VFS Cannot encode file: {0}", - "VFS cannot create : {0}":"VFS cannot create : {0}", - "VFS cannot delete : {0}":"VFS cannot delete : {0}", - "VFS cannot download file : {0}":"VFS cannot download file : {0}", - "VFS cannot get meta data for {0}":"VFS cannot get meta data for {0}", - "VFS cannot init {0}: {1}":"VFS cannot init {0}: {1}", - "VFS cannot move : {0}":"VFS cannot move : {0}", - "VFS cannot read : {0}":"VFS cannot read : {0}", - "VFS cannot save : {0}":"VFS cannot save : {0}", - "VFS cannot write : {0}":"VFS cannot write : {0}", - "VFS unknown action: {0}":"VFS unknown action: {0}", - "VFS unknown handler: {0}":"VFS unknown handler: {0}", - "View":"View", - "Wed":"Wed", - "Would you like to login to {0}?":"Would you like to login to {0}?", - "Wrong format: it should be [name] url":"Wrong format: it should be [name] url", - "Yes":"Yes", - "{0} is not a directory":"{0} is not a directory" -} diff --git a/src/core/languages/gen.sh b/src/core/languages/gen.sh index e0a391a..2a0948f 100755 --- a/src/core/languages/gen.sh +++ b/src/core/languages/gen.sh @@ -20,7 +20,7 @@ ord() { LC_CTYPE=C printf '%d' "'$1" } -grep --include=\*.coffee -roh "$1" -e '__("[^"]*"' | while read -r line ; do +grep --include=\*.ts -roh "$1" -e '__("[^"]*"' | while read -r line ; do SUBSTRING=$(echo $line| cut -d'"' -f 2) if test -f "$2" && [ ! -z "$(grep -F "\"$SUBSTRING\":" "$2")" ] then @@ -29,7 +29,7 @@ grep --include=\*.coffee -roh "$1" -e '__("[^"]*"' | while read -r line ; do echo -e "\t\"$SUBSTRING\":\"$SUBSTRING\"," >> "tmp.json" fi done -grep --include=\*.{coffee,html} -roh "$1" -e '\"__([^\"]*)\"' | while read -r line; do +grep --include=\*.{ts,html} -roh "$1" -e '\"__([^\"]*)\"' | while read -r line; do len=$(( ${#line} - 6 )) #echo $len #echo $line diff --git a/src/core/vfs.ts b/src/core/vfs.ts index deae749..c7da6f4 100644 --- a/src/core/vfs.ts +++ b/src/core/vfs.ts @@ -1244,10 +1244,10 @@ namespace OS { * only work with file * * @protected - * @returns {Promise} + * @returns {Promise} * @memberof RemoteFileHandle */ - protected _down(): Promise { + protected _down(): Promise { return new Promise((resolve, reject) => { if (this.info.type === "dir") { return API.throwe( @@ -1527,10 +1527,10 @@ namespace OS { * Download the buffer file * * @protected - * @returns {Promise} + * @returns {Promise} * @memberof BufferFileHandle */ - protected _down(): Promise { + protected _down(): Promise { return new Promise((resolve, reject) => { const blob = new Blob([this.cache], { type: "octet/stream", @@ -1714,10 +1714,10 @@ namespace OS { * Download shared file * * @protected - * @returns {Promise} + * @returns {Promise} * @memberof SharedFileHandle */ - protected _down(): Promise { + protected _down(): Promise { return new Promise((resolve, reject) => { if (this.info.type === "dir") { return reject( diff --git a/src/packages/CodePad/ACEModel.ts b/src/packages/CodePad/ACEModel.ts new file mode 100644 index 0000000..a7ecfe5 --- /dev/null +++ b/src/packages/CodePad/ACEModel.ts @@ -0,0 +1,139 @@ +var ace: any; +namespace OS { + export namespace application { + + + export class CodePadACEModel extends CodePadBaseEditorModel { + private modes: GenericObject; + constructor(app: CodePad, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) { + ace.config.set("basePath", "scripts/ace"); + ace.require("ace/ext/language_tools"); + super(app,tabbar,editorarea); + this.modes = ace.require("ace/ext/modelist"); + } + + + getModes(): GenericObject[] { + const list = []; + let v: GenericObject; + for (v of Array.from(this.modes.modes)) { + list.push({ text: v.caption, mode: v.mode }); + } + return list; + } + setTheme(theme: string): void { + this.editor.setTheme(theme); + } + protected setUndoManager(um: GenericObject): void { + this.editor.getSession().setUndoManager(um); + } + protected setCursor(c: GenericObject): void { + this.editor.renderer.scrollCursorIntoView( + { + row: c.row, + column: c.column, + }, + 0.5 + ); + this.editor.selection.moveTo( + c.row, + c.column + ); + } + setMode(m: GenericObject): void { + this.currfile.langmode = m; + this.editor.getSession().setMode(m.mode); + } + protected getCursor(): GenericObject { + return this.editor.getCursorPosition(); + } + protected newUndoManager(): GenericObject { + return new ace.UndoManager(); + } + + /** + * Reference to the editor instance + * + * @protected + * @type {GenericObject} + * @memberof CodePad + */ + protected editor: GenericObject; + + protected editorSetup(el: HTMLElement): void { + this.editor = ace.edit(el); + this.editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + highlightActiveLine: true, + highlightSelectedWord: true, + behavioursEnabled: true, + wrap: true, + fontSize: "10pt", + showInvisibles: true, + }); + this.editor.setTheme("ace/theme/monokai"); + this.editor.completers.push({ + getCompletions( + editor: any, + session: any, + pos: any, + prefix: any, + callback: any + ) { }, + }); + this.editor.getSession().setUseWrapMode(true); + + } + + on(evt_str: string, callback: () => void): void { + switch (evt_str) { + case "input": + case "focus": + this.editor.on(evt_str, callback); + break; + case "changeCursor": + this.editor + .getSession() + .selection.on(evt_str, callback); + break; + default: + break; + } + } + resize(): void { + this.editor.resize(); + } + focus(): void { + this.editor.focus(); + } + protected getModeForPath(path: string): GenericObject { + const m = this.modes.getModeForPath(path); + return { + text: m.caption, + mode: m.mode + } + } + getEditorStatus(): GenericObject { + const c = this.editor.session.selection.getCursor(); + const l = this.editor.session.getLength(); + return { + row: c.row, + column: c.column, + line: l, + langmode: this.currfile.langmode, + file: this.currfile.path + } + } + + getValue(): string { + return this.editor.getValue(); + } + setValue(value: string): void { + this.editor.setValue(value, -1); + } + + } + } +} \ No newline at end of file diff --git a/src/packages/CodePad/AntOSDK.ts b/src/packages/CodePad/AntOSDK.ts index c384592..316582d 100644 --- a/src/packages/CodePad/AntOSDK.ts +++ b/src/packages/CodePad/AntOSDK.ts @@ -153,7 +153,7 @@ namespace OS { await this.mkfileAll(files, path, name); this.app.currdir = rpath.asFileHandle(); this.app.toggleSideBar(); - return this.app.openFile( + return this.app.eum.active.openFile( `${rpath}/README.md`.asFileHandle() as application.CodePadFileHandle ); } catch (e) { @@ -173,10 +173,10 @@ namespace OS { * * @private * @param {string[]} list - * @returns {Promise} + * @returns {Promise} * @memberof AntOSDK */ - private verify(list: string[]): Promise { + private verify(list: string[]): Promise { return new Promise((resolve, reject) => { if (list.length === 0) { return resolve(); @@ -242,10 +242,10 @@ namespace OS { * @private * @param {GenericObject} meta * @param {boolean} debug - * @returns {Promise} + * @returns {Promise} * @memberof AntOSDK */ - private build(meta: GenericObject, debug: boolean): Promise { + private build(meta: GenericObject, debug: boolean): Promise { const dirs = [ `${meta.root}/build`, `${meta.root}/build/debug`, @@ -268,7 +268,7 @@ namespace OS { })(), src ); - await new Promise(async function (r, e) { + await new Promise(async function (r, e) { let code = jsrc; if (!debug) { const options = { @@ -306,7 +306,7 @@ namespace OS { return e(__e(ex)); } }); - await new Promise(async (r, e) => { + await new Promise(async (r, e) => { const txt = await this.cat( (() => { const result1 = []; diff --git a/src/packages/CodePad/BaseEditorModel.ts b/src/packages/CodePad/BaseEditorModel.ts new file mode 100644 index 0000000..9b4e6b2 --- /dev/null +++ b/src/packages/CodePad/BaseEditorModel.ts @@ -0,0 +1,339 @@ +namespace OS { + export namespace application { + + export abstract class CodePadBaseEditorModel { + /** + * Reference to the current editing file handle + * + * @protected + * @type {CodePadFileHandle} + * @memberof CodePad + */ + protected currfile: CodePadFileHandle; + + private app: CodePad; + + /** + * Reference to the editor tab bar UI + * + * @private + * @type {GUI.tag.TabBarTag} + * @memberof CodePad + */ + private tabbar: GUI.tag.TabBarTag; + + private container: HTMLElement; + + onstatuschange: (stat: GenericObject) => void; + + /** + * Editor mutex + * + * @private + * @type {boolean} + * @memberof CodePad + */ + private editormux: boolean; + + constructor(app: CodePad, tabbar: GUI.tag.TabBarTag, editorarea: HTMLElement) { + this.container = editorarea; + this.currfile = "Untitled".asFileHandle() as CodePadFileHandle; + this.tabbar = tabbar; + this.editorSetup(editorarea); + this.app = app; + this.editormux = false; + this.onstatuschange = undefined; + + this.on("focus", () =>{ + if(this.onstatuschange) + this.onstatuschange(this.getEditorStatus()); + }); + this.on("input", () => { + if (this.editormux) { + this.editormux = false; + return false; + } + if (!this.currfile.dirty) { + this.currfile.dirty = true; + this.currfile.text += "*"; + return this.tabbar.update(undefined); + } + }); + + this.on("changeCursor", () => { + if (this.onstatuschange) + this.onstatuschange(this.getEditorStatus()); + }); + + this.tabbar.ontabselect = (e) => { + return this.selecteTab($(e.data.item).index()); + }; + this.tabbar.ontabclose = (e) => { + const it = e.data.item; + if (!it) { + return false; + } + if (!it.data.dirty) { + return this.closeTab(it); + } + this.app.openDialog("YesNoDialog", { + title: __("Close tab"), + text: __("Close without saving ?"), + }).then((d) => { + if (d) { + return this.closeTab(it); + } + return this.focus(); + }); + return false; + }; + } + + /** + * Find a tab on the tabbar corresponding to a file handle + * + * @private + * @param {CodePadFileHandle} file then file handle to search + * @returns {number} + * @memberof CodePad + */ + private findTabByFile(file: CodePadFileHandle): number { + const lst = this.tabbar.items; + const its = (() => { + const result = []; + for (let i = 0; i < lst.length; i++) { + const d = lst[i]; + if (d.hash() === file.hash()) { + result.push(i); + } + } + return result; + })(); + if (its.length === 0) { + return -1; + } + return its[0]; + } + + /** + * Create new tab when opening a file + * + * @private + * @param {CodePadFileHandle} file + * @memberof CodePad + */ + private newTab(file: CodePadFileHandle): void { + file.text = file.basename ? file.basename : file.path; + if (!file.cache) { + file.cache = ""; + } + file.um = this.newUndoManager(); + this.currfile.selected = false; + file.selected = true; + //console.log cnt + this.tabbar.push(file); + } + + /** + * Close a tab when a file is closed + * + * @private + * @param {GUI.tag.ListViewItemTag} it reference to the tab to close + * @returns {boolean} + * @memberof CodePad + */ + private closeTab(it: GUI.tag.ListViewItemTag): boolean { + this.tabbar.delete(it); + const cnt = this.tabbar.items.length; + + if (cnt === 0) { + this.openFile( + "Untitled".asFileHandle() as CodePadFileHandle + ); + return false; + } + this.tabbar.selected = cnt - 1; + return false; + } + + /** + * Select a tab by its index + * + * @private + * @param {number} i tab index + * @returns {void} + * @memberof CodePad + */ + private selecteTab(i: number): void { + //return if i is @tabbar.get "selidx" + const file = this.tabbar.items[i] as CodePadFileHandle; + if (!file) { + return; + } + //return if file is @currfile + if (this.currfile !== file) { + this.currfile.cache = this.getValue(); + this.currfile.cursor = this.getCursor(); + this.currfile.selected = false; + this.currfile = file; + } + + if (!file.langmode) { + if (file.path.toString() !== "Untitled") { + file.langmode = this.getModeForPath(file.path); + } else { + file.langmode = { + text: "Text", + mode: "ace/mode/text", + }; + } + } + this.editormux = true; + this.setUndoManager(this.newUndoManager()); + this.setValue(file.cache); + this.setMode(file.langmode); + + if (file.cursor) { + this.setCursor(file.cursor); + } + this.setUndoManager(file.um); + if (this.onstatuschange) + this.onstatuschange(this.getEditorStatus()); + this.focus(); + } + + selectFile(file: CodePadFileHandle | string): void { + const i = this.findTabByFile( + file.asFileHandle() as CodePadFileHandle + ); + if (i !== -1) { + this.tabbar.selected = i; + } + } + /** + * Open a file in new tab. If the file is already opened, + * the just select the tab + * + * + * @param {CodePadFileHandle} file file to open + * @returns {void} + * @memberof CodePad + */ + openFile(file: CodePadFileHandle): void { + //find tab + const i = this.findTabByFile(file); + if (i !== -1) { + this.tabbar.selected = i; + return; + } + if (file.path.toString() === "Untitled") { + this.newTab(file); + return; + } + + file.read() + .then((d) => { + file.cache = d || ""; + return this.newTab(file); + }) + .catch((e) => { + return this.app.error( + __("Unable to open: {0}", file.path), + e + ); + }); + } + + /** + * write a file + * + * @private + * @param {CodePadFileHandle} file + * @memberof CodePad + */ + private write(file: CodePadFileHandle): void { + this.currfile.cache = this.getValue(); + file.write("text/plain") + .then((d) => { + file.dirty = false; + file.text = file.basename; + this.tabbar.update(undefined); + }) + .catch((e) => + this.app.error(__("Unable to save file: {0}", file.path), e) + ); + } + + save(): void { + this.currfile.cache = this.getValue(); + if (this.currfile.basename) { + return this.write(this.currfile); + } + return this.saveAs(); + } + + /** + * Save the current file as another file + * + * @public + * @memberof CodePad + */ + saveAs(): void { + this.app.openDialog("FileDialog", { + title: __("Save as"), + file: this.currfile, + }).then((f) => { + let d = f.file.path.asFileHandle(); + if (f.file.type === "file") { + d = d.parent(); + } + this.currfile.setPath(`${d.path}/${f.name}`); + this.write(this.currfile); + }); + } + + dirties(): CodePadFileHandle[] { + const result = []; + for (let v of Array.from(this.tabbar.items)) { + if (v.dirty) { + result.push(v); + } + } + return result; + } + + set contextmenuHandle(cb:(e: any,m: any)=>void) + { + this.container.contextmenuHandle = cb; + } + + closeAll(): void + { + this.tabbar.items = []; + this.setValue(""); + this.setUndoManager(this.newUndoManager()); + } + + isDirty(): boolean + { + return this.dirties().length > 0; + } + + protected abstract editorSetup(el: HTMLElement): void; + abstract on(evt_str: string, callback: () => void): void; + abstract resize(): void; + abstract focus(): void; + protected abstract getModeForPath(path: string): GenericObject; + abstract getEditorStatus(): GenericObject; + abstract getValue(): string; + abstract setValue(value: string): void; + protected abstract getCursor(): GenericObject; + protected abstract newUndoManager(): GenericObject; + protected abstract setUndoManager(um: GenericObject): void; + abstract setMode(m: GenericObject): void; + protected abstract setCursor(c: GenericObject): void; + abstract setTheme(theme: string): void; + abstract getModes(): GenericObject[]; + } + } +} \ No newline at end of file diff --git a/src/packages/CodePad/BaseExtension.ts b/src/packages/CodePad/BaseExtension.ts index c10c953..405c1f8 100644 --- a/src/packages/CodePad/BaseExtension.ts +++ b/src/packages/CodePad/BaseExtension.ts @@ -137,8 +137,8 @@ namespace OS { * @returns {Promise} * @memberof BaseExtension */ - protected copy(files: string[], to: string): Promise { - return new Promise((resolve, reject) => { + protected copy(files: string[], to: string): Promise { + return new Promise((resolve, reject) =>{ if (files.length === 0) { return resolve(); } @@ -287,7 +287,7 @@ namespace OS { * @returns {Promise} * @memberof BaseExtension */ - protected mkar(src: string, dest: string): Promise { + protected mkar(src: string, dest: string): Promise { this.logger().info(__("Preparing for release")); return new Promise((resolve, reject) => { return new Promise(async (r, e) => { @@ -352,7 +352,7 @@ namespace OS { * @returns {Promise} * @memberof BaseExtension */ - protected mkdirAll(list: string[]): Promise { + protected mkdirAll(list: string[]): Promise { return new Promise((resolve, reject) => { if (list.length === 0) { return resolve(); @@ -388,7 +388,7 @@ namespace OS { list: Array, path: string, name: string - ): Promise { + ): Promise { return new Promise((resolve, reject) => { if (list.length === 0) { return resolve(); diff --git a/src/packages/CodePad/ExtensionMaker.ts b/src/packages/CodePad/ExtensionMaker.ts index 3e079e7..25ecfde 100644 --- a/src/packages/CodePad/ExtensionMaker.ts +++ b/src/packages/CodePad/ExtensionMaker.ts @@ -135,7 +135,7 @@ namespace OS { await this.mkfileAll(files, path, name); this.app.currdir = rpath.asFileHandle(); this.app.toggleSideBar(); - return this.app.openFile( + return this.app.eum.active.openFile( `${rpath}/${name}.coffee`.asFileHandle() as application.CodePadFileHandle ); } catch (e) { @@ -155,10 +155,10 @@ namespace OS { * * @private * @param {string[]} list - * @returns {Promise} + * @returns {Promise} * @memberof ExtensionMaker */ - private verify(list: string[]): Promise { + private verify(list: string[]): Promise { return new Promise((resolve, reject) => { if (list.length === 0) { return resolve(); @@ -220,10 +220,10 @@ namespace OS { * * @private * @param {GenericObject} meta - * @returns {Promise} + * @returns {Promise} * @memberof ExtensionMaker */ - private build(meta: GenericObject): Promise { + private build(meta: GenericObject): Promise { return new Promise(async (resolve, reject) => { try { const src = await this.compile(meta); @@ -239,7 +239,7 @@ namespace OS { })(), src ); - await new Promise((r, e) => + await new Promise((r, e) => `${meta.root}/build/debug/${meta.meta.name}.js` .asFileHandle() .setCache(jsrc) @@ -280,10 +280,10 @@ namespace OS { * * @private * @param {GenericObject} meta - * @returns {Promise} + * @returns {Promise} * @memberof ExtensionMaker */ - private run(meta: GenericObject): Promise { + private run(meta: GenericObject): Promise { return new Promise(async (resolve, reject) => { const path = `${meta.root}/build/debug/${meta.meta.name}.js`; if (API.shared[path]) { @@ -331,10 +331,10 @@ namespace OS { * @private * @param {string[]} files * @param {*} zip - * @returns {Promise} + * @returns {Promise} * @memberof ExtensionMaker */ - private installExtension(files: string[], zip: any): Promise { + private installExtension(files: string[], zip: any): Promise { return new Promise((resolve, reject) => { const idx = files.indexOf("extension.json"); if (idx < 0) { @@ -370,8 +370,8 @@ namespace OS { private installFiles( files: string[], zip: any, - meta: GenericObject - ): Promise { + meta: GenericObject + ): Promise { if (files.length === 0) { return this.installMeta(meta); } @@ -405,10 +405,10 @@ namespace OS { * * @private * @param {GenericObject} meta - * @returns {Promise} + * @returns {Promise} * @memberof ExtensionMaker */ - private installMeta(meta: GenericObject): Promise { + private installMeta(meta: GenericObject): Promise { return new Promise(async (resolve, reject) => { const file = `${this.app.meta().path }/extensions.json`.asFileHandle(); @@ -443,7 +443,7 @@ namespace OS { * @returns {Promise} * @memberof ExtensionMaker */ - private installZip(path: string): Promise { + private installZip(path: string): Promise { return new Promise((resolve, reject) => { this.import(["os://scripts/jszip.min.js"]) .then(() => { diff --git a/src/packages/CodePad/Makefile b/src/packages/CodePad/Makefile index 6c6ebad..d560538 100644 --- a/src/packages/CodePad/Makefile +++ b/src/packages/CodePad/Makefile @@ -1,4 +1,4 @@ -module_files = main.js BaseExtension.js +module_files = main.js BaseExtension.js BaseEditorModel.js ACEModel.js libfiles = diff --git a/src/packages/CodePad/assets/scheme.html b/src/packages/CodePad/assets/scheme.html index fe5cf66..906faf3 100644 --- a/src/packages/CodePad/assets/scheme.html +++ b/src/packages/CodePad/assets/scheme.html @@ -8,8 +8,17 @@ - -
+ + + +
+
+ + + +
+
+
@@ -21,9 +30,9 @@
- - - + + +
\ No newline at end of file diff --git a/src/packages/CodePad/css/main.css b/src/packages/CodePad/css/main.css index 48b34dd..db26b89 100644 --- a/src/packages/CodePad/css/main.css +++ b/src/packages/CodePad/css/main.css @@ -91,7 +91,6 @@ afx-app-window[data-id = "codepad"] .afx-window-wrapper div[data-id="statctn"]{ } afx-app-window[data-id = "codepad"] .afx-window-wrapper div[data-id="statctn"] afx-label { - float: right; padding-left: 10px; } @@ -161,6 +160,10 @@ afx-app-window[data-id = "codepad"] div[data-id="output-tab"] pre.code-pad-log-w color: orange; } +afx-app-window[data-id = "codepad"] div[data-id="output-tab"] pre.code-pad-log-info { + color: white; +} + afx-app-window[data-id = "codepad"] afx-button[ data-id="logger-clear" ] button{ border: 0; background: transparent; diff --git a/src/packages/CodePad/main.ts b/src/packages/CodePad/main.ts index 93e3c4e..605f522 100644 --- a/src/packages/CodePad/main.ts +++ b/src/packages/CodePad/main.ts @@ -1,4 +1,3 @@ -var ace: any; namespace OS { export namespace application { /** @@ -69,14 +68,16 @@ namespace OS { * @extends {BaseApplication} */ export class CodePad extends BaseApplication { + + /** - * Reference to the current editing file handle + * Reference to the editor manager instance * * @private - * @type {CodePadFileHandle} + * @type {EditorModelManager} * @memberof CodePad */ - private currfile: CodePadFileHandle; + eum: EditorModelManager; /** * Reference to the current working directory @@ -121,14 +122,7 @@ namespace OS { * @memberof CodePad */ private bottombar: GUI.tag.TabContainerTag; - /** - * Reference to the editor tab bar UI - * - * @private - * @type {GUI.tag.TabBarTag} - * @memberof CodePad - */ - private tabbar: GUI.tag.TabBarTag; + /** * Reference to the language status bar @@ -149,31 +143,13 @@ namespace OS { private editorstat: GUI.tag.LabelTag; /** - * Reference to the editor instance + * Reference to the file status bar * * @private - * @type {GenericObject} + * @type {GUI.tag.LabelTag} * @memberof CodePad */ - private editor: GenericObject; - - /** - * Editor language modes - * - * @private - * @type {GenericObject} - * @memberof CodePad - */ - private modes: GenericObject; - - /** - * Editor mutex - * - * @private - * @type {boolean} - * @memberof CodePad - */ - private editormux: boolean; + private filestat: GUI.tag.LabelTag; /** * Reference to the CommandPalette's spotlight @@ -184,6 +160,15 @@ namespace OS { spotlight: CMDMenu; + /** + * Is the split mode enabled + * + * @private + * @type {boolean} + * @memberof CodePad + */ + private split_mode: boolean; + /** * Reference to the editor logger * @@ -243,16 +228,7 @@ namespace OS { */ constructor(args: AppArgumentsType[]) { super("CodePad", args); - this.currfile = "Untitled".asFileHandle() as CodePadFileHandle; this.currdir = undefined; - if (this.args && this.args.length > 0) { - if (this.args[0].type === "dir") { - this.currdir = this.args[0].path.asFileHandle() as CodePadFileHandle; - } else { - this.currfile = this.args[0].path.asFileHandle() as CodePadFileHandle; - this.currdir = this.currfile.parent(); - } - } } /** @@ -263,13 +239,31 @@ namespace OS { */ main(): void { this.extensions = {}; + this.eum = new EditorModelManager(); this.fileview = this.find("fileview") as GUI.tag.FileViewTag; this.sidebar = this.find("sidebar") as GUI.tag.VBoxTag; this.bottombar = this.find("bottombar") as GUI.tag.TabContainerTag; - this.tabbar = this.find("tabbar") as GUI.tag.TabBarTag; this.langstat = this.find("langstat") as GUI.tag.LabelTag; this.editorstat = this.find("editorstat") as GUI.tag.LabelTag; + this.filestat = this.find("current-file-lbl") as GUI.tag.LabelTag; this.logger = new Logger(this.find("output-tab")); + + this.split_mode = true; + + // add editor instance + this.eum + .add(new CodePadACEModel( + this, + this.find("left-tabbar") as GUI.tag.TabBarTag, + this.find("left-editorarea")) as CodePadBaseEditorModel) + .add(new CodePadACEModel( + this, + this.find("right-tabbar") as GUI.tag.TabBarTag, + this.find("right-editorarea")) as CodePadBaseEditorModel); + + this.eum.onstatuschange = (st) => + this.updateStatus(st) + this.fileview.fetch = (path) => new Promise(async function (resolve, reject) { let dir: API.VFS.BaseFileHandle; @@ -289,7 +283,17 @@ namespace OS { return reject(__e(e)); } }); - return this.setup(); + let file = "Untitled".asFileHandle() as CodePadFileHandle; + if (this.args && this.args.length > 0) { + if (this.args[0].type === "dir") { + this.currdir = this.args[0].path.asFileHandle() as CodePadFileHandle; + } else { + file = this.args[0].path.asFileHandle() as CodePadFileHandle; + this.currdir = file.parent(); + } + } + this.setup(); + return this.eum.active.openFile(file); } /** @@ -300,73 +304,6 @@ namespace OS { * @memberof CodePad */ private setup(): void { - ace.config.set("basePath", "scripts/ace"); - ace.require("ace/ext/language_tools"); - this.editor = ace.edit(this.find("datarea")); - this.editor.setOptions({ - enableBasicAutocompletion: true, - enableSnippets: true, - enableLiveAutocompletion: true, - highlightActiveLine: true, - highlightSelectedWord: true, - behavioursEnabled: true, - wrap: true, - fontSize: "10pt", - showInvisibles: true, - }); - //themes = ace.require "ace/ext/themelist" - this.editor.setTheme("ace/theme/monokai"); - this.modes = ace.require("ace/ext/modelist"); - this.editor.completers.push({ - getCompletions( - editor: any, - session: any, - pos: any, - prefix: any, - callback: any - ) { }, - }); - this.editor.getSession().setUseWrapMode(true); - this.editormux = false; - this.editor.on("input", () => { - if (this.editormux) { - this.editormux = false; - return false; - } - if (!this.currfile.dirty) { - this.currfile.dirty = true; - this.currfile.text += "*"; - return this.tabbar.update(undefined); - } - }); - this.editor - .getSession() - .selection.on("changeCursor", (e: any) => { - return this.updateStatus(); - }); - - this.tabbar.ontabselect = (e) => { - return this.selecteTab($(e.data.item).index()); - }; - this.tabbar.ontabclose = (e) => { - const it = e.data.item; - if (!it) { - return false; - } - if (!it.data.dirty) { - return this.closeTab(it); - } - this.openDialog("YesNoDialog", { - title: __("Close tab"), - text: __("Close without saving ?"), - }).then((d) => { - if (d) { - return this.closeTab(it); - } - return this.editor.focus(); - }); - return false; - }; this.fileview.onfileopen = (e) => { if (!e.data || !e.data.path) { return; @@ -374,7 +311,7 @@ namespace OS { if (e.data.type === "dir") { return; } - return this.openFile( + return this.eum.active.openFile( e.data.path.asFileHandle() as CodePadFileHandle ); }; @@ -386,19 +323,14 @@ namespace OS { if (e.data.type === "dir") { return; } - const i = this.findTabByFile( - e.data.path.asFileHandle() as CodePadFileHandle - ); - if (i !== -1) { - return (this.tabbar.selected = i); - } + this.eum.active.selectFile(e.data.path); }; - this.on("resize", () => this.editor.resize()); - this.on("focus", () => this.editor.focus()); + this.on("resize", () => this.eum.resize()); + this.on("focus", () => this.eum.active.focus()); this.spotlight = new CMDMenu(__("Command palette")); this.bindKey("ALT-P", () => this.spotlight.run(this)); - this.find("datarea").contextmenuHandle = (e, m) => { + this.eum.contextmenuHandle = (e, m) => { m.items = [ { text: __("Command palete"), @@ -477,166 +409,8 @@ namespace OS { this.loadExtensionMetaData(); this.initCommandPalete(); this.toggleSideBar(); + this.toggleSplitMode(); this.applyAllSetting(); - return this.openFile(this.currfile); - } - - /** - * Open a file in new tab. If the file is already opened, - * the just select the tab - * - * - * @param {CodePadFileHandle} file file to open - * @returns {void} - * @memberof CodePad - */ - openFile(file: CodePadFileHandle): void { - //find tab - const i = this.findTabByFile(file); - if (i !== -1) { - this.tabbar.selected = i; - return; - } - if (file.path.toString() === "Untitled") { - this.newTab(file); - return; - } - - file.read() - .then((d) => { - file.cache = d || ""; - return this.newTab(file); - }) - .catch((e) => { - return this.error( - __("Unable to open: {0}", file.path), - e - ); - }); - } - - /** - * Find a tab on the tabbar corresponding to a file handle - * - * @private - * @param {CodePadFileHandle} file then file handle to search - * @returns {number} - * @memberof CodePad - */ - private findTabByFile(file: CodePadFileHandle): number { - const lst = this.tabbar.items; - const its = (() => { - const result = []; - for (let i = 0; i < lst.length; i++) { - const d = lst[i]; - if (d.hash() === file.hash()) { - result.push(i); - } - } - return result; - })(); - if (its.length === 0) { - return -1; - } - return its[0]; - } - - /** - * Create new tab when opening a file - * - * @private - * @param {CodePadFileHandle} file - * @memberof CodePad - */ - private newTab(file: CodePadFileHandle): void { - file.text = file.basename ? file.basename : file.path; - if (!file.cache) { - file.cache = ""; - } - file.um = new ace.UndoManager(); - this.currfile.selected = false; - file.selected = true; - //console.log cnt - this.tabbar.push(file); - } - - /** - * Close a tab when a file is closed - * - * @private - * @param {GUI.tag.ListViewItemTag} it reference to the tab to close - * @returns {boolean} - * @memberof CodePad - */ - private closeTab(it: GUI.tag.ListViewItemTag): boolean { - this.tabbar.delete(it); - const cnt = this.tabbar.items.length; - - if (cnt === 0) { - this.openFile( - "Untitled".asFileHandle() as CodePadFileHandle - ); - return false; - } - this.tabbar.selected = cnt - 1; - return false; - } - - /** - * Select a tab by its index - * - * @private - * @param {number} i tab index - * @returns {void} - * @memberof CodePad - */ - private selecteTab(i: number): void { - //return if i is @tabbar.get "selidx" - const file = this.tabbar.items[i] as CodePadFileHandle; - if (!file) { - return; - } - (this - .scheme as GUI.tag.WindowTag).apptitle = file.text.toString(); - //return if file is @currfile - if (this.currfile !== file) { - this.currfile.cache = this.editor.getValue(); - this.currfile.cursor = this.editor.selection.getCursor(); - this.currfile.selected = false; - this.currfile = file; - } - - if (!file.langmode) { - if (file.path.toString() !== "Untitled") { - const m = this.modes.getModeForPath(file.path); - file.langmode = { caption: m.caption, mode: m.mode }; - } else { - file.langmode = { - caption: "Text", - mode: "ace/mode/text", - }; - } - } - this.editormux = true; - this.editor.getSession().setUndoManager(new ace.UndoManager()); - this.editor.setValue(file.cache, -1); - this.editor.getSession().setMode(file.langmode.mode); - if (file.cursor) { - this.editor.renderer.scrollCursorIntoView( - { - row: file.cursor.row, - column: file.cursor.column, - }, - 0.5 - ); - this.editor.selection.moveTo( - file.cursor.row, - file.cursor.column - ); - } - this.editor.getSession().setUndoManager(file.um); - this.updateStatus(); - this.editor.focus(); } /** @@ -645,16 +419,17 @@ namespace OS { * @private * @memberof CodePad */ - private updateStatus(): void { - const c = this.editor.session.selection.getCursor(); - const l = this.editor.session.getLength(); + private updateStatus(stat: GenericObject = undefined): void { + if (!stat) + stat = this.eum.active.getEditorStatus(); this.editorstat.text = __( "Row {0}, col {1}, lines: {2}", - c.row + 1, - c.column + 1, - l + stat.row + 1, + stat.column + 1, + stat.line ); - this.langstat.text = this.currfile.langmode.caption; + this.langstat.text = stat.langmode.text; + this.filestat.text = stat.file } /** @@ -713,6 +488,33 @@ namespace OS { this.showBottomBar(!this.setting.showBottomBar); } + private toggleSplitMode():void { + const right_pannel = this.find("right-panel"); + const right_editor = this.eum.editors[1]; + const left_editor = this.eum.editors[0]; + if(this.split_mode) + { + // before hide check if there is dirty files + if(right_editor.isDirty()) + { + this.notify(__("Unable to disable split view: Please save changes of modified files on the right panel")); + return; + } + right_editor.closeAll(); + $(right_pannel).hide(); + this.split_mode = false; + left_editor.focus(); + } + else + { + $(right_pannel).show(); + this.split_mode = true; + right_editor.openFile("Untitled".asFileHandle() as CodePadFileHandle); + right_editor.focus(); + } + this.trigger("resize"); + } + /** * Add an action to the [[CommandPalette]]'s spotlight * @@ -758,26 +560,22 @@ namespace OS { r: CodePad ) { const data = d.data.item.data; - r.editor.setTheme(data.theme); - return r.editor.focus(); + r.eum.active.setTheme(data.theme); + return r.eum.active.focus(); }); this.spotlight.addAction(cmdtheme); const cmdmode = new CMDMenu(__("Change language mode")); - for (v of Array.from(this.modes.modes)) { - cmdmode.addAction({ text: v.caption, mode: v.mode }); + for (v of Array.from(this.eum.active.getModes())) { + cmdmode.addAction({ text: v.text, mode: v.mode }); } cmdmode.onchildselect(function ( d: GUI.TagEventType, r: CodePad ) { const data = d.data.item.data; - r.editor.session.setMode(data.mode); - r.currfile.langmode = { - caption: data.text, - mode: data.mode, - }; + r.eum.active.setMode(data); r.updateStatus(); - r.editor.focus(); + r.eum.active.focus(); }); this.spotlight.addAction(cmdmode); this.addAction(CMDMenu.fromMenu(this.fileMenu())); @@ -1074,46 +872,9 @@ namespace OS { } } - /** - * Save a file - * - * @private - * @param {CodePadFileHandle} file - * @memberof CodePad - */ - private save(file: CodePadFileHandle): void { - file.write("text/plain") - .then((d) => { - file.dirty = false; - file.text = file.basename; - this.tabbar.update(undefined); - (this - .scheme as GUI.tag.WindowTag).apptitle = `${this.currfile.basename}`; - }) - .catch((e) => - this.error(__("Unable to save file: {0}", file.path), e) - ); - } - /** - * Save the current file as another file - * - * @private - * @memberof CodePad - */ - private saveAs(): void { - this.openDialog("FileDialog", { - title: __("Save as"), - file: this.currfile, - }).then((f) => { - let d = f.file.path.asFileHandle(); - if (f.file.type === "file") { - d = d.parent(); - } - this.currfile.setPath(`${d.path}/${f.name}`); - this.save(this.currfile); - }); - } + + /** * Menu action definition @@ -1131,7 +892,7 @@ namespace OS { } switch (dataid) { case "new": - return me.openFile("Untitled".asFileHandle() as CodePadFileHandle); + return me.eum.active.openFile("Untitled".asFileHandle() as CodePadFileHandle); case "open": return me .openDialog("FileDialog", { @@ -1141,7 +902,7 @@ namespace OS { ), }) .then((f: API.FileInfoType) => - me.openFile(f.file.path.asFileHandle()) + me.eum.active.openFile(f.file.path.asFileHandle()) ); case "opendir": return me @@ -1154,14 +915,10 @@ namespace OS { return me.toggleSideBar(); }); case "save": - me.currfile.cache = me.editor.getValue(); - if (me.currfile.basename) { - return me.save(me.currfile); - } - return me.saveAs(); + return me.eum.active.save(); + case "saveas": - me.currfile.cache = me.editor.getValue(); - return me.saveAs(); + return me.eum.active.saveAs(); default: return console.log(dataid); } @@ -1176,15 +933,7 @@ namespace OS { */ cleanup(evt: BaseEvent): void { let v: GenericObject; - const dirties = (() => { - const result = []; - for (v of Array.from(this.tabbar.items)) { - if (v.dirty) { - result.push(v); - } - } - return result; - })(); + const dirties = this.eum.dirties(); if (dirties.length === 0) { return; } @@ -1231,6 +980,10 @@ namespace OS { { text: "__(Toggle bottom bar)", dataid: "bottombar" + }, + { + text: "__(Toggle split view)", + dataid: "splitview" } ], onchildselect: ( @@ -1243,6 +996,10 @@ namespace OS { case "bottombar": return this.toggleBottomBar(); + + case "splitview": + return this.toggleSplitMode(); + break; default: break; @@ -1355,7 +1112,102 @@ namespace OS { return m; }; + /** + * Helper class to manager several instances + * of editor models + * + * @class EditorModelManager + */ + class EditorModelManager { + /** + * Referent to the active editor model + * + * @private + * @type {CodePadBaseEditorModel} + * @memberof EditorModelManager + */ + private active_editor: CodePadBaseEditorModel; + + /** + * Store a list of editor models + * + * @private + * @type {CodePadBaseEditorModel[]} + * @memberof EditorModelManager + */ + private models: CodePadBaseEditorModel[]; + + /** + * Creates an instance of EditorModelManager. + * @memberof EditorModelManager + */ + constructor() { + this.active_editor = undefined; + this.models = []; + } + + get editors(): CodePadBaseEditorModel[]{ + return this.models; + } + set contextmenuHandle(cb: (e: any, m: any) => void) { + for (let ed of this.models) { + ed.contextmenuHandle = cb; + } + } + + /** + * Get the active editor model + * + * @readonly + * @type {CodePadBaseEditorModel} + * @memberof EditorModelManager + */ + get active(): CodePadBaseEditorModel { + return this.active_editor; + } + + /** + * Add a model to the manager + * + * @param {CodePadBaseEditorModel} model + * @memberof EditorModelManager + */ + add(model: CodePadBaseEditorModel): EditorModelManager { + this.models.push(model); + if (!this.active_editor) + this.active_editor = model; + model.on("focus", () => { + this.active_editor = model; + }); + return this; + } + + set onstatuschange(cb: (stat: GenericObject) => void) { + for (let ed of this.models) { + ed.onstatuschange = cb; + } + } + + dirties(): CodePadFileHandle[] { + let list = []; + for (let ed of this.models) { + list = list.concat(ed.dirties()); + } + return list; + } + + /** + * Resize all editor + * + * @memberof EditorModelManager + */ + resize(): void { + for (let ed of this.models) { + ed.resize(); + } + } + } /** * This class handles log output to the Editor output container * diff --git a/src/packages/CodePad/package.json b/src/packages/CodePad/package.json index ccf8c0a..a915f63 100644 --- a/src/packages/CodePad/package.json +++ b/src/packages/CodePad/package.json @@ -7,7 +7,7 @@ "email": "xsang.le@gmail.com", "licences": "GPLv3" }, - "version":"0.0.3-b", + "version":"0.1.1-b", "category":"Developments", "iconclass":"fa fa-pencil-square-o", "mimes":[