src/file.cpp

changeset 378
bc3f40dcfa9e
parent 377
271d1da66b7e
child 379
f5f3faac60cd
equal deleted inserted replaced
377:271d1da66b7e 378:bc3f40dcfa9e
1 /* 1 /*
2 * LDForge: LDraw parts authoring CAD 2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013 Santeri Piippo 3 * Copyright (C) 2013 Santeri Piippo
4 * 4 *
5 * This program is free software: you can redistribute it and/or modify 5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by 6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or 7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version. 8 * (at your option) any later version.
9 * 9 *
10 * This program is distributed in the hope that it will be useful, 10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details. 13 * GNU General Public License for more details.
14 * 14 *
15 * You should have received a copy of the GNU General Public License 15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */ 17 */
18 18
19 #include <QMessageBox> 19 #include <QMessageBox>
30 #include "gui.h" 30 #include "gui.h"
31 #include "history.h" 31 #include "history.h"
32 #include "dialogs.h" 32 #include "dialogs.h"
33 #include "gldraw.h" 33 #include "gldraw.h"
34 34
35 cfg( str, io_ldpath, "" ); 35 cfg (str, io_ldpath, "");
36 cfg( str, io_recentfiles, "" ); 36 cfg (str, io_recentfiles, "");
37 37
38 static bool g_loadingMainFile = false; 38 static bool g_loadingMainFile = false;
39 static const int g_MaxRecentFiles = 5; 39 static const int g_MaxRecentFiles = 5;
40 static bool g_aborted = false; 40 static bool g_aborted = false;
41 41
42 // ============================================================================= 42 // =============================================================================
43 namespace LDPaths 43 namespace LDPaths {
44 {
45 static str pathError; 44 static str pathError;
46 45
47 struct { 46 struct {
48 str LDConfigPath; 47 str LDConfigPath;
49 str partsPath, primsPath; 48 str partsPath, primsPath;
50 } pathInfo; 49 } pathInfo;
51 50
52 void initPaths() { 51 void initPaths() {
53 if( !tryConfigure( io_ldpath )) { 52 if (!tryConfigure (io_ldpath)) {
54 LDrawPathDialog dlg (false); 53 LDrawPathDialog dlg (false);
55 54
56 if( !dlg.exec () ) 55 if (!dlg.exec ())
57 exit( 0 ); 56 exit (0);
58 57
59 io_ldpath = dlg.filename(); 58 io_ldpath = dlg.filename();
60 } 59 }
61 } 60 }
62 61
63 bool tryConfigure( str path ) { 62 bool tryConfigure (str path) {
64 QDir dir; 63 QDir dir;
65 64
66 if( !dir.cd( path )) { 65 if (!dir.cd (path)) {
67 pathError = "Directory does not exist."; 66 pathError = "Directory does not exist.";
68 return false; 67 return false;
69 } 68 }
70 69
71 QStringList mustHave = { "LDConfig.ldr", "parts", "p" }; 70 QStringList mustHave = { "LDConfig.ldr", "parts", "p" };
72 QStringList contents = dir.entryList( mustHave ); 71 QStringList contents = dir.entryList (mustHave);
73 72
74 if( contents.size() != mustHave.size() ) { 73 if (contents.size() != mustHave.size()) {
75 pathError = "Not an LDraw directory! Must<br />have LDConfig.ldr, parts/ and p/."; 74 pathError = "Not an LDraw directory! Must<br />have LDConfig.ldr, parts/ and p/.";
76 return false; 75 return false;
77 } 76 }
78 77
79 pathInfo.partsPath = fmt( "%1" DIRSLASH "parts", path ); 78 pathInfo.partsPath = fmt ("%1" DIRSLASH "parts", path);
80 pathInfo.LDConfigPath = fmt( "%1" DIRSLASH "LDConfig.ldr", path ); 79 pathInfo.LDConfigPath = fmt ("%1" DIRSLASH "LDConfig.ldr", path);
81 pathInfo.primsPath = fmt( "%1" DIRSLASH "p", path ); 80 pathInfo.primsPath = fmt ("%1" DIRSLASH "p", path);
82 81
83 return true; 82 return true;
84 } 83 }
85 84
86 // Accessors 85 // Accessors
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
419 } 419 }
420 420
421 // ============================================================================= 421 // =============================================================================
422 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 422 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
423 // ============================================================================= 423 // =============================================================================
424 void closeAll() 424 void closeAll() {
425 { 425 if (!g_loadedFiles.size())
426 if( !g_loadedFiles.size() )
427 return; 426 return;
428 427
429 // Remove all loaded files and the objects they contain 428 // Remove all loaded files and the objects they contain
430 for( LDOpenFile* file : g_loadedFiles ) 429 for (LDOpenFile* file : g_loadedFiles)
431 delete file; 430 delete file;
432 431
433 // Clear the array 432 // Clear the array
434 g_loadedFiles.clear(); 433 g_loadedFiles.clear();
435 g_curfile = null; 434 g_curfile = null;
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 }

mercurial