195 return path.mid (lastpos + 1); |
196 return path.mid (lastpos + 1); |
196 |
197 |
197 return path; |
198 return path; |
198 } |
199 } |
199 |
200 |
200 QString DocumentManager::findDocumentPath (QString relativePath, bool subdirs) |
201 QString DocumentManager::findDocument(QString name) const |
201 { |
202 { |
202 // LDraw models use backslashes as path separators. Replace those into forward slashes for Qt. |
203 name = name.replace("\\", "/"); |
203 relativePath.replace ("\\", "/"); |
204 |
204 |
205 for (const Library& library : ::config->libraries()) |
205 // Try find it relative to other currently open documents. We want a file in the immediate vicinity of a current |
206 { |
206 // part model to override stock LDraw stuff. |
207 for (const QString& subdirectory : {"parts", "p"}) |
207 QString relativeTopDir = Basename (Dirname (relativePath)); |
208 { |
208 |
209 QDir dir {library.path + "/" + subdirectory}; |
209 for (LDDocument* document : m_documents) |
210 |
210 { |
211 if (dir.exists(name)) |
211 QString partpath = format ("%1/%2", Dirname (document->fullPath()), relativePath); |
212 return QDir::cleanPath(dir.filePath(name)); |
212 QFileInfo fileinfo (partpath); |
213 } |
213 |
214 } |
214 if (fileinfo.exists()) |
215 |
215 { |
216 return {}; |
216 // Ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48 |
217 } |
217 QString partTopDir = Basename (Dirname (partpath)); |
218 |
218 |
219 void DocumentManager::printParseErrorMessage(QString message) |
219 for (QString subdir : specialSubdirectories) |
220 { |
|
221 print(message); |
|
222 } |
|
223 |
|
224 LDDocument* DocumentManager::openDocument( |
|
225 QString path, |
|
226 bool search, |
|
227 bool implicit, |
|
228 LDDocument* fileToOverride |
|
229 ) { |
|
230 if (search and not QFileInfo {path}.exists()) |
|
231 { |
|
232 // Convert the file name to lowercase when searching because some parts contain subfile |
|
233 // subfile references with uppercase file names. I'll assume here that the library will |
|
234 // always use lowercase file names for the part files. |
|
235 path = this->findDocument(path.toLower()); |
|
236 } |
|
237 |
|
238 QFile file {path}; |
|
239 |
|
240 if (file.open(QIODevice::ReadOnly)) |
|
241 { |
|
242 LDDocument* load = fileToOverride; |
|
243 |
|
244 if (fileToOverride == nullptr) |
|
245 load = m_window->newDocument(implicit); |
|
246 |
|
247 load->setFullPath(path); |
|
248 load->setName(LDDocument::shortenName(path)); |
|
249 |
|
250 // Loading the file shouldn't count as actual edits to the document. |
|
251 load->history()->setIgnoring (true); |
|
252 |
|
253 Parser parser {file}; |
|
254 Winding winding = NoWinding; |
|
255 load->header = parser.parseHeader(winding); |
|
256 load->setWinding(winding); |
|
257 parser.parseBody(*load); |
|
258 file.close(); |
|
259 |
|
260 if (m_loadingMainFile) |
|
261 { |
|
262 int numWarnings = 0; |
|
263 |
|
264 for (LDObject* object : load->objects()) |
220 { |
265 { |
221 if ((partTopDir == subdir) != (relativeTopDir == subdir)) |
266 if (object->type() == LDObjectType::Error) |
222 goto skipthis; |
267 numWarnings += 1; |
223 } |
268 } |
224 |
269 |
225 return partpath; |
270 m_window->changeDocument(load); |
226 } |
271 print(tr("File %1 opened successfully (%2 errors)."), load->name(), numWarnings); |
227 skipthis: |
272 } |
228 continue; |
273 |
229 } |
274 load->history()->setIgnoring (false); |
230 |
275 return load; |
231 if (QFileInfo::exists (relativePath)) |
276 } |
232 return relativePath; |
277 else |
233 |
278 { |
234 // Try with just the LDraw path first |
|
235 QString fullPath = format ("%1" DIRSLASH "%2", m_config->lDrawPath(), relativePath); |
|
236 |
|
237 if (QFileInfo::exists (fullPath)) |
|
238 return fullPath; |
|
239 |
|
240 if (subdirs) |
|
241 { |
|
242 // Look in sub-directories: parts and p. Also look in the download path, since that's where we download parts |
|
243 // from the PT to. |
|
244 QStringList dirs = { m_config->lDrawPath(), m_config->downloadFilePath() }; |
|
245 for (const QString& topdir : dirs) |
|
246 { |
|
247 for (const QString& subdir : QStringList ({ "parts", "p" })) |
|
248 { |
|
249 fullPath = format ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relativePath); |
|
250 |
|
251 if (QFile::exists (fullPath)) |
|
252 return fullPath; |
|
253 } |
|
254 } |
|
255 } |
|
256 |
|
257 // Did not find the file. |
|
258 return ""; |
|
259 } |
|
260 |
|
261 QFile* DocumentManager::openLDrawFile (QString relpath, bool subdirs, QString* pathpointer) |
|
262 { |
|
263 print ("Opening %1...\n", relpath); |
|
264 QString path = findDocumentPath (relpath, subdirs); |
|
265 |
|
266 if (pathpointer) |
|
267 *pathpointer = path; |
|
268 |
|
269 if (path.isEmpty()) |
|
270 return nullptr; |
279 return nullptr; |
271 |
280 } |
272 QFile* fp = new QFile (path); |
|
273 |
|
274 if (fp->open (QIODevice::ReadOnly)) |
|
275 return fp; |
|
276 |
|
277 fp->deleteLater(); |
|
278 return nullptr; |
|
279 } |
|
280 |
|
281 void DocumentManager::printParseErrorMessage(QString message) |
|
282 { |
|
283 print(message); |
|
284 } |
|
285 |
|
286 LDDocument* DocumentManager::openDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride) |
|
287 { |
|
288 // Convert the file name to lowercase when searching because some parts contain subfile |
|
289 // subfile references with uppercase file names. I'll assume here that the library will always |
|
290 // use lowercase file names for the part files. |
|
291 QFile* fp; |
|
292 QString fullpath; |
|
293 |
|
294 if (search) |
|
295 { |
|
296 fp = openLDrawFile (path.toLower(), true, &fullpath); |
|
297 } |
|
298 else |
|
299 { |
|
300 fp = new QFile (path); |
|
301 fullpath = path; |
|
302 |
|
303 if (not fp->open (QIODevice::ReadOnly)) |
|
304 { |
|
305 delete fp; |
|
306 return nullptr; |
|
307 } |
|
308 } |
|
309 |
|
310 if (not fp) |
|
311 return nullptr; |
|
312 |
|
313 LDDocument* load = (fileToOverride ? fileToOverride : m_window->newDocument (implicit)); |
|
314 load->setFullPath (fullpath); |
|
315 load->setName (LDDocument::shortenName (load->fullPath())); |
|
316 |
|
317 // Loading the file shouldn't count as actual edits to the document. |
|
318 load->history()->setIgnoring (true); |
|
319 |
|
320 int numWarnings; |
|
321 Parser parser {*fp}; |
|
322 Winding winding = NoWinding; |
|
323 load->header = parser.parseHeader(winding); |
|
324 load->setWinding(winding); |
|
325 parser.parseBody(*load); |
|
326 fp->close(); |
|
327 fp->deleteLater(); |
|
328 |
|
329 if (m_loadingMainFile) |
|
330 { |
|
331 int numWarnings = 0; |
|
332 |
|
333 for (LDObject* object : load->objects()) |
|
334 { |
|
335 if (object->type() == LDObjectType::Error) |
|
336 numWarnings += 1; |
|
337 } |
|
338 |
|
339 m_window->changeDocument (load); |
|
340 print (tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings); |
|
341 } |
|
342 |
|
343 load->history()->setIgnoring (false); |
|
344 return load; |
|
345 } |
281 } |
346 |
282 |
347 void DocumentManager::addRecentFile (QString path) |
283 void DocumentManager::addRecentFile (QString path) |
348 { |
284 { |
349 QStringList recentFiles = m_config->recentFiles(); |
285 QStringList recentFiles = m_config->recentFiles(); |