1 #include "cleandialog.h"
2 #include "ui_cleandialog.h"
3 
4 #include "configmanager.h"
5 #include "utilsUI.h"
6 
7 QString CleanDialog::defaultExtensions = "log,aux,dvi,lof,lot,bit,idx,glo,bbl,bcf,ilg,toc,ind,out,blg,fdb_latexmk,fls,run.xml";
8 QString CleanDialog::currentExtensions = CleanDialog::defaultExtensions;
9 int CleanDialog::scopeID = 0;
10 
11 static const int AbsFilePathRole = Qt::UserRole;
12 
CleanDialog(QWidget * parent)13 CleanDialog::CleanDialog(QWidget *parent) :
14 	QDialog(parent),
15 	ui(new Ui::CleanDialog)
16 {
17 	ui->setupUi(this);
18 	UtilsUi::resizeInFontHeight(this, 38, 22);
19 
20 	ConfigManagerInterface *config = ConfigManager::getInstance();
21 	config->registerOption("CleanDialog/Extensions", &currentExtensions, defaultExtensions);
22 	config->registerOption("CleanDialog/Scope", &scopeID, 0);
23 	if (scopeID < 0 || scopeID >= MAX_SCOPE) scopeID = 0;
24 
25 	QString allowedChars = "[^\\\\/\\?\\%\\*:|\"<>\\s,;]";
26     QRegularExpressionValidator *rxValExtensionList = new QRegularExpressionValidator(QRegularExpression(QString("(%1+\\.)*%1+(,(%1+\\.)*%1+)*").arg(allowedChars)), this);
27 	int dummyPos;
28 	if (rxValExtensionList->validate(currentExtensions, dummyPos) == QValidator::Acceptable) {
29 		ui->leExtensions->setText(currentExtensions);
30 	} else {
31 		UtilsUi::txsWarning("Invalid extension list found. Resetting to default.");
32 		currentExtensions = defaultExtensions;
33 		ui->leExtensions->setText(currentExtensions);
34 	}
35 	ui->leExtensions->setValidator(rxValExtensionList);
36 
37 	ui->pbResetExtensions->setIcon(getRealIcon("edit-undo"));
38 
39 	connect(ui->pbResetExtensions, SIGNAL(clicked()), SLOT(resetExtensions()));
40 	connect(ui->cbScope, SIGNAL(currentIndexChanged(int)), SLOT(updateFilesToRemove()));
41 	connect(ui->leExtensions, SIGNAL(editingFinished()), SLOT(updateFilesToRemove()));
42 	connect(ui->buttonBox, SIGNAL(accepted()), SLOT(onAccept()));
43 	connect(ui->buttonBox, SIGNAL(rejected()), SLOT(onReject()));
44 }
45 
~CleanDialog()46 CleanDialog::~CleanDialog() {
47 	delete ui;
48 }
49 
50 /* internally sets up possible clean variants. Call before exec()
51    Returns true, if at least one variant can be applied. */
checkClean(const LatexDocuments & docs)52 bool CleanDialog::checkClean(const LatexDocuments &docs) {
53 	bool somethingToClean = false;
54 	ui->cbScope->blockSignals(true);  // don't emit currentIndexChanged while populating the combo box
55 	if (docs.masterDocument) {
56 		masterFile = docs.masterDocument->getFileName();
57 		ui->cbScope->addItem(tr("Project (Master file folder and all subfolders)"), CleanDialog::Project);
58 		if (scopeID == CleanDialog::Project) ui->cbScope->setCurrentIndex(ui->cbScope->count()-1);
59 		somethingToClean = true;
60 	}
61 	if (docs.currentDocument && docs.currentDocument->getFileInfo().suffix().toLower() == "tex") {
62 		currentTexFile = docs.currentDocument->getFileName();
63 		ui->cbScope->addItem(tr("Current File"), CleanDialog::CurrentTexFile);
64 		if (scopeID == CleanDialog::CurrentTexFile) ui->cbScope->setCurrentIndex(ui->cbScope->count()-1);
65 		ui->cbScope->addItem(tr("Current File Folder"), CleanDialog::CurrentFileFolder);
66 		if (scopeID == CleanDialog::CurrentFileFolder) ui->cbScope->setCurrentIndex(ui->cbScope->count()-1);
67 		somethingToClean = true;
68 	}
69 	foreach (LatexDocument *doc, docs.documents) {
70 		if (doc->getFileInfo().suffix().toLower() == "tex") openTexFiles.append(doc->getFileName());
71 	}
72 	if (!openTexFiles.isEmpty()) {
73 		ui->cbScope->addItem(tr("Open Files"), CleanDialog::OpenTexFiles);
74 		if (scopeID == CleanDialog::OpenTexFiles) ui->cbScope->setCurrentIndex(ui->cbScope->count()-1);
75 		somethingToClean = true;
76 	}
77 	ui->cbScope->blockSignals(false);
78 
79 	if (somethingToClean)
80 		updateFilesToRemove();
81 	return somethingToClean;
82 }
83 
resetExtensions()84 void CleanDialog::resetExtensions() {
85 	ui->leExtensions->setText(defaultExtensions);
86 }
87 
onAccept()88 void CleanDialog::onAccept() {
89 	for (int i=0; i<ui->lwFiles->count(); i++) {
90 		QFile::remove(ui->lwFiles->item(i)->data(AbsFilePathRole).toString());
91 	}
92 	accept();
93 }
94 
onReject()95 void CleanDialog::onReject() {
96 	reject();
97 }
98 
updateFilesToRemove()99 void CleanDialog::updateFilesToRemove() {
100 	scopeID = ui->cbScope->itemData(ui->cbScope->currentIndex()).toInt();
101 	Scope scope = (Scope) scopeID;
102 #if (QT_VERSION>=QT_VERSION_CHECK(5,14,0))
103     QStringList extList(ui->leExtensions->text().split(',', Qt::SkipEmptyParts));
104 #else
105     QStringList extList(ui->leExtensions->text().split(',', QString::SkipEmptyParts));
106 #endif
107 	QStringList forbiddenExtensions = QStringList() << "tex" << "lytex";
108 	QStringList found;
109 	foreach (const QString &ext, forbiddenExtensions) {
110 		if (extList.contains(ext))
111 			found << ext;
112 	}
113 	if (!found.isEmpty()) {
114 		UtilsUi::txsWarning(tr("For your own safety clean will not delete the files with the following extensions:") + QString("\n") + extList.join(", "));
115 		foreach (QString ext, found)
116 			extList.removeAll(ext);
117 	}
118 	currentExtensions = extList.join(",");
119 	ui->leExtensions->setText(currentExtensions);
120 
121 	QStringList files;
122 	files << filesToRemove(scope, extList);
123 	ui->lwFiles->clear();
124 
125 	foreach (const QString &absName, files) {
126 		QFileInfo f(absName);
127 		QListWidgetItem *item = new QListWidgetItem(f.fileName());
128 		item->setData(AbsFilePathRole, absName);
129 		item->setToolTip(absName);
130 		ui->lwFiles->addItem(item);
131 	}
132 }
133 
filesToRemove(CleanDialog::Scope scope,const QStringList & extensionFilter)134 QStringList CleanDialog::filesToRemove(CleanDialog::Scope scope, const QStringList &extensionFilter) {
135 	QStringList files;
136 	switch (scope) {
137 	case Project:
138 		{
139 			QStringList filterList;
140 			foreach (const QString &ext, extensionFilter) filterList << "*." + ext;
141 			files << filesToRemoveFromDir(QFileInfo(masterFile).absoluteDir(), filterList);
142 		}
143 		break;
144 	case CurrentTexFile:
145 		{
146 			QFileInfo fi(currentTexFile);
147 			QString basename=fi.absolutePath()+"/"+fi.completeBaseName();
148 			foreach(const QString& ext, extensionFilter) {
149 				QFileInfo f(basename + "." + ext);
150 				if (f.exists())
151 					files << f.absoluteFilePath();
152 			}
153 		}
154 		break;
155 	case CurrentFileFolder:
156 		{
157 			QStringList filterList;
158 			foreach (const QString &ext, extensionFilter) filterList << "*." + ext;
159 			files << filesToRemoveFromDir(QFileInfo(currentTexFile).absoluteDir(), filterList);
160 		}
161 		break;
162 	case OpenTexFiles:
163 		{
164 			foreach(const QString& finame, openTexFiles){
165 				QFileInfo fi(finame);
166 				QString basename=fi.absolutePath()+"/"+fi.completeBaseName();
167 				foreach(const QString& ext, extensionFilter) {
168 					QFileInfo f(basename + "." + ext);
169 					if (f.exists())
170 						files << f.absoluteFilePath();
171 				}
172 			}
173 		}
174 		break;
175 	case None:
176 		break;
177     default:
178         break;
179 	}
180 	return files;
181 }
182 
filesToRemoveFromDir(const QDir & dir,const QStringList & extensionFilter,bool recursive)183 QStringList CleanDialog::filesToRemoveFromDir(const QDir &dir, const QStringList &extensionFilter, bool recursive) {
184 	QStringList files;
185 	foreach (const QFileInfo &fi, dir.entryInfoList(extensionFilter, QDir::Files)) {
186 		files << fi.absoluteFilePath();
187 	}
188 	if (recursive) {
189 		foreach (const QFileInfo &fi, dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
190             if(fi.fileName().startsWith("."))
191                 continue; // filter directories starting with . (e.g. .git) since they are not automatiocally hidden on windows
192 			files << filesToRemoveFromDir(fi.absoluteFilePath(), extensionFilter, recursive);
193 		}
194 	}
195 	return files;
196 }
197