src/gui_editactions.cc

changeset 629
b75c6cce02e2
parent 628
6b13e4c2e97b
child 630
42ec68fcad9e
child 675
450827da2376
equal deleted inserted replaced
628:6b13e4c2e97b 629:b75c6cce02e2
1 /*
2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013, 2014 Santeri Piippo
4 *
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
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
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/>.
17 */
18
19 #include <QSpinBox>
20 #include <QCheckBox>
21 #include <QBoxLayout>
22 #include <QClipboard>
23
24 #include "gui.h"
25 #include "main.h"
26 #include "document.h"
27 #include "colorSelectDialog.h"
28 #include "misc.h"
29 #include "widgets.h"
30 #include "gldraw.h"
31 #include "dialogs.h"
32 #include "colors.h"
33 #include "ui_replcoords.h"
34 #include "ui_editraw.h"
35 #include "ui_flip.h"
36 #include "ui_addhistoryline.h"
37
38 cfg (Bool, edit_schemanticinline, false);
39 extern_cfg (String, ld_defaultuser);
40
41 // =============================================================================
42 // -----------------------------------------------------------------------------
43 static int copyToClipboard()
44 {
45 LDObjectList objs = selection();
46 int num = 0;
47
48 // Clear the clipboard first.
49 qApp->clipboard()->clear();
50
51 // Now, copy the contents into the clipboard.
52 QString data;
53
54 for (LDObject* obj : objs)
55 {
56 if (data.length() > 0)
57 data += "\n";
58
59 data += obj->raw();
60 ++num;
61 }
62
63 qApp->clipboard()->setText (data);
64 return num;
65 }
66
67 // =============================================================================
68 // -----------------------------------------------------------------------------
69 DEFINE_ACTION (Cut, CTRL (X))
70 {
71 int num = copyToClipboard();
72 deleteSelection();
73 log (tr ("%1 objects cut"), num);
74 }
75
76 // =============================================================================
77 // -----------------------------------------------------------------------------
78 DEFINE_ACTION (Copy, CTRL (C))
79 {
80 int num = copyToClipboard();
81 log (tr ("%1 objects copied"), num);
82 }
83
84 // =============================================================================
85 // -----------------------------------------------------------------------------
86 DEFINE_ACTION (Paste, CTRL (V))
87 {
88 const QString clipboardText = qApp->clipboard()->text();
89 int idx = getInsertionPoint();
90 getCurrentDocument()->clearSelection();
91 int num = 0;
92
93 for (QString line : clipboardText.split ("\n"))
94 {
95 LDObject* pasted = parseLine (line);
96 getCurrentDocument()->insertObj (idx++, pasted);
97 pasted->select();
98 R()->compileObject (pasted);
99 ++num;
100 }
101
102 log (tr ("%1 objects pasted"), num);
103 refresh();
104 scrollToSelection();
105 }
106
107 // =============================================================================
108 // -----------------------------------------------------------------------------
109 DEFINE_ACTION (Delete, KEY (Delete))
110 {
111 int num = deleteSelection();
112 log (tr ("%1 objects deleted"), num);
113 }
114
115 // =============================================================================
116 // -----------------------------------------------------------------------------
117 static void doInline (bool deep)
118 {
119 LDObjectList sel = selection();
120
121 for (LDObject* obj : sel)
122 {
123 // Get the index of the subfile so we know where to insert the
124 // inlined contents.
125 long idx = obj->getIndex();
126
127 if (idx == -1)
128 continue;
129
130 LDObjectList objs;
131
132 if (obj->getType() == LDObject::ESubfile)
133 objs = static_cast<LDSubfile*> (obj)->inlineContents (
134 (LDSubfile::InlineFlags)
135 ( (deep) ? LDSubfile::DeepInline : 0) |
136 LDSubfile::CacheInline
137 );
138 else
139 continue;
140
141 // Merge in the inlined objects
142 for (LDObject * inlineobj : objs)
143 {
144 QString line = inlineobj->raw();
145 inlineobj->deleteSelf();
146 LDObject* newobj = parseLine (line);
147 getCurrentDocument()->insertObj (idx++, newobj);
148 newobj->select();
149 g_win->R()->compileObject (newobj);
150 }
151
152 // Delete the subfile now as it's been inlined.
153 obj->deleteSelf();
154 }
155
156 g_win->refresh();
157 }
158
159 DEFINE_ACTION (Inline, CTRL (I))
160 {
161 doInline (false);
162 }
163
164 DEFINE_ACTION (InlineDeep, CTRL_SHIFT (I))
165 {
166 doInline (true);
167 }
168
169 // =============================================================================
170 // -----------------------------------------------------------------------------
171 DEFINE_ACTION (SplitQuads, 0)
172 {
173 LDObjectList objs = selection();
174 int num = 0;
175
176 for (LDObject* obj : objs)
177 {
178 if (obj->getType() != LDObject::EQuad)
179 continue;
180
181 // Find the index of this quad
182 long index = obj->getIndex();
183
184 if (index == -1)
185 return;
186
187 QList<LDTriangle*> triangles = static_cast<LDQuad*> (obj)->splitToTriangles();
188
189 // Replace the quad with the first triangle and add the second triangle
190 // after the first one.
191 getCurrentDocument()->setObject (index, triangles[0]);
192 getCurrentDocument()->insertObj (index + 1, triangles[1]);
193
194 for (LDTriangle* t : triangles)
195 R()->compileObject (t);
196
197 // Delete this quad now, it has been split.
198 obj->deleteSelf();
199
200 num++;
201 }
202
203 log ("%1 quadrilaterals split", num);
204 refresh();
205 }
206
207 // =============================================================================
208 // -----------------------------------------------------------------------------
209 DEFINE_ACTION (EditRaw, KEY (F9))
210 {
211 if (selection().size() != 1)
212 return;
213
214 LDObject* obj = selection()[0];
215 QDialog* dlg = new QDialog;
216 Ui::EditRawUI ui;
217
218 ui.setupUi (dlg);
219 ui.code->setText (obj->raw());
220
221 if (obj->getType() == LDObject::EError)
222 ui.errorDescription->setText (static_cast<LDError*> (obj)->reason);
223 else
224 {
225 ui.errorDescription->hide();
226 ui.errorIcon->hide();
227 }
228
229 if (!dlg->exec())
230 return;
231
232 LDObject* oldobj = obj;
233
234 // Reinterpret it from the text of the input field
235 obj = parseLine (ui.code->text());
236 oldobj->replace (obj);
237
238 // Refresh
239 R()->compileObject (obj);
240 refresh();
241 }
242
243 // =============================================================================
244 // -----------------------------------------------------------------------------
245 DEFINE_ACTION (SetColor, KEY (C))
246 {
247 if (selection().isEmpty())
248 return;
249
250 int colnum;
251 int defcol = -1;
252
253 LDObjectList objs = selection();
254
255 // If all selected objects have the same color, said color is our default
256 // value to the color selection dialog.
257 defcol = getSelectedColor();
258
259 // Show the dialog to the user now and ask for a color.
260 if (ColorSelector::selectColor (colnum, defcol, g_win))
261 {
262 for (LDObject* obj : objs)
263 {
264 if (obj->isColored() == false)
265 continue;
266
267 obj->setColor (colnum);
268 R()->compileObject (obj);
269 }
270
271 refresh();
272 }
273 }
274
275 // =============================================================================
276 // -----------------------------------------------------------------------------
277 DEFINE_ACTION (Borders, CTRL_SHIFT (B))
278 {
279 LDObjectList objs = selection();
280 int num = 0;
281
282 for (LDObject* obj : objs)
283 {
284 const LDObject::Type type = obj->getType();
285 if (type != LDObject::EQuad && type != LDObject::ETriangle)
286 continue;
287
288 int numLines;
289 LDLine* lines[4];
290
291 if (type == LDObject::EQuad)
292 {
293 numLines = 4;
294
295 LDQuad* quad = static_cast<LDQuad*> (obj);
296 lines[0] = new LDLine (quad->getVertex (0), quad->getVertex (1));
297 lines[1] = new LDLine (quad->getVertex (1), quad->getVertex (2));
298 lines[2] = new LDLine (quad->getVertex (2), quad->getVertex (3));
299 lines[3] = new LDLine (quad->getVertex (3), quad->getVertex (0));
300 }
301 else
302 {
303 numLines = 3;
304
305 LDTriangle* tri = static_cast<LDTriangle*> (obj);
306 lines[0] = new LDLine (tri->getVertex (0), tri->getVertex (1));
307 lines[1] = new LDLine (tri->getVertex (1), tri->getVertex (2));
308 lines[2] = new LDLine (tri->getVertex (2), tri->getVertex (0));
309 }
310
311 for (int i = 0; i < numLines; ++i)
312 {
313 long idx = obj->getIndex() + i + 1;
314
315 lines[i]->setColor (edgecolor);
316 getCurrentDocument()->insertObj (idx, lines[i]);
317 R()->compileObject (lines[i]);
318 }
319
320 num += numLines;
321 }
322
323 log (tr ("Added %1 border lines"), num);
324 refresh();
325 }
326
327 // =============================================================================
328 // -----------------------------------------------------------------------------
329 DEFINE_ACTION (CornerVerts, 0)
330 {
331 int num = 0;
332
333 for (LDObject* obj : selection())
334 {
335 if (obj->vertices() < 2)
336 continue;
337
338 int idx = obj->getIndex();
339
340 for (int i = 0; i < obj->vertices(); ++i)
341 {
342 LDVertex* vert = new LDVertex;
343 vert->pos = obj->getVertex (i);
344 vert->setColor (obj->getColor());
345
346 getCurrentDocument()->insertObj (++idx, vert);
347 R()->compileObject (vert);
348 ++num;
349 }
350 }
351
352 log (tr ("Added %1 vertices"), num);
353 refresh();
354 }
355
356 // =============================================================================
357 // -----------------------------------------------------------------------------
358 static void doMoveSelection (const bool up)
359 {
360 LDObjectList objs = selection();
361 LDObject::moveObjects (objs, up);
362 g_win->buildObjList();
363 }
364
365 // =============================================================================
366 // -----------------------------------------------------------------------------
367 DEFINE_ACTION (MoveUp, KEY (PageUp))
368 {
369 doMoveSelection (true);
370 }
371
372 DEFINE_ACTION (MoveDown, KEY (PageDown))
373 {
374 doMoveSelection (false);
375 }
376
377 // =============================================================================
378 // -----------------------------------------------------------------------------
379 DEFINE_ACTION (Undo, CTRL (Z))
380 {
381 getCurrentDocument()->undo();
382 }
383
384 DEFINE_ACTION (Redo, CTRL_SHIFT (Z))
385 {
386 getCurrentDocument()->redo();
387 }
388
389 // =============================================================================
390 // -----------------------------------------------------------------------------
391 void doMoveObjects (Vertex vect)
392 {
393 // Apply the grid values
394 vect[X] *= *currentGrid().confs[Grid::X];
395 vect[Y] *= *currentGrid().confs[Grid::Y];
396 vect[Z] *= *currentGrid().confs[Grid::Z];
397
398 for (LDObject* obj : selection())
399 {
400 obj->move (vect);
401 g_win->R()->compileObject (obj);
402 }
403
404 g_win->refresh();
405 }
406
407 // =============================================================================
408 // -----------------------------------------------------------------------------
409 DEFINE_ACTION (MoveXNeg, KEY (Left))
410 {
411 doMoveObjects ({ -1, 0, 0});
412 }
413
414 DEFINE_ACTION (MoveYNeg, KEY (Home))
415 {
416 doMoveObjects ({0, -1, 0});
417 }
418
419 DEFINE_ACTION (MoveZNeg, KEY (Down))
420 {
421 doMoveObjects ({0, 0, -1});
422 }
423
424 DEFINE_ACTION (MoveXPos, KEY (Right))
425 {
426 doMoveObjects ({1, 0, 0});
427 }
428
429 DEFINE_ACTION (MoveYPos, KEY (End))
430 {
431 doMoveObjects ({0, 1, 0});
432 }
433
434 DEFINE_ACTION (MoveZPos, KEY (Up))
435 {
436 doMoveObjects ({0, 0, 1});
437 }
438
439 // =============================================================================
440 // -----------------------------------------------------------------------------
441 DEFINE_ACTION (Invert, CTRL_SHIFT (W))
442 {
443 LDObjectList sel = selection();
444
445 for (LDObject* obj : sel)
446 {
447 obj->invert();
448 R()->compileObject (obj);
449 }
450
451 refresh();
452 }
453
454 // =============================================================================
455 // -----------------------------------------------------------------------------
456 static void rotateVertex (Vertex& v, const Vertex& rotpoint, const Matrix& transform)
457 {
458 v.move (-rotpoint);
459 v.transform (transform, g_origin);
460 v.move (rotpoint);
461 }
462
463 // =============================================================================
464 // -----------------------------------------------------------------------------
465 static void doRotate (const int l, const int m, const int n)
466 {
467 LDObjectList sel = selection();
468 QList<Vertex*> queue;
469 const Vertex rotpoint = rotPoint (sel);
470 const double angle = (pi * *currentGrid().confs[Grid::Angle]) / 180,
471 cosangle = cos (angle),
472 sinangle = sin (angle);
473
474 // ref: http://en.wikipedia.org/wiki/Transformation_matrix#Rotation_2
475 Matrix transform (
476 {
477 (l* l * (1 - cosangle)) + cosangle,
478 (m* l * (1 - cosangle)) - (n* sinangle),
479 (n* l * (1 - cosangle)) + (m* sinangle),
480
481 (l* m * (1 - cosangle)) + (n* sinangle),
482 (m* m * (1 - cosangle)) + cosangle,
483 (n* m * (1 - cosangle)) - (l* sinangle),
484
485 (l* n * (1 - cosangle)) - (m* sinangle),
486 (m* n * (1 - cosangle)) + (l* sinangle),
487 (n* n * (1 - cosangle)) + cosangle
488 });
489
490 // Apply the above matrix to everything
491 for (LDObject* obj : sel)
492 {
493 if (obj->vertices())
494 {
495 for (int i = 0; i < obj->vertices(); ++i)
496 {
497 Vertex v = obj->getVertex (i);
498 rotateVertex (v, rotpoint, transform);
499 obj->setVertex (i, v);
500 }
501 }
502 elif (obj->hasMatrix())
503 {
504 LDMatrixObject* mo = dynamic_cast<LDMatrixObject*> (obj);
505
506 // Transform the position
507 Vertex v = mo->getPosition();
508 rotateVertex (v, rotpoint, transform);
509 mo->setPosition (v);
510
511 // Transform the matrix
512 mo->setTransform (transform * mo->getTransform());
513 }
514 elif (obj->getType() == LDObject::EVertex)
515 {
516 LDVertex* vert = static_cast<LDVertex*> (obj);
517 Vertex v = vert->pos;
518 rotateVertex (v, rotpoint, transform);
519 vert->pos = v;
520 }
521
522 g_win->R()->compileObject (obj);
523 }
524
525 g_win->refresh();
526 }
527
528 // =============================================================================
529 // -----------------------------------------------------------------------------
530 DEFINE_ACTION (RotateXPos, CTRL (Right))
531 {
532 doRotate (1, 0, 0);
533 }
534 DEFINE_ACTION (RotateYPos, CTRL (End))
535 {
536 doRotate (0, 1, 0);
537 }
538 DEFINE_ACTION (RotateZPos, CTRL (Up))
539 {
540 doRotate (0, 0, 1);
541 }
542 DEFINE_ACTION (RotateXNeg, CTRL (Left))
543 {
544 doRotate (-1, 0, 0);
545 }
546 DEFINE_ACTION (RotateYNeg, CTRL (Home))
547 {
548 doRotate (0, -1, 0);
549 }
550 DEFINE_ACTION (RotateZNeg, CTRL (Down))
551 {
552 doRotate (0, 0, -1);
553 }
554
555 DEFINE_ACTION (RotationPoint, (0))
556 {
557 configRotationPoint();
558 }
559
560 // =============================================================================
561 // -----------------------------------------------------------------------------
562 DEFINE_ACTION (RoundCoordinates, 0)
563 {
564 setlocale (LC_ALL, "C");
565 int num = 0;
566
567 for (LDObject* obj : selection())
568 {
569 LDMatrixObject* mo = dynamic_cast<LDMatrixObject*> (obj);
570
571 if (mo != null)
572 {
573 Vertex v = mo->getPosition();
574 Matrix t = mo->getTransform();
575
576 for_axes (ax)
577 roundToDecimals (v[ax], 3);
578
579 // Let matrix values be rounded to 4 decimals,
580 // they need that extra precision
581 for (int i = 0; i < 9; ++i)
582 roundToDecimals (t[i], 4);
583
584 mo->setPosition (v);
585 mo->setTransform (t);
586 num += 10;
587 }
588 else
589 {
590 for (int i = 0; i < obj->vertices(); ++i)
591 {
592 Vertex v = obj->getVertex (i);
593
594 for_axes (ax)
595 roundToDecimals (v[ax], 3);
596
597 obj->setVertex (i, v);
598 R()->compileObject (obj);
599 num += 3;
600 }
601 }
602 }
603
604 log (tr ("Rounded %1 values"), num);
605 refreshObjectList();
606 refresh();
607 }
608
609 // =============================================================================
610 // -----------------------------------------------------------------------------
611 DEFINE_ACTION (Uncolorize, 0)
612 {
613 int num = 0;
614
615 for (LDObject* obj : selection())
616 {
617 if (obj->isColored() == false)
618 continue;
619
620 int col = maincolor;
621
622 if (obj->getType() == LDObject::ELine || obj->getType() == LDObject::ECondLine)
623 col = edgecolor;
624
625 obj->setColor (col);
626 R()->compileObject (obj);
627 num++;
628 }
629
630 log (tr ("%1 objects uncolored"), num);
631 refresh();
632 }
633
634 // =============================================================================
635 // -----------------------------------------------------------------------------
636 DEFINE_ACTION (ReplaceCoords, CTRL (R))
637 {
638 QDialog* dlg = new QDialog (g_win);
639 Ui::ReplaceCoordsUI ui;
640 ui.setupUi (dlg);
641
642 if (!dlg->exec())
643 return;
644
645 const double search = ui.search->value(),
646 replacement = ui.replacement->value();
647 const bool any = ui.any->isChecked(),
648 rel = ui.relative->isChecked();
649
650 QList<Axis> sel;
651 int num = 0;
652
653 if (ui.x->isChecked()) sel << X;
654 if (ui.y->isChecked()) sel << Y;
655 if (ui.z->isChecked()) sel << Z;
656
657 for (LDObject* obj : selection())
658 {
659 for (int i = 0; i < obj->vertices(); ++i)
660 {
661 Vertex v = obj->getVertex (i);
662
663 for (Axis ax : sel)
664 {
665 double& coord = v[ax];
666
667 if (any || coord == search)
668 {
669 if (!rel)
670 coord = 0;
671
672 coord += replacement;
673 num++;
674 }
675 }
676
677 obj->setVertex (i, v);
678 R()->compileObject (obj);
679 }
680 }
681
682 log (tr ("Altered %1 values"), num);
683 refresh();
684 }
685
686 // =============================================================================
687 // -----------------------------------------------------------------------------
688 DEFINE_ACTION (Flip, CTRL_SHIFT (F))
689 {
690 QDialog* dlg = new QDialog;
691 Ui::FlipUI ui;
692 ui.setupUi (dlg);
693
694 if (!dlg->exec())
695 return;
696
697 QList<Axis> sel;
698
699 if (ui.x->isChecked()) sel << X;
700 if (ui.y->isChecked()) sel << Y;
701 if (ui.z->isChecked()) sel << Z;
702
703 for (LDObject* obj : selection())
704 {
705 for (int i = 0; i < obj->vertices(); ++i)
706 {
707 Vertex v = obj->getVertex (i);
708
709 for (Axis ax : sel)
710 v[ax] *= -1;
711
712 obj->setVertex (i, v);
713 R()->compileObject (obj);
714 }
715 }
716
717 refresh();
718 }
719
720 // =============================================================================
721 // -----------------------------------------------------------------------------
722 DEFINE_ACTION (Demote, 0)
723 {
724 LDObjectList sel = selection();
725 int num = 0;
726
727 for (LDObject* obj : sel)
728 {
729 if (obj->getType() != LDObject::ECondLine)
730 continue;
731
732 LDLine* repl = static_cast<LDCondLine*> (obj)->demote();
733 R()->compileObject (repl);
734 ++num;
735 }
736
737 log (tr ("Demoted %1 conditional lines"), num);
738 refresh();
739 }
740
741 // =============================================================================
742 // -----------------------------------------------------------------------------
743 static bool isColorUsed (int colnum)
744 {
745 for (LDObject* obj : getCurrentDocument()->getObjects())
746 if (obj->isColored() && obj->getColor() == colnum)
747 return true;
748
749 return false;
750 }
751
752 // =============================================================================
753 // -----------------------------------------------------------------------------
754 DEFINE_ACTION (Autocolor, 0)
755 {
756 int colnum = 0;
757
758 while (colnum < MAX_COLORS && (getColor (colnum) == null || isColorUsed (colnum)))
759 colnum++;
760
761 if (colnum >= MAX_COLORS)
762 {
763 log (tr ("Cannot auto-color: all colors are in use!"));
764 return;
765 }
766
767 for (LDObject* obj : selection())
768 {
769 if (obj->isColored() == false)
770 continue;
771
772 obj->setColor (colnum);
773 R()->compileObject (obj);
774 }
775
776 log (tr ("Auto-colored: new color is [%1] %2"), colnum, getColor (colnum)->name);
777 refresh();
778 }
779
780 // =============================================================================
781 // -----------------------------------------------------------------------------
782 DEFINE_ACTION (AddHistoryLine, 0)
783 {
784 LDObject* obj;
785 bool ishistory = false,
786 prevIsHistory = false;
787
788 QDialog* dlg = new QDialog;
789 Ui_AddHistoryLine* ui = new Ui_AddHistoryLine;
790 ui->setupUi (dlg);
791 ui->m_username->setText (ld_defaultuser);
792 ui->m_date->setDate (QDate::currentDate());
793 ui->m_comment->setFocus();
794
795 if (!dlg->exec())
796 return;
797
798 // Create the comment object based on input
799 QString commentText = fmt ("!HISTORY %1 [%2] %3",
800 ui->m_date->date().toString ("yyyy-MM-dd"),
801 ui->m_username->text(),
802 ui->m_comment->text());
803
804 LDComment* comm = new LDComment (commentText);
805
806 // Find a spot to place the new comment
807 for (
808 obj = getCurrentDocument()->getObject (0);
809 obj && obj->next() && !obj->next()->isScemantic();
810 obj = obj->next()
811 )
812 {
813 LDComment* comm = dynamic_cast<LDComment*> (obj);
814
815 if (comm && comm->text.startsWith ("!HISTORY "))
816 ishistory = true;
817
818 if (prevIsHistory && !ishistory)
819 {
820 // Last line was history, this isn't, thus insert the new history
821 // line here.
822 break;
823 }
824
825 prevIsHistory = ishistory;
826 }
827
828 int idx = obj ? obj->getIndex() : 0;
829 getCurrentDocument()->insertObj (idx++, comm);
830
831 // If we're adding a history line right before a scemantic object, pad it
832 // an empty line
833 if (obj && obj->next() && obj->next()->isScemantic())
834 getCurrentDocument()->insertObj (idx, new LDEmpty);
835
836 buildObjList();
837 delete ui;
838 }

mercurial