1 /* Written by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 import std
.concurrency
;
25 import arsd
.simpledisplay
;
31 import iv
.nanovg
.oui
.blendish
;
41 // ////////////////////////////////////////////////////////////////////////// //
42 BookText
loadBook (string fname
) {
43 __gshared
bool eliteShipsLoaded
= false;
46 //import core.memory : GC;
47 fname
= fname
.expandTilde
.absolutePath
;
48 //writeln("loading '", fname, "'...");
50 auto stt
= MonoTime
.currTime
;
51 auto book
= new BookText(fname
);
54 { import std
.stdio
; writeln("loaded: '", fname
, "' in ", (MonoTime
.currTime
-stt
).total
!"msecs", " milliseconds"); }
59 foreach (string line
; VFile(buildPath(RcDir
, ".lastfile")).byLineCopy
) {
63 while (lines
.length
&& lines
[$-1].isComment
) {
64 comments
~= lines
[$-1];
65 lines
= lines
[0..$-1];
69 } catch (Exception
) {}
70 try { import std
.file
; mkdirRecurse(RcDir
); } catch (Exception
) {}
71 auto fo
= VFile(buildPath(RcDir
, ".lastfile"), "w");
72 foreach (string s
; lines
) fo
.writeln(s
);
73 foreach_reverse (string s
; comments
) fo
.writeln(s
);
76 stateFileName
= buildPath(RcDir
, fname
.baseName
~".rc");
78 if (!eliteShipsLoaded
) {
80 eliteShipsLoaded
= true;
87 // ////////////////////////////////////////////////////////////////////////// //
88 public void formatBook (BookText book
, LayText lay
) {
89 assert(book
!is null);
91 static void fixFont (LayText lay
) {
92 if (lay
.fontStyle
.italic
) {
93 if (lay
.fontStyle
.bold
) lay
.fontStyle
.fontface
= lay
.fontFace("textz");
94 else lay
.fontStyle
.fontface
= lay
.fontFace("texti");
95 } else if (lay
.fontStyle
.bold
) {
96 if (lay
.fontStyle
.italic
) lay
.fontStyle
.fontface
= lay
.fontFace("textz");
97 else lay
.fontStyle
.fontface
= lay
.fontFace("textb");
99 lay
.fontStyle
.fontface
= lay
.fontFace("text");
103 static void setNormalStyle (LayText lay
) {
104 lay
.fontStyle
.flags
= 0;
105 lay
.fontStyle
.fontsize
= fsizeText
;
106 lay
.lineStyle
.leftpad
= 0;
107 lay
.lineStyle
.rightpad
= 0;
108 lay
.lineStyle
.paraIndent
= 3;
109 if (optJustify
) lay
.lineStyle
.setJustify
; else lay
.lineStyle
.setLeft
;
113 static void setTitleStyle (LayText lay
) {
114 lay
.fontStyle
.flags
= 0;
115 lay
.fontStyle
.fontsize
= fsizeText
+4;
116 lay
.lineStyle
.leftpad
= 0;
117 lay
.lineStyle
.rightpad
= 0;
118 lay
.lineStyle
.paraIndent
= 0;
119 lay
.lineStyle
.setCenter
;
123 static void setSubtitleStyle (LayText lay
) {
124 lay
.fontStyle
.flags
= 0;
125 lay
.fontStyle
.fontsize
= fsizeText
+2;
126 lay
.lineStyle
.leftpad
= 0;
127 lay
.lineStyle
.rightpad
= 0;
128 lay
.lineStyle
.paraIndent
= 0;
129 lay
.lineStyle
.setCenter
;
133 static void setCiteStyle (LayText lay
) {
134 lay
.fontStyle
.flags
= 0;
135 lay
.fontStyle
.fontsize
= fsizeText
;
136 lay
.fontStyle
.italic
= true;
137 lay
.lineStyle
.leftpad
= fsizeText
*4;
138 lay
.lineStyle
.rightpad
= fsizeText
*4;
139 lay
.lineStyle
.paraIndent
= 3;
140 if (optJustify
) lay
.lineStyle
.setJustify
; else lay
.lineStyle
.setLeft
;
144 static void setEpigraphStyle (LayText lay
) {
145 lay
.fontStyle
.flags
= 0;
146 lay
.fontStyle
.fontsize
= fsizeText
;
147 lay
.fontStyle
.italic
= true;
148 lay
.lineStyle
.leftpad
= lay
.width
/2;
149 lay
.lineStyle
.rightpad
= 0;
150 lay
.lineStyle
.paraIndent
= 0;
151 lay
.lineStyle
.setRight
;
155 static void setPoemStyle (LayText lay
) {
156 lay
.fontStyle
.flags
= 0;
157 lay
.fontStyle
.fontsize
= fsizeText
;
158 lay
.fontStyle
.italic
= true;
159 lay
.lineStyle
.leftpad
= fsizeText
*4;
160 lay
.lineStyle
.rightpad
= 0;
161 lay
.lineStyle
.paraIndent
= 0;
162 lay
.lineStyle
.setLeft
;
166 void dumpTree (Tag ct
) {
167 while (ct
!is null) {
168 { import std
.stdio
; writeln("tag: ", ct
.name
); }
173 void badTag (Tag tag
, string msg
=null) {
174 import std
.string
: format
, indexOf
;
175 assert(tag
!is null);
177 if (msg
.length
== 0) msg
= "invalid tag: '%s'";
178 if (msg
.indexOf("%s") >= 0) {
179 throw new Exception(msg
.format(tag
.name
));
181 throw new Exception(msg
);
185 void putParaContentInternal (Tag ct
, int boldc
=0, int italicc
=0, int underc
=0) {
186 if (ct
is null) return;
188 case "strong": ++boldc
; lay
.fontStyle
.bold
= true; break;
189 case "emphasis": ++italicc
; lay
.fontStyle
.italic
= true; break;
190 case "image": return;
191 case "style": return;
192 case "a": ++underc
; lay
.fontStyle
.underline
= true; break;
193 case "": lay
.put(ct
.text
); return;
197 foreach (Tag tag
; ct
.children
) putParaContentInternal(tag
, boldc
, italicc
, underc
);
199 case "strong": if (--boldc
== 0) lay
.fontStyle
.bold
= false; break;
200 case "emphasis": if (--italicc
== 0) lay
.fontStyle
.italic
= false; break;
201 case "a": if (--underc
== 0) lay
.fontStyle
.underline
= false; break;
207 void putTagContents (Tag ct
) {
208 if (ct
is null) return;
209 foreach (Tag tag
; ct
.children
) putParaContentInternal(tag
);
213 void putParas (Tag ct
) {
214 foreach (Tag tag
; ct
.children
) {
215 if (tag
.name
.length
== 0) continue;
216 if (tag
.name
== "p") putTagContents(tag
);
217 else if (tag
.name
== "empty-line") lay
.endLine();
218 else if (tag
.name
== "text-author") {}
223 void putAuthor (Tag ct
) {
224 foreach (Tag tag
; ct
.children
) {
225 if (tag
.name
.length
== 0) continue;
226 if (tag
.name
== "text-author") {
233 void putCite (Tag ct
) {
235 scope(exit
) lay
.popStyles
;
241 void putEpigraph (Tag ct
) {
243 scope(exit
) lay
.popStyles
;
244 setEpigraphStyle(lay
);
249 void putStanza (Tag ct
) {
250 foreach (Tag tag
; ct
.children
) {
251 if (tag
.name
.length
== 0) continue;
252 if (tag
.name
== "text-author") continue;
253 if (tag
.name
== "title") badTag(tag
, "titles in stanzas are not supported yet");
254 if (tag
.name
== "subtitle") badTag(tag
, "subtitles in stanzas are not supported yet");
255 if (tag
.name
== "epigraph") badTag(tag
, "epigraphs in poems are not supported yet");
256 if (tag
.name
== "date") continue;
257 if (tag
.name
== "v") { putTagContents(tag
); continue; }
262 void putPoem (Tag ct
) {
264 scope(exit
) lay
.popStyles
;
266 // put epigraph (not yet)
267 // put title and subtitle
268 foreach (Tag tag
; ct
.children
) {
269 if (tag
.name
== "title") {
271 scope(exit
) lay
.popStyles
;
273 lay
.endPara(); // space
274 } else if (tag
.name
== "subtitle") {
276 scope(exit
) lay
.popStyles
;
278 lay
.endPara(); // space
281 foreach (Tag tag
; ct
.children
) {
282 if (tag
.name
.length
== 0) continue;
283 if (tag
.name
== "text-author") continue;
284 if (tag
.name
== "title") continue;
285 if (tag
.name
== "subtitle") continue;
286 if (tag
.name
== "epigraph") badTag(tag
, "epigraphs in poems are not supported yet");
287 if (tag
.name
== "date") continue;
288 if (tag
.name
== "stanza") { putStanza(tag
); lay
.put(LayText
.EndParaCh
); continue; }
294 void putSection (Tag sc
) {
295 bool sectionRegistered
= false;
297 void registerSection (Tag tag
) {
299 import std.conv : to;
300 if (sectionRegistered) return;
301 sectionRegistered = true;
302 string text = tag.textContent.xstrip;
303 lay.sections ~= lay.curWordIndex;
305 while (text.length) {
306 char ch = text.ptr[0];
308 if (ch <= ' ' || ch == 127) {
309 if (name.length == 0) continue;
310 if (ch != '\n') ch = ' ';
312 if (name[$-1] > ' ') name ~= "\n...";
315 if (name[$-1] > ' ') name ~= ' ';
322 if (name.length == 0) name = "* * *";
323 lay.sectionNames ~= name.to!dstring;
327 foreach (Tag tag
; sc
.children
) {
328 if (tag
.name
.length
== 0) continue;
329 if (tag
.name
== "title") {
331 scope(exit
) lay
.popStyles
;
333 registerSection(tag
);
335 } else if (tag
.name
== "subtitle") {
337 scope(exit
) lay
.popStyles
;
338 setSubtitleStyle(lay
);
339 registerSection(tag
);
341 } else if (tag
.name
== "epigraph") {
343 scope(exit
) lay
.popStyles
;
344 setEpigraphStyle(lay
);
346 } else if (tag
.name
== "section") {
348 } else if (tag
.name
== "p") {
350 } else if (tag
.name
== "empty-line") {
351 lay
.put(LayText
.EndParaCh
);
352 } else if (tag
.name
== "cite") {
354 } else if (tag
.name
== "table") {
356 scope(exit
) lay
.popStyles
;
357 lay
.fontStyle
.fontsize
+= 8;
358 lay
.lineStyle
.setCenter
;
359 lay
.put("TABLE SKIPPED");
361 } else if (tag
.name
== "poem") {
363 } else if (tag
.name
== "image") {
364 } else if (tag
.name
== "style") {
371 foreach (Tag tag
; book
.content
.children
) {
372 if (tag
.name
== "title") {
374 scope(exit
) lay
.popStyles
;
379 } else if (tag
.name
== "subtitle") {
381 scope(exit
) lay
.popStyles
;
385 } else if (tag
.name
== "epigraph") {
387 } else if (tag
.name
== "section") {
389 scope(exit
) lay
.popStyles
;
399 // ////////////////////////////////////////////////////////////////////////// //
400 private __gshared LayFontStash laf
; // layouter font stash
403 // ////////////////////////////////////////////////////////////////////////// //
404 private void loadFmtFonts () {
405 laf
= new LayFontStash();
407 laf
.addFont("text", textFontName
);
408 laf
.addFont("texti", textiFontName
);
409 laf
.addFont("textb", textbFontName
);
410 laf
.addFont("textz", textzFontName
);
412 laf
.addFont("mono", monoFontName
);
413 laf
.addFont("monoi", monoiFontName
);
414 laf
.addFont("monob", monobFontName
);
415 laf
.addFont("monoz", monozFontName
);
419 // ////////////////////////////////////////////////////////////////////////// //
421 struct ReformatWork
{
422 shared(BookText
) booktext
;
427 struct ReformatWorkComplete
{
429 shared(BookText
) booktext
;
430 shared(LayText
) laytext
;
437 // ////////////////////////////////////////////////////////////////////////// //
438 void reformatThreadFn (Tid ownerTid
) {
443 int newW
= -1, newH
= -1;
450 //{ import std.stdio; writeln("reformat request received..."); }
451 book
= cast(BookText
)w
.booktext
;
452 newFileName
= w
.bookFileName
;
455 if (newW
< 1) newW
= 1;
456 if (newH
< 1) newH
= 1;
462 if (!doQuit
&& newW
> 0 && newH
> 0) {
465 //writeln("loading new book: '", newFileName, "'");
466 book
= loadBook(newFileName
);
469 int maxWidth
= newW
-4-2-BND_SCROLLBAR_WIDTH
-2;
470 if (maxWidth
< 64) maxWidth
= 64;
473 //writeln("layouting...");
474 auto stt
= MonoTime
.currTime
;
475 auto lay
= new LayText(laf
, maxWidth
);
477 book
.formatBook(lay
);
479 { import std
.stdio
; writeln("layouted in ", (MonoTime
.currTime
-stt
).total
!"msecs", " milliseconds"); }
480 auto res
= ReformatWorkComplete(newW
, newH
, cast(shared)book
, cast(shared)lay
);
482 } catch (Throwable e
) {
483 // here, we are dead and fucked (the exact order doesn't matter)
484 import core
.stdc
.stdlib
: abort
;
485 import core
.stdc
.stdio
: fprintf
, stderr
;
486 import core
.memory
: GC
;
487 import core
.thread
: thread_suspendAll
;
488 GC
.disable(); // yeah
489 thread_suspendAll(); // stop right here, you criminal scum!
490 auto s
= e
.toString();
491 fprintf(stderr
, "\n=== FATAL ===\n%.*s\n", cast(uint)s
.length
, s
.ptr
);
492 abort(); // die, you bitch!
496 send(ownerTid
, QuitWork());