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