278 *sizeptr = 0; |
278 *sizeptr = 0; |
279 |
279 |
280 // File is open, now save the model to it. Note that LDraw requires files to have DOS line endings. |
280 // File is open, now save the model to it. Note that LDraw requires files to have DOS line endings. |
281 for (LDObject* obj : objects()) |
281 for (LDObject* obj : objects()) |
282 { |
282 { |
283 QByteArray subdata ((obj->asText() + "\r\n").toUtf8()); |
283 QByteArray subdata((obj->asText() + "\r\n").toUtf8()); |
284 data.append (subdata); |
284 data.append(subdata); |
285 |
285 |
286 if (sizeptr) |
286 if (sizeptr) |
287 *sizeptr += subdata.size(); |
287 *sizeptr += subdata.size(); |
288 } |
288 } |
289 |
289 |
290 QFile f (path); |
290 QFile f(path); |
291 |
291 |
292 if (not f.open (QIODevice::WriteOnly)) |
292 if (not f.open(QIODevice::WriteOnly)) |
293 return false; |
293 return false; |
294 |
294 |
295 f.write (data); |
295 f.write(data); |
296 f.close(); |
296 f.close(); |
297 |
297 |
298 // We have successfully saved, update the save position now. |
298 // We have successfully saved, update the save position now. |
299 setSavePosition (history()->position()); |
299 setSavePosition(history()->position()); |
300 setFullPath (path); |
300 setFullPath(path); |
301 setName (shortenName (path)); |
301 setName(shortenName(path)); |
302 m_window->updateDocumentListItem (this); |
302 m_window->updateDocumentListItem(this); |
303 m_window->updateTitle(); |
303 m_window->updateTitle(); |
304 return true; |
304 return true; |
305 } |
305 } |
306 |
306 |
307 // ============================================================================= |
307 // ============================================================================= |
308 // |
308 // |
309 void LDDocument::clear() |
309 void LDDocument::clear() |
310 { |
310 { |
311 for (LDObject* obj : objects()) |
311 for (LDObject* obj : objects()) |
312 forgetObject (obj); |
312 forgetObject(obj); |
313 } |
313 } |
314 |
314 |
315 // ============================================================================= |
315 // ============================================================================= |
316 // |
316 // |
317 static void CheckTokenCount (const QStringList& tokens, int num) |
317 static void CheckTokenCount(const QStringList& tokens, int num) |
318 { |
318 { |
319 if (tokens.size() != num) |
319 if (tokens.size() != num) |
320 throw QString (format ("Bad amount of tokens, expected %1, got %2", num, tokens.size())); |
320 throw QString(format("Bad amount of tokens, expected %1, got %2", num, tokens.size())); |
321 } |
321 } |
322 |
322 |
323 // ============================================================================= |
323 // ============================================================================= |
324 // |
324 // |
325 static void CheckTokenNumbers (const QStringList& tokens, int min, int max) |
325 static void CheckTokenNumbers(const QStringList& tokens, int min, int max) |
326 { |
326 { |
327 bool ok; |
327 bool ok; |
328 QRegExp scientificRegex ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+"); |
328 QRegExp scientificRegex("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+"); |
329 |
329 |
330 for (int i = min; i <= max; ++i) |
330 for (int i = min; i <= max; ++i) |
331 { |
331 { |
332 // Check for floating point |
332 // Check for floating point |
333 tokens[i].toDouble (&ok); |
333 tokens[i].toDouble(&ok); |
334 if (ok) |
334 if (ok) |
335 return; |
335 return; |
336 |
336 |
337 // Check hex |
337 // Check hex |
338 if (tokens[i].startsWith ("0x")) |
338 if (tokens[i].startsWith("0x")) |
339 { |
339 { |
340 tokens[i].mid (2).toInt (&ok, 16); |
340 tokens[i].mid(2).toInt(&ok, 16); |
341 |
341 |
342 if (ok) |
342 if (ok) |
343 return; |
343 return; |
344 } |
344 } |
345 |
345 |
346 // Check scientific notation, e.g. 7.99361e-15 |
346 // Check scientific notation, e.g. 7.99361e-15 |
347 if (scientificRegex.exactMatch (tokens[i])) |
347 if (scientificRegex.exactMatch(tokens[i])) |
348 return; |
348 return; |
349 |
349 |
350 throw QString (format ("Token #%1 was `%2`, expected a number (matched length: %3)", |
350 throw QString(format("Token #%1 was `%2`, expected a number(matched length: %3)", |
351 (i + 1), tokens[i], scientificRegex.matchedLength())); |
351 (i + 1), tokens[i], scientificRegex.matchedLength())); |
352 } |
352 } |
353 } |
353 } |
354 |
354 |
355 // ============================================================================= |
355 // ============================================================================= |
356 // |
356 // |
357 static Vertex ParseVertex (QStringList& s, const int n) |
357 static Vertex ParseVertex(QStringList& s, const int n) |
358 { |
358 { |
359 Vertex v; |
359 Vertex v; |
360 v.apply ([&] (Axis ax, double& a) { a = s[n + ax].toDouble(); }); |
360 v.apply([&](Axis ax, double& a) { a = s[n + ax].toDouble(); }); |
361 return v; |
361 return v; |
362 } |
362 } |
363 |
363 |
364 static int32 StringToNumber (QString a, bool* ok = nullptr) |
364 static int32 StringToNumber(QString a, bool* ok = nullptr) |
365 { |
365 { |
366 int base = 10; |
366 int base = 10; |
367 |
367 |
368 if (a.startsWith ("0x")) |
368 if (a.startsWith("0x")) |
369 { |
369 { |
370 a.remove (0, 2); |
370 a.remove(0, 2); |
371 base = 16; |
371 base = 16; |
372 } |
372 } |
373 |
373 |
374 return a.toLong (ok, base); |
374 return a.toLong(ok, base); |
375 } |
375 } |
376 |
376 |
377 // ============================================================================= |
377 // ============================================================================= |
378 // This is the LDraw code parser function. It takes in a string containing LDraw |
378 // This is the LDraw code parser function. It takes in a string containing LDraw |
379 // code and returns the object parsed from it. parseLine never returns null, |
379 // code and returns the object parsed from it. parseLine never returns null, |
380 // the object will be LDError if it could not be parsed properly. |
380 // the object will be LDError if it could not be parsed properly. |
381 // ============================================================================= |
381 // ============================================================================= |
382 LDObject* ParseLine (QString line) |
382 LDObject* ParseLine(QString line) |
383 { |
383 { |
384 try |
384 try |
385 { |
385 { |
386 QStringList tokens = line.split (" ", QString::SkipEmptyParts); |
386 QStringList tokens = line.split(" ", QString::SkipEmptyParts); |
387 |
387 |
388 if (tokens.size() <= 0) |
388 if (tokens.size() <= 0) |
389 { |
389 { |
390 // Line was empty, or only consisted of whitespace |
390 // Line was empty, or only consisted of whitespace |
391 return LDSpawn<LDEmpty>(); |
391 return LDSpawn<LDEmpty>(); |
392 } |
392 } |
393 |
393 |
394 if (tokens[0].length() != 1 or not tokens[0][0].isDigit()) |
394 if (tokens[0].length() != 1 or not tokens[0][0].isDigit()) |
395 throw QString ("Illogical line code"); |
395 throw QString("Illogical line code"); |
396 |
396 |
397 int num = tokens[0][0].digitValue(); |
397 int num = tokens[0][0].digitValue(); |
398 |
398 |
399 switch (num) |
399 switch(num) |
400 { |
400 { |
401 case 0: |
401 case 0: |
402 { |
402 { |
403 // Comment |
403 // Comment |
404 QString commentText (line.mid (line.indexOf ("0") + 2)); |
404 QString commentText(line.mid(line.indexOf("0") + 2)); |
405 QString commentTextSimplified (commentText.simplified()); |
405 QString commentTextSimplified(commentText.simplified()); |
406 |
406 |
407 // Handle BFC statements |
407 // Handle BFC statements |
408 if (tokens.size() > 2 and tokens[1] == "BFC") |
408 if (tokens.size() > 2 and tokens[1] == "BFC") |
409 { |
409 { |
410 for_enum (BfcStatement, i) |
410 for_enum(BfcStatement, i) |
411 { |
411 { |
412 if (commentTextSimplified == format ("BFC %1", LDBfc::statementToString (i))) |
412 if (commentTextSimplified == format("BFC %1", LDBfc::statementToString(i))) |
413 return LDSpawn<LDBfc> (i); |
413 return LDSpawn<LDBfc>(i); |
414 } |
414 } |
415 |
415 |
416 // MLCAD is notorious for stuffing these statements in parts it |
416 // MLCAD is notorious for stuffing these statements in parts it |
417 // creates. The above block only handles valid statements, so we |
417 // creates. The above block only handles valid statements, so we |
418 // need to handle MLCAD-style invertnext, clip and noclip separately. |
418 // need to handle MLCAD-style invertnext, clip and noclip separately. |
419 if (commentTextSimplified == "BFC CERTIFY INVERTNEXT") |
419 if (commentTextSimplified == "BFC CERTIFY INVERTNEXT") |
420 return LDSpawn<LDBfc> (BfcStatement::InvertNext); |
420 return LDSpawn<LDBfc>(BfcStatement::InvertNext); |
421 else if (commentTextSimplified == "BFC CERTIFY CLIP") |
421 else if (commentTextSimplified == "BFC CERTIFY CLIP") |
422 return LDSpawn<LDBfc> (BfcStatement::Clip); |
422 return LDSpawn<LDBfc>(BfcStatement::Clip); |
423 else if (commentTextSimplified == "BFC CERTIFY NOCLIP") |
423 else if (commentTextSimplified == "BFC CERTIFY NOCLIP") |
424 return LDSpawn<LDBfc> (BfcStatement::NoClip); |
424 return LDSpawn<LDBfc>(BfcStatement::NoClip); |
425 } |
425 } |
426 |
426 |
427 if (tokens.size() > 2 and tokens[1] == "!LDFORGE") |
427 if (tokens.size() > 2 and tokens[1] == "!LDFORGE") |
428 { |
428 { |
429 // Handle LDForge-specific types, they're embedded into comments too |
429 // Handle LDForge-specific types, they're embedded into comments too |
430 if (tokens[2] == "OVERLAY") |
430 if (tokens[2] == "OVERLAY") |
431 { |
431 { |
432 CheckTokenCount (tokens, 9); |
432 CheckTokenCount(tokens, 9); |
433 CheckTokenNumbers (tokens, 5, 8); |
433 CheckTokenNumbers(tokens, 5, 8); |
434 |
434 |
435 LDOverlay* obj = LDSpawn<LDOverlay>(); |
435 LDOverlay* obj = LDSpawn<LDOverlay>(); |
436 obj->setFileName (tokens[3]); |
436 obj->setFileName(tokens[3]); |
437 obj->setCamera (tokens[4].toLong()); |
437 obj->setCamera(tokens[4].toLong()); |
438 obj->setX (tokens[5].toLong()); |
438 obj->setX(tokens[5].toLong()); |
439 obj->setY (tokens[6].toLong()); |
439 obj->setY(tokens[6].toLong()); |
440 obj->setWidth (tokens[7].toLong()); |
440 obj->setWidth(tokens[7].toLong()); |
441 obj->setHeight (tokens[8].toLong()); |
441 obj->setHeight(tokens[8].toLong()); |
442 return obj; |
442 return obj; |
443 } |
443 } |
444 else if (tokens[2] == "BEZIER_CURVE") |
444 else if (tokens[2] == "BEZIER_CURVE") |
445 { |
445 { |
446 CheckTokenCount (tokens, 16); |
446 CheckTokenCount(tokens, 16); |
447 CheckTokenNumbers (tokens, 3, 15); |
447 CheckTokenNumbers(tokens, 3, 15); |
448 LDBezierCurve* obj = LDSpawn<LDBezierCurve>(); |
448 LDBezierCurve* obj = LDSpawn<LDBezierCurve>(); |
449 obj->setColor (StringToNumber (tokens[3])); |
449 obj->setColor(StringToNumber(tokens[3])); |
450 |
450 |
451 for (int i = 0; i < 4; ++i) |
451 for (int i = 0; i < 4; ++i) |
452 obj->setVertex (i, ParseVertex (tokens, 4 + (i * 3))); |
452 obj->setVertex(i, ParseVertex(tokens, 4 +(i * 3))); |
453 |
453 |
454 return obj; |
454 return obj; |
455 } |
455 } |
456 } |
456 } |
457 |
457 |
458 // Just a regular comment: |
458 // Just a regular comment: |
459 LDComment* obj = LDSpawn<LDComment>(); |
459 LDComment* obj = LDSpawn<LDComment>(); |
460 obj->setText (commentText); |
460 obj->setText(commentText); |
461 return obj; |
461 return obj; |
462 } |
462 } |
463 |
463 |
464 case 1: |
464 case 1: |
465 { |
465 { |
466 // Subfile |
466 // Subfile |
467 CheckTokenCount (tokens, 15); |
467 CheckTokenCount(tokens, 15); |
468 CheckTokenNumbers (tokens, 1, 13); |
468 CheckTokenNumbers(tokens, 1, 13); |
469 LDDocument* document = g_win->documents()->getDocumentByName (tokens[14]); |
469 LDDocument* document = g_win->documents()->getDocumentByName(tokens[14]); |
470 |
470 |
471 // If we cannot open the file, mark it an error. Note we cannot use LDParseError |
471 // If we cannot open the file, mark it an error. Note we cannot use LDParseError |
472 // here because the error object needs the document reference. |
472 // here because the error object needs the document reference. |
473 if (not document) |
473 if (not document) |
474 { |
474 { |
475 LDError* obj = LDSpawn<LDError> (line, format ("Could not open %1", tokens[14])); |
475 LDError* obj = LDSpawn<LDError>(line, format("Could not open %1", tokens[14])); |
476 obj->setFileReferenced (tokens[14]); |
476 obj->setFileReferenced(tokens[14]); |
477 return obj; |
477 return obj; |
478 } |
478 } |
479 |
479 |
480 LDSubfileReference* obj = LDSpawn<LDSubfileReference>(); |
480 LDSubfileReference* obj = LDSpawn<LDSubfileReference>(); |
481 obj->setColor (StringToNumber (tokens[1])); |
481 obj->setColor(StringToNumber(tokens[1])); |
482 obj->setPosition (ParseVertex (tokens, 2)); // 2 - 4 |
482 obj->setPosition(ParseVertex(tokens, 2)); // 2 - 4 |
483 |
483 |
484 Matrix transform; |
484 Matrix transform; |
485 |
485 |
486 for (int i = 0; i < 9; ++i) |
486 for (int i = 0; i < 9; ++i) |
487 transform[i] = tokens[i + 5].toDouble(); // 5 - 13 |
487 transform[i] = tokens[i + 5].toDouble(); // 5 - 13 |
488 |
488 |
489 obj->setTransform (transform); |
489 obj->setTransform(transform); |
490 obj->setFileInfo (document); |
490 obj->setFileInfo(document); |
491 return obj; |
491 return obj; |
492 } |
492 } |
493 |
493 |
494 case 2: |
494 case 2: |
495 { |
495 { |
496 CheckTokenCount (tokens, 8); |
496 CheckTokenCount(tokens, 8); |
497 CheckTokenNumbers (tokens, 1, 7); |
497 CheckTokenNumbers(tokens, 1, 7); |
498 |
498 |
499 // Line |
499 // Line |
500 LDLine* obj (LDSpawn<LDLine>()); |
500 LDLine* obj(LDSpawn<LDLine>()); |
501 obj->setColor (StringToNumber (tokens[1])); |
501 obj->setColor(StringToNumber(tokens[1])); |
502 |
502 |
503 for (int i = 0; i < 2; ++i) |
503 for (int i = 0; i < 2; ++i) |
504 obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 7 |
504 obj->setVertex(i, ParseVertex(tokens, 2 +(i * 3))); // 2 - 7 |
505 |
505 |
506 return obj; |
506 return obj; |
507 } |
507 } |
508 |
508 |
509 case 3: |
509 case 3: |
510 { |
510 { |
511 CheckTokenCount (tokens, 11); |
511 CheckTokenCount(tokens, 11); |
512 CheckTokenNumbers (tokens, 1, 10); |
512 CheckTokenNumbers(tokens, 1, 10); |
513 |
513 |
514 // Triangle |
514 // Triangle |
515 LDTriangle* obj (LDSpawn<LDTriangle>()); |
515 LDTriangle* obj(LDSpawn<LDTriangle>()); |
516 obj->setColor (StringToNumber (tokens[1])); |
516 obj->setColor(StringToNumber(tokens[1])); |
517 |
517 |
518 for (int i = 0; i < 3; ++i) |
518 for (int i = 0; i < 3; ++i) |
519 obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 10 |
519 obj->setVertex(i, ParseVertex(tokens, 2 +(i * 3))); // 2 - 10 |
520 |
520 |
521 return obj; |
521 return obj; |
522 } |
522 } |
523 |
523 |
524 case 4: |
524 case 4: |
525 case 5: |
525 case 5: |
526 { |
526 { |
527 CheckTokenCount (tokens, 14); |
527 CheckTokenCount(tokens, 14); |
528 CheckTokenNumbers (tokens, 1, 13); |
528 CheckTokenNumbers(tokens, 1, 13); |
529 |
529 |
530 // Quadrilateral / Conditional line |
530 // Quadrilateral / Conditional line |
531 LDObject* obj; |
531 LDObject* obj; |
532 |
532 |
533 if (num == 4) |
533 if (num == 4) |
534 obj = LDSpawn<LDQuad>(); |
534 obj = LDSpawn<LDQuad>(); |
535 else |
535 else |
536 obj = LDSpawn<LDCondLine>(); |
536 obj = LDSpawn<LDCondLine>(); |
537 |
537 |
538 obj->setColor (StringToNumber (tokens[1])); |
538 obj->setColor(StringToNumber(tokens[1])); |
539 |
539 |
540 for (int i = 0; i < 4; ++i) |
540 for (int i = 0; i < 4; ++i) |
541 obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 13 |
541 obj->setVertex(i, ParseVertex(tokens, 2 +(i * 3))); // 2 - 13 |
542 |
542 |
543 return obj; |
543 return obj; |
544 } |
544 } |
545 |
545 |
546 default: |
546 default: |
547 throw QString ("Unknown line code number"); |
547 throw QString("Unknown line code number"); |
548 } |
548 } |
549 } |
549 } |
550 catch (QString& e) |
550 catch(QString& e) |
551 { |
551 { |
552 // Strange line we couldn't parse |
552 // Strange line we couldn't parse |
553 return LDSpawn<LDError> (line, e); |
553 return LDSpawn<LDError>(line, e); |
554 } |
554 } |
555 } |
555 } |
556 |
556 |
557 // ============================================================================= |
557 // ============================================================================= |
558 // |
558 // |
559 void LDDocument::reloadAllSubfiles() |
559 void LDDocument::reloadAllSubfiles() |
560 { |
560 { |
561 print ("Reloading subfiles of %1", getDisplayName()); |
561 print("Reloading subfiles of %1", getDisplayName()); |
562 |
562 |
563 // Go through all objects in the current file and reload the subfiles |
563 // Go through all objects in the current file and reload the subfiles |
564 for (LDObject* obj : objects()) |
564 for (LDObject* obj : objects()) |
565 { |
565 { |
566 if (obj->type() == OBJ_SubfileReference) |
566 if (obj->type() == OBJ_SubfileReference) |
567 { |
567 { |
568 LDSubfileReference* ref = static_cast<LDSubfileReference*> (obj); |
568 LDSubfileReference* ref = static_cast<LDSubfileReference*>(obj); |
569 LDDocument* fileInfo = m_documents->getDocumentByName (ref->fileInfo()->name()); |
569 LDDocument* fileInfo = m_documents->getDocumentByName(ref->fileInfo()->name()); |
570 |
570 |
571 if (fileInfo) |
571 if (fileInfo) |
572 { |
572 { |
573 ref->setFileInfo (fileInfo); |
573 ref->setFileInfo(fileInfo); |
574 } |
574 } |
575 else |
575 else |
576 { |
576 { |
577 ref->replace (LDSpawn<LDError> (ref->asText(), |
577 ref->replace(LDSpawn<LDError>(ref->asText(), |
578 format ("Could not open %1", ref->fileInfo()->name()))); |
578 format("Could not open %1", ref->fileInfo()->name()))); |
579 } |
579 } |
580 } |
580 } |
581 |
581 |
582 // Reparse gibberish files. It could be that they are invalid because |
582 // Reparse gibberish files. It could be that they are invalid because |
583 // of loading errors. Circumstances may be different now. |
583 // of loading errors. Circumstances may be different now. |
584 if (obj->type() == OBJ_Error) |
584 if (obj->type() == OBJ_Error) |
585 obj->replace (ParseLine (static_cast<LDError*> (obj)->contents())); |
585 obj->replace(ParseLine(static_cast<LDError*>(obj)->contents())); |
586 } |
586 } |
587 |
587 |
588 m_needsReCache = true; |
588 m_needsReCache = true; |
589 |
589 |
590 if (this == m_window->currentDocument()) |
590 if (this == m_window->currentDocument()) |
591 m_window->buildObjectList(); |
591 m_window->buildObjectList(); |
592 } |
592 } |
593 |
593 |
594 // ============================================================================= |
594 // ============================================================================= |
595 // |
595 // |
596 int LDDocument::addObject (LDObject* obj) |
596 int LDDocument::addObject(LDObject* obj) |
597 { |
597 { |
598 history()->add (new AddHistoryEntry (objects().size(), obj)); |
598 history()->add(new AddHistoryEntry(objects().size(), obj)); |
599 m_objects << obj; |
599 m_objects << obj; |
600 addKnownVertices (obj); |
600 addKnownVertices(obj); |
601 obj->setDocument (this); |
601 obj->setDocument(this); |
602 m_window->renderer()->compileObject (obj); |
602 m_window->renderer()->compileObject(obj); |
603 return getObjectCount() - 1; |
603 return getObjectCount() - 1; |
604 } |
604 } |
605 |
605 |
606 // ============================================================================= |
606 // ============================================================================= |
607 // |
607 // |
608 void LDDocument::addObjects (const LDObjectList& objs) |
608 void LDDocument::addObjects(const LDObjectList& objs) |
609 { |
609 { |
610 for (LDObject* obj : objs) |
610 for (LDObject* obj : objs) |
611 { |
611 { |
612 if (obj) |
612 if (obj) |
613 addObject (obj); |
613 addObject(obj); |
614 } |
614 } |
615 } |
615 } |
616 |
616 |
617 // ============================================================================= |
617 // ============================================================================= |
618 // |
618 // |
619 void LDDocument::insertObj (int pos, LDObject* obj) |
619 void LDDocument::insertObj(int pos, LDObject* obj) |
620 { |
620 { |
621 history()->add (new AddHistoryEntry (pos, obj)); |
621 history()->add(new AddHistoryEntry(pos, obj)); |
622 m_objects.insert (pos, obj); |
622 m_objects.insert(pos, obj); |
623 obj->setDocument (this); |
623 obj->setDocument(this); |
624 m_window->renderer()->compileObject (obj); |
624 m_window->renderer()->compileObject(obj); |
625 |
625 |
626 |
626 |
627 #ifdef DEBUG |
627 #ifdef DEBUG |
628 if (not isCache()) |
628 if (not isCache()) |
629 dprint ("Inserted object #%1 (%2) at %3\n", obj->id(), obj->typeName(), pos); |
629 dprint("Inserted object #%1(%2) at %3\n", obj->id(), obj->typeName(), pos); |
630 #endif |
630 #endif |
631 } |
631 } |
632 |
632 |
633 // ============================================================================= |
633 // ============================================================================= |
634 // |
634 // |
635 void LDDocument::addKnownVertices (LDObject* obj) |
635 void LDDocument::addKnownVertices(LDObject* obj) |
636 { |
636 { |
637 auto it = m_objectVertices.find (obj); |
637 auto it = m_objectVertices.find(obj); |
638 |
638 |
639 if (it == m_objectVertices.end()) |
639 if (it == m_objectVertices.end()) |
640 it = m_objectVertices.insert (obj, QVector<Vertex>()); |
640 it = m_objectVertices.insert(obj, QVector<Vertex>()); |
641 else |
641 else |
642 it->clear(); |
642 it->clear(); |
643 |
643 |
644 obj->getVertices (*it); |
644 obj->getVertices(*it); |
645 needVertexMerge(); |
645 needVertexMerge(); |
646 } |
646 } |
647 |
647 |
648 // ============================================================================= |
648 // ============================================================================= |
649 // |
649 // |
650 void LDDocument::forgetObject (LDObject* obj) |
650 void LDDocument::forgetObject(LDObject* obj) |
651 { |
651 { |
652 int idx = obj->lineNumber(); |
652 int idx = obj->lineNumber(); |
653 |
653 |
654 if (m_objects[idx] == obj) |
654 if (m_objects[idx] == obj) |
655 { |
655 { |
656 obj->deselect(); |
656 obj->deselect(); |
657 |
657 |
658 if (not isCache() and not m_beingDestroyed) |
658 if (not isCache() and not m_beingDestroyed) |
659 { |
659 { |
660 history()->add (new DelHistoryEntry (idx, obj)); |
660 history()->add(new DelHistoryEntry(idx, obj)); |
661 m_objectVertices.remove (obj); |
661 m_objectVertices.remove(obj); |
662 } |
662 } |
663 |
663 |
664 m_objects.removeAt (idx); |
664 m_objects.removeAt(idx); |
665 obj->setDocument (nullptr); |
665 obj->setDocument(nullptr); |
666 } |
666 } |
667 } |
667 } |
668 |
668 |
669 // ============================================================================= |
669 // ============================================================================= |
670 // |
670 // |
671 void LDDocument::setObject (int idx, LDObject* obj) |
671 void LDDocument::setObject(int idx, LDObject* obj) |
672 { |
672 { |
673 if (idx < 0 or idx >= m_objects.size()) |
673 if (idx < 0 or idx >= m_objects.size()) |
674 return; |
674 return; |
675 |
675 |
676 // Mark this change to history |
676 // Mark this change to history |
677 if (not m_history->isIgnoring()) |
677 if (not m_history->isIgnoring()) |
678 { |
678 { |
679 QString oldcode = getObject (idx)->asText(); |
679 QString oldcode = getObject(idx)->asText(); |
680 QString newcode = obj->asText(); |
680 QString newcode = obj->asText(); |
681 m_history->add (new EditHistoryEntry (idx, oldcode, newcode)); |
681 m_history->add(new EditHistoryEntry(idx, oldcode, newcode)); |
682 } |
682 } |
683 |
683 |
684 m_objectVertices.remove (m_objects[idx]); |
684 m_objectVertices.remove(m_objects[idx]); |
685 m_objects[idx]->deselect(); |
685 m_objects[idx]->deselect(); |
686 m_objects[idx]->setDocument (nullptr); |
686 m_objects[idx]->setDocument(nullptr); |
687 obj->setDocument (this); |
687 obj->setDocument(this); |
688 addKnownVertices (obj); |
688 addKnownVertices(obj); |
689 m_window->renderer()->compileObject (obj); |
689 m_window->renderer()->compileObject(obj); |
690 m_objects[idx] = obj; |
690 m_objects[idx] = obj; |
691 needVertexMerge(); |
691 needVertexMerge(); |
692 } |
692 } |
693 |
693 |
694 // ============================================================================= |
694 // ============================================================================= |
695 // |
695 // |
696 LDObject* LDDocument::getObject (int pos) const |
696 LDObject* LDDocument::getObject(int pos) const |
697 { |
697 { |
698 if (pos < m_objects.size()) |
698 if (pos < m_objects.size()) |
699 return m_objects[pos]; |
699 return m_objects[pos]; |
700 else |
700 else |
701 return nullptr; |
701 return nullptr; |