1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
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.
8 *
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.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "project.h"
18#include "editor.h"
19#include "utils.h"
20#include "systemconsts.h"
21#include "editorlist.h"
22#include <parser/cppparser.h>
23#include "utils.h"
24#include "qt_utils/charsetinfo.h"
25#include "projecttemplate.h"
26#include "systemconsts.h"
27#include "iconsmanager.h"
28
29#include <QFileSystemWatcher>
30#include <QDir>
31#include <QFileDialog>
32#include <QFileInfo>
33#include <QMessageBox>
34#include <QTextCodec>
35#include <QMessageBox>
36#include <QDirIterator>
37#include <QMimeDatabase>
38#include <QDesktopServices>
39#include "customfileiconprovider.h"
40#include <QMimeData>
41#include "settings.h"
42#include "vcs/gitrepository.h"
43
44Project::Project(const QString &filename, const QString &name,
45 EditorList* editorList,
46 QFileSystemWatcher* fileSystemWatcher,
47 QObject *parent) :
48 QObject(parent),
49 mModel(this),
50 mEditorList(editorList),
51 mFileSystemWatcher(fileSystemWatcher)
52{
53 mFilename = QFileInfo(filename).absoluteFilePath();
54 mParser = std::make_shared<CppParser>();
55 mParser->setOnGetFileStream(
56 std::bind(
57 &EditorList::getContentFromOpenedEditor,mEditorList,
58 std::placeholders::_1, std::placeholders::_2));
59 if (name == DEV_INTERNAL_OPEN) {
60 mModified = false;
61 open();
62 } else {
63 mName = name;
64 SimpleIni ini;
65 ini.SetValue("Project","filename", toByteArray(extractRelativePath(directory(),mFilename)));
66 ini.SetValue("Project","name", toByteArray(mName));
67 ini.SaveFile(mFilename.toLocal8Bit());
68 mRootNode = makeProjectNode();
69 }
70 resetCppParser(mParser,mOptions.compilerSet);
71}
72
73Project::~Project()
74{
75 mEditorList->beginUpdate();
76 foreach (const PProjectUnit& unit, mUnits) {
77 Editor * editor = unitEditor(unit);
78 if (editor) {
79 mEditorList->forceCloseEditor(editor);
80 }
81 }
82 mEditorList->endUpdate();
83}
84
85QString Project::directory() const
86{
87 QFileInfo fileInfo(mFilename);
88 return fileInfo.absolutePath();
89}
90
91QString Project::executable() const
92{
93 QString exeFileName;
94 if (mOptions.overrideOutput && !mOptions.overridenOutput.isEmpty()) {
95 exeFileName = mOptions.overridenOutput;
96 } else {
97 switch(mOptions.type) {
98 case ProjectType::StaticLib:
99 exeFileName = changeFileExt(extractFileName(mFilename),STATIC_LIB_EXT);
100 break;
101 case ProjectType::DynamicLib:
102 exeFileName = changeFileExt(extractFileName(mFilename),DYNAMIC_LIB_EXT);
103 break;
104 default:
105 exeFileName = changeFileExt(extractFileName(mFilename),EXECUTABLE_EXT);
106 }
107 }
108 QString exePath;
109 if (!mOptions.exeOutput.isEmpty()) {
110 QDir baseDir(directory());
111 exePath = baseDir.filePath(mOptions.exeOutput);
112 } else {
113 exePath = directory();
114 }
115 QDir exeDir(exePath);
116 return exeDir.filePath(exeFileName);
117}
118
119QString Project::makeFileName()
120{
121 if (mOptions.useCustomMakefile)
122 return mOptions.customMakefile;
123 else
124 return QDir(directory()).filePath(MAKEFILE_NAME);
125}
126
127bool Project::modified() const
128{
129 // Project file modified? Done
130 if (mModified)
131 return true;// quick exit avoids loop over all units
132
133 // Otherwise, check all units
134 foreach (const PProjectUnit& unit, mUnits){
135 if (unit->modified()) {
136 return true;
137 }
138 }
139 return false;
140}
141
142void Project::open()
143{
144 mModel.beginUpdate();
145 auto action = finally([this]{
146 mModel.endUpdate();
147 });
148// QFile fileInfo(mFilename);
149 SimpleIni ini;
150 ini.LoadFile(mFilename.toLocal8Bit());
151 loadOptions(ini);
152
153 mRootNode = makeProjectNode();
154
155 checkProjectFileForUpdate(ini);
156 int uCount = ini.GetLongValue("Project","UnitCount",0);
157 createFolderNodes();
158 QDir dir(directory());
159 for (int i=0;i<uCount;i++) {
160 PProjectUnit newUnit = std::make_shared<ProjectUnit>(this);
161 QByteArray groupName = toByteArray(QString("Unit%1").arg(i+1));
162 newUnit->setFileName(
163 dir.absoluteFilePath(
164 fromByteArray(ini.GetValue(groupName,"FileName",""))));
165// if (!QFileInfo(newUnit->fileName()).exists()) {
166// QMessageBox::critical(nullptr,
167// tr("File Not Found"),
168// tr("Project file '%1' can't be found!")
169// .arg(newUnit->fileName()),
170// QMessageBox::Ok);
171// newUnit->setModified(true);
172// } else {
173 newUnit->setFileMissing(!QFileInfo(newUnit->fileName()).exists());
174 newUnit->setFolder(fromByteArray(ini.GetValue(groupName,"Folder","")));
175 newUnit->setCompile(ini.GetBoolValue(groupName,"Compile", true));
176 newUnit->setCompileCpp(
177 ini.GetBoolValue(groupName,"CompileCpp",mOptions.isCpp));
178
179 newUnit->setLink(ini.GetBoolValue(groupName,"Link", true));
180 newUnit->setPriority(ini.GetLongValue(groupName,"Priority", 1000));
181 newUnit->setOverrideBuildCmd(ini.GetBoolValue(groupName,"OverrideBuildCmd", false));
182 newUnit->setBuildCmd(fromByteArray(ini.GetValue(groupName,"BuildCmd", "")));
183 QByteArray defaultEncoding = toByteArray(mOptions.encoding);
184 if (ini.GetBoolValue(groupName,"DetectEncoding",true)){
185 defaultEncoding = ENCODING_AUTO_DETECT;
186 }
187 newUnit->setEncoding(ini.GetValue(groupName, "FileEncoding",defaultEncoding));
188 if (QTextCodec::codecForName(newUnit->encoding())==nullptr) {
189 newUnit->setEncoding(ENCODING_AUTO_DETECT);
190 }
191 newUnit->setNew(false);
192 newUnit->setParent(this);
193 newUnit->setNode(makeNewFileNode(extractFileName(newUnit->fileName()), false, folderNodeFromName(newUnit->folder())));
194 newUnit->node()->unitIndex = mUnits.count();
195 mUnits.append(newUnit);
196 }
197 rebuildNodes();
198}
199
200void Project::setFileName(QString value)
201{
202 value = QFileInfo(value).absoluteFilePath();
203 if (mFilename!=value) {
204 QFile::rename(mFilename,value);
205 mFilename = value;
206 setModified(true);
207 }
208}
209
210void Project::setModified(bool value)
211{
212 if (mModified!=value) {
213 mModified=value;
214 emit modifyChanged(mModified);
215 }
216}
217
218PProjectModelNode Project::makeNewFileNode(const QString &s, bool isFolder, PProjectModelNode newParent)
219{
220 PProjectModelNode node = std::make_shared<ProjectModelNode>();
221 if (!newParent) {
222 newParent = mRootNode;
223 }
224 newParent->children.append(node);
225 node->parent = newParent;
226 node->text = s;
227 if (newParent) {
228 node->level = newParent->level+1;
229 }
230 if (isFolder) {
231 node->unitIndex = -1;
232 node->priority = 0;
233 node->folderNodeType = ProjectSpecialFolderNode::NonSpecial;
234 } else {
235 node->folderNodeType = ProjectSpecialFolderNode::NotFolder;
236 }
237 return node;
238}
239
240PProjectModelNode Project::makeProjectNode()
241{
242 PProjectModelNode node = std::make_shared<ProjectModelNode>();
243 node->text = mName;
244 node->level = 0;
245 node->unitIndex = -1;
246 node->folderNodeType = ProjectSpecialFolderNode::NonSpecial;
247 return node;
248}
249
250PProjectUnit Project::newUnit(PProjectModelNode parentNode, const QString& customFileName)
251{
252 PProjectUnit newUnit = std::make_shared<ProjectUnit>(this);
253
254 // Select folder to add unit to
255 if (!parentNode)
256 parentNode = mRootNode; // project root node
257
258 if (parentNode->unitIndex>=0) { //it's a file
259 parentNode = mRootNode;
260 }
261 QString s;
262 QDir dir(directory());
263 // Find unused 'new' filename
264 if (customFileName.isEmpty()) {
265 do {
266 s = dir.absoluteFilePath(tr("untitled")+QString("%1").arg(getNewFileNumber()));
267 } while (fileExists(s));
268 } else {
269 s = dir.absoluteFilePath(customFileName);
270 }
271 // Add
272 int count = mUnits.count();
273 mUnits.append(newUnit);
274
275 // Set all properties
276 newUnit->setFileName(s);
277 newUnit->setNew(true);
278 newUnit->setFolder(getFolderPath(parentNode));
279 newUnit->setNode(makeNewFileNode(extractFileName(newUnit->fileName()),
280 false, parentNode));
281 newUnit->node()->unitIndex = count;
282 //parentNode.Expand(True);
283 switch(getFileType(customFileName)) {
284 case FileType::CSource:
285 newUnit->setCompile(true);
286 newUnit->setCompileCpp(false);
287 newUnit->setLink(true);
288 break;
289 case FileType::CppSource:
290 newUnit->setCompile(true);
291 newUnit->setCompileCpp(true);
292 newUnit->setLink(true);
293 break;
294 case FileType::WindowsResourceSource:
295 newUnit->setCompile(true);
296 newUnit->setCompileCpp(mOptions.isCpp);
297 newUnit->setLink(false);
298 break;
299 default:
300 newUnit->setCompile(false);
301 newUnit->setCompileCpp(false);
302 newUnit->setLink(false);
303 }
304 newUnit->setPriority(1000);
305 newUnit->setOverrideBuildCmd(false);
306 newUnit->setBuildCmd("");
307 newUnit->setModified(true);
308 newUnit->setEncoding(toByteArray(mOptions.encoding));
309 return newUnit;
310}
311
312Editor *Project::openUnit(int index, bool forceOpen)
313{
314 if ((index < 0) || (index >= mUnits.count()))
315 return nullptr;
316
317 PProjectUnit unit = mUnits[index];
318
319 if (!unit->fileName().isEmpty() && fileExists(unit->fileName())) {
320 if (getFileType(unit->fileName())==FileType::Other) {
321 if (forceOpen)
322 QDesktopServices::openUrl(QUrl::fromLocalFile(unit->fileName()));
323 return nullptr;
324 }
325
326 Editor * editor = mEditorList->getOpenedEditorByFilename(unit->fileName());
327 if (editor) {//already opened in the editors
328 editor->setInProject(true);
329 editor->activate();
330 return editor;
331 }
332 QByteArray encoding;
333 encoding = unit->encoding();
334 editor = mEditorList->newEditor(unit->fileName(), encoding, true, unit->isNew());
335 if (editor) {
336 editor->setInProject(true);
337 //unit->setEncoding(encoding);
338 editor->activate();
339 loadUnitLayout(editor,index);
340 return editor;
341 }
342 }
343 return nullptr;
344}
345
346Editor *Project::unitEditor(const PProjectUnit &unit) const
347{
348 return mEditorList->getOpenedEditorByFilename(unit->fileName());
349}
350
351Editor *Project::unitEditor(const ProjectUnit *unit) const
352{
353 return mEditorList->getOpenedEditorByFilename(unit->fileName());
354}
355
356void Project::rebuildNodes()
357{
358 mModel.beginUpdate();
359 // Delete everything
360 mRootNode->children.clear();
361 mFolderNodes.clear();
362 mSpecialNodes.clear();
363 mFileSystemFolderNodes.clear();
364
365 // Recreate everything
366 switch(mOptions.modelType) {
367 case ProjectModelType::Custom:
368 createFolderNodes();
369
370 for (int idx=0;idx<mUnits.count();idx++) {
371 QFileInfo fileInfo(mUnits[idx]->fileName());
372 mUnits[idx]->setNode(
373 makeNewFileNode(
374 fileInfo.fileName(),
375 false,
376 folderNodeFromName(mUnits[idx]->folder())
377 )
378 );
379 mUnits[idx]->node()->unitIndex = idx;
380 mUnits[idx]->node()->priority = mUnits[idx]->priority();
381 }
382 break;
383 case ProjectModelType::FileSystem:
384 createFileSystemFolderNodes();
385
386 for (int idx=0;idx<mUnits.count();idx++) {
387 QFileInfo fileInfo(mUnits[idx]->fileName());
388 mUnits[idx]->setNode(
389 makeNewFileNode(
390 fileInfo.fileName(),
391 false,
392 getParentFolderNode(
393 mUnits[idx]->fileName())
394 )
395 );
396 mUnits[idx]->node()->unitIndex = idx;
397 mUnits[idx]->node()->priority = mUnits[idx]->priority();
398 }
399
400 break;
401 }
402
403 mModel.endUpdate();
404 emit nodesChanged();
405}
406
407bool Project::removeUnit(int index, bool doClose , bool removeFile)
408{
409 mModel.beginUpdate();
410 auto action = finally([this]{
411 mModel.endUpdate();
412 });
413 if (index<0 || index>=mUnits.count())
414 return false;
415
416 PProjectUnit unit = mUnits[index];
417
418// qDebug()<<unit->fileName();
419// qDebug()<<(qint64)unit->editor();
420 // Attempt to close it
421 if (doClose) {
422 Editor* editor = unitEditor(unit);
423 if (editor) {
424 editor->setInProject(false);
425 mEditorList->closeEditor(editor);
426 }
427 }
428
429 if (removeFile) {
430 QFile::remove(unit->fileName());
431 }
432
433//if not fUnits.GetItem(index).fNew then
434 PProjectModelNode node = unit->node();
435 PProjectModelNode parent = node->parent.lock();
436 if (parent) {
437 parent->children.removeAll(node);
438 }
439 mUnits.removeAt(index);
440 updateNodeIndexes();
441 setModified(true);
442 return true;
443}
444
445bool Project::removeFolder(PProjectModelNode node)
446{
447 mModel.beginUpdate();
448 auto action = finally([this]{
449 mModel.endUpdate();
450 });
451 // Sanity check
452 if (!node)
453 return false;
454
455 // Check if this is actually a folder
456 if (node->unitIndex>=0 || node->level<1)
457 return false;
458
459 // Let this function call itself
460 removeFolderRecurse(node);
461
462 // Update list of folders (sets modified)
463 updateFolders();
464 return true;
465}
466
467void Project::resetParserProjectFiles()
468{
469 mParser->clearProjectFiles();
470 mParser->clearProjectIncludePaths();
471 foreach (const PProjectUnit& unit, mUnits) {
472 mParser->addFileToScan(unit->fileName());
473 }
474 foreach (const QString& s, mOptions.includeDirs) {
475 mParser->addProjectIncludePath(s);
476 }
477}
478
479void Project::saveAll()
480{
481 if (!saveUnits())
482 return;
483 saveOptions(); // update other data, and save to disk
484 saveLayout(); // save current opened files, and which is "active".
485
486 // We have saved everything to disk, so mark unmodified
487 setModified(false);
488}
489
490void Project::saveLayout()
491{
492 SimpleIni layIni;
493 QStringList sl;
494 // Write list of open project files
495 for (int i=0;i<mEditorList->pageCount();i++) {
496 Editor* e= (*mEditorList)[i];
497 if (e && e->inProject())
498 sl.append(QString("%1").arg(indexInUnits(e)));
499 }
500 layIni.SetValue("Editors","Order",sl.join(",").toUtf8());
501
502 Editor *e, *e2;
503 // Remember what files were visible
504 mEditorList->getVisibleEditors(e, e2);
505 if (e)
506 layIni.SetLongValue("Editors","Focused", indexInUnits(e));
507 // save editor info
508 for (int i=0;i<mUnits.count();i++) {
509 QByteArray groupName = QString("Editor_%1").arg(i).toUtf8();
510 PProjectUnit unit = mUnits[i];
511 Editor* editor = unitEditor(unit);
512 if (editor) {
513 layIni.SetLongValue(groupName,"CursorCol", editor->caretX());
514 layIni.SetLongValue(groupName,"CursorRow", editor->caretY());
515 layIni.SetLongValue(groupName,"TopLine", editor->topLine());
516 layIni.SetLongValue(groupName,"LeftChar", editor->leftChar());
517 }
518 // remove old data from project file
519// SimpleIni ini;
520// ini.LoadFile(filename().toLocal8Bit());
521// groupName = toByteArray(QString("Unit%1").arg(i+1));
522// ini.Delete(groupName,"Open");
523// ini.Delete(groupName,"Top");
524// ini.Delete(groupName,"CursorCol");
525// ini.Delete(groupName,"CursorRow");
526// ini.Delete(groupName,"TopLine");
527// ini.Delete(groupName,"LeftChar");
528// ini.SaveFile(filename().toLocal8Bit());
529 }
530 layIni.SaveFile(changeFileExt(filename(), "layout").toLocal8Bit());
531}
532
533void Project::saveUnitAs(int i, const QString &sFileName, bool syncEditor)
534{
535 if ((i < 0) || (i >= mUnits.count()))
536 return;
537 PProjectUnit unit = mUnits[i];
538 if (fileExists(unit->fileName())) {
539 unit->setNew(false);
540 }
541 Editor * editor=unitEditor(unit);
542 if (editor && syncEditor) {
543 //prevent recurse
544 editor->saveAs(sFileName,true);
545 }
546 unit->setNew(false);
547 unit->setFileName(sFileName);
548 SimpleIni ini;
549 ini.LoadFile(mFilename.toLocal8Bit());
550 QByteArray groupName = toByteArray(QString("Unit%1").arg(i+1));
551 ini.SetValue(
552 groupName,
553 "FileName",
554 toByteArray(
555 extractRelativePath(
556 directory(),
557 sFileName)));
558 ini.SaveFile(mFilename.toLocal8Bit());
559 setModified(true);
560 if (!syncEditor) {
561 //the call it's from editor, we need to update model
562 mModel.beginUpdate();
563 mModel.endUpdate();
564 }
565}
566
567void Project::saveUnitLayout(Editor *e, int index)
568{
569 if (!e)
570 return;
571 SimpleIni layIni;
572 QByteArray groupName = (QString("Editor_%1").arg(index)).toUtf8();
573 layIni.SetLongValue(groupName,"CursorCol", e->caretX());
574 layIni.SetLongValue(groupName,"CursorRow", e->caretY());
575 layIni.SetLongValue(groupName,"TopLine", e->topLine());
576 layIni.SetLongValue(groupName,"LeftChar", e->leftChar());
577 layIni.SaveFile((changeFileExt(filename(), "layout")).toLocal8Bit());
578}
579
580bool Project::saveUnits()
581{
582 int count = 0;
583 SimpleIni ini;
584 SI_Error error = ini.LoadFile(mFilename.toLocal8Bit());
585 if (error != SI_Error::SI_OK)
586 return false;
587 for (int idx = 0; idx < mUnits.count(); idx++) {
588 PProjectUnit unit = mUnits[idx];
589 QByteArray groupName = toByteArray(QString("Unit%1").arg(count+1));
590 if (!unit->FileMissing()) {
591 bool rd_only = false;
592 if (unit->modified() && fileExists(unit->fileName())
593 && isReadOnly(unit->fileName())) {
594 // file is read-only
595 QMessageBox::critical(nullptr,
596 tr("Can't save file"),
597 tr("Can't save file '%1'").arg(unit->fileName()),
598 QMessageBox::Ok
599 );
600 rd_only = true;
601 }
602 if (!rd_only) {
603 if (!unit->save() && unit->isNew())
604 return false;
605 }
606 }
607
608 // saved new file or an existing file add to project file
609 ini.SetValue(
610 groupName,
611 "FileName",
612 toByteArray(
613 extractRelativePath(
614 directory(),
615 unit->fileName())));
616 count++;
617 switch(getFileType(unit->fileName())) {
618 case FileType::CHeader:
619 case FileType::CSource:
620 case FileType::CppHeader:
621 case FileType::CppSource:
622 ini.SetLongValue(groupName,"CompileCpp", unit->compileCpp());
623 break;
624 case FileType::WindowsResourceSource:
625 unit->setFolder("Resources");
626 default:
627 break;
628 }
629 unit->setNew(false);
630 ini.SetValue(groupName,"Folder", toByteArray(unit->folder()));
631 ini.SetLongValue(groupName,"Compile", unit->compile());
632 ini.SetLongValue(groupName,"Link", unit->link());
633 ini.SetLongValue(groupName,"Priority", unit->priority());
634 ini.SetLongValue(groupName,"OverrideBuildCmd", unit->overrideBuildCmd());
635 ini.SetValue(groupName,"BuildCmd", toByteArray(unit->buildCmd()));
636 ini.SetLongValue(groupName,"DetectEncoding", unit->encoding()==ENCODING_AUTO_DETECT);
637 if (unit->encoding() != options().encoding)
638 ini.SetValue(groupName,"FileEncoding", toByteArray(unit->encoding()));
639 }
640 ini.SetLongValue("Project","UnitCount",count);
641 ini.SaveFile(mFilename.toLocal8Bit());
642 return true;
643}
644
645PProjectUnit Project::findUnitByFilename(const QString &filename)
646{
647 foreach(PProjectUnit unit, mUnits) {
648 if (QString::compare(unit->fileName(),filename, PATH_SENSITIVITY)==0)
649 return unit;
650 }
651 return PProjectUnit();
652}
653
654void Project::associateEditor(Editor *editor)
655{
656 if (!editor)
657 return;
658 PProjectUnit unit = findUnitByFilename(editor->filename());
659 associateEditorToUnit(editor,unit);
660}
661
662void Project::associateEditorToUnit(Editor *editor, PProjectUnit unit)
663{
664 if (!unit) {
665 if (editor)
666 editor->setInProject(false);
667 return;
668 }
669 if (editor) {
670 unit->setEncoding(editor->encodingOption());
671 editor->setInProject(true);
672 }
673}
674
675//bool Project::setCompileOption(const QString &key, int valIndex)
676//{
677// Settings::PCompilerSet pSet = pSettings->compilerSets().getSet(mOptions.compilerSet);
678// if (!pSet)
679// return false;
680// PCompilerOption op = CompilerInfoManager::getCompilerOption(
681// pSet->compilerType(), key);
682// if (!op)
683// return false;
684// if (op->choices.isEmpty()) {
685// if (valIndex>0)
686// mOptions.compilerOptions.insert(key,COMPILER_OPTION_ON);
687// else
688// mOptions.compilerOptions.insert(key,"");
689// } else {
690// if (valIndex>0 && valIndex <= op->choices.length()) {
691// mOptions.compilerOptions.insert(key,op->choices[valIndex-1].second);
692// } else {
693// mOptions.compilerOptions.insert(key,"");
694// }
695// }
696// return true;
697//}
698
699bool Project::setCompileOption(const QString &key, const QString &value)
700{
701 Settings::PCompilerSet pSet = pSettings->compilerSets().getSet(mOptions.compilerSet);
702 if (!pSet)
703 return false;
704 PCompilerOption op = CompilerInfoManager::getCompilerOption(
705 pSet->compilerType(), key);
706 if (!op)
707 return false;
708 mOptions.compilerOptions.insert(key,value);
709 return true;
710}
711
712QString Project::getCompileOption(const QString &key) const
713{
714 return mOptions.compilerOptions.value(key,"");
715}
716
717void Project::updateFolders()
718{
719 mFolders.clear();
720 updateFolderNode(mRootNode);
721 for (int idx = 0; idx < mUnits.count();idx++)
722 mUnits[idx]->setFolder(
723 getFolderPath(
724 mUnits[idx]->node()->parent.lock()
725 )
726 );
727 setModified(true);
728}
729
730void Project::updateNodeIndexes()
731{
732 for (int idx = 0;idx<mUnits.count();idx++)
733 mUnits[idx]->node()->unitIndex = idx;
734}
735
736PProjectModelNode Project::pointerToNode(ProjectModelNode *p, PProjectModelNode parent)
737{
738 if (!p)
739 return PProjectModelNode();
740 if (!parent) {
741 parent = mRootNode;
742 }
743 if (p==mRootNode.get())
744 return mRootNode;
745 foreach (const PProjectModelNode& node , parent->children) {
746 if (node.get()==p)
747 return node;
748 PProjectModelNode result = pointerToNode(p,node);
749 if (result)
750 return result;
751 }
752 return PProjectModelNode();
753}
754
755void Project::setCompilerSet(int compilerSetIndex)
756{
757 if (mOptions.compilerSet != compilerSetIndex) {
758 mOptions.compilerSet = compilerSetIndex;
759 updateCompilerSetType();
760 setModified(true);
761 }
762}
763
764bool Project::assignTemplate(const std::shared_ptr<ProjectTemplate> aTemplate, bool useCpp)
765{
766 if (!aTemplate) {
767 return true;
768 }
769 mOptions = aTemplate->options();
770 mOptions.compilerSet = pSettings->compilerSets().defaultIndex();
771 mOptions.isCpp = useCpp;
772 updateCompilerSetType();
773 mOptions.icon = aTemplate->icon();
774
775 // Copy icon to project directory
776 if (!mOptions.icon.isEmpty()) {
777 QString originIcon = QFileInfo(aTemplate->fileName()).absoluteDir().absoluteFilePath(mOptions.icon);
778 if (fileExists(originIcon)) {
779 QString destIcon = QFileInfo(mFilename).absoluteDir().absoluteFilePath("app.ico");
780 QFile::copy(originIcon,destIcon);
781 mOptions.icon = destIcon;
782 } else {
783 mOptions.icon = "";
784 }
785 }
786 // Add list of files
787 if (aTemplate->version() > 0) {
788 QDir dir(aTemplate->folder());
789 for (int i=0;i<aTemplate->unitCount();i++) {
790 // Pick file contents
791 PTemplateUnit templateUnit = aTemplate->unit(i);
792 if (!templateUnit->Source.isEmpty()) {
793 QString target = templateUnit->Source;
794 PProjectUnit unit;
795 if (!templateUnit->Target.isEmpty())
796 target = templateUnit->Target;
797 QFile::copy(
798 dir.absoluteFilePath(templateUnit->Source),
799 includeTrailingPathDelimiter(this->directory())+target);
800 unit = newUnit(mRootNode, target);
801 } else {
802 QString s;
803 PProjectUnit unit;
804 if (mOptions.isCpp) {
805 s = templateUnit->CppText;
806 unit = newUnit(mRootNode, templateUnit->CppName);
807 } else {
808 s = templateUnit->CText;
809 unit = newUnit(mRootNode,templateUnit->CName);
810 }
811
812 Editor * editor = mEditorList->newEditor(
813 unit->fileName(),
814 unit->encoding(),
815 true,
816 true);
817
818 QString s2 = dir.absoluteFilePath(s);
819 if (fileExists(s2) && !s.isEmpty()) {
820 try {
821 editor->loadFile(s2);
822 } catch(FileError& e) {
823 QMessageBox::critical(nullptr,
824 tr("Error Load File"),
825 e.reason());
826 }
827 } else {
828 s.replace("#13#10","\r\n");
829 editor->insertString(s,false);
830 }
831 editor->save(true,false);
832 editor->activate();
833 }
834 }
835 }
836 rebuildNodes();
837 return true;
838}
839
840bool Project::saveAsTemplate(const QString &templateFolder,
841 const QString& name,
842 const QString& description,
843 const QString& category)
844{
845 QDir dir(templateFolder);
846 if (!dir.mkpath(templateFolder)) {
847 QMessageBox::critical(nullptr,
848 tr("Error"),
849 tr("Can't create folder %1 ").arg(templateFolder),
850 QMessageBox::Ok);
851 return false;
852 }
853
854 QString fileName = dir.absoluteFilePath(TEMPLATE_INFO_FILE);
855 PSimpleIni ini = std::make_shared<SimpleIni>();
856
857 ini->SetLongValue("Template","Ver",3);
858 // template info
859 ini->SetValue("Template", "Name", name.toUtf8());
860 ini->SetValue("Template", "Category", category.toUtf8());
861 ini->SetValue("Template", "Description", description.toUtf8());
862 if (fileExists(mOptions.icon)) {
863 QString iconName = extractFileName(mOptions.icon);
864 copyFile(mOptions.icon, dir.absoluteFilePath(iconName),true);
865 if (dir.exists(iconName))
866 ini->SetValue("Template", "Icon", iconName.toUtf8());
867 }
868
869 ini->SetLongValue("Project", "Type", static_cast<int>(mOptions.type));
870 if (!mOptions.objFiles.isEmpty())
871 ini->SetValue("Project", "ObjFiles", mOptions.objFiles.join(";").toUtf8());
872 if (!mOptions.includeDirs.isEmpty())
873 ini->SetValue("Project", "Includes", mOptions.includeDirs.join(";").toUtf8());
874 if (!mOptions.resourceIncludes.isEmpty())
875 ini->SetValue("Project", "ResourceIncludes", mOptions.resourceIncludes.join(";").toUtf8());
876 if (!mOptions.binDirs.isEmpty())
877 ini->SetValue("Project", "Bins", mOptions.binDirs.join(";").toUtf8());
878 if (!mOptions.libDirs.isEmpty())
879 ini->SetValue("Project", "Libs", mOptions.libDirs.join(";").toUtf8());
880 if (!mOptions.compilerCmd.isEmpty())
881 ini->SetValue("Project", "Compiler", mOptions.compilerCmd.toUtf8());
882 if (!mOptions.cppCompilerCmd.isEmpty())
883 ini->SetValue("Project", "CppCompiler", mOptions.cppCompilerCmd.toUtf8());
884 if (!mOptions.linkerCmd.isEmpty())
885 ini->SetValue("Project", "Linker",mOptions.linkerCmd.toUtf8());
886 ini->SetBoolValue("Project", "IsCpp", mOptions.isCpp);
887 if (mOptions.includeVersionInfo)
888 ini->SetBoolValue("Project", "IncludeVersionInfo", true);
889 if (mOptions.supportXPThemes)
890 ini->SetBoolValue("Project", "SupportXPThemes", true);
891 if (!mOptions.exeOutput.isEmpty())
892 ini->SetValue("Project", "ExeOutput", mOptions.exeOutput.toUtf8());
893 if (!mOptions.objectOutput.isEmpty())
894 ini->SetValue("Project", "ObjectOutput", mOptions.objectOutput.toUtf8());
895 if (!mOptions.logOutput.isEmpty())
896 ini->SetValue("Project", "LogOutput", mOptions.logOutput.toUtf8());
897 if (mOptions.execEncoding!=ENCODING_SYSTEM_DEFAULT)
898 ini->SetValue("Project","ExecEncoding", mOptions.execEncoding);
899
900// if (!mOptions.staticLink)
901// ini->SetBoolValue("Project", "StaticLink",false);
902 if (!mOptions.addCharset)
903 ini->SetBoolValue("Project", "AddCharset",false);
904 if (mOptions.encoding!=ENCODING_AUTO_DETECT)
905 ini->SetValue("Project","Encoding",mOptions.encoding.toUtf8());
906 if (mOptions.modelType!=ProjectModelType::FileSystem)
907 ini->SetLongValue("Project", "ModelType", (int)mOptions.modelType);
908
909 for (int i=0;i<mUnits.count();i++) {
910 const PProjectUnit& unit=mUnits[i];
911 QString unitName = extractFileName(unit->fileName());
912 QByteArray section = toByteArray(QString("Unit%1").arg(i));
913 if (!copyFile(unit->fileName(), dir.absoluteFilePath(unitName),true)) {
914 QMessageBox::warning(nullptr,
915 tr("Warning"),
916 tr("Can't save file %1").arg(dir.absoluteFilePath(unitName)),
917 QMessageBox::Ok);
918 }
919 switch(getFileType(unit->fileName())) {
920 case FileType::CSource:
921 ini->SetValue(section,"C", unitName.toUtf8());
922 ini->SetValue(section,"CName", unitName.toUtf8());
923 break;
924 case FileType::CppSource:
925 ini->SetValue(section,"Cpp", unitName.toUtf8());
926 ini->SetValue(section,"CppName", unitName.toUtf8());
927 break;
928 case FileType::CHeader:
929 case FileType::CppHeader:
930 ini->SetValue(section,"C", unitName.toUtf8());
931 ini->SetValue(section,"CName", unitName.toUtf8());
932 ini->SetValue(section,"Cpp", unitName.toUtf8());
933 ini->SetValue(section,"CppName", unitName.toUtf8());
934 break;
935 default:
936 ini->SetValue(section,"Source", unitName.toUtf8());
937 ini->SetValue(section,"Target", unitName.toUtf8());
938 }
939 }
940 ini->SetLongValue("Project","UnitCount",mUnits.count());
941 if (ini->SaveFile(fileName.toLocal8Bit())!=SI_OK) {
942 QMessageBox::critical(nullptr,
943 tr("Error"),
944 tr("Can't save file %1").arg(fileName),
945 QMessageBox::Ok);
946 return false;
947 }
948 return true;
949}
950
951void Project::saveOptions()
952{
953 SimpleIni ini;
954 ini.LoadFile(mFilename.toLocal8Bit());
955 ini.SetValue("Project","FileName", toByteArray(extractRelativePath(directory(), mFilename)));
956 ini.SetValue("Project","Name", toByteArray(mName));
957 ini.SetLongValue("Project","Type", static_cast<int>(mOptions.type));
958 ini.SetLongValue("Project","Ver", 3); // Is 3 as of Red Panda C++.0
959 ini.SetValue("Project","ObjFiles", toByteArray(mOptions.objFiles.join(";")));
960 ini.SetValue("Project","Includes", toByteArray(mOptions.includeDirs.join(";")));
961 ini.SetValue("Project","Libs", toByteArray(mOptions.libDirs.join(";")));
962 ini.SetValue("Project","Bins", toByteArray(mOptions.binDirs.join(";")));
963 ini.SetValue("Project","PrivateResource", toByteArray(mOptions.privateResource));
964 ini.SetValue("Project","ResourceIncludes", toByteArray(mOptions.resourceIncludes.join(";")));
965 ini.SetValue("Project","MakeIncludes", toByteArray(mOptions.makeIncludes.join(";")));
966 ini.SetValue("Project","Compiler", toByteArray(mOptions.compilerCmd));
967 ini.SetValue("Project","CppCompiler", toByteArray(mOptions.cppCompilerCmd));
968 ini.SetValue("Project","Linker", toByteArray(mOptions.linkerCmd));
969 ini.SetLongValue("Project","IsCpp", mOptions.isCpp);
970 ini.SetValue("Project","Icon", toByteArray(extractRelativePath(directory(), mOptions.icon)));
971 ini.SetValue("Project","ExeOutput", toByteArray(mOptions.exeOutput));
972 ini.SetValue("Project","ObjectOutput", toByteArray(mOptions.objectOutput));
973 ini.SetValue("Project","LogOutput", toByteArray(mOptions.logOutput));
974 ini.SetLongValue("Project","LogOutputEnabled", mOptions.logOutputEnabled);
975 ini.SetLongValue("Project","OverrideOutput", mOptions.overrideOutput);
976 ini.SetValue("Project","OverrideOutputName", toByteArray(mOptions.overridenOutput));
977 ini.SetValue("Project","HostApplication", toByteArray(mOptions.hostApplication));
978 ini.SetLongValue("Project","UseCustomMakefile", mOptions.useCustomMakefile);
979 ini.SetValue("Project","CustomMakefile", toByteArray(mOptions.customMakefile));
980 ini.SetLongValue("Project","UsePrecompiledHeader", mOptions.usePrecompiledHeader);
981 ini.SetValue("Project","PrecompiledHeader", toByteArray(mOptions.precompiledHeader));
982 ini.SetValue("Project","CommandLine", toByteArray(mOptions.cmdLineArgs));
983 ini.SetValue("Project","Folders", toByteArray(mFolders.join(";")));
984 ini.SetLongValue("Project","IncludeVersionInfo", mOptions.includeVersionInfo);
985 ini.SetLongValue("Project","SupportXPThemes", mOptions.supportXPThemes);
986 ini.SetLongValue("Project","CompilerSet", mOptions.compilerSet);
987 ini.SetLongValue("Project","CompilerSetType", mOptions.compilerSetType);
988 ini.Delete("Project","CompilerSettings"); // remove old compiler settings
989 ini.Delete("CompilerSettings",nullptr); // remove old compiler settings
990 foreach (const QString& key, mOptions.compilerOptions.keys()) {
991 ini.SetValue("CompilerSettings",toByteArray(key),toByteArray(mOptions.compilerOptions.value(key)));
992 }
993 ini.SetLongValue("Project","StaticLink", mOptions.staticLink);
994 ini.SetLongValue("Project","AddCharset", mOptions.addCharset);
995 ini.SetValue("Project","ExecEncoding", mOptions.execEncoding);
996 ini.SetValue("Project","Encoding",toByteArray(mOptions.encoding));
997 ini.SetLongValue("Project","ModelType", (int)mOptions.modelType);
998 //for Red Panda Dev C++ 6 compatibility
999 ini.SetLongValue("Project","UseUTF8",mOptions.encoding == ENCODING_UTF8);
1000
1001 ini.SetLongValue("VersionInfo","Major", mOptions.versionInfo.major);
1002 ini.SetLongValue("VersionInfo","Minor", mOptions.versionInfo.minor);
1003 ini.SetLongValue("VersionInfo","Release", mOptions.versionInfo.release);
1004 ini.SetLongValue("VersionInfo","Build", mOptions.versionInfo.build);
1005 ini.SetLongValue("VersionInfo","LanguageID", mOptions.versionInfo.languageID);
1006 ini.SetLongValue("VersionInfo","CharsetID", mOptions.versionInfo.charsetID);
1007 ini.SetValue("VersionInfo","CompanyName", toByteArray(mOptions.versionInfo.companyName));
1008 ini.SetValue("VersionInfo","FileVersion", toByteArray(mOptions.versionInfo.fileVersion));
1009 ini.SetValue("VersionInfo","FileDescription", toByteArray(mOptions.versionInfo.fileDescription));
1010 ini.SetValue("VersionInfo","InternalName", toByteArray(mOptions.versionInfo.internalName));
1011 ini.SetValue("VersionInfo","LegalCopyright", toByteArray(mOptions.versionInfo.legalCopyright));
1012 ini.SetValue("VersionInfo","LegalTrademarks", toByteArray(mOptions.versionInfo.legalTrademarks));
1013 ini.SetValue("VersionInfo","OriginalFilename", toByteArray(mOptions.versionInfo.originalFilename));
1014 ini.SetValue("VersionInfo","ProductName", toByteArray(mOptions.versionInfo.productName));
1015 ini.SetValue("VersionInfo","ProductVersion", toByteArray(mOptions.versionInfo.productVersion));
1016 ini.SetLongValue("VersionInfo","AutoIncBuildNr", mOptions.versionInfo.autoIncBuildNr);
1017 ini.SetLongValue("VersionInfo","SyncProduct", mOptions.versionInfo.syncProduct);
1018
1019
1020 //delete outdated dev4 project options
1021 ini.Delete("Project","NoConsole");
1022 ini.Delete("Project","IsDLL");
1023 ini.Delete("Project","ResFiles");
1024 ini.Delete("Project","IncludeDirs");
1025 ini.Delete("Project","CompilerOptions");
1026 ini.Delete("Project","Use_GPP");
1027
1028 ini.SaveFile(mFilename.toLocal8Bit());
1029}
1030
1031void Project::addFolder(const QString &s)
1032{
1033 if (mFolders.indexOf(s)<0) {
1034 mModel.beginUpdate();
1035 auto action = finally([this]{
1036 mModel.endUpdate();
1037 });
1038 mFolders.append(s);
1039 rebuildNodes();
1040 //todo: MainForm.ProjectView.Select(FolderNodeFromName(s));
1041 //folderNodeFromName(s)->makeVisible();
1042 setModified(true);
1043 }
1044}
1045
1046PProjectUnit Project::addUnit(const QString &inFileName, PProjectModelNode parentNode, bool rebuild)
1047{
1048 PProjectUnit newUnit;
1049 // Don't add if it already exists
1050 if (fileAlreadyExists(inFileName)) {
1051 QMessageBox::critical(nullptr,
1052 tr("File Exists"),
1053 tr("File '%1' is already in the project"),
1054 QMessageBox::Ok);
1055 return newUnit;
1056 }
1057 newUnit = std::make_shared<ProjectUnit>(this);
1058
1059 // Set all properties
1060 newUnit->setFileName(QDir(directory()).filePath(inFileName));
1061 newUnit->setNew(false);
1062 Editor * e= unitEditor(newUnit);
1063 if (e) {
1064 newUnit->setEncoding(e->fileEncoding());
1065 e->setInProject(true);
1066 } else {
1067 newUnit->setEncoding(options().encoding.toUtf8());
1068 }
1069 newUnit->setFolder(getFolderPath(parentNode));
1070 newUnit->setNode(makeNewFileNode(extractFileName(newUnit->fileName()), false, parentNode));
1071 newUnit->node()->unitIndex = mUnits.count();
1072 mUnits.append(newUnit);
1073
1074 // Determine compilation flags
1075 switch(getFileType(inFileName)) {
1076 case FileType::CSource:
1077 newUnit->setCompile(true);
1078 newUnit->setCompileCpp(false);
1079 newUnit->setLink(true);
1080 break;
1081 case FileType::CppSource:
1082 newUnit->setCompile(true);
1083 newUnit->setCompileCpp(true);
1084 newUnit->setLink(true);
1085 break;
1086 case FileType::WindowsResourceSource:
1087 newUnit->setCompile(true);
1088 newUnit->setCompileCpp(mOptions.isCpp);
1089 newUnit->setLink(false);
1090 break;
1091 default:
1092 newUnit->setCompile(false);
1093 newUnit->setCompileCpp(false);
1094 newUnit->setLink(false);
1095 }
1096 newUnit->setPriority(1000);
1097 newUnit->setOverrideBuildCmd(false);
1098 newUnit->setBuildCmd("");
1099 if (rebuild) {
1100 rebuildNodes();
1101 }
1102 setModified(true);
1103 return newUnit;
1104}
1105
1106QString Project::folder()
1107{
1108 return extractFileDir(filename());
1109}
1110
1111void Project::buildPrivateResource(bool forceSave)
1112{
1113 int comp = 0;
1114 foreach (const PProjectUnit& unit,mUnits) {
1115 if (
1116 (getFileType(unit->fileName()) == FileType::WindowsResourceSource)
1117 && unit->compile() )
1118 comp++;
1119 }
1120
1121 // if project has no other resources included
1122 // and does not have an icon
1123 // and does not include the XP style manifest
1124 // and does not include version info
1125 // then do not create a private resource file
1126 if ((comp == 0) &&
1127 (! mOptions.supportXPThemes)
1128 && (! mOptions.includeVersionInfo)
1129 && (mOptions.icon == "")) {
1130 mOptions.privateResource="";
1131 return;
1132 }
1133
1134 // change private resource from <project_filename>.res
1135 // to <project_filename>_private.res
1136 //
1137 // in many cases (like in importing a MSVC project)
1138 // the project's resource file has already the
1139 // <project_filename>.res filename.
1140 QString rcFile;
1141 if (!mOptions.privateResource.isEmpty()) {
1142 rcFile = QDir(directory()).filePath(mOptions.privateResource);
1143 if (changeFileExt(rcFile, DEV_PROJECT_EXT) == mFilename) {
1144 QFileInfo fileInfo(mFilename);
1145 rcFile = includeTrailingPathDelimiter(fileInfo.absolutePath())
1146 + fileInfo.baseName()
1147 + "_private."
1148 + RC_EXT;
1149 }
1150 } else {
1151 QFileInfo fileInfo(mFilename);
1152 rcFile = includeTrailingPathDelimiter(fileInfo.absolutePath())
1153 + fileInfo.baseName()
1154 + "_private."
1155 + RC_EXT;
1156 }
1157 rcFile = extractRelativePath(mFilename, rcFile);
1158 rcFile.replace(' ','_');
1159
1160 // don't run the private resource file and header if not modified,
1161 // unless ForceSave is true
1162 if (!forceSave
1163 && fileExists(rcFile)
1164 && fileExists(changeFileExt(rcFile, H_EXT))
1165 && !mModified)
1166 return;
1167
1168 QStringList contents;
1169 contents.append("/* THIS FILE WILL BE OVERWRITTEN BY Red Panda C++ */");
1170 contents.append("/* DO NOT EDIT! */");
1171 contents.append("");
1172
1173 if (mOptions.includeVersionInfo) {
1174 contents.append("#include <windows.h> // include for version info constants");
1175 contents.append("");
1176 }
1177
1178 foreach (const PProjectUnit& unit, mUnits) {
1179 if (
1180 (getFileType(unit->fileName()) == FileType::WindowsResourceSource)
1181 && unit->compile() )
1182 contents.append("#include \"" +
1183 genMakePath(
1184 extractRelativePath(directory(), unit->fileName()),
1185 false,
1186 false) + "\"");
1187 }
1188
1189 if (!mOptions.icon.isEmpty()) {
1190 contents.append("");
1191 QString icon = mOptions.icon;
1192 if (fileExists(icon)) {
1193 icon = extractRelativePath(mFilename, icon);
1194 icon.replace('\\', '/');
1195 contents.append("A ICON \"" + icon + '"');
1196 } else
1197 mOptions.icon = "";
1198 }
1199
1200 if (mOptions.supportXPThemes) {
1201 contents.append("");
1202 contents.append("//");
1203 contents.append("// SUPPORT FOR WINDOWS XP THEMES:");
1204 contents.append("// THIS WILL MAKE THE PROGRAM USE THE COMMON CONTROLS");
1205 contents.append("// LIBRARY VERSION 6.0 (IF IT IS AVAILABLE)");
1206 contents.append("//");
1207 if (!mOptions.exeOutput.isEmpty())
1208 contents.append(
1209 "1 24 \"" +
1210 genMakePath2(
1211 includeTrailingPathDelimiter(mOptions.exeOutput)
1212 + extractFileName(executable()))
1213 + ".Manifest\"");
1214 else
1215 contents.append("1 24 \"" + extractFileName(executable()) + ".Manifest\"");
1216 }
1217
1218 if (mOptions.includeVersionInfo) {
1219 contents.append("");
1220 contents.append("//");
1221 contents.append("// TO CHANGE VERSION INFORMATION, EDIT PROJECT OPTIONS...");
1222 contents.append("//");
1223 contents.append("1 VERSIONINFO");
1224 contents.append("FILEVERSION " +
1225 QString("%1,%2,%3,%4")
1226 .arg(mOptions.versionInfo.major)
1227 .arg(mOptions.versionInfo.minor)
1228 .arg(mOptions.versionInfo.release)
1229 .arg(mOptions.versionInfo.build));
1230 contents.append("PRODUCTVERSION " +
1231 QString("%1,%2,%3,%4")
1232 .arg(mOptions.versionInfo.major)
1233 .arg(mOptions.versionInfo.minor)
1234 .arg(mOptions.versionInfo.release)
1235 .arg(mOptions.versionInfo.build));
1236 switch(mOptions.type) {
1237 case ProjectType::GUI:
1238 case ProjectType::Console:
1239 contents.append("FILETYPE VFT_APP");
1240 break;
1241 case ProjectType::StaticLib:
1242 contents.append("FILETYPE VFT_STATIC_LIB");
1243 break;
1244 case ProjectType::DynamicLib:
1245 contents.append("FILETYPE VFT_DLL");
1246 break;
1247 }
1248 contents.append("{");
1249 contents.append(" BLOCK \"StringFileInfo\"");
1250 contents.append(" {");
1251 contents.append(" BLOCK \"" +
1252 QString("%1%2")
1253 .arg(mOptions.versionInfo.languageID,4,16,QChar('0'))
1254 .arg(mOptions.versionInfo.charsetID,4,16,QChar('0'))
1255 + '"');
1256 contents.append(" {");
1257 contents.append(" VALUE \"CompanyName\", \""
1258 + mOptions.versionInfo.companyName
1259 + "\"");
1260 contents.append(" VALUE \"FileVersion\", \""
1261 + mOptions.versionInfo.fileVersion
1262 + "\"");
1263 contents.append(" VALUE \"FileDescription\", \""
1264 + mOptions.versionInfo.fileDescription
1265 + "\"");
1266 contents.append(" VALUE \"InternalName\", \""
1267 + mOptions.versionInfo.internalName
1268 + "\"");
1269 contents.append(" VALUE \"LegalCopyright\", \""
1270 + mOptions.versionInfo.legalCopyright
1271 + '"');
1272 contents.append(" VALUE \"LegalTrademarks\", \""
1273 + mOptions.versionInfo.legalTrademarks
1274 + "\"");
1275 contents.append(" VALUE \"OriginalFilename\", \""
1276 + mOptions.versionInfo.originalFilename
1277 + "\"");
1278 contents.append(" VALUE \"ProductName\", \""
1279 + mOptions.versionInfo.productName + "\"");
1280 contents.append(" VALUE \"ProductVersion\", \""
1281 + mOptions.versionInfo.productVersion + "\"");
1282 contents.append(" }");
1283 contents.append(" }");
1284
1285 // additional block for windows 95->NT
1286 contents.append(" BLOCK \"VarFileInfo\"");
1287 contents.append(" {");
1288 contents.append(" VALUE \"Translation\", " +
1289 QString("0x%1, %2")
1290 .arg(mOptions.versionInfo.languageID,4,16,QChar('0'))
1291 .arg(mOptions.versionInfo.charsetID));
1292 contents.append(" }");
1293
1294 contents.append("}");
1295 }
1296
1297 rcFile = QDir(directory()).absoluteFilePath(rcFile);
1298 if (contents.count() > 3) {
1299 stringsToFile(contents,rcFile);
1300 mOptions.privateResource = extractRelativePath(directory(), rcFile);
1301 } else {
1302 if (fileExists(rcFile))
1303 QFile::remove(rcFile);
1304 QString resFile = changeFileExt(rcFile, RES_EXT);
1305 if (fileExists(resFile))
1306 QFile::remove(resFile);
1307 mOptions.privateResource = "";
1308 }
1309// if fileExists(Res) then
1310// FileSetDate(Res, DateTimeToFileDate(Now)); // fix the "Clock skew detected" warning ;)
1311
1312 // create XP manifest
1313 if (mOptions.supportXPThemes) {
1314 QStringList content;
1315 content.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
1316 content.append("<assembly");
1317 content.append(" xmlns=\"urn:schemas-microsoft-com:asm.v1\"");
1318 content.append(" manifestVersion=\"1.0\">");
1319 content.append("<assemblyIdentity");
1320 QString name = mName;
1321 name.replace(' ','_');
1322 content.append(" name=\"DevCpp.Apps." + name + '\"');
1323 content.append(" processorArchitecture=\"*\"");
1324 content.append(" version=\"1.0.0.0\"");
1325 content.append(" type=\"win32\"/>");
1326 content.append("<description>" + name + "</description>");
1327 content.append("<dependency>");
1328 content.append(" <dependentAssembly>");
1329 content.append(" <assemblyIdentity");
1330 content.append(" type=\"win32\"");
1331 content.append(" name=\"Microsoft.Windows.Common-Controls\"");
1332 content.append(" version=\"6.0.0.0\"");
1333 content.append(" processorArchitecture=\"*\"");
1334 content.append(" publicKeyToken=\"6595b64144ccf1df\"");
1335 content.append(" language=\"*\"");
1336 content.append(" />");
1337 content.append(" </dependentAssembly>");
1338 content.append("</dependency>");
1339 content.append("</assembly>");
1340 stringsToFile(content,executable() + ".Manifest");
1341 } else if (fileExists(executable() + ".Manifest"))
1342 QFile::remove(executable() + ".Manifest");
1343
1344 // create private header file
1345 QString hFile = changeFileExt(rcFile, H_EXT);
1346 contents.clear();
1347 QString def = extractFileName(rcFile);
1348 def.replace(".","_");
1349 contents.append("/* THIS FILE WILL BE OVERWRITTEN BY Red Panda C++ */");
1350 contents.append("/* DO NOT EDIT ! */");
1351 contents.append("");
1352 contents.append("#ifndef " + def);
1353 contents.append("#define " + def);
1354 contents.append("");
1355 contents.append("/* VERSION DEFINITIONS */");
1356 contents.append("#define VER_STRING\t" +
1357 QString("\"%1.%2.%3.%4\"")
1358 .arg(mOptions.versionInfo.major)
1359 .arg(mOptions.versionInfo.minor)
1360 .arg(mOptions.versionInfo.release)
1361 .arg(mOptions.versionInfo.build));
1362 contents.append(QString("#define VER_MAJOR\t%1").arg(mOptions.versionInfo.major));
1363 contents.append(QString("#define VER_MINOR\t%1").arg(mOptions.versionInfo.minor));
1364 contents.append(QString("#define VER_RELEASE\t%1").arg(mOptions.versionInfo.release));
1365 contents.append(QString("#define VER_BUILD\t%1").arg(mOptions.versionInfo.build));
1366 contents.append(QString("#define COMPANY_NAME\t\"%1\"")
1367 .arg(mOptions.versionInfo.companyName));
1368 contents.append(QString("#define FILE_VERSION\t\"%1\"")
1369 .arg(mOptions.versionInfo.fileVersion));
1370 contents.append(QString("#define FILE_DESCRIPTION\t\"%1\"")
1371 .arg(mOptions.versionInfo.fileDescription));
1372 contents.append(QString("#define INTERNAL_NAME\t\"%1\"")
1373 .arg(mOptions.versionInfo.internalName));
1374 contents.append(QString("#define LEGAL_COPYRIGHT\t\"%1\"")
1375 .arg(mOptions.versionInfo.legalCopyright));
1376 contents.append(QString("#define LEGAL_TRADEMARKS\t\"%1\"")
1377 .arg(mOptions.versionInfo.legalTrademarks));
1378 contents.append(QString("#define ORIGINAL_FILENAME\t\"%1\"")
1379 .arg(mOptions.versionInfo.originalFilename));
1380 contents.append(QString("#define PRODUCT_NAME\t\"%1\"")
1381 .arg(mOptions.versionInfo.productName));
1382 contents.append(QString("#define PRODUCT_VERSION\t\"%1\"")
1383 .arg(mOptions.versionInfo.productVersion));
1384 contents.append("");
1385 contents.append("#endif /*" + def + "*/");
1386 stringsToFile(contents,hFile);
1387}
1388
1389void Project::checkProjectFileForUpdate(SimpleIni &ini)
1390{
1391 bool cnvt = false;
1392 int uCount = ini.GetLongValue("Project","UnitCount", 0);
1393 // check if using old way to store resources and fix it
1394 QString oldRes = QString::fromLocal8Bit(ini.GetValue("Project","Resources", ""));
1395 if (!oldRes.isEmpty()) {
1396 QFile::copy(mFilename,mFilename+".bak");
1397 QStringList sl;
1398 sl = oldRes.split(';',
1399#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1400 Qt::SkipEmptyParts
1401#else
1402 QString::SkipEmptyParts
1403#endif
1404 );
1405 for (int i=0;i<sl.count();i++){
1406 const QString& s = sl[i];
1407 QByteArray groupName = toByteArray(QString("Unit%1").arg(uCount+i));
1408 ini.SetValue(groupName,"Filename", toByteArray(s));
1409 ini.SetValue(groupName,"Folder", "Resources");
1410 ini.SetLongValue(groupName,"Compile",true);
1411 }
1412 ini.SetLongValue("Project","UnitCount",uCount+sl.count());
1413 QString folders = QString::fromLocal8Bit(ini.GetValue("Project","Folders",""));
1414 if (!folders.isEmpty())
1415 folders += ",Resources";
1416 else
1417 folders = "Resources";
1418 ini.SetValue("Project","Folders",toByteArray(folders));
1419 cnvt = true;
1420 ini.Delete("Project","Resources");
1421 ini.Delete("Project","Focused");
1422 ini.Delete("Project","Order");
1423 ini.Delete("Project","DebugInfo");
1424 ini.Delete("Project","ProfileInfo");
1425 ini.SaveFile(mFilename.toLocal8Bit());
1426 }
1427
1428 if (cnvt)
1429 QMessageBox::information(
1430 nullptr,
1431 tr("Project Updated"),
1432 tr("Your project was succesfully updated to a newer file format!")
1433 +"<br />"
1434 +tr("If something has gone wrong, we kept a backup-file: '%1'...")
1435 .arg(mFilename+".bak"),
1436 QMessageBox::Ok);
1437}
1438
1439void Project::closeUnit(int index)
1440{
1441 PProjectUnit unit = mUnits[index];
1442 Editor * editor =unitEditor(unit);
1443 if (editor) {
1444 saveUnitLayout(editor,index);
1445 editor->setInProject(false);
1446 mEditorList->forceCloseEditor(editor);
1447 }
1448}
1449
1450void Project::createFolderNodes()
1451{
1452 for (int idx=0;idx<mFolders.count();idx++) {
1453 PProjectModelNode node = mRootNode;
1454 QString s = mFolders[idx];
1455 int i = s.indexOf('/');
1456 while (i>=0) {
1457 PProjectModelNode findnode;
1458 for (int c=0;c<node->children.count();c++) {
1459 if (node->children[c]->text == s.mid(0,i))
1460 findnode = node->children[c];
1461 }
1462 if (!findnode)
1463 node = makeNewFileNode(s.mid(0,i),true,node);
1464 else
1465 node = findnode;
1466 node->unitIndex = -1;
1467 s.remove(0,i+1);
1468 i = s.indexOf('/');
1469 }
1470 node = makeNewFileNode(s, true, node);
1471 node->unitIndex = -1;
1472 mFolderNodes.append(node);
1473 }
1474}
1475
1476static void addFolderRecursively(QSet<QString>& folders, QString folder) {
1477 if (folder.isEmpty())
1478 return;
1479 folders.insert(excludeTrailingPathDelimiter(folder));
1480 QString parentFolder = QFileInfo(folder).absolutePath();
1481 if (parentFolder==folder)
1482 return;
1483 addFolderRecursively(folders, parentFolder);
1484}
1485
1486void Project::createFileSystemFolderNodes()
1487{
1488 QSet<QString> headerFolders;
1489 QSet<QString> sourceFolders;
1490 QSet<QString> otherFolders;
1491 for (int idx=0;idx<mUnits.count();idx++) {
1492 QFileInfo fileInfo(mUnits[idx]->fileName());
1493 if (isHFile(fileInfo.fileName())) {
1494 addFolderRecursively(headerFolders,fileInfo.absolutePath());
1495 } else if (isCFile(fileInfo.fileName())) {
1496 addFolderRecursively(sourceFolders,fileInfo.absolutePath());
1497 } else {
1498 addFolderRecursively(otherFolders,fileInfo.absolutePath());
1499 }
1500 }
1501 PProjectModelNode node = makeNewFileNode(tr("Headers"),true,mRootNode);
1502 node->folderNodeType = ProjectSpecialFolderNode::HEADERS;
1503 node->priority = 1000;
1504 createFileSystemFolderNode(ProjectSpecialFolderNode::HEADERS,folder(),node, headerFolders);
1505 mFolderNodes.append(node);
1506 mSpecialNodes.insert(ProjectSpecialFolderNode::HEADERS,node);
1507
1508 node = makeNewFileNode(tr("Sources"),true,mRootNode);
1509 node->folderNodeType = ProjectSpecialFolderNode::SOURCES;
1510 node->priority = 900;
1511 createFileSystemFolderNode(ProjectSpecialFolderNode::SOURCES,folder(),node, sourceFolders);
1512 mFolderNodes.append(node);
1513 mSpecialNodes.insert(ProjectSpecialFolderNode::SOURCES,node);
1514
1515 node = makeNewFileNode(tr("Others"),true,mRootNode);
1516 node->folderNodeType = ProjectSpecialFolderNode::OTHERS;
1517 node->priority = 800;
1518 createFileSystemFolderNode(ProjectSpecialFolderNode::OTHERS,folder(),node, otherFolders);
1519 mFolderNodes.append(node);
1520 mSpecialNodes.insert(ProjectSpecialFolderNode::OTHERS,node);
1521}
1522
1523void Project::createFileSystemFolderNode(
1524 ProjectSpecialFolderNode folderType,
1525 const QString &folderName,
1526 PProjectModelNode parent,
1527 const QSet<QString>& validFolders)
1528{
1529 QDirIterator iter(folderName);
1530 while (iter.hasNext()) {
1531 iter.next();
1532 QFileInfo fileInfo = iter.fileInfo();
1533 if (fileInfo.isHidden() || fileInfo.fileName().startsWith('.'))
1534 continue;
1535 if (fileInfo.isDir() && validFolders.contains(fileInfo.absoluteFilePath())) {
1536 PProjectModelNode node = makeNewFileNode(fileInfo.fileName(),true,parent);
1537 mFileSystemFolderNodes.insert(QString("%1/%2").arg((int)folderType).arg(fileInfo.absoluteFilePath()),node);
1538 createFileSystemFolderNode(folderType,fileInfo.absoluteFilePath(), node, validFolders);
1539 }
1540 }
1541}
1542
1543void Project::doAutoOpen()
1544{
1545 loadLayout();
1546}
1547
1548bool Project::fileAlreadyExists(const QString &s)
1549{
1550 foreach (const PProjectUnit& unit, mUnits) {
1551 if (unit->fileName() == s)
1552 return true;
1553 }
1554 return false;
1555}
1556
1557PProjectModelNode Project::findFolderNode(const QString &folderPath, ProjectSpecialFolderNode nodeType)
1558{
1559 PProjectModelNode node = mFileSystemFolderNodes.value(QString("%1/%2").arg((int)nodeType).arg(folderPath),
1560 PProjectModelNode());
1561 if (node)
1562 return node;
1563 PProjectModelNode parentNode = mSpecialNodes.value(nodeType,PProjectModelNode());
1564 if (parentNode)
1565 return parentNode;
1566 return mRootNode;
1567}
1568
1569PProjectModelNode Project::folderNodeFromName(const QString &name)
1570{
1571 int index = mFolders.indexOf(name);
1572 if (index>=0) {
1573 return mFolderNodes[index];
1574 }
1575 return mRootNode;
1576}
1577
1578QString Project::getFolderPath(PProjectModelNode node)
1579{
1580 QString result;
1581 if (!node)
1582 return result;
1583
1584 if (node->unitIndex>=0) // not a folder
1585 return result;
1586
1587 PProjectModelNode p = node;
1588 while (p && p->unitIndex==-1 && p!=mRootNode) {
1589 if (!result.isEmpty())
1590 result = p->text + "/" + result;
1591 else
1592 result = p->text;
1593 p = p->parent.lock();
1594 }
1595 return result;
1596}
1597
1598int Project::getUnitFromString(const QString &s)
1599{
1600 return indexInUnits(s);
1601}
1602
1603PProjectModelNode Project::getParentFolderNode(const QString &filename)
1604{
1605 QFileInfo fileInfo(filename);
1606 ProjectSpecialFolderNode folderNodeType;
1607 if (isHFile(fileInfo.fileName())) {
1608 folderNodeType = ProjectSpecialFolderNode::HEADERS;
1609 } else if (isCFile(fileInfo.fileName())) {
1610 folderNodeType = ProjectSpecialFolderNode::SOURCES;
1611 } else {
1612 folderNodeType = ProjectSpecialFolderNode::OTHERS;
1613 }
1614 return findFolderNode(fileInfo.absolutePath(),folderNodeType);
1615}
1616
1617void Project::incrementBuildNumber()
1618{
1619 mOptions.versionInfo.build++;
1620 mOptions.versionInfo.fileVersion = QString("%1.%2.%3.%3")
1621 .arg(mOptions.versionInfo.major)
1622 .arg(mOptions.versionInfo.minor)
1623 .arg(mOptions.versionInfo.release)
1624 .arg(mOptions.versionInfo.build);
1625 if (mOptions.versionInfo.syncProduct)
1626 mOptions.versionInfo.productVersion = mOptions.versionInfo.fileVersion;
1627 setModified(true);
1628}
1629
1630void Project::loadLayout()
1631{
1632 SimpleIni layIni;
1633 SI_Error error = layIni.LoadFile(changeFileExt(filename(), "layout").toLocal8Bit());
1634 if (error!=SI_OK)
1635 return;
1636 int topLeft = layIni.GetLongValue("Editors","Focused",1);
1637 //TopRight := layIni.ReadInteger('Editors', 'FocusedRight', -1);
1638 QString temp =layIni.GetValue("Editors","Order", "");
1639 QStringList sl = temp.split(",",
1640#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1641 Qt::SkipEmptyParts
1642#else
1643 QString::SkipEmptyParts
1644#endif
1645 );
1646
1647 foreach (const QString& s,sl) {
1648 bool ok;
1649 int currIdx = s.toInt(&ok);
1650 if (ok) {
1651 openUnit(currIdx);
1652 }
1653 }
1654 if (topLeft>=0 && topLeft<mUnits.count()) {
1655 Editor * editor = unitEditor(mUnits[topLeft]);
1656 if (editor)
1657 editor->activate();
1658 }
1659}
1660
1661void Project::loadOptions(SimpleIni& ini)
1662{
1663 mName = fromByteArray(ini.GetValue("Project","name", ""));
1664 QString icon = fromByteArray(ini.GetValue("Project", "icon", ""));
1665 if (icon.isEmpty()) {
1666 mOptions.icon = "";
1667 } else {
1668 mOptions.icon = QDir(directory()).absoluteFilePath(icon);
1669 }
1670 mOptions.version = ini.GetLongValue("Project", "Ver", 0);
1671 if (mOptions.version > 0) { // ver > 0 is at least a v5 project
1672 if (mOptions.version < 3) {
1673 mOptions.version = 3;
1674 QMessageBox::information(nullptr,
1675 tr("Settings need update"),
1676 tr("The compiler settings format of Red Panda C++ has changed.")
1677 +"<BR /><BR />"
1678 +tr("Please update your settings at Project >> Project Options >> Compiler and save your project."),
1679 QMessageBox::Ok);
1680 }
1681
1682 mOptions.type = static_cast<ProjectType>(ini.GetLongValue("Project", "type", 0));
1683 mOptions.compilerCmd = fromByteArray(ini.GetValue("Project", "Compiler", ""));
1684 mOptions.cppCompilerCmd = fromByteArray(ini.GetValue("Project", "CppCompiler", ""));
1685 mOptions.linkerCmd = fromByteArray(ini.GetValue("Project", "Linker", ""));
1686 mOptions.objFiles = fromByteArray(ini.GetValue("Project", "ObjFiles", "")).split(";",
1687#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1688 Qt::SkipEmptyParts
1689#else
1690 QString::SkipEmptyParts
1691#endif
1692 );
1693 mOptions.binDirs = fromByteArray(ini.GetValue("Project", "Bins", "")).split(";",
1694#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1695 Qt::SkipEmptyParts
1696#else
1697 QString::SkipEmptyParts
1698#endif
1699 );
1700 mOptions.libDirs = fromByteArray(ini.GetValue("Project", "Libs", "")).split(";",
1701#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1702 Qt::SkipEmptyParts
1703#else
1704 QString::SkipEmptyParts
1705#endif
1706 );
1707 mOptions.includeDirs = fromByteArray(ini.GetValue("Project", "Includes", "")).split(";",
1708#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1709 Qt::SkipEmptyParts
1710#else
1711 QString::SkipEmptyParts
1712#endif
1713 );
1714 mOptions.privateResource = fromByteArray(ini.GetValue("Project", "PrivateResource", ""));
1715 mOptions.resourceIncludes = fromByteArray(ini.GetValue("Project", "ResourceIncludes", "")).split(";",
1716#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1717 Qt::SkipEmptyParts
1718#else
1719 QString::SkipEmptyParts
1720#endif
1721 );
1722 mOptions.makeIncludes = fromByteArray(ini.GetValue("Project", "MakeIncludes", "")).split(";",
1723#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1724 Qt::SkipEmptyParts
1725#else
1726 QString::SkipEmptyParts
1727#endif
1728 );
1729 mOptions.isCpp = ini.GetBoolValue("Project", "IsCpp", false);
1730 mOptions.exeOutput = fromByteArray(ini.GetValue("Project", "ExeOutput", ""));
1731 mOptions.objectOutput = fromByteArray(ini.GetValue("Project", "ObjectOutput", ""));
1732 mOptions.logOutput = fromByteArray(ini.GetValue("Project", "LogOutput", ""));
1733 mOptions.logOutputEnabled = ini.GetBoolValue("Project", "LogOutputEnabled", false);
1734 mOptions.overrideOutput = ini.GetBoolValue("Project", "OverrideOutput", false);
1735 mOptions.overridenOutput = fromByteArray(ini.GetValue("Project", "OverrideOutputName", ""));
1736 mOptions.hostApplication = fromByteArray(ini.GetValue("Project", "HostApplication", ""));
1737 mOptions.useCustomMakefile = ini.GetBoolValue("Project", "UseCustomMakefile", false);
1738 mOptions.customMakefile = fromByteArray(ini.GetValue("Project", "CustomMakefile", ""));
1739 mOptions.usePrecompiledHeader = ini.GetBoolValue("Project", "UsePrecompiledHeader", false);
1740 mOptions.precompiledHeader = fromByteArray(ini.GetValue("Project", "PrecompiledHeader", ""));
1741 mOptions.cmdLineArgs = fromByteArray(ini.GetValue("Project", "CommandLine", ""));
1742 mFolders = fromByteArray(ini.GetValue("Project", "Folders", "")).split(";",
1743 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1744 Qt::SkipEmptyParts
1745 #else
1746 QString::SkipEmptyParts
1747 #endif
1748 );
1749 mOptions.includeVersionInfo = ini.GetBoolValue("Project", "IncludeVersionInfo", false);
1750 mOptions.supportXPThemes = ini.GetBoolValue("Project", "SupportXPThemes", false);
1751 mOptions.compilerSet = ini.GetLongValue("Project", "CompilerSet", pSettings->compilerSets().defaultIndex());
1752 mOptions.modelType = (ProjectModelType)ini.GetLongValue("Project", "ModelType", (int)ProjectModelType::Custom);
1753
1754 if (mOptions.compilerSet >= (int)pSettings->compilerSets().size()
1755 || mOptions.compilerSet < 0) { // TODO: change from indices to names
1756 QMessageBox::critical(
1757 nullptr,
1758 tr("Compiler not found"),
1759 tr("The compiler set you have selected for this project, no longer exists.")
1760 +"<BR />"
1761 +tr("It will be substituted by the global compiler set."),
1762 QMessageBox::Ok
1763 );
1764 setCompilerSet(pSettings->compilerSets().defaultIndex());
1765 }
1766
1767 Settings::PCompilerSet pSet = pSettings->compilerSets().getSet(mOptions.compilerSet);
1768 if (pSet) {
1769 QByteArray oldCompilerOptions = ini.GetValue("Project", "CompilerSettings", "");
1770 if (!oldCompilerOptions.isEmpty()) {
1771 //version 2 compatibility
1772 // test if it is created by old dev-c++
1773 SimpleIni::TNamesDepend oKeys;
1774 ini.GetAllKeys("Project", oKeys);
1775 bool isNewDev=false;
1776 for(const SimpleIni::Entry& entry:oKeys) {
1777 QString key(entry.pItem);
1778 if (key=="UsePrecompiledHeader"
1779 || key == "CompilerSetType"
1780 || key == "StaticLink"
1781 || key == "AddCharset"
1782 || key == "ExecEncoding"
1783 || key == "Encoding"
1784 || key == "UseUTF8") {
1785 isNewDev = true;
1786 break;
1787 }
1788 }
1789 if (!isNewDev && oldCompilerOptions.length()>=25) {
1790 char t = oldCompilerOptions[18];
1791 oldCompilerOptions[18]=oldCompilerOptions[21];
1792 oldCompilerOptions[21]=t;
1793 }
1794 for (int i=0;i<oldCompilerOptions.length();i++) {
1795 QString key = pSettings->compilerSets().getKeyFromCompilerCompatibleIndex(i);
1796 PCompilerOption pOption = CompilerInfoManager::getCompilerOption(
1797 pSet->compilerType(), key);
1798 if (pOption) {
1799 int val = Settings::CompilerSet::charToValue(oldCompilerOptions[i]);
1800 if (pOption->choices.isEmpty()) {
1801 if (val>0)
1802 mOptions.compilerOptions.insert(key,COMPILER_OPTION_ON);
1803 else
1804 mOptions.compilerOptions.insert(key,"");
1805 } else {
1806 if (val>0 && val <= pOption->choices.length())
1807 mOptions.compilerOptions.insert(key,pOption->choices[val-1].second);
1808 else
1809 mOptions.compilerOptions.insert(key,"");
1810 }
1811 }
1812 }
1813 } else {
1814 //version 3
1815 SimpleIni::TNamesDepend oKeys;
1816 ini.GetAllKeys("CompilerSettings", oKeys);
1817 for(const SimpleIni::Entry& entry:oKeys) {
1818 QString key(entry.pItem);
1819 mOptions.compilerOptions.insert(
1820 key,
1821 ini.GetValue("CompilerSettings", entry.pItem, ""));
1822 }
1823 }
1824 }
1825
1826 mOptions.staticLink = ini.GetBoolValue("Project", "StaticLink", true);
1827 mOptions.execEncoding = ini.GetValue("Project","ExecEncoding", ENCODING_SYSTEM_DEFAULT);
1828 mOptions.addCharset = ini.GetBoolValue("Project", "AddCharset", true);
1829
1830 if (mOptions.compilerSetType<0) {
1831 updateCompilerSetType();
1832 }
1833 bool useUTF8 = ini.GetBoolValue("Project", "UseUTF8", false);
1834 if (useUTF8) {
1835 mOptions.encoding = fromByteArray(ini.GetValue("Project","Encoding", ENCODING_UTF8));
1836 } else {
1837 mOptions.encoding = fromByteArray(ini.GetValue("Project","Encoding", ENCODING_AUTO_DETECT));
1838 }
1839
1840 mOptions.versionInfo.major = ini.GetLongValue("VersionInfo", "Major", 0);
1841 mOptions.versionInfo.minor = ini.GetLongValue("VersionInfo", "Minor", 1);
1842 mOptions.versionInfo.release = ini.GetLongValue("VersionInfo", "Release", 1);
1843 mOptions.versionInfo.build = ini.GetLongValue("VersionInfo", "Build", 1);
1844 mOptions.versionInfo.languageID = ini.GetLongValue("VersionInfo", "LanguageID", 0x0409);
1845 mOptions.versionInfo.charsetID = ini.GetLongValue("VersionInfo", "CharsetID", 0x04E4);
1846 mOptions.versionInfo.companyName = fromByteArray(ini.GetValue("VersionInfo", "CompanyName", ""));
1847 mOptions.versionInfo.fileVersion = fromByteArray(ini.GetValue("VersionInfo", "FileVersion", "0.1"));
1848 mOptions.versionInfo.fileDescription = fromByteArray(ini.GetValue("VersionInfo", "FileDescription",
1849 toByteArray(tr("Developed using the Red Panda C++ IDE"))));
1850 mOptions.versionInfo.internalName = fromByteArray(ini.GetValue("VersionInfo", "InternalName", ""));
1851 mOptions.versionInfo.legalCopyright = fromByteArray(ini.GetValue("VersionInfo", "LegalCopyright", ""));
1852 mOptions.versionInfo.legalTrademarks = fromByteArray(ini.GetValue("VersionInfo", "LegalTrademarks", ""));
1853 mOptions.versionInfo.originalFilename = fromByteArray(ini.GetValue("VersionInfo", "OriginalFilename",
1854 toByteArray(extractFileName(executable()))));
1855 mOptions.versionInfo.productName = fromByteArray(ini.GetValue("VersionInfo", "ProductName", toByteArray(mName)));
1856 mOptions.versionInfo.productVersion = fromByteArray(ini.GetValue("VersionInfo", "ProductVersion", "0.1.1.1"));
1857 mOptions.versionInfo.autoIncBuildNr = ini.GetBoolValue("VersionInfo", "AutoIncBuildNr", false);
1858 mOptions.versionInfo.syncProduct = ini.GetBoolValue("VersionInfo", "SyncProduct", false);
1859
1860 } else { // dev-c < 4
1861 mOptions.version = 3;
1862 if (!ini.GetBoolValue("VersionInfo", "NoConsole", true))
1863 mOptions.type = ProjectType::Console;
1864 else if (ini.GetBoolValue("VersionInfo", "IsDLL", false))
1865 mOptions.type = ProjectType::DynamicLib;
1866 else
1867 mOptions.type = ProjectType::GUI;
1868
1869 mOptions.privateResource = fromByteArray(ini.GetValue("Project", "PrivateResource", ""));
1870 mOptions.resourceIncludes = fromByteArray(ini.GetValue("Project", "ResourceIncludes", "")).split(";",
1871#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1872 Qt::SkipEmptyParts
1873#else
1874 QString::SkipEmptyParts
1875#endif
1876 );
1877 mOptions.objFiles = fromByteArray(ini.GetValue("Project", "ObjFiles", "")).split(";",
1878#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1879 Qt::SkipEmptyParts
1880#else
1881 QString::SkipEmptyParts
1882#endif
1883 );
1884 mOptions.includeDirs = fromByteArray(ini.GetValue("Project", "IncludeDirs", "")).split(";",
1885#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1886 Qt::SkipEmptyParts
1887#else
1888 QString::SkipEmptyParts
1889#endif
1890 );
1891 mOptions.compilerCmd = fromByteArray(ini.GetValue("Project", "CompilerOptions", ""));
1892 mOptions.isCpp = ini.GetBoolValue("Project", "Use_GPP", false);
1893 mOptions.exeOutput = fromByteArray(ini.GetValue("Project", "ExeOutput", ""));
1894 mOptions.objectOutput = fromByteArray(ini.GetValue("Project", "ObjectOutput", ""));
1895 mOptions.overrideOutput = ini.GetBoolValue("Project", "OverrideOutput", false);
1896 mOptions.overridenOutput = fromByteArray(ini.GetValue("Project", "OverrideOutputName", ""));
1897 mOptions.hostApplication = fromByteArray(ini.GetValue("Project", "HostApplication", ""));
1898 }
1899}
1900
1901void Project::loadUnitLayout(Editor *e, int index)
1902{
1903 if (!e)
1904 return;
1905 SimpleIni layIni;
1906 SI_Error error;
1907 error = layIni.LoadFile(changeFileExt(filename(), "layout").toLocal8Bit());
1908 if (error != SI_Error::SI_OK)
1909 return;
1910 QByteArray groupName = (QString("Editor_%1").arg(index)).toUtf8();
1911 e->setCaretY(layIni.GetLongValue(groupName,"CursorRow",1));
1912 e->setCaretX(layIni.GetLongValue(groupName,"CursorCol",1));
1913 e->setTopLine(layIni.GetLongValue(groupName,"TopLine",1));
1914 e->setLeftChar(layIni.GetLongValue(groupName,"LeftChar",1));
1915}
1916
1917PCppParser Project::cppParser()
1918{
1919 return mParser;
1920}
1921
1922int Project::indexInUnits(const QString &fileName) const
1923{
1924 QDir dir(directory());
1925 for (int i=0;i<mUnits.count();i++) {
1926 PProjectUnit unit = mUnits[i];
1927 if (dir.absoluteFilePath(fileName) == dir.absoluteFilePath(unit->fileName()))
1928 return i;
1929 }
1930 return -1;
1931}
1932
1933int Project::indexInUnits(const Editor *editor) const
1934{
1935 if (!editor)
1936 return -1;
1937 return indexInUnits(editor->filename());
1938}
1939
1940void Project::removeFolderRecurse(PProjectModelNode node)
1941{
1942 if (!node)
1943 return ;
1944 // Recursively remove folders
1945 for (int i=node->children.count()-1;i>=0;i++) {
1946 PProjectModelNode childNode = node->children[i];
1947 // Remove folder inside folder
1948 if (childNode->unitIndex<0 && childNode->level>0) {
1949 removeFolderRecurse(childNode);
1950 // Or remove editors at this level
1951 } else if (childNode->unitIndex >= 0 && childNode->level > 0) {
1952 // Remove editor in folder from project
1953 int editorIndex = childNode->unitIndex;
1954 if (!removeUnit(editorIndex,true))
1955 return;
1956 }
1957 }
1958
1959 PProjectModelNode parent = node->parent.lock();
1960 if (parent) {
1961 parent->children.removeAll(node);
1962 }
1963}
1964
1965void Project::updateFolderNode(PProjectModelNode node)
1966{
1967 for (int i=0;i<node->children.count();i++){
1968 PProjectModelNode child = node->children[i];
1969 if (child->unitIndex<0) {
1970 mFolders.append(getFolderPath(child));
1971 updateFolderNode(child);
1972 }
1973 }
1974}
1975
1976void Project::updateCompilerSetType()
1977{
1978 Settings::PCompilerSet defaultSet = pSettings->compilerSets().getSet(mOptions.compilerSet);
1979 if (defaultSet) {
1980 mOptions.compilerSetType=defaultSet->compilerSetType();
1981 mOptions.staticLink = defaultSet->staticLink();
1982 mOptions.compilerOptions = defaultSet->compileOptions();
1983 } else {
1984 mOptions.compilerSetType=CST_DEBUG;
1985 mOptions.staticLink = false;
1986 }
1987}
1988
1989QFileSystemWatcher *Project::fileSystemWatcher() const
1990{
1991 return mFileSystemWatcher;
1992}
1993
1994QString Project::fileSystemNodeFolderPath(const PProjectModelNode &node)
1995{
1996 QString result;
1997 if (node != mRootNode) {
1998 PProjectModelNode pNode = node;
1999 while (pNode && pNode->folderNodeType == ProjectSpecialFolderNode::NonSpecial) {
2000 result = node->text + "/" +result;
2001 pNode = pNode->parent.lock();
2002 }
2003 }
2004 result = folder() + "/" + result;
2005 return result;
2006}
2007
2008QStringList Project::binDirs()
2009{
2010 QStringList lst = options().binDirs;
2011 Settings::PCompilerSet compilerSet = pSettings->compilerSets().getSet(options().compilerSet);
2012 if (compilerSet) {
2013 lst.append(compilerSet->binDirs());
2014 }
2015 return lst;
2016}
2017
2018EditorList *Project::editorList() const
2019{
2020 return mEditorList;
2021}
2022
2023const QList<PProjectUnit> &Project::units() const
2024{
2025 return mUnits;
2026}
2027
2028ProjectModelType Project::modelType() const
2029{
2030 return mOptions.modelType;
2031}
2032
2033void Project::setModelType(ProjectModelType type)
2034{
2035 if (type!=mOptions.modelType) {
2036 mOptions.modelType = type;
2037 rebuildNodes();
2038 }
2039}
2040
2041ProjectOptions &Project::options()
2042{
2043 return mOptions;
2044}
2045
2046ProjectModel *Project::model()
2047{
2048 return &mModel;
2049}
2050
2051const PProjectModelNode &Project::rootNode() const
2052{
2053 return mRootNode;
2054}
2055
2056const QString &Project::name() const
2057{
2058 return mName;
2059}
2060
2061void Project::setName(const QString &newName)
2062{
2063 if (newName != mName) {
2064 mName = newName;
2065 mRootNode->text = newName;
2066 setModified(true);
2067 }
2068}
2069
2070const QString &Project::filename() const
2071{
2072 return mFilename;
2073}
2074
2075ProjectUnit::ProjectUnit(Project* parent)
2076{
2077 mNode = nullptr;
2078 mParent = parent;
2079 mFileMissing = false;
2080}
2081
2082Project *ProjectUnit::parent() const
2083{
2084 return mParent;
2085}
2086
2087void ProjectUnit::setParent(Project* newParent)
2088{
2089 mParent = newParent;
2090}
2091
2092const QString &ProjectUnit::fileName() const
2093{
2094 return mFileName;
2095}
2096
2097void ProjectUnit::setFileName(QString newFileName)
2098{
2099 newFileName = QFileInfo(newFileName).absoluteFilePath();
2100 if (mFileName != newFileName) {
2101 mFileName = newFileName;
2102 if (mNode) {
2103 mNode->text = extractFileName(mFileName);
2104 }
2105 }
2106}
2107
2108bool ProjectUnit::isNew() const
2109{
2110 return mNew;
2111}
2112
2113void ProjectUnit::setNew(bool newNew)
2114{
2115 mNew = newNew;
2116}
2117
2118const QString &ProjectUnit::folder() const
2119{
2120 return mFolder;
2121}
2122
2123void ProjectUnit::setFolder(const QString &newFolder)
2124{
2125 mFolder = newFolder;
2126}
2127
2128bool ProjectUnit::compile() const
2129{
2130 return mCompile;
2131}
2132
2133void ProjectUnit::setCompile(bool newCompile)
2134{
2135 mCompile = newCompile;
2136}
2137
2138bool ProjectUnit::compileCpp() const
2139{
2140 return mCompileCpp;
2141}
2142
2143void ProjectUnit::setCompileCpp(bool newCompileCpp)
2144{
2145 mCompileCpp = newCompileCpp;
2146}
2147
2148bool ProjectUnit::overrideBuildCmd() const
2149{
2150 return mOverrideBuildCmd;
2151}
2152
2153void ProjectUnit::setOverrideBuildCmd(bool newOverrideBuildCmd)
2154{
2155 mOverrideBuildCmd = newOverrideBuildCmd;
2156}
2157
2158const QString &ProjectUnit::buildCmd() const
2159{
2160 return mBuildCmd;
2161}
2162
2163void ProjectUnit::setBuildCmd(const QString &newBuildCmd)
2164{
2165 mBuildCmd = newBuildCmd;
2166}
2167
2168bool ProjectUnit::link() const
2169{
2170 return mLink;
2171}
2172
2173void ProjectUnit::setLink(bool newLink)
2174{
2175 mLink = newLink;
2176}
2177
2178int ProjectUnit::priority() const
2179{
2180 return mPriority;
2181}
2182
2183void ProjectUnit::setPriority(int newPriority)
2184{
2185 if (mPriority!=newPriority) {
2186 mPriority = newPriority;
2187 if (mNode)
2188 mNode->priority = mPriority;
2189 }
2190}
2191
2192const QByteArray &ProjectUnit::encoding() const
2193{
2194 return mEncoding;
2195}
2196
2197void ProjectUnit::setEncoding(const QByteArray &newEncoding)
2198{
2199 if (mEncoding != newEncoding) {
2200 Editor * editor=mParent->unitEditor(this);
2201 if (editor) {
2202 editor->setEncodingOption(newEncoding);
2203 }
2204 mEncoding = newEncoding;
2205 }
2206}
2207
2208bool ProjectUnit::modified() const
2209{
2210 Editor * editor=mParent->unitEditor(this);
2211 if (editor) {
2212 return editor->modified();
2213 } else {
2214 return false;
2215 }
2216}
2217
2218void ProjectUnit::setModified(bool value)
2219{
2220 Editor * editor=mParent->unitEditor(this);
2221 // Mark the change in the coupled editor
2222 if (editor) {
2223 return editor->setModified(value);
2224 }
2225
2226 // If modified is set to true, mark project as modified too
2227 if (value) {
2228 mParent->setModified(true);
2229 }
2230}
2231
2232bool ProjectUnit::save()
2233{
2234 bool previous=mParent->fileSystemWatcher()->blockSignals(true);
2235 auto action = finally([&previous,this](){
2236 mParent->fileSystemWatcher()->blockSignals(previous);
2237 });
2238 bool result=true;
2239 Editor * editor=mParent->unitEditor(this);
2240 if (!editor && !fileExists(mFileName)) {
2241 // file is neither open, nor saved
2242 QStringList temp;
2243 stringsToFile(temp,mFileName);
2244 } else if (editor && editor->modified()) {
2245 result = editor->save();
2246 }
2247 if (mNode) {
2248 mNode->text = extractFileName(mFileName);
2249 }
2250 return result;
2251}
2252
2253PProjectModelNode &ProjectUnit::node()
2254{
2255 return mNode;
2256}
2257
2258void ProjectUnit::setNode(const PProjectModelNode &newNode)
2259{
2260 mNode = newNode;
2261}
2262
2263bool ProjectUnit::FileMissing() const
2264{
2265 return mFileMissing;
2266}
2267
2268void ProjectUnit::setFileMissing(bool newDontSave)
2269{
2270 mFileMissing = newDontSave;
2271}
2272
2273ProjectModel::ProjectModel(Project *project, QObject *parent):
2274 QAbstractItemModel(parent),
2275 mProject(project)
2276{
2277 mUpdateCount = 0;
2278 mIconProvider = new CustomFileIconProvider();
2279}
2280
2281ProjectModel::~ProjectModel()
2282{
2283 delete mIconProvider;
2284}
2285
2286void ProjectModel::beginUpdate()
2287{
2288 if (mUpdateCount==0) {
2289 beginResetModel();
2290 }
2291 mUpdateCount++;
2292}
2293
2294void ProjectModel::endUpdate()
2295{
2296 mUpdateCount--;
2297 if (mUpdateCount==0) {
2298 mIconProvider->setRootFolder(mProject->folder());
2299 endResetModel();
2300 }
2301}
2302
2303CustomFileIconProvider *ProjectModel::iconProvider() const
2304{
2305 return mIconProvider;
2306}
2307
2308Project *ProjectModel::project() const
2309{
2310 return mProject;
2311}
2312
2313QModelIndex ProjectModel::index(int row, int column, const QModelIndex &parent) const
2314{
2315 if (!parent.isValid()) {
2316 return createIndex(row,column,mProject->rootNode().get());
2317 }
2318 ProjectModelNode* parentNode = static_cast<ProjectModelNode*>(parent.internalPointer());
2319 if (!parentNode) {
2320 return QModelIndex();
2321 }
2322 if (row<0 || row>=parentNode->children.count())
2323 return QModelIndex();
2324 return createIndex(row,column,parentNode->children[row].get());
2325}
2326
2327QModelIndex ProjectModel::parent(const QModelIndex &child) const
2328{
2329 if (!child.isValid())
2330 return QModelIndex();
2331 ProjectModelNode * node = static_cast<ProjectModelNode*>(child.internalPointer());
2332 if (!node)
2333 return QModelIndex();
2334 return getParentIndex(node);
2335}
2336
2337int ProjectModel::rowCount(const QModelIndex &parent) const
2338{
2339 if (!parent.isValid())
2340 return 1;
2341 ProjectModelNode* p = static_cast<ProjectModelNode*>(parent.internalPointer());
2342 if (p) {
2343 return p->children.count();
2344 } else {
2345 return mProject->rootNode()->children.count();
2346 }
2347}
2348
2349int ProjectModel::columnCount(const QModelIndex &) const
2350{
2351 return 1;
2352}
2353
2354QVariant ProjectModel::data(const QModelIndex &index, int role) const
2355{
2356 if (!index.isValid())
2357 return QVariant();
2358 ProjectModelNode* p = static_cast<ProjectModelNode*>(index.internalPointer());
2359 if (!p)
2360 return QVariant();
2361 if (role == Qt::DisplayRole) {
2362 if (p == mProject->rootNode().get()) {
2363 QString branch;
2364 if (mIconProvider->VCSRepository()->hasRepository(branch))
2365 return QString("%1 [%2]").arg(p->text,branch);
2366 }
2367 return p->text;
2368 } else if (role==Qt::EditRole) {
2369 return p->text;
2370 } else if (role == Qt::DecorationRole) {
2371 QIcon icon;
2372 if (p->unitIndex>=0) {
2373 icon = mIconProvider->icon(mProject->units()[p->unitIndex]->fileName());
2374 } else {
2375 if (p == mProject->rootNode().get()) {
2376 QString branch;
2377 if (mIconProvider->VCSRepository()->hasRepository(branch))
2378 icon = pIconsManager->getIcon(IconsManager::FILESYSTEM_GIT);
2379 } else {
2380 switch(p->folderNodeType) {
2381 case ProjectSpecialFolderNode::HEADERS:
2382 icon = pIconsManager->getIcon(IconsManager::FILESYSTEM_HEADERS_FOLDER);
2383 break;
2384 case ProjectSpecialFolderNode::SOURCES:
2385 icon = pIconsManager->getIcon(IconsManager::FILESYSTEM_SOURCES_FOLDER);
2386 break;
2387 default:
2388 icon = pIconsManager->getIcon(IconsManager::FILESYSTEM_FOLDER);
2389 }
2390 }
2391 if (icon.isNull())
2392 icon = mIconProvider->icon(QFileIconProvider::Folder);
2393 }
2394 return icon;
2395 }
2396 return QVariant();
2397}
2398
2399Qt::ItemFlags ProjectModel::flags(const QModelIndex &index) const
2400{
2401 if (!index.isValid())
2402 return Qt::NoItemFlags;
2403 ProjectModelNode* p = static_cast<ProjectModelNode*>(index.internalPointer());
2404 if (!p)
2405 return Qt::NoItemFlags;
2406 if (p==mProject->rootNode().get())
2407 return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
2408 if (mProject && mProject->modelType() == ProjectModelType::FileSystem) {
2409 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
2410 if (p->unitIndex>=0)
2411 flags.setFlag(Qt::ItemIsEditable);
2412 return flags;
2413 } else {
2414 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
2415 if (p->unitIndex<0) {
2416 flags.setFlag(Qt::ItemIsDropEnabled);
2417 flags.setFlag(Qt::ItemIsDragEnabled,false);
2418 }
2419 return flags;
2420 }
2421}
2422
2423bool ProjectModel::setData(const QModelIndex &index, const QVariant &value, int role)
2424{
2425 if (!index.isValid())
2426 return false;
2427 ProjectModelNode* p = static_cast<ProjectModelNode*>(index.internalPointer());
2428 PProjectModelNode node = mProject->pointerToNode(p);
2429 if (!node)
2430 return false;
2431 if (role == Qt::EditRole) {
2432 if (node == mProject->rootNode()) {
2433 QString newName = value.toString().trimmed();
2434 if (newName.isEmpty())
2435 return false;
2436 mProject->setName(newName);
2437 emit dataChanged(index,index);
2438 return true;
2439 }
2440 int idx = node->unitIndex;
2441 if (idx >= 0) {
2442 //change unit name
2443 PProjectUnit unit = mProject->units()[idx];
2444 QString newName = value.toString().trimmed();
2445 if (newName.isEmpty())
2446 return false;
2447 if (newName == node->text)
2448 return false;
2449 QString oldName = unit->fileName();
2450 QString curDir = extractFilePath(oldName);
2451 newName = QDir(curDir).absoluteFilePath(newName);
2452 // Only continue if the user says so...
2453 if (fileExists(newName) && newName.compare(oldName, PATH_SENSITIVITY)!=0) {
2454 // don't remove when changing case for example
2455 if (QMessageBox::question(nullptr,
2456 tr("File exists"),
2457 tr("File '%1' already exists. Delete it now?")
2458 .arg(newName),
2459 QMessageBox::Yes | QMessageBox::No,
2460 QMessageBox::No) == QMessageBox::Yes) {
2461 // Close the target file...
2462 Editor * e=mProject->editorList()->getOpenedEditorByFilename(newName);
2463 if (e)
2464 mProject->editorList()->closeEditor(e);
2465
2466 // Remove it from the current project...
2467 int projindex = mProject->indexInUnits(newName);
2468 if (projindex>=0) {
2469 mProject->removeUnit(projindex,false);
2470 }
2471
2472 // All references to the file are removed. Delete the file from disk
2473 if (!QFile::remove(newName)) {
2474 QMessageBox::critical(nullptr,
2475 tr("Remove failed"),
2476 tr("Failed to remove file '%1'")
2477 .arg(newName),
2478 QMessageBox::Ok);
2479 return false;
2480 }
2481 } else {
2482 return false;
2483 }
2484 }
2485 // Target filename does not exist anymore. Do a rename
2486 // change name in project file first (no actual file renaming on disk)
2487 //save old file, if it is openned;
2488 // remove old file from monitor list
2489 mProject->fileSystemWatcher()->removePath(oldName);
2490
2491 if (!QFile::rename(oldName,newName)) {
2492 QMessageBox::critical(nullptr,
2493 tr("Rename failed"),
2494 tr("Failed to rename file '%1' to '%2'")
2495 .arg(oldName,newName),
2496 QMessageBox::Ok);
2497 return false;
2498 }
2499 mProject->saveUnitAs(idx,newName);
2500
2501 // Add new filename to file minitor
2502 mProject->fileSystemWatcher()->addPath(newName);
2503
2504 //suffix changed
2505 if (mProject && mProject->modelType() == ProjectModelType::FileSystem
2506 && QFileInfo(oldName).suffix()!=QFileInfo(newName).suffix()) {
2507 mProject->rebuildNodes();
2508 } else
2509 emit dataChanged(index,index);
2510 return true;
2511 } else {
2512 //change folder name
2513 QString newName = value.toString().trimmed();
2514 if (newName.isEmpty())
2515 return false;
2516 if (newName == node->text)
2517 return false;
2518 node->text = newName;
2519 mProject->updateFolders();
2520 mProject->saveAll();
2521 emit dataChanged(index,index);
2522 return true;
2523 }
2524
2525 }
2526 return false;
2527}
2528
2529QModelIndex ProjectModel::getParentIndex(ProjectModelNode * node) const
2530{
2531 PProjectModelNode parent = node->parent.lock();
2532 if (!parent) // root node
2533 return QModelIndex();
2534 PProjectModelNode grand = parent->parent.lock();
2535 if (!grand) {
2536 return createIndex(0,0,parent.get());
2537 }
2538
2539 int row = grand->children.indexOf(parent);
2540 if (row<0)
2541 return QModelIndex();
2542 return createIndex(row,0,parent.get());
2543}
2544
2545bool ProjectModel::canDropMimeData(const QMimeData * data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent) const
2546{
2547
2548 if (!data || action != Qt::MoveAction)
2549 return false;
2550 if (!parent.isValid())
2551 return false;
2552 // check if the format is supported
2553 QStringList types = mimeTypes();
2554 if (types.isEmpty())
2555 return false;
2556 QString format = types.at(0);
2557 if (!data->hasFormat(format))
2558 return false;
2559
2560 QModelIndex idx = parent;
2561// if (row >= rowCount(parent) || row < 0) {
2562// return false;
2563// } else {
2564// idx= index(row,column,parent);
2565// }
2566 ProjectModelNode* p= static_cast<ProjectModelNode*>(idx.internalPointer());
2567 PProjectModelNode node = mProject->pointerToNode(p);
2568 if (node->unitIndex>=0)
2569 return false;
2570 QByteArray encoded = data->data(format);
2571 QDataStream stream(&encoded, QIODevice::ReadOnly);
2572 while (!stream.atEnd()) {
2573 qint32 r, c;
2574 quintptr v;
2575 stream >> r >> c >> v;
2576 ProjectModelNode* droppedPointer= (ProjectModelNode*)(v);
2577 PProjectModelNode droppedNode = mProject->pointerToNode(droppedPointer);
2578 PProjectModelNode oldParent = droppedNode->parent.lock();
2579 if (oldParent == node)
2580 return false;
2581 }
2582 return true;
2583}
2584
2585Qt::DropActions ProjectModel::supportedDropActions() const
2586{
2587 return Qt::MoveAction;
2588}
2589
2590bool ProjectModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent)
2591{
2592 // check if the action is supported
2593 if (!data || action != Qt::MoveAction)
2594 return false;
2595 // check if the format is supported
2596 QStringList types = mimeTypes();
2597 if (types.isEmpty())
2598 return false;
2599 QString format = types.at(0);
2600 if (!data->hasFormat(format))
2601 return false;
2602
2603 if (!parent.isValid())
2604 return false;
2605 ProjectModelNode* p= static_cast<ProjectModelNode*>(parent.internalPointer());
2606 PProjectModelNode node = mProject->pointerToNode(p);
2607
2608 QByteArray encoded = data->data(format);
2609 QDataStream stream(&encoded, QIODevice::ReadOnly);
2610 QVector<int> rows,cols;
2611 QVector<intptr_t> pointers;
2612 while (!stream.atEnd()) {
2613 qint32 r, c;
2614 quintptr v;
2615 stream >> r >> c >> v;
2616 rows.append(r);
2617 cols.append(c);
2618 pointers.append(v);
2619 }
2620 for (int i=pointers.count()-1;i>=0;i--) {
2621 int r = rows[i];
2622 intptr_t v = pointers[i];
2623 ProjectModelNode* droppedPointer= (ProjectModelNode*)(v);
2624 PProjectModelNode droppedNode = mProject->pointerToNode(droppedPointer);
2625 PProjectModelNode oldParent = droppedNode->parent.lock();
2626 QModelIndex oldParentIndex = getParentIndex(droppedPointer);
2627 beginRemoveRows(oldParentIndex,r,r);
2628 if (oldParent)
2629 oldParent->children.removeAt(r);
2630 endRemoveRows();
2631 droppedNode->parent = node;
2632 node->children.append(droppedNode);
2633 if (droppedNode->unitIndex>=0) {
2634 PProjectUnit unit = mProject->units()[droppedNode->unitIndex];
2635 unit->setFolder(mProject->getFolderPath(node));
2636 }
2637 QModelIndex newParentIndex = getParentIndex(droppedPointer);
2638 beginInsertRows(newParentIndex,node->children.count()-1,node->children.count()-1);
2639 endInsertRows();
2640 mProject->saveAll();
2641 return true;
2642 }
2643
2644 return false;
2645}
2646
2647QMimeData *ProjectModel::mimeData(const QModelIndexList &indexes) const
2648{
2649 if (indexes.count() <= 0)
2650 return nullptr;
2651 QStringList types = mimeTypes();
2652 if (types.isEmpty())
2653 return nullptr;
2654 QMimeData *data = new QMimeData();
2655 QString format = types.at(0);
2656 QByteArray encoded;
2657 QDataStream stream(&encoded, QIODevice::WriteOnly);
2658 QModelIndexList::ConstIterator it = indexes.begin();
2659 QList<QUrl> urls;
2660 for (; it != indexes.end(); ++it) {
2661 stream << (qint32)((*it).row()) << (qint32)((*it).column()) << (quintptr)((*it).internalPointer());
2662 ProjectModelNode* p = static_cast<ProjectModelNode*>((*it).internalPointer());
2663 if (p && p->unitIndex>=0) {
2664 urls.append(QUrl::fromLocalFile(mProject->units()[p->unitIndex]->fileName()));
2665 }
2666 }
2667 if (!urls.isEmpty())
2668 data->setUrls(urls);
2669 data->setData(format, encoded);
2670 return data;
2671}
2672
2673ProjectModelSortFilterProxy::ProjectModelSortFilterProxy(QObject *parent):
2674 QSortFilterProxyModel(parent)
2675{
2676
2677}
2678
2679bool ProjectModelSortFilterProxy::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
2680{
2681 if (!sourceModel())
2682 return false;
2683 ProjectModelNode* pLeft=nullptr;
2684 if (source_left.isValid())
2685 pLeft = static_cast<ProjectModelNode*>(source_left.internalPointer());
2686 ProjectModelNode* pRight=nullptr;
2687 if (source_right.isValid())
2688 pRight = static_cast<ProjectModelNode*>(source_right.internalPointer());
2689 if (!pLeft)
2690 return true;
2691 if (!pRight)
2692 return false;
2693 if (pLeft->unitIndex<0 && pRight->unitIndex>=0)
2694 return true;
2695 if (pLeft->unitIndex>=0 && pRight->unitIndex<0)
2696 return false;
2697 if (pLeft->priority!=pRight->priority)
2698 return pLeft->priority>pRight->priority;
2699 return QString::compare(pLeft->text, pRight->text)<0;
2700}
2701