Krita Source Code Documentation
Loading...
Searching...
No Matches
ToolReferenceImages.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8
9#include <QDesktopServices>
10#include <QFile>
11#include <QLayout>
12#include <QMenu>
13#include <QMessageBox>
14#include <QAction>
15#include <QApplication>
16
17#include <KoSelection.h>
18#include <KoShapeRegistry.h>
19#include <KoShapeManager.h>
20#include <KoShapeController.h>
21#include <KoFileDialog.h>
22#include "KisMimeDatabase.h"
23
24#include <kis_action_registry.h>
25#include <kis_canvas2.h>
27#include <kis_node_manager.h>
28#include <KisViewManager.h>
29#include <KisDocument.h>
31#include <kis_image.h>
32#include "QClipboard"
33#include "kis_action.h"
35
38
40 : DefaultTool(canvas, false)
41{
42 setObjectName("ToolReferenceImages");
43}
44
48
49void ToolReferenceImages::activate(const QSet<KoShape*> &shapes)
50{
52
53 auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
54 KIS_ASSERT(kisCanvas);
55 connect(kisCanvas->image(), SIGNAL(sigNodeAddedAsync(KisNodeSP, KisNodeAdditionFlags)), this, SLOT(slotNodeAdded(KisNodeSP, KisNodeAdditionFlags)));
56 connect(kisCanvas->imageView()->document(), &KisDocument::sigReferenceImagesLayerChanged, this, qOverload<KisNodeSP>(&ToolReferenceImages::slotNodeAdded));
57
58 auto referenceImageLayer = document()->referenceImagesLayer();
59 if (referenceImageLayer) {
60 setReferenceImageLayer(referenceImageLayer);
61 }
62}
63
68
73
74void ToolReferenceImages::slotNodeAdded(KisNodeSP node, KisNodeAdditionFlags flags)
75{
76 Q_UNUSED(flags)
77
78 auto *referenceImagesLayer = dynamic_cast<KisReferenceImagesLayer*>(node.data());
79
80 if (referenceImagesLayer) {
81 setReferenceImageLayer(referenceImagesLayer);
82 }
83}
84
86{
87 m_layer = layer;
88 connect(layer.data(), SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
89 connect(layer->shapeManager(), SIGNAL(selectionChanged()), this, SLOT(repaintDecorations()));
90 connect(layer->shapeManager(), SIGNAL(selectionContentChanged()), this, SLOT(repaintDecorations()));
91}
92
94{
95 const KoShapeManager *manager = shapeManager();
96 return manager && manager->selection()->count() != 0;
97}
98
100{
101 auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
102 KIS_ASSERT_RECOVER_RETURN(kisCanvas);
103
104 KoFileDialog dialog(kisCanvas->viewManager()->mainWindowAsQWidget(), KoFileDialog::OpenFile, "OpenReferenceImage");
105 dialog.setCaption(i18n("Select a Reference Image"));
106
107 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
108 if (!locations.isEmpty()) {
109 dialog.setDefaultDir(locations.first());
110 }
111
112 QString filename = dialog.filename();
113 if (filename.isEmpty()) return;
114 if (!QFileInfo(filename).exists()) return;
115
116 auto *reference = KisReferenceImage::fromFile(filename, *kisCanvas->coordinatesConverter(), canvas()->canvasWidget());
117 if (reference) {
118 if (document()->referenceImagesLayer()) {
119 reference->setZIndex(document()->referenceImagesLayer()->shapes().size());
120 }
122 }
123}
124
131
138
140{
141 KisCanvas2* kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
142 KIS_ASSERT_RECOVER_RETURN(kisCanvas);
143
145 if (reference) {
146 if (document()->referenceImagesLayer()) {
147 reference->setZIndex(document()->referenceImagesLayer()->shapes().size());
148 }
150 } else {
151 if (canvas()->canvasWidget()) {
152 QMessageBox::critical(canvas()->canvasWidget(), i18nc("@title:window", "Krita"), i18n("Could not load reference image from clipboard"));
153 }
154 }
155}
156
158{
159 auto layer = m_layer.toStrongRef();
160 if (!layer) return;
161 if (!koSelection()) return;
162 if (koSelection()->selectedEditableShapes().isEmpty()) return;
163
164 canvas()->addCommand(layer->removeReferenceImages(document(), koSelection()->selectedEditableShapes()));
165}
166
168{
169 auto layer = m_layer.toStrongRef();
170 if (!layer) return;
171
172 canvas()->addCommand(layer->removeReferenceImages(document(), layer->shapes()));
173}
174
176{
177 auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
178 KIS_ASSERT_RECOVER_RETURN(kisCanvas);
179
180 KoFileDialog dialog(kisCanvas->viewManager()->mainWindowAsQWidget(), KoFileDialog::OpenFile, "OpenReferenceImageCollection");
181 dialog.setMimeTypeFilters(QStringList() << "application/x-krita-reference-images");
182 dialog.setCaption(i18n("Load Reference Images"));
183
184 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
185 if (!locations.isEmpty()) {
186 dialog.setDefaultDir(locations.first());
187 }
188
189 QString filename = dialog.filename();
190 if (filename.isEmpty()) return;
191 if (!QFileInfo(filename).exists()) return;
192
193 QFile file(filename);
194 if (!file.open(QIODevice::ReadOnly)) {
195 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not open '%1'.", filename));
196 return;
197 }
198
200
201 int currentZIndex = 0;
202 if (document()->referenceImagesLayer()) {
203 currentZIndex = document()->referenceImagesLayer()->shapes().size();
204 }
205
206 if (collection.load(&file)) {
207 QList<KoShape*> shapes;
208 Q_FOREACH(auto *reference, collection.referenceImages()) {
209 reference->setZIndex(currentZIndex);
210 shapes.append(reference);
211 currentZIndex += 1;
212 }
213
215 } else {
216 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not load reference images from '%1'.", filename));
217 }
218 file.close();
219}
220
222{
223 KisCursorOverrideLock cursorLock(Qt::BusyCursor);
224
225 auto layer = m_layer.toStrongRef();
226 if (!layer || layer->shapeCount() == 0) return;
227
228 auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
229 KIS_ASSERT_RECOVER_RETURN(kisCanvas);
230
231 KoFileDialog dialog(kisCanvas->viewManager()->mainWindowAsQWidget(), KoFileDialog::SaveFile, "SaveReferenceImageCollection");
232 QString mimetype = "application/x-krita-reference-images";
233 dialog.setMimeTypeFilters(QStringList() << mimetype, mimetype);
234 dialog.setCaption(i18n("Save Reference Images"));
235
236 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
237 if (!locations.isEmpty()) {
238 dialog.setDefaultDir(locations.first());
239 }
240
241 QString filename = dialog.filename();
242 if (filename.isEmpty()) return;
243
244 QString fileMime = KisMimeDatabase::mimeTypeForFile(filename, false);
245 if (fileMime != "application/x-krita-reference-images") {
246 filename.append(filename.endsWith(".") ? "krf" : ".krf");
247 }
248
249 QFile file(filename);
250 if (!file.open(QIODevice::WriteOnly)) {
251 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Could not open '%1' for saving.", filename));
252 return;
253 }
254
255 KisReferenceImageCollection collection(layer->referenceImages());
256 bool ok = collection.save(&file);
257 file.close();
258
259 if (!ok) {
260 QMessageBox::critical(qApp->activeWindow(), i18nc("@title:window", "Krita"), i18n("Failed to save reference images."));
261 }
262}
263
265{
266 auto layer = m_layer.toStrongRef();
267 if (!layer) return;
268
269 m_optionsWidget->selectionChanged(layer->shapeManager()->selection());
271}
272
274{
275 // Instead of inheriting DefaultTool's multi-tab implementation, inherit straight from KoToolBase
277}
278
280{
281 if (!m_optionsWidget) {
283 // See https://bugs.kde.org/show_bug.cgi?id=316896
284 QWidget *specialSpacer = new QWidget(m_optionsWidget);
285 specialSpacer->setObjectName("SpecialSpacer");
286 specialSpacer->setFixedSize(0, 0);
287 m_optionsWidget->layout()->addWidget(specialSpacer);
288 }
289 return m_optionsWidget;
290}
291
293{
294 return true;
295}
296
298{
299 auto layer = m_layer.toStrongRef();
300 return layer ? layer->shapeManager() : nullptr;
301}
302
304{
305 auto manager = shapeManager();
306 return manager ? manager->selection() : nullptr;
307}
308
310{
311 action("object_group")->setEnabled(false);
312 action("object_unite")->setEnabled(false);
313 action("object_intersect")->setEnabled(false);
314 action("object_subtract")->setEnabled(false);
315 action("object_split")->setEnabled(false);
316 action("object_ungroup")->setEnabled(false);
317}
318
320{
321 auto layer = m_layer.toStrongRef();
322 if (!layer) return;
323
325
326 if (!shapes.empty()) {
327 canvas()->addCommand(layer->removeReferenceImages(document(), shapes));
328 }
329}
330
332{
333 if (m_contextMenu) {
334 m_contextMenu->clear();
335 m_contextMenu->addSection(i18n("Reference Image Actions"));
336 m_contextMenu->addSeparator();
337
338 QMenu *transform = m_contextMenu->addMenu(i18n("Transform"));
339
340 transform->addAction(action("object_transform_rotate_90_cw"));
341 transform->addAction(action("object_transform_rotate_90_ccw"));
342 transform->addAction(action("object_transform_rotate_180"));
343 transform->addSeparator();
344 transform->addAction(action("object_transform_mirror_horizontally"));
345 transform->addAction(action("object_transform_mirror_vertically"));
346 transform->addSeparator();
347 transform->addAction(action("object_transform_reset"));
348
349 m_contextMenu->addSeparator();
350
351 m_contextMenu->addAction(action("edit_cut"));
352 m_contextMenu->addAction(action("edit_copy"));
353 m_contextMenu->addAction(action("edit_paste"));
354
355 m_contextMenu->addSeparator();
356
357 m_contextMenu->addAction(action("object_order_front"));
358 m_contextMenu->addAction(action("object_order_raise"));
359 m_contextMenu->addAction(action("object_order_lower"));
360 m_contextMenu->addAction(action("object_order_back"));
361 }
362
363 return m_contextMenu.data();
364}
365
367{
368 copy();
370}
371
373{
375 if (!shapes.isEmpty()) {
376 KoShape* shape = shapes.at(0);
377 KisReferenceImage *reference = dynamic_cast<KisReferenceImage*>(shape);
379 QClipboard *cb = QApplication::clipboard();
380 cb->setImage(reference->getImage());
381 }
382}
383
385{
387 return true;
388}
389
391{
392 Q_FOREACH(KoShape *shape, shapeManager()->shapes()) {
393 if (!shape->isSelectable()) continue;
394 koSelection()->select(shape);
395 }
397
398 return true;
399}
400
406
408{
409 auto kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
410 KIS_ASSERT(kisCanvas);
411 return kisCanvas->imageView()->document();
412}
413
415{
417 QList<QAction *> actions;
418
419 QStringList actionNames;
420 actionNames << "object_order_front"
421 << "object_order_raise"
422 << "object_order_lower"
423 << "object_order_back"
424 << "object_transform_rotate_90_cw"
425 << "object_transform_rotate_90_ccw"
426 << "object_transform_rotate_180"
427 << "object_transform_mirror_horizontally"
428 << "object_transform_mirror_vertically"
429 << "object_transform_reset";
430
431 Q_FOREACH(QAction *action, defaultActions) {
432 if (actionNames.contains(action->objectName())) {
433 actions << action;
434 } else {
435 delete action;
436 }
437 }
438 return actions;
439}
QList< QString > QStringList
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
QList< QAction * > createActionsImpl() override
createActionsImpl should be reimplemented if the tool needs any actions. The actions should have a va...
QScopedPointer< QMenu > m_contextMenu
void updateActions()
Update actions on selection change.
void deactivate() override
void activate(const QSet< KoShape * > &shapes) override
KisCoordinatesConverter * coordinatesConverter
KisViewManager * viewManager() const
KisSharedPtr< KisReferenceImagesLayer > referenceImagesLayer() const
void sigReferenceImagesLayerChanged(KisSharedPtr< KisReferenceImagesLayer > layer)
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
void createReferenceImageFromVisible()
void createReferenceImageFromLayer()
const QVector< KisReferenceImage * > & referenceImages() const
The KisReferenceImage class represents a single reference image.
static KisReferenceImage * fromFile(const QString &filename, const KisCoordinatesConverter &converter, QWidget *parent)
static KisReferenceImage * fromClipboard(const KisCoordinatesConverter &converter)
static KUndo2Command * addReferenceImages(KisDocument *document, QList< KoShape * > referenceImages)
KoShapeManager * shapeManager() const
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
KisSharedPtr< T > toStrongRef() const
toStrongRef returns a KisSharedPtr which may be dereferenced.
virtual void addCommand(KUndo2Command *command)=0
void deselectAll()
clear the selections list
void select(KoShape *shape)
int count() const
return the selection count, i.e. the number of all selected shapes
const QList< KoShape * > selectedShapes() const
QList< KoShape * > shapes() const
KoSelection * selection
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:954
bool isSelectable() const
Definition KoShape.cpp:1014
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
void selectionChanged(bool hasSelection)
virtual QList< QPointer< QWidget > > createOptionWidgets()
virtual void repaintDecorations()
QAction * action(const QString &name) const
QList< QAction * > createActionsImpl() override
createActionsImpl should be reimplemented if the tool needs any actions. The actions should have a va...
void selectionChanged(KoSelection *selection)
void deleteSelection() override
reimplemented
friend class ToolReferenceImagesWidget
void setReferenceImageLayer(KisSharedPtr< KisReferenceImagesLayer > layer)
KisDocument * document() const
QList< QPointer< QWidget > > createOptionWidgets() override
ToolReferenceImagesWidget * m_optionsWidget
bool isValidForCurrentLayer() const override
QWidget * createOptionWidget() override
KoShapeManager * shapeManager() const override
QMenu * popupActionsMenu() override
void copy() const override
void slotNodeAdded(KisNodeSP node)
ToolReferenceImages(KoCanvasBase *canvas)
KisWeakSharedPtr< KisReferenceImagesLayer > m_layer
KoSelection * koSelection() const override
void activate(const QSet< KoShape * > &shapes) override
void updateDistinctiveActions(const QList< KoShape * > &editableShapes) override
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT(cond)
Definition kis_assert.h:33