| 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 } |