4aac8f9b930751525dca78f26b0c5a136f0b88a1
[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(std::string confDir)
75 {
76     if(!mFSHelp.dirExists(confDir))
77         throw std::string("Configuration directory not found!");
78
79     mConfDir = mFSHelp.addDirLastSlash(confDir);
80 }
81
82 void Model::clearMembers()
83 {
84     mAuthors.clear();
85     mChapters.clear();
86     mTemplFileString.clear();
87     mOutPath.clear();
88     mSourcePath.clear();
89     mCoverSheet.clear();
90     //mConfDir.clear();
91     mDocName.clear();
92     mSubject.clear();
93     bool mDoxygen=false;
94     mDoxygenName.clear();
95     bool mSourceChapter=false;
96     mSourceChapterName.clear();
97     bool mHasCoverSheet=false;
98     int mChapterNum=1;
99     int mAuthorNum=0;
100 }
101
102 void Model::newTemplate(std::string name)
103 {
104     setSubjectName(name);
105     chapterInsertBack("Example", "Example.tex");
106     authorInsertBack();
107     mChapterNum = 1;
108     mAuthorNum = 0;
109     getSelChapter()->SetText("%Example Chapter");
110 }
111
112 void Model::setTemplFile(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 Titelseite ------------
167     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("cover_sheet") == nullptr)
168         throw std::string("Missing config: <docsettings> -> <cover_sheet>");
169
170     std::string str = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("cover_sheet")->GetText();
171     if(str == "true")
172         mHasCoverSheet = true;
173     else if(str == "false")
174         mHasCoverSheet = false;
175     else
176         throw std::string("Wrong config value <docsettings> -> <cover_sheet>: " + str);
177
178
179
180     //------------ Parse Quellcode Kapitel ------------
181     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")==nullptr)
182         throw std::string("Missing config: <docsettings> -> <sourcecode>");
183
184     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("enabled")==nullptr)
185         throw std::string("Missing config: <docsettings> -> <sourcecode> -> <enabled>");
186
187     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("chaptername")==nullptr)
188         throw std::string("Missing config: <docsettings> -> <sourcecode> -> <chaptername>");
189
190     mSourceChapterName = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("chaptername")->GetText();
191     str = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("enabled")->GetText();
192     if(str == "true")
193         mSourceChapter = true;
194     else if(str == "false")
195         mSourceChapter = false;
196     else
197         throw std::string("Wrong config value <docsettings> -> <sourcecode> -> <enabled>: " + str);
198
199
200
201     //------------ Parse Doxygen Kapitel ------------
202     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")==nullptr)
203         throw std::string("Missing config: <docsettings> -> <doxygen>");
204
205     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("enabled")==nullptr)
206         throw std::string("Missing config: <docsettings> -> <doxygen> -> <enabled>");
207
208     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("chaptername")==nullptr)
209         throw std::string("Missing config: <docsettings> -> <doxygen> -> <chaptername>");
210
211     mDoxygenName = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("chaptername")->GetText();
212     str = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("enabled")->GetText();
213     if(str == "true")
214         mDoxygen = true;
215     else if(str == "false")
216         mDoxygen = false;
217     else
218         throw std::string("Wrong config value <docsettings> -> <doxygen> -> <enabled>: " + str);
219
220
221     //------------ Parse Kapitel ------------
222     Chapter curChap;
223     int val = 0;
224
225     if(mTemplFile.FirstChildElement("chapters")==nullptr)
226         throw std::string("XML Syntax error: Missing <chapters> tag");
227
228     tinyxml2::XMLElement* curElem = mTemplFile.FirstChildElement("chapters")->FirstChildElement("chapter");
229
230     // Erstes Element
231     if( curElem == nullptr ||
232             curElem->FirstChildElement("num") == nullptr ||
233             curElem->FirstChildElement("filename") == nullptr ||
234             curElem->FirstChildElement("prettyName") == nullptr)
235         throw std::string("XML Syntax error parsing Chapter content.");
236
237     curElem->FirstChildElement("num")->QueryIntText(&val);
238     curChap.SetNum(val);
239     curChap.SetOutFileName(curElem->FirstChildElement("filename")->GetText());
240     curChap.SetPrettyName(curElem->FirstChildElement("prettyName")->GetText());
241     curChap.SetDone(false);
242     curChap.SetText(curElem->FirstChildElement("textTemplate")->GetText());
243     mChapters.push_back(std::make_shared<Chapter>(curChap));
244
245     // Alle weiteren Elemente
246     while(curElem != mTemplFile.FirstChildElement("chapters")->LastChildElement("chapter"))
247     {
248         curElem = curElem->NextSiblingElement("chapter");
249
250         if( curElem == nullptr ||
251                 curElem->FirstChildElement("num") == nullptr ||
252                 curElem->FirstChildElement("filename") == nullptr ||
253                 curElem->FirstChildElement("prettyName") == nullptr)
254             throw std::string("XML Syntax error parsing Chapter content.");
255
256         curElem->FirstChildElement("num")->QueryIntText(&val);
257         curChap.SetNum(val);
258         curChap.SetOutFileName(curElem->FirstChildElement("filename")->GetText());
259         curChap.SetPrettyName(curElem->FirstChildElement("prettyName")->GetText());
260         curChap.SetDone(false);
261         curChap.SetText(curElem->FirstChildElement("textTemplate")->GetText());
262         mChapters.push_back(std::make_shared<Chapter>(curChap));
263     }
264     mChapterNum = 1;
265
266     //------------ Parse Author ------------
267     Author curAuthor;
268     unsigned int num = 0;
269     unsigned int name_x = 0;
270     unsigned int name_y = 0;
271     unsigned int time_spent_x = 0;
272     unsigned int time_spent_y = 0;
273     unsigned int time_est_x = 0;
274     unsigned int time_est_y = 0;
275
276     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")==nullptr)
277         throw std::string("XML Syntax error: Missing <authors> tag");
278
279     curElem = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")->FirstChildElement("author");
280
281     // Erstes Element
282     if( curElem == nullptr ||
283             curElem->FirstChildElement("num") == nullptr ||
284             curElem->FirstChildElement("x_coor") == nullptr ||
285             curElem->FirstChildElement("y_coor") == nullptr ||
286             curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor") == nullptr ||
287             curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor") == nullptr ||
288             curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor") == nullptr ||
289             curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor") == nullptr)
290         throw std::string("XML syntax error parsing author content.");
291
292     throwXmlError(curElem->FirstChildElement("num")->QueryUnsignedText(&num));
293     throwXmlError(curElem->FirstChildElement("x_coor")->QueryUnsignedText(&name_x));
294     throwXmlError(curElem->FirstChildElement("y_coor")->QueryUnsignedText(&name_y));
295     throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor")->QueryUnsignedText(&time_spent_x));
296     throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor")->QueryUnsignedText(&time_spent_y));
297     throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor")->QueryUnsignedText(&time_est_x));
298     throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor")->QueryUnsignedText(&time_est_y));
299     curAuthor.SetNum(num);
300     curAuthor.SetXNameCoordinate(name_x);
301     curAuthor.SetYNameCoordinate(name_y);
302     curAuthor.SetXTimeEstimatedCoordinate(time_est_x);
303     curAuthor.SetYTimeEstimatedCoordinate(time_est_y);
304     curAuthor.SetXTimeSpentCoordinate(time_spent_x);
305     curAuthor.SetYTimeSpentCoordinate(time_spent_y);
306     curAuthor.SetName("Author " + std::to_string(num));
307     curAuthor.SetTimeEstimated("0 h");
308     curAuthor.SetTimeSpent("0 h");
309     mAuthors.push_back(std::make_shared<Author>(curAuthor));
310
311     // Alle weiteren Elemente
312     while(curElem != mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")->LastChildElement("author"))
313     {
314         curElem = curElem->NextSiblingElement("author");
315
316         if( curElem == nullptr ||
317                 curElem->FirstChildElement("num") == nullptr ||
318                 curElem->FirstChildElement("x_coor") == nullptr ||
319                 curElem->FirstChildElement("y_coor") == nullptr ||
320                 curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor") == nullptr ||
321                 curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor") == nullptr ||
322                 curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor") == nullptr ||
323                 curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor") == nullptr)
324             throw std::string("XML syntax error parsing author content.");
325
326         throwXmlError(curElem->FirstChildElement("num")->QueryUnsignedText(&num));
327         throwXmlError(curElem->FirstChildElement("x_coor")->QueryUnsignedText(&name_x));
328         throwXmlError(curElem->FirstChildElement("y_coor")->QueryUnsignedText(&name_y));
329         throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor")->QueryUnsignedText(&time_spent_x));
330         throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor")->QueryUnsignedText(&time_spent_y));
331         throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor")->QueryUnsignedText(&time_est_x));
332         throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor")->QueryUnsignedText(&time_est_y));
333         curAuthor.SetNum(num);
334         curAuthor.SetXNameCoordinate(name_x);
335         curAuthor.SetYNameCoordinate(name_y);
336         curAuthor.SetXTimeEstimatedCoordinate(time_est_x);
337         curAuthor.SetYTimeEstimatedCoordinate(time_est_y);
338         curAuthor.SetXTimeSpentCoordinate(time_spent_x);
339         curAuthor.SetYTimeSpentCoordinate(time_spent_y);
340         curAuthor.SetName("Author " + std::to_string(num));
341         curAuthor.SetTimeEstimated("0 h");
342         curAuthor.SetTimeSpent("0 h");
343         mAuthors.push_back(std::make_shared<Author>(curAuthor));
344     }
345     mAuthorNum = 0;
346
347     // Sortiere Listen
348     std::sort(mAuthors.begin(), mAuthors.end(), [](const Author::SPtr first, const Author::SPtr sec)
349     {
350         return first->GetNum() < sec->GetNum();
351     });
352     std::sort(mChapters.begin(), mChapters.end(), [](const Chapter::SPtr first, const Chapter::SPtr sec)
353     {
354         return first->GetNum() < sec->GetNum();
355     });
356 }
357
358 std::list<std::string> Model::getChapterNames()
359 {
360     std::list<std::string> retVal;
361
362     for(auto curChap : mChapters)
363     {
364         retVal.push_back(curChap->GetPrettyName());
365     }
366
367     return retVal;
368 }
369
370 std::list<std::string> Model::getAuthorNames()
371 {
372     std::list<std::string> retVal;
373
374     for(auto curAuth : mAuthors)
375     {
376         retVal.push_back(curAuth->GetName());
377     }
378
379     return retVal;
380 }
381
382 void Model::loadChapter(int num)
383 {
384     mChapterNum = num;
385 }
386
387 void Model::loadAuthor(int num)
388 {
389     mAuthorNum = num;
390 }
391
392 Author::SPtr Model::getSelAuthor()
393 {
394     return mAuthors.at(mAuthorNum);
395 }
396
397 Chapter::SPtr Model::getSelChapter()
398 {
399     return mChapters.at(mChapterNum-1);
400 }
401
402 int Model::getNumChapters()
403 {
404     return mChapters.size();
405 }
406
407 int Model::getNumFinishedChapters()
408 {
409     int retVal=0;
410     for(auto curChapt : mChapters)
411         if(curChapt->GetDone())
412             retVal++;
413
414     return retVal;
415 }
416
417 int Model::getChapterNum()
418 {
419     return mChapterNum;
420 }
421
422 int Model::getAuthorNum()
423 {
424     return mAuthorNum;
425 }
426
427 std::string Model::getSubjectName()
428 {
429     return mSubject;
430 }
431
432 void Model::setSubjectName(std::string name)
433 {
434     if(!mFSHelp.validFileName(name))
435         throw std::string("Subject name contains illegal characters.");
436
437     mSubject = name;
438 }
439
440 void Model::chapterUp()
441 {
442     // Umrechnung, da mChapterNum 1 Basierend ist
443     int chaptElem = mChapterNum-1;
444     if(chaptElem>0)
445     {
446         mChapters.at(chaptElem)->SetNum(mChapterNum-1);
447         mChapters.at(chaptElem-1)->SetNum(mChapterNum);
448         std::iter_swap(mChapters.begin() + chaptElem, mChapters.begin() + chaptElem-1);
449         mChapterNum=mChapterNum-1;
450     }
451 }
452
453 void Model::chapterDown()
454 {
455     // Umrechnung, da mChapterNum 1 Basierend ist
456     int chaptElem = mChapterNum-1;
457     if(chaptElem<getNumChapters()-1)
458     {
459         mChapters.at(chaptElem)->SetNum(mChapterNum+1);
460         mChapters.at(chaptElem+1)->SetNum(mChapterNum);
461         std::iter_swap(mChapters.begin() + chaptElem, mChapters.begin() + chaptElem+1);
462         mChapterNum=mChapterNum+1;
463     }
464 }
465
466 void Model::chapterInsertBack(std::string chaptName, std::string outFileName)
467 {
468     if(chaptName.empty())
469         throw std::string("Please set an chapter name!");
470
471     if(outFileName.empty())
472         throw std::string("Please set an output file name!");
473
474     // Überprüfe ob ein anderes kapitel den selben dateinamen besitzt
475     auto found = std::find_if(mChapters.begin(), mChapters.end(), [outFileName](Chapter::SPtr chapt)
476     {
477         return (outFileName == chapt->GetOutFileName());
478     }
479                              );
480
481     if(found !=  std::end(mChapters))
482         throw std::string("Filename " + outFileName + " is already used by chapter " + std::to_string((*found)->GetNum()) + " (" + (*found)->GetPrettyName()  + ")" );
483
484     Chapter::SPtr newChapt {std::make_shared<Chapter>()};
485     newChapt->SetNum(getNumChapters() + 1);
486     newChapt->SetOutFileName(outFileName);
487     newChapt->SetPrettyName(chaptName);
488     newChapt->SetDone(false);
489     mChapters.push_back(newChapt);
490     setOutPath(mOutPath); // Lade text aus hinzugefügter kapiteldatei, falls sie existiert
491 }
492
493 void Model::authorInsertBack()
494 {
495     Author::SPtr newAuthor {std::make_shared<Author>()};
496     newAuthor->SetNum(mAuthors.size()+1);
497     newAuthor->SetXNameCoordinate(0);
498     newAuthor->SetYNameCoordinate(0);
499     newAuthor->SetXTimeEstimatedCoordinate(0);
500     newAuthor->SetYTimeEstimatedCoordinate(0);
501     newAuthor->SetXTimeSpentCoordinate(0);
502     newAuthor->SetYTimeSpentCoordinate(0);
503     newAuthor->SetTimeEstimated("0 h");
504     newAuthor->SetTimeSpent("0 h");
505
506     newAuthor->SetName("Author " + std::to_string(mAuthors.size()+1));
507     mAuthors.push_back(newAuthor);
508 }
509
510 void Model::chapterRemove()
511 {
512     if(getNumChapters()-1>0)
513     {
514         std::vector <Chapter::SPtr>::iterator it;
515         for( it = mChapters.begin()+mChapterNum-1; it != mChapters.end(); it++)
516             (*it)->SetNum((*it)->GetNum()-1);
517
518         mChapters.erase(mChapters.begin() + mChapterNum-1);
519         mChapterNum=1;
520     }
521 }
522
523 void Model::authorRemove()
524 {
525     if(mAuthors.size()-1>0)
526     {
527         std::vector <Author::SPtr>::iterator it;
528         for( it = mAuthors.begin()+mAuthorNum; it != mAuthors.end(); it++)
529         {
530             if((*it)->GetName() == "Author " + std::to_string((*it)->GetNum()))
531                 (*it)->SetName("Author " + std::to_string((*it)->GetNum()-1));
532
533             (*it)->SetNum((*it)->GetNum()-1);
534         }
535
536         mAuthors.erase(mAuthors.begin() + mAuthorNum);
537         mAuthorNum=0;
538     }
539 }
540
541 void Model::setOutPath(std::string outPath)
542 {
543     if(!mFSHelp.dirExists(outPath))
544         throw std::string("Output path not found!");
545
546     mOutPath = mFSHelp.addDirLastSlash(outPath);
547
548     if(!mFSHelp.createFile(mOutPath + "Log.txt"))
549         throw std::string("Could not create log file!");
550
551
552     // Lade text aus ev. existierenden Dateien
553     for(auto curChapt : mChapters)
554     {
555         std::ifstream iFstr;
556         iFstr.open(mOutPath + curChapt->GetOutFileName());
557         if(iFstr.good())
558         {
559             std::stringstream buffer;
560             buffer << iFstr.rdbuf();
561             if(buffer.good())
562                 curChapt->SetText(buffer.str());
563             else
564                 throw std::string("Error reading existing chapter file!");
565
566         }
567         iFstr.close();
568     }
569 }
570
571 void Model::generateDocument()
572 {
573     // Überprüfe dateien + pfade und versuche diese zu kopieren
574     if(!mFSHelp.dirExists(mOutPath))
575         throw std::string("Output path not found! Please set an existing output directory.");
576
577     if(!mFSHelp.copyFile(mConfDir + "packages.tex", mOutPath + "packages.tex"))
578         throw std::string("Failed to copy packages.tex!");
579     if(!mFSHelp.copyFile(mConfDir + "doxygen.sty", mOutPath + "doxygen.sty"))
580         throw std::string("Failed to copy doxygen.sty!");
581     if(!mFSHelp.copyFile(mConfDir + "setup_doxygen.tex", mOutPath + "setup_doxygen.tex"))
582         throw std::string("Failed to copy setup_doxygen.tex!");
583     if(!mFSHelp.copyFile(mConfDir + "codesettings.tex", mOutPath + "codesettings.tex"))
584         throw std::string("Failed to copy codesettings.tex!");
585     if(!mFSHelp.copyFile(mConfDir + "main.tex", mOutPath + "main.tex"))
586         throw std::string("Failed to copy main.tex");
587     if(!mFSHelp.copyFile(mConfDir + "docsettings.tex", mOutPath + "docsettings.tex"))
588         throw std::string("Failed to copy docsettings.tex");
589
590     // Öffne main.tex
591     std::ofstream mainFile;
592     mainFile.open( mOutPath + "main.tex", std::ios_base::app);
593     if(!mainFile.good())
594         throw std::string("Could not open main.tex");
595
596     // Füge subjec name hinzu
597     mainFile << "\\newcommand{\\headerleft}{" << mSubject <<"}" << std::endl;
598
599     // Füge Abteilug hinzu
600     mainFile << "\\newcommand{\\department}{" << mDepartment <<"}" << std::endl;
601
602     // Füge dokumentennamen hinzu
603     mainFile << "\\title{" << mDocName << "}" << std::endl;
604
605     mainFile << "\\date{\\today{}, " << mLocation << "}" << std::endl;
606
607     mainFile << "\\author{";
608     for(auto curAuthor : mAuthors)
609     {
610         mainFile << curAuthor->GetName();
611         if(curAuthor != *(mAuthors.end()-1))
612             mainFile << ", ";
613     }
614     mainFile << "}" << std::endl;
615
616
617     mainFile << std::endl;
618     mainFile << "\\input{codesettings.tex}" << std::endl;
619     mainFile << "\\begin{document}" << std::endl;
620     mainFile << "\\input{docsettings.tex}" << std::endl;
621     mainFile << std::endl;
622
623     // FÜge kapitel hinzu
624     for(auto curChap : mChapters)
625     {
626         curChap->WriteFile(mOutPath);
627         mainFile << "\t\\chapter{" << curChap->GetPrettyName() << "}" << std::endl;
628         mainFile << "\t\t\\input{" << curChap->GetOutFileName() << "}" << std::endl;
629         mainFile << std::endl;
630     }
631
632
633     //Füge Angabenseite hinzu
634     if(shouldAddCoverSheet())
635     {
636         // Öffne docsettings.tex
637         std::ofstream docSettingsFile;
638         docSettingsFile.open( mOutPath + "docsettings.tex", std::ios_base::app);
639         if(!docSettingsFile.good())
640             throw std::string("Could not open docsettings.tex");
641
642
643         docSettingsFile << "\\begin{titlepage}\n\t\\includepdf[pages={-},\n\t\tpicturecommand*={" << std::endl;
644         for(auto curAuthor : mAuthors)
645         {
646             docSettingsFile << "\t\t\\put(" << curAuthor->GetXNameCoordinate() << "," << curAuthor->GetYNameCoordinate() << "){" << curAuthor->GetName() << "}" << std::endl;
647             docSettingsFile << "\t\t\\put(" << curAuthor->GetXTimeEstimatedCoordinate() << "," << curAuthor->GetYTimeEstimatedCoordinate() << "){" << curAuthor->GetTimeEstimated() << "}" << std::endl;
648             docSettingsFile << "\t\t\\put(" << curAuthor->GetXTimeSpentCoordinate() << "," << curAuthor->GetYTimeSpentCoordinate() << "){" << curAuthor->GetTimeSpent() << "}" << std::endl;
649         }
650
651         docSettingsFile << "\t\t\\put(" << mDepartmentX << "," << mDepartmentY << "){" << mDepartment << "}" << std::endl;
652
653         docSettingsFile << "\t}]{coversheet.pdf}\n\\end{titlepage}" << std::endl;
654
655         docSettingsFile.close();
656     }
657
658     std::ofstream docSettingsFile;
659     docSettingsFile.open( mOutPath + "docsettings.tex", std::ios_base::app);
660     if(!docSettingsFile.good())
661         throw std::string("Could not open docsettings.tex");
662
663     // Hardcoded settings
664     docSettingsFile << "\\maketitle" << std::endl;
665     docSettingsFile << "\\tableofcontents" << std::endl;
666     docSettingsFile << "\\setcounter{tocdepth}{3}" << std::endl;
667     docSettingsFile << "\\newpage" << std::endl;
668
669     docSettingsFile.close();
670
671
672     if(shouldGenDoxygen())
673     {
674         if(!mFSHelp.copyFolder(mSourcePath, mOutPath + "src"))
675             throw std::string("Failed to copy sourcecode directory!");
676
677         if(!mFSHelp.copyFile(mConfDir + "doxyfile", mOutPath + "doxyfile"))
678             throw std::string("Failed to copy doxyfile!");
679
680         if(!mFSHelp.makeDir(mOutPath + "doxygen"))
681             throw std::string("Failed to create doxygen output directory!");
682
683         // Starte DoxyGen
684         system(std::string("cd " + mOutPath + " && doxygen " + mOutPath + "doxyfile >> " + mOutPath + "Log.txt").c_str());
685
686         if(!mFSHelp.copyFile(mOutPath + "doxygen/doxygen.sty", mOutPath + "doxygen.sty"))
687             throw std::string("Failed to copy doxygen.sty!");
688
689         std::vector<FSHelper::File> doxyFiles;
690         if(!mFSHelp.listFilesRecursive(mOutPath + "doxygen", doxyFiles, true))
691             throw std::string("Failed to get filenames of source files!");
692
693         // Nach class_ filtern
694         auto it = std::remove_if(doxyFiles.begin(),
695                                  doxyFiles.end(),
696                                  [](FSHelper::File x)
697         {
698
699             unsigned int ind = x.second.find_first_of("_");
700             if(ind ==  std::string::npos) return true; //Keine Dateiendung
701
702             // Daeien beginnend mit class_
703             if((x.second.substr(0, ind+1) == "class_"))
704                 return false;
705
706             // Alle anderen
707             return true;
708         });
709         doxyFiles.erase(it, doxyFiles.end());
710
711         // Nach namen sortieren
712         std::sort(doxyFiles.begin(), doxyFiles.end(),
713                   [](FSHelper::File left, FSHelper::File right)
714         {
715             return left.second < right.second;
716         }
717
718                  );
719
720         std::ofstream out;
721         out.open(mOutPath + "doxyfiles.tex", std::ios_base::out);
722         if(out.good())
723         {
724             out << "\\input{doxygen/hierarchy.tex}" << std::endl;
725             out << "\\input{doxygen/annotated.tex}" << std::endl;
726             for(auto curElem : doxyFiles)
727             {
728                 curElem.first.replace(curElem.first.find(mOutPath), mOutPath.length(), ""); //Erzeuge relativen Pfade
729                 out << "\\input{" << mFSHelp.convertPathToUnix(curElem.first) << curElem.second <<"}" << std::endl;
730             }
731         }
732         else
733             throw std::string("Failed to write doxygen chapter.");
734
735         out.close();
736
737         mainFile << "\t\\chapter{" << mDoxygenName << "}" << std::endl;
738         mainFile << "\t\t\\input{doxyfiles.tex}" << std::endl;
739         mainFile << std::endl;
740     }
741
742     // Füge Quellcode hinzu
743     if(shouldAddSource())
744     {
745         if(!mFSHelp.copyFolder(mSourcePath, mOutPath + "src"))
746             throw std::string("Failed to copy sourcecode directory!");
747
748         std::vector<FSHelper::File> sourceFiles;
749         if(!mFSHelp.listFilesRecursive(mOutPath + "src", sourceFiles, true))
750             throw std::string("Failed to get filenames of source files!");
751
752
753
754         // Nach dateiendung filtern
755         auto it = std::remove_if(sourceFiles.begin(),
756                                  sourceFiles.end(),
757                                  [this](FSHelper::File x)
758         {
759
760             unsigned int ind = x.second.find_last_of(".");
761             if(ind ==  std::string::npos) return true; //Keine Dateiendung
762
763             // Dateiendung cpp oder h
764             if((x.second.substr(ind) == mSourceExtension) ||
765                     (x.second.substr(ind) == mHeaderExtension))
766                 return false;
767
768             // Alle anderen
769             return true;
770         });
771         sourceFiles.erase(it, sourceFiles.end());
772
773         // Nach namen sortieren (header vor cpp)
774         std::sort(sourceFiles.begin(), sourceFiles.end(),
775                   [](FSHelper::File left, FSHelper::File right)
776         {
777             return left.second > right.second;
778         }
779
780                  );
781
782
783
784         std::ofstream out;
785         out.open(mOutPath + "sourcefiles.tex", std::ios_base::out);
786         if(out.good())
787             for(auto curElem : sourceFiles)
788             {
789                 curElem.first.replace(curElem.first.find(mOutPath), mOutPath.length(), ""); //Erzeuge relativen Pfade
790                 out << "\\lstinputlisting[language=C++]{" << mFSHelp.convertPathToUnix(curElem.first) << curElem.second <<"}" << std::endl;
791             }
792         else
793             throw std::string("Failed to write sourcefiles chapter.");
794
795         out.close();
796
797         mainFile << "\t\\chapter{" << mSourceChapterName << "}" << std::endl;
798         mainFile << "\t\t\\input{sourcefiles.tex}" << std::endl;
799         mainFile << std::endl;
800     }
801
802     if(shouldAddCoverSheet())
803         if(!mFSHelp.copyFile(mCoverSheet, mOutPath + "coversheet.pdf"))
804             throw std::string("Failed to copy cover sheet!");
805
806     if(!mFSHelp.copyFolder(mConfDir + "resources", mOutPath + "resources"))
807         throw std::string("Failed to copy resource directory!");
808
809     mainFile << "\\end{document}" << std::endl;
810     mainFile.close();
811
812     //start dokument generierung
813     system(std::string("cd " + mOutPath + " && pdflatex -draftmode -interaction=nonstopmode main.tex >> " + mOutPath + "Log.txt").c_str());
814     system(std::string("cd " + mOutPath + " && pdflatex -interaction=nonstopmode main.tex >> " + mOutPath + "Log.txt").c_str());
815 }
816
817 void Model::saveTemplFile()
818 {
819     tinyxml2::XMLDocument outDoc;
820
821     tinyxml2::XMLNode * pDocSettings = outDoc.NewElement("docsettings");
822     tinyxml2::XMLNode * pChapters = outDoc.NewElement("chapters");
823
824     outDoc.InsertFirstChild(pDocSettings);
825     outDoc.InsertAfterChild(pDocSettings, pChapters);
826
827     // DocSettings
828     tinyxml2::XMLElement * pSubjName = outDoc.NewElement("subjectname");
829     tinyxml2::XMLElement * pLocation = outDoc.NewElement("location");
830     tinyxml2::XMLElement * pCoverSheet = outDoc.NewElement("cover_sheet");
831     pSubjName->SetText(mSubject.c_str());
832     pLocation->SetText(mLocation.c_str());
833     pCoverSheet->SetText(mHasCoverSheet);
834     pDocSettings->InsertEndChild(pSubjName);
835     pDocSettings->InsertEndChild(pLocation);
836     pDocSettings->InsertEndChild(pCoverSheet);
837
838     // Fileextensions
839     tinyxml2::XMLNode * pFileExtensions= outDoc.NewElement("fileextensions");
840     tinyxml2::XMLElement * pFileExtHeader = outDoc.NewElement("header");
841     tinyxml2::XMLElement * pFileExtSource= outDoc.NewElement("source");
842     if(mHeaderExtension.empty() || mSourceExtension.empty())
843         throw std::string("Please set header and source file extension to use!");
844
845     pFileExtHeader->SetText(mHeaderExtension.c_str());
846     pFileExtSource->SetText(mSourceExtension.c_str());
847     pFileExtensions->InsertEndChild(pFileExtHeader);
848     pFileExtensions->InsertEndChild(pFileExtSource);
849     pDocSettings->InsertEndChild(pFileExtensions);
850
851     // Doxygen
852     tinyxml2::XMLNode * pDoxyGen = outDoc.NewElement("doxygen");
853     tinyxml2::XMLElement * pDoxyEnabled = outDoc.NewElement("enabled");
854     tinyxml2::XMLElement * pDoxyChaptName = outDoc.NewElement("chaptername");
855     pDoxyEnabled->SetText(mDoxygen);
856     if(mDoxygen)
857         pDoxyChaptName->SetText(mDoxygenName.c_str());
858     else
859         pDoxyChaptName->SetText("Not set");
860     pDoxyGen->InsertEndChild(pDoxyChaptName);
861     pDoxyGen->InsertEndChild(pDoxyEnabled);
862     pDocSettings->InsertEndChild(pDoxyGen);
863
864     // Department
865     tinyxml2::XMLNode * pDepartment = outDoc.NewElement("department");
866     tinyxml2::XMLElement * pDepX = outDoc.NewElement("x_coor");
867     tinyxml2::XMLElement * pDepY = outDoc.NewElement("y_coor");
868     pDepX->SetText(mDepartmentX);
869     pDepY->SetText(mDepartmentY);
870     pDepartment->InsertEndChild(pDepX);
871     pDepartment->InsertEndChild(pDepY);
872     pDocSettings->InsertEndChild(pDepartment);
873
874     // Sourcecode
875     tinyxml2::XMLNode * pSource= outDoc.NewElement("sourcecode");
876     tinyxml2::XMLElement * pSrcEnabled = outDoc.NewElement("enabled");
877     tinyxml2::XMLElement * pSrcChaptName = outDoc.NewElement("chaptername");
878     pSrcEnabled->SetText(mSourceChapter);
879     if(mSourceChapter)
880         pSrcChaptName->SetText(mSourceChapterName.c_str());
881     else
882         pSrcChaptName->SetText("Not set");
883     pSource->InsertEndChild(pSrcEnabled);
884     pSource->InsertEndChild(pSrcChaptName);
885     pDocSettings->InsertEndChild(pSource);
886
887     // Speichere Kapitel zu XML
888     tinyxml2::XMLNode * pAuthors = outDoc.NewElement("authors");
889     for(auto curAuth: mAuthors)
890     {
891
892         tinyxml2::XMLNode * pAuthor = outDoc.NewElement("author");
893         tinyxml2::XMLElement * pNum = outDoc.NewElement("num");
894         tinyxml2::XMLElement * pNameX = outDoc.NewElement("x_coor");
895         tinyxml2::XMLElement * pNameY = outDoc.NewElement("y_coor");
896         tinyxml2::XMLNode * pTimeEst= outDoc.NewElement("time_estimated");
897         tinyxml2::XMLNode * pTimeSpent = outDoc.NewElement("time_spent");
898
899         tinyxml2::XMLElement * pTimeEstX = outDoc.NewElement("x_coor");
900         tinyxml2::XMLElement * pTimeEstY = outDoc.NewElement("y_coor");
901
902         tinyxml2::XMLElement * pTimeSpenX = outDoc.NewElement("x_coor");
903         tinyxml2::XMLElement * pTimeSpenY = outDoc.NewElement("y_coor");
904
905         pNum->SetText(curAuth->GetNum());
906         pNameX->SetText(curAuth->GetXNameCoordinate());
907         pNameY->SetText(curAuth->GetYNameCoordinate());
908         pTimeSpenX->SetText(curAuth->GetXTimeSpentCoordinate());
909         pTimeSpenY->SetText(curAuth->GetYTimeSpentCoordinate());
910         pTimeEstX->SetText(curAuth->GetXTimeEstimatedCoordinate());
911         pTimeEstY->SetText(curAuth->GetYTimeEstimatedCoordinate());
912
913         pAuthor->InsertEndChild(pNum);
914         pAuthor->InsertEndChild(pNameX);
915         pAuthor->InsertEndChild(pNameY);
916
917         pTimeEst->InsertEndChild(pTimeEstX);
918         pTimeEst->InsertEndChild(pTimeEstY);
919         pAuthor->InsertEndChild(pTimeEst);
920
921         pTimeSpent->InsertEndChild(pTimeSpenX);
922         pTimeSpent->InsertEndChild(pTimeSpenY);
923         pAuthor->InsertEndChild(pTimeSpent);
924
925         pAuthors->InsertEndChild(pAuthor);
926     }
927     pDocSettings->InsertEndChild(pAuthors);
928
929     // Speichere Kapitel zu XML
930     for(auto curChapt : mChapters)
931     {
932
933         tinyxml2::XMLNode * pChapter = outDoc.NewElement("chapter");
934         tinyxml2::XMLElement * pNum = outDoc.NewElement("num");
935         tinyxml2::XMLElement * pName = outDoc.NewElement("prettyName");
936         tinyxml2::XMLElement * pFileName = outDoc.NewElement("filename");
937         tinyxml2::XMLElement * pText = outDoc.NewElement("textTemplate");
938
939         pNum->SetText(curChapt->GetNum());
940         pName->SetText(curChapt->GetPrettyName().c_str());
941         pFileName->SetText(curChapt->GetOutFileName().c_str());
942
943         if(curChapt->GetText().empty())
944             pText->SetText("% Enter text here.");
945         else
946             pText->SetText(curChapt->GetText().c_str());
947
948         pChapter->InsertEndChild(pName);
949         pChapter->InsertEndChild(pNum);
950         pChapter->InsertEndChild(pFileName);
951         pChapter->InsertEndChild(pText);
952
953         pChapters->InsertEndChild(pChapter);
954     }
955
956     throwXmlError(outDoc.SaveFile(std::string(mOutPath + mSubject + ".xml").c_str()));
957 }
958
959 bool Model::shouldAddCoverSheet()
960 {
961     return mHasCoverSheet;
962 }
963
964 bool Model::shouldGenDoxygen()
965 {
966     return mDoxygen;
967 }
968
969 bool Model::shouldAddSource()
970 {
971     return mSourceChapter;
972 }
973
974 std::string Model::getSourceDir()
975 {
976     return mSourcePath;
977 }
978
979 std::string Model::getCovSheet()
980 {
981     return mCoverSheet;
982 }
983
984 void Model::setSourceDir(std::string path)
985 {
986     if(!mFSHelp.dirExists(path))
987         throw std::string("Source directory not found!");
988
989     mSourcePath = mFSHelp.addDirLastSlash(path);
990 }
991
992 void Model::setCovSheet(std::string file)
993 {
994     if(!mFSHelp.fileExists(file))
995         throw std::string("File not found!");
996     mCoverSheet = file;
997 }
998
999 std::string Model::getConfDir()
1000 {
1001     return mConfDir;
1002 }
1003
1004 void Model::saveChaptersToFiles()
1005 {
1006     for(auto curChap : mChapters)
1007         curChap->WriteFile(mOutPath);
1008 }
1009
1010 bool Model::getGenerateEnable()
1011 {
1012     if(getNumChapters() == getNumFinishedChapters())
1013     {
1014
1015         if(shouldAddCoverSheet())
1016             if(!mFSHelp.fileExists(mCoverSheet))
1017                 return false;
1018
1019         if(shouldAddSource())
1020             if(!mFSHelp.dirExists(mSourcePath))
1021                 return false;
1022
1023         if(mDocName.empty())
1024             return false;
1025
1026         if(mDepartment.empty())
1027             return false;
1028
1029         return true;
1030     }
1031     return false;
1032 }
1033
1034 std::string Model::getSourceChapterName()
1035 {
1036     return mSourceChapterName;
1037 }
1038
1039 std::string Model::getDoxygenName()
1040 {
1041     return mDoxygenName;
1042 }
1043
1044 void Model::setAddCoverSheet(bool val)
1045 {
1046     mHasCoverSheet = val;
1047 }
1048
1049 void Model::setAddSource(bool val)
1050 {
1051     mSourceChapter = val;
1052 }
1053
1054 void Model::setGenDoxygen(bool val)
1055 {
1056     mDoxygen = val;
1057 }
1058
1059 void Model::setSourceChapterName(std::string name)
1060 {
1061     mSourceChapterName = name;
1062 }
1063
1064 void Model::setDoxygenName(std::string name)
1065 {
1066     mDoxygenName = name;
1067 }
1068
1069 std::string Model::getDocName()
1070 {
1071     return mDocName;
1072 }
1073
1074 void Model::setDocName(std::string name)
1075 {
1076     mDocName = name;
1077 }
1078
1079 std::string Model::getLocation()
1080 {
1081     return mLocation;
1082 }
1083
1084 std::string Model::getDepartment()
1085 {
1086     return mDepartment;
1087 }
1088
1089 void Model::setLocation(std::string location)
1090 {
1091     mLocation = location;
1092 }
1093
1094 void Model::setDepartment(std::string department)
1095 {
1096     mDepartment = department;
1097 }
1098
1099 int Model::getDepartmentX()
1100 {
1101     return mDepartmentX;
1102 }
1103
1104 int Model::getDepartmentY()
1105 {
1106     return mDepartmentY;
1107 }
1108
1109 void Model::setDepartmentX(unsigned int x)
1110 {
1111     mDepartmentX = x;
1112 }
1113
1114 void Model::setDepartmentY(unsigned int y)
1115 {
1116     mDepartmentY = y;
1117 }
1118
1119 std::string Model::getOutPath()
1120 {
1121     return mOutPath;
1122 }
1123
1124 std::string Model::getHeaderFileExtension()
1125 {
1126     return mHeaderExtension;
1127 }
1128
1129 std::string Model::getSourceFileExtension()
1130 {
1131     return mSourceExtension;
1132 }
1133
1134 void Model::setHeaderFileExtension(std::string fileExt)
1135 {
1136     if(fileExt.empty())
1137         throw std::string("Please set a header file extension!");
1138
1139     mHeaderExtension = fileExt;
1140 }
1141
1142 void Model::setSourceFileExtension(std::string fileExt)
1143 {
1144     if(fileExt.empty())
1145         throw std::string("Please set a source file extension!");
1146
1147     mSourceExtension = fileExt;
1148 }