From 43b2c9d2d4ffcc946d371a81e2afec240738e800 Mon Sep 17 00:00:00 2001 From: fenix-hub Date: Tue, 8 Oct 2019 00:23:37 +0200 Subject: [PATCH 1/5] Add files via upload --- addons/README.md | 63 ++++++++++++++++++++++++++++++++++ addons/VERSION.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 addons/README.md create mode 100644 addons/VERSION.md diff --git a/addons/README.md b/addons/README.md new file mode 100644 index 0000000..6c35125 --- /dev/null +++ b/addons/README.md @@ -0,0 +1,63 @@ +Check my **[Discord](https://discord.gg/KnJGY9S)** to stay updated on this repository. +*(Recommended since the AssetLibrary is not automatically updated)* + +This plugin is now supported in [Godot Extended Library Discord](https://discord.gg/JNrcucg), check out the [Godot Extended Library Project](https://github.com/godot-extended-libraries)! + +# Godot Text Editor +A little plugin to easy-way manage your text files inside your project folder. + +Author: *"Nicolo (fenix) Santilio"* +Version: *1.4.3* +Godot Version: *3.1.1-stable* + +**This repository was pushed directly from Godot Engine Editor thanks to [GitHub Integration](https://github.com/fenix-hub/godot-engine.github-integration)!** + +## What is this? +This is a little plugin I've made to easily edit text files in your project folder. + +## How does it work? +You can open an existing file, create a new file and delete a file. +When opening / creating a file, the editor will open and you will be able to edit it and save it. +You can just *Save* the file, or *Save file As* a new file (if it is a new file but also to make some copies). +You will also be able to see some informations about the file you are editing (time and date of last edit) and you can set your editor to *Read Only* if you don't want to make changes but still read the content of the file. +Multiple files can be opened in different tabs. +![preview1](https://i.imgur.com/mlh1rOX.png)![preview2](https://i.imgur.com/od5nQff.png) + +## How do I install it? +**Manual** +Just download this whole repository and put it in your `res://addons` folder inside the project you want to work on. +Then, go to `Project > Plugins > "File Editor" > Status > Activate`. + +**Automatic** +You can find this plugin in the AssetLib of Godot Engine Editor. Just download it from there and everything should be fine! +(Remember to activate this plugin) + +## Supported formats ++ "*.txt ; Plain Text File", ++ "*.rtf ; Rich Text Format File", ++ "*.log ; Log File", ++ "*.md ; MD File", ++ "*.doc ; WordPad Document", ++ "*.doc ; Microsoft Word Document", ++ "*.docm ; Word Open XML Macro-Enabled Document", ++ "*.docx ; Microsoft Word Open XML Document", ++ "*.bbs ; Bulletin Board System Text", ++ "*.dat ; Data File", ++ "*.xml ; XML File", ++ "*.sql ; SQL database file", ++ "*.json ; JavaScript Object Notation File", ++ "*.html ; HyperText Markup Language" ++ "*.cfg ; Configuration File" ++ "*.ini ; Initialization File (same as .cfg Configuration File)" + +#### Current version +To check all the features included in the current version, please read the [VERSION file](./VERSION.md) + +#### Upcoming features +To check all the features I'm currently working on, please read the [TODO file](./TODO.md) + +# Disclaimer +This addon was built for a **personal use** intention. It was released as an open source plugin in the hope that it could be useful to the Godot Engine Community. +As a "work in progress" project, there is *no warranty* for any eventual issue and bug that may broke your project. +I don't assume any responsibility for possible corruptions of your project files. It is always advisable to keep a copy of your files and check any changes. + diff --git a/addons/VERSION.md b/addons/VERSION.md new file mode 100644 index 0000000..b6c087f --- /dev/null +++ b/addons/VERSION.md @@ -0,0 +1,87 @@ +**version 0.0.1** +*added* +- Plugin Created + +----------------------- + +**version 0.1.1** +*added* +- "Create new File" option +- "Open File" option +- "Delete File" option +- "Save File" option +- "Save File As.." option + +----------------------- + +**version 0.1.2** +*fixed* +- Repository Installation, folder order + +----------------------- + +**version 0.2.5** +*removed* +- Old layout + +*added* +- New Layout +- Last modified time and date +- Tabs + +----------------------- + +**version 0.3.1** +*added* +- Version check +- Preview support +- Context menu in editor +- BBCode converter +- Light Mardkwon converter (DEMO) + +----------------------- + +**version 1.2.1** +*removed* +- Old layout, the plugin won't appear in docs +- Icons file extensions, plugin size reduced +- Old Mardkwon preview method + +*added* +- More supported files +1. "*.dat ; Data File", +2. "*.xml ; XML File", +3. "*.sql ; SQL database file", +4. "*.json ; JavaScript Object Notation File", +5. "*.html ; HyperText Markup Language +- New Markdown preview method ( Markdown -> BBCode converter) +- New HTML preview method ( HTML -> BBCode converter) +- New Plugin Layout: a new ButtonTool "File" in you TopBar will appear +- Error check +- Message popups for closing unsaved files + +----------------------- + +**version 1.4.3** +*removed* +- Old layout + +*added* +- More supported files: +1. "*.cfg ; Configuration File", +2. "*.ini ; Initialization File (same as .cfg Configuration File)", +- Added some sample files +- Memorize system of last opened files +- Added *Editor* button , you can now choose which editor to use: +1. Vanilla Editor (simple text editor) +2. Cfg/Ini Editor (table editor for Cfg/Ini files) + Editors are automatically updated, so if you update a cfg/ini file in the Vanilla Editor it will be updated in the Cfg/Ini Editor, and viceversa +- Added editor shorcuts: +1. *Ctrl + N* (new file) +2. *Ctrl + O* (open file) +3. *Ctrl + Alt + C* (close file) +4. *Ctrl + S* (save file) +5. *Ctrl + Alt + S* (save file as...) +6. *Ctrl + D* (delete file) +7. *Ctrl + 1* (Vanilla Editor) +8. *Ctrl + 3* (Cfg/Ini Editor) From fab4d9de2efdc9272bbbe4de7ea7239678d898d2 Mon Sep 17 00:00:00 2001 From: fenix-hub Date: Tue, 8 Oct 2019 00:25:16 +0200 Subject: [PATCH 2/5] Version 1.4.3 Latest Update 08/10/2019 --- addons/file-editor/README.md | 9 +- addons/file-editor/TODO.md | 8 +- addons/file-editor/VERSION.md | 152 ++++--- addons/file-editor/file.samples/sample.bbs | 8 + addons/file-editor/file.samples/sample.cfg | 19 + addons/file-editor/file.samples/sample.html | 7 + addons/file-editor/file.samples/sample.ini | 10 + addons/file-editor/file.samples/sample.md | 11 + addons/file-editor/file.samples/sample.xml | 120 ++++++ addons/file-editor/icon.png | Bin 889 -> 1447 bytes addons/file-editor/icon.psd | Bin 0 -> 94789 bytes .../file-editor/icons.pngs/add.png.buttonicon | Bin 0 -> 146 bytes .../icons.pngs/edit_.png.buttonicon | Bin 0 -> 186 bytes .../icons.pngs/keys.png.buttonicon | Bin 0 -> 222 bytes .../icons.pngs/remove.png.buttonicon | Bin 0 -> 226 bytes .../icons.pngs/section.png.buttonicon | Bin 0 -> 178 bytes .../icons.pngs/sections.png.buttonicon | Bin 0 -> 141 bytes addons/file-editor/lastopenedfiles.lastcfg | 4 + addons/file-editor/plugin.cfg | 2 +- addons/file-editor/scenes/FileEditor.tscn | 241 +++++++---- addons/file-editor/scenes/IniEditor.tscn | 369 ++++++++++++++++ addons/file-editor/scenes/Preview.tscn | 11 +- addons/file-editor/scenes/VanillaEditor.tscn | 88 ++++ addons/file-editor/scripts/FileEditor.gd | 405 +++++++++++++++--- addons/file-editor/scripts/FileScene.gd | 121 +----- addons/file-editor/scripts/IconLoader.gd | 3 + addons/file-editor/scripts/IniVisualEditor.gd | 274 ++++++++++++ addons/file-editor/scripts/LastOpenedFiles.gd | 33 ++ addons/file-editor/scripts/Preview.gd | 30 +- addons/file-editor/scripts/file-editor.gd | 3 + 30 files changed, 1610 insertions(+), 318 deletions(-) create mode 100644 addons/file-editor/file.samples/sample.bbs create mode 100644 addons/file-editor/file.samples/sample.cfg create mode 100644 addons/file-editor/file.samples/sample.html create mode 100644 addons/file-editor/file.samples/sample.ini create mode 100644 addons/file-editor/file.samples/sample.md create mode 100644 addons/file-editor/file.samples/sample.xml create mode 100644 addons/file-editor/icon.psd create mode 100644 addons/file-editor/icons.pngs/add.png.buttonicon create mode 100644 addons/file-editor/icons.pngs/edit_.png.buttonicon create mode 100644 addons/file-editor/icons.pngs/keys.png.buttonicon create mode 100644 addons/file-editor/icons.pngs/remove.png.buttonicon create mode 100644 addons/file-editor/icons.pngs/section.png.buttonicon create mode 100644 addons/file-editor/icons.pngs/sections.png.buttonicon create mode 100644 addons/file-editor/lastopenedfiles.lastcfg create mode 100644 addons/file-editor/scenes/IniEditor.tscn create mode 100644 addons/file-editor/scenes/VanillaEditor.tscn create mode 100644 addons/file-editor/scripts/IniVisualEditor.gd create mode 100644 addons/file-editor/scripts/LastOpenedFiles.gd diff --git a/addons/file-editor/README.md b/addons/file-editor/README.md index e9ccbcc..6c35125 100644 --- a/addons/file-editor/README.md +++ b/addons/file-editor/README.md @@ -1,11 +1,13 @@ Check my **[Discord](https://discord.gg/KnJGY9S)** to stay updated on this repository. *(Recommended since the AssetLibrary is not automatically updated)* +This plugin is now supported in [Godot Extended Library Discord](https://discord.gg/JNrcucg), check out the [Godot Extended Library Project](https://github.com/godot-extended-libraries)! + # Godot Text Editor A little plugin to easy-way manage your text files inside your project folder. Author: *"Nicolo (fenix) Santilio"* -Version: *1.2.1* +Version: *1.4.3* Godot Version: *3.1.1-stable* **This repository was pushed directly from Godot Engine Editor thanks to [GitHub Integration](https://github.com/fenix-hub/godot-engine.github-integration)!** @@ -19,7 +21,7 @@ When opening / creating a file, the editor will open and you will be able to edi You can just *Save* the file, or *Save file As* a new file (if it is a new file but also to make some copies). You will also be able to see some informations about the file you are editing (time and date of last edit) and you can set your editor to *Read Only* if you don't want to make changes but still read the content of the file. Multiple files can be opened in different tabs. -![preview](https://i.imgur.com/LcrGCGS.png) +![preview1](https://i.imgur.com/mlh1rOX.png)![preview2](https://i.imgur.com/od5nQff.png) ## How do I install it? **Manual** @@ -45,6 +47,8 @@ You can find this plugin in the AssetLib of Godot Engine Editor. Just download i + "*.sql ; SQL database file", + "*.json ; JavaScript Object Notation File", + "*.html ; HyperText Markup Language" ++ "*.cfg ; Configuration File" ++ "*.ini ; Initialization File (same as .cfg Configuration File)" #### Current version To check all the features included in the current version, please read the [VERSION file](./VERSION.md) @@ -56,3 +60,4 @@ To check all the features I'm currently working on, please read the [TODO file]( This addon was built for a **personal use** intention. It was released as an open source plugin in the hope that it could be useful to the Godot Engine Community. As a "work in progress" project, there is *no warranty* for any eventual issue and bug that may broke your project. I don't assume any responsibility for possible corruptions of your project files. It is always advisable to keep a copy of your files and check any changes. + diff --git a/addons/file-editor/TODO.md b/addons/file-editor/TODO.md index 9e8a120..f88ece3 100644 --- a/addons/file-editor/TODO.md +++ b/addons/file-editor/TODO.md @@ -1,6 +1,4 @@ ### to do (v-0.x.x) -- XML Tree Visualizer -- JSON Tree Visualizer -- CSV Table Visualizer -- .cfg support -- CFG Tree Visualizer +- Popup dialog when closing a tab if there is new content +- Module for markdown support/conversion + diff --git a/addons/file-editor/VERSION.md b/addons/file-editor/VERSION.md index c7f740f..b6c087f 100644 --- a/addons/file-editor/VERSION.md +++ b/addons/file-editor/VERSION.md @@ -1,65 +1,87 @@ -**version 0.0.1** -*added* -- Plugin Created - ------------------------ - -**version 0.1.1** -*added* -- "Create new File" option -- "Open File" option -- "Delete File" option -- "Save File" option -- "Save File As.." option - ------------------------ - -**version 0.1.2** -*fixed* -- Repository Installation, folder order - ------------------------ - -**version 0.2.5** -*removed* -- Old layout - -*added* -- New Layout -- Last modified time and date -- Tabs - ------------------------ - -**version 0.3.1** -*added* -- Version check -- Preview support -- Context menu in editor -- BBCode converter -- Light Mardkwon converter (DEMO) - ------------------------ - -**version 1.2.1** -*removed* -- Old layout, the plugin won't appear in docs -- Icons file extensions, plugin size reduced -- Old Mardkwon preview method - -*added* -- More supported files -1. "*.dat ; Data File", -2. "*.xml ; XML File", -3. "*.sql ; SQL database file", -4. "*.json ; JavaScript Object Notation File", -5. "*.html ; HyperText Markup Language -- New Markdown preview method ( Markdown -> BBCode converter) -- New HTML preview method ( HTML -> BBCode converter) -- New Plugin Layout: a new ButtonTool "File" in you TopBar will appear -- Error check -- Message popups for closing unsaved files - ------------------------ - - +**version 0.0.1** +*added* +- Plugin Created + +----------------------- + +**version 0.1.1** +*added* +- "Create new File" option +- "Open File" option +- "Delete File" option +- "Save File" option +- "Save File As.." option + +----------------------- + +**version 0.1.2** +*fixed* +- Repository Installation, folder order + +----------------------- + +**version 0.2.5** +*removed* +- Old layout + +*added* +- New Layout +- Last modified time and date +- Tabs + +----------------------- + +**version 0.3.1** +*added* +- Version check +- Preview support +- Context menu in editor +- BBCode converter +- Light Mardkwon converter (DEMO) + +----------------------- + +**version 1.2.1** +*removed* +- Old layout, the plugin won't appear in docs +- Icons file extensions, plugin size reduced +- Old Mardkwon preview method + +*added* +- More supported files +1. "*.dat ; Data File", +2. "*.xml ; XML File", +3. "*.sql ; SQL database file", +4. "*.json ; JavaScript Object Notation File", +5. "*.html ; HyperText Markup Language +- New Markdown preview method ( Markdown -> BBCode converter) +- New HTML preview method ( HTML -> BBCode converter) +- New Plugin Layout: a new ButtonTool "File" in you TopBar will appear +- Error check +- Message popups for closing unsaved files + +----------------------- + +**version 1.4.3** +*removed* +- Old layout + +*added* +- More supported files: +1. "*.cfg ; Configuration File", +2. "*.ini ; Initialization File (same as .cfg Configuration File)", +- Added some sample files +- Memorize system of last opened files +- Added *Editor* button , you can now choose which editor to use: +1. Vanilla Editor (simple text editor) +2. Cfg/Ini Editor (table editor for Cfg/Ini files) + Editors are automatically updated, so if you update a cfg/ini file in the Vanilla Editor it will be updated in the Cfg/Ini Editor, and viceversa +- Added editor shorcuts: +1. *Ctrl + N* (new file) +2. *Ctrl + O* (open file) +3. *Ctrl + Alt + C* (close file) +4. *Ctrl + S* (save file) +5. *Ctrl + Alt + S* (save file as...) +6. *Ctrl + D* (delete file) +7. *Ctrl + 1* (Vanilla Editor) +8. *Ctrl + 3* (Cfg/Ini Editor) diff --git a/addons/file-editor/file.samples/sample.bbs b/addons/file-editor/file.samples/sample.bbs new file mode 100644 index 0000000..317eacf --- /dev/null +++ b/addons/file-editor/file.samples/sample.bbs @@ -0,0 +1,8 @@ +[b]bolded text[/b] +[i]italicized text[/i] +[u]underlined text[/u] +[s]strikethrough text[/s] +[url]https://en.wikipedia.org[/url] +[url=https://en.wikipedia.org]English Wikipedia[/url] +[code]monospaced text[/code] +left [center]center[/center] [right]right[/right] diff --git a/addons/file-editor/file.samples/sample.cfg b/addons/file-editor/file.samples/sample.cfg new file mode 100644 index 0000000..4a00d73 --- /dev/null +++ b/addons/file-editor/file.samples/sample.cfg @@ -0,0 +1,19 @@ +[Locale] +Language="en" +[Version] +Major="2" +Minor="1" +Micro="3" +[Directories] +TempDir="C:\Users\Jon\AppData\Local\Audacity\SessionData" +[AudioIO] +RecordingDevice="Microphone (Blue Snowball)" +Host="MME" +PlaybackDevice="Speakers / Headphones (Realtek)" +EffectsPreviewLen="6" +CutPreviewBeforeLen="2" +CutPreviewAfterLen="1" +SeekShortPeriod="1" +SeekLongPeriod="15" +Duplex="1" +SWPlaythrough="0" diff --git a/addons/file-editor/file.samples/sample.html b/addons/file-editor/file.samples/sample.html new file mode 100644 index 0000000..213413c --- /dev/null +++ b/addons/file-editor/file.samples/sample.html @@ -0,0 +1,7 @@ +bolded text, +italicized text, +underlined text +strikethrough text +English Wikipedia + + diff --git a/addons/file-editor/file.samples/sample.ini b/addons/file-editor/file.samples/sample.ini new file mode 100644 index 0000000..16f04fd --- /dev/null +++ b/addons/file-editor/file.samples/sample.ini @@ -0,0 +1,10 @@ +; last modified 1 April 2001 by John Doe +[owner] +name="John Doe" +organization="Acme Widgets Inc." + +[database] +; use IP address in case network name resolution is not working +server="192.0.2.62" +port="143" +file="payroll.dat" diff --git a/addons/file-editor/file.samples/sample.md b/addons/file-editor/file.samples/sample.md new file mode 100644 index 0000000..5c80f83 --- /dev/null +++ b/addons/file-editor/file.samples/sample.md @@ -0,0 +1,11 @@ +*italic* +**bold** +`monospace` +~~strikethrough~~ +[link](http://example.com) +![image](res://addons/file-editor/icon.png) + +Shopping list: +- apples +- oranges +- pears diff --git a/addons/file-editor/file.samples/sample.xml b/addons/file-editor/file.samples/sample.xml new file mode 100644 index 0000000..7c254f1 --- /dev/null +++ b/addons/file-editor/file.samples/sample.xml @@ -0,0 +1,120 @@ + + + + Gambardella, Matthew + XML Developer's Guide + Computer + 44.95 + 2000-10-01 + An in-depth look at creating applications + with XML. + + + Ralls, Kim + Midnight Rain + Fantasy + 5.95 + 2000-12-16 + A former architect battles corporate zombies, + an evil sorceress, and her own childhood to become queen + of the world. + + + Corets, Eva + Maeve Ascendant + Fantasy + 5.95 + 2000-11-17 + After the collapse of a nanotechnology + society in England, the young survivors lay the + foundation for a new society. + + + Corets, Eva + Oberon's Legacy + Fantasy + 5.95 + 2001-03-10 + In post-apocalypse England, the mysterious + agent known only as Oberon helps to create a new life + for the inhabitants of London. Sequel to Maeve + Ascendant. + + + Corets, Eva + The Sundered Grail + Fantasy + 5.95 + 2001-09-10 + The two daughters of Maeve, half-sisters, + battle one another for control of England. Sequel to + Oberon's Legacy. + + + Randall, Cynthia + Lover Birds + Romance + 4.95 + 2000-09-02 + When Carla meets Paul at an ornithology + conference, tempers fly as feathers get ruffled. + + + Thurman, Paula + Splish Splash + Romance + 4.95 + 2000-11-02 + A deep sea diver finds true love twenty + thousand leagues beneath the sea. + + + Knorr, Stefan + Creepy Crawlies + Horror + 4.95 + 2000-12-06 + An anthology of horror stories about roaches, + centipedes, scorpions and other insects. + + + Kress, Peter + Paradox Lost + Science Fiction + 6.95 + 2000-11-02 + After an inadvertant trip through a Heisenberg + Uncertainty Device, James Salway discovers the problems + of being quantum. + + + O'Brien, Tim + Microsoft .NET: The Programming Bible + Computer + 36.95 + 2000-12-09 + Microsoft's .NET initiative is explored in + detail in this deep programmer's reference. + + + O'Brien, Tim + MSXML3: A Comprehensive Guide + Computer + 36.95 + 2000-12-01 + The Microsoft MSXML3 parser is covered in + detail, with attention to XML DOM interfaces, XSLT processing, + SAX and more. + + + Galos, Mike + Visual Studio 7: A Comprehensive Guide + Computer + 49.95 + 2001-04-16 + Microsoft Visual Studio 7 is explored in depth, + looking at how Visual Basic, Visual C++, C#, and ASP+ are + integrated into a comprehensive development + environment. + + diff --git a/addons/file-editor/icon.png b/addons/file-editor/icon.png index 84164c3ec2cb7dd9486d9cc7d02985144deac7df..471326bc865031358935066e0cc67705c5218ab8 100644 GIT binary patch delta 1383 zcmV-t1(^Ez2B!-liBL{Q4GJ0x0000DNk~Le0001R0001R2m=5B0P#Kqd66L(e=Z{c z04^f|c%?sf0003;P)t-s0000gL|Q6DS}H_ZDnwc;L|Q6DS}H_ZDnwc;L|Q6DS}R3d zFH2)GQED_(Z8TJGHC1pkQfx6$X)Q=#Dnwc=M_n{iZZc46El6K3N@6NRS}#mxEJt23 zPiQVmVK7Z*DnwdLUu2gS^Ks(3QN}#oFu4)Z3S+we0iwjJDUi)aQb*)0ei*xz62;uE$1wrp4CdpuExb`TW-6 z>(Altft9AN#@MOJ;bM`r(cbB`%iHMh^z--mfTqG&im!{U$M*UAN`R=zf7#`~(%@{E zxObnuvd`q$=kez5^X~KaqQ25jgQ`-6td_OR+~)48!`9yD?{uELU5&BX-{hjPy=sD% zb&H)_b&HOmuU~kMFivJINntHXU^7u`Dnwc`P-!YeS|ox_t^fc4ZFEvjQveV^f4|@F z|F4f=|NsC0|NsC0|DXT=3jhEAQ2+n`|NqaEQ2`{AU;!1AU;z++aQ`6M1{bmb00V$Y zL_t(|obB8FQxZWK2k=8)7L?>Hic~byM@a#JLP-(B)I-r!us5|=D=X{&|G7Ot!Qm%s#dHsPttt~FM7vE}0z~c4Er=m`8d4! zQycegk%TQ&(13WAnM2se9fz-Zqv8$rOjCKwOywOmn19a<`qXx`!oH%%C%E7dU1%}` zaDobd?(z1(b8vjU2mZ|mxl>yTO2|_i6j?dUf`K~@4c=%Kq@k}m-$%- z`05Ni1&C)j-*Ig)byodh$*iq^$Em0Jw)VZ-{OE8<6I5QJq!Jt?#H)Ff zR6^EMD>Dlaa;6F#HLF@xmJ0r?6@02KhIA<`_tbyY%RSA4)}RV6*eGaKRkHh=GUv@A zXz4|}^!!Npq70gjMH2lZlN+P$NBpjdfJbt+8Y1%oF9pAa7T3;?E p>2v>W|A&N;fJY8C5$JDT&3}|*vnMqjtnUB-002ovPDHLkV1ld9pzZ(w delta 846 zcmV-U1F`(43;6~iiBL{Q4GJ0x0000DNk~Le0000$0000$2m=5B0G+pi?2#cCe+v@; z01FcV0GgZ_0002qP)t-s0000XK~o?@R4PSVDnwc;L|PUqJ|8<%DnwcnEvW2p^Z5Ht zYJ!EBsod%FjJMabzsYETl+ohvqsHG`b&Hy+w?bff+27=IiJXq0uddAFK3sN2WPDh1 zi7G@|A3RkbJx(D!RVzhXB0o?cJ5v`dJSjw4C`4NlZhI~O002sKQchC<7ci3A^#2H& z{RhJT|NsC0ng9PUk^kBAlfeNff7+6gF#j)`2g1VoJv@^D00G`fL_t(|oYmKBQ-VMg zfZ<{jl}j*w#|CAr=ee^SaZIiUsF z^^6th2`SQyJdmM9QljYV_gR*f2&JE|Pu237xbsR?C{X$hP&K_KuHI;>r-4AL?X!HH z*69WTs`e95GXw$z%pS~i7wcjH0*y+4w@Sm^zhw#q5dgNi2GotPb0rbLMbrBf+Rj-Z z045YLfKWgITR|Gw2<=O0e}D^mSqA8Avb`;|@PKZ7d)t6!m{VYV z71YN7-JS;OUH|1NL)_ViTL)4MyG1}1eM4pv}{MffpUI@~ diff --git a/addons/file-editor/icon.psd b/addons/file-editor/icon.psd new file mode 100644 index 0000000000000000000000000000000000000000..487d52abce29633871802cc16b2bc258293e88e5 GIT binary patch literal 94789 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 literal 0 HcmV?d00001 diff --git a/addons/file-editor/icons.pngs/edit_.png.buttonicon b/addons/file-editor/icons.pngs/edit_.png.buttonicon new file mode 100644 index 0000000000000000000000000000000000000000..20bfe8fd6d68082a34a0fb5d2ea5a10d29562ebb GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFVFPLHd|NR2*0}3+yG(qasJzX3_D&{07 zI50V#3TT@oWW~()K#6OK!Gm2bA`5x03urX2Ww=?v@Xm($(*Bzw_kda$JYD@<);T3K F0RSM$KCA!$ literal 0 HcmV?d00001 diff --git a/addons/file-editor/icons.pngs/keys.png.buttonicon b/addons/file-editor/icons.pngs/keys.png.buttonicon new file mode 100644 index 0000000000000000000000000000000000000000..e7dd81c6f29ce7ed8a0191b4ecb1bdf747f2cb47 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFE*^Om&@jk-WEHE|YW&%^@FS zsXYgI4;XM9@!Q3_Dy`Awq_qK;aLIqoQ-WVZLbj|BC+#6ar&kKIubsuOvgQu&X%Q~loCIG*V BP09cO literal 0 HcmV?d00001 diff --git a/addons/file-editor/icons.pngs/section.png.buttonicon b/addons/file-editor/icons.pngs/section.png.buttonicon new file mode 100644 index 0000000000000000000000000000000000000000..5fa6183346c4b45479cc4fdd0ff43429332df2bf GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPF5#EtEfFkOiE{-7;bCMGd zF!XqNd9f+52~Swaki6o6L_(y7bE6NNc8g#`rX|DLja cannot be empty! Please, give your file a name.") - func open_filelist(): update_list() FileList.popup() FileList.set_position(OS.get_screen_size()/2 - FileList.get_size()/2) -func open_selected_file(): - update_list() - FileList.mode = FileDialog.MODE_OPEN_FILE - FileList.set_title("Select a file you want to edit") - if FileList.is_connected("file_selected",self,"delete_file"): - FileList.disconnect("file_selected",self,"delete_file") - if not FileList.is_connected("file_selected",self,"open_file"): - FileList.connect("file_selected",self,"open_file") - else: - FileList.disconnect("file_selected",self,"open_file") - FileList.connect("file_selected",self,"open_file") - open_filelist() +func _on_vanillaeditor_text_changed(): + if not OpenFileList.get_item_text(current_file_index).ends_with("(*)"): + OpenFileList.set_item_text(current_file_index,OpenFileList.get_item_text(current_file_index)+"(*)") -func delete_selected_file(): - update_list() - FileList.mode = FileDialog.MODE_OPEN_FILE - FileList.set_title("Select a file you want to delete") - if FileList.is_connected("file_selected",self,"open_file"): - FileList.disconnect("file_selected",self,"open_file") - if not FileList.is_connected("file_selected",self,"delete_file"): - FileList.connect("file_selected",self,"delete_file") - else: - FileList.disconnect("file_selected",self,"delete_file") - FileList.connect("file_selected",self,"delete_file") - open_filelist() - -func delete_file(path : String): - var dir = Directory.new() - dir.remove(path) +func create_new_file(given_path : String): + var current_file = File.new() + current_file.open(given_path,File.WRITE) + if save_as : + current_file.store_line(VanillaEditor.get_node("TextEditor").get_text()) + current_file.close() + open_file(given_path) + +func save_file(current_path : String): + var current_file = File.new() + current_file.open(current_path,File.WRITE) + var current_content = VanillaEditor.get_node("TextEditor").get_text() + if current_content == null: + current_content = "" + current_file.store_line(current_content) + current_file.close() + + current_file_path = current_file_path + + var last_modified = OS.get_datetime_from_unix_time(current_file.get_modified_time(current_path)) + + VanillaEditor.update_lastmodified(last_modified,"save") + OpenFileList.set_item_metadata(current_file_index,[current_content,last_modified,current_path]) + + if OpenFileList.get_item_text(current_file_index).ends_with("(*)"): + OpenFileList.set_item_text(current_file_index,OpenFileList.get_item_text(current_file_index).rstrip("(*)")) + + open_in_inieditor(current_file_path) update_list() +func clean_editor(): + OpenFileName.clear() + OpenFileList.clear() + VanillaEditor.clean_editor() + IniEditor.clean_editor() + +func csv_preview(): + var preview = Preview.instance() + get_parent().get_parent().get_parent().add_child(preview) + preview.popup() + preview.window_title += " ("+current_file_path.get_file()+")" + var lines = VanillaEditor.get_node("TextEditor").get_line_count() + var rows = [] + for i in range(0,lines-1): + rows.append(VanillaEditor.get_node("TextEditor").get_line(i).rsplit(",",false)) + preview.print_csv(rows) + +func bbcode_preview(): + var preview = Preview.instance() + get_parent().get_parent().get_parent().add_child(preview) + preview.popup() + preview.window_title += " ("+current_file_path.get_file()+")" + preview.print_bb(VanillaEditor.get_node("TextEditor").get_text()) + +func markdown_preview(): + var preview = Preview.instance() + get_parent().get_parent().get_parent().add_child(preview) + preview.popup() + preview.window_title += " ("+current_file_path.get_file()+")" + preview.print_markdown(VanillaEditor.get_node("TextEditor").get_text()) + +func html_preview(): + var preview = Preview.instance() + get_parent().get_parent().get_parent().add_child(preview) + preview.popup() + preview.window_title += " ("+current_file_path.get_file()+")" + preview.print_html(VanillaEditor.get_node("TextEditor").get_text()) + +func xml_preview(): + pass + +func json_preview(): + pass + func update_list(): FileList.invalidate() \ No newline at end of file diff --git a/addons/file-editor/scripts/FileScene.gd b/addons/file-editor/scripts/FileScene.gd index ddd1adf..3ee0120 100644 --- a/addons/file-editor/scripts/FileScene.gd +++ b/addons/file-editor/scripts/FileScene.gd @@ -1,10 +1,8 @@ tool extends VBoxContainer -onready var CloseFile = $TopBar/close_btn onready var ReadOnly = $FileInfo/Readonly -onready var FileButton = $TopBar/FileButton.get_popup() onready var TextEditor = $TextEditor @@ -14,8 +12,6 @@ onready var FileList = get_parent().get_parent().get_parent().get_node("FileList onready var ClosingFile = get_parent().get_parent().get_parent().get_node("ConfirmationDialog") -onready var FilePath = $TopBar/filepath - onready var LastModifiedIcon = $FileInfo/lastmodified_icon var current_path = "" @@ -23,121 +19,37 @@ var current_filename = "" var old_file_content = "" var Preview = load("res://addons/file-editor/scenes/Preview.tscn") +signal text_changed() + func _ready(): - FileButton.connect("id_pressed",self,"button_pressed") ClosingFile.connect("confirmed",self,"queue_free") - CloseFile.connect("pressed",self,"close_editor") - ReadOnly.connect("toggled",self,"_on_Readonly_toggled") ReadOnly.set("custom_icons/checked",IconLoader.load_icon_from_name("read")) ReadOnly.set("custom_icons/unchecked",IconLoader.load_icon_from_name("edit")) -func new_file_open(file_path, file_content, last_modified) : - current_path = file_path - var filename_ = file_path.get_file().replace(".","-") - - FilePath.set_text(current_path) - - if get_parent().has_node(filename_): - queue_free() - else: - set_name(filename_) - current_filename = filename_ - old_file_content = file_content - TextEditor.set_text(file_content) - - LastModifiedIcon.texture = IconLoader.load_icon_from_name("saveas") - LastModified.set_text(str(last_modified.hour)+":"+str(last_modified.minute)+" "+str(last_modified.day)+"/"+str(last_modified.month)+"/"+str(last_modified.year)) - +func clean_editor(): + TextEditor.set_text("") + LastModifiedIcon.texture = IconLoader.load_icon_from_name("save") + LastModified.set_text("") FileList.invalidate() +func new_file_open(file_content, last_modified): + TextEditor.set_text(file_content) + update_lastmodified(last_modified,"save") + FileList.invalidate() + +func update_lastmodified(last_modified : Dictionary, icon : String): + LastModified.set_text(str(last_modified.hour)+":"+str(last_modified.minute)+" "+str(last_modified.day)+"/"+str(last_modified.month)+"/"+str(last_modified.year)) + LastModifiedIcon.texture = IconLoader.load_icon_from_name(icon) + func new_file_create(file_name): - set_name(file_name) - current_filename = file_name - FileButton.set_item_disabled(0,true) + TextEditor.set_text("") FileList.invalidate() -func save_file(current_path : String): - var current_file = File.new() - current_file.open(current_path,File.WRITE) - var current_content = TextEditor.get_text() - if current_content == null: - current_content = "" - current_file.store_line(current_content) - current_file.close() - old_file_content = current_content - - var last_modified = OS.get_datetime_from_unix_time(current_file.get_modified_time(current_path)) - LastModified.set_text(str(last_modified.hour)+":"+str(last_modified.minute)+" "+str(last_modified.day)+"/"+str(last_modified.month)+"/"+str(last_modified.year)) - LastModifiedIcon.texture = IconLoader.load_icon_from_name("save") - FilePath.set_text(current_path) - - FileList.invalidate() - -func save_file_as(): - var current_content = TextEditor.get_text() - FileList.mode = FileDialog.MODE_SAVE_FILE - FileList.set_title("Save this file as...") - if FileList.is_connected("file_selected",self,"delete_file"): - FileList.disconnect("file_selected",self,"delete_file") - if not FileList.is_connected("file_selected",self,"save_file"): - FileList.connect("file_selected",self,"save_file") - FileList.current_file = current_filename.replace("-",".") - open_filelist() - old_file_content = current_content - - FileList.invalidate() - -func open_filelist(): - FileList.popup() - FileList.set_position(OS.get_screen_size()/2 - FileList.get_size()/2) - -func button_pressed(id : int): - if id == 0: - save_file(current_path) - elif id == 1: - save_file_as() - elif id == 2: - open_preview() - elif id == 3: - bbcode_preview() - elif id == 4: - markdown_preview() - elif id == 5: - html_preview() - -func open_preview(): - var preview = Preview.instance() - get_parent().get_parent().get_parent().add_child(preview) - preview.popup() - preview.window_title += " ("+current_filename+")" - preview.print_preview(TextEditor.get_text()) - -func bbcode_preview(): - var preview = Preview.instance() - get_parent().get_parent().get_parent().add_child(preview) - preview.popup() - preview.window_title += " ("+current_filename+")" - preview.print_bb(TextEditor.get_text()) - -func markdown_preview(): - var preview = Preview.instance() - get_parent().get_parent().get_parent().add_child(preview) - preview.popup() - preview.window_title += " ("+current_filename+")" - preview.print_markdown(TextEditor.get_text()) - -func html_preview(): - var preview = Preview.instance() - get_parent().get_parent().get_parent().add_child(preview) - preview.popup() - preview.window_title += " ("+current_filename+")" - preview.print_html(TextEditor.get_text()) - func close_editor(): if old_file_content != TextEditor.get_text(): ClosingFile.popup() @@ -154,3 +66,4 @@ func _on_Readonly_toggled(button_pressed): func _on_TextEditor_text_changed(): LastModifiedIcon.texture = IconLoader.load_icon_from_name("saveas") + emit_signal("text_changed") diff --git a/addons/file-editor/scripts/IconLoader.gd b/addons/file-editor/scripts/IconLoader.gd index 068ee1b..a830b0e 100644 --- a/addons/file-editor/scripts/IconLoader.gd +++ b/addons/file-editor/scripts/IconLoader.gd @@ -1,6 +1,9 @@ tool extends Node +func _ready(): + pass + func load_icon_from_name(icon_name : String) -> ImageTexture: var file = File.new() var image = Image.new() diff --git a/addons/file-editor/scripts/IniVisualEditor.gd b/addons/file-editor/scripts/IniVisualEditor.gd new file mode 100644 index 0000000..fb1b34c --- /dev/null +++ b/addons/file-editor/scripts/IniVisualEditor.gd @@ -0,0 +1,274 @@ +extends Control +tool + +onready var Keys = $VBoxContainer/HSplitContainer/VBoxContainer2/keys +onready var Sections = $VBoxContainer/HSplitContainer/VBoxContainer/sections2 + +onready var BtnAddSection = $VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer2/btn_add_section +onready var BtnRemoveSection = $VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer2/btn_remove_section + +onready var BtnAddKey = $VBoxContainer/HSplitContainer/VBoxContainer2/HBoxContainer3/btn_add_key +onready var BtnEditKey = $VBoxContainer/HSplitContainer/VBoxContainer2/HBoxContainer3/btn_edit_key +onready var BtnRemoveKey = $VBoxContainer/HSplitContainer/VBoxContainer2/HBoxContainer3/btn_remove_key + +onready var Section = $Section +onready var Key = $Key + +var selected_key +var selected_section : int = -1 +var root : TreeItem + +var current_file_path : String = "" + +signal update_file() + +func _ready(): + create_table_names() + connect_signals() + load_icons() + clean_editor() + + + +# var metadata = [["name","Godot Engine"],["version","1.0.0"],["color","Light Blue"]] +# load_section("Engine", metadata) + +func connect_signals(): + Sections.connect("item_selected",self,"_on_section_selected") + Sections.connect("nothing_selected",self,"_on_nosection_selected") + + BtnAddSection.connect("pressed",self,"_on_addsection_pressed") + BtnRemoveSection.connect("pressed",self,"_on_removesection_pressed") + + Keys.connect("item_selected",self,"_on_key_selected") + Keys.connect("nothing_selected",self,"_on_nokey_selected") + + BtnAddKey.connect("pressed",self,"_on_addkey_pressed") + BtnRemoveKey.connect("pressed",self,"_on_removekey_pressed") + BtnEditKey.connect("pressed",self,"_on_editkey_pressed") + +func create_table_names(): + create_root() + Keys.hide_root = true + + Keys.set_column_titles_visible(true) + Keys.set_column_title(0,"Name") + Keys.set_column_title(1,"Value") + +func load_icons(): + $VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/sections_icon.texture = IconLoader.load_icon_from_name("sections") + $VBoxContainer/HSplitContainer/VBoxContainer2/HBoxContainer2/keys_icon.texture = IconLoader.load_icon_from_name("keys") + BtnAddSection.icon = IconLoader.load_icon_from_name("add") + BtnAddSection.hint_tooltip = "Add a new Section" + BtnRemoveSection.icon = IconLoader.load_icon_from_name("delete") + BtnRemoveSection.hint_tooltip = "Remove selected Section" + + BtnAddKey.icon = IconLoader.load_icon_from_name("add") + BtnAddKey.hint_tooltip = "Add a new Key" + BtnRemoveKey.icon = IconLoader.load_icon_from_name("delete") + BtnRemoveKey.hint_tooltip = "Remove selected Key" + BtnEditKey.icon = IconLoader.load_icon_from_name("edit_") + BtnEditKey.hint_tooltip = "Edit selected Key" + +func _on_addsection_pressed(): + Section.get_node("Container/section/_name").show() + Section.window_title = "Add a new Section" + if not Section.is_connected("confirmed",self,"new_section"): + Section.connect("confirmed",self,"new_section") + if Section.is_connected("confirmed",self,"remove_section"): + Section.disconnect("confirmed",self,"remove_section") + Section.popup() + +func _on_removesection_pressed(): + Section.get_node("Container").hide() + Section.window_title = "Remove selected Section" + Section.dialog_text = "Are you sure you want to remove this Section?" + if not Section.is_connected("confirmed",self,"remove_section"): + Section.connect("confirmed",self,"remove_section") + if Section.is_connected("confirmed",self,"new_section"): + Section.disconnect("confirmed",self,"new_section") + Section.popup() + +func _on_addkey_pressed(): + Key.get_node("data").show() + Key.get_node("data/HBoxContainer/name").editable = true + Key.get_node("data/HBoxContainer/name").set_text("") + Key.window_title = "Add a new Key" + Key.dialog_text = "" + if not Key.is_connected("confirmed",self,"new_key"): + Key.connect("confirmed",self,"new_key") + if Key.is_connected("confirmed",self,"edit_key"): + Key.disconnect("confirmed",self,"edit_key") + if Key.is_connected("confirmed",self,"remove_key"): + Key.disconnect("confirmed",self,"remove_key") + Key.popup() + +func _on_removekey_pressed(): + Key.get_node("data").hide() + Key.window_title = "Delete selected Key" + Key.dialog_text = "Are you sure you want to remove the selected Key?" + if not Key.is_connected("confirmed",self,"remove_key"): + Key.connect("confirmed",self,"remove_key") + if Key.is_connected("confirmed",self,"edit_key"): + Key.disconnect("confirmed",self,"edit_key") + if Key.is_connected("confirmed",self,"new_key"): + Key.disconnect("confirmed",self,"new_key") + Key.popup() + +func _on_editkey_pressed(): + Key.get_node("data").show() + Key.get_node("data/HBoxContainer/name").editable = false + Key.get_node("data/HBoxContainer/name").set_text(str(selected_key.get_text(0))) + Key.window_title = "Edit selected Key" + Key.dialog_text = "" + if not Key.is_connected("confirmed",self,"edit_key"): + Key.connect("confirmed",self,"edit_key") + if Key.is_connected("confirmed",self,"remove_key"): + Key.disconnect("confirmed",self,"remove_key") + if Key.is_connected("confirmed",self,"new_key"): + Key.disconnect("confirmed",self,"new_key") + Key.popup() + +func clean_editor(): + Keys.clear() + Sections.clear() + selected_section = -1 + BtnAddKey.disabled = true + if current_file_path == "": + BtnAddSection.disabled = true + else: + BtnAddSection.disabled = false + BtnEditKey.disabled = true + BtnRemoveKey.disabled = true + BtnRemoveSection.disabled = true + + create_root() + +func open_file(filemap : Array): + clean_editor() + for section in filemap: + load_sections(section[0],section[1]) + +func new_section(): + var file = ConfigFile.new() + file.load(current_file_path) + + var section_name = str(Section.get_node("Container/section/_name").get_text()) + var key_name = str(Section.get_node("Container/key/_name").get_text()) + var key_value = Section.get_node("Container/value/_value").get_text() + + if section_name and key_name and key_value: + file.set_value(section_name,key_name,key_value) + file.save(current_file_path) + + load_sections(section_name,[[key_name,key_value]]) + + emit_signal("update_file") + else: + print("Section <",section_name,"> with Key name: <",key_name,"> and Key value: <",key_value,"> not valid.") + +func remove_section(): + var file = ConfigFile.new() + file.load(current_file_path) + var current_section = Sections.get_item_text(selected_section) + file.erase_section(current_section) + Sections.remove_item(selected_section) + file.save(current_file_path) + + emit_signal("update_file") + +func new_key(): + var key_name = str(Key.get_node("data/HBoxContainer/name").get_text()) + var key_value = Key.get_node("data/HBoxContainer2/value").get_text() + if key_name and key_value: + + var file = ConfigFile.new() + file.load(current_file_path) + + var current_section = Sections.get_item_text(selected_section) + + file.set_value(current_section,key_name,key_value) + file.save(current_file_path) + + load_keys_selected_section([[key_name,key_value]]) + + file.save(current_file_path) + + emit_signal("update_file") + else: + print("Key name: <",key_name,"> with Key value: <",key_value,"> not valid.") + +func remove_key(): + var section = Sections.get_item_text(selected_section) + var sectionmetadata = Sections.get_item_metadata(selected_section) + + for meta in sectionmetadata: + if meta.has(selected_key.get_text(0)): + sectionmetadata.erase(meta) + + Sections.set_item_metadata(selected_section,sectionmetadata) + + if Sections.get_item_metadata(selected_section) == []: + Sections.remove_item(selected_section) + + var file = ConfigFile.new() + file.load(current_file_path) + file.set_value(section,selected_key.get_text(0),null) + file.save(current_file_path) + + Keys.clear() + create_root() + load_keys_selected_section(sectionmetadata) + + emit_signal("update_file") + +func edit_key(): + remove_key() + new_key() + +# load a section with custom fields @section_name = name of section ; @section_metadata = keys of this section with keys' properties +func load_sections(section_name : String, section_metadata : Array): + Sections.add_item(section_name,IconLoader.load_icon_from_name("section"),true) + Sections.set_item_metadata(Sections.get_item_count()-1,section_metadata) + +# load a key of a selected section to fill the "keys" list +func load_keys_selected_section(metadata : Array): + for key in metadata: + var key_item = Keys.create_item(root) + key_item.set_text(0,key[0]) + key_item.set_text(1,key[1]) + +func _on_section_selected(index : int): + Keys.clear() + create_root() + BtnRemoveSection.disabled = false + BtnAddSection.disabled = false + BtnAddKey.disabled = false + BtnRemoveKey.disabled = true + BtnEditKey.disabled = true + + selected_section = index + if Sections.get_item_metadata(index): + load_keys_selected_section(Sections.get_item_metadata(index)) + +func _on_key_selected(): + selected_key = Keys.get_selected() + BtnRemoveKey.disabled = false + BtnEditKey.disabled = false + +func _on_nosection_selected(): + BtnRemoveKey.disabled = true + BtnAddKey.disabled = true + BtnEditKey.disabled = true + BtnRemoveSection.disabled = true + Keys.clear() + selected_section = -1 + +func _on_nokey_selected(): + BtnRemoveKey.disabled = true + BtnEditKey.disabled = true + +func create_root(): + root = Keys.create_item() + root.set_text(0,"KEY_NAME") + root.set_text(1,"KEY_VALUE") \ No newline at end of file diff --git a/addons/file-editor/scripts/LastOpenedFiles.gd b/addons/file-editor/scripts/LastOpenedFiles.gd new file mode 100644 index 0000000..063f0e6 --- /dev/null +++ b/addons/file-editor/scripts/LastOpenedFiles.gd @@ -0,0 +1,33 @@ +tool +extends Node + +const lastopenedfile_path : String = "res://addons/file-editor/lastopenedfiles.lastcfg" + +func _ready(): + pass + +func store_opened_files(filecontainer : Control): + var file = ConfigFile.new() + file.load(lastopenedfile_path) + for child in range(0,filecontainer.get_item_count()): + var filepath = filecontainer.get_item_metadata(child)[2] + file.set_value("Opened",filepath.get_file(),filepath) + + file.save(lastopenedfile_path) + +func remove_opened_file(index : int , filecontainer : Control): + var file = ConfigFile.new() + file.load(lastopenedfile_path) + var filepath = filecontainer.get_item_metadata(index)[2] + file.set_value("Opened",filepath.get_file(),null) + file.save(lastopenedfile_path) + +func load_opened_files() -> Array: + var file = ConfigFile.new() + file.load(lastopenedfile_path) + var keys = [] + if file.has_section("Opened"): + var openedfiles = file.get_section_keys("Opened") + for openedfile in openedfiles: + keys.append([openedfile,file.get_value("Opened",openedfile)]) + return keys \ No newline at end of file diff --git a/addons/file-editor/scripts/Preview.gd b/addons/file-editor/scripts/Preview.gd index 8934fef..83e8fd6 100644 --- a/addons/file-editor/scripts/Preview.gd +++ b/addons/file-editor/scripts/Preview.gd @@ -2,15 +2,19 @@ tool extends WindowDialog onready var TextPreview = $Container/TextPreview +onready var TablePreview = $Container/TablePreview func _ready(): - pass + TextPreview.hide() + TablePreview.hide() func print_preview(content : String): TextPreview.append_bbcode(content) + TextPreview.show() func print_bb(content : String): TextPreview.append_bbcode(content) + TextPreview.show() func print_markdown(content : String): var result = "" @@ -49,7 +53,7 @@ func print_markdown(content : String): for res in result: coded.append(res.get_string("coded")) - regex.compile("[+-](?\\s.*)") + regex.compile("[+-*](?\\s.*)") result = regex.search_all(content) if result: for res in result: @@ -89,8 +93,11 @@ func print_markdown(content : String): content = content.replace("-"+element,"[indent]-"+element+"[/indent]") if content.find("+ "+element): content = content.replace("+"+element,"[indent]-"+element+"[/indent]") + if content.find("* "+element): + content = content.replace("+"+element,"[indent]-"+element+"[/indent]") TextPreview.append_bbcode(content) + TextPreview.show() func print_html(content : String): content = content.replace("","[i]") @@ -99,6 +106,10 @@ func print_html(content : String): content = content.replace("","[/b]") content = content.replace("","[u]") content = content.replace("","[/u]") + content = content.replace("","[u]") + content = content.replace("","[/u]") + content = content.replace("","[s]") + content = content.replace("","[/s]") content = content.replace('',"]") content = content.replace("","[/url]") @@ -111,6 +122,21 @@ func print_html(content : String): content = content.replace("","[/center]") TextPreview.append_bbcode(content) + TextPreview.show() + +func print_csv(rows : Array): + TablePreview.columns = rows[0].size() + for item in rows: + for string in item: + var label = Label.new() + label.text = str(string) + label.set_h_size_flags(SIZE_EXPAND) + label.set_align(1) + label.set_valign(1) + TablePreview.add_child(label) + + + TablePreview.show() func _on_Preview_popup_hide(): queue_free() diff --git a/addons/file-editor/scripts/file-editor.gd b/addons/file-editor/scripts/file-editor.gd index 576a861..444da96 100644 --- a/addons/file-editor/scripts/file-editor.gd +++ b/addons/file-editor/scripts/file-editor.gd @@ -9,12 +9,15 @@ var IconLoader = preload("res://addons/file-editor/scripts/IconLoader.gd") func _enter_tree(): add_autoload_singleton("IconLoader","res://addons/file-editor/scripts/IconLoader.gd") + add_autoload_singleton("LastOpenedFiles","res://addons/file-editor/scripts/LastOpenedFiles.gd") get_editor_interface().get_editor_viewport().add_child(doc) doc.hide() func _exit_tree(): + doc.clean_editor() get_editor_interface().get_editor_viewport().remove_child(doc) remove_autoload_singleton("IconLoader") + remove_autoload_singleton("LastOpenedFiles") func has_main_screen(): return true From 435ed9899ed5be0c8c92b5fe2927ed0295c324b0 Mon Sep 17 00:00:00 2001 From: fenix-hub Date: Tue, 8 Oct 2019 00:26:05 +0200 Subject: [PATCH 3/5] Add files via upload --- README.md | 9 +++- VERSION.md | 152 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 94 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index e9ccbcc..6c35125 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ Check my **[Discord](https://discord.gg/KnJGY9S)** to stay updated on this repository. *(Recommended since the AssetLibrary is not automatically updated)* +This plugin is now supported in [Godot Extended Library Discord](https://discord.gg/JNrcucg), check out the [Godot Extended Library Project](https://github.com/godot-extended-libraries)! + # Godot Text Editor A little plugin to easy-way manage your text files inside your project folder. Author: *"Nicolo (fenix) Santilio"* -Version: *1.2.1* +Version: *1.4.3* Godot Version: *3.1.1-stable* **This repository was pushed directly from Godot Engine Editor thanks to [GitHub Integration](https://github.com/fenix-hub/godot-engine.github-integration)!** @@ -19,7 +21,7 @@ When opening / creating a file, the editor will open and you will be able to edi You can just *Save* the file, or *Save file As* a new file (if it is a new file but also to make some copies). You will also be able to see some informations about the file you are editing (time and date of last edit) and you can set your editor to *Read Only* if you don't want to make changes but still read the content of the file. Multiple files can be opened in different tabs. -![preview](https://i.imgur.com/LcrGCGS.png) +![preview1](https://i.imgur.com/mlh1rOX.png)![preview2](https://i.imgur.com/od5nQff.png) ## How do I install it? **Manual** @@ -45,6 +47,8 @@ You can find this plugin in the AssetLib of Godot Engine Editor. Just download i + "*.sql ; SQL database file", + "*.json ; JavaScript Object Notation File", + "*.html ; HyperText Markup Language" ++ "*.cfg ; Configuration File" ++ "*.ini ; Initialization File (same as .cfg Configuration File)" #### Current version To check all the features included in the current version, please read the [VERSION file](./VERSION.md) @@ -56,3 +60,4 @@ To check all the features I'm currently working on, please read the [TODO file]( This addon was built for a **personal use** intention. It was released as an open source plugin in the hope that it could be useful to the Godot Engine Community. As a "work in progress" project, there is *no warranty* for any eventual issue and bug that may broke your project. I don't assume any responsibility for possible corruptions of your project files. It is always advisable to keep a copy of your files and check any changes. + diff --git a/VERSION.md b/VERSION.md index c7f740f..b6c087f 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1,65 +1,87 @@ -**version 0.0.1** -*added* -- Plugin Created - ------------------------ - -**version 0.1.1** -*added* -- "Create new File" option -- "Open File" option -- "Delete File" option -- "Save File" option -- "Save File As.." option - ------------------------ - -**version 0.1.2** -*fixed* -- Repository Installation, folder order - ------------------------ - -**version 0.2.5** -*removed* -- Old layout - -*added* -- New Layout -- Last modified time and date -- Tabs - ------------------------ - -**version 0.3.1** -*added* -- Version check -- Preview support -- Context menu in editor -- BBCode converter -- Light Mardkwon converter (DEMO) - ------------------------ - -**version 1.2.1** -*removed* -- Old layout, the plugin won't appear in docs -- Icons file extensions, plugin size reduced -- Old Mardkwon preview method - -*added* -- More supported files -1. "*.dat ; Data File", -2. "*.xml ; XML File", -3. "*.sql ; SQL database file", -4. "*.json ; JavaScript Object Notation File", -5. "*.html ; HyperText Markup Language -- New Markdown preview method ( Markdown -> BBCode converter) -- New HTML preview method ( HTML -> BBCode converter) -- New Plugin Layout: a new ButtonTool "File" in you TopBar will appear -- Error check -- Message popups for closing unsaved files - ------------------------ - - +**version 0.0.1** +*added* +- Plugin Created + +----------------------- + +**version 0.1.1** +*added* +- "Create new File" option +- "Open File" option +- "Delete File" option +- "Save File" option +- "Save File As.." option + +----------------------- + +**version 0.1.2** +*fixed* +- Repository Installation, folder order + +----------------------- + +**version 0.2.5** +*removed* +- Old layout + +*added* +- New Layout +- Last modified time and date +- Tabs + +----------------------- + +**version 0.3.1** +*added* +- Version check +- Preview support +- Context menu in editor +- BBCode converter +- Light Mardkwon converter (DEMO) + +----------------------- + +**version 1.2.1** +*removed* +- Old layout, the plugin won't appear in docs +- Icons file extensions, plugin size reduced +- Old Mardkwon preview method + +*added* +- More supported files +1. "*.dat ; Data File", +2. "*.xml ; XML File", +3. "*.sql ; SQL database file", +4. "*.json ; JavaScript Object Notation File", +5. "*.html ; HyperText Markup Language +- New Markdown preview method ( Markdown -> BBCode converter) +- New HTML preview method ( HTML -> BBCode converter) +- New Plugin Layout: a new ButtonTool "File" in you TopBar will appear +- Error check +- Message popups for closing unsaved files + +----------------------- + +**version 1.4.3** +*removed* +- Old layout + +*added* +- More supported files: +1. "*.cfg ; Configuration File", +2. "*.ini ; Initialization File (same as .cfg Configuration File)", +- Added some sample files +- Memorize system of last opened files +- Added *Editor* button , you can now choose which editor to use: +1. Vanilla Editor (simple text editor) +2. Cfg/Ini Editor (table editor for Cfg/Ini files) + Editors are automatically updated, so if you update a cfg/ini file in the Vanilla Editor it will be updated in the Cfg/Ini Editor, and viceversa +- Added editor shorcuts: +1. *Ctrl + N* (new file) +2. *Ctrl + O* (open file) +3. *Ctrl + Alt + C* (close file) +4. *Ctrl + S* (save file) +5. *Ctrl + Alt + S* (save file as...) +6. *Ctrl + D* (delete file) +7. *Ctrl + 1* (Vanilla Editor) +8. *Ctrl + 3* (Cfg/Ini Editor) From da9c27d45d8d691afd605cb6e46747fd14f64692 Mon Sep 17 00:00:00 2001 From: fenix-hub Date: Tue, 8 Oct 2019 00:26:29 +0200 Subject: [PATCH 4/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c35125..8d127e5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Check my **[Discord](https://discord.gg/KnJGY9S)** to stay updated on this repository. *(Recommended since the AssetLibrary is not automatically updated)* -This plugin is now supported in [Godot Extended Library Discord](https://discord.gg/JNrcucg), check out the [Godot Extended Library Project](https://github.com/godot-extended-libraries)! +This plugin is now supported in [Godot Extended Libraries Discord](https://discord.gg/JNrcucg), check out the [Godot Extended Libraries Project](https://github.com/godot-extended-libraries)! # Godot Text Editor A little plugin to easy-way manage your text files inside your project folder. From 3a51301e154ce3e9e715090918919f35d09e5d73 Mon Sep 17 00:00:00 2001 From: fenix-hub Date: Tue, 8 Oct 2019 00:28:08 +0200 Subject: [PATCH 5/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d127e5..a9cdd5d 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ You can find this plugin in the AssetLib of Godot Engine Editor. Just download i To check all the features included in the current version, please read the [VERSION file](./VERSION.md) #### Upcoming features -To check all the features I'm currently working on, please read the [TODO file](./TODO.md) +To check all the features I'm currently working on, please read the ["To Do" Column](https://github.com/fenix-hub/godot-engine.text-editor/projects/1#column-6702439) # Disclaimer This addon was built for a **personal use** intention. It was released as an open source plugin in the hope that it could be useful to the Godot Engine Community.