89 str prims() { return pathInfo.primsPath; } |
88 str prims() { return pathInfo.primsPath; } |
90 str parts() { return pathInfo.partsPath; } |
89 str parts() { return pathInfo.partsPath; } |
91 } |
90 } |
92 |
91 |
93 // ============================================================================= |
92 // ============================================================================= |
94 LDOpenFile::LDOpenFile() |
93 LDOpenFile::LDOpenFile() { |
95 { |
94 setImplicit (true); |
96 setImplicit( true ); |
95 setSavePos (-1); |
97 setSavePos( -1 ); |
96 m_history.setFile (this); |
98 m_history.setFile( this ); |
97 } |
99 } |
98 |
100 |
99 // ============================================================================= |
101 // ============================================================================= |
100 LDOpenFile::~LDOpenFile() { |
102 LDOpenFile::~LDOpenFile() |
|
103 { |
|
104 // Clear everything from the model |
101 // Clear everything from the model |
105 for( LDObject* obj : m_objs ) |
102 for (LDObject* obj : m_objs) |
106 delete obj; |
103 delete obj; |
107 |
104 |
108 // Clear the cache as well |
105 // Clear the cache as well |
109 for( LDObject* obj : m_cache ) |
106 for (LDObject* obj : m_cache) |
110 delete obj; |
107 delete obj; |
111 } |
108 } |
112 |
109 |
113 // ============================================================================= |
110 // ============================================================================= |
114 LDOpenFile* findLoadedFile( str name ) |
111 LDOpenFile* findLoadedFile (str name) { |
115 { |
112 for (LDOpenFile* file : g_loadedFiles) |
116 for( LDOpenFile* file : g_loadedFiles ) |
113 if (file->name () == name) |
117 if( file->name () == name ) |
|
118 return file; |
114 return file; |
119 |
115 |
120 return null; |
116 return null; |
121 } |
117 } |
122 |
118 |
123 // ============================================================================= |
119 // ============================================================================= |
124 str dirname( str path ) { |
120 str dirname (str path) { |
125 long lastpos = path.lastIndexOf( DIRSLASH ); |
121 long lastpos = path.lastIndexOf (DIRSLASH); |
126 |
122 |
127 if( lastpos > 0 ) |
123 if (lastpos > 0) |
128 return path.left( lastpos ); |
124 return path.left (lastpos); |
129 |
125 |
130 #ifndef _WIN32 |
126 #ifndef _WIN32 |
131 if( path[0] == DIRSLASH_CHAR ) |
127 if (path[0] == DIRSLASH_CHAR) |
132 return DIRSLASH; |
128 return DIRSLASH; |
133 #endif // _WIN32 |
129 #endif // _WIN32 |
134 |
130 |
135 return ""; |
131 return ""; |
136 } |
132 } |
137 |
133 |
138 // ============================================================================= |
134 // ============================================================================= |
139 str basename (str path) { |
135 str basename (str path) { |
140 long lastpos = path.lastIndexOf( DIRSLASH ); |
136 long lastpos = path.lastIndexOf (DIRSLASH); |
141 |
137 |
142 if( lastpos != -1 ) |
138 if (lastpos != -1) |
143 return path.mid( lastpos + 1 ); |
139 return path.mid (lastpos + 1); |
144 |
140 |
145 return path; |
141 return path; |
146 } |
142 } |
147 |
143 |
148 // ============================================================================= |
144 // ============================================================================= |
149 File* openLDrawFile (str relpath, bool subdirs) { |
145 File* openLDrawFile (str relpath, bool subdirs) { |
150 print( "%1: Try to open %2\n", __func__, relpath ); |
146 print ("%1: Try to open %2\n", __func__, relpath); |
151 File* f = new File; |
147 File* f = new File; |
152 str fullPath; |
148 str fullPath; |
153 |
149 |
154 // LDraw models use Windows-style path separators. If we're not on Windows, |
150 // LDraw models use Windows-style path separators. If we're not on Windows, |
155 // replace the path separator now before opening any files. |
151 // replace the path separator now before opening any files. |
156 #ifndef WIN32 |
152 #ifndef WIN32 |
157 relpath.replace( "\\", "/" ); |
153 relpath.replace ("\\", "/"); |
158 #endif // WIN32 |
154 #endif // WIN32 |
159 |
155 |
160 if( g_curfile ) { |
156 if (g_curfile) { |
161 // First, try find the file in the current model's file path. We want a file |
157 // First, try find the file in the current model's file path. We want a file |
162 // in the immediate vicinity of the current model to override stock LDraw stuff. |
158 // in the immediate vicinity of the current model to override stock LDraw stuff. |
163 str partpath = fmt ("%1" DIRSLASH "%2", dirname (g_curfile->name ()), relpath); |
159 str partpath = fmt ("%1" DIRSLASH "%2", dirname (g_curfile->name ()), relpath); |
164 |
160 |
165 if (f->open (partpath, File::Read)) { |
161 if (f->open (partpath, File::Read)) { |
166 return f; }} |
162 return f; |
|
163 } |
|
164 } |
167 |
165 |
168 if (f->open (relpath, File::Read)) |
166 if (f->open (relpath, File::Read)) |
169 return f; |
167 return f; |
170 |
168 |
171 // Try with just the LDraw path first |
169 // Try with just the LDraw path first |
172 fullPath = fmt ("%1" DIRSLASH "%2", io_ldpath, relpath); |
170 fullPath = fmt ("%1" DIRSLASH "%2", io_ldpath, relpath); |
173 |
171 |
174 if( f->open( fullPath, File::Read )) |
172 if (f->open (fullPath, File::Read)) |
175 return f; |
173 return f; |
176 |
174 |
177 if( subdirs ) { |
175 if (subdirs) { |
178 // Look in sub-directories: parts and p |
176 // Look in sub-directories: parts and p |
179 for( auto subdir : initlist<const str>({ "parts", "p" })) { |
177 for (auto subdir : initlist<const str> ({ "parts", "p" })) { |
180 fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", |
178 fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", io_ldpath, subdir, relpath); |
181 io_ldpath, subdir, relpath); |
|
182 |
179 |
183 if (f->open (fullPath, File::Read)) |
180 if (f->open (fullPath, File::Read)) |
184 return f; }} |
181 return f; |
|
182 } |
|
183 } |
185 |
184 |
186 // Did not find the file. |
185 // Did not find the file. |
187 print( "could not find %1\n", relpath ); |
186 print ("could not find %1\n", relpath); |
188 delete f; |
187 delete f; |
189 return null; |
188 return null; |
190 } |
189 } |
191 |
190 |
192 // ============================================================================= |
191 // ============================================================================= |
193 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
192 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
194 // ============================================================================= |
193 // ============================================================================= |
195 void FileLoader::start() { |
194 void FileLoader::start() { |
196 setDone( false ); |
195 setDone (false); |
197 setProgress( 0 ); |
196 setProgress (0); |
198 setAborted( false ); |
197 setAborted (false); |
199 |
198 |
200 if( concurrent() ) { |
199 if (concurrent()) { |
201 g_aborted = false; |
200 g_aborted = false; |
202 |
201 |
203 // Show a progress dialog if we're loading the main file here and move |
202 // Show a progress dialog if we're loading the main file here and move |
204 // the actual work to a separate thread as this can be a rather intensive |
203 // the actual work to a separate thread as this can be a rather intensive |
205 // operation and if we don't respond quickly enough, the program can be |
204 // operation and if we don't respond quickly enough, the program can be |
206 // deemed inresponsive.. which is a bad thing. |
205 // deemed inresponsive.. which is a bad thing. |
207 dlg = new OpenProgressDialog (g_win); |
206 dlg = new OpenProgressDialog (g_win); |
208 dlg->setNumLines( lines().size() ); |
207 dlg->setNumLines (lines().size()); |
209 dlg->setModal( true ); |
208 dlg->setModal (true); |
210 dlg->show(); |
209 dlg->show(); |
211 |
210 |
212 // Connect the loader in so we can show updates |
211 // Connect the loader in so we can show updates |
213 connect( this, SIGNAL( workDone() ), dlg, SLOT( accept() )); |
212 connect (this, SIGNAL (workDone()), dlg, SLOT (accept())); |
214 connect( dlg, SIGNAL( rejected() ), this, SLOT( abort() )); |
213 connect (dlg, SIGNAL (rejected()), this, SLOT (abort())); |
215 } else |
214 } else |
216 dlg = null; |
215 dlg = null; |
217 |
216 |
218 work( 0 ); |
217 work (0); |
219 } |
218 } |
220 |
219 |
221 void FileLoader::work( ulong i ) { |
220 void FileLoader::work (ulong i) { |
222 if( aborted() ) { |
221 if (aborted()) { |
223 // We were flagged for abortion, so abort. |
222 // We were flagged for abortion, so abort. |
224 for( LDObject* obj : m_objs ) |
223 for (LDObject* obj : m_objs) |
225 delete obj; |
224 delete obj; |
226 |
225 |
227 m_objs.clear(); |
226 m_objs.clear(); |
228 setDone( true ); |
227 setDone (true); |
229 return; |
228 return; |
230 } |
229 } |
231 |
230 |
232 ulong max = i + 300; |
231 ulong max = i + 300; |
233 for( ; i < max && i < lines().size(); ++i ) { |
232 |
234 str line = lines()[i]; |
233 for (; i < max && i < lines().size(); ++i) { |
|
234 str line = lines() [i]; |
235 |
235 |
236 // Trim the trailing newline |
236 // Trim the trailing newline |
237 qchar c; |
237 qchar c; |
238 while(( c = line[line.length () - 1] ) == '\n' || c == '\r' ) |
238 |
239 line.chop( 1 ); |
239 while ((c = line[line.length () - 1]) == '\n' || c == '\r') |
240 |
240 line.chop (1); |
241 LDObject* obj = parseLine( line ); |
241 |
|
242 LDObject* obj = parseLine (line); |
242 |
243 |
243 // Check for parse errors and warn about tthem |
244 // Check for parse errors and warn about tthem |
244 if( obj->getType () == LDObject::Error ) { |
245 if (obj->getType () == LDObject::Error) { |
245 log( "Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDErrorObject*> (obj)->reason ); |
246 log ("Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDErrorObject*> (obj)->reason); |
246 |
247 |
247 if( m_warningsPointer ) { |
248 if (m_warningsPointer) { |
248 ( *m_warningsPointer )++; }} |
249 (*m_warningsPointer) ++; |
|
250 } |
|
251 } |
249 |
252 |
250 m_objs << obj; |
253 m_objs << obj; |
251 setProgress( i ); |
254 setProgress (i); |
252 |
255 |
253 if( concurrent() ) |
256 if (concurrent()) |
254 dlg->updateProgress( i ); |
257 dlg->updateProgress (i); |
255 } |
258 } |
256 |
259 |
257 if( i >= lines().size() - 1 ) { |
260 if (i >= lines().size() - 1) { |
258 emit workDone(); |
261 emit workDone(); |
259 setDone( true ); } |
262 setDone (true); |
260 |
263 } |
261 if( !done() ) { |
264 |
262 if( concurrent() ) |
265 if (!done()) { |
263 QMetaObject::invokeMethod( this, "work", Qt::QueuedConnection, Q_ARG( ulong, i + 1 )); |
266 if (concurrent()) |
|
267 QMetaObject::invokeMethod (this, "work", Qt::QueuedConnection, Q_ARG (ulong, i + 1)); |
264 else |
268 else |
265 work( i + 1 ); |
269 work (i + 1); |
266 } |
270 } |
267 } |
271 } |
268 |
272 |
269 void FileLoader::abort() { |
273 void FileLoader::abort() { |
270 setAborted( true ); |
274 setAborted (true); |
271 |
275 |
272 if( concurrent() ) |
276 if (concurrent()) |
273 g_aborted = true; |
277 g_aborted = true; |
274 } |
278 } |
275 |
279 |
276 // ============================================================================= |
280 // ============================================================================= |
277 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
281 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
278 // ============================================================================= |
282 // ============================================================================= |
279 vector<LDObject*> loadFileContents (File* f, ulong* numWarnings, bool* ok) { |
283 vector<LDObject*> loadFileContents (File* f, ulong* numWarnings, bool* ok) { |
280 vector<str> lines; |
284 vector<str> lines; |
281 vector<LDObject*> objs; |
285 vector<LDObject*> objs; |
282 |
286 |
283 if( numWarnings ) |
287 if (numWarnings) |
284 *numWarnings = 0; |
288 *numWarnings = 0; |
285 |
289 |
286 // Calculate the amount of lines |
290 // Calculate the amount of lines |
287 for( str line : *f ) |
291 for (str line : *f) |
288 lines << line; |
292 lines << line; |
|
293 |
289 f->rewind(); |
294 f->rewind(); |
290 |
295 |
291 FileLoader* loader = new FileLoader; |
296 FileLoader* loader = new FileLoader; |
292 loader->setWarningsPointer( numWarnings ); |
297 loader->setWarningsPointer (numWarnings); |
293 loader->setLines( lines ); |
298 loader->setLines (lines); |
294 loader->setConcurrent( g_loadingMainFile ); |
299 loader->setConcurrent (g_loadingMainFile); |
295 loader->start(); |
300 loader->start(); |
296 |
301 |
297 while( loader->done() == false ) |
302 while (loader->done() == false) |
298 qApp->processEvents(); |
303 qApp->processEvents(); |
299 |
304 |
300 // If we wanted the success value, supply that now |
305 // If we wanted the success value, supply that now |
301 if( ok ) |
306 if (ok) |
302 *ok = !loader->aborted(); |
307 *ok = !loader->aborted(); |
303 |
308 |
304 objs = loader->objs(); |
309 objs = loader->objs(); |
305 return objs; |
310 return objs; |
306 } |
311 } |
307 |
312 |
308 // ============================================================================= |
313 // ============================================================================= |
309 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
314 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
310 // ============================================================================= |
315 // ============================================================================= |
311 LDOpenFile* openDATFile( str path, bool search ) |
316 LDOpenFile* openDATFile (str path, bool search) { |
312 { |
|
313 // Convert the file name to lowercase since some parts contain uppercase |
317 // Convert the file name to lowercase since some parts contain uppercase |
314 // file names. I'll assume here that the library will always use lowercase |
318 // file names. I'll assume here that the library will always use lowercase |
315 // file names for the actual parts.. |
319 // file names for the actual parts.. |
316 File* f; |
320 File* f; |
317 if( search ) |
321 |
|
322 if (search) |
318 f = openLDrawFile (path.toLower (), true); |
323 f = openLDrawFile (path.toLower (), true); |
319 else { |
324 else { |
320 f = new File( path, File::Read ); |
325 f = new File (path, File::Read); |
321 |
326 |
322 if( !*f ) { |
327 if (!*f) { |
323 delete f; |
328 delete f; |
324 return null; }} |
329 return null; |
325 |
330 } |
326 if( !f ) |
331 } |
|
332 |
|
333 if (!f) |
327 return null; |
334 return null; |
328 |
335 |
329 LDOpenFile* oldLoad = g_curfile; |
336 LDOpenFile* oldLoad = g_curfile; |
330 LDOpenFile* load = new LDOpenFile; |
337 LDOpenFile* load = new LDOpenFile; |
331 load->setName( path ); |
338 load->setName (path); |
332 |
339 |
333 if( g_loadingMainFile ) |
340 if (g_loadingMainFile) { |
334 { |
|
335 g_curfile = load; |
341 g_curfile = load; |
336 g_win->R()->setFile( load ); |
342 g_win->R()->setFile (load); |
337 } |
343 } |
338 |
344 |
339 ulong numWarnings; |
345 ulong numWarnings; |
340 bool ok; |
346 bool ok; |
341 vector<LDObject*> objs = loadFileContents( f, &numWarnings, &ok ); |
347 vector<LDObject*> objs = loadFileContents (f, &numWarnings, &ok); |
342 |
348 |
343 if( !ok ) |
349 if (!ok) { |
344 { |
350 if (g_loadingMainFile) { |
345 if( g_loadingMainFile ) |
|
346 { |
|
347 g_curfile = oldLoad; |
351 g_curfile = oldLoad; |
348 g_win->R()->setFile( oldLoad ); |
352 g_win->R()->setFile (oldLoad); |
349 } |
353 } |
350 |
354 |
351 return null; |
355 return null; |
352 } |
356 } |
353 |
357 |
354 for( LDObject* obj : objs ) |
358 for (LDObject* obj : objs) |
355 load->addObject( obj ); |
359 load->addObject (obj); |
356 |
360 |
357 delete f; |
361 delete f; |
358 g_loadedFiles << load; |
362 g_loadedFiles << load; |
359 |
363 |
360 /* |
364 if (g_loadingMainFile) |
361 logf ("File %s parsed successfully (%lu warning%s).\n", |
365 log (QObject::tr ("File %1 parsed successfully (%2 warnings)."), path, numWarnings); |
362 qchars (path), numWarnings, plural (numWarnings)); |
|
363 */ |
|
364 |
366 |
365 return load; |
367 return load; |
366 } |
368 } |
367 |
369 |
368 // ============================================================================= |
370 // ============================================================================= |
369 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
371 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
370 // ============================================================================= |
372 // ============================================================================= |
371 bool LDOpenFile::safeToClose() |
373 bool LDOpenFile::safeToClose() { |
372 { |
|
373 typedef QMessageBox msgbox; |
374 typedef QMessageBox msgbox; |
374 setlocale( LC_ALL, "C" ); |
375 setlocale (LC_ALL, "C"); |
375 |
376 |
376 // If we have unsaved changes, warn and give the option of saving. |
377 // If we have unsaved changes, warn and give the option of saving. |
377 if( !implicit() && history().pos() != savePos() ) |
378 if (!implicit() && history().pos() != savePos()) { |
378 { |
379 str message = fmt ("There are unsaved changes to %1. Should it be saved?", |
379 str message = fmt( "There are unsaved changes to %1. Should it be saved?", |
380 (name().length() > 0) ? name() : "<anonymous>"); |
380 (name().length() > 0) ? name() : "<anonymous>"); |
381 |
381 switch( msgbox::question( g_win, "Unsaved Changes", message, |
382 int button = msgbox::question (g_win, "Unsaved Changes", message, |
382 ( msgbox::Yes | msgbox::No | msgbox::Cancel ), msgbox::Cancel )) |
383 (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); |
383 { |
384 |
|
385 switch (button) { |
384 case msgbox::Yes: |
386 case msgbox::Yes: |
385 // If we don't have a file path yet, we have to ask the user for one. |
387 // If we don't have a file path yet, we have to ask the user for one. |
386 if( name().length() == 0 ) |
388 if (name().length() == 0) { |
387 { |
389 str newpath = QFileDialog::getSaveFileName (g_win, "Save As", |
388 str newpath = QFileDialog::getSaveFileName( g_win, "Save As", |
390 g_curfile->name(), "LDraw files (*.dat *.ldr)"); |
389 g_curfile->name(), "LDraw files (*.dat *.ldr)" ); |
|
390 |
391 |
391 if( newpath.length() == 0 ) |
392 if (newpath.length() == 0) |
392 return false; |
393 return false; |
393 |
394 |
394 setName( newpath ); |
395 setName (newpath); |
395 } |
396 } |
396 |
397 |
397 if( !save () ) { |
398 if (!save ()) { |
398 message = fmt( QObject::tr( "Failed to save %1: %2\nDo you still want to close?" ), |
399 message = fmt (QObject::tr ("Failed to save %1: %2\nDo you still want to close?"), |
399 name(), strerror( errno )); |
400 name(), strerror (errno)); |
400 |
401 |
401 if( QMessageBox::critical( g_win, "Save Failure", message, |
402 if (msgbox::critical (g_win, "Save Failure", message, |
402 ( QMessageBox::Yes | QMessageBox::No ), QMessageBox::No ) == QMessageBox::No) |
403 (msgbox::Yes | msgbox::No), msgbox::No) == msgbox::No) |
403 { |
404 { |
404 return false; |
405 return false; |
405 } |
406 } |
406 } |
407 } |
407 |
|
408 break; |
408 break; |
409 |
409 |
410 case msgbox::Cancel: |
410 case msgbox::Cancel: |
411 return false; |
411 return false; |
412 |
412 |
439 } |
438 } |
440 |
439 |
441 // ============================================================================= |
440 // ============================================================================= |
442 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
441 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
443 // ============================================================================= |
442 // ============================================================================= |
444 void newFile () |
443 void newFile () { |
445 { |
|
446 closeAll(); |
444 closeAll(); |
447 |
445 |
448 // Create a new anonymous file and set it to our current |
446 // Create a new anonymous file and set it to our current |
449 LDOpenFile* f = new LDOpenFile; |
447 LDOpenFile* f = new LDOpenFile; |
450 f->setName( "" ); |
448 f->setName (""); |
451 f->setImplicit( false ); |
449 f->setImplicit (false); |
452 g_loadedFiles << f; |
450 g_loadedFiles << f; |
453 g_curfile = f; |
451 g_curfile = f; |
454 |
452 |
455 g_BBox.reset(); |
453 g_BBox.reset(); |
456 g_win->R()->setFile( f ); |
454 g_win->R()->setFile (f); |
457 g_win->fullRefresh(); |
455 g_win->fullRefresh(); |
458 g_win->updateTitle(); |
456 g_win->updateTitle(); |
459 f->history().updateActions(); |
457 f->history().updateActions(); |
460 } |
458 } |
461 |
459 |
462 // ============================================================================= |
460 // ============================================================================= |
463 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
461 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
464 // ============================================================================= |
462 // ============================================================================= |
465 void addRecentFile( str path ) |
463 void addRecentFile (str path) { |
466 { |
464 QStringList rfiles = io_recentfiles.value.split ('@'); |
467 QStringList rfiles = io_recentfiles.value.split( '@' ); |
465 int idx = rfiles.indexOf (path); |
468 |
|
469 int idx = rfiles.indexOf( path ); |
|
470 |
466 |
471 // If this file already is in the list, pop it out. |
467 // If this file already is in the list, pop it out. |
472 if( idx != -1 ) |
468 if (idx != -1) { |
473 { |
469 if (rfiles.size () == 1) |
474 if( rfiles.size () == 1 ) |
|
475 return; // only recent file - abort and do nothing |
470 return; // only recent file - abort and do nothing |
476 |
471 |
477 // Pop it out. |
472 // Pop it out. |
478 rfiles.removeAt( idx ); |
473 rfiles.removeAt (idx); |
479 } |
474 } |
480 |
475 |
481 // If there's too many recent files, drop one out. |
476 // If there's too many recent files, drop one out. |
482 while( rfiles.size() > ( g_MaxRecentFiles - 1 )) |
477 while (rfiles.size() > (g_MaxRecentFiles - 1)) |
483 rfiles.removeAt( 0 ); |
478 rfiles.removeAt (0); |
484 |
479 |
485 // Add the file |
480 // Add the file |
486 rfiles.push_back( path ); |
481 rfiles << path; |
487 |
482 |
488 // Rebuild the config string |
483 // Rebuild the config string |
489 io_recentfiles = rfiles.join( "@" ); |
484 io_recentfiles = rfiles.join ("@"); |
490 |
485 |
491 config::save(); |
486 config::save(); |
492 g_win->updateRecentFilesMenu(); |
487 g_win->updateRecentFilesMenu(); |
493 } |
488 } |
494 |
489 |
495 // ============================================================================= |
490 // ============================================================================= |
496 // ----------------------------------------------------------------------------- |
491 // ----------------------------------------------------------------------------- |
497 // Open an LDraw file and set it as the main model |
492 // Open an LDraw file and set it as the main model |
498 // ============================================================================= |
493 // ============================================================================= |
499 void openMainFile( str path ) |
494 void openMainFile (str path) { |
500 { |
|
501 g_loadingMainFile = true; |
495 g_loadingMainFile = true; |
502 closeAll(); |
496 closeAll(); |
503 |
497 |
504 LDOpenFile* file = openDATFile( path, false ); |
498 LDOpenFile* file = openDATFile (path, false); |
505 |
499 |
506 if (!file) |
500 if (!file) { |
507 { |
|
508 // Loading failed, thus drop down to a new file since we |
501 // Loading failed, thus drop down to a new file since we |
509 // closed everything prior. |
502 // closed everything prior. |
510 newFile (); |
503 newFile (); |
511 |
504 |
512 if( !g_aborted ) |
505 if (!g_aborted) { |
513 { |
|
514 // Tell the user loading failed. |
506 // Tell the user loading failed. |
515 setlocale( LC_ALL, "C" ); |
507 setlocale (LC_ALL, "C"); |
516 critical( fmt( QObject::tr( "Failed to open %1: %2" ), path, strerror( errno ))); |
508 critical (fmt (QObject::tr ("Failed to open %1: %2"), path, strerror (errno))); |
517 } |
509 } |
518 |
510 |
519 g_loadingMainFile = false; |
511 g_loadingMainFile = false; |
520 return; |
512 return; |
521 } |
513 } |
522 |
514 |
523 file->setImplicit( false ); |
515 file->setImplicit (false); |
524 g_curfile = file; |
516 g_curfile = file; |
525 |
517 |
526 // Recalculate the bounding box |
518 // Recalculate the bounding box |
527 g_BBox.calculate(); |
519 g_BBox.calculate(); |
528 |
520 |
529 // Rebuild the object tree view now. |
521 // Rebuild the object tree view now. |
530 g_win->fullRefresh(); |
522 g_win->fullRefresh(); |
531 g_win->updateTitle(); |
523 g_win->updateTitle(); |
532 g_win->R()->setFile( file ); |
524 g_win->R()->setFile (file); |
533 g_win->R()->resetAngles(); |
525 g_win->R()->resetAngles(); |
534 |
526 |
535 // Add it to the recent files list. |
527 // Add it to the recent files list. |
536 addRecentFile( path ); |
528 addRecentFile (path); |
537 g_loadingMainFile = false; |
529 g_loadingMainFile = false; |
538 } |
530 } |
539 |
531 |
540 // ============================================================================= |
532 // ============================================================================= |
541 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
533 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
542 // ============================================================================= |
534 // ============================================================================= |
543 bool LDOpenFile::save( str savepath ) |
535 bool LDOpenFile::save (str savepath) { |
544 { |
536 if (!savepath.length()) |
545 if( !savepath.length() ) |
|
546 savepath = name(); |
537 savepath = name(); |
547 |
538 |
548 File f( savepath, File::Write ); |
539 File f (savepath, File::Write); |
549 |
540 |
550 if( !f ) |
541 if (!f) |
551 return false; |
542 return false; |
552 |
543 |
553 // If the second object in the list holds the file name, update that now. |
544 // If the second object in the list holds the file name, update that now. |
554 // Only do this if the file is explicitly open. If it's saved into a directory |
545 // Only do this if the file is explicitly open. If it's saved into a directory |
555 // called "s" or "48", prepend that into the name. |
546 // called "s" or "48", prepend that into the name. |
556 LDCommentObject* fpathComment = null; |
547 LDCommentObject* fpathComment = null; |
557 LDObject* first = object( 1 ); |
548 LDObject* first = object (1); |
558 if( !implicit() && first != null && first->getType() == LDObject::Comment ) |
549 |
559 { |
550 if (!implicit() && first != null && first->getType() == LDObject::Comment) { |
560 fpathComment = static_cast<LDCommentObject*>( first ); |
551 fpathComment = static_cast<LDCommentObject*> (first); |
561 |
552 |
562 if( fpathComment->text.left( 6 ) == "Name: " ) { |
553 if (fpathComment->text.left (6) == "Name: ") { |
563 str newname; |
554 str newname; |
564 str dir = basename( dirname( savepath )); |
555 str dir = basename (dirname (savepath)); |
565 |
556 |
566 if( dir == "s" || dir == "48" ) |
557 if (dir == "s" || dir == "48") |
567 newname = dir + "\\"; |
558 newname = dir + "\\"; |
568 |
559 |
569 newname += basename( savepath ); |
560 newname += basename (savepath); |
570 fpathComment->text = fmt( "Name: %1", newname ); |
561 fpathComment->text = fmt ("Name: %1", newname); |
571 g_win->buildObjList(); |
562 g_win->buildObjList(); |
572 } |
563 } |
573 } |
564 } |
574 |
565 |
575 // File is open, now save the model to it. Note that LDraw requires files to |
566 // File is open, now save the model to it. Note that LDraw requires files to |
576 // have DOS line endings, so we terminate the lines with \r\n. |
567 // have DOS line endings, so we terminate the lines with \r\n. |
577 for( LDObject* obj : objs() ) |
568 for (LDObject* obj : objs()) |
578 f.write( obj->raw () + "\r\n" ); |
569 f.write (obj->raw () + "\r\n"); |
579 |
570 |
580 // File is saved, now clean up. |
571 // File is saved, now clean up. |
581 f.close(); |
572 f.close(); |
582 |
573 |
583 // We have successfully saved, update the save position now. |
574 // We have successfully saved, update the save position now. |
584 setSavePos( history().pos() ); |
575 setSavePos (history().pos()); |
585 setName( savepath ); |
576 setName (savepath); |
586 |
577 |
587 g_win->updateTitle(); |
578 g_win->updateTitle(); |
588 return true; |
579 return true; |
589 } |
580 } |
590 |
581 |
597 |
588 |
598 #define CHECK_TOKEN_NUMBERS( MIN, MAX ) \ |
589 #define CHECK_TOKEN_NUMBERS( MIN, MAX ) \ |
599 for (ushort i = MIN; i <= MAX; ++i) \ |
590 for (ushort i = MIN; i <= MAX; ++i) \ |
600 if (!isNumber (tokens[i])) \ |
591 if (!isNumber (tokens[i])) \ |
601 return new LDErrorObject (line, fmt ("Token #%1 was `%2`, expected a number", \ |
592 return new LDErrorObject (line, fmt ("Token #%1 was `%2`, expected a number", \ |
602 (i + 1), tokens[i])); |
593 (i + 1), tokens[i])); |
603 |
594 |
604 static vertex parseVertex( QStringList& s, const ushort n ) |
595 static vertex parseVertex (QStringList& s, const ushort n) { |
605 { |
|
606 vertex v; |
596 vertex v; |
607 for( const Axis ax : g_Axes ) |
597 |
608 v[ax] = atof( s[n + ax] ); |
598 for (const Axis ax : g_Axes) |
|
599 v[ax] = atof (s[n + ax]); |
609 |
600 |
610 return v; |
601 return v; |
611 } |
602 } |
612 |
603 |
613 // ============================================================================= |
604 // ============================================================================= |
614 // ----------------------------------------------------------------------------- |
605 // ----------------------------------------------------------------------------- |
615 // This is the LDraw code parser function. It takes in a string containing LDraw |
606 // This is the LDraw code parser function. It takes in a string containing LDraw |
616 // code and returns the object parsed from it. parseLine never returns null, |
607 // code and returns the object parsed from it. parseLine never returns null, |
617 // the object will be LDError if it could not be parsed properly. |
608 // the object will be LDError if it could not be parsed properly. |
618 // ============================================================================= |
609 // ============================================================================= |
619 LDObject* parseLine( str line ) |
610 LDObject* parseLine (str line) { |
620 { |
611 QStringList tokens = line.split (" ", str::SkipEmptyParts); |
621 QStringList tokens = line.split( " ", str::SkipEmptyParts ); |
612 |
622 |
613 if (tokens.size() <= 0) { |
623 if( tokens.size() <= 0 ) |
|
624 { |
|
625 // Line was empty, or only consisted of whitespace |
614 // Line was empty, or only consisted of whitespace |
626 return new LDEmptyObject; |
615 return new LDEmptyObject; |
627 } |
616 } |
628 |
617 |
629 if( tokens[0].length() != 1 || tokens[0][0].isDigit() == false ) |
618 if (tokens[0].length() != 1 || tokens[0][0].isDigit() == false) |
630 return new LDErrorObject( line, "Illogical line code" ); |
619 return new LDErrorObject (line, "Illogical line code"); |
631 |
620 |
632 int num = tokens[0][0].digitValue(); |
621 int num = tokens[0][0].digitValue(); |
633 switch (num) |
622 |
634 { |
623 switch (num) { |
635 case 0: |
624 case 0: { |
636 { |
625 // Comment |
637 // Comment |
626 str comm = line.mid (line.indexOf ("0") + 1); |
638 str comm = line.mid( line.indexOf( "0" ) + 1 ); |
627 |
|
628 // Remove any leading whitespace |
|
629 while (comm[0] == ' ') |
|
630 comm.remove (0, 1); |
|
631 |
|
632 // Handle BFC statements |
|
633 if (tokens.size() > 2 && tokens[1] == "BFC") { |
|
634 for (short i = 0; i < LDBFCObject::NumStatements; ++i) |
|
635 if (comm == fmt ("BFC %1", LDBFCObject::statements [i])) |
|
636 return new LDBFCObject ( (LDBFCObject::Type) i); |
639 |
637 |
640 // Remove any leading whitespace |
638 // MLCAD is notorious for stuffing these statements in parts it |
641 while( comm[0] == ' ' ) |
639 // creates. The above block only handles valid statements, so we |
642 comm.remove( 0, 1 ); |
640 // need to handle MLCAD-style invertnext separately. |
643 |
641 if (comm == "BFC CERTIFY INVERTNEXT") |
644 // Handle BFC statements |
642 return new LDBFCObject (LDBFCObject::InvertNext); |
645 if (tokens.size() > 2 && tokens[1] == "BFC") { |
643 } |
646 for (short i = 0; i < LDBFCObject::NumStatements; ++i) |
644 |
647 if (comm == fmt ("BFC %1", LDBFCObject::statements [i])) |
645 if (tokens.size() > 2 && tokens[1] == "!LDFORGE") { |
648 return new LDBFCObject ((LDBFCObject::Type) i); |
646 // Handle LDForge-specific types, they're embedded into comments too |
|
647 if (tokens[2] == "VERTEX") { |
|
648 // Vertex (0 !LDFORGE VERTEX) |
|
649 CHECK_TOKEN_COUNT (7) |
|
650 CHECK_TOKEN_NUMBERS (3, 6) |
649 |
651 |
650 // MLCAD is notorious for stuffing these statements in parts it |
652 LDVertexObject* obj = new LDVertexObject; |
651 // creates. The above block only handles valid statements, so we |
653 obj->setColor (tokens[3].toLong ()); |
652 // need to handle MLCAD-style invertnext separately. |
654 |
653 if (comm == "BFC CERTIFY INVERTNEXT") |
655 for (const Axis ax : g_Axes) |
654 return new LDBFCObject (LDBFCObject::InvertNext); |
656 obj->pos[ax] = tokens[4 + ax].toDouble (); // 4 - 6 |
|
657 |
|
658 return obj; |
|
659 } elif (tokens[2] == "OVERLAY") { |
|
660 CHECK_TOKEN_COUNT (9); |
|
661 CHECK_TOKEN_NUMBERS (5, 8) |
|
662 |
|
663 LDOverlayObject* obj = new LDOverlayObject; |
|
664 obj->setFilename (tokens[3]); |
|
665 obj->setCamera (tokens[4].toLong()); |
|
666 obj->setX (tokens[5].toLong()); |
|
667 obj->setY (tokens[6].toLong()); |
|
668 obj->setWidth (tokens[7].toLong()); |
|
669 obj->setHeight (tokens[8].toLong()); |
|
670 return obj; |
655 } |
671 } |
656 |
672 } |
657 if (tokens.size() > 2 && tokens[1] == "!LDFORGE") |
673 |
658 { |
674 // Just a regular comment: |
659 // Handle LDForge-specific types, they're embedded into comments too |
675 LDCommentObject* obj = new LDCommentObject; |
660 if (tokens[2] == "VERTEX") { |
676 obj->text = comm; |
661 // Vertex (0 !LDFORGE VERTEX) |
677 return obj; |
662 CHECK_TOKEN_COUNT (7) |
678 } |
663 CHECK_TOKEN_NUMBERS (3, 6) |
679 |
664 |
680 case 1: { |
665 LDVertexObject* obj = new LDVertexObject; |
681 // Subfile |
666 obj->setColor (tokens[3].toLong ()); |
682 CHECK_TOKEN_COUNT (15) |
667 |
683 CHECK_TOKEN_NUMBERS (1, 13) |
668 for (const Axis ax : g_Axes) |
684 |
669 obj->pos[ax] = tokens[4 + ax].toDouble (); // 4 - 6 |
685 // Try open the file. Disable g_loadingMainFile temporarily since we're |
670 |
686 // not loading the main file now, but the subfile in question. |
671 return obj; |
687 bool tmp = g_loadingMainFile; |
672 } elif (tokens[2] == "OVERLAY") { |
688 g_loadingMainFile = false; |
673 CHECK_TOKEN_COUNT (9); |
689 LDOpenFile* load = getFile (tokens[14]); |
674 CHECK_TOKEN_NUMBERS (5, 8) |
690 g_loadingMainFile = tmp; |
675 |
691 |
676 LDOverlayObject* obj = new LDOverlayObject; |
692 // If we cannot open the file, mark it an error |
677 obj->setFilename( tokens[3] ); |
693 if (!load) |
678 obj->setCamera( tokens[4].toLong() ); |
694 return new LDErrorObject (line, "Could not open referred file"); |
679 obj->setX( tokens[5].toLong() ); |
695 |
680 obj->setY( tokens[6].toLong() ); |
696 LDSubfileObject* obj = new LDSubfileObject; |
681 obj->setWidth( tokens[7].toLong() ); |
697 obj->setColor (tokens[1].toLong()); |
682 obj->setHeight( tokens[8].toLong() ); |
698 obj->setPosition (parseVertex (tokens, 2)); // 2 - 4 |
683 return obj; |
699 |
684 } |
700 matrix transform; |
685 } |
701 |
686 |
702 for (short i = 0; i < 9; ++i) |
687 // Just a regular comment: |
703 transform[i] = tokens[i + 5].toDouble(); // 5 - 13 |
688 LDCommentObject* obj = new LDCommentObject; |
704 |
689 obj->text = comm; |
705 obj->setTransform (transform); |
690 return obj; |
706 obj->setFileInfo (load); |
691 } |
707 return obj; |
692 |
708 } |
693 case 1: |
709 |
694 { |
710 case 2: { |
695 // Subfile |
711 CHECK_TOKEN_COUNT (8) |
696 CHECK_TOKEN_COUNT( 15 ) |
712 CHECK_TOKEN_NUMBERS (1, 7) |
697 CHECK_TOKEN_NUMBERS( 1, 13 ) |
713 |
698 |
714 // Line |
699 // Try open the file. Disable g_loadingMainFile temporarily since we're |
715 LDLineObject* obj = new LDLineObject; |
700 // not loading the main file now, but the subfile in question. |
716 obj->setColor (tokens[1].toLong()); |
701 bool tmp = g_loadingMainFile; |
717 |
702 g_loadingMainFile = false; |
718 for (short i = 0; i < 2; ++i) |
703 LDOpenFile* load = getFile( tokens[14] ); |
719 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 7 |
704 g_loadingMainFile = tmp; |
720 |
705 |
721 return obj; |
706 // If we cannot open the file, mark it an error |
722 } |
707 if( !load ) |
723 |
708 return new LDErrorObject( line, "Could not open referred file" ); |
724 case 3: { |
709 |
725 CHECK_TOKEN_COUNT (11) |
710 LDSubfileObject* obj = new LDSubfileObject; |
726 CHECK_TOKEN_NUMBERS (1, 10) |
711 obj->setColor( tokens[1].toLong() ); |
727 |
712 obj->setPosition( parseVertex( tokens, 2 )); // 2 - 4 |
728 // Triangle |
713 |
729 LDTriangleObject* obj = new LDTriangleObject; |
714 matrix transform; |
730 obj->setColor (tokens[1].toLong()); |
715 for( short i = 0; i < 9; ++i ) |
731 |
716 transform[i] = tokens[i + 5].toDouble(); // 5 - 13 |
732 for (short i = 0; i < 3; ++i) |
717 |
733 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 10 |
718 obj->setTransform( transform ); |
734 |
719 obj->setFileInfo( load ); |
735 return obj; |
720 return obj; |
736 } |
721 } |
|
722 |
|
723 case 2: |
|
724 { |
|
725 CHECK_TOKEN_COUNT( 8 ) |
|
726 CHECK_TOKEN_NUMBERS( 1, 7 ) |
|
727 |
|
728 // Line |
|
729 LDLineObject* obj = new LDLineObject; |
|
730 obj->setColor( tokens[1].toLong() ); |
|
731 for( short i = 0; i < 2; ++i ) |
|
732 obj->setVertex( i, parseVertex( tokens, 2 + ( i * 3 ))); // 2 - 7 |
|
733 return obj; |
|
734 } |
|
735 |
|
736 case 3: |
|
737 { |
|
738 CHECK_TOKEN_COUNT( 11 ) |
|
739 CHECK_TOKEN_NUMBERS( 1, 10 ) |
|
740 |
|
741 // Triangle |
|
742 LDTriangleObject* obj = new LDTriangleObject; |
|
743 obj->setColor( tokens[1].toLong() ); |
|
744 |
|
745 for( short i = 0; i < 3; ++i ) |
|
746 obj->setVertex( i, parseVertex( tokens, 2 + ( i * 3 ))); // 2 - 10 |
|
747 |
|
748 return obj; |
|
749 } |
|
750 |
737 |
751 case 4: |
738 case 4: |
752 case 5: |
739 case 5: { |
753 { |
740 CHECK_TOKEN_COUNT (14) |
754 CHECK_TOKEN_COUNT (14) |
741 CHECK_TOKEN_NUMBERS (1, 13) |
755 CHECK_TOKEN_NUMBERS (1, 13) |
742 |
756 |
743 // Quadrilateral / Conditional line |
757 // Quadrilateral / Conditional line |
744 LDObject* obj = (num == 4) ? ( (LDObject*) new LDQuadObject) : ( (LDObject*) new LDCondLineObject); |
758 LDObject* obj = (num == 4) ? ((LDObject*) new LDQuadObject ) : ((LDObject*) new LDCondLineObject); |
745 obj->setColor (tokens[1].toLong()); |
759 obj->setColor( tokens[1].toLong() ); |
746 |
760 |
747 for (short i = 0; i < 4; ++i) |
761 for (short i = 0; i < 4; ++i) |
748 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 13 |
762 obj->setVertex( i, parseVertex( tokens, 2 + ( i * 3 ))); // 2 - 13 |
749 |
763 |
750 return obj; |
764 return obj; |
751 } |
765 } |
|
766 |
752 |
767 default: // Strange line we couldn't parse |
753 default: // Strange line we couldn't parse |
768 return new LDErrorObject (line, "Unknown line code number"); |
754 return new LDErrorObject (line, "Unknown line code number"); |
769 } |
755 } |
770 } |
756 } |
771 |
757 |
772 // ============================================================================= |
758 // ============================================================================= |
773 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
759 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
774 // ============================================================================= |
760 // ============================================================================= |
775 LDOpenFile* getFile( str filename ) |
761 LDOpenFile* getFile (str filename) { |
776 { |
|
777 // Try find the file in the list of loaded files |
762 // Try find the file in the list of loaded files |
778 LDOpenFile* load = findLoadedFile( filename ); |
763 LDOpenFile* load = findLoadedFile (filename); |
779 |
764 |
780 // If it's not loaded, try open it |
765 // If it's not loaded, try open it |
781 if( !load ) |
766 if (!load) |
782 load = openDATFile( filename, true ); |
767 load = openDATFile (filename, true); |
783 |
768 |
784 return load; |
769 return load; |
785 } |
770 } |
786 |
771 |
787 // ============================================================================= |
772 // ============================================================================= |
788 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
773 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
789 // ============================================================================= |
774 // ============================================================================= |
790 void reloadAllSubfiles () { |
775 void reloadAllSubfiles () { |
791 if( !g_curfile ) |
776 if (!g_curfile) |
792 return; |
777 return; |
793 |
778 |
794 g_loadedFiles.clear(); |
779 g_loadedFiles.clear(); |
795 g_loadedFiles << g_curfile; |
780 g_loadedFiles << g_curfile; |
796 |
781 |
797 // Go through all objects in the current file and reload the subfiles |
782 // Go through all objects in the current file and reload the subfiles |
798 for( LDObject* obj : g_curfile->objs() ) |
783 for (LDObject* obj : g_curfile->objs()) { |
799 { |
784 if (obj->getType() == LDObject::Subfile) { |
800 if( obj->getType() == LDObject::Subfile ) |
785 LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj); |
801 { |
786 LDOpenFile* fileInfo = getFile (ref->fileInfo()->name()); |
802 LDSubfileObject* ref = static_cast<LDSubfileObject*>( obj ); |
|
803 LDOpenFile* fileInfo = getFile( ref->fileInfo()->name() ); |
|
804 |
787 |
805 if (fileInfo) |
788 if (fileInfo) |
806 ref->setFileInfo( fileInfo ); |
789 ref->setFileInfo (fileInfo); |
807 else |
790 else |
808 ref->replace( new LDErrorObject( ref->raw(), "Could not open referred file" )); |
791 ref->replace (new LDErrorObject (ref->raw(), "Could not open referred file")); |
809 } |
792 } |
810 |
793 |
811 // Reparse gibberish files. It could be that they are invalid because |
794 // Reparse gibberish files. It could be that they are invalid because |
812 // of loading errors. Circumstances may be different now. |
795 // of loading errors. Circumstances may be different now. |
813 if( obj->getType() == LDObject::Error ) |
796 if (obj->getType() == LDObject::Error) |
814 obj->replace( parseLine( static_cast<LDErrorObject*>( obj )->contents )); |
797 obj->replace (parseLine (static_cast<LDErrorObject*> (obj)->contents)); |
815 } |
798 } |
816 |
799 |
817 // Close all files left unused |
800 // Close all files left unused |
818 LDOpenFile::closeUnused(); |
801 LDOpenFile::closeUnused(); |
819 } |
802 } |
820 |
803 |
821 // ============================================================================= |
804 // ============================================================================= |
822 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
805 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
823 // ============================================================================= |
806 // ============================================================================= |
824 ulong LDOpenFile::addObject (LDObject* obj) |
807 ulong LDOpenFile::addObject (LDObject* obj) { |
825 { |
808 PROP_NAME (history).add (new AddHistory (PROP_NAME (objs).size(), obj)); |
826 PROP_NAME( history ).add( new AddHistory( PROP_NAME( objs ).size(), obj )); |
809 PROP_NAME (objs) << obj; |
827 PROP_NAME( objs ) << obj; |
810 |
828 |
811 if (obj->getType() == LDObject::Vertex) |
829 if( obj->getType() == LDObject::Vertex ) |
812 PROP_NAME (vertices) << obj; |
830 PROP_NAME( vertices ) << obj; |
813 |
831 |
814 if (this == g_curfile) |
832 if( this == g_curfile ) |
815 g_BBox.calcObject (obj); |
833 g_BBox.calcObject( obj ); |
|
834 |
816 |
835 return numObjs() - 1; |
817 return numObjs() - 1; |
836 } |
818 } |
837 |
819 |
838 // ============================================================================= |
820 // ============================================================================= |
839 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
821 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
840 // ============================================================================= |
822 // ============================================================================= |
841 void LDOpenFile::insertObj (const ulong pos, LDObject* obj) { |
823 void LDOpenFile::insertObj (const ulong pos, LDObject* obj) { |
842 m_history.add( new AddHistory( pos, obj )); |
824 m_history.add (new AddHistory (pos, obj)); |
843 m_objs.insert( pos, obj ); |
825 m_objs.insert (pos, obj); |
844 |
826 |
845 if( this == g_curfile ) |
827 if (this == g_curfile) |
846 g_BBox.calcObject( obj ); |
828 g_BBox.calcObject (obj); |
847 } |
829 } |
848 |
830 |
849 // ============================================================================= |
831 // ============================================================================= |
850 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
832 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
851 // ============================================================================= |
833 // ============================================================================= |
852 void LDOpenFile::forgetObject( LDObject* obj ) { |
834 void LDOpenFile::forgetObject (LDObject* obj) { |
853 ulong idx = obj->getIndex( this ); |
835 ulong idx = obj->getIndex (this); |
854 m_history.add( new DelHistory( idx, obj )); |
836 m_history.add (new DelHistory (idx, obj)); |
855 m_objs.erase( idx ); |
837 m_objs.erase (idx); |
856 |
838 |
857 // Update the bounding box |
839 // Update the bounding box |
858 if( this == g_curfile ) |
840 if (this == g_curfile) |
859 g_BBox.calculate (); |
841 g_BBox.calculate (); |
860 } |
842 } |
861 |
843 |
862 // ============================================================================= |
844 // ============================================================================= |
863 bool safeToCloseAll () { |
845 bool safeToCloseAll () { |
864 for( LDOpenFile* f : g_loadedFiles ) |
846 for (LDOpenFile* f : g_loadedFiles) |
865 if( !f->safeToClose() ) |
847 if (!f->safeToClose()) |
866 return false; |
848 return false; |
867 |
849 |
868 return true; |
850 return true; |
869 } |
851 } |
870 |
852 |
871 // ============================================================================= |
853 // ============================================================================= |
872 void LDOpenFile::setObject (ulong idx, LDObject* obj) { |
854 void LDOpenFile::setObject (ulong idx, LDObject* obj) { |
873 assert( idx < numObjs() ); |
855 assert (idx < numObjs()); |
874 |
856 |
875 // Mark this change to history |
857 // Mark this change to history |
876 str oldcode = object( idx )->raw (); |
858 str oldcode = object (idx)->raw (); |
877 str newcode = obj->raw(); |
859 str newcode = obj->raw(); |
878 m_history << new EditHistory( idx, oldcode, newcode ); |
860 m_history << new EditHistory (idx, oldcode, newcode); |
879 |
861 |
880 m_objs[idx] = obj; |
862 m_objs[idx] = obj; |
881 } |
863 } |
882 |
864 |
883 static vector<LDOpenFile*> getFilesUsed (LDOpenFile* node) { |
865 static vector<LDOpenFile*> getFilesUsed (LDOpenFile* node) { |
884 vector<LDOpenFile*> filesUsed; |
866 vector<LDOpenFile*> filesUsed; |
885 |
867 |
886 for (LDObject* obj : *node) |
868 for (LDObject* obj : *node) { |
887 { |
869 if (obj->getType() != LDObject::Subfile) |
888 if( obj->getType() != LDObject::Subfile ) |
|
889 continue; |
870 continue; |
890 |
871 |
891 LDSubfileObject* ref = static_cast<LDSubfileObject*>( obj ); |
872 LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj); |
892 filesUsed << ref->fileInfo(); |
873 filesUsed << ref->fileInfo(); |
893 filesUsed << getFilesUsed( ref->fileInfo() ); |
874 filesUsed << getFilesUsed (ref->fileInfo()); |
894 } |
875 } |
895 |
876 |
896 return filesUsed; |
877 return filesUsed; |
897 } |
878 } |
898 |
879 |
899 // ============================================================================= |
880 // ============================================================================= |
900 // Find out which files are unused and close them. |
881 // Find out which files are unused and close them. |
901 void LDOpenFile::closeUnused () |
882 void LDOpenFile::closeUnused () { |
902 { |
|
903 vector<LDOpenFile*> filesUsed = getFilesUsed (g_curfile); |
883 vector<LDOpenFile*> filesUsed = getFilesUsed (g_curfile); |
904 |
884 |
905 // Anything that's explicitly opened must not be closed |
885 // Anything that's explicitly opened must not be closed |
906 for( LDOpenFile* file : g_loadedFiles ) |
886 for (LDOpenFile* file : g_loadedFiles) |
907 if( !file->implicit() ) |
887 if (!file->implicit()) |
908 filesUsed << file; |
888 filesUsed << file; |
909 |
889 |
910 // Remove duplicated entries |
890 // Remove duplicated entries |
911 filesUsed.makeUnique(); |
891 filesUsed.makeUnique(); |
912 |
892 |
913 // Close all open files that aren't in filesUsed |
893 // Close all open files that aren't in filesUsed |
914 for( LDOpenFile* file : g_loadedFiles ) |
894 for (LDOpenFile* file : g_loadedFiles) { |
915 { |
|
916 bool isused = false; |
895 bool isused = false; |
917 |
896 |
918 for( LDOpenFile* usedFile : filesUsed ) |
897 for (LDOpenFile* usedFile : filesUsed) { |
919 { |
898 if (file == usedFile) { |
920 if( file == usedFile ) |
|
921 { |
|
922 isused = true; |
899 isused = true; |
923 break; |
900 break; |
924 } |
901 } |
925 } |
902 } |
926 |
903 |
927 if( !isused ) |
904 if (!isused) |
928 delete file; |
905 delete file; |
929 } |
906 } |
930 |
907 |
931 g_loadedFiles.clear(); |
908 g_loadedFiles.clear(); |
932 g_loadedFiles << filesUsed; |
909 g_loadedFiles << filesUsed; |
933 } |
910 } |
934 |
911 |
935 LDObject* LDOpenFile::object( ulong pos ) const |
912 LDObject* LDOpenFile::object (ulong pos) const { |
936 { |
913 if (m_objs.size() <= pos) |
937 if( m_objs.size() <= pos ) |
|
938 return null; |
914 return null; |
939 |
915 |
940 return m_objs[pos]; |
916 return m_objs[pos]; |
941 } |
917 } |
942 |
918 |
943 LDOpenFile& LDOpenFile::operator<<( vector<LDObject*> objs ) |
919 LDOpenFile& LDOpenFile::operator<< (vector<LDObject*> objs) { |
944 { |
920 for (LDObject* obj : objs) |
945 for( LDObject* obj : objs ) |
|
946 m_objs << obj; |
921 m_objs << obj; |
947 |
922 |
948 return *this; |
923 return *this; |
949 } |
924 } |