[doc_tool] Added ability to mark all chapters at once as done.
[hsd_doku_tool.git] / src / Model.cxx
1 ///////////////////////////////////////////////////////////////////////////
2 // Workfile: Model.cxx
3 // Author: Daniel Giritzer <daniel@giritzer.eu>
4 // Date: 25.03.2018
5 // Description: Diese Klasse stellt die Daten und Programmlogik dieses
6 //              Programmes dar.
7 // Remarks:
8 ///////////////////////////////////////////////////////////////////////////
9 #include "Model.h"
10 #include <iostream>
11 #include <algorithm>
12 #include <fstream>
13 #include <sstream>
14
15 void Model::throwXmlError(tinyxml2::XMLError error)
16 {
17     if (error != tinyxml2::XML_SUCCESS)
18     {
19         if(error == tinyxml2::XML_ERROR_FILE_NOT_FOUND)
20             throw std::string("XML config file not found!");
21
22         if(error == tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED)
23             throw std::string("XML config could not be opened!");
24
25         if(error == tinyxml2::XML_ERROR_EMPTY_DOCUMENT)
26             throw std::string("XML config is emty!");
27
28         if(error == tinyxml2::XML_ERROR_FILE_READ_ERROR)
29             throw std::string("XML config could not be read!");
30
31         if(error == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE)
32             throw std::string("Error, wrong attribute type!");
33
34         if(error == tinyxml2::XML_NO_ATTRIBUTE)
35             throw std::string("Error, no attribute!");
36
37         if(error == tinyxml2::XML_ERROR_PARSING_ELEMENT)
38             throw std::string("Error parsing element!");
39
40         if(error == tinyxml2::XML_ERROR_PARSING_ATTRIBUTE)
41             throw std::string("Error parsing attribute!");
42
43         if(error == tinyxml2::XML_ERROR_PARSING_TEXT)
44             throw std::string("Error parsing text!");
45
46         if(error == tinyxml2::XML_ERROR_PARSING_CDATA)
47             throw std::string("Error parsing CDATA!");
48
49         if(error == tinyxml2::XML_ERROR_PARSING_COMMENT)
50             throw std::string("Error parsing comment!");
51
52         if(error == tinyxml2::XML_ERROR_PARSING_DECLARATION)
53             throw std::string("Error parsing declaration!");
54
55         if(error == tinyxml2::XML_ERROR_PARSING_UNKNOWN)
56             throw std::string("Error parsing unknown!");
57
58         if(error == tinyxml2::XML_ERROR_MISMATCHED_ELEMENT)
59             throw std::string("Error mismatched element!");
60
61         if(error == tinyxml2::XML_ERROR_PARSING)
62             throw std::string("Parsing Error!");
63
64         if(error == tinyxml2::XML_CAN_NOT_CONVERT_TEXT)
65             throw std::string("Error, could not convert text!");
66
67         if(error == tinyxml2::XML_NO_TEXT_NODE)
68             throw std::string("Error, no text node!");
69
70         throw std::string("Unknown XML Error!");
71     }
72 }
73
74 Model::Model(const std::string &confDir, FSHelper help, const std::string &log)
75 {
76     mFSHelp = help;
77
78     if(!mFSHelp.dirExists(confDir))
79         throw std::string("Configuration directory not found!");
80
81     mLogFile = log;
82     mConfDir = mFSHelp.addDirLastSlash(confDir);
83 }
84
85 void Model::clearMembers()
86 {
87     mAuthors.clear();
88     mChapters.clear();
89     mTemplFileString.clear();
90     mOutPath.clear();
91     mCoverSheet.clear();
92     mDocName.clear();
93     mSubject.clear();
94     bool mHasCoverSheet=false;
95     bool mHasTitlePage=false;
96     bool mHasTableOfContent=false;
97     unsigned int mTableOfContentDepth=3;
98     int mChapterNum=1;
99     int mAuthorNum=0;
100 }
101
102 void Model::newTemplate(const std::string &name)
103 {
104     setSubjectName(name);
105     chapterInsertBack("Example", "Example.tex", ChapterFactory::TypeTextChapter);
106     authorInsertBack();
107     mChapterNum = 1;
108     mAuthorNum = 0;
109     getSelChapter()->SetContent("%Example Chapter");
110 }
111
112 void Model::setTemplFile(const std::string &templFile)
113 {
114
115     clearMembers();
116
117     if(templFile == "")
118         throw std::string("Invalid config file!");
119
120     // Lade XML Datei
121     throwXmlError(mTemplFile.LoadFile(templFile.c_str()));
122     // Speichere XML Dateinamen + Pfad
123     mTemplFileString = templFile;
124
125     if(mTemplFile.FirstChildElement("docsettings")==nullptr)
126         throw std::string("XML Syntax error: Missing <docsettings> tag");
127
128     //------------ Parse Fileextensions ------------
129     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions") == nullptr)
130         throw std::string("Missing config: <fileextensions>");
131
132     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("source") == nullptr)
133         throw std::string("Missing config: <fileextensions> -> <source>");
134
135     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("header") == nullptr)
136         throw std::string("Missing config: <fileextensions> -> <header>");
137
138     mSourceExtension = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("source")->GetText();
139     mHeaderExtension = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("header")->GetText();
140
141     //------------ Parse Location ------------
142     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("location") == nullptr)
143         throw std::string("XML parsing error while loading location!");
144
145     mLocation = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("location")->GetText();
146
147     //------------ Parse Template Typ ------------
148     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("subjectname") == nullptr)
149         throw std::string("XML parsing error while loading subject name!");
150
151     mSubject = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("subjectname")->GetText();
152
153     //------------ Parse Abteilung Koordinaten ------------
154     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department") == nullptr)
155         throw std::string("Missing config: <docsettings> -> <department>");
156
157     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("x_coor") == nullptr)
158         throw std::string("Missing config: <docsettings> -> <department> -> <x_coor>");
159
160     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("y_coor") == nullptr)
161         throw std::string("Missing config: <docsettings> -> <department> -> <y_coor>");
162
163     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("x_coor")->QueryUnsignedText(&mDepartmentX));
164     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("y_coor")->QueryUnsignedText(&mDepartmentY));
165
166     //------------ Parse Deckblatt ------------
167     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("cover_sheet") == nullptr)
168         throw std::string("Missing config: <docsettings> -> <cover_sheet>");
169
170     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("cover_sheet")->QueryBoolText(&mHasCoverSheet));
171
172     //------------ Parse Titelseite ------------
173     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("title_page") == nullptr)
174         throw std::string("Missing config: <docsettings> -> <title_page>");
175
176     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("title_page")->QueryBoolText(&mHasTitlePage));
177
178     //------------ Parse Inhaltsverzeichnis ------------
179     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("toc") == nullptr)
180         throw std::string("Missing config: <docsettings> -> <toc>");
181
182     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("toc")->FirstChildElement("enabled") == nullptr)
183         throw std::string("Missing config: <docsettings> -> <toc> -> <enabled>");
184
185     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("toc")->FirstChildElement("depth") == nullptr)
186         throw std::string("Missing config: <docsettings> -> <toc> -> <depth>");
187
188     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("toc")->FirstChildElement("depth")->QueryUnsignedText(&mTableOfContentDepth));
189     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("toc")->FirstChildElement("enabled")->QueryBoolText(&mHasTableOfContent));
190
191     //------------ Parse Kapitel ------------
192     if(mTemplFile.FirstChildElement("chapters")==nullptr)
193         throw std::string("XML Syntax error: Missing <chapters> tag");
194
195     for(tinyxml2::XMLElement* curElem = mTemplFile.FirstChildElement("chapters")->FirstChildElement("chapter");
196             curElem != nullptr;
197             curElem = curElem->NextSiblingElement("chapter"))
198     {
199
200         if( curElem == nullptr ||
201                 curElem->FirstChildElement("num") == nullptr ||
202                 curElem->FirstChildElement("filename") == nullptr ||
203                 curElem->FirstChildElement("prettyName") == nullptr ||
204                 curElem->FirstChildElement("content") == nullptr ||
205                 curElem->FirstChildElement("hierachy") == nullptr ||
206                 curElem->FirstChildElement("type") == nullptr)
207             throw std::string("XML Syntax error parsing Chapter content.");
208
209         int val = 0;
210         ChapterIF::SPtr curChap = mChaptFactory.createChapter(curElem->FirstChildElement("type")->GetText());
211         curElem->FirstChildElement("num")->QueryIntText(&val);
212         curChap->SetNum(val);
213         curChap->SetOutFileName(curElem->FirstChildElement("filename")->GetText());
214         curChap->SetPrettyName(curElem->FirstChildElement("prettyName")->GetText());
215         curChap->SetDone(false);
216         curChap->SetContent(curElem->FirstChildElement("content")->GetText());
217         curChap->SetHierachy(curElem->FirstChildElement("hierachy")->GetText());
218         mChapters.push_back(curChap);
219     }
220     mChapterNum = 1;
221
222     //------------ Parse Autoren ------------
223     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")==nullptr)
224         throw std::string("XML Syntax error: Missing <authors> tag");
225
226     for(tinyxml2::XMLElement* curElem = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")->FirstChildElement("author");
227             curElem != nullptr;
228             curElem = curElem->NextSiblingElement("author"))
229     {
230
231         if( curElem == nullptr ||
232                 curElem->FirstChildElement("num") == nullptr ||
233                 curElem->FirstChildElement("x_coor") == nullptr ||
234                 curElem->FirstChildElement("y_coor") == nullptr ||
235                 curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor") == nullptr ||
236                 curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor") == nullptr ||
237                 curElem->FirstChildElement("id")->FirstChildElement("x_coor") == nullptr ||
238                 curElem->FirstChildElement("id")->FirstChildElement("y_coor") == nullptr ||
239                 curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor") == nullptr ||
240                 curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor") == nullptr)
241             throw std::string("XML syntax error parsing author content.");
242
243         Author curAuthor;
244         unsigned int num = 0;
245         unsigned int name_x = 0;
246         unsigned int name_y = 0;
247         unsigned int id_x = 0;
248         unsigned int id_y = 0;
249         unsigned int time_spent_x = 0;
250         unsigned int time_spent_y = 0;
251         unsigned int time_est_x = 0;
252         unsigned int time_est_y = 0;
253
254         throwXmlError(curElem->FirstChildElement("num")->QueryUnsignedText(&num));
255         throwXmlError(curElem->FirstChildElement("x_coor")->QueryUnsignedText(&name_x));
256         throwXmlError(curElem->FirstChildElement("y_coor")->QueryUnsignedText(&name_y));
257         throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor")->QueryUnsignedText(&time_spent_x));
258         throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor")->QueryUnsignedText(&time_spent_y));
259         throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor")->QueryUnsignedText(&time_est_x));
260         throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor")->QueryUnsignedText(&time_est_y));
261         throwXmlError(curElem->FirstChildElement("id")->FirstChildElement("x_coor")->QueryUnsignedText(&id_x));
262         throwXmlError(curElem->FirstChildElement("id")->FirstChildElement("y_coor")->QueryUnsignedText(&id_y));
263         curAuthor.SetNum(num);
264         curAuthor.SetXNameCoordinate(name_x);
265         curAuthor.SetYNameCoordinate(name_y);
266         curAuthor.SetXTimeEstimatedCoordinate(time_est_x);
267         curAuthor.SetYTimeEstimatedCoordinate(time_est_y);
268         curAuthor.SetXTimeSpentCoordinate(time_spent_x);
269         curAuthor.SetYTimeSpentCoordinate(time_spent_y);
270         curAuthor.SetXid(id_x);
271         curAuthor.SetYid(id_y);
272         curAuthor.SetName("Author " + std::to_string(num));
273         curAuthor.SetTimeEstimated("0 h");
274         curAuthor.SetTimeSpent("0 h");
275         mAuthors.push_back(std::make_shared<Author>(curAuthor));
276     }
277     mAuthorNum = 0;
278
279     // Sortiere Listen
280     std::sort(mAuthors.begin(), mAuthors.end(), [](const Author::SPtr first, const Author::SPtr sec)
281     {
282         return first->GetNum() < sec->GetNum();
283     });
284     std::sort(mChapters.begin(), mChapters.end(), [](const ChapterIF::SPtr first, const ChapterIF::SPtr sec)
285     {
286         return first->GetNum() < sec->GetNum();
287     });
288 }
289
290 std::list<std::string> Model::getChapterNames()
291 {
292     std::list<std::string> retVal;
293
294     for(auto curChap : mChapters)
295     {
296         retVal.push_back(curChap->GetPrettyName());
297     }
298
299     return retVal;
300 }
301
302 std::list<std::string> Model::getAuthorNames()
303 {
304     std::list<std::string> retVal;
305
306     for(auto curAuth : mAuthors)
307     {
308         retVal.push_back(curAuth->GetName());
309     }
310
311     return retVal;
312 }
313
314 void Model::loadChapter(int num)
315 {
316     mChapterNum = num;
317 }
318
319 void Model::loadAuthor(int num)
320 {
321     mAuthorNum = num;
322 }
323
324 Author::SPtr Model::getSelAuthor()
325 {
326     return mAuthors.at(mAuthorNum);
327 }
328
329 ChapterIF::SPtr Model::getSelChapter()
330 {
331     return mChapters.at(mChapterNum-1);
332 }
333
334 int Model::getNumChapters()
335 {
336     return mChapters.size();
337 }
338
339 int Model::getNumFinishedChapters()
340 {
341     int retVal=0;
342     for(auto curChapt : mChapters)
343         if(curChapt->GetDone())
344             retVal++;
345
346     return retVal;
347 }
348
349 int Model::getChapterNum()
350 {
351     return mChapterNum;
352 }
353
354 int Model::getAuthorNum()
355 {
356     return mAuthorNum;
357 }
358
359 std::string Model::getSubjectName()
360 {
361     return mSubject;
362 }
363
364 void Model::setSubjectName(const std::string &name)
365 {
366     if(!mFSHelp.validFileName(name))
367         throw std::string("Subject name contains illegal characters.");
368
369     mSubject = name;
370 }
371
372 void Model::chapterUp()
373 {
374     // Umrechnung, da mChapterNum 1 Basierend ist
375     int chaptElem = mChapterNum-1;
376     if(chaptElem>0)
377     {
378         mChapters.at(chaptElem)->SetNum(mChapterNum-1);
379         mChapters.at(chaptElem-1)->SetNum(mChapterNum);
380         std::iter_swap(mChapters.begin() + chaptElem, mChapters.begin() + chaptElem-1);
381         mChapterNum=mChapterNum-1;
382     }
383 }
384
385 void Model::chapterDown()
386 {
387     // Umrechnung, da mChapterNum 1 Basierend ist
388     int chaptElem = mChapterNum-1;
389     if(chaptElem<getNumChapters()-1)
390     {
391         mChapters.at(chaptElem)->SetNum(mChapterNum+1);
392         mChapters.at(chaptElem+1)->SetNum(mChapterNum);
393         std::iter_swap(mChapters.begin() + chaptElem, mChapters.begin() + chaptElem+1);
394         mChapterNum=mChapterNum+1;
395     }
396 }
397
398 void Model::chapterInsertBack(const std::string &chaptName, const std::string &outFileName, const std::string &type, int hierachy)
399 {
400     if(chaptName.empty())
401         throw std::string("Please set an chapter name!");
402
403     if(outFileName.empty())
404         throw std::string("Please set an output file name!");
405
406     // Überprüfe ob ein anderes kapitel den selben dateinamen besitzt
407     auto found = std::find_if(mChapters.begin(), mChapters.end(), [outFileName](ChapterIF::SPtr chapt)
408     {
409         return (outFileName == chapt->GetOutFileName());
410     }
411                              );
412
413     if(found !=  std::end(mChapters))
414         throw std::string("Filename " + outFileName + " is already used by chapter " + std::to_string((*found)->GetNum()) + " (" + (*found)->GetPrettyName()  + ")" );
415
416     ChapterIF::SPtr newChapt = mChaptFactory.createChapter(type);
417     newChapt->SetNum(getNumChapters() + 1);
418     newChapt->SetOutFileName(outFileName);
419     newChapt->SetPrettyName(chaptName);
420     newChapt->SetHierachy(hierachy);
421     newChapt->SetDone(false);
422     mChapters.push_back(newChapt);
423     setOutPath(mOutPath); // Lade text aus hinzugefügter kapiteldatei, falls sie existiert
424 }
425
426 void Model::chapterInsertBack(const std::string &chaptName, const std::string &outFileName, int type, int hierachy)
427 {
428     chapterInsertBack(chaptName, outFileName, mChaptFactory.getChapterTypes().at(type), hierachy);
429 }
430
431 void Model::authorInsertBack()
432 {
433     Author::SPtr newAuthor {std::make_shared<Author>()};
434     newAuthor->SetNum(mAuthors.size()+1);
435     newAuthor->SetXNameCoordinate(0);
436     newAuthor->SetYNameCoordinate(0);
437     newAuthor->SetXTimeEstimatedCoordinate(0);
438     newAuthor->SetYTimeEstimatedCoordinate(0);
439     newAuthor->SetXTimeSpentCoordinate(0);
440     newAuthor->SetYTimeSpentCoordinate(0);
441     newAuthor->SetXid(0);
442     newAuthor->SetYid(0);
443     newAuthor->SetTimeEstimated("0 h");
444     newAuthor->SetTimeSpent("0 h");
445
446     newAuthor->SetName("Author " + std::to_string(mAuthors.size()+1));
447     mAuthors.push_back(newAuthor);
448 }
449
450 void Model::chapterRemove()
451 {
452     if(getNumChapters()-1>0)
453     {
454         std::vector <ChapterIF::SPtr>::iterator it;
455         for( it = mChapters.begin()+mChapterNum-1; it != mChapters.end(); it++)
456             (*it)->SetNum((*it)->GetNum()-1);
457
458         mChapters.erase(mChapters.begin() + mChapterNum-1);
459         mChapterNum=1;
460     }
461 }
462
463 void Model::authorRemove()
464 {
465     if(mAuthors.size()-1>0)
466     {
467         std::vector <Author::SPtr>::iterator it;
468         for( it = mAuthors.begin()+mAuthorNum; it != mAuthors.end(); it++)
469         {
470             if((*it)->GetName() == "Author " + std::to_string((*it)->GetNum()))
471                 (*it)->SetName("Author " + std::to_string((*it)->GetNum()-1));
472
473             (*it)->SetNum((*it)->GetNum()-1);
474         }
475
476         mAuthors.erase(mAuthors.begin() + mAuthorNum);
477         mAuthorNum=0;
478     }
479 }
480
481 void Model::setOutPath(const std::string &outPath)
482 {
483     if(!mFSHelp.dirExists(outPath))
484         throw std::string("Output path not found!");
485
486     mOutPath = mFSHelp.addDirLastSlash(outPath);
487
488     // Lade text aus ev. existierenden Text Dateien
489     for(auto curChapt : mChapters)
490     {
491         if(curChapt->GetType() == mChaptFactory.getChapterTypes().at(ChapterFactory::TypeTextChapter))
492         {
493             std::ifstream iFstr;
494             iFstr.open(mOutPath + curChapt->GetOutFileName());
495             if(iFstr.good())
496             {
497                 std::stringstream buffer;
498                 buffer << iFstr.rdbuf();
499                 if(buffer.good())
500                     curChapt->SetContent(buffer.str());
501                 else
502                     throw std::string("Error reading existing chapter file!");
503
504             }
505             iFstr.close();
506         }
507     }
508 }
509
510 void Model::generateDocument()
511 {
512     // Überprüfe dateien + pfade und versuche diese zu kopieren
513     if(!mFSHelp.dirExists(mOutPath))
514         throw std::string("Output path not found! Please set an existing output directory.");
515
516     // Kopiere Vorlage
517     mFSHelp.copyFile(mConfDir + "packages.tex", mOutPath + "packages.tex");
518     mFSHelp.copyFile(mConfDir + "doxygen.sty", mOutPath + "doxygen.sty");
519     mFSHelp.copyFile(mConfDir + "setup_doxygen.tex", mOutPath + "setup_doxygen.tex");
520     mFSHelp.copyFile(mConfDir + "codesettings.tex", mOutPath + "codesettings.tex");
521     mFSHelp.copyFile(mConfDir + "main.tex", mOutPath + "main.tex");
522     mFSHelp.copyFile(mConfDir + "docsettings.tex", mOutPath + "docsettings.tex");
523
524     // main.tex
525     std::ofstream mainFile;
526     mainFile.open( mOutPath + "main.tex", std::ios_base::app);
527     if(!mainFile.good())
528         throw std::string("Could not open main.tex");
529
530     // Füge subject name hinzu
531     mainFile << "\\newcommand{\\docsubject}{" << mSubject <<"}" << std::endl;
532
533     // Füge Abteilug hinzu
534     mainFile << "\\newcommand{\\department}{" << mDepartment <<"}" << std::endl;
535
536     // Füge dokumentennamen hinzu
537     mainFile << "\\title{" << mDocName << "}" << std::endl;
538
539     // Ort hinzufügen
540     mainFile << "\\date{\\today{}, " << mLocation << "}" << std::endl;
541
542     // AUthoren hinzufügen
543     mainFile << "\\author{";
544     for(auto curAuthor : mAuthors)
545     {
546         mainFile << curAuthor->GetName();
547         if(curAuthor != *(mAuthors.end()-1))
548             mainFile << ", ";
549     }
550     mainFile << "}" << std::endl;
551
552     // Einstellungsdateien/Styledateien eibinden
553     mainFile << std::endl;
554     mainFile << "\\input{codesettings.tex}" << std::endl;
555     mainFile << "\\begin{document}" << std::endl;
556     mainFile << "\\input{docsettings.tex}" << std::endl;
557     mainFile << std::endl;
558
559     // Füge kapitel hinzu + erzeuge Kapiteldateien
560     for(auto curChap : mChapters)
561     {
562         curChap->WriteFile(this);
563         mainFile << "\t\\"+curChap->GetHierachy()+"{" << curChap->GetPrettyName() << "}" << std::endl;
564         mainFile << "\t\t\\input{" << curChap->GetOutFileName() << "}" << std::endl;
565         mainFile << std::endl;
566     }
567
568
569     // Öffne docsettings.tex
570     std::ofstream docSettingsFile;
571     docSettingsFile.open( mOutPath + "docsettings.tex", std::ios_base::app);
572     if(!docSettingsFile.good())
573         throw std::string("Could not open docsettings.tex");
574
575     //---- Füge Angabenseite/Deckblatt hinzu ----
576     if(mHasCoverSheet)
577     {
578         mFSHelp.copyFile(mCoverSheet, mOutPath + "coversheet.pdf");
579
580         // Deckblatt ausfüllen
581         docSettingsFile << "\\begin{titlepage}\n\t\\includepdf[pages={-},\n\t\tpicturecommand*={" << std::endl;
582         for(auto curAuthor : mAuthors)
583         {
584             docSettingsFile << "\t\t\\put(" << curAuthor->GetXNameCoordinate() << "," << curAuthor->GetYNameCoordinate() << "){" << curAuthor->GetName() << "}" << std::endl;
585             docSettingsFile << "\t\t\\put(" << curAuthor->GetXTimeEstimatedCoordinate() << "," << curAuthor->GetYTimeEstimatedCoordinate() << "){" << curAuthor->GetTimeEstimated() << "}" << std::endl;
586             docSettingsFile << "\t\t\\put(" << curAuthor->GetXTimeSpentCoordinate() << "," << curAuthor->GetYTimeSpentCoordinate() << "){" << curAuthor->GetTimeSpent() << "}" << std::endl;
587             docSettingsFile << "\t\t\\put(" << curAuthor->GetXid() << "," << curAuthor->GetYid() << "){" << curAuthor->GetID() << "}" << std::endl;
588         }
589         docSettingsFile << "\t\t\\put(" << mDepartmentX << "," << mDepartmentY << "){" << mDepartment << "}" << std::endl;
590         docSettingsFile << "\t}]{coversheet.pdf}\n\\end{titlepage}" << std::endl;
591     }
592
593     // Latex Titelblatt erzeugen
594     if(mHasTitlePage)
595         docSettingsFile << "\\maketitle" << std::endl;
596
597     // Inhaltsverzeichnis erzeugen
598     if(mHasTableOfContent)
599         docSettingsFile << std::endl << "\\setcounter{tocdepth}{" << std::to_string(mTableOfContentDepth) << "}" <<
600         std::endl << "\\tableofcontents" << std::endl << "\\newpage" << std::endl;
601
602     docSettingsFile.close();
603
604
605     // kopiere resources ordner
606     mFSHelp.copyDir(mConfDir + "resources", mOutPath + "resources");
607
608     mainFile << "\\end{document}" << std::endl;
609     mainFile.close();
610
611     // Generiere Dokument
612     std::vector<std::string> paramsDryRun = {"-draftmode", "-interaction=nonstopmode", "main.tex"};
613     std::vector<std::string> paramsGen = {"-interaction=nonstopmode", "main.tex"};
614     mFSHelp.executeCommand(mOutPath, "pdflatex", paramsDryRun, mFSHelp.addDirLastSlash(mOutPath) + mLogFile);
615     mFSHelp.executeCommand(mOutPath, "pdflatex", paramsGen, mFSHelp.addDirLastSlash(mOutPath) + mLogFile);
616 }
617
618 void Model::saveTemplFile()
619 {
620     tinyxml2::XMLDocument outDoc;
621
622     tinyxml2::XMLNode * pDocSettings = outDoc.NewElement("docsettings");
623     tinyxml2::XMLNode * pChapters = outDoc.NewElement("chapters");
624
625     outDoc.InsertFirstChild(pDocSettings);
626     outDoc.InsertAfterChild(pDocSettings, pChapters);
627
628     // DocSettings
629     tinyxml2::XMLElement * pSubjName = outDoc.NewElement("subjectname");
630     tinyxml2::XMLElement * pLocation = outDoc.NewElement("location");
631     tinyxml2::XMLElement * pCoverSheet = outDoc.NewElement("cover_sheet");
632     tinyxml2::XMLElement * pTitlePage = outDoc.NewElement("title_page");
633     pSubjName->SetText(mSubject.c_str());
634     pLocation->SetText(mLocation.c_str());
635     pCoverSheet->SetText(mHasCoverSheet);
636     pTitlePage->SetText(mHasTitlePage);
637     pDocSettings->InsertEndChild(pSubjName);
638     pDocSettings->InsertEndChild(pLocation);
639     pDocSettings->InsertEndChild(pCoverSheet);
640     pDocSettings->InsertEndChild(pTitlePage);
641
642     // Inhaltsverzeichnis
643     tinyxml2::XMLNode * pToC = outDoc.NewElement("toc");
644     tinyxml2::XMLElement * pToCenabled = outDoc.NewElement("enabled");
645     tinyxml2::XMLElement * pToCdepth = outDoc.NewElement("depth");
646     pToCenabled->SetText(mHasTableOfContent);
647     pToCdepth->SetText(mTableOfContentDepth);
648     pToC->InsertEndChild(pToCenabled);
649     pToC->InsertEndChild(pToCdepth);
650     pDocSettings->InsertEndChild(pToC);
651
652     // Fileextensions
653     tinyxml2::XMLNode * pFileExtensions= outDoc.NewElement("fileextensions");
654     tinyxml2::XMLElement * pFileExtHeader = outDoc.NewElement("header");
655     tinyxml2::XMLElement * pFileExtSource= outDoc.NewElement("source");
656     if(mHeaderExtension.empty() || mSourceExtension.empty())
657         throw std::string("Please set header and source file extension to use!");
658
659     pFileExtHeader->SetText(mHeaderExtension.c_str());
660     pFileExtSource->SetText(mSourceExtension.c_str());
661     pFileExtensions->InsertEndChild(pFileExtHeader);
662     pFileExtensions->InsertEndChild(pFileExtSource);
663     pDocSettings->InsertEndChild(pFileExtensions);
664
665     // Department
666     tinyxml2::XMLNode * pDepartment = outDoc.NewElement("department");
667     tinyxml2::XMLElement * pDepX = outDoc.NewElement("x_coor");
668     tinyxml2::XMLElement * pDepY = outDoc.NewElement("y_coor");
669     pDepX->SetText(mDepartmentX);
670     pDepY->SetText(mDepartmentY);
671     pDepartment->InsertEndChild(pDepX);
672     pDepartment->InsertEndChild(pDepY);
673     pDocSettings->InsertEndChild(pDepartment);
674
675     // Speichere Autoren zu XML
676     tinyxml2::XMLNode * pAuthors = outDoc.NewElement("authors");
677     for(auto curAuth: mAuthors)
678     {
679
680         tinyxml2::XMLNode * pAuthor = outDoc.NewElement("author");
681         tinyxml2::XMLElement * pNum = outDoc.NewElement("num");
682         tinyxml2::XMLElement * pNameX = outDoc.NewElement("x_coor");
683         tinyxml2::XMLElement * pNameY = outDoc.NewElement("y_coor");
684         tinyxml2::XMLNode * pTimeEst= outDoc.NewElement("time_estimated");
685         tinyxml2::XMLNode * pTimeSpent = outDoc.NewElement("time_spent");
686         tinyxml2::XMLNode * pId = outDoc.NewElement("id");
687
688         tinyxml2::XMLElement * pTimeEstX = outDoc.NewElement("x_coor");
689         tinyxml2::XMLElement * pTimeEstY = outDoc.NewElement("y_coor");
690
691         tinyxml2::XMLElement * pTimeSpenX = outDoc.NewElement("x_coor");
692         tinyxml2::XMLElement * pTimeSpenY = outDoc.NewElement("y_coor");
693
694         tinyxml2::XMLElement * pIdX = outDoc.NewElement("x_coor");
695         tinyxml2::XMLElement * pIdY = outDoc.NewElement("y_coor");
696
697         pNum->SetText(curAuth->GetNum());
698         pNameX->SetText(curAuth->GetXNameCoordinate());
699         pNameY->SetText(curAuth->GetYNameCoordinate());
700         pTimeSpenX->SetText(curAuth->GetXTimeSpentCoordinate());
701         pTimeSpenY->SetText(curAuth->GetYTimeSpentCoordinate());
702         pTimeEstX->SetText(curAuth->GetXTimeEstimatedCoordinate());
703         pTimeEstY->SetText(curAuth->GetYTimeEstimatedCoordinate());
704         pIdX->SetText(curAuth->GetXid());
705         pIdY->SetText(curAuth->GetYid());
706
707         pAuthor->InsertEndChild(pNum);
708         pAuthor->InsertEndChild(pNameX);
709         pAuthor->InsertEndChild(pNameY);
710
711         pTimeEst->InsertEndChild(pTimeEstX);
712         pTimeEst->InsertEndChild(pTimeEstY);
713         pAuthor->InsertEndChild(pTimeEst);
714
715         pId->InsertEndChild(pIdX);
716         pId->InsertEndChild(pIdY);
717         pAuthor->InsertEndChild(pId);
718
719         pTimeSpent->InsertEndChild(pTimeSpenX);
720         pTimeSpent->InsertEndChild(pTimeSpenY);
721         pAuthor->InsertEndChild(pTimeSpent);
722
723         pAuthors->InsertEndChild(pAuthor);
724     }
725     pDocSettings->InsertEndChild(pAuthors);
726
727     // Speichere Kapitel zu XML
728     for(auto curChapt : mChapters)
729     {
730
731         tinyxml2::XMLNode * pChapter = outDoc.NewElement("chapter");
732         tinyxml2::XMLElement * pNum = outDoc.NewElement("num");
733         tinyxml2::XMLElement * pName = outDoc.NewElement("prettyName");
734         tinyxml2::XMLElement * pFileName = outDoc.NewElement("filename");
735         tinyxml2::XMLElement * pContent = outDoc.NewElement("content");
736         tinyxml2::XMLElement * pType = outDoc.NewElement("type");
737         tinyxml2::XMLElement * pHierachy = outDoc.NewElement("hierachy");
738
739         pNum->SetText(curChapt->GetNum());
740         pName->SetText(curChapt->GetPrettyName().c_str());
741         pFileName->SetText(curChapt->GetOutFileName().c_str());
742         pType->SetText(curChapt->GetType().c_str());
743         pHierachy->SetText(curChapt->GetHierachy().c_str());
744
745         if(curChapt->GetType() == "Text")
746         {
747             if(curChapt->GetContent().empty())
748                 pContent->SetText("% Enter text here.");
749             else
750                 pContent->SetText(curChapt->GetContent().c_str());
751         }
752         else
753             pContent->SetText("Select Path");
754
755         pChapter->InsertEndChild(pName);
756         pChapter->InsertEndChild(pNum);
757         pChapter->InsertEndChild(pFileName);
758         pChapter->InsertEndChild(pContent);
759         pChapter->InsertEndChild(pType);
760         pChapter->InsertEndChild(pHierachy);
761
762         pChapters->InsertEndChild(pChapter);
763     }
764
765     throwXmlError(outDoc.SaveFile(std::string(mOutPath + mSubject + ".xml").c_str()));
766 }
767
768 bool Model::shouldAddCoverSheet()
769 {
770     return mHasCoverSheet;
771 }
772
773
774 void Model::setAddTitlePage(bool val)
775 {
776     mHasTitlePage = val;
777 }
778
779
780 void Model::setAddTableOfContent(bool val)
781 {
782     mHasTableOfContent = val;
783 }
784
785
786 void Model::setTableOfContentDepth(int depth)
787 {
788     mTableOfContentDepth = depth;
789 }
790
791 bool Model::shouldAddTitlePage()
792 {
793     return mHasTitlePage;
794 }
795
796 bool Model::shouldAddTableOfContent()
797 {
798     return mHasTableOfContent;
799 }
800
801 int Model::getTableOfContentDepth()
802 {
803     return mTableOfContentDepth;
804 }
805
806 std::string Model::getCovSheet()
807 {
808     return mCoverSheet;
809 }
810
811 void Model::setCovSheet(const std::string &file)
812 {
813     if(!mFSHelp.fileExists(file))
814         throw std::string("File not found!");
815     mCoverSheet = file;
816 }
817
818 std::string Model::getConfDir()
819 {
820     return mConfDir;
821 }
822
823 void Model::saveChaptersToFiles()
824 {
825     for(auto curChap : mChapters)
826         if(curChap->GetType() == mChaptFactory.getChapterTypes().at(ChapterFactory::TypeTextChapter))
827             curChap->WriteFile(this);
828 }
829
830 bool Model::getGenerateEnable()
831 {
832     if(getNumChapters() == getNumFinishedChapters())
833     {
834
835         if(mHasCoverSheet)
836             if(!mFSHelp.fileExists(mCoverSheet))
837                 return false;
838
839         if(mDocName.empty())
840             return false;
841
842         if(mDepartment.empty())
843             return false;
844
845         return true;
846     }
847     return false;
848 }
849
850 void Model::setAddCoverSheet(bool val)
851 {
852     mHasCoverSheet = val;
853 }
854
855 std::string Model::getDocName()
856 {
857     return mDocName;
858 }
859
860 void Model::setDocName(const std::string &name)
861 {
862     mDocName = name;
863 }
864
865 std::string Model::getLocation()
866 {
867     return mLocation;
868 }
869
870 std::string Model::getDepartment()
871 {
872     return mDepartment;
873 }
874
875 void Model::setLocation(const std::string &location)
876 {
877     mLocation = location;
878 }
879
880 void Model::setDepartment(const std::string &department)
881 {
882     mDepartment = department;
883 }
884
885 int Model::getDepartmentX()
886 {
887     return mDepartmentX;
888 }
889
890 int Model::getDepartmentY()
891 {
892     return mDepartmentY;
893 }
894
895 void Model::setDepartmentX(unsigned int x)
896 {
897     mDepartmentX = x;
898 }
899
900 void Model::setDepartmentY(unsigned int y)
901 {
902     mDepartmentY = y;
903 }
904
905 std::string Model::getOutPath()
906 {
907     return mOutPath;
908 }
909
910 std::string Model::getHeaderFileExtension()
911 {
912     return mHeaderExtension;
913 }
914
915 std::string Model::getSourceFileExtension()
916 {
917     return mSourceExtension;
918 }
919
920 void Model::setHeaderFileExtension(const std::string &fileExt)
921 {
922     if(fileExt.empty())
923         throw std::string("Please set a header file extension!");
924
925     mHeaderExtension = fileExt;
926 }
927
928 void Model::setLogFile(const std::string &log)
929 {
930     mLogFile = log;
931 }
932
933 std::string Model::getLogFile()
934 {
935     return mLogFile;
936 }
937
938 void Model::setSourceFileExtension(const std::string &fileExt)
939 {
940     if(fileExt.empty())
941         throw std::string("Please set a source file extension!");
942
943     mSourceExtension = fileExt;
944 }
945
946 std::vector<std::string> Model::getChapterTypes()
947 {
948     return mChaptFactory.getChapterTypes();
949 }
950
951 std::vector<ChapterIF::SPtr> Model::getChapters()
952 {
953     return mChapters;
954 }