1 module editor;
2 
3 import pngtext.pngtext;
4 
5 import utils.misc;
6 
7 import qui.qui;
8 import qui.widgets;
9 
10 import std.stdio;
11 import std.conv : to;
12 
13 /// The in-terminal text editor using qui.widgets.MemoWidget
14 class Editor{
15 private:
16 	/// the terminal
17 	QTerminal _terminal;
18 	/// the text editor
19 	MemoWidget _editor;
20 	/// to occupy space on left of 
21 	SplitterWidget _statusBarLeft;
22 	/// shows the shortcut keys, sits next to _statusLabel
23 	TextLabelWidget _shortcutLabel;
24 	/// Contains the and _shortcutLabel
25 	QLayout _statusBar;
26 	/// filename of original png
27 	string _inputPng;
28 	/// filename of output png
29 	string _outputPng;
30 public:
31 	/// constructor
32 	/// 
33 	/// if text only has to be displayed, use readOnly=true, and no need to specify saveAs, just leave it blank
34 	this(string image, string saveAs, bool readOnly=false){
35 		_inputPng = image;
36 		_outputPng = saveAs;
37 		// setup the terminal
38 		_terminal = new QTerminal(QLayout.Type.Vertical);
39 		_statusBar = new QLayout(QLayout.Type.Horizontal);
40 		_editor = new MemoWidget(!readOnly);
41 		_statusBarLeft = new SplitterWidget();
42 		_shortcutLabel = new TextLabelWidget();
43 		// set up each widget
44 		// first comes the editor:
45 		_editor.wantsTab = false;
46 		_editor.lines.loadArray(separateLines((cast(string)cast(char[])readDataFromPng(_inputPng)).to!dstring));//load the lines
47 		// now comes the _statusLabel
48 		// now the _shortcutLabel
49 		_shortcutLabel.textColor = DEFAULT_BG;
50 		_shortcutLabel.backgroundColor = DEFAULT_FG;
51 		_shortcutLabel.caption = "Ctrl+C - Save & Exit";
52 		_shortcutLabel.size.maxWidth = _shortcutLabel.caption.length;
53 		// put both of these in _statusBar, and set it up too
54 		_statusBar.addWidget([_statusBarLeft, _shortcutLabel]);
55 		_statusBar.size.maxHeight = 1;
56 		_statusBar.size.minHeight = 1;
57 		// put all those in QTerminal
58 		_terminal.addWidget([_editor, _statusBar]);
59 		// register all widgets
60 		_terminal.registerWidget([_editor, _statusBar, _statusBarLeft, _shortcutLabel]);
61 		// and its done, ready to start*/
62 	}
63 	/// destructor
64 	~this(){
65 		.destroy(_terminal);
66 		.destroy(_editor);
67 		.destroy(_statusBar);
68 		.destroy(_statusBarLeft);
69 		.destroy(_shortcutLabel);
70 	}
71 	/// runs the editor
72 	/// 
73 	/// Returns: false if there were errors(s)
74 	bool run(){
75 		_terminal.run;
76 		// save
77 		const dstring[] lines = _editor.lines.toArray;
78 		string[] utf8Lines;
79 		utf8Lines.length = lines.length;
80 		foreach (i, line; lines){
81 			utf8Lines[i] = line.to!string;
82 		}
83 		ubyte[] data;
84 		uinteger writeTo = 0;
85 		foreach (line; utf8Lines){
86 			data.length += line.length+1;
87 			foreach (ch; line){
88 				data[writeTo] = cast(ubyte)ch;
89 				writeTo++;
90 			}
91 			data[writeTo] = '\n';
92 			writeTo++;
93 		}
94 		const string[] errors = writeDataToPng(_inputPng, _outputPng, data);
95 		if (errors.length){
96 			stderr.writeln("Errors while writing to image:");
97 			foreach (err; errors){
98 				stderr.writeln(err);
99 			}
100 			return false;
101 		}
102 		return true;
103 	}
104 
105 }
106 
107 /// reads a single string into string[], separating the lines
108 private dstring[] separateLines(dstring s){
109 	dstring[] r;
110 	for(uinteger i = 0, readFrom = 0; i < s.length; i ++){
111 		if (s[i] == '\n'){
112 			r ~= s[readFrom .. i].dup;
113 			readFrom = i+1;
114 		}else if (i+1 == s.length){
115 			r ~= s[readFrom .. i + 1].dup;
116 		}
117 	}
118 	return r;
119 }