Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_assistant_tool.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2010 Geoffry Song <goffrie@gmail.com>
4 * SPDX-FileCopyrightText: 2017 Scott Petrovic <scottpetrovic@gmail.com>
5 * SPDX-FileCopyrightText: 2021 Nabil Maghfur Usman <nmaghfurusman@gmail.com>
6 *
7 * SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10#include <cstdio>
11#include <kis_assistant_tool.h>
12
13#include <kis_debug.h>
14#include <klocalizedstring.h>
15
16#include <QPainter>
17#include <QPainterPath>
18#include <QXmlStreamReader>
19#include <QXmlStreamWriter>
20#include <QStandardPaths>
21#include <QFile>
22#include <QLineF>
23#include <QMessageBox>
24
25#include <KoIcon.h>
26#include <KoFileDialog.h>
27#include <KoViewConverter.h>
28#include <KoPointerEvent.h>
30
31#include <canvas/kis_canvas2.h>
34#include <kis_cursor.h>
36#include <kis_dom_utils.h>
37#include <kis_global.h>
38#include <kis_image.h>
40#include <kis_undo_adapter.h>
41
42#include <KisViewManager.h>
43
46#include "RulerAssistant.h"
47#include "TwoPointAssistant.h"
49
50#include <math.h>
51#include <queue>
52
54 : KisTool(canvas, KisCursor::arrowCursor())
55 , m_canvas(dynamic_cast<KisCanvas2*>(canvas))
56 , m_assistantDrag(0)
57 , m_newAssistant(0)
58 , m_optionsWidget(0)
59 , m_unitManager(new KisDocumentAwareSpinBoxUnitManager(this))
60{
61 Q_ASSERT(m_canvas);
62 setObjectName("tool_assistanttool");
63}
64
68
69void KisAssistantTool::activate(const QSet<KoShape*> &shapes)
70{
71
72 KisTool::activate(shapes);
73
74 m_canvas->paintingAssistantsDecoration()->activateAssistantsEditor();
75 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
76
78 m_canvas->paintingAssistantsDecoration()->setHandleSize(m_handleSize);
79
80
81 if (m_optionsWidget) {
82 m_canvas->paintingAssistantsDecoration()->deselectAssistant();
84 }
85
86 m_canvas->updateCanvas();
87
88}
89
91{
92 m_canvas->paintingAssistantsDecoration()->deactivateAssistantsEditor();
93 m_canvas->updateCanvas();
95}
96
98{
100 m_origAssistantList = KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants());
101
102 bool newAssistantAllowed = true;
103
104 KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration();
105
106 if (m_newAssistant) {
108
109 if (!snap(event)) {
110 *m_newAssistant->handles().back() = canvasDecoration->snapToGuide(event, QPointF(), false);
111 }
112
113 if (m_newAssistant->handles().size() == m_newAssistant->numHandles()) {
114 addAssistant();
115 } else {
116 m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL);
117 }
118 m_canvas->updateCanvas();
119 return;
120 }
121 m_handleDrag = 0;
122 double minDist = m_handleMaxDist;
123
124
125 QPointF mousePos = m_canvas->viewConverter()->documentToView(canvasDecoration->snapToGuide(event, QPointF(), false));//m_canvas->viewConverter()->documentToView(event->point);
126
127 // syncs the assistant handles to the handles reference we store in this tool
128 // they can get out of sync with the way the actions and paintevents occur
129 // we probably need to stop storing a reference in m_handles and call the assistants directly
130 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
131
132
133 Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) {
134
135 if (assistant->isLocked()) {
136 continue; // let's not modify an assistant that is locked
137 }
138
139
140 // find out which handle on all assistants is closest to the mouse position
141 // vanishing points have "side handles", so make sure to include that
142 {
143 QList<KisPaintingAssistantHandleSP> allAssistantHandles;
144 allAssistantHandles.append(assistant->handles());
145 allAssistantHandles.append(assistant->sideHandles());
146
147 Q_FOREACH (const KisPaintingAssistantHandleSP handle, allAssistantHandles) {
148
149 double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle));
150 if (dist < minDist) {
151 minDist = dist;
152 m_handleDrag = handle;
153
154 assistantSelected(assistant); // whatever handle is the closest contains the selected assistant
155 m_canvas->paintingAssistantsDecoration()->raiseAssistant(assistant);
156 }
157 }
158 }
159
160
161
162
163 if(m_handleDrag && assistant->id() == "perspective") {
164 // Look for the handle which was pressed
165
166
167 if (m_handleDrag == assistant->topLeft()) {
168 double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag));
169 if (dist < minDist) {
170 minDist = dist;
171 }
172 m_dragStart = QPointF(assistant->topRight().data()->x(),assistant->topRight().data()->y());
174 } else if (m_handleDrag == assistant->topRight()) {
175 double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag));
176 if (dist < minDist) {
177 minDist = dist;
178 }
180 m_dragStart = QPointF(assistant->topLeft().data()->x(),assistant->topLeft().data()->y());
181 } else if (m_handleDrag == assistant->bottomLeft()) {
182 double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag));
183 if (dist < minDist) {
184 minDist = dist;
185 }
187 m_dragStart = QPointF(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y());
188 } else if (m_handleDrag == assistant->bottomRight()) {
189 double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*m_handleDrag));
190 if (dist < minDist) {
191 minDist = dist;
192 }
194 m_dragStart = QPointF(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y());
195 } else if (m_handleDrag == assistant->leftMiddle()) {
197 m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->topLeft().data()->x())*0.5,
198 (assistant->bottomLeft().data()->y()+assistant->topLeft().data()->y())*0.5);
199 m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y());
200 m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y());
201 m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant());
202 m_newAssistant->addHandle(assistant->topLeft(), HandleType::NORMAL );
205 m_newAssistant->addHandle(assistant->bottomLeft(), HandleType::NORMAL);
206 m_dragEnd = event->point;
207 m_handleDrag = 0;
208 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
209 return;
210 } else if (m_handleDrag == assistant->rightMiddle()) {
211 m_dragStart = QPointF((assistant->topRight().data()->x()+assistant->bottomRight().data()->x())*0.5,
212 (assistant->topRight().data()->y()+assistant->bottomRight().data()->y())*0.5);
214 m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y());
215 m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y());
216 m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant());
217 m_newAssistant->addHandle(assistant->topRight(), HandleType::NORMAL);
220 m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL);
221 m_dragEnd = event->point;
222 m_handleDrag = 0;
223 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
224 return;
225 } else if (m_handleDrag == assistant->topMiddle()) {
226 m_dragStart = QPointF((assistant->topLeft().data()->x()+assistant->topRight().data()->x())*0.5,
227 (assistant->topLeft().data()->y()+assistant->topRight().data()->y())*0.5);
229 m_selectedNode1 = new KisPaintingAssistantHandle(assistant->topLeft().data()->x(),assistant->topLeft().data()->y());
230 m_selectedNode2 = new KisPaintingAssistantHandle(assistant->topRight().data()->x(),assistant->topRight().data()->y());
231 m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant());
234 m_newAssistant->addHandle(assistant->topRight(), HandleType::NORMAL);
235 m_newAssistant->addHandle(assistant->topLeft(), HandleType::NORMAL);
236 m_dragEnd = event->point;
237 m_handleDrag = 0;
238 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
239 return;
240 } else if (m_handleDrag == assistant->bottomMiddle()) {
241 m_dragStart = QPointF((assistant->bottomLeft().data()->x()+assistant->bottomRight().data()->x())*0.5,
242 (assistant->bottomLeft().data()->y()+assistant->bottomRight().data()->y())*0.5);
244 m_selectedNode1 = new KisPaintingAssistantHandle(assistant->bottomLeft().data()->x(),assistant->bottomLeft().data()->y());
245 m_selectedNode2 = new KisPaintingAssistantHandle(assistant->bottomRight().data()->x(),assistant->bottomRight().data()->y());
246 m_newAssistant = toQShared(KisPaintingAssistantFactoryRegistry::instance()->get("perspective")->createPaintingAssistant());
247 m_newAssistant->addHandle(assistant->bottomLeft(), HandleType::NORMAL);
248 m_newAssistant->addHandle(assistant->bottomRight(), HandleType::NORMAL);
251 m_dragEnd = event->point;
252 m_handleDrag = 0;
253 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
254 return;
255 }
256 m_snapIsRadial = false;
257 }
258 else if (m_handleDrag && assistant->handles().size()>1 && (assistant->id() == "ruler" ||
259 assistant->id() == "parallel ruler" ||
260 assistant->id() == "infinite ruler" ||
261 assistant->id() == "spline" ||
262 assistant->id() == "curvilinear-perspective")){
263 if (m_handleDrag == assistant->handles()[0]) {
264 m_dragStart = *assistant->handles()[1];
265 } else if (m_handleDrag == assistant->handles()[1]) {
266 m_dragStart = *assistant->handles()[0];
267 } else if(assistant->handles().size()==4){
268 if (m_handleDrag == assistant->handles()[2]) {
269 m_dragStart = *assistant->handles()[0];
270 } else if (m_handleDrag == assistant->handles()[3]) {
271 m_dragStart = *assistant->handles()[1];
272 }
273 }
274 m_snapIsRadial = false;
275 } else if (m_handleDrag && assistant->handles().size()>2 && (assistant->id() == "ellipse" ||
276 assistant->id() == "concentric ellipse" ||
277 assistant->id() == "fisheye-point")){
278 m_snapIsRadial = false;
279 if (m_handleDrag == assistant->handles()[0]) {
280 m_dragStart = *assistant->handles()[1];
281 m_snapIsRadial = false;
282 } else if (m_handleDrag == assistant->handles()[1]) {
283 m_dragStart = *assistant->handles()[0];
284 m_snapIsRadial = false;
285 } else if (m_handleDrag == assistant->handles()[2]) {
286 m_dragStart = assistant->getEditorPosition();
287 m_radius = QLineF(m_dragStart, *assistant->handles()[0]);
288 m_snapIsRadial = true;
289 }
290 } else if (m_handleDrag && assistant->handles().size()>2 && assistant->id() == "two point") {
291
292 // If the user left the assistant's handles in an invalid
293 // state (ie 3rd handle isn't between the 1st and 2nd
294 // handle), then compute a sensible value for m_dragStart
295 // that respects it
296 QList<KisPaintingAssistantHandleSP> handles = assistant->handles();
297
298 const QPointF p1 = *assistant->handles()[0];
299 const QPointF p2 = *assistant->handles()[1];
300 const QPointF p3 = *assistant->handles()[2];
301
302 qreal size = 0;
303 QTransform t = qSharedPointerCast<TwoPointAssistant>(m_newAssistant)->localTransform(p1,p2,p3,&size);
304 QTransform inv = t.inverted();
305 if (t.map(p1).x() * t.map(p2).x() > 0) {
306
307 // We only care about m_dragStart if user is dragging a VP
308 if (m_handleDrag == assistant->handles()[0]) {
309 const QPointF safe_start = QPointF(-1.0*t.map(p1).x(),t.map(p1).y());
310 m_dragStart = inv.map(safe_start);
311 } else if (m_handleDrag == assistant->handles()[1]) {
312 const QPointF safe_start = QPointF(-1.0*t.map(p2).x(),t.map(p1).y());
313 m_dragStart = inv.map(safe_start);
314 }
315
316 m_snapIsRadial = false;
317 } else {
319 m_snapIsRadial = false;
320 }
321
322 } else if (m_handleDrag && assistant->id() == "vanishing point" &&
323 m_handleDrag == assistant->handles()[0]){
324 m_dragStart = assistant->getEditorPosition();
325 m_snapIsRadial = false;
326 }
327 }
328
329 m_currentAdjustment = QPointF();
330
331 if (m_handleDrag) {
332 // TODO: Shift-press should now be handled using the alternate actions
333 // if (event->modifiers() & Qt::ShiftModifier) {
334 // m_handleDrag->uncache();
335 // m_handleDrag = m_handleDrag->split()[0];
336 // m_handles = m_canvas->view()->paintingAssistantsDecoration()->handles();
337 // }
338 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
339 return;
340 }
341
342 m_assistantDrag.clear();
343
344 // list will contain the assistants whose control widgets are affected by the click event...
345 QList<KisPaintingAssistantSP> assistantsPressed;
346
347 // data for calculating mouse intersection with control widget.
348 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
349 const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter();
350 QTransform initialTransform = converter->documentToWidgetTransform();
351 // for UI control widget options with move, show, and delete -- disregard document transforms like rotating and mirroring.
352 // otherwise the UI controls get awkward to use when they are at 45 degree angles or the order of controls gets flipped backwards
353 QPointF uiMousePosition = initialTransform.map(canvasDecoration->snapToGuide(event, QPointF(), false));
354
355 // find control widgets pressed...
356 Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) {
357
358 QPointF actionsPosition = initialTransform.map(assistant->viewportConstrainedEditorPosition(converter, globalEditorWidgetData.boundingSize));
359
360 // first we must find if the click event intersects any assistant control widget rectangles.
361 // as it is possible for control widgets to overlap, we must then determine which control widget is actually being clicked based on the
362 // order of the assistants, which represent the z position hierarchy of their respective control widgets.
363
364 // calculate widget rect bound
365 QPointF actionsBGRectangle(actionsPosition + QPointF(globalEditorWidgetData.widgetOffset,globalEditorWidgetData.widgetOffset));
366 QRectF editorWidget = QRectF(actionsBGRectangle.x(), actionsBGRectangle.y(), globalEditorWidgetData.boundingSize.width(), globalEditorWidgetData.boundingSize.height());
367 // add all assistants that intersect the mouse to a list.
368 if (editorWidget.contains(uiMousePosition)) {
369 assistantsPressed.push_back(assistant);
370 }
371
372 }
373
374 // get assistant pressed...
375 if (!assistantsPressed.isEmpty()) {
376 // get closest assistant
377 KisPaintingAssistantSP assistant = assistantsPressed.last();
378 // move assistant to front of assistants list.
379 m_canvas->paintingAssistantsDecoration()->raiseAssistant(assistant);
380
381 assistantSelected(assistant);
382
383 QPointF actionsPosition = initialTransform.map(assistant->viewportConstrainedEditorPosition(converter, globalEditorWidgetData.boundingSize));
384
385 // loop through all activated buttons and see if any are being clicked
386 if(globalEditorWidgetData.moveButtonActivated) {
387 QPointF iconMovePosition(actionsPosition + globalEditorWidgetData.moveIconPosition);
388 QRectF moveRect(iconMovePosition, QSizeF(globalEditorWidgetData.buttonSize, globalEditorWidgetData.buttonSize));
389
390 if (moveRect.contains(uiMousePosition) && !assistant->isLocked()) {
391 m_assistantDrag = assistant;
392 m_cursorStart = event->point;
394
395 assistantSelected(assistant); // whatever handle is the closest contains the selected assistant
396
397 return;
398 }
399 }
400 if(globalEditorWidgetData.snapButtonActivated) {
401 QPointF iconSnapPosition(actionsPosition + globalEditorWidgetData.snapIconPosition);
402 QRectF visibleRect(iconSnapPosition, QSizeF(globalEditorWidgetData.buttonSize, globalEditorWidgetData.buttonSize));
403 if (visibleRect.contains(uiMousePosition)) {
404 newAssistantAllowed = false;
405 assistant->setSnappingActive(!assistant->isSnappingActive()); // toggle
406 assistant->uncache();//this updates the cache of the assistant, very important.
407
408 assistantSelected(assistant); // whatever handle is the closest contains the selected assistant
409 }
410 }
411 if(globalEditorWidgetData.lockButtonActivated) {
412 QPointF iconLockPosition(actionsPosition + globalEditorWidgetData.lockedIconPosition);
413 QRectF lockRect(iconLockPosition, QSizeF(globalEditorWidgetData.buttonSize, globalEditorWidgetData.buttonSize));
414 if (lockRect.contains(uiMousePosition)) {
415
416 assistant->setLocked(!assistant->isLocked());
417 assistantSelected(assistant); // whatever handle is the closest contains the selected assistant
419
420 return;
421 }
422 }
423 if(globalEditorWidgetData.duplicateButtonActivated) {
424 QPointF iconDuplicatePosition(actionsPosition + globalEditorWidgetData.duplicateIconPosition);
425 QRectF duplicateRect(iconDuplicatePosition,QSizeF(globalEditorWidgetData.buttonSize,globalEditorWidgetData.buttonSize));
426
427 if (duplicateRect.contains(uiMousePosition)) {
428 //create new assistant from old one
429 QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> handleMap;
430 m_newAssistant = assistant->clone(handleMap);
431 m_newAssistant->copySharedData(assistant);
432 m_newAssistant->setDuplicating(true);
433 //add assistant to global list and register UNDO/REDO object
434 m_canvas->paintingAssistantsDecoration()->addAssistant(m_newAssistant);
435 QList<KisPaintingAssistantSP> assistants = m_canvas->paintingAssistantsDecoration()->assistants();
437 m_canvas->viewManager()->undoAdapter()->addCommand(addAssistantCmd);
438 m_origAssistantList = KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants());
439 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
440 m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(m_newAssistant);
442
443 // if assistant is locked simply move the control widget, not the entire assistant
444 if(assistant->isLocked()) {
445 newAssistantAllowed = false;
447 m_dragStart = event->point;
448 m_dragEnd = event->point;
449 m_newAssistant.clear();
450 } else {
452 m_newAssistant.clear();
453 m_cursorStart = event->point;
455 m_dragStart = event->point;
456 m_dragEnd = event->point;
457 }
458
460 m_canvas->updateCanvas();
461 return;
462 }
463
464 }
465 if(globalEditorWidgetData.deleteButtonActivated) {
466 QPointF iconDeletePosition(actionsPosition + globalEditorWidgetData.deleteIconPosition);
467 QRectF deleteRect(iconDeletePosition, QSizeF(globalEditorWidgetData.buttonSize, globalEditorWidgetData.buttonSize));
468 if (deleteRect.contains(uiMousePosition) && !assistant->isLocked()) {
469 removeAssistant(assistant);
470 if(m_canvas->paintingAssistantsDecoration()->assistants().isEmpty()) {
473 m_canvas->updateCanvas();
474 return;
475 }
476
477 }
478 // if user clicking control widget background.
479 if((QRectF(actionsPosition + QPointF(10, 10), globalEditorWidgetData.boundingSize).adjusted(-2, -2, 2, 2).contains(uiMousePosition))) {
480 newAssistantAllowed = false;
482 assistantSelected(assistant);
483 m_dragStart = event->point;
484 m_dragEnd = event->point;
485
486 }
487
488
489 }
490
491 if (newAssistantAllowed==true) {//don't make a new assistant when I'm just toggling visibility//
492 QString key = m_options.availableAssistantsComboBox->model()->index( m_options.availableAssistantsComboBox->currentIndex(), 0 ).data(Qt::UserRole).toString();
493 KConfigGroup cfg = KSharedConfig::openConfig()->group(toolId());
494 cfg.writeEntry("AssistantType", key);
496 if (m_newAssistant->canBeLocal()) {
497 m_newAssistant->setLocal(m_options.localAssistantCheckbox->isChecked());
498 }
500 m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL);
501 if (m_newAssistant->numHandles() <= 1) {
502 addAssistant();
503 } else {
504 m_newAssistant->addHandle(new KisPaintingAssistantHandle(canvasDecoration->snapToGuide(event, QPointF(), false)), HandleType::NORMAL);
505 }
506 }
507
508 if (m_newAssistant) {
509 m_newAssistant->setAssistantGlobalColorCache(m_canvas->paintingAssistantsDecoration()->globalAssistantsColor());
510 }
511
512 m_canvas->updateCanvas();
513}
514
516{
517 KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration();
518
519 if (m_handleDrag) {
521 *m_handleDrag = event->point;
522
523 KisPaintingAssistantSP selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
524
525 if (!snap(event)) {
526 *m_handleDrag = canvasDecoration->snapToGuide(event, QPointF(), false);
527 }
529
530 m_handleCombine = 0;
531 if (!(event->modifiers() & Qt::ShiftModifier)) {
532 double minDist = 49.0;
533 QPointF mousePos = m_canvas->viewConverter()->documentToView(event->point);
534 Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) {
535 if (handle == m_handleDrag)
536 continue;
537
538
539 double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle));
540 if (dist < minDist) {
541 minDist = dist;
542 m_handleCombine = handle;
543 }
544 }
545 }
546 m_canvas->updateCanvas();
547 } else if (m_assistantDrag) {
548 QPointF newAdjustment = canvasDecoration->snapToGuide(event, QPointF(), false) - m_cursorStart;
549 if (event->modifiers() & Qt::ShiftModifier ) {
550 newAdjustment = snapToClosestNiceAngle(newAdjustment, QPointF(0, 0), M_PI / 4);
551 }
552 Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->handles()) {
553 *handle += (newAdjustment - m_currentAdjustment);
554 }
555 if (m_assistantDrag->id()== "vanishing point" || m_assistantDrag->id()== "two point"){
556 Q_FOREACH (KisPaintingAssistantHandleSP handle, m_assistantDrag->sideHandles()) {
557 *handle += (newAdjustment - m_currentAdjustment);
558 }
559 }
560 m_assistantDrag->uncache();
561 m_currentAdjustment = newAdjustment;
562 m_canvas->updateCanvas();
563 m_dragEnd = event->point;
564
566
567 KisPaintingAssistantSP selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
568 QPointF currentOffset = selectedAssistant->editorWidgetOffset();
569 selectedAssistant->setEditorWidgetOffset(currentOffset + (event->point - m_dragEnd));
570 m_dragEnd = event->point;
571
572 } else {
573 event->ignore();
574 }
575
576 bool wasHighlightedNode = m_highlightedNode != 0;
577 QPointF mousep = m_canvas->viewConverter()->documentToView(event->point);
578 QList <KisPaintingAssistantSP> pAssistant= m_canvas->paintingAssistantsDecoration()->assistants();
579
580 Q_FOREACH (KisPaintingAssistantSP assistant, pAssistant) {
581 if(assistant->id() == "perspective") {
582 if ((m_highlightedNode = assistant->closestCornerHandleFromPoint(mousep))) {
585 } else {
586 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
587 break;
588 }
589 }
590 }
591
592 //this following bit sets the translations for the vanishing-point handles.
593 if(m_handleDrag && assistant->id() == "vanishing point" && assistant->sideHandles().size()==4) {
594 //for inner handles, the outer handle gets translated.
595 if (m_handleDrag == assistant->sideHandles()[0]) {
596 QLineF perspectiveline = QLineF(*assistant->handles()[0],
597 *assistant->sideHandles()[0]);
598
599 qreal length = QLineF(*assistant->sideHandles()[0],
600 *assistant->sideHandles()[1]).length();
601
602 if (length < 2.0){
603 length = 2.0;
604 }
605
606 length += perspectiveline.length();
607 perspectiveline.setLength(length);
608 *assistant->sideHandles()[1] = perspectiveline.p2();
609 }
610 else if (m_handleDrag == assistant->sideHandles()[2]){
611 QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]);
612 qreal length = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length();
613
614 if (length<2.0){
615 length=2.0;
616 }
617
618 length += perspectiveline.length();
619 perspectiveline.setLength(length);
620 *assistant->sideHandles()[3] = perspectiveline.p2();
621 } // for outer handles, only the vanishing point is translated, but only if there's an intersection.
622 else if (m_handleDrag == assistant->sideHandles()[1]|| m_handleDrag == assistant->sideHandles()[3]){
623 QPointF vanishingpoint(0,0);
624 QLineF perspectiveline = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]);
625 QLineF perspectiveline2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]);
626
627 if (QLineF(perspectiveline2).intersects(QLineF(perspectiveline), &vanishingpoint) != QLineF::NoIntersection){
628 *assistant->handles()[0] = vanishingpoint;
629 }
630 }// and for the vanishing point itself, only the outer handles get translated.
631 else if (m_handleDrag == assistant->handles()[0]){
632 QLineF perspectiveline = QLineF(*assistant->handles()[0], *assistant->sideHandles()[0]);
633 QLineF perspectiveline2 = QLineF(*assistant->handles()[0], *assistant->sideHandles()[2]);
634 qreal length = QLineF(*assistant->sideHandles()[0], *assistant->sideHandles()[1]).length();
635 qreal length2 = QLineF(*assistant->sideHandles()[2], *assistant->sideHandles()[3]).length();
636
637 if (length < 2.0) {
638 length = 2.0;
639 }
640
641 if (length2 < 2.0) {
642 length2=2.0;
643 }
644
645 length += perspectiveline.length();
646 length2 += perspectiveline2.length();
647 perspectiveline.setLength(length);
648 perspectiveline2.setLength(length2);
649 *assistant->sideHandles()[1] = perspectiveline.p2();
650 *assistant->sideHandles()[3] = perspectiveline2.p2();
651 }
652
653 }
654 if (m_handleDrag && assistant->id() == "two point" && assistant->handles().size() >= 3 &&
655 assistant->sideHandles().size() == 8) {
656
657 QList<KisPaintingAssistantHandleSP> hndl = assistant->handles();
658 QList<KisPaintingAssistantHandleSP> side_hndl = assistant->sideHandles();
659
660 const bool far_handle_is_dragged =
661 m_handleDrag == side_hndl[1] || m_handleDrag == side_hndl[3] ||
662 m_handleDrag == side_hndl[5] || m_handleDrag == side_hndl[7];
663
664 if (far_handle_is_dragged) {
665 QLineF perspective_line_a, perspective_line_b;
666 QPointF vp_new_pos(0,0);
668 if (m_handleDrag == side_hndl[1] || m_handleDrag == side_hndl[5]) {
669 vp_moved = hndl[0];
670 perspective_line_a = QLineF(*side_hndl[0],*side_hndl[1]);
671 perspective_line_b = QLineF(*side_hndl[4],*side_hndl[5]);
672 } else {
673 vp_moved = hndl[1];
674 perspective_line_a = QLineF(*side_hndl[3],*side_hndl[2]);
675 perspective_line_b = QLineF(*side_hndl[6],*side_hndl[7]);
676 }
677 if (perspective_line_a.intersects(perspective_line_b, &vp_new_pos) != QLineF::NoIntersection) {
678 *vp_moved = vp_new_pos;
679 }
680 } else {
681 QLineF perspective_line_a1;
682 QLineF perspective_line_b1;
683 QLineF perspective_line_a2;
684 QLineF perspective_line_b2;
685
686 perspective_line_a1 = QLineF(*hndl[0], *side_hndl[0]);
687 perspective_line_a1.setLength(QLineF(*side_hndl[0],*side_hndl[1]).length());
688 perspective_line_a1.translate(*side_hndl[0] - perspective_line_a1.p1());
689 *side_hndl[1] = perspective_line_a1.p2();
690
691 perspective_line_b1 = QLineF(*hndl[0], *side_hndl[4]);
692 perspective_line_b1.setLength(QLineF(*side_hndl[4],*side_hndl[5]).length());
693 perspective_line_b1.translate(*side_hndl[4] - perspective_line_b1.p1());
694 *side_hndl[5] = perspective_line_b1.p2();
695
696 perspective_line_a2 = QLineF(*hndl[1], *side_hndl[2]);
697 perspective_line_a2.setLength(QLineF(*side_hndl[2],*side_hndl[3]).length());
698 perspective_line_a2.translate(*side_hndl[2] - perspective_line_a2.p1());
699 *side_hndl[3] = perspective_line_a2.p2();
700
701 perspective_line_b2 = QLineF(*hndl[1], *side_hndl[6]);
702 perspective_line_b2.setLength(QLineF(*side_hndl[6],*side_hndl[7]).length());
703 perspective_line_b2.translate(*side_hndl[6] - perspective_line_b2.p1());
704 *side_hndl[7] = perspective_line_b2.p2();
705 }
706 }
707 // The following section handles rulers that have been set to a
708 // constant length
709 if (m_handleDrag && (assistant->id() == "ruler" || assistant->id() == "infinite ruler")
710 && qSharedPointerCast<RulerAssistant>(assistant)->hasFixedLength()
711 && assistant->isAssistantComplete()) {
712
713 QSharedPointer<RulerAssistant> ruler = qSharedPointerCast<RulerAssistant>(assistant);
714
715 if (m_handleDrag == ruler->handles()[0]) {
716 // Dragging handle 1 moves the ruler
717 *ruler->handles()[1] += *ruler->handles()[0] - m_previousHandlePos;
718 }
719 else if (m_handleDrag == ruler->handles()[1]) {
720 // Dragging handle 2 only allows rotation
721 QPointF center = *ruler->handles()[0];
722 QPointF handle = *ruler->handles()[1];
723
724 QPointF direction = handle - center;
725 qreal distance = sqrt(KisPaintingAssistant::norm2(direction));
726 QPointF delta = direction / distance * ruler->fixedLength();
727
728 *ruler->handles()[1] = center + delta;
729 }
730 }
731 }
732 if (wasHighlightedNode && !m_highlightedNode) {
733 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
734 }
735}
736
738{
740 // release duplication button flag
742 KisPaintingAssistantSP selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
743 selectedAssistant->setDuplicating(false);
745
746 // offset control widget if user simple click-releases duplication button
747 if (m_dragEnd == m_dragStart) {
748 // calculate offset independent from canvas resolution and zoom
749 const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter();
750 // create offset unit, considering canvas resolution
751 const int offset = 20;
752 const float offsetUnit = offset / m_canvas->image()->xRes();
753 // use unit to calculate final offset, considering canvas zoom
754 QPointF widgetDuplicationOffset = QPointF(offsetUnit / converter->effectiveZoom(),offsetUnit / converter->effectiveZoom());
755 // apply offset to control widget
756 QPointF currentOffset = selectedAssistant->editorWidgetOffset();
757 selectedAssistant->setEditorWidgetOffset(currentOffset + widgetDuplicationOffset);
758 }
759 }
760
762 if (m_handleDrag) {
763 if (!(event->modifiers() & Qt::ShiftModifier) && m_handleCombine) {
766 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
767 }
769 } else {
770 m_assistantDrag.clear();
771 }
772 dbgUI << "creating undo command...";
773 KUndo2Command *command = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()));
774 m_canvas->viewManager()->undoAdapter()->addCommand(command);
775 dbgUI << "done";
777 addAssistant();
780 KisPaintingAssistantSP selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
781 QPointF currentOffset = selectedAssistant->editorWidgetOffset();
782 selectedAssistant->setEditorWidgetOffset(currentOffset + (event->point - m_dragEnd));
783 }
784 else {
785 event->ignore();
786 }
787
788 m_canvas->updateCanvas(); // TODO update only the relevant part of the canvas
789}
790
792{
793 m_canvas->paintingAssistantsDecoration()->addAssistant(m_newAssistant);
794
795 // generate the side handles for the Two Point assistant
796 if (m_newAssistant->id() == "two point"){
798 QSharedPointer<TwoPointAssistant> assis = qSharedPointerCast<TwoPointAssistant>(m_newAssistant);
799
800 if (*handles[0] == *handles[1] || *handles[1] == *handles[2]) {
801 // Place handles in sensible default position if any of
802 // them are overlapping (maybe because user
803 // double-clicked)
804 const QTransform transform = m_canvas->coordinatesConverter()->documentToWidgetTransform();
805 const QTransform inverted = transform.inverted();
806 const int size = inverted.map(QPointF(m_canvas->canvasWidget()->width(),0)).x();
807 *handles[0] = *handles[2] - QPointF(-size/3,0);
808 *handles[1] = *handles[2] - QPointF(size/3,0);
809 }
810
811 const QPointF p1 = *handles[0];
812 const QPointF p2 = *handles[1];
813 const QPointF p3 = *handles[2];
814
815 qreal size = 0;
816 QTransform t = assis->localTransform(p1,p2,p3,&size);
817 QTransform inv = t.inverted();
818
819 if (t.map(p1).x() * t.map(p2).x() > 0) {
820 // Put third handle between first and second if user
821 // placed it outside of them, then re-define the transform
822 const QLineF horizon = QLineF(t.map(p1),t.map(p2));
823 const QPointF origin = QPointF(horizon.center().x(),0);
824 *handles[2] = inv.map(origin);
825 t = assis->localTransform(p1,p2,*handles[2],&size);
826 inv = t.inverted();
827 }
828
829 const QPointF above = inv.map(QPointF(0,t.map(p1).y()+size));
830 const QPointF below = inv.map(QPointF(0,t.map(p1).y()-size));
831
832 Q_FOREACH (QPointF side, QList<QPointF>({above,below})) {
833 Q_FOREACH (QPointF vp, QList<QPointF>({p1, p2})) {
834 QLineF bar = QLineF(side, vp);
835 m_newAssistant->addHandle(new KisPaintingAssistantHandle(bar.pointAt(0.8)), HandleType::SIDE);
836 m_newAssistant->addHandle(new KisPaintingAssistantHandle(bar.pointAt(0.4)), HandleType::SIDE);
837 }
838 }
839 }
840
841
842 QList<KisPaintingAssistantSP> assistants = m_canvas->paintingAssistantsDecoration()->assistants();
844 m_canvas->viewManager()->undoAdapter()->addCommand(addAssistantCmd);
845
846 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
847 m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(m_newAssistant);
848 updateToolOptionsUI(); // vanishing point assistant will get an extra option
849
850 m_newAssistant.clear();
851}
852
854{
855
856 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
857 if( !globalEditorWidgetData.moveButtonActivated && !globalEditorWidgetData.snapButtonActivated && !globalEditorWidgetData.lockButtonActivated &&
858 !globalEditorWidgetData.duplicateButtonActivated && !globalEditorWidgetData.deleteButtonActivated) {
859 globalEditorWidgetData.widgetActivated = false;
860 } else globalEditorWidgetData.widgetActivated = true;
861
862 int horizontalButtonLimit = globalEditorWidgetData.horizontalButtonLimit;
863 int buttonCount = 0;
864 int horizontalButtonCount = 0;
865 int positionX = 15;
866 int positionY = 15;
867
868 //loop through all buttons and calculate positions
869 if(globalEditorWidgetData.moveButtonActivated) {
870
871 buttonCount++;
872 horizontalButtonCount++;
873 if(horizontalButtonCount > horizontalButtonLimit)
874 {
875 horizontalButtonCount = 1;
876 positionX = 15;
877 positionY += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
878 }
879 //the size icon is a little smaller than the others visually, so the icon is scaled
880 //and the positioin is adjusted by -5 to compinsate
881 globalEditorWidgetData.moveIconPosition.setX(positionX-5);
882 globalEditorWidgetData.moveIconPosition.setY(positionY-5);
883 positionX += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
884 }
885 if(globalEditorWidgetData.snapButtonActivated) {
886 buttonCount++;
887 horizontalButtonCount++;
888 if(horizontalButtonCount > horizontalButtonLimit)
889 {
890 horizontalButtonCount = 1;
891 positionX = 15;
892 positionY += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
893 }
894 globalEditorWidgetData.snapIconPosition.setX(positionX);
895 globalEditorWidgetData.snapIconPosition.setY(positionY);
896 positionX += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
897 }
898 if(globalEditorWidgetData.lockButtonActivated) {
899 buttonCount++;
900 horizontalButtonCount++;
901 if(horizontalButtonCount > horizontalButtonLimit)
902 {
903 horizontalButtonCount = 1;
904 positionX = 15;
905 positionY += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
906 }
907 globalEditorWidgetData.lockedIconPosition.setX(positionX);
908 globalEditorWidgetData.lockedIconPosition.setY(positionY);
909 positionX += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
910 }
911 if(globalEditorWidgetData.duplicateButtonActivated) {
912 buttonCount++;
913 horizontalButtonCount++;
914 if(horizontalButtonCount > horizontalButtonLimit)
915 {
916 horizontalButtonCount = 0;
917 positionX = 15;
918 positionY += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
919 }
920 globalEditorWidgetData.duplicateIconPosition.setX(positionX);
921 globalEditorWidgetData.duplicateIconPosition.setY(positionY);
922 positionX += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
923 }
924 if(globalEditorWidgetData.deleteButtonActivated) {
925 buttonCount++;
926 horizontalButtonCount++;
927 if(horizontalButtonCount > horizontalButtonLimit)
928 {
929 horizontalButtonCount = 1;
930 positionX = 15;
931 positionY += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
932 }
933 globalEditorWidgetData.deleteIconPosition.setX(positionX);
934 globalEditorWidgetData.deleteIconPosition.setY(positionY);
935 positionX += globalEditorWidgetData.buttonSize + globalEditorWidgetData.buttonPadding;
936 }
937
938 int buttonSection = globalEditorWidgetData.buttonSize+globalEditorWidgetData.buttonPadding;
939 int boundingWidgetWidth = (buttonCount < horizontalButtonLimit) ? buttonCount*buttonSection:horizontalButtonLimit*buttonSection;
940 boundingWidgetWidth += 5;
941 globalEditorWidgetData.boundingSize.setWidth(boundingWidgetWidth+globalEditorWidgetData.dragDecorationWidth);
942
943 int buttonToWidthRatio = (buttonCount/horizontalButtonLimit);
944 if(buttonCount%horizontalButtonLimit != 0) {
945 buttonToWidthRatio++;
946 }
947
948 int boundingWidgetHeight = buttonToWidthRatio*buttonSection;
949 boundingWidgetHeight += 5;
950 globalEditorWidgetData.boundingSize.setHeight(boundingWidgetHeight);
951
952
953 m_canvas->updateCanvasDecorations();
954}
955
957{
958 QList<KisPaintingAssistantSP> assistants = m_canvas->paintingAssistantsDecoration()->assistants();
959
960 m_canvas->paintingAssistantsDecoration()->removeAssistant(assistant);
961
962 KUndo2Command *removeAssistantCmd = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()), EditAssistantsCommand::REMOVE, assistants.indexOf(assistant));
963 m_canvas->viewManager()->undoAdapter()->addCommand(removeAssistantCmd);
964
965 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
966 m_canvas->paintingAssistantsDecoration()->deselectAssistant();
968}
969
971{
972 m_canvas->paintingAssistantsDecoration()->setSelectedAssistant(assistant);
974}
975
977{
978 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
979
980 bool hasActiveAssistant = m_selectedAssistant ? true : false;
981 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
982
983 if(globalEditorWidgetData.moveButtonActivated) {
984 m_options.showMove->setChecked(true);
985 }
986 if(globalEditorWidgetData.snapButtonActivated) {
987 m_options.showSnap->setChecked(true);
988 }
989 if(globalEditorWidgetData.lockButtonActivated) {
990 m_options.showLock->setChecked(true);
991 }
992 if(globalEditorWidgetData.duplicateButtonActivated) {
993 m_options.showDuplicate->setChecked(true);
994 }
995 if(globalEditorWidgetData.deleteButtonActivated) {
996 m_options.showDelete->setChecked(true);
997 }
998
999 if (m_selectedAssistant) {
1000 bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point";
1001 bool isTwoPointAssistant = m_selectedAssistant->id() == "two point";
1002 bool isRulerAssistant = m_selectedAssistant->id() == "ruler"
1003 || m_selectedAssistant->id() == "infinite ruler";
1004 bool isPerspectiveAssistant = m_selectedAssistant->id() == "perspective";
1005
1006 m_options.vanishingPointAngleSpinbox->setVisible(isVanishingPointAssistant);
1007 m_options.twoPointDensitySpinbox->setVisible(isTwoPointAssistant);
1008 m_options.twoPointUseVerticalCheckbox->setVisible(isTwoPointAssistant);
1009 m_options.subdivisionsSpinbox->setVisible(isRulerAssistant || isPerspectiveAssistant);
1010 m_options.minorSubdivisionsSpinbox->setVisible(isRulerAssistant);
1011 m_options.fixedLengthCheckbox->setVisible(isRulerAssistant);
1012 // show checkboxes for controlling which control widget buttons are visible
1013 m_options.showMove->setVisible(true);
1014 m_options.showSnap->setVisible(true);
1015 m_options.showLock->setVisible(true);
1016 m_options.showDuplicate->setVisible(true);
1017 m_options.showDelete->setVisible(true);
1018
1019
1020
1021 if (isVanishingPointAssistant) {
1022 QSharedPointer <VanishingPointAssistant> assis = qSharedPointerCast<VanishingPointAssistant>(m_selectedAssistant);
1023 m_options.vanishingPointAngleSpinbox->setValue(assis->referenceLineDensity());
1024 }
1025
1026 if (isTwoPointAssistant) {
1027 QSharedPointer <TwoPointAssistant> assis = qSharedPointerCast<TwoPointAssistant>(m_selectedAssistant);
1028 m_options.twoPointDensitySpinbox->setValue(assis->gridDensity());
1029 m_options.twoPointUseVerticalCheckbox->setChecked(assis->useVertical());
1030 }
1031
1032 if (isRulerAssistant) {
1033 QSharedPointer <RulerAssistant> assis = qSharedPointerCast<RulerAssistant>(m_selectedAssistant);
1034 m_options.subdivisionsSpinbox->setValue(assis->subdivisions());
1035 m_options.minorSubdivisionsSpinbox->setValue(assis->minorSubdivisions());
1036 m_options.fixedLengthCheckbox->setChecked(assis->hasFixedLength());
1037
1038 m_options.fixedLengthSpinbox->setVisible(assis->hasFixedLength());
1039 m_options.fixedLengthUnit->setVisible(assis->hasFixedLength());
1040 {
1041 // Block valueChanged signals during unit switch
1042 QSignalBlocker b(m_options.fixedLengthSpinbox);
1043 m_unitManager->setApparentUnitFromSymbol(assis->fixedLengthUnit());
1044 m_options.fixedLengthUnit->setCurrentIndex(m_unitManager->getApparentUnitId());
1045 m_options.fixedLengthSpinbox->changeValue(assis->fixedLength());
1046 }
1047 m_options.fixedLengthSpinbox->setPrefix("");
1048 } else {
1049 m_options.fixedLengthSpinbox->setVisible(false);
1050 m_options.fixedLengthUnit->setVisible(false);
1051 }
1052
1053 if (isPerspectiveAssistant) {
1054 QSharedPointer <PerspectiveAssistant> assis = qSharedPointerCast<PerspectiveAssistant>(m_selectedAssistant);
1055 m_options.subdivisionsSpinbox->setValue(assis->subdivisions());
1056 }
1057
1058 // load custom color settings from assistant (this happens when changing assistant
1059 m_options.useCustomAssistantColor->setChecked(m_selectedAssistant->useCustomColor());
1060 m_options.customAssistantColorButton->setColor(m_selectedAssistant->assistantCustomColor());
1061
1062
1063 double opacity = (double)m_selectedAssistant->assistantCustomColor().alpha()/(double)255.00 * (double)100.00 ;
1064 opacity = ceil(opacity); // helps keep the 0-100% slider from shifting
1065
1066 m_options.customColorOpacitySlider->blockSignals(true);
1067 m_options.customColorOpacitySlider->setValue((double)opacity);
1068 m_options.customColorOpacitySlider->blockSignals(false);
1069
1070 } else {
1071 m_options.vanishingPointAngleSpinbox->setVisible(false);
1072 m_options.twoPointDensitySpinbox->setVisible(false);
1073 m_options.twoPointUseVerticalCheckbox->setVisible(false);
1074 m_options.subdivisionsSpinbox->setVisible(false);
1075 m_options.minorSubdivisionsSpinbox->setVisible(false);
1076 m_options.fixedLengthCheckbox->setVisible(false);
1077 m_options.fixedLengthSpinbox->setVisible(false);
1078 m_options.fixedLengthUnit->setVisible(false);
1079 }
1080
1081 // show/hide elements if an assistant is selected or not
1082 m_options.useCustomAssistantColor->setVisible(hasActiveAssistant);
1083
1084 // hide custom color options if use custom color is not selected
1085 bool showCustomColorSettings = m_options.useCustomAssistantColor->isChecked() && hasActiveAssistant;
1086 m_options.customColorOpacitySlider->setVisible(showCustomColorSettings);
1087 m_options.customAssistantColorButton->setVisible(showCustomColorSettings);
1088
1089 // disable global color settings if we are using the custom color
1090 m_options.assistantsGlobalOpacitySlider->setEnabled(!showCustomColorSettings);
1091 m_options.assistantsColor->setEnabled(!showCustomColorSettings);
1092 m_options.globalColorLabel->setEnabled(!showCustomColorSettings);
1093
1094 QString key = m_options.availableAssistantsComboBox->model()->index( m_options.availableAssistantsComboBox->currentIndex(), 0 ).data(Qt::UserRole).toString();
1095 m_options.localAssistantCheckbox->setVisible(key == "two point" || key == "vanishing point" || key == "parallel ruler");
1096
1097}
1098
1100{
1101 if ( m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1102 return;
1103 }
1104
1105 // get the selected assistant and change the angle value
1106 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1107 if (m_selectedAssistant) {
1108 bool isVanishingPointAssistant = m_selectedAssistant->id() == "vanishing point";
1109
1110 if (isVanishingPointAssistant) {
1111 QSharedPointer <VanishingPointAssistant> assis = qSharedPointerCast<VanishingPointAssistant>(m_selectedAssistant);
1112 assis->setReferenceLineDensity((float)value);
1113 }
1114 }
1115
1116 m_canvas->updateCanvasDecorations();
1117}
1118
1120{
1121 if ( m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1122 return;
1123 }
1124
1125 // get the selected assistant and change the angle value
1126 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1127 if (m_selectedAssistant) {
1128 bool isTwoPointAssistant = m_selectedAssistant->id() == "two point";
1129
1130 if (isTwoPointAssistant) {
1131 QSharedPointer <TwoPointAssistant> assis = qSharedPointerCast<TwoPointAssistant>(m_selectedAssistant);
1132 assis->setGridDensity((float)value);
1133 }
1134 }
1135
1136 m_canvas->updateCanvasDecorations();
1137}
1138
1140{
1141 if ( m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1142 return;
1143 }
1144
1145 // get the selected assistant and change the angle value
1146 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1147 if (m_selectedAssistant) {
1148 bool isTwoPointAssistant = m_selectedAssistant->id() == "two point";
1149
1150 if (isTwoPointAssistant) {
1151 QSharedPointer <TwoPointAssistant> assis = qSharedPointerCast<TwoPointAssistant>(m_selectedAssistant);
1152 assis->setUseVertical(value == Qt::Checked);
1153 }
1154 }
1155
1156 m_canvas->updateCanvasDecorations();
1157}
1158
1160 if (m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1161 return;
1162 }
1163
1164 // get the selected assistant and change the angle value
1165 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1166 if (m_selectedAssistant) {
1167 bool isRulerAssistant = m_selectedAssistant->id() == "ruler"
1168 || m_selectedAssistant->id() == "infinite ruler";
1169 bool isPerspectiveAssistant = m_selectedAssistant->id() == "perspective";
1170
1171 if (isRulerAssistant) {
1172 QSharedPointer <RulerAssistant> assis = qSharedPointerCast<RulerAssistant>(m_selectedAssistant);
1173 assis->setSubdivisions(value);
1174
1175 m_options.minorSubdivisionsSpinbox->setEnabled(value > 0);
1176 }
1177
1178 else if (isPerspectiveAssistant) {
1179 QSharedPointer <PerspectiveAssistant> assis = qSharedPointerCast<PerspectiveAssistant>(m_selectedAssistant);
1180 assis->setSubdivisions(value);
1181 }
1182 }
1183
1184 m_canvas->updateCanvasDecorations();
1185}
1186
1188 if (m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1189 return;
1190 }
1191
1192 // get the selected assistant and change the angle value
1193 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1194 if (m_selectedAssistant) {
1195 bool isRulerAssistant = m_selectedAssistant->id() == "ruler"
1196 || m_selectedAssistant->id() == "infinite ruler";
1197
1198 if (isRulerAssistant) {
1199 QSharedPointer <RulerAssistant> assis = qSharedPointerCast<RulerAssistant>(m_selectedAssistant);
1200 assis->setMinorSubdivisions(value);
1201 }
1202 }
1203
1204 m_canvas->updateCanvasDecorations();
1205}
1206
1208 if (m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1209 return;
1210 }
1211
1212 // get the selected assistant and change the angle value
1213 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1214 if (m_selectedAssistant) {
1215 bool isRulerAssistant = m_selectedAssistant->id() == "ruler"
1216 || m_selectedAssistant->id() == "infinite ruler";
1217
1218 if (isRulerAssistant) {
1219 QSharedPointer <RulerAssistant> assis = qSharedPointerCast<RulerAssistant>(m_selectedAssistant);
1220
1221 m_options.fixedLengthSpinbox->setVisible(enabled);
1222 m_options.fixedLengthUnit->setVisible(enabled);
1223
1224 if (enabled && !assis->hasFixedLength() && assis->handles().size() >= 2) {
1225 // When enabling, set the length to the current length.
1226 QPointF a = *assis->handles()[0];
1227 QPointF b = *assis->handles()[1];
1228 qreal length = sqrt(KisPaintingAssistant::norm2(b - a));
1229 assis->setFixedLength(length);
1230 m_options.fixedLengthSpinbox->changeValue(length);
1231 }
1232
1233 assis->enableFixedLength(enabled);
1234 }
1235 }
1236
1237 m_canvas->updateCanvasDecorations();
1238}
1239
1241 // This slot should only be called when the user actually changes the
1242 // value, not when it is changed through unit conversions. Use
1243 // QSignalBlocker on the spin box when converting units to achieve this.
1244
1245 if (m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1246 return;
1247 }
1248
1249 // get the selected assistant and change the angle value
1250 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1251 if (m_selectedAssistant) {
1252 bool isRulerAssistant = m_selectedAssistant->id() == "ruler"
1253 || m_selectedAssistant->id() == "infinite ruler";
1254
1255 if (isRulerAssistant) {
1256 QSharedPointer <RulerAssistant> assis = qSharedPointerCast<RulerAssistant>(m_selectedAssistant);
1257 // Stores the newly entered data (length & unit) in the assistant
1258 assis->setFixedLengthUnit(m_unitManager->getApparentUnitSymbol());
1259 assis->setFixedLength(m_options.fixedLengthSpinbox->value());
1260 assis->ensureLength();
1261 m_options.fixedLengthSpinbox->setPrefix("");
1262 }
1263 }
1264
1265 m_canvas->updateCanvasDecorations();
1266}
1267
1269 if (m_canvas->paintingAssistantsDecoration()->assistants().length() == 0) {
1270 return;
1271 }
1272
1273 // get the selected assistant and change the angle value
1274 KisPaintingAssistantSP m_selectedAssistant =
1275 m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1276 if (m_selectedAssistant) {
1277 bool isRulerAssistant = m_selectedAssistant->id() == "ruler" ||
1278 m_selectedAssistant->id() == "infinite ruler";
1279
1280 if (isRulerAssistant) {
1281 QSharedPointer<RulerAssistant> assis = qSharedPointerCast<RulerAssistant>(m_selectedAssistant);
1282
1283 // Saving and restoring the actual length manually, since the
1284 // unit manager rounds without warning during conversion
1285 qreal current_length = assis->fixedLength();
1286 {
1287 // Block signals from the spinbox while changing the unit to
1288 // avoid edits via valueChanged() in the meantime
1289 QSignalBlocker b(m_options.fixedLengthSpinbox);
1291 m_options.fixedLengthSpinbox->changeValue(current_length);
1292 }
1293
1294 QString unit = m_unitManager->getApparentUnitSymbol();
1295
1296 if (unit == assis->fixedLengthUnit()) {
1297 // If the previous unit is selected, show no prefix: perfect match
1298 m_options.fixedLengthSpinbox->setPrefix("");
1299 }
1300 else {
1301 if (abs(m_options.fixedLengthSpinbox->value() - current_length) > 1e-3) {
1302 // If the units don't match, show the approximate symbol as prefix
1303 m_options.fixedLengthSpinbox->setPrefix(u8"\u2248");
1304 } else {
1305 // If the units don't match but converted perfectly
1306 // (close enough: +- 0.001 pt), show equals instead
1307 m_options.fixedLengthSpinbox->setPrefix("=");
1308 }
1309 }
1310 }
1311 }
1312}
1313
1315{
1316 m_handleHover = 0;
1318
1319 KisPaintingAssistantHandleSP new_handle = m_newAssistant->handles().back();
1320 if (!snap(event)) {
1321 KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration();
1322 *new_handle = canvasDecoration->snapToGuide(event, QPointF(), false);
1323 }
1324
1326 QPointF translate = event->point - m_dragEnd;
1327 m_dragEnd = event->point;
1328 m_selectedNode1.data()->operator = (QPointF(m_selectedNode1.data()->x(),m_selectedNode1.data()->y()) + translate);
1329 m_selectedNode2.data()->operator = (QPointF(m_selectedNode2.data()->x(),m_selectedNode2.data()->y()) + translate);
1330 } else if (mode() == KisTool::HOVER_MODE) {
1331
1332 // find a handle underneath...
1333 double minDist = m_handleMaxDist;
1334
1335 QPointF mousePos = m_canvas->viewConverter()->documentToView(event->point);
1336
1337 Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) {
1338 QList<KisPaintingAssistantHandleSP> allAssistantHandles;
1339 allAssistantHandles.append(assistant->handles());
1340 allAssistantHandles.append(assistant->sideHandles());
1341
1342 Q_FOREACH (const KisPaintingAssistantHandleSP handle, allAssistantHandles) {
1343
1344 double dist = KisPaintingAssistant::norm2(mousePos - m_canvas->viewConverter()->documentToView(*handle));
1345 if (dist < minDist) {
1346 minDist = dist;
1347 m_handleHover = handle;
1348 }
1349 }
1350 }
1351 }
1352
1353 m_canvas->updateCanvasDecorations();
1354}
1355
1356
1358{
1359 // When the user is in the middle of creating a new
1360 // assistant the escape key can be used to cancel this process.
1361 if (event->key()==Qt::Key_Escape && (m_newAssistant)) {
1362 // Clear shared pointer to the assistant being created so
1363 // it gets cleaned-up
1364 m_newAssistant.clear();
1365 m_canvas->updateCanvas();
1366 event->accept();
1367 } else {
1368 event->ignore();
1369 }
1370}
1371
1372void KisAssistantTool::paint(QPainter& _gc, const KoViewConverter &_converter)
1373{
1374 QRectF canvasSize = QRectF(QPointF(0, 0), QSizeF(m_canvas->image()->size()));
1375
1376 // show special display while a new assistant is in the process of being created
1377 if (m_newAssistant) {
1378
1379 QColor assistantColor = m_canvas->displayRendererInterface()->convertColorToDisplayColorSpace(KoColor(m_newAssistant->effectiveAssistantColor(), KoColorSpaceRegistry::instance()->rgb8()));
1380 assistantColor.setAlpha(80);
1381
1382 m_newAssistant->drawAssistant(_gc, canvasSize, m_canvas->coordinatesConverter(), m_canvas->displayRendererInterface(), false, m_canvas, true, false);
1383 Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_newAssistant->handles()) {
1384 QPainterPath path;
1385 path.addEllipse(QRectF(_converter.documentToView(*handle) - QPointF(m_handleSize * 0.5, m_handleSize * 0.5), QSizeF(m_handleSize, m_handleSize)));
1386
1387 _gc.save();
1388 _gc.setPen(Qt::NoPen);
1389 _gc.setBrush(assistantColor);
1390 _gc.drawPath(path);
1391 _gc.restore();
1392 }
1393 }
1394
1395
1396 Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) {
1397
1398 QColor assistantColor = m_canvas->displayRendererInterface()->convertColorToDisplayColorSpace(KoColor(assistant->effectiveAssistantColor(), KoColorSpaceRegistry::instance()->rgb8()));
1399 assistantColor.setAlpha(80);
1400
1401 Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) {
1402 QRectF ellipse(_converter.documentToView(*handle) - QPointF(m_handleSize * 0.5, m_handleSize * 0.5),
1403 QSizeF(m_handleSize, m_handleSize));
1404
1405 // render handles differently if it is the one being dragged.
1406 if (handle == m_handleDrag || handle == m_handleCombine || (handle == m_handleHover && !handle->chiefAssistant()->isLocked())) {
1407 QPen stroke(assistantColor, 4);
1408 _gc.save();
1409 _gc.setPen(stroke);
1410 _gc.setBrush(Qt::NoBrush);
1411 _gc.drawEllipse(ellipse);
1412 _gc.restore();
1413 }
1414
1415 }
1416 }
1417}
1418
1420{
1421 m_origAssistantList = m_canvas->paintingAssistantsDecoration()->assistants();
1422
1423 m_canvas->paintingAssistantsDecoration()->removeAll();
1424
1425 KUndo2Command *removeAssistantCmd = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()));
1426 m_canvas->viewManager()->undoAdapter()->addCommand(removeAssistantCmd);
1427
1428 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
1429 m_canvas->updateCanvas();
1430
1431 m_canvas->paintingAssistantsDecoration()->deselectAssistant();
1433}
1434
1436{
1437 KoFileDialog dialog(m_canvas->viewManager()->mainWindowAsQWidget(), KoFileDialog::OpenFile, "OpenAssistant");
1438 dialog.setCaption(i18n("Select an Assistant"));
1439 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1440 dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant");
1441 QString filename = dialog.filename();
1442
1443 QFile file(filename);
1444 if (!file.open(QIODevice::ReadOnly)) { return; }
1445
1446 QByteArray data = file.readAll();
1447 QXmlStreamReader xml(data);
1448 QMap<int, KisPaintingAssistantHandleSP> handleMap;
1449 QMap<int, KisPaintingAssistantHandleSP> sideHandleMap;
1450 KisPaintingAssistantSP assistant;
1451 bool errors = false;
1452
1453 m_origAssistantList = KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants());
1454
1455
1456 while (!xml.atEnd()) {
1457 switch (xml.readNext()) {
1458 case QXmlStreamReader::StartElement:
1459 if (xml.name() == "handle") {
1460 if (assistant && !xml.attributes().value("ref").isEmpty()) {
1461 KisPaintingAssistantHandleSP handle = handleMap.value(xml.attributes().value("ref").toString().toInt());
1462 if (handle) {
1463 assistant->addHandle(handle, HandleType::NORMAL);
1464 } else {
1465 errors = true;
1466 }
1467 } else {
1468 QString strId = xml.attributes().value("id").toString(),
1469 strX = xml.attributes().value("x").toString(),
1470 strY = xml.attributes().value("y").toString();
1471
1472
1473
1474 if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) {
1475 int id = strId.toInt();
1476 double x = strX.toDouble(),
1477 y = strY.toDouble();
1478 if (!handleMap.contains(id)) {
1479 handleMap.insert(id, new KisPaintingAssistantHandle(x, y));
1480 } else {
1481 errors = true;
1482 }
1483 } else {
1484 errors = true;
1485 }
1486 }
1487 // for vanishing point assistant
1488 } else if (xml.name() == "sidehandle"){
1489
1490 // read in sidehandles
1491 if (!xml.attributes().value("id").isEmpty()) {
1492 QString strId = xml.attributes().value("id").toString(),
1493 strX = xml.attributes().value("x").toString(),
1494 strY = xml.attributes().value("y").toString();
1495 if (!strId.isEmpty() && !strX.isEmpty() && !strY.isEmpty()) {
1496 int id = strId.toInt();
1497 double x = strX.toDouble();
1498 double y = strY.toDouble();
1499 if (!sideHandleMap.contains(id)) {
1500 sideHandleMap.insert(id, new KisPaintingAssistantHandle(x,y));
1501 }}
1502 }
1503 // addHandle to assistant
1504 if (!xml.attributes().value("ref").isEmpty() && assistant) {
1505 KisPaintingAssistantHandleSP handle = sideHandleMap.value(xml.attributes().value("ref").toString().toInt());
1506 if (handle) {
1507 assistant->addHandle(handle, HandleType::SIDE);
1508 }
1509 }
1510
1511 } else if (xml.name() == "assistant") {
1512 const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(xml.attributes().value("type").toString());
1513
1514 if (factory) {
1515 if (assistant) {
1516 errors = true;
1517 assistant.clear();
1518 }
1519 assistant = toQShared(factory->createPaintingAssistant());
1520 } else {
1521 errors = true;
1522 }
1523
1524 if (assistant) {
1525 // load custom shared assistant properties
1526 if (xml.attributes().hasAttribute("useCustomColor")) {
1527 auto useCustomColor = xml.attributes().value("useCustomColor");
1528
1529 bool usingColor = false;
1530 if (useCustomColor.toString() == "1") {
1531 usingColor = true;
1532 }
1533 assistant->setUseCustomColor(usingColor);
1534 }
1535
1536 if ( xml.attributes().hasAttribute("useCustomColor")) {
1537 auto customColor = xml.attributes().value("customColor");
1538 assistant->setAssistantCustomColor( KisDomUtils::qStringToQColor(customColor.toString()) );
1539
1540 }
1541 }
1542 }
1543
1544 if (assistant) {
1545 assistant->loadCustomXml(&xml);
1546 }
1547
1548
1549 break;
1550 case QXmlStreamReader::EndElement:
1551 if (xml.name() == "assistant") {
1552 if (assistant) {
1553 if (assistant->handles().size() == assistant->numHandles()) {
1554 if (assistant->id() == "vanishing point" && sideHandleMap.empty()){
1555 // Create side handles if the saved vp assistant doesn't have any.
1556 QPointF pos = *assistant->handles()[0];
1557 assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-70,0)), HandleType::SIDE);
1558 assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(-140,0)), HandleType::SIDE);
1559 assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(70,0)), HandleType::SIDE);
1560 assistant->addHandle(new KisPaintingAssistantHandle(pos+QPointF(140,0)), HandleType::SIDE);
1561 }
1562 m_canvas->paintingAssistantsDecoration()->addAssistant(assistant);
1563 } else {
1564 errors = true;
1565 }
1566 assistant.clear();
1567 }
1568 }
1569
1570 break;
1571 default:
1572 break;
1573 }
1574
1575 }
1576 if (assistant) {
1577 errors = true;
1578 assistant.clear();
1579 }
1580 if (xml.hasError()) {
1581 QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita"), xml.errorString());
1582 }
1583 if (errors) {
1584 QMessageBox::warning(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Errors were encountered. Not all assistants were successfully loaded."));
1585 }
1586
1587 KUndo2Command *command = new EditAssistantsCommand(m_canvas, m_origAssistantList, KisPaintingAssistant::cloneAssistantList(m_canvas->paintingAssistantsDecoration()->assistants()));
1588 m_canvas->viewManager()->undoAdapter()->addCommand(command);
1589
1590 m_handles = m_canvas->paintingAssistantsDecoration()->handles();
1591 m_canvas->updateCanvas();
1592}
1593
1595{
1596
1597 if (m_handles.isEmpty()) return;
1598
1599 QByteArray data;
1600 QXmlStreamWriter xml(&data);
1601 xml.writeStartDocument();
1602 xml.writeStartElement("paintingassistant");
1603 xml.writeAttribute("color",
1605 m_canvas->paintingAssistantsDecoration()->globalAssistantsColor())); // global color if no custom color used
1606
1607
1608 xml.writeStartElement("handles");
1609 QMap<KisPaintingAssistantHandleSP, int> handleMap;
1610 Q_FOREACH (const KisPaintingAssistantHandleSP handle, m_handles) {
1611 int id = handleMap.size();
1612 handleMap.insert(handle, id);
1613 xml.writeStartElement("handle");
1614 //xml.writeAttribute("type", handle->handleType());
1615 xml.writeAttribute("id", QString::number(id));
1616 xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3));
1617 xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3));
1618 xml.writeEndElement();
1619 }
1620 xml.writeEndElement();
1621 xml.writeStartElement("sidehandles");
1622 QMap<KisPaintingAssistantHandleSP, int> sideHandleMap;
1623 Q_FOREACH (KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) {
1624 Q_FOREACH (KisPaintingAssistantHandleSP handle, assistant->sideHandles()) {
1625 int id = sideHandleMap.size();
1626 sideHandleMap.insert(handle, id);
1627 xml.writeStartElement("sidehandle");
1628 xml.writeAttribute("id", QString::number(id));
1629 xml.writeAttribute("x", QString::number(double(handle->x()), 'f', 3));
1630 xml.writeAttribute("y", QString::number(double(handle->y()), 'f', 3));
1631 xml.writeEndElement();
1632 }
1633 }
1634 xml.writeStartElement("assistants");
1635
1636
1637 Q_FOREACH (const KisPaintingAssistantSP assistant, m_canvas->paintingAssistantsDecoration()->assistants()) {
1638 xml.writeStartElement("assistant");
1639 xml.writeAttribute("type", assistant->id());
1640 xml.writeAttribute("useCustomColor", QString::number(assistant->useCustomColor()));
1641 xml.writeAttribute("customColor", KisDomUtils::qColorToQString(assistant->assistantCustomColor()));
1642
1643
1644
1645 // custom assistant properties like angle density on vanishing point
1646 assistant->saveCustomXml(&xml);
1647
1648 // handle information
1649 xml.writeStartElement("handles");
1650 Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->handles()) {
1651 xml.writeStartElement("handle");
1652 xml.writeAttribute("ref", QString::number(handleMap.value(handle)));
1653 xml.writeEndElement();
1654 }
1655 xml.writeEndElement();
1656 if (!sideHandleMap.empty()) {
1657 xml.writeStartElement("sidehandles");
1658 Q_FOREACH (const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) {
1659 xml.writeStartElement("sidehandle");
1660 xml.writeAttribute("ref", QString::number(sideHandleMap.value(handle)));
1661 xml.writeEndElement();
1662 }
1663 xml.writeEndElement();
1664 }
1665 xml.writeEndElement();
1666 }
1667 xml.writeEndElement();
1668 xml.writeEndElement();
1669 xml.writeEndDocument();
1670
1671 KoFileDialog dialog(m_canvas->viewManager()->mainWindowAsQWidget(), KoFileDialog::SaveFile, "OpenAssistant");
1672 dialog.setCaption(i18n("Save Assistant"));
1673 dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
1674 dialog.setMimeTypeFilters(QStringList() << "application/x-krita-assistant", "application/x-krita-assistant");
1675 QString filename = dialog.filename();
1676 if (filename.isEmpty()) return;
1677
1678 QFile file(filename);
1679 if (file.open(QIODevice::WriteOnly)) {
1680 file.write(data);
1681 } else {
1682 qWarning() << "Couldn't open file" << file.fileName() << "for writing:" << file.errorString();
1683 }
1684}
1685
1687{
1688 if (!m_optionsWidget) {
1689 m_optionsWidget = new QWidget;
1690 m_options.setupUi(m_optionsWidget);
1691
1692 KConfigGroup cfg = KSharedConfig::openConfig()->group(toolId());
1693
1694 // See https://bugs.kde.org/show_bug.cgi?id=316896
1695 QWidget *specialSpacer = new QWidget(m_optionsWidget);
1696 specialSpacer->setObjectName("SpecialSpacer");
1697 specialSpacer->setFixedSize(0, 0);
1698 m_optionsWidget->layout()->addWidget(specialSpacer);
1699
1700 m_options.loadAssistantButton->setIcon(KisIconUtils::loadIcon("folder"));
1701 m_options.loadAssistantButton->setIconSize(QSize(16, 16));
1702 m_options.saveAssistantButton->setIcon(KisIconUtils::loadIcon("document-save-16"));
1703 m_options.saveAssistantButton->setIconSize(QSize(16, 16));
1704 m_options.deleteAllAssistantsButton->setIcon(KisIconUtils::loadIcon("edit-delete"));
1705 m_options.deleteAllAssistantsButton->setIconSize(QSize(16, 16));
1706
1707 m_options.showDockerOptionsButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
1708 m_options.showDockerOptionsButton->setIconSize(QSize(16, 16));
1709
1710 QList<KoID> assistants;
1711 Q_FOREACH (const QString& key, KisPaintingAssistantFactoryRegistry::instance()->keys()) {
1712 QString name = KisPaintingAssistantFactoryRegistry::instance()->get(key)->name();
1713 assistants << KoID(key, name);
1714 }
1715 std::sort(assistants.begin(), assistants.end(), KoID::compareNames);
1716
1717 QString currentAssistantType = cfg.readEntry("AssistantType", "two point");
1718 int i = 0;
1719 int currentAssistantIndex = 0;
1720 Q_FOREACH(const KoID &id, assistants) {
1721 m_options.availableAssistantsComboBox->addItem(id.name(), id.id());
1722 if (id.id() == currentAssistantType) {
1723 currentAssistantIndex = i;
1724 }
1725 i++;
1726 }
1727 m_options.availableAssistantsComboBox->setCurrentIndex(currentAssistantIndex);
1728
1729 connect(m_options.availableAssistantsComboBox, SIGNAL(currentIndexChanged(int)), SLOT(slotSelectedAssistantTypeChanged()));
1730
1731 connect(m_options.saveAssistantButton, SIGNAL(clicked()), SLOT(saveAssistants()));
1732 connect(m_options.loadAssistantButton, SIGNAL(clicked()), SLOT(loadAssistants()));
1733 connect(m_options.deleteAllAssistantsButton, SIGNAL(clicked()), SLOT(removeAllAssistants()));
1734
1735 connect(m_options.assistantsColor, SIGNAL(changed(QColor)), SLOT(slotGlobalAssistantsColorChanged(QColor)));
1736 connect(m_options.assistantsGlobalOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotGlobalAssistantOpacityChanged()));
1737
1738 connect(m_options.vanishingPointAngleSpinbox, SIGNAL(valueChanged(double)), this, SLOT(slotChangeVanishingPointAngle(double)));
1739 connect(m_options.twoPointDensitySpinbox, SIGNAL(valueChanged(double)), this, SLOT(slotChangeTwoPointDensity(double)));
1740 connect(m_options.twoPointUseVerticalCheckbox, SIGNAL(stateChanged(int)), this, SLOT(slotChangeTwoPointUseVertical(int)));
1741
1742 connect(m_options.subdivisionsSpinbox, SIGNAL(valueChanged(int)), this, SLOT(slotChangeSubdivisions(int)));
1743 connect(m_options.minorSubdivisionsSpinbox, SIGNAL(valueChanged(int)), this, SLOT(slotChangeMinorSubdivisions(int)));
1744 connect(m_options.fixedLengthCheckbox, SIGNAL(stateChanged(int)), this, SLOT(slotEnableFixedLength(int)));
1745 connect(m_options.fixedLengthSpinbox, SIGNAL(valueChangedPt(double)), this, SLOT(slotChangeFixedLength(double)));
1746
1747 //update EditorWidgetData when checkbox clicked
1748 connect(m_options.showMove, SIGNAL(stateChanged(int)), this, SLOT(slotToggleMoveButton(int)));
1749 connect(m_options.showSnap, SIGNAL(stateChanged(int)), this, SLOT(slotToggleSnapButton(int)));
1750 connect(m_options.showLock, SIGNAL(stateChanged(int)), this, SLOT(slotToggleLockButton(int)));
1751 connect(m_options.showDuplicate, SIGNAL(stateChanged(int)), this, SLOT(slotToggleDuplicateButton(int)));
1752 connect(m_options.showDelete, SIGNAL(stateChanged(int)), this, SLOT(slotToggleDeleteButton(int)));
1753
1754 // initialize UI elements with existing data if possible
1755 if (m_canvas && m_canvas->paintingAssistantsDecoration()) {
1756 const QColor color = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor();
1757
1758 QColor opaqueColor = color;
1759 opaqueColor.setAlpha(255);
1760
1761 m_options.assistantsColor->setColor(opaqueColor);
1762 m_options.customAssistantColorButton->setColor(opaqueColor);
1763 m_options.assistantsGlobalOpacitySlider->setValue(color.alphaF() * 100.0);
1764
1765 } else {
1766 m_options.assistantsColor->setColor(QColor(176, 176, 176, 255)); // grey default for all assistants
1767 m_options.assistantsGlobalOpacitySlider->setValue(100); // 100%
1768 }
1769
1770 m_options.assistantsGlobalOpacitySlider->setPrefix(i18n("Opacity: "));
1771 m_options.assistantsGlobalOpacitySlider->setSuffix(" %");
1772
1773
1774 // custom color of selected assistant
1775 m_options.customColorOpacitySlider->setValue(100); // 100%
1776 m_options.customColorOpacitySlider->setPrefix(i18n("Opacity: "));
1777 m_options.customColorOpacitySlider->setSuffix(" %");
1778
1779 connect(m_options.useCustomAssistantColor, SIGNAL(clicked(bool)), this, SLOT(slotUpdateCustomColor()));
1780 connect(m_options.customAssistantColorButton, SIGNAL(changed(QColor)), this, SLOT(slotUpdateCustomColor()));
1781 connect(m_options.customColorOpacitySlider, SIGNAL(valueChanged(int)), SLOT(slotCustomOpacityChanged()));
1782
1783 m_options.twoPointDensitySpinbox->setPrefix(i18n("Density: "));
1784 m_options.twoPointDensitySpinbox->setRange(0.1, 4.0, 2);
1785 m_options.twoPointDensitySpinbox->setSingleStep(0.1);
1786
1787 m_options.vanishingPointAngleSpinbox->setPrefix(i18n("Density: "));
1788 m_options.vanishingPointAngleSpinbox->setSuffix(QStringLiteral("°"));
1789 m_options.vanishingPointAngleSpinbox->setRange(1.0, 180.0);
1790 m_options.vanishingPointAngleSpinbox->setSingleStep(1.0);
1791
1792 m_options.subdivisionsSpinbox->setPrefix(i18n("Subdivisions: "));
1793 m_options.subdivisionsSpinbox->setRange(0, 100);
1794 m_options.subdivisionsSpinbox->setSoftRange(0, 20);
1795
1796 m_options.minorSubdivisionsSpinbox->setPrefix(i18n("Minor Subdivisions: "));
1797 m_options.minorSubdivisionsSpinbox->setRange(1, 5);
1798
1800 m_options.fixedLengthSpinbox->setUnitManager(m_unitManager);
1801 m_options.fixedLengthSpinbox->setDisplayUnit(false);
1802 m_options.fixedLengthSpinbox->setMinimum(0);
1803 m_options.fixedLengthUnit->setModel(m_unitManager);
1805 connect(m_options.fixedLengthUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChangeFixedLengthUnit(int)));
1806 connect(m_unitManager, SIGNAL(unitChanged(int)), m_options.fixedLengthUnit, SLOT(setCurrentIndex(int)));
1807
1808 m_options.vanishingPointAngleSpinbox->setVisible(false);
1809 m_options.twoPointDensitySpinbox->setVisible(false);
1810 m_options.subdivisionsSpinbox->setVisible(false);
1811 m_options.minorSubdivisionsSpinbox->setVisible(false);
1812 m_options.fixedLengthCheckbox->setVisible(false);
1813 m_options.fixedLengthSpinbox->setVisible(false);
1814 m_options.fixedLengthUnit->setVisible(false);
1815
1816
1817 m_options.localAssistantCheckbox->setChecked(cfg.readEntry("LimitAssistantToArea", false));
1818
1819 connect(m_options.localAssistantCheckbox, SIGNAL(stateChanged(int)), SLOT(slotLocalAssistantCheckboxChanged()));
1820
1821 // show panel for docker tool option visibility
1822 connect(m_options.showDockerOptionsButton, SIGNAL(clicked()), this, SLOT(slotToggleDockToolOptionsVisible()));
1823 // set control widget buttons on first startup.
1824 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
1825
1826
1827 if (globalEditorWidgetData.moveButtonActivated) {
1828 m_options.showMove->setChecked(true);
1829 }
1830 if (globalEditorWidgetData.snapButtonActivated) {
1831 m_options.showSnap->setChecked(true);
1832 }
1833 if (globalEditorWidgetData.lockButtonActivated) {
1834 m_options.showLock->setChecked(true);
1835 }
1836 if (globalEditorWidgetData.duplicateButtonActivated) {
1837 m_options.showDuplicate->setChecked(true);
1838 }
1839 if (globalEditorWidgetData.deleteButtonActivated) {
1840 m_options.showDelete->setChecked(true);
1841 }
1842 }
1843
1845
1846 return m_optionsWidget;
1847}
1848
1850{
1851 // color and alpha are stored separately, so we need to merge the values before sending it on
1852 int oldAlpha = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor().alpha();
1853
1854 QColor newColor = setColor;
1855 newColor.setAlpha(oldAlpha);
1856
1857 m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor);
1858
1859 m_canvas->paintingAssistantsDecoration()->uncache();
1860 m_canvas->updateCanvasDecorations();
1861}
1862
1864{
1865 QColor newColor = m_canvas->paintingAssistantsDecoration()->globalAssistantsColor();
1866 qreal newOpacity = m_options.assistantsGlobalOpacitySlider->value() * 0.01 * 255.0;
1867 newColor.setAlpha(int(newOpacity));
1868 m_canvas->paintingAssistantsDecoration()->setGlobalAssistantsColor(newColor);
1869
1870 m_canvas->paintingAssistantsDecoration()->uncache();
1871 m_canvas->updateCanvasDecorations();
1872}
1873
1875{
1876 // get the selected assistant and change the angle value
1877 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1878 if (m_selectedAssistant) {
1879 m_selectedAssistant->setUseCustomColor(m_options.useCustomAssistantColor->isChecked());
1880
1881 // changing color doesn't keep alpha, so update that before we send it on
1882 QColor newColor = m_options.customAssistantColorButton->color();
1883 newColor.setAlpha(m_selectedAssistant->assistantCustomColor().alpha());
1884
1885 m_selectedAssistant->setAssistantCustomColor(newColor);
1886 m_selectedAssistant->uncache();
1887 }
1888
1890 m_canvas->updateCanvasDecorations();
1891}
1892
1894{
1895 KisPaintingAssistantSP m_selectedAssistant = m_canvas->paintingAssistantsDecoration()->selectedAssistant();
1896 if (m_selectedAssistant) {
1897 QColor newColor = m_selectedAssistant->assistantCustomColor();
1898 qreal newOpacity = m_options.customColorOpacitySlider->value() * 0.01 * 255.0;
1899 newColor.setAlpha(int(newOpacity));
1900 m_selectedAssistant->setAssistantCustomColor(newColor);
1901 m_selectedAssistant->uncache();
1902 }
1903
1904 // this forces the canvas to refresh to see the changes immediately
1905 m_canvas->paintingAssistantsDecoration()->uncache();
1906 m_canvas->updateCanvasDecorations();
1907}
1908
1910{
1911 KConfigGroup cfg = KSharedConfig::openConfig()->group(toolId());
1912 cfg.writeEntry("LimitAssistantToArea", m_options.localAssistantCheckbox->isChecked());
1913}
1914
1920
1922 m_options.showDockerOptionsPanel->setVisible(ToggleDockToolOptionsVisible);
1923
1925 m_options.showDockerOptionsButton->setIcon(KisIconUtils::loadIcon("arrow-down"));
1926 }
1927 else{
1928 m_options.showDockerOptionsButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
1929 }
1930
1931 m_options.showDockerOptionsButton->setIconSize(QSize(16, 16));
1932
1933}
1935{
1936 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
1937 globalEditorWidgetData.moveButtonActivated = (index > 0) ? true:false;
1939}
1941{
1942 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
1943 globalEditorWidgetData.snapButtonActivated = (index > 0) ? true:false;
1945}
1947{
1948 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
1949 globalEditorWidgetData.lockButtonActivated = (index > 0) ? true:false;
1951}
1953{
1954 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
1955 globalEditorWidgetData.duplicateButtonActivated = (index > 0) ? true:false;
1957}
1959{
1960 AssistantEditorData &globalEditorWidgetData = m_canvas->paintingAssistantsDecoration()->globalEditorWidgetData;
1961 globalEditorWidgetData.deleteButtonActivated = (index > 0) ? true:false;
1963}
1964
1966{
1967 Q_UNUSED(action);
1968 beginActionImpl(event);
1969}
1970
1972{
1973 Q_UNUSED(action);
1974 continueActionImpl(event);
1975}
1976
1978{
1979 Q_UNUSED(action);
1980 endActionImpl(event);
1981}
1982
1987
1992
1997
1999{
2000 if (event->modifiers() == Qt::NoModifier) {
2001 return false;
2002 }
2003
2004 if (m_handleDrag) {
2005 KisPaintingAssistantsDecorationSP canvasDecoration = m_canvas->paintingAssistantsDecoration();
2006 KisPaintingAssistantSP selectedAssistant = canvasDecoration->selectedAssistant();
2007 QList<KisPaintingAssistantHandleSP> handles = selectedAssistant->handles();
2008
2009 if (selectedAssistant->id() == "two point" && m_handleDrag != handles[2] &&
2010 event->modifiers() != Qt::ShiftModifier) {
2011 // Snapping interactions that are specific to the two point assistant.
2012 // Skip this code block when only Shift is pressed, as
2013 // Shift means we only need closest-axis snapping.
2014
2015 QSharedPointer<TwoPointAssistant> assis = qSharedPointerCast<TwoPointAssistant>(selectedAssistant);
2016 KisPaintingAssistantHandleSP handleOpp = m_handleDrag == handles[0] ? handles[1] : handles[0];
2017 const QPointF prevPoint = m_currentAdjustment.isNull() ? m_dragStart : m_currentAdjustment;
2018
2019 qreal size = 0;
2020 const QTransform t = assis->localTransform(prevPoint,*handleOpp,*handles[2],&size);
2021 const QTransform inv = t.inverted();
2022
2023 // Exact alignment matters here, so fudge horizon line
2024 // to be perfectly horizontal instead of trusting the
2025 // QTransform calculation to do it
2026 const QLineF horizon = QLineF(t.map(prevPoint), QPointF(t.map(*handleOpp).x(),t.map(prevPoint).y()));
2027 const QPointF sp = QPointF(0,horizon.p1().y()+size);
2028
2029 const bool preserve_distortion_snap = event->modifiers() == Qt::ControlModifier;
2030 const bool preserve_left_right_ratio_snap = event->modifiers() == (Qt::ControlModifier|Qt::ShiftModifier);
2031 const bool preserve_horizon_snap = event->modifiers() == Qt::AltModifier;
2032
2033 QPointF snap_point;
2034 QPointF opp_snap_point;
2035 QLineF sp_to_opp_vp;
2036
2037 if (preserve_distortion_snap) {
2038 const QLineF sp_to_vp = QLineF(sp, t.map(*m_handleDrag));
2039 sp_to_opp_vp = sp_to_vp.normalVector();
2040 sp_to_vp.intersects(horizon,&snap_point);
2041 } else if (preserve_left_right_ratio_snap) {
2042 const QLineF prev_sp_to_vp = QLineF(sp, horizon.p1());
2043 QLineF new_sp_to_vp = prev_sp_to_vp.translated(t.map(*m_handleDrag)-sp);
2044 QPointF new_sp;
2045 new_sp_to_vp.intersects(QLineF(QPoint(0,0),QPointF(0,1)),&new_sp);
2046 sp_to_opp_vp = new_sp_to_vp.normalVector().translated(new_sp-new_sp_to_vp.p1());
2047 new_sp_to_vp.intersects(horizon,&snap_point);
2048 } else if (preserve_horizon_snap) {
2049 snap_point = QPointF(t.map(*m_handleDrag).x(),horizon.p1().y());
2050 sp_to_opp_vp = QLineF(sp,QPointF(t.map(prevPoint).x(),horizon.p1().y())).normalVector();
2051 }
2052
2053 // The snapping modes must be robust against falling into
2054 // invalid configurations, so test if the new snap points
2055 // actually do make sense
2056 const bool no_intersection =
2057 // NB: opp_snap_point is initialized here
2058 sp_to_opp_vp.intersects(horizon, &opp_snap_point) == QLineF::NoIntersection;
2059 const bool origin_is_between =
2060 (snap_point.x() < 0 && opp_snap_point.x() > 0) ||
2061 (snap_point.x() > 0 && opp_snap_point.x() < 0);
2062 const bool null_opp_point =
2063 qFuzzyIsNull(opp_snap_point.x()) ||
2064 qFuzzyIsNull(opp_snap_point.y());
2065 const bool overlapping_snap_points =
2066 qFuzzyCompare(opp_snap_point.x(),snap_point.x());
2067
2068 // Revert to original state if new points are invalid
2069 if (!origin_is_between || no_intersection || null_opp_point || overlapping_snap_points) {
2071 QPointF oppStart;
2072 // Use different recovery method for different
2073 // snapping modes
2074 if (preserve_distortion_snap) {
2075 sp_to_opp_vp = QLineF(sp, t.map(m_dragStart)).normalVector();
2076 sp_to_opp_vp.intersects(horizon, &oppStart);
2077 } else {
2078 const QPointF p1 = t.map(m_dragStart);
2079 const qreal p2x = preserve_horizon_snap ? t.map(*handleOpp).x() : -p1.x();
2080 const QPointF p2 = QPointF(p2x,p1.y());
2081 const QLineF new_horizon = QLineF(p1,p2);
2082 const qreal new_size = sqrt(pow(new_horizon.length()/2.0,2) -
2083 pow(abs(new_horizon.center().x()),2));
2084 const QPointF new_sp = QPointF(0,horizon.p1().y()+new_size);
2085 sp_to_opp_vp = QLineF(new_sp, t.map(m_dragStart)).normalVector();
2086 }
2087 sp_to_opp_vp.intersects(horizon, &oppStart);
2088 *handleOpp=inv.map(oppStart);
2089 m_currentAdjustment = QPointF(0,0); // clear
2090 } else {
2091 // otherwise use the new configuration if it's valid
2092 *m_handleDrag = inv.map(snap_point);
2093 *handleOpp = inv.map(opp_snap_point);
2095 }
2096 } else if (m_snapIsRadial == true) {
2097 QLineF dragRadius = QLineF(m_dragStart, event->point);
2098 dragRadius.setLength(m_radius.length());
2099 *m_handleDrag = dragRadius.p2();
2100 } else {
2101 QPointF snap_point = snapToClosestNiceAngle(event->point, m_dragStart);
2102 *m_handleDrag = snap_point;
2103 }
2104 } else {
2107 KisPaintingAssistantHandleSP handle_snap = handles.back();
2108 // for any assistant, snap 2nd handle to x or y axis relative to first handle
2109 if (handles.size() == 2) {
2110 QPointF snap_point = snapToClosestNiceAngle(event->point, (QPointF)(*handles[0]));
2111 *handle_snap = snap_point;
2112 } else {
2113 bool was_snapped = false;
2114 if (m_newAssistant->id() == "spline") {
2116 handles.size() == 3 ? start = handles[0] : start = handles[1];
2117 QPointF snap_point = snapToClosestNiceAngle(event->point, (QPointF)(*start));
2118 *handle_snap = snap_point;
2119 was_snapped = true;
2120 }
2121
2122 if (m_newAssistant->id() == "ellipse" ||
2123 m_newAssistant->id() == "concentric ellipse" ||
2124 m_newAssistant->id() == "fisheye-point") {
2125 QPointF center = QLineF(*handles[0], *handles[1]).center();
2126 QLineF radius = QLineF(center,*handles[0]);
2127 QLineF dragRadius = QLineF(center, event->point);
2128 dragRadius.setLength(radius.length());
2129 *handle_snap = dragRadius.p2();
2130 was_snapped = true;
2131 }
2132
2133 if (m_newAssistant->id() == "perspective") {
2135 handles.size() == 3 ? start = handles[1] : start = handles[2];
2136 QPointF snap_point = snapToClosestNiceAngle(event->point, (QPointF)(*start));
2137 *handle_snap = snap_point;
2138 was_snapped = true;
2139 }
2140 return was_snapped;
2141 }
2142 }
2143 }
2144 return true;
2145}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
QPointF p2
QPointF p3
QPointF p1
VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v)
QList< QString > QStringList
qreal distance(const QPointF &p1, const QPointF &p2)
KisPaintingAssistantHandleSP m_selectedNode2
void endPrimaryAction(KoPointerEvent *event) override
void slotChangeFixedLength(double value)
Ui::AssistantsToolOptions m_options
KisPaintingAssistantHandleSP m_handleHover
KisPaintingAssistantHandleSP m_handleDrag
void slotChangeTwoPointUseVertical(int value)
bool snap(KoPointerEvent *event)
void assistantSelected(KisPaintingAssistantSP assistant)
void slotToggleMoveButton(int index)
KisPaintingAssistantSP m_assistantDrag
void activate(const QSet< KoShape * > &shapes) override
void slotToggleDuplicateButton(int index)
void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override
void endAlternateAction(KoPointerEvent *event, AlternateAction action) override
QList< KisPaintingAssistantHandleSP > m_handles
void beginPrimaryAction(KoPointerEvent *event) override
void slotChangeFixedLengthUnit(int index)
void mouseMoveEvent(KoPointerEvent *event) override
void slotChangeTwoPointDensity(double value)
KisPaintingAssistantSP m_newAssistant
void removeAssistant(KisPaintingAssistantSP assistant)
void paint(QPainter &gc, const KoViewConverter &converter) override
void slotChangeMinorSubdivisions(int value)
void slotToggleLockButton(int index)
void slotChangeVanishingPointAngle(double value)
void slotGlobalAssistantsColorChanged(const QColor &)
KisPaintingAssistantHandleSP m_highlightedNode
void keyPressEvent(QKeyEvent *event) override
void beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) override
KisPaintingAssistantHandleSP m_handleCombine
KisAssistantTool(KoCanvasBase *canvas)
KisSpinBoxUnitManager * m_unitManager
void endActionImpl(KoPointerEvent *event)
void slotToggleSnapButton(int index)
void continuePrimaryAction(KoPointerEvent *event) override
void beginActionImpl(KoPointerEvent *event)
void slotChangeSubdivisions(int value)
PerspectiveAssistantEditionMode m_internalMode
QList< KisPaintingAssistantSP > m_origAssistantList
QWidget * createOptionWidget() override
void deactivate() override
void slotToggleDeleteButton(int index)
void slotEnableFixedLength(int enabled)
QPointer< KisCanvas2 > m_canvas
void continueActionImpl(KoPointerEvent *event)
KisPaintingAssistantHandleSP m_selectedNode1
The KisDocumentAwareSpinBoxUnitManager class is a KisSpinBoxUnitManager that is able to connect to th...
static KisPaintingAssistantFactoryRegistry * instance()
virtual QString name() const =0
void mergeWith(KisPaintingAssistantHandleSP)
KisPaintingAssistant * chiefAssistant() const
static QList< KisPaintingAssistantSP > cloneAssistantList(const QList< KisPaintingAssistantSP > &list)
static double norm2(const QPointF &p)
QPointF snapToGuide(KoPointerEvent *e, const QPointF &offset, bool useModifiers)
void setApparentUnitFromSymbol(QString pSymbol)
void setUnitDimension(UnitDimension dimension)
int getApparentUnitId() const
get the position of the apparent unit in the list of units. It is useful if we want to build a model ...
T get(const QString &id) const
Definition KoID.h:30
static bool compareNames(const KoID &id1, const KoID &id2)
Definition KoID.h:87
Qt::KeyboardModifiers modifiers() const
QPointF point
The point in document coordinates.
Q_INVOKABLE QString toolId() const
QAction * action(const QString &name) const
KoToolFactoryBase * factory() const
virtual QPointF documentToView(const QPointF &documentPoint) const
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)
#define dbgUI
Definition kis_debug.h:52
PointType snapToClosestNiceAngle(PointType point, PointType startPoint, qreal angle=(2 *M_PI)/24)
Definition kis_global.h:209
#define M_PI
Definition kis_global.h:111
QSharedPointer< T > toQShared(T *ptr)
QString qColorToQString(QColor color)
QColor qStringToQColor(QString colorString)
QIcon loadIcon(const QString &name)
virtual ToolMode mode() const
Definition kis_tool.cc:407
void activate(const QSet< KoShape * > &shapes) override
Definition kis_tool.cc:93
void deactivate() override
Definition kis_tool.cc:131
@ PAINT_MODE
Definition kis_tool.h:300
@ HOVER_MODE
Definition kis_tool.h:299
AlternateAction
Definition kis_tool.h:134
virtual void setMode(ToolMode mode)
Definition kis_tool.cc:403
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())