1 #include <QScrollBar>
2 #include <QDateTime>
3 #include <QTreeWidgetItem>
4 #include <QFileInfo>
5 #include <QDir>
6 #include <QHash>
7 #include <QHashIterator>
8 #if defined(QMC2_OS_WIN)
9 #include <windows.h>
10 #endif
11 
12 #include "manualscanner.h"
13 #include "machinelist.h"
14 #include "softwarelist.h"
15 #include "settings.h"
16 #include "qmc2main.h"
17 #include "macros.h"
18 
19 extern Settings *qmc2Config;
20 extern MachineList *qmc2MachineList;
21 extern SoftwareList *qmc2SoftwareList;
22 extern MainWindow *qmc2MainWindow;
23 extern QHash<QString, QTreeWidgetItem *> qmc2MachineListItemHash;
24 
25 #define userDataDb	qmc2MachineList->userDataDb()
26 
ManualScanner(int mode,QWidget * parent)27 ManualScanner::ManualScanner(int mode, QWidget *parent) :
28 	QDialog(parent),
29 	m_mode(mode)
30 {
31 	setupUi(this);
32 	QFont logFont;
33 	logFont.fromString(qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/LogFont").toString());
34 	plainTextEdit->setFont(logFont);
35 	switch ( m_mode ) {
36 		case QMC2_MANUALSCANNER_MODE_SYSTEMS:
37 			setWindowTitle(tr("System manual scanner"));
38 			m_settingsKey = "SystemManualScanner";
39 			break;
40 		case QMC2_MANUALSCANNER_MODE_SOFTWARE:
41 			setWindowTitle(tr("Software manual scanner"));
42 			m_settingsKey = "SoftwareManualScanner";
43 			break;
44 	}
45 	m_logScrollTimer.setSingleShot(true);
46 	connect(&m_logScrollTimer, SIGNAL(timeout()), this, SLOT(scrollLog()));
47 	log(tr("click 'scan now' to start"));
48 }
49 
~ManualScanner()50 ManualScanner::~ManualScanner()
51 {
52 	// NOP
53 }
54 
on_pushButtonScanNow_clicked()55 void ManualScanner::on_pushButtonScanNow_clicked()
56 {
57 	pushButtonClose->setEnabled(false);
58 	pushButtonScanNow->setEnabled(false);
59 	plainTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
60 	plainTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
61 	plainTextEdit->clear();
62 	log(tr("scanner started"));
63 	scan();
64 	log(tr("scanner ended"));
65 	plainTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
66 	plainTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
67 	pushButtonScanNow->setEnabled(true);
68 	pushButtonClose->setEnabled(true);
69 }
70 
log(const QString & message)71 void ManualScanner::log(const QString &message)
72 {
73 	plainTextEdit->appendPlainText(QDateTime::currentDateTime().toString("hh:mm:ss.zzz") + ": " + message);
74 	m_logScrollTimer.start(10);
75 }
76 
scrollLog()77 void ManualScanner::scrollLog()
78 {
79 	plainTextEdit->horizontalScrollBar()->setValue(plainTextEdit->horizontalScrollBar()->minimum());
80 	plainTextEdit->verticalScrollBar()->setValue(plainTextEdit->verticalScrollBar()->maximum());
81 }
82 
showEvent(QShowEvent * e)83 void ManualScanner::showEvent(QShowEvent *e)
84 {
85 	restoreGeometry(qmc2Config->value(QMC2_FRONTEND_PREFIX + QString("Layout/%1/Geometry").arg(m_settingsKey), QByteArray()).toByteArray());
86 	QDialog::showEvent(e);
87 }
88 
hideEvent(QHideEvent * e)89 void ManualScanner::hideEvent(QHideEvent *e)
90 {
91 	qmc2Config->setValue(QMC2_FRONTEND_PREFIX + QString("Layout/%1/Geometry").arg(m_settingsKey), saveGeometry());
92 	QDialog::hideEvent(e);
93 }
94 
closeEvent(QCloseEvent * e)95 void ManualScanner::closeEvent(QCloseEvent *e)
96 {
97 	if ( pushButtonClose->isEnabled() )
98 		e->accept();
99 	else
100 		e->ignore();
101 }
102 
scan()103 void ManualScanner::scan()
104 {
105 	QStringList pathList;
106 	switch ( m_mode ) {
107 		case QMC2_MANUALSCANNER_MODE_SYSTEMS:
108 			pathList = qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SystemManualFolder").toString().split(';', QString::SkipEmptyParts);
109 			break;
110 		case QMC2_MANUALSCANNER_MODE_SOFTWARE:
111 			pathList = qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/SoftwareManualFolder").toString().split(';', QString::SkipEmptyParts);
112 			break;
113 	}
114 	QHash<QString, QStringList> pdfManualHash;
115 	foreach (QString path, pathList) {
116 		QDir d(QDir::cleanPath(path));
117 		path = d.absolutePath();
118 		QStringList fileList;
119 		log(tr("reading file list for path '%1'").arg(path));
120 		qApp->processEvents();
121 		recursiveFileList(path, &fileList);
122 		log(tr("scanning %n file(s)", "", fileList.count()));
123 		quint64 numIgnored = 0, numInaccessible = 0, numStoredInDb = 0, fileCount = 0;
124 		foreach (QString file, fileList) {
125 			QFileInfo fi(file);
126 			if ( fi.suffix().toLower() == "pdf" ) {
127 				if ( fi.isReadable() ) {
128 					QString entryName(fi.baseName().toLower());
129 					if ( m_mode == QMC2_MANUALSCANNER_MODE_SYSTEMS ) {
130 						if ( qmc2MachineListItemHash.contains(entryName) ) {
131 							log(tr("adding '%1' as a manual for '%2'").arg(file).arg(entryName));
132 							numStoredInDb++;
133 							pdfManualHash[entryName] << file;
134 						} else {
135 							log(tr("ignoring '%1' because its name doesn't match any machine").arg(file));
136 							numIgnored++;
137 						}
138 					} else {
139 						QString listName(fi.absolutePath());
140 						listName.remove(path + '/');
141 						QString setKey(listName + ':' + entryName);
142 						log(tr("adding '%1' as a manual for '%2'").arg(file).arg(setKey));
143 						numStoredInDb++;
144 						pdfManualHash[setKey] << file;
145 					}
146 				} else {
147 					log(tr("can't read '%1' - please check access permissions").arg(file));
148 					numInaccessible++;
149 				}
150 			} else {
151 				log(tr("ignoring '%1' because it doesn't appear to be a PDF document").arg(file));
152 				numIgnored++;
153 			}
154 			if ( fileCount++ % QMC2_MANUALSCANNER_SCAN_RESPONSE == 0 )
155 				qApp->processEvents();
156 		}
157 		log(tr("scan statistics for path '%1'").arg(path) + ": " + tr("%n file(s) scanned", "", fileList.count()) + ", " + tr("%n manual(s) stored in database", "", numStoredInDb) + ", " + tr("%n file(s) ignored", "", numIgnored) + ", " + tr("%n file(s) inaccessible", "", numInaccessible));
158 	}
159 	log(tr("updating the database"));
160 	switch ( m_mode ) {
161 		case QMC2_MANUALSCANNER_MODE_SYSTEMS:
162 			userDataDb->recreateSystemManualTable();
163 			break;
164 		case QMC2_MANUALSCANNER_MODE_SOFTWARE:
165 			userDataDb->recreateSoftwareManualTable();
166 			break;
167 	}
168 	userDataDb->beginTransaction();
169 	QHashIterator<QString, QStringList> pdfManualHashIterator(pdfManualHash);
170 	quint64 fileCount = 0;
171 	while ( pdfManualHashIterator.hasNext() ) {
172 		pdfManualHashIterator.next();
173 		QStringList keyWords;
174 		switch ( m_mode ) {
175 			case QMC2_MANUALSCANNER_MODE_SYSTEMS:
176 				userDataDb->setSystemManualPath(pdfManualHashIterator.key(), pdfManualHashIterator.value().join(";"));
177 				break;
178 			case QMC2_MANUALSCANNER_MODE_SOFTWARE:
179 				keyWords = pdfManualHashIterator.key().split(':', QString::SkipEmptyParts);
180 				userDataDb->setSoftwareManualPath(keyWords.at(0), keyWords.at(1), pdfManualHashIterator.value().join(";"));
181 				break;
182 		}
183 		if ( fileCount++ % QMC2_MANUALSCANNER_DB_COMMIT == 0 ) {
184 			userDataDb->commitTransaction();
185 			qApp->processEvents();
186 			userDataDb->beginTransaction();
187 		}
188 	}
189 	userDataDb->commitTransaction();
190 	log(tr("database update finished"));
191 	switch ( m_mode ) {
192 		case QMC2_MANUALSCANNER_MODE_SYSTEMS:
193 			QTimer::singleShot(0, qmc2MainWindow, SLOT(checkSystemManualAvailability()));
194 			break;
195 		case QMC2_MANUALSCANNER_MODE_SOFTWARE:
196 			if ( qmc2SoftwareList )
197 				QTimer::singleShot(0, qmc2SoftwareList, SLOT(checkSoftwareManualAvailability()));
198 			break;
199 	}
200 }
201 
recursiveFileList(const QString & sDir,QStringList * fileNames)202 void ManualScanner::recursiveFileList(const QString &sDir, QStringList *fileNames)
203 {
204 #if defined(QMC2_OS_WIN)
205 	WIN32_FIND_DATA ffd;
206 	QString dirName(QDir::toNativeSeparators(QDir::cleanPath(sDir + "/*")));
207 #ifdef UNICODE
208 	HANDLE hFind = FindFirstFile((TCHAR *)dirName.utf16(), &ffd);
209 #else
210 	HANDLE hFind = FindFirstFile((TCHAR *)dirName.toUtf8().constData(), &ffd);
211 #endif
212 	if ( hFind != INVALID_HANDLE_VALUE ) {
213 		do {
214 #ifdef UNICODE
215 			QString fName(QString::fromUtf16((ushort*)ffd.cFileName));
216 #else
217 			QString fName(QString::fromLocal8Bit(ffd.cFileName));
218 #endif
219 			if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
220 				if ( fName != ".." && fName != "." )
221 					recursiveFileList(sDir + "/" + fName, fileNames);
222 			} else {
223 				fileNames->append(sDir + "/" + fName);
224 				if ( fileNames->count() % QMC2_MANUALSCANNER_SCAN_RESPONSE == 0 )
225 					qApp->processEvents();
226 			}
227 		} while ( FindNextFile(hFind, &ffd) != 0 );
228 	}
229 #else
230 	QDir dir(sDir);
231 	foreach (QFileInfo info, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::System)) {
232 		QString path(info.filePath());
233 		if ( info.isDir() ) {
234 			// directory recursion
235 			if ( info.fileName() != ".." && info.fileName() != "." )
236 				recursiveFileList(path, fileNames);
237 		} else {
238 			fileNames->append(path);
239 			if ( fileNames->count() % QMC2_MANUALSCANNER_SCAN_RESPONSE == 0 )
240 				qApp->processEvents();
241 		}
242 	}
243 #endif
244 }
245