43 // ============================================================================= |
42 // ============================================================================= |
44 namespace LDPaths |
43 namespace LDPaths |
45 { |
44 { |
46 static str pathError; |
45 static str pathError; |
47 |
46 |
48 struct |
47 struct { |
49 { |
|
50 str LDConfigPath; |
48 str LDConfigPath; |
51 str partsPath, primsPath; |
49 str partsPath, primsPath; |
52 } pathInfo; |
50 } pathInfo; |
53 |
51 |
54 void initPaths () |
52 void initPaths() { |
55 { |
53 if( !tryConfigure( io_ldpath )) { |
56 if( !tryConfigure ( io_ldpath )) |
|
57 { |
|
58 LDrawPathDialog dlg (false); |
54 LDrawPathDialog dlg (false); |
59 |
55 |
60 if( !dlg.exec () ) |
56 if( !dlg.exec () ) |
61 exit( 0 ); |
57 exit( 0 ); |
62 |
58 |
63 io_ldpath = dlg.filename(); |
59 io_ldpath = dlg.filename(); |
64 } |
60 } |
65 } |
61 } |
66 |
62 |
67 bool tryConfigure( str path ) |
63 bool tryConfigure( str path ) { |
68 { |
|
69 QDir dir; |
64 QDir dir; |
70 |
65 |
71 if( !dir.cd( path )) |
66 if( !dir.cd( path )) { |
72 { |
|
73 pathError = "Directory does not exist."; |
67 pathError = "Directory does not exist."; |
74 return false; |
68 return false; |
75 } |
69 } |
76 |
70 |
77 QStringList mustHave = { "LDConfig.ldr", "parts", "p" }; |
71 QStringList mustHave = { "LDConfig.ldr", "parts", "p" }; |
78 QStringList contents = dir.entryList( mustHave ); |
72 QStringList contents = dir.entryList( mustHave ); |
79 |
73 |
80 if (contents.size () != mustHave.size ()) { |
74 if( contents.size() != mustHave.size() ) { |
81 pathError = "Not an LDraw directory! Must<br />have LDConfig.ldr, parts/ and p/."; |
75 pathError = "Not an LDraw directory! Must<br />have LDConfig.ldr, parts/ and p/."; |
82 return false; |
76 return false; |
83 } |
77 } |
84 |
78 |
85 pathInfo.partsPath = fmt( "%1" DIRSLASH "parts", path ); |
79 pathInfo.partsPath = fmt( "%1" DIRSLASH "parts", path ); |
153 |
147 |
154 // ============================================================================= |
148 // ============================================================================= |
155 File* openLDrawFile (str relpath, bool subdirs) { |
149 File* openLDrawFile (str relpath, bool subdirs) { |
156 print( "%1: Try to open %2\n", __func__, relpath ); |
150 print( "%1: Try to open %2\n", __func__, relpath ); |
157 File* f = new File; |
151 File* f = new File; |
|
152 str fullPath; |
158 |
153 |
159 // LDraw models use Windows-style path separators. If we're not on Windows, |
154 // LDraw models use Windows-style path separators. If we're not on Windows, |
160 // replace the path separator now before opening any files. |
155 // replace the path separator now before opening any files. |
161 #ifndef WIN32 |
156 #ifndef WIN32 |
162 relpath.replace( "\\", "/" ); |
157 relpath.replace( "\\", "/" ); |
163 #endif // WIN32 |
158 #endif // WIN32 |
164 |
159 |
165 if( g_curfile ) |
160 if( g_curfile ) { |
166 { |
|
167 // First, try find the file in the current model's file path. We want a file |
161 // First, try find the file in the current model's file path. We want a file |
168 // in the immediate vicinity of the current model to override stock LDraw stuff. |
162 // in the immediate vicinity of the current model to override stock LDraw stuff. |
169 str partpath = fmt ("%1" DIRSLASH "%2", dirname (g_curfile->name ()), relpath); |
163 str partpath = fmt ("%1" DIRSLASH "%2", dirname (g_curfile->name ()), relpath); |
170 |
164 |
171 if (f->open (partpath, File::Read)) |
165 if (f->open (partpath, File::Read)) { |
172 return f; |
166 return f; }} |
173 } |
|
174 |
167 |
175 if (f->open (relpath, File::Read)) |
168 if (f->open (relpath, File::Read)) |
176 return f; |
169 return f; |
177 |
170 |
178 str fullPath; |
171 // Try with just the LDraw path first |
179 if( io_ldpath.value.length() > 0 ) |
172 fullPath = fmt ("%1" DIRSLASH "%2", io_ldpath, relpath); |
180 { |
173 |
181 // Try with just the LDraw path first |
174 if( f->open( fullPath, File::Read )) |
182 fullPath = fmt ("%1" DIRSLASH "%2", io_ldpath, relpath); |
175 return f; |
183 |
176 |
184 if( f->open( fullPath, File::Read )) |
177 if( subdirs ) { |
185 return f; |
178 // Look in sub-directories: parts and p |
186 |
179 for( auto subdir : initlist<const str>({ "parts", "p" })) { |
187 if( subdirs ) |
180 fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", |
188 { |
181 io_ldpath, subdir, relpath); |
189 // Look in sub-directories: parts and p |
182 |
190 for( auto subdir : initlist<const str>({ "parts", "p" })) |
183 if (f->open (fullPath, File::Read)) |
191 { |
184 return f; }} |
192 fullPath = fmt ("%1" DIRSLASH "%2" DIRSLASH "%3", |
|
193 io_ldpath, subdir, relpath); |
|
194 |
|
195 if (f->open (fullPath, File::Read)) |
|
196 return f; |
|
197 } |
|
198 } |
|
199 } |
|
200 |
185 |
201 // Did not find the file. |
186 // Did not find the file. |
|
187 print( "could not find %1\n", relpath ); |
202 delete f; |
188 delete f; |
203 return null; |
189 return null; |
204 } |
190 } |
205 |
191 |
206 // ============================================================================= |
192 // ============================================================================= |
207 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
193 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
208 // ============================================================================= |
194 // ============================================================================= |
209 void FileLoader::work() |
195 void FileLoader::start() { |
210 { |
|
211 setDone( false ); |
196 setDone( false ); |
212 setProgress( 0 ); |
197 setProgress( 0 ); |
213 abortflag = false; |
198 abortflag = false; |
214 |
199 |
215 for( str line : *PROP_NAME( file )) { |
200 if( concurrent() ) { |
|
201 // Show a progress dialog if we're loading the main file here and move |
|
202 // the actual work to a separate thread as this can be a rather intensive |
|
203 // operation and if we don't respond quickly enough, the program can be |
|
204 // deemed inresponsive.. which is a bad thing. |
|
205 dlg = new OpenProgressDialog (g_win); |
|
206 dlg->setNumLines( lines().size() ); |
|
207 dlg->setModal( true ); |
|
208 dlg->show(); |
|
209 |
|
210 // Connect the loader in so we can show updates |
|
211 connect( this, SIGNAL( workDone() ), dlg, SLOT( accept() )); } |
|
212 else |
|
213 dlg = null; |
|
214 |
|
215 work( 0 ); |
|
216 } |
|
217 |
|
218 void FileLoader::work( ulong i ) { |
|
219 print( "%1: %2\n", this, i ); |
|
220 |
|
221 if( abortflag ) { |
|
222 // We were flagged for abortion, so abort. |
|
223 for( LDObject* obj : m_objs ) |
|
224 delete obj; |
|
225 |
|
226 m_objs.clear(); |
|
227 abortflag = false; |
|
228 return; |
|
229 } |
|
230 |
|
231 ulong max = i + 300; |
|
232 for( ; i < max && i < lines().size(); ++i ) { |
|
233 str line = lines()[i]; |
|
234 |
216 // Trim the trailing newline |
235 // Trim the trailing newline |
217 qchar c; |
236 qchar c; |
218 while(( c = line[line.length () - 1] ) == '\n' || c == '\r' ) |
237 while(( c = line[line.length () - 1] ) == '\n' || c == '\r' ) |
219 line.chop( 1 ); |
238 line.chop( 1 ); |
220 |
239 |
221 LDObject* obj = parseLine( line ); |
240 LDObject* obj = parseLine( line ); |
222 |
241 |
223 // Check for parse errors and warn about tthem |
242 // Check for parse errors and warn about tthem |
224 if( obj->getType () == LDObject::Gibberish ) |
243 if( obj->getType () == LDObject::Gibberish ) { |
225 { |
|
226 log( "Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDGibberish*> (obj)->reason ); |
244 log( "Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDGibberish*> (obj)->reason ); |
227 log( "- Line was: %1", line ); |
245 |
228 |
246 if( m_warningsPointer ) { |
229 if( m_warningsPointer ) |
247 ( *m_warningsPointer )++; }} |
230 ( *m_warningsPointer )++; |
|
231 } |
|
232 |
248 |
233 m_objs << obj; |
249 m_objs << obj; |
234 m_progress++; |
250 setProgress( i ); |
235 emit progressUpdate( m_progress ); |
251 |
236 |
252 if( concurrent() ) |
237 if( abortflag ) |
253 dlg->updateProgress( i ); |
238 { |
254 } |
239 // We were flagged for abortion, so abort. |
255 |
240 for( LDObject* obj : m_objs ) |
256 if( i >= lines().size() - 1 ) { |
241 delete obj; |
257 emit workDone(); |
242 |
258 setDone( true ); } |
243 m_objs.clear(); |
259 |
244 return; |
260 if( !done() ) { |
245 } |
261 if( concurrent() ) |
246 } |
262 QMetaObject::invokeMethod( this, "work", Qt::QueuedConnection, Q_ARG( ulong, i + 1 )); |
247 |
263 else |
248 emit workDone(); |
264 work( i + 1 ); |
249 m_done = true; |
265 } |
250 } |
266 } |
251 |
267 |
252 // ============================================================================= |
268 // ============================================================================= |
253 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
269 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
254 // ============================================================================= |
270 // ============================================================================= |
258 g_aborted = false; |
274 g_aborted = false; |
259 |
275 |
260 if( numWarnings ) |
276 if( numWarnings ) |
261 *numWarnings = 0; |
277 *numWarnings = 0; |
262 |
278 |
|
279 // Calculate the amount of lines |
|
280 for( str line : *f ) |
|
281 lines << line; |
|
282 f->rewind(); |
|
283 |
263 FileLoader* loader = new FileLoader; |
284 FileLoader* loader = new FileLoader; |
264 loader->setFile( f ); |
|
265 loader->setWarningsPointer( numWarnings ); |
285 loader->setWarningsPointer( numWarnings ); |
266 |
286 loader->setLines( lines ); |
267 // Calculate the amount of lines |
287 loader->setConcurrent( g_loadingMainFile ); |
268 ulong numLines = 0; |
288 loader->start(); |
269 for( str line : *f ) |
289 |
270 numLines++; |
290 while( loader->done() == false ) |
271 |
291 qApp->processEvents(); |
272 f->rewind(); |
|
273 |
|
274 if (g_loadingMainFile) |
|
275 { |
|
276 // Show a progress dialog if we're loading the main file here and move |
|
277 // the actual work to a separate thread as this can be a rather intensive |
|
278 // operation and if we don't respond quickly enough, the program can be |
|
279 // deemed inresponsive.. which is a bad thing. |
|
280 |
|
281 // Init the thread and move the loader into it |
|
282 QThread* loaderThread = new QThread; |
|
283 QObject::connect( loaderThread, SIGNAL( started() ), loader, SLOT( work() )); |
|
284 QObject::connect( loaderThread, SIGNAL( finished() ), loader, SLOT( deleteLater() )); |
|
285 loader->moveToThread (loaderThread); |
|
286 loaderThread->start (); |
|
287 |
|
288 // Now create a progress prompt for the operation |
|
289 OpenProgressDialog* dlg = new OpenProgressDialog (g_win); |
|
290 dlg->setNumLines( numLines ); |
|
291 |
|
292 // Connect the loader in so we can show updates |
|
293 QObject::connect( loader, SIGNAL( progressUpdate( int )), dlg, SLOT( updateProgress( int ))); |
|
294 QObject::connect( loader, SIGNAL( workDone() ), dlg, SLOT( accept() )); |
|
295 |
|
296 // Show the prompt. If the user hits cancel, tell the loader to abort. |
|
297 if( !dlg->exec() ) |
|
298 { |
|
299 loader->abortflag = true; |
|
300 g_aborted = true; |
|
301 } |
|
302 } else |
|
303 loader->work(); |
|
304 |
292 |
305 // If we wanted the success value, supply that now |
293 // If we wanted the success value, supply that now |
306 if( ok ) |
294 if( ok ) |
307 *ok = loader->done(); |
295 *ok = loader->done(); |
308 |
296 |
309 // If the loader was done, return the objects it generated |
297 objs = loader->objs(); |
310 if( loader->done() ) |
|
311 objs = loader->objs(); |
|
312 |
|
313 return objs; |
298 return objs; |
314 } |
299 } |
315 |
300 |
316 // ============================================================================= |
301 // ============================================================================= |
317 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
302 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |